@madgex/design-system 11.0.0 → 12.0.0
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.
- package/README.md +3 -0
- package/dist/js/index-fractal.js +1 -1
- package/package.json +1 -1
- package/src/components/inputs/combobox/README.md +28 -6
- package/src/components/inputs/combobox/_template.njk +42 -31
- package/src/components/inputs/combobox/combobox.config.js +14 -2
- package/src/components/inputs/combobox/combobox.njk +21 -11
- package/src/js/index-fractal.js +0 -4
- package/src/js/fractal-scripts/combobox.js +0 -88
package/README.md
CHANGED
package/dist/js/index-fractal.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";const e=e=>{e.classList.remove("mds-switch-state--default"),e.classList.add("mds-switch-state--inverse")},t=e=>{e.classList.remove("mds-switch-state--inverse"),e.classList.add("mds-switch-state--default")},
|
|
1
|
+
(()=>{"use strict";const e=e=>{e.classList.remove("mds-switch-state--default"),e.classList.add("mds-switch-state--inverse")},t=e=>{e.classList.remove("mds-switch-state--inverse"),e.classList.add("mds-switch-state--default")},s=()=>{Array.from(document.querySelectorAll(".js-mds-switch-state")).forEach(s=>{s.addEventListener("click",s=>{s.preventDefault(),s.stopPropagation();const a=s.currentTarget;a.classList.contains("mds-switch-state--default")?e(a):(a.classList.contains("mds-switch-state--inverse"),t(a))})})},a="mds-notification",i={init:(e,t,s=3e3)=>{const{body:r}=document;i.hide(r);const n=document.createElement("div");n.setAttribute("role","alert"),n.classList.add(a,"mds-message",`mds-message--${t}`),n.innerText=e,i.show(r,n),setTimeout(i.hide,s,r)},show:(e,t)=>{e.appendChild(t)},hide:e=>{Array.from(e.querySelectorAll(`.${a}`)).forEach(e=>{e.parentNode.removeChild(e)})}},r=i,n=()=>{Array.from(document.querySelectorAll(".js-notification-example")).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation();const t=e.currentTarget,s=t.innerText,{messageType:a}=t.dataset;r.init(s,a)})})};document.addEventListener("DOMContentLoaded",()=>{s(),n()})})();
|
package/package.json
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
- `id`: the id of your combobox
|
|
4
4
|
- `name`: the name of the input for form submission. Uses ID unless specified - _optional_
|
|
5
5
|
- `labelText`: the text used in the label
|
|
6
|
-
- `options`: a
|
|
6
|
+
- `options`: a JSON array, e.g `[{ label: 'Orange', value: 45 }]`. Populates both mds-combobox, and fallback select options
|
|
7
|
+
- `apiUrl`: when populated, `options` is ignored and data is fetching from an API URL instead
|
|
8
|
+
- `apiQueryKey`: the query paramter name added to `apiUrl` - _optional_ (defaults to 'searchText')
|
|
9
|
+
- `apiOptionsPath`: where to grab an array of options on api response, e.g. `data.options` would be an array of options. _optional_ . leave undefined to use root api response as array
|
|
10
|
+
- `optionLabelPath`: where to grab the visual label propertly from the option object e.g. 'label' or 'title' or 'nested.object.label' _optional_ (defaults to `label`)
|
|
7
11
|
- `value`: a default selected value for the input - _optional_
|
|
8
12
|
- `fallbackTo`: the form element to use as a fallback. Should be either 'select' or 'input'
|
|
9
13
|
- `placeholder`: the placeholder for your input (defaults to 'Please select')
|
|
@@ -29,13 +33,31 @@ i18n: {
|
|
|
29
33
|
}
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
##
|
|
36
|
+
## Updating hidden input values
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
When a user selects an option, any inputs passed inside MdsCombobox with the data attribute `data-key` will be populated with values from that chosen option.
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
### Example 1
|
|
41
|
+
|
|
42
|
+
Populate the hidden input with the `value` property from the selected option `{label:"my label", value: 56}`.
|
|
43
|
+
|
|
44
|
+
```njk
|
|
45
|
+
{\% call MdsCombobox({...}) \%}
|
|
46
|
+
<input type="hidden" data-key="value" /> <!-- value will be 56 -->
|
|
47
|
+
{\% endcall \%}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Example 2
|
|
51
|
+
|
|
52
|
+
Populate the hidden input with the `nested.thing` property from the selected option `{label:"my label", nested: {thing: 989 }, lat: "56.202", lon: '0.142'}`.
|
|
53
|
+
|
|
54
|
+
```njk
|
|
55
|
+
{\% call MdsCombobox({...}) \%}
|
|
56
|
+
<input type="hidden" data-key="nested.thing" /> <!-- value will be 989 -->
|
|
57
|
+
<input type="hidden" data-key="lat" /> <!-- value will be 56.202 -->
|
|
58
|
+
<input type="hidden" data-key="lon" /> <!-- value will be 0.142 -->
|
|
59
|
+
{\% endcall \%}
|
|
60
|
+
```
|
|
39
61
|
|
|
40
62
|
## Accessibility
|
|
41
63
|
|
|
@@ -52,47 +52,58 @@
|
|
|
52
52
|
validationErrorId: validationErrorId,
|
|
53
53
|
validationError: params.validationError
|
|
54
54
|
}) }}
|
|
55
|
-
<div class="mds-form-element__fallback">
|
|
56
|
-
{% if params.fallbackTo === 'select' and params.options %}
|
|
57
|
-
<select
|
|
58
|
-
class="mds-form-control"
|
|
59
|
-
id="{{ comboboxId }}"
|
|
60
|
-
name="{{ comboboxName }}"
|
|
61
|
-
value="{{ params.defaultValue|default('') }}"
|
|
62
|
-
{% if ariaDescribedBy %}aria-describedby="{{ariaDescribedBy}}"{% endif %}
|
|
63
|
-
>
|
|
64
|
-
<option>{{ placeholder }}</option>
|
|
65
|
-
{%- if params.options -%}
|
|
66
|
-
{%- for value, option in params.options -%}
|
|
67
|
-
<option value="{{ value }}">{{ option }}</option>
|
|
68
|
-
{%- endfor -%}
|
|
69
|
-
{%- endif -%}
|
|
70
|
-
</select>
|
|
71
|
-
{% elseif params.fallbackTo === 'input' %}
|
|
72
|
-
<input
|
|
73
|
-
class="mds-form-control"
|
|
74
|
-
type="text"
|
|
75
|
-
name="{{ comboboxName }}"
|
|
76
|
-
autocomplete="off"
|
|
77
|
-
id="{{ comboboxId }}"
|
|
78
|
-
value="{{ params.value|default('') }}"
|
|
79
|
-
placeholder="{{ placeholder }}"
|
|
80
|
-
{% if params.validationError %}aria-invalid="true"{% endif %}
|
|
81
|
-
{% if ariaDescribedBy %}aria-describedby="{{ariaDescribedBy}}"{% endif %}
|
|
82
|
-
/>
|
|
83
|
-
{% endif %}
|
|
84
|
-
</div>
|
|
85
55
|
{# Leave the custom element at the bottom so it has access to the above elements on render #}
|
|
86
56
|
<mds-combobox
|
|
87
57
|
comboboxid="{{ comboboxId }}"
|
|
88
58
|
placeholder="{{ params.placeholder }}"
|
|
89
59
|
iconpath="{{ defaultIconPath }}"
|
|
60
|
+
{% if params.options.length %}options="{{params.options | dump}}"{% endif %}
|
|
61
|
+
{% if params.apiUrl %}api-url="{{params.apiUrl}}"{% endif%}
|
|
62
|
+
{% if params.apiQueryKey %}api-query-key="{{params.apiQueryKey}}"{% endif%}
|
|
63
|
+
{% if params.apiOptionsPath %}api-options-path="{{params.apiOptionsPath}}"{% endif%}
|
|
64
|
+
{% if params.optionLabelPath %}option-label-path="{{params.optionLabelPath}}"{% endif%}
|
|
90
65
|
i18n="{{ params.i18n | dump }}"
|
|
91
66
|
data-aria-invalid="{{ params.validationError }}"
|
|
92
67
|
{% if params.minSearchCharacters %}min-search-characters="{{ params.minSearchCharacters }}"{% endif %}
|
|
93
68
|
{% if params.fallbackTo === 'input' %}name="{{ comboboxName }}"{% endif %}
|
|
94
69
|
{% if params.value %}value="{{ params.value }}"{% endif %}
|
|
95
70
|
{% if ariaDescribedBy %} describedby-id="{{ariaDescribedBy}}"{% endif -%}
|
|
96
|
-
|
|
71
|
+
>
|
|
72
|
+
<div class="mds-form-element__fallback">
|
|
73
|
+
{% if params.fallbackTo === 'select' and params.options %}
|
|
74
|
+
<select
|
|
75
|
+
class="mds-form-control"
|
|
76
|
+
id="{{ comboboxId }}"
|
|
77
|
+
name="{{ comboboxName }}"
|
|
78
|
+
value="{{ params.defaultValue|default('') }}"
|
|
79
|
+
{% if ariaDescribedBy %}aria-describedby="{{ariaDescribedBy}}"{% endif %}
|
|
80
|
+
>
|
|
81
|
+
<option>{{ placeholder }}</option>
|
|
82
|
+
{%- if params.options -%}
|
|
83
|
+
{%- for option in params.options -%}
|
|
84
|
+
<option value="{{ option.value }}">{{ option.label }}</option>
|
|
85
|
+
{%- endfor -%}
|
|
86
|
+
{%- endif -%}
|
|
87
|
+
</select>
|
|
88
|
+
{% elseif params.fallbackTo === 'input' %}
|
|
89
|
+
<input
|
|
90
|
+
class="mds-form-control"
|
|
91
|
+
type="text"
|
|
92
|
+
name="{{ comboboxName }}"
|
|
93
|
+
autocomplete="off"
|
|
94
|
+
id="{{ comboboxId }}"
|
|
95
|
+
value="{{ params.value|default('') }}"
|
|
96
|
+
placeholder="{{ placeholder }}"
|
|
97
|
+
{% if params.validationError %}aria-invalid="true"{% endif %}
|
|
98
|
+
{% if ariaDescribedBy %}aria-describedby="{{ariaDescribedBy}}"{% endif %}
|
|
99
|
+
/>
|
|
100
|
+
{% endif %}
|
|
101
|
+
</div>
|
|
102
|
+
{% if caller %}
|
|
103
|
+
<span slot="target-inputs">
|
|
104
|
+
{{- caller() -}}
|
|
105
|
+
</span>
|
|
106
|
+
{% endif %}
|
|
107
|
+
</mds-combobox>
|
|
97
108
|
</div>
|
|
98
109
|
{%- endif -%}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
const optionsSalary =
|
|
2
|
-
|
|
1
|
+
const optionsSalary = new Array(20)
|
|
2
|
+
.fill()
|
|
3
|
+
.map((_, index) => ({ label: `Up to £${10 + index * 10}k`, value: 10 + index * 10 }));
|
|
4
|
+
const optionsDistance = new Array(20)
|
|
5
|
+
.fill()
|
|
6
|
+
.map((_, index) => ({ label: `Within ${5 * (1 + index)} miles`, value: 5 * (1 + index) }));
|
|
3
7
|
|
|
4
8
|
module.exports = {
|
|
5
9
|
title: 'Combobox',
|
|
@@ -58,6 +62,10 @@ module.exports = {
|
|
|
58
62
|
placeholder: 'eg. Web developer',
|
|
59
63
|
fallbackTo: 'input',
|
|
60
64
|
minSearchCharacters: 3,
|
|
65
|
+
apiUrl: 'https://api.datamuse.com/sug',
|
|
66
|
+
apiQueryKey: 's',
|
|
67
|
+
apiOptionsPath: undefined,
|
|
68
|
+
optionLabelPath: 'word',
|
|
61
69
|
i18n: {
|
|
62
70
|
requiredIcon: 'Required (test i18n)',
|
|
63
71
|
loadingText: 'Loading (test i18n)',
|
|
@@ -82,6 +90,10 @@ module.exports = {
|
|
|
82
90
|
vModel: 'Initial Value',
|
|
83
91
|
fallbackTo: 'input',
|
|
84
92
|
minSearchCharacters: 3,
|
|
93
|
+
apiUrl: 'https://api.datamuse.com/sug',
|
|
94
|
+
apiQueryKey: 's',
|
|
95
|
+
apiOptionsPath: undefined,
|
|
96
|
+
optionLabelPath: 'word',
|
|
85
97
|
},
|
|
86
98
|
},
|
|
87
99
|
{
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{% from "./inputs/combobox/_macro.njk" import MdsCombobox %}
|
|
2
2
|
|
|
3
3
|
{% if not multiple %}
|
|
4
|
-
|
|
5
4
|
<div class="mds-grid-row">
|
|
6
5
|
<div class="mds-grid-col-6">
|
|
7
6
|
<h3>{{ variantTitle }}</h3>
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
{% call MdsCombobox({
|
|
9
9
|
id: id,
|
|
10
10
|
name: name,
|
|
11
11
|
labelText: labelText,
|
|
@@ -19,17 +19,27 @@
|
|
|
19
19
|
validationError: validationError,
|
|
20
20
|
helpText: helpText,
|
|
21
21
|
i18n: i18n,
|
|
22
|
-
minSearchCharacters: minSearchCharacters
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
minSearchCharacters: minSearchCharacters,
|
|
23
|
+
apiUrl: apiUrl,
|
|
24
|
+
apiQueryKey: apiQueryKey,
|
|
25
|
+
apiOptionsPath: apiOptionsPath,
|
|
26
|
+
optionLabelPath: optionLabelPath
|
|
27
|
+
}) %}
|
|
28
|
+
{#
|
|
29
|
+
The below demonstrates how a target input value can be set on option selection, or on clear of combobox
|
|
30
|
+
#}
|
|
31
|
+
{% if id === 'salary-selection' %}
|
|
32
|
+
<input disabled data-key="value" placeholder="normally hidden target" style="position:absolute; left: 100%; top: 0;" />
|
|
33
|
+
{% endif %}
|
|
34
|
+
{% if id === 'keywords-lookup' or id === 'keywords-lookup-prefilled' %}
|
|
35
|
+
<input disabled data-key="word" placeholder="normally hidden target" style="position:absolute; left: 100%; top: 0;" />
|
|
36
|
+
{% endif %}
|
|
37
|
+
{% endcall %}
|
|
38
|
+
|
|
29
39
|
{% if id === 'salary-selection' %}
|
|
30
|
-
<p class="mds-margin-top-b3">A hidden input is cleared when
|
|
31
|
-
<input type="hidden" id="salary-selection-hidden-input" />
|
|
40
|
+
<p class="mds-margin-top-b3">A hidden input is cleared when combobox is cleared.
|
|
32
41
|
{% endif %}
|
|
42
|
+
|
|
33
43
|
<br><br>
|
|
34
44
|
</div>
|
|
35
45
|
</div>
|
package/src/js/index-fractal.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import switchStateScript from './fractal-scripts/switch-state';
|
|
2
2
|
import notificationScript from './fractal-scripts/notification';
|
|
3
|
-
import comboboxScript from './fractal-scripts/combobox';
|
|
4
3
|
|
|
5
4
|
document.addEventListener('DOMContentLoaded', () => {
|
|
6
5
|
switchStateScript.init();
|
|
7
6
|
notificationScript.init();
|
|
8
|
-
setTimeout(() => {
|
|
9
|
-
comboboxScript.init();
|
|
10
|
-
}, 1000);
|
|
11
7
|
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
const elementName = 'mds-combobox';
|
|
2
|
-
|
|
3
|
-
function bindToLocationSelect() {
|
|
4
|
-
const el = document.querySelector(`[data-combobox-id="distance-selection"]`);
|
|
5
|
-
|
|
6
|
-
if (el) {
|
|
7
|
-
const selectInput = el.querySelector('select');
|
|
8
|
-
const options = Array.from(selectInput.querySelectorAll('option'));
|
|
9
|
-
const vueSelect = el.querySelector(elementName);
|
|
10
|
-
|
|
11
|
-
vueSelect.options = options.slice(1).map((opt) => ({ value: opt.value, label: opt.textContent }));
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function bindToSalarySelect() {
|
|
16
|
-
const el = document.querySelector(`[data-combobox-id="salary-selection"]`);
|
|
17
|
-
|
|
18
|
-
if (el) {
|
|
19
|
-
const selectInput = el.querySelector('select');
|
|
20
|
-
const options = Array.from(selectInput.querySelectorAll('option'));
|
|
21
|
-
const vueSelect = el.querySelector(elementName);
|
|
22
|
-
|
|
23
|
-
vueSelect.options = options.slice(1).map((opt) => ({ value: opt.value, label: opt.textContent }));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function setSalaryHiddenField() {
|
|
28
|
-
const comboBoxEl = document.querySelector(`[data-combobox-id="salary-selection"] mds-combobox`);
|
|
29
|
-
const hiddenInputEl = document.querySelector('#salary-selection-hidden-input');
|
|
30
|
-
|
|
31
|
-
if (comboBoxEl && hiddenInputEl) {
|
|
32
|
-
comboBoxEl.addEventListener('select-option', () => {
|
|
33
|
-
console.log('Setting #salary-selection-hidden-input..');
|
|
34
|
-
hiddenInputEl.value = hiddenInputEl.value === 'flip' ? 'flop' : 'flip';
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function clearSalaryHiddenField() {
|
|
40
|
-
const comboBoxEl = document.querySelector(`[data-combobox-id="salary-selection"] mds-combobox`);
|
|
41
|
-
const hiddenInputEl = document.querySelector('#salary-selection-hidden-input');
|
|
42
|
-
|
|
43
|
-
if (comboBoxEl && hiddenInputEl) {
|
|
44
|
-
comboBoxEl.addEventListener('clear-all', () => {
|
|
45
|
-
console.log('Clearing #salary-selection-hidden-input..');
|
|
46
|
-
hiddenInputEl.value = '';
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function bindToApi() {
|
|
52
|
-
const el = document.querySelector(`[data-combobox-id="keywords-lookup"]`);
|
|
53
|
-
|
|
54
|
-
if (el) {
|
|
55
|
-
const vueSelect = el.querySelector(elementName);
|
|
56
|
-
|
|
57
|
-
vueSelect.filterOptions = false;
|
|
58
|
-
vueSelect.addEventListener('search', (event) => {
|
|
59
|
-
const [searchValue] = event.detail;
|
|
60
|
-
|
|
61
|
-
if (searchValue && searchValue.length > 2) {
|
|
62
|
-
fetch(`https://api.datamuse.com/sug?s=${searchValue}`)
|
|
63
|
-
.then((res) => res.json())
|
|
64
|
-
.then((data) => {
|
|
65
|
-
const options = data.map(({ word }) => ({ value: word, label: word }));
|
|
66
|
-
|
|
67
|
-
vueSelect.options = options;
|
|
68
|
-
return options;
|
|
69
|
-
})
|
|
70
|
-
.catch(console.log);
|
|
71
|
-
} else {
|
|
72
|
-
vueSelect.options = [];
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const combobox = {
|
|
79
|
-
init: () => {
|
|
80
|
-
bindToLocationSelect();
|
|
81
|
-
bindToSalarySelect();
|
|
82
|
-
setSalaryHiddenField();
|
|
83
|
-
clearSalaryHiddenField();
|
|
84
|
-
bindToApi();
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export default combobox;
|