@madgex/design-system 14.2.0 → 14.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,402 @@
1
+ // eslint-disable-next-line n/no-unpublished-import
2
+ import { describe, it, expect, afterEach } from 'vitest';
3
+ import { MdsConditionalSection, shouldShow } from './conditional-section.js';
4
+
5
+ if (!customElements.get('mds-conditional-section')) {
6
+ customElements.define('mds-conditional-section', MdsConditionalSection);
7
+ }
8
+
9
+ function emitInput(target) {
10
+ target.dispatchEvent(new Event('input', { bubbles: true }));
11
+ }
12
+
13
+ function mountScenario({ inputsHtml, fieldName, showWhen, includeShowWhen = true }) {
14
+ const container = document.createElement('div');
15
+ const showWhenAttr = includeShowWhen ? `show-when="${String(showWhen).replace(/"/g, '"')}"` : '';
16
+ container.innerHTML = `
17
+ <form id="test-conditional-form">
18
+ ${inputsHtml}
19
+ <mds-conditional-section field-name="${fieldName}" ${showWhenAttr}>
20
+ <p data-testid="conditional-content">Conditional content</p>
21
+ </mds-conditional-section>
22
+ </form>
23
+ `;
24
+ document.body.appendChild(container);
25
+ const form = container.querySelector('form');
26
+ const section = container.querySelector('mds-conditional-section');
27
+ return { container, form, section };
28
+ }
29
+
30
+ describe('shouldShow', () => {
31
+ it('is true when any checked value matches a comma-separated trigger (OR)', () => {
32
+ const form = document.createElement('form');
33
+ form.innerHTML = `
34
+ <input type="checkbox" name="i" value="sports" checked />
35
+ <input type="checkbox" name="i" value="music" />
36
+ `;
37
+ expect(shouldShow(form.elements.i, 'sports,music')).toBe(true);
38
+
39
+ form.querySelector('[value="sports"]').checked = false;
40
+ form.querySelector('[value="music"]').checked = true;
41
+ expect(shouldShow(form.elements.i, 'sports,music')).toBe(true);
42
+ });
43
+
44
+ it('is false when no checked value matches triggers', () => {
45
+ const form = document.createElement('form');
46
+ form.innerHTML = `
47
+ <input type="checkbox" name="i" value="sports" />
48
+ <input type="checkbox" name="i" value="music" />
49
+ <input type="checkbox" name="i" value="travel" checked />
50
+ `;
51
+ expect(shouldShow(form.elements.i, 'sports,music')).toBe(false);
52
+ });
53
+
54
+ it('is false when a checkbox group has nothing checked', () => {
55
+ const form = document.createElement('form');
56
+ form.innerHTML = `
57
+ <input type="checkbox" name="i" value="sports" />
58
+ <input type="checkbox" name="i" value="music" />
59
+ `;
60
+ expect(shouldShow(form.elements.i, 'sports')).toBe(false);
61
+ });
62
+
63
+ it('trims trigger segments and ignores empty parts', () => {
64
+ const form = document.createElement('form');
65
+ form.innerHTML = `
66
+ <input type="checkbox" name="i" value="a" checked />
67
+ <input type="checkbox" name="i" value="b" />
68
+ `;
69
+ expect(shouldShow(form.elements.i, ' a , b ')).toBe(true);
70
+ });
71
+
72
+ it('is false when show-when is null or empty', () => {
73
+ const form = document.createElement('form');
74
+ form.innerHTML = `<input type="checkbox" name="i" value="x" checked />`;
75
+ expect(shouldShow(form.elements.i, null)).toBe(false);
76
+ expect(shouldShow(form.elements.i, '')).toBe(false);
77
+ });
78
+
79
+ it('for a lone checkbox uses true/false against triggers', () => {
80
+ const form = document.createElement('form');
81
+ form.innerHTML = '<input type="checkbox" name="c" />';
82
+ const field = form.elements.c;
83
+ expect(shouldShow(field, 'false')).toBe(true);
84
+ expect(shouldShow(field, 'true')).toBe(false);
85
+ field.checked = true;
86
+ expect(shouldShow(field, 'true')).toBe(true);
87
+ expect(shouldShow(field, 'false')).toBe(false);
88
+ });
89
+
90
+ it('for a lone checkbox with a custom value matches show-when against that value when checked', () => {
91
+ const form = document.createElement('form');
92
+ form.innerHTML = '<input type="checkbox" name="c" value="yes" />';
93
+ const field = form.elements.c;
94
+ expect(shouldShow(field, 'yes')).toBe(false);
95
+ field.checked = true;
96
+ expect(shouldShow(field, 'yes')).toBe(true);
97
+ expect(shouldShow(field, 'no')).toBe(false);
98
+ });
99
+
100
+ it('for a lone checkbox with a custom value still supports show-when true/false', () => {
101
+ const form = document.createElement('form');
102
+ form.innerHTML = '<input type="checkbox" name="c" value="yes" />';
103
+ const field = form.elements.c;
104
+ field.checked = true;
105
+ expect(shouldShow(field, 'true')).toBe(true);
106
+ expect(shouldShow(field, 'false')).toBe(false);
107
+ field.checked = false;
108
+ expect(shouldShow(field, 'false')).toBe(true);
109
+ });
110
+
111
+ it('for a checkbox group matches checked values', () => {
112
+ const form = document.createElement('form');
113
+ form.innerHTML = `
114
+ <input type="checkbox" name="i" value="sports" />
115
+ <input type="checkbox" name="i" value="music" checked />
116
+ <input type="checkbox" name="i" value="travel" />
117
+ `;
118
+ expect(shouldShow(form.elements.i, 'music')).toBe(true);
119
+ });
120
+
121
+ it('uses "on" when a checked checkbox has no value attribute', () => {
122
+ const form = document.createElement('form');
123
+ form.innerHTML = `
124
+ <input type="checkbox" name="i" checked />
125
+ <input type="checkbox" name="i" />
126
+ `;
127
+ expect(shouldShow(form.elements.i, 'on')).toBe(true);
128
+ });
129
+
130
+ it('for a radio group matches the selected value', () => {
131
+ const form = document.createElement('form');
132
+ form.innerHTML = `
133
+ <input type="radio" name="r" value="a" />
134
+ <input type="radio" name="r" value="b" checked />
135
+ `;
136
+ expect(shouldShow(form.elements.r, 'b')).toBe(true);
137
+ expect(shouldShow(form.elements.r, 'a')).toBe(false);
138
+ });
139
+
140
+ it('reads multiple same-name controls from a RadioNodeList', () => {
141
+ const form = document.createElement('form');
142
+ form.innerHTML = `
143
+ <input type="checkbox" name="i" value="a" />
144
+ <input type="checkbox" name="i" value="b" />
145
+ `;
146
+ const list = form.elements.i;
147
+ expect(list).toBeInstanceOf(RadioNodeList);
148
+ expect(list.length).toBe(2);
149
+ list[1].checked = true;
150
+ expect(shouldShow(list, 'b')).toBe(true);
151
+ });
152
+
153
+ it('for a multiple select matches any selected option against comma-separated triggers', () => {
154
+ const form = document.createElement('form');
155
+ form.innerHTML = `
156
+ <select name="skills" id="skills" multiple>
157
+ <option value="js">JavaScript</option>
158
+ <option value="css">CSS</option>
159
+ <option value="html">HTML</option>
160
+ </select>
161
+ `;
162
+ const select = form.elements.skills;
163
+ expect(shouldShow(select, 'js,css')).toBe(false);
164
+
165
+ select.options[1].selected = true;
166
+ expect(shouldShow(select, 'js,css')).toBe(true);
167
+
168
+ select.options[1].selected = false;
169
+ select.options[2].selected = true;
170
+ expect(shouldShow(select, 'js,css')).toBe(false);
171
+ });
172
+
173
+ it('for a multiple select matches when a non-first selected option matches show-when alone', () => {
174
+ const form = document.createElement('form');
175
+ form.innerHTML = `
176
+ <select name="skills" id="skills" multiple>
177
+ <option value="js">JavaScript</option>
178
+ <option value="css">CSS</option>
179
+ </select>
180
+ `;
181
+ const select = form.elements.skills;
182
+ select.options[0].selected = true;
183
+ select.options[1].selected = true;
184
+ expect(shouldShow(select, 'css')).toBe(true);
185
+ });
186
+
187
+ it('for a multiple select stays visible while any matching option remains selected', () => {
188
+ const form = document.createElement('form');
189
+ form.innerHTML = `
190
+ <select name="skills" id="skills" multiple>
191
+ <option value="js">JavaScript</option>
192
+ <option value="css">CSS</option>
193
+ </select>
194
+ `;
195
+ const select = form.elements.skills;
196
+ select.options[0].selected = true;
197
+ select.options[1].selected = true;
198
+ expect(shouldShow(select, 'js,css')).toBe(true);
199
+
200
+ select.options[0].selected = false;
201
+ expect(shouldShow(select, 'js,css')).toBe(true);
202
+
203
+ select.options[1].selected = false;
204
+ expect(shouldShow(select, 'js,css')).toBe(false);
205
+ });
206
+ });
207
+
208
+ describe('MdsConditionalSection', () => {
209
+ let container;
210
+
211
+ afterEach(() => {
212
+ container?.remove();
213
+ container = undefined;
214
+ });
215
+
216
+ describe('checkbox group', () => {
217
+ it('shows when a non-first checkbox matching show-when is checked', () => {
218
+ ({ container } = mountScenario({
219
+ fieldName: 'interests',
220
+ showWhen: 'music',
221
+ inputsHtml: `
222
+ <input type="checkbox" name="interests" value="sports" id="cb-sports" />
223
+ <input type="checkbox" name="interests" value="music" id="cb-music" />
224
+ <input type="checkbox" name="interests" value="travel" id="cb-travel" />
225
+ `,
226
+ }));
227
+ const section = container.querySelector('mds-conditional-section');
228
+ expect(section.hidden).toBe(true);
229
+
230
+ const music = container.querySelector('#cb-music');
231
+ music.checked = true;
232
+ emitInput(music);
233
+
234
+ expect(section.hidden).toBe(false);
235
+ });
236
+
237
+ it('matches any comma-separated trigger (OR)', () => {
238
+ ({ container } = mountScenario({
239
+ fieldName: 'interests',
240
+ showWhen: 'sports,music',
241
+ inputsHtml: `
242
+ <input type="checkbox" name="interests" value="sports" id="cb-sports" />
243
+ <input type="checkbox" name="interests" value="music" id="cb-music" />
244
+ <input type="checkbox" name="interests" value="travel" id="cb-travel" />
245
+ `,
246
+ }));
247
+ const section = container.querySelector('mds-conditional-section');
248
+ const travel = container.querySelector('#cb-travel');
249
+ travel.checked = true;
250
+ emitInput(travel);
251
+ expect(section.hidden).toBe(true);
252
+
253
+ const music = container.querySelector('#cb-music');
254
+ music.checked = true;
255
+ emitInput(music);
256
+ expect(section.hidden).toBe(false);
257
+ });
258
+
259
+ it('stays visible if one matching trigger is unchecked but another remains checked', () => {
260
+ ({ container } = mountScenario({
261
+ fieldName: 'interests',
262
+ showWhen: 'sports,music',
263
+ inputsHtml: `
264
+ <input type="checkbox" name="interests" value="sports" id="cb-sports" />
265
+ <input type="checkbox" name="interests" value="music" id="cb-music" />
266
+ `,
267
+ }));
268
+ const section = container.querySelector('mds-conditional-section');
269
+ const sports = container.querySelector('#cb-sports');
270
+ const music = container.querySelector('#cb-music');
271
+ sports.checked = true;
272
+ music.checked = true;
273
+ emitInput(sports);
274
+
275
+ expect(section.hidden).toBe(false);
276
+
277
+ sports.checked = false;
278
+ emitInput(sports);
279
+ expect(section.hidden).toBe(false);
280
+
281
+ music.checked = false;
282
+ emitInput(music);
283
+ expect(section.hidden).toBe(true);
284
+ });
285
+ });
286
+
287
+ describe('regressions', () => {
288
+ it('single checkbox with show-when true toggles visibility', () => {
289
+ ({ container } = mountScenario({
290
+ fieldName: 'agree',
291
+ showWhen: 'true',
292
+ inputsHtml: '<input type="checkbox" name="agree" id="agree" />',
293
+ }));
294
+ const section = container.querySelector('mds-conditional-section');
295
+ const cb = container.querySelector('#agree');
296
+ expect(section.hidden).toBe(true);
297
+ cb.checked = true;
298
+ emitInput(cb);
299
+ expect(section.hidden).toBe(false);
300
+ });
301
+
302
+ it('hide-salary pattern: show-when false when checkbox unchecked', () => {
303
+ ({ container } = mountScenario({
304
+ fieldName: 'HideSalary',
305
+ showWhen: 'false',
306
+ inputsHtml: '<input type="checkbox" name="HideSalary" id="HideSalary" />',
307
+ }));
308
+ const section = container.querySelector('mds-conditional-section');
309
+ expect(section.hidden).toBe(false);
310
+ const cb = container.querySelector('#HideSalary');
311
+ cb.checked = true;
312
+ emitInput(cb);
313
+ expect(section.hidden).toBe(true);
314
+ });
315
+
316
+ it('single checkbox with custom value and show-when toggles visibility', () => {
317
+ ({ container } = mountScenario({
318
+ fieldName: 'newsletter',
319
+ showWhen: 'weekly',
320
+ inputsHtml: '<input type="checkbox" name="newsletter" id="newsletter" value="weekly" />',
321
+ }));
322
+ const section = container.querySelector('mds-conditional-section');
323
+ const cb = container.querySelector('#newsletter');
324
+ expect(section.hidden).toBe(true);
325
+ cb.checked = true;
326
+ emitInput(cb);
327
+ expect(section.hidden).toBe(false);
328
+ });
329
+
330
+ it('radio group show-when matches selected value', () => {
331
+ ({ container } = mountScenario({
332
+ fieldName: 'option',
333
+ showWhen: 'Broccoli',
334
+ inputsHtml: `
335
+ <input type="radio" name="option" value="Donkey" id="r1" />
336
+ <input type="radio" name="option" value="Broccoli" id="r2" />
337
+ `,
338
+ }));
339
+ const section = container.querySelector('mds-conditional-section');
340
+ expect(section.hidden).toBe(true);
341
+ container.querySelector('#r2').checked = true;
342
+ emitInput(container.querySelector('#r2'));
343
+ expect(section.hidden).toBe(false);
344
+ });
345
+
346
+ it('select show-when matches option value', () => {
347
+ ({ container } = mountScenario({
348
+ fieldName: 'pets',
349
+ showWhen: 'cat',
350
+ inputsHtml: `
351
+ <select name="pets" id="pets">
352
+ <option value="">Choose</option>
353
+ <option value="dog">Dog</option>
354
+ <option value="cat">Cat</option>
355
+ </select>
356
+ `,
357
+ }));
358
+ const section = container.querySelector('mds-conditional-section');
359
+ const sel = container.querySelector('#pets');
360
+ sel.value = 'cat';
361
+ emitInput(sel);
362
+ expect(section.hidden).toBe(false);
363
+ });
364
+
365
+ it('multiple select show-when matches any selected option (OR)', () => {
366
+ ({ container } = mountScenario({
367
+ fieldName: 'skills',
368
+ showWhen: 'js,css',
369
+ inputsHtml: `
370
+ <label for="skills">Pick your skills (hold Ctrl/Cmd to select several):</label>
371
+ <select name="skills" id="skills" multiple size="3">
372
+ <option value="js">JavaScript</option>
373
+ <option value="css">CSS</option>
374
+ <option value="html">HTML</option>
375
+ </select>
376
+ `,
377
+ }));
378
+ const section = container.querySelector('mds-conditional-section');
379
+ const sel = container.querySelector('#skills');
380
+ expect(section.hidden).toBe(true);
381
+
382
+ sel.options[1].selected = true;
383
+ emitInput(sel);
384
+ expect(section.hidden).toBe(false);
385
+ });
386
+ });
387
+
388
+ describe('missing show-when', () => {
389
+ it('keeps section hidden when show-when attribute is omitted', () => {
390
+ ({ container } = mountScenario({
391
+ fieldName: 'interests',
392
+ showWhen: '',
393
+ includeShowWhen: false,
394
+ inputsHtml: `
395
+ <input type="checkbox" name="interests" value="sports" checked />
396
+ `,
397
+ }));
398
+ const section = container.querySelector('mds-conditional-section');
399
+ expect(section.hidden).toBe(true);
400
+ });
401
+ });
402
+ });
@@ -2,34 +2,34 @@
2
2
 
3
3
  This component provides autocomplete search functionality to select an option from a list of options.
4
4
 
5
- Options can be provided via the `options` parameter, or obtained from an API using the `apiUrl` parameter.
6
-
7
- `value` is an Object or Array of Objects representing the selected option or options.
5
+ Options can be provided via `<option value="value">label</option>` children, or obtained from an API using the `apiUrl` parameter.
8
6
 
9
7
  ## Parameters - Nunjucks
10
8
 
11
9
  - `id`: the id of your combobox **required**
12
10
  - `name`: the name of the input for form submission. Uses ID unless specified - **recommended**
13
11
  - `labelText`: the text used in the label **required**
14
- - `value`: the populated option `object` ( `{ label: 'Orange', value: 45 }`) or `array` of option `objects` ( `[{ label: 'Orange', value: 45 }, { label: 'Green', value: 33 }]`)
15
12
  - `searchText`: _Not_ the value, but the current text inside the search input.
16
- - `options`: an `array`, e.g `[{ label: 'Orange', value: 45 }]`. Populates both mds-combobox, and fallback select options
17
- - `apiUrl`: when populated, `options` is ignored and data is fetched from an API URL instead
13
+ - `apiUrl`: when populated, options data is fetched from an API URL
18
14
  - `apiQueryKey`: the query parameter name added to `apiUrl` - (defaults to 'searchText')
19
15
  - `apiOptionsPath`: where to grab an array of options on api response, e.g. `data.options` would be an array of options. leave undefined to use root api response as array
20
- - `optionLabelPath`: where to grab the visual label from the option object e.g. 'label' or 'title' or 'nested.object.label' (defaults to `label`)
21
- - `optionValuePath`: where to grab the value from the option object e.g. `value` or `nested.object.value` (defaults to `value`)
16
+ - `apiOptionLabelPath`: relative to options object returned from API response, e.g. `label` or `title` or `nested.object.label` (defaults to `label`)
17
+ - `apiOptionValuePath`: relative to options object returned from API response, e.g. `value` or `score` or `nested.object.value` (defaults to `value`)
22
18
  - `multiple`: Boolean, whether to treat `value` input and output as an Array, or a singular. Also to display pills. default `false`
23
19
  - `fallbackTo`: the form element to use as a fallback. Should be either 'select' or 'input' **recommended** (see notes underneath)
24
- - `placeholder`: the placeholder for your input (defaults to 'Please select') **recommended**
20
+ - `fallbackValue`: only when `fallbackTo` is `input`, the `value` attribute of the fallback `input` element
21
+ - `placeholder`: the placeholder for your input **recommended**
25
22
  - `classes`: add extra classes to the trigger
26
23
  - `helpText`: Helper text to display under the label
27
24
  - `tooltipMessage`: Toggles a tooltip with this message to appear on the input
28
25
  - `validationError`: The error message provided by validation
29
26
  - `state`: The current state of the input, currently the only allowed value is `error`
30
- - `type`: applied as data-type` attribute, the name of the options api e.g "location-lookup"
27
+ - `type`: applied as `data-type` attribute, the name of the options api e.g "location-lookup"
31
28
  - `i18n`: an `object`, Text to translate/customise
32
29
  - `minSearchCharacters`: The minimum number of characters inside the input before a search is performed to avoid low specificity searches. This should be matched by your implementation's search handler.
30
+ - `attributes`: an `object`, attribute key/values applied directly to `mds-combobox` element
31
+ - `hideLabel` — Boolean, visually hides the label.
32
+ - `optional` - Boolean, marks field as optional in the label only
33
33
 
34
34
  ```
35
35
  i18n: {
@@ -48,13 +48,7 @@ i18n: {
48
48
 
49
49
  ## Usage
50
50
 
51
- MdsCombobox usage revolves around either options provided by `params.options` or an API (`apiUrl` in use), and being in `multiple` mode via `params.multiple`.
52
- If `multiple` mode is true,`params.value` must be undefined or an `array`, otherwise undefined or an `object`.
53
-
54
- The params `value`, `options`, `optionLabelPath` and `optionValuePath` are tightly coupled and must be compatible with each other:
55
-
56
- - `options`: an array of objects, each object has the property stated by `optionLabelPath` and `optionValuePath`
57
- - `value`: an object ( or array of objects if `multiple:true`), object is the same shape as the ones in `options`, each object has the property stated by `optionLabelPath` and `optionValuePath`
51
+ MdsCombobox usage revolves around options provided by `<option value="">label</option>` child elements as available options and the initial value comes from `<option selected>` children, and optionally an API (`apiUrl` in use), and being in `multiple` mode via `params.multiple`.
58
52
 
59
53
  ## Fallback
60
54
 
@@ -63,65 +57,52 @@ When JavaScript is unavailable, the combobox will gracefully degrade to a native
63
57
  Both fallback elements share the same `id` as the combobox to ensure the label remains properly associated. However, the fallback's `name` attribute will be `[name]_fallback` to distinguish it from the JavaScript-enabled version. This allows your server-side code to detect when the fallback was used and handle the form submission appropriately.
64
58
 
65
59
  **Choosing a fallback type:**
60
+
66
61
  - Use `fallbackTo: 'select'` when you have a static, predefined list of options that work well in a standard dropdown.
67
62
  - Use `fallbackTo: 'input'` when options are dynamic (from an API) or when you prefer a text input. With a text input fallback, users can type and submit values that your server can then process (e.g., look up a location ID from a location name).
63
+ - Use `fallbackValue` when using `fallbackTo: 'input'` to populate the fallback's value
68
64
 
69
- ### Example - Single mode - no API - default option object shape
70
-
71
- As our options object uses the default property names, we don't need to specify them.
72
-
73
- ```njk
74
- {\% call MdsCombobox({
75
- id:'my-id',
76
- name: 'my-combo',
77
- labelText:'My Combo',
78
- options: [{label: 'my-label-1', value: 'value-1'},{label: 'my-label-2', value: 'value-2'} ]
79
- }) \%}
80
- {\% endcall \%}
81
-
82
- ```
83
-
84
- #### with prefilled value
65
+ ### Example - Single mode - no API - no option selected
85
66
 
86
67
  ```njk
87
68
  {\% call MdsCombobox({
88
69
  id:'my-id',
89
70
  name: 'my-combo',
90
71
  labelText:'My Combo',
91
- value: {value: 'value-2', label: 'my-label-2'},
92
- options: [{label: 'my-label-1', value: 'value-1'},{label: 'my-label-2', value: 'value-2'} ]
93
72
  }) \%}
73
+ <option value="value-1">My label 1</option>
74
+ <option value="value-2">My label 2</option>
94
75
  {\% endcall \%}
95
76
 
96
77
  ```
97
78
 
98
- ### Example - Multiple mode - no API - default option object shape
79
+ #### with pre-selected value
99
80
 
100
81
  ```njk
101
82
  {\% call MdsCombobox({
102
83
  id:'my-id',
103
84
  name: 'my-combo',
104
85
  labelText:'My Combo',
105
- options: [{label: 'my-label-1', value: 'value-1'},{label: 'my-label-2', value: 'value-2'} ],
106
- multiple: true
86
+ fallbackTo:'input',
87
+ fallbackValue:'value-2'
107
88
  }) \%}
89
+ <option value="value-1">My label 1</option>
90
+ <option value="value-2" selected>My label 2</option>
108
91
  {\% endcall \%}
109
92
 
110
93
  ```
111
94
 
112
- #### with prefilled value
113
-
114
- Note the value should be an array when using multiple mode.
95
+ ### Example - Multiple mode - no API
115
96
 
116
97
  ```njk
117
98
  {\% call MdsCombobox({
118
99
  id:'my-id',
119
100
  name: 'my-combo',
120
101
  labelText:'My Combo',
121
- value: [{value: 'value-2', label: 'my-label-2'}],
122
- options: [{label: 'my-label-1', value: 'value-1'},{label: 'my-label-2', value: 'value-2'} ],
123
102
  multiple: true
124
103
  }) \%}
104
+ <option value="value-1">My label 1</option>
105
+ <option value="value-2">My label 2</option>
125
106
  {\% endcall \%}
126
107
 
127
108
  ```
@@ -138,8 +119,8 @@ like so `[{word: 'something', score: 12},...]`. It also used `?keyword` for the
138
119
  labelText:'My Combo',
139
120
  apiUrl: '/api/location-lookup',
140
121
  apiQueryKey: 'keyword',
141
- optionLabelPath: 'word',
142
- optionValuePath: 'score'
122
+ apiOptionLabelPath: 'word',
123
+ apiOptionValuePath: 'score'
143
124
  }) \%}
144
125
  {\% endcall \%}
145
126
 
@@ -152,12 +133,13 @@ like so `[{word: 'something', score: 12},...]`. It also used `?keyword` for the
152
133
  id:'my-id',
153
134
  name: 'my-combo',
154
135
  labelText:'My Combo',
155
- value: {word: 'my-label-2', score: 'value-2'},
156
136
  apiUrl: '/api/location-lookup',
157
137
  apiQueryKey: 'keyword',
158
- optionLabelPath: 'word',
159
- optionValuePath: 'score'
138
+ apiOptionLabelPath: 'word',
139
+ apiOptionValuePath: 'score'
160
140
  }) \%}
141
+ {# we can render selected options on the server even when using API #}
142
+ <option value="value-2" selected>My label 2</option>
161
143
  {\% endcall \%}
162
144
 
163
145
  ```
@@ -183,18 +165,18 @@ options:
183
165
 
184
166
  #### with prefilled value
185
167
 
186
- Note the value is an array for multiple mode.
187
-
188
168
  ```njk
189
169
  {\% call MdsCombobox({
190
170
  id:'my-id',
191
171
  name: 'my-combo',
192
172
  labelText:'My Combo',
193
- value: [{label: 'my-label-1', value: 'value-1'},{label: 'my-label-2', value: 'value-2'} ],
194
173
  apiUrl: '/api/location-lookup',
195
174
  apiOptionsPath: 'data',
196
175
  multiple: true
197
176
  }) \%}
177
+ {# we can render selected options on the server even when using API #}
178
+ <option value="value-1" selected>My label 1</option>
179
+ <option value="value-2" selected>My label 2</option>
198
180
  {\% endcall \%}
199
181
 
200
182
  ```
@@ -208,3 +190,14 @@ When Javascript is not available, either a native combobox or native input will
208
190
  aria-describedBy has been added to notify screen reader users how to interact with the autocomplete suggestions.
209
191
 
210
192
  Note: There is a known issue with the `aria-activedescendant` attribute when using VoiceOver + Safari (it works on other browsers). VO doesn't read the current option. See https://bugs.webkit.org/show_bug.cgi?id=231724
193
+
194
+ ## Vue only usage - `<mds-combobox/>`
195
+
196
+ ### Props
197
+
198
+ - `value` : `{Array|Object}`, the selected option(s)
199
+ - `options`: Alterative to child Web Component's `<option/>` elements
200
+
201
+ ### Events
202
+
203
+ - `update:value` & `change` : on value change