Skip to content

Commit 3eb8171

Browse files
authored
fix(api-reference): value in requeste example code block (scalar#6284)
* fix: value in request example + dropdown * docs(changeset): fix: value in request example code * fix: tests * fix: add checkbox back in * fix: test
1 parent 716a83a commit 3eb8171

File tree

5 files changed

+97
-100
lines changed

5 files changed

+97
-100
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@scalar/api-reference': patch
3+
---
4+
5+
fix: value in request example code

packages/api-reference/src/features/example-responses/ExampleResponses.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ const id = useId()
3232
3333
const { copyToClipboard } = useClipboard()
3434
35-
const selectedExampleKey = ref<string>('')
36-
3735
// Bring the status codes in the right order.
3836
const orderedStatusCodes = computed(() => Object.keys(responses ?? {}).sort())
3937
@@ -74,6 +72,10 @@ const currentJsonResponse = computed(() => {
7472
)
7573
})
7674
75+
const selectedExampleKey = ref<string>(
76+
Object.keys(currentJsonResponse.value.examples ?? {})[0] ?? '',
77+
)
78+
7779
/**
7880
* Gets the first example response if there are multiple example responses
7981
* or the only example if there is only one example response.

packages/api-reference/src/v2/blocks/scalar-request-example-block/components/ExamplePicker.test.ts

Lines changed: 53 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,20 @@ describe('ExamplePicker', () => {
3232
})
3333

3434
expect(wrapper.find('[data-testid="example-picker"]').exists()).toBe(true)
35-
expect(wrapper.text()).toContain('First Example')
35+
expect(wrapper.text()).toContain('Select an example')
3636
})
3737

38-
it('generates correct options from examples', () => {
38+
it('renders dropdown button with correct text', () => {
3939
const wrapper = mount(ExamplePicker, {
4040
props: {
4141
examples: mockExamples,
4242
modelValue: '',
4343
},
4444
})
4545

46-
const combobox = wrapper.findComponent({ name: 'ScalarCombobox' })
47-
const options = combobox.props('options')
48-
49-
expect(options).toHaveLength(3)
50-
expect(options).toEqual([
51-
{ id: 'example-1', label: 'First Example' },
52-
{ id: 'example-2', label: 'Second Example' },
53-
{ id: 'example-3', label: 'example-3' }, // Falls back to key when no summary
54-
])
46+
const button = wrapper.find('[data-testid="example-picker"]')
47+
expect(button.exists()).toBe(true)
48+
expect(button.text()).toContain('Select an example')
5549
})
5650

5751
it('handles empty examples object', () => {
@@ -62,10 +56,6 @@ describe('ExamplePicker', () => {
6256
},
6357
})
6458

65-
const combobox = wrapper.findComponent({ name: 'ScalarCombobox' })
66-
const options = combobox.props('options')
67-
68-
expect(options).toHaveLength(0)
6959
expect(wrapper.text()).toContain('Select an example')
7060
})
7161

@@ -84,10 +74,8 @@ describe('ExamplePicker', () => {
8474
},
8575
})
8676

87-
const combobox = wrapper.findComponent({ name: 'ScalarCombobox' })
88-
const options = combobox.props('options')
89-
90-
expect(options).toEqual([{ id: 'key-only', label: 'key-only' }])
77+
// The button should show "Select an example" when no example is selected
78+
expect(wrapper.text()).toContain('Select an example')
9179
})
9280

9381
it('handles null key in getLabel function', () => {
@@ -110,40 +98,41 @@ describe('ExamplePicker', () => {
11098
},
11199
})
112100

113-
// Initially no example selected
114-
expect(wrapper.text()).toContain('First Example')
101+
// Initially shows "Select an example"
102+
expect(wrapper.text()).toContain('Select an example')
115103

116104
// Set a selected example
117105
await wrapper.setProps({
118106
examples: mockExamples,
119107
modelValue: 'example-1',
120108
})
121109

122-
// Simulate model update
123-
const combobox = wrapper.findComponent({ name: 'ScalarCombobox' })
124-
await combobox.vm.$emit('update:modelValue', { id: 'example-2', label: 'Second Example' })
125-
126110
await nextTick()
127111

128112
// The button should now show the selected example
129-
expect(wrapper.text()).toContain('Second Example')
113+
expect(wrapper.text()).toContain('First Example')
130114
})
131115

132-
it('emits model update when example is selected', async () => {
116+
it('updates model value when example is selected', async () => {
133117
const wrapper = mount(ExamplePicker, {
134118
props: {
135119
examples: mockExamples,
136120
modelValue: '',
137121
},
138122
})
139123

140-
const combobox = wrapper.findComponent({ name: 'ScalarCombobox' })
141-
const selectedOption = { id: 'example-2', label: 'Second Example' }
124+
// Initially no example selected
125+
expect(wrapper.vm.selectedExampleKey).toBe('')
126+
127+
// Update the model value directly
128+
await wrapper.setProps({
129+
examples: mockExamples,
130+
modelValue: 'example-2',
131+
})
142132

143-
await combobox.vm.$emit('update:modelValue', selectedOption)
133+
await nextTick()
144134

145-
// Verify the selectExample method was called with the correct option
146-
// This tests the internal logic of the component
135+
// Verify the selectedExampleKey was updated
147136
expect(wrapper.vm.selectedExampleKey).toBe('example-2')
148137
})
149138

@@ -170,44 +159,33 @@ describe('ExamplePicker', () => {
170159
},
171160
})
172161

173-
const combobox = wrapper.findComponent({ name: 'ScalarCombobox' })
174-
const options = combobox.props('options')
175-
176-
expect(options).toEqual([
177-
{ id: 'example-with-dashes', label: 'Dashed Example' },
178-
{ id: 'example_with_underscores', label: 'Underscore Example' },
179-
{ id: 'example.with.dots', label: 'Dotted Example' },
180-
])
162+
// Test that getLabel works with special characters
163+
expect(wrapper.vm.getLabel('example-with-dashes')).toBe('Dashed Example')
164+
expect(wrapper.vm.getLabel('example_with_underscores')).toBe('Underscore Example')
165+
expect(wrapper.vm.getLabel('example.with.dots')).toBe('Dotted Example')
181166
})
182167

183-
it('computes selected example correctly', () => {
168+
it('shows correct label for selected example', () => {
184169
const wrapper = mount(ExamplePicker, {
185170
props: {
186171
examples: mockExamples,
187-
modelValue: '',
172+
modelValue: 'example-1',
188173
},
189174
})
190175

191-
const vm = wrapper.vm
192-
193-
// Test when an example is selected
194-
vm.selectedExampleKey = 'example-1'
195-
expect(vm.selectedExample).toEqual({
196-
id: 'example-1',
197-
label: 'First Example',
198-
})
176+
// Should show the summary when available
177+
expect(wrapper.text()).toContain('First Example')
199178

200-
vm.selectedExampleKey = 'example-2'
201-
expect(vm.selectedExample).toEqual({
202-
id: 'example-2',
203-
label: 'Second Example',
179+
// Test with example that has no summary
180+
const wrapper2 = mount(ExamplePicker, {
181+
props: {
182+
examples: mockExamples,
183+
modelValue: 'example-3',
184+
},
204185
})
205186

206-
vm.selectedExampleKey = 'example-3'
207-
expect(vm.selectedExample).toEqual({
208-
id: 'example-3',
209-
label: 'example-3',
210-
})
187+
// Should fall back to the key when no summary
188+
expect(wrapper2.text()).toContain('example-3')
211189
})
212190

213191
it('handles examples with null or undefined values', () => {
@@ -223,13 +201,22 @@ describe('ExamplePicker', () => {
223201
},
224202
})
225203

226-
const combobox = wrapper.findComponent({ name: 'ScalarCombobox' })
227-
const options = combobox.props('options')
204+
// Should handle null/undefined gracefully in getLabel
205+
expect(wrapper.vm.getLabel('null-example')).toBe('null-example')
206+
expect(wrapper.vm.getLabel('undefined-example')).toBe('undefined-example')
207+
})
208+
209+
it('generates correct labels for all examples', () => {
210+
const wrapper = mount(ExamplePicker, {
211+
props: {
212+
examples: mockExamples,
213+
modelValue: '',
214+
},
215+
})
228216

229-
// Should handle null/undefined gracefully
230-
expect(options).toEqual([
231-
{ id: 'null-example', label: 'null-example' },
232-
{ id: 'undefined-example', label: 'undefined-example' },
233-
])
217+
// Test that getLabel generates correct labels for all examples
218+
expect(wrapper.vm.getLabel('example-1')).toBe('First Example')
219+
expect(wrapper.vm.getLabel('example-2')).toBe('Second Example')
220+
expect(wrapper.vm.getLabel('example-3')).toBe('example-3') // Falls back to key when no summary
234221
})
235222
})
Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,19 @@
11
<script setup lang="ts">
22
import {
33
ScalarButton,
4-
ScalarCombobox,
5-
type ScalarComboboxOption,
4+
ScalarDropdown,
5+
ScalarDropdownItem,
6+
ScalarIcon,
67
} from '@scalar/components'
78
import { ScalarIconCaretDown } from '@scalar/icons'
89
import type { ExampleObject } from '@scalar/workspace-store/schemas/v3.1/strict/example'
9-
import { computed } from 'vue'
1010
1111
const props = defineProps<{
1212
examples: Record<string, ExampleObject>
1313
}>()
1414
1515
const selectedExampleKey = defineModel<string>({ required: true })
1616
17-
/** Generate the options for the combobox */
18-
const exampleOptions = computed<ScalarComboboxOption[]>(() => {
19-
return Object.keys(props.examples).map((key) => ({
20-
id: key,
21-
label: getLabel(key),
22-
}))
23-
})
24-
25-
/** Get the currently selected example */
26-
const selectedExample = computed<ScalarComboboxOption>(
27-
() =>
28-
exampleOptions.value.find(
29-
(option) => option.id === selectedExampleKey.value,
30-
) ?? exampleOptions.value[0],
31-
)
32-
3317
/** Generate label for an example */
3418
const getLabel = (key: string | null) => {
3519
if (!key) {
@@ -41,33 +25,46 @@ const getLabel = (key: string | null) => {
4125
}
4226
4327
/** Handle example selection */
44-
const selectExample = (option: ScalarComboboxOption) => {
45-
selectedExampleKey.value = option.id
28+
const selectExample = (key: string) => {
29+
selectedExampleKey.value = key
4630
}
4731
4832
// For testing
4933
defineExpose({
5034
getLabel,
51-
selectedExample,
5235
selectedExampleKey,
5336
})
5437
</script>
5538

5639
<template>
57-
<ScalarCombobox
58-
class="max-h-80"
59-
:modelValue="selectedExample"
60-
:options="exampleOptions"
61-
teleport
62-
placement="bottom-start"
63-
@update:modelValue="selectExample">
40+
<ScalarDropdown placement="bottom-start">
6441
<ScalarButton
6542
data-testid="example-picker"
6643
class="text-c-1 hover:bg-b-2 flex h-full w-fit gap-1.5 px-1.5 py-0.75 font-normal"
6744
fullWidth
6845
variant="ghost">
69-
<span>{{ selectedExample?.label || 'Select an example' }}</span>
46+
<span>{{ getLabel(selectedExampleKey) }}</span>
7047
<ScalarIconCaretDown />
7148
</ScalarButton>
72-
</ScalarCombobox>
49+
<template #items>
50+
<ScalarDropdownItem
51+
v-for="(_, key) in examples"
52+
:key="key"
53+
@click="selectExample(key)">
54+
<div
55+
class="flex h-4 w-4 items-center justify-center rounded-full p-[3px]"
56+
:class="
57+
selectedExampleKey === key
58+
? 'bg-c-accent text-b-1'
59+
: 'shadow-border text-transparent'
60+
">
61+
<ScalarIcon
62+
class="size-2.5"
63+
icon="Checkmark"
64+
thickness="3" />
65+
</div>
66+
<span class="overflow-hidden text-ellipsis">{{ getLabel(key) }}</span>
67+
</ScalarDropdownItem>
68+
</template>
69+
</ScalarDropdown>
7370
</template>

packages/api-reference/src/v2/blocks/scalar-request-example-block/components/RequestExample.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-met
8888
import { ScalarIconCaretDown } from '@scalar/icons'
8989
import type { XCodeSample } from '@scalar/openapi-types/schemas/extensions'
9090
import { type AvailableClients } from '@scalar/snippetz'
91+
import type { ExampleObject } from '@scalar/workspace-store/schemas/v3.1/strict/example'
9192
import type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/path-operations'
9293
import type { SecuritySchemeObject } from '@scalar/workspace-store/schemas/v3.1/strict/security-scheme'
9394
import type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/server'
@@ -196,6 +197,11 @@ const generatedCode = computed<string>(() => {
196197
)
197198
}
198199
200+
const selectedExample =
201+
operationExamples.value[selectedExampleKey.value || '']
202+
const example =
203+
(selectedExample as ExampleObject)?.value ?? selectedExample?.summary
204+
199205
return generateCodeSnippet({
200206
clientId: localSelectedClient.value.id as AvailableClients[number],
201207
operation,
@@ -204,7 +210,7 @@ const generatedCode = computed<string>(() => {
204210
securitySchemes,
205211
contentType: selectedContentType,
206212
path,
207-
example: operationExamples.value[selectedExampleKey.value || ''],
213+
example,
208214
})
209215
} catch (error) {
210216
console.error('[generateSnippet]', error)

0 commit comments

Comments
 (0)