@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 CHANGED
@@ -137,3 +137,6 @@ await styleDictionary.buildPlatform('json-variables');
137
137
 
138
138
  await cleanTempFiles();
139
139
  ```
140
+
141
+ \_
142
+ \_
@@ -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")},o=()=>{Array.from(document.querySelectorAll(".js-mds-switch-state")).forEach(o=>{o.addEventListener("click",o=>{o.preventDefault(),o.stopPropagation();const s=o.currentTarget;s.classList.contains("mds-switch-state--default")?e(s):(s.classList.contains("mds-switch-state--inverse"),t(s))})})},s="mds-notification",n={init:(e,t,o=3e3)=>{const{body:c}=document;n.hide(c);const a=document.createElement("div");a.setAttribute("role","alert"),a.classList.add(s,"mds-message",`mds-message--${t}`),a.innerText=e,n.show(c,a),setTimeout(n.hide,o,c)},show:(e,t)=>{e.appendChild(t)},hide:e=>{Array.from(e.querySelectorAll(`.${s}`)).forEach(e=>{e.parentNode.removeChild(e)})}},c=n,a=()=>{Array.from(document.querySelectorAll(".js-notification-example")).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation();const t=e.currentTarget,o=t.innerText,{messageType:s}=t.dataset;c.init(o,s)})})},i="mds-combobox",l=()=>{!function(){const e=document.querySelector('[data-combobox-id="distance-selection"]');if(e){const t=e.querySelector("select"),o=Array.from(t.querySelectorAll("option"));e.querySelector(i).options=o.slice(1).map(e=>({value:e.value,label:e.textContent}))}}(),function(){const e=document.querySelector('[data-combobox-id="salary-selection"]');if(e){const t=e.querySelector("select"),o=Array.from(t.querySelectorAll("option"));e.querySelector(i).options=o.slice(1).map(e=>({value:e.value,label:e.textContent}))}}(),function(){const e=document.querySelector('[data-combobox-id="salary-selection"] mds-combobox'),t=document.querySelector("#salary-selection-hidden-input");e&&t&&e.addEventListener("select-option",()=>{console.log("Setting #salary-selection-hidden-input.."),t.value="flip"===t.value?"flop":"flip"})}(),function(){const e=document.querySelector('[data-combobox-id="salary-selection"] mds-combobox'),t=document.querySelector("#salary-selection-hidden-input");e&&t&&e.addEventListener("clear-all",()=>{console.log("Clearing #salary-selection-hidden-input.."),t.value=""})}(),function(){const e=document.querySelector('[data-combobox-id="keywords-lookup"]');if(e){const t=e.querySelector(i);t.filterOptions=!1,t.addEventListener("search",e=>{const[o]=e.detail;o&&o.length>2?fetch(`https://api.datamuse.com/sug?s=${o}`).then(e=>e.json()).then(e=>{const o=e.map(({word:e})=>({value:e,label:e}));return t.options=o,o}).catch(console.log):t.options=[]})}}()};document.addEventListener("DOMContentLoaded",()=>{o(),a(),setTimeout(()=>{l()},1e3)})})();
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
@@ -2,7 +2,7 @@
2
2
  "name": "@madgex/design-system",
3
3
  "author": "Madgex",
4
4
  "license": "UNLICENSED",
5
- "version": "11.0.0",
5
+ "version": "12.0.0",
6
6
  "main": "dist/js/index.js",
7
7
  "exports": {
8
8
  ".": "./dist/js/index.js",
@@ -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 json object of key, value pairs e.g { 45: 'Orange' }. To be used when falling back to a native select if Javascript is not available
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
- ## Events
36
+ ## Updating hidden input values
33
37
 
34
- The following events are emiited.
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
- - `select-option`
37
- - `search`
38
- - `clear-all`
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
- ></mds-combobox>
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 = { ...new Array(20).fill().map((_, index) => `Up to £${10 + index * 10}k`) };
2
- const optionsDistance = { ...new Array(20).fill().map((_, index) => `Within ${5 * (1 + index)} miles`) };
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
- {{ MdsCombobox({
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
- The below only applies to one combobox example.
26
- Demonstrates how the clear-all event (fired when combobox is cleared),
27
- can be used to clear hidden form fields.
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 clear-all event fires. Check <em>#salary-selection-hidden-input</em> in dev tools.</p>
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>
@@ -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;