@madgex/design-system 10.1.3 → 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/assets/icons.json +1 -1
- package/dist/js/index-fractal.js +1 -1
- package/package.json +1 -1
- package/src/components/button/README.md +1 -6
- package/src/components/button/_template.njk +8 -37
- package/src/components/button/button.config.js +8 -27
- package/src/components/button/button.njk +0 -27
- 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/components/inputs/file-upload/_template.njk +9 -9
- package/src/components/modal/_template.njk +1 -1
- package/src/components/modal/long-modal-content.njk +1 -1
- package/src/components/modal/modal-content.njk +1 -1
- package/src/components/section-title/02-title-with-action.njk +0 -1
- package/src/components/section-title/_template.njk +0 -1
- package/src/components/tabs/README.md +3 -1
- package/src/components/tabs/_template.njk +4 -3
- package/src/components/tabs/tabs.config.js +2 -2
- package/src/js/index-fractal.js +0 -4
- package/src/layout/containers/02-branded-containers.njk +0 -1
- package/src/sub-components/attributes/macro.njk +94 -0
- package/src/js/fractal-scripts/combobox.js +0 -88
package/README.md
CHANGED
package/dist/assets/icons.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"asterisk"},{"name":"calendar"},{"name":"cart"},{"name":"
|
|
1
|
+
[{"name":"asterisk"},{"name":"calendar"},{"name":"cart"},{"name":"check"},{"name":"chevron-down"},{"name":"chevron-left"},{"name":"chevron-right"},{"name":"chevron-up"},{"name":"close"},{"name":"cross"},{"name":"doc-pdf"},{"name":"doc"},{"name":"email"},{"name":"external"},{"name":"flag"},{"name":"information"},{"name":"job"},{"name":"list-bullets"},{"name":"list-numbers"},{"name":"location-pin"},{"name":"menu"},{"name":"minus"},{"name":"plus-small"},{"name":"plus"},{"name":"question-mark"},{"name":"redo"},{"name":"search"},{"name":"settings"},{"name":"share"},{"name":"social-facebook"},{"name":"social-linkedin"},{"name":"social-pinterest"},{"name":"social-reddit"},{"name":"social-twitter"},{"name":"spinner"},{"name":"star-fill"},{"name":"star-outline"},{"name":"text-bold"},{"name":"text-italic"},{"name":"text-link"},{"name":"text-strike-through"},{"name":"text-underline"},{"name":"triangle-down"},{"name":"triangle-right"},{"name":"triangle-up"},{"name":"undo"},{"name":"upload"},{"name":"user"}]
|
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
|
@@ -5,14 +5,9 @@ Use the [callable](https://mozilla.github.io/nunjucks/templating.html#call) synt
|
|
|
5
5
|
|
|
6
6
|
## Parameters
|
|
7
7
|
|
|
8
|
-
- `
|
|
9
|
-
- `name`: add `name` attribute
|
|
10
|
-
- `element`: we're using `<button>` by default but you can change to be a link or an input
|
|
11
|
-
- `href`: if `element` is a link then add the url using `href`
|
|
8
|
+
- `href`: this sets the href and changes the button to an anchor element
|
|
12
9
|
- `classes`: css classes to add on the button (for example `mds-width-full`)
|
|
13
|
-
- `disabled`: disable button
|
|
14
10
|
- `attributes`: you can add extra attributes by passing an object to the parameter. Example: `attributes: { attribute-name: 'attribute-value' }`
|
|
15
|
-
- `type`: by default it will be `submit`
|
|
16
11
|
|
|
17
12
|
## Full width button
|
|
18
13
|
|
|
@@ -1,40 +1,11 @@
|
|
|
1
|
-
{
|
|
1
|
+
{% from "../../sub-components/attributes/macro.njk" import MdsAttributes %}
|
|
2
2
|
|
|
3
|
-
{%- if params.
|
|
4
|
-
{
|
|
5
|
-
{
|
|
6
|
-
{% if params.href %}
|
|
7
|
-
{% set element = 'a' %}
|
|
8
|
-
{% else %}
|
|
9
|
-
{% set element = 'button' %}
|
|
10
|
-
{% endif %}
|
|
11
|
-
{%- endif -%}
|
|
12
|
-
|
|
13
|
-
{#- Define common attributes that we can use across all element types #}
|
|
14
|
-
|
|
15
|
-
{%- set commonAttributes %} class="mds-button
|
|
16
|
-
{%- if params.classes %} {{ params.classes }}{% endif -%}
|
|
17
|
-
{%- if params.disabled %} mds-button--disabled{% endif %}"
|
|
18
|
-
{%- for attribute, value in params.attributes %} {{attribute}}="{{value}}"{% endfor %} data-test="button{% if params.id %}-{{params.id}}{% endif %}"
|
|
19
|
-
{%- endset -%}
|
|
20
|
-
|
|
21
|
-
{#- Define common attributes we can use for both button and input types #}
|
|
22
|
-
|
|
23
|
-
{%- set buttonAttributes %}
|
|
24
|
-
{% if params.name %} name="{{ params.name }}"{% endif %} type="{{ params.type if params.type else 'submit' }}"{% if params.disabled %} disabled="disabled" aria-disabled="true"{% endif %}{% endset %}
|
|
25
|
-
|
|
26
|
-
{#- Actually create a button... or a link! #}
|
|
27
|
-
|
|
28
|
-
{%- if element == 'a' %}
|
|
29
|
-
<a href="{{ params.href if params.href else '#' }}" draggable="false" {{- commonAttributes | safe }}>
|
|
30
|
-
{{ caller() }}
|
|
3
|
+
{%- if params.href -%}
|
|
4
|
+
<a href="{{ params.href }}" class="mds-button {{ params.classes }}" {{- MdsAttributes(params.attributes) -}}>
|
|
5
|
+
{{- caller() -}}
|
|
31
6
|
</a>
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
{{ caller() }}
|
|
7
|
+
{%- else -%}
|
|
8
|
+
<button class="mds-button {{ params.classes }}" {{- MdsAttributes(params.attributes) -}}>
|
|
9
|
+
{{- caller() -}}
|
|
36
10
|
</button>
|
|
37
|
-
|
|
38
|
-
{%- elseif element == 'input' %}
|
|
39
|
-
<input value="{{ params.value }}" {{- buttonAttributes | safe }} {{- commonAttributes | safe }}>
|
|
40
|
-
{%- endif %}
|
|
11
|
+
{%- endif -%}
|
|
@@ -3,62 +3,43 @@ module.exports = {
|
|
|
3
3
|
status: 'wip',
|
|
4
4
|
context: {
|
|
5
5
|
items: [
|
|
6
|
-
{ name: 'link-buttons', href: 'http://madgex.com', text: 'Link button 1', id: 'link-1' },
|
|
7
6
|
{
|
|
8
|
-
name: 'link-buttons',
|
|
9
7
|
href: 'http://madgex.com',
|
|
10
|
-
text: 'Link
|
|
11
|
-
id: 'link-2',
|
|
12
|
-
classes: 'mds-button--plain',
|
|
8
|
+
text: 'Link button 1',
|
|
13
9
|
},
|
|
14
10
|
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
id: 'link-3',
|
|
11
|
+
href: 'http://madgex.com',
|
|
12
|
+
text: 'Link Plain button 2',
|
|
13
|
+
classes: 'mds-button--plain',
|
|
19
14
|
},
|
|
20
15
|
{
|
|
21
|
-
name: 'plain-buttons',
|
|
22
16
|
text: 'Plain button',
|
|
23
17
|
classes: 'mds-button--plain',
|
|
24
|
-
id: 'plain',
|
|
25
18
|
},
|
|
26
19
|
{
|
|
27
|
-
name: 'small-buttons',
|
|
28
20
|
text: 'Small button',
|
|
29
21
|
classes: 'mds-button--small',
|
|
30
|
-
id: 'small',
|
|
31
22
|
},
|
|
32
23
|
{
|
|
33
|
-
name: 'large-buttons',
|
|
34
24
|
text: 'Large button',
|
|
35
25
|
classes: 'mds-button--large',
|
|
36
|
-
id: 'large',
|
|
37
26
|
},
|
|
38
27
|
{
|
|
39
|
-
name: 'icon-buttons',
|
|
40
|
-
useIcon: true,
|
|
41
|
-
classes: 'mds-button--icon',
|
|
42
|
-
id: 'icon',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: 'full-width-example',
|
|
46
28
|
text: 'Full width example',
|
|
47
29
|
classes: 'mds-width-full mds-width-md-auto',
|
|
48
|
-
id: 'full',
|
|
49
30
|
},
|
|
50
31
|
{
|
|
51
|
-
name: 'neutral',
|
|
52
32
|
text: 'Neutral',
|
|
53
33
|
classes: 'mds-button--neutral',
|
|
54
|
-
id: 'neutral',
|
|
55
34
|
},
|
|
56
35
|
{
|
|
57
|
-
name: 'prevent double submit',
|
|
58
36
|
text: 'Prevent double submit',
|
|
59
|
-
id: 'double-submit',
|
|
60
37
|
classes: 'js-mds-button-double-submit',
|
|
61
38
|
},
|
|
39
|
+
{
|
|
40
|
+
text: 'With attributes',
|
|
41
|
+
attributes: { attributed: 'yes', 'other-thing': 'also' },
|
|
42
|
+
},
|
|
62
43
|
],
|
|
63
44
|
},
|
|
64
45
|
};
|
|
@@ -1,41 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
{% from "./button/_macro.njk" import MdsButton %}
|
|
3
|
-
{% from "./icons/_macro.njk" import MdsIcon %}
|
|
4
3
|
|
|
5
4
|
{% for item in items %}
|
|
6
|
-
|
|
7
|
-
{% if item.useIcon %}
|
|
8
|
-
{%- set html -%}
|
|
9
|
-
{{ MdsIcon({
|
|
10
|
-
iconName: 'search',
|
|
11
|
-
classes: 'mds-icon--md',
|
|
12
|
-
visuallyHiddenLabel: 'Search icon'
|
|
13
|
-
}) }}
|
|
14
|
-
{%- endset -%}
|
|
15
|
-
{% call MdsButton({
|
|
16
|
-
element: item.element,
|
|
17
|
-
id: item.id,
|
|
18
|
-
value: item.value,
|
|
19
|
-
href: item.href,
|
|
20
|
-
classes: item.classes,
|
|
21
|
-
attributes: item.attributes
|
|
22
|
-
}) -%}
|
|
23
|
-
<!-- example - in normal use `safe` is discouraged as it is dangerous, and not needed -->
|
|
24
|
-
{{ html | safe }}
|
|
25
|
-
{%- endcall %}
|
|
26
|
-
{% else %}
|
|
27
5
|
{% call MdsButton({
|
|
28
|
-
element: item.element,
|
|
29
|
-
id: item.id,
|
|
30
|
-
value: item.value,
|
|
31
6
|
href: item.href,
|
|
32
7
|
classes: item.classes,
|
|
33
8
|
attributes: item.attributes
|
|
34
9
|
}) -%}
|
|
35
10
|
{{ item.text }}
|
|
36
11
|
{%- endcall %}
|
|
37
|
-
{% endif %}
|
|
38
|
-
|
|
39
12
|
{% endfor %}
|
|
40
13
|
|
|
41
14
|
|
|
@@ -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>
|
|
@@ -80,19 +80,19 @@
|
|
|
80
80
|
<div class="mds-file-upload__selected-state">
|
|
81
81
|
<div class="mds-file-upload__file-name-container">
|
|
82
82
|
{{- MdsIcon({
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
iconName: 'check',
|
|
84
|
+
classes: 'mds-icon--md',
|
|
85
|
+
hasContainer: true,
|
|
86
|
+
containerClasses: 'mds-icon-container--circle mds-icon-container--success mds-icon-container--before'
|
|
87
|
+
})
|
|
88
|
+
-}}
|
|
89
89
|
<span class="mds-visually-hidden">{{ params.i18n.selectedFileText | default('Selected file:') }} </span>
|
|
90
90
|
<span class="mds-file-upload__file-name">{% if params.value %}{{ params.value }}{% endif %}</span>
|
|
91
91
|
</div>
|
|
92
92
|
{% call MdsButton({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
classes: 'mds-button--plain mds-button--small mds-file-upload__remove-button',
|
|
94
|
+
attributes: { type: 'button' }
|
|
95
|
+
}) -%}
|
|
96
96
|
{{- params.i18n.removeButtonText | default('Remove file') -}}
|
|
97
97
|
{{-
|
|
98
98
|
MdsIcon({
|
|
@@ -13,7 +13,7 @@ Donec dapibus consectetur fermentum. Proin vel lacus elit. Nunc eu massa magna.
|
|
|
13
13
|
Nullam faucibus pulvinar felis. Sed et tortor enim. Nulla luctus maximus mi aliquam tempor. Vivamus dignissim condimentum augue sed commodo. Donec a elementum risus, id interdum sem. Morbi malesuada tempor dapibus. Nam varius augue augue, vel pellentesque felis venenatis nec. Vivamus eget vulputate risus. Curabitur mollis, neque id tempus laoreet, nibh lectus aliquam felis, euismod varius mi turpis a sapien. Etiam ut mi a nisi faucibus egestas at eget ante.</p>
|
|
14
14
|
<p>{% call MdsButton({
|
|
15
15
|
classes: 'mds-button--neutral js-mds-modal-close',
|
|
16
|
-
type: 'button'
|
|
16
|
+
attributes: { type: 'button' }
|
|
17
17
|
}) -%}
|
|
18
18
|
Close
|
|
19
19
|
{%- endcall %}
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
- `classes`: add extra CSS classes to the component
|
|
5
5
|
- `allHeadersTag`: the html tag to use for the panel header (h2 as default)
|
|
6
6
|
- `allHeadersDisplay`: wheter the panel header should be always visible or not (default false)
|
|
7
|
-
- `content`: is an array of objects containing [label, selected, id, content, headerText, linkCustomAttr] for each tab, the content for the panel can also be a custom component, headerText is
|
|
7
|
+
- `content`: is an array of objects containing [label, selected, id, content, headerText, linkCustomAttr] for each tab, the content for the panel can also be a custom component, headerText is
|
|
8
|
+
text used for each panel header
|
|
9
|
+
- `content[{linkCustomAttr}]`: you can add extra attributes by passing an object to the parameter. Example: `attributes: { attribute-name: 'attribute-value' }`
|
|
8
10
|
|
|
9
11
|
## Variations
|
|
10
12
|
Tabs have different style depending on the amount of items to display, if 2 tabs then it will always display tabs (except when js is disabled), if more than 2 then it will display a list of links for small devices and tabs for desktop
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% from "./tab-id.njk" import TabId %}
|
|
2
|
+
{% from "../../sub-components/attributes/macro.njk" import MdsAttributes %}
|
|
2
3
|
|
|
3
4
|
{% if params.content %}
|
|
4
5
|
{% set kebabName -%}
|
|
@@ -11,12 +12,12 @@
|
|
|
11
12
|
mds-tabs--full-tabbed js-full-tabbed
|
|
12
13
|
{%- endif -%}
|
|
13
14
|
{%- endset %}
|
|
14
|
-
<div class="mds-tabs {{ tabVariation }}{%- if params.name %} mds-tabs--{{ kebabName }}{%- endif -%}{% if params.classes %} {{ params.classes }}{% endif %}"
|
|
15
|
+
<div class="mds-tabs {{ tabVariation }}{%- if params.name %} mds-tabs--{{ kebabName }}{%- endif -%}{% if params.classes %} {{ params.classes }}{% endif %}">
|
|
15
16
|
<ul class="mds-tabs__list">
|
|
16
17
|
{%- for item in params.content -%}
|
|
17
18
|
{%- set tabId = TabId(item, params, loop.index) -%}
|
|
18
19
|
<li class="mds-tabs__list-item">
|
|
19
|
-
<a href="#{{ tabId }}" id="label-{{tabId}}" class="mds-tabs__tab{%- if item.selected %} mds-tabs__tab--selected{%- endif %} js-tabs-item"
|
|
20
|
+
<a href="#{{ tabId }}" id="label-{{ tabId }}" class="mds-tabs__tab{%- if item.selected %} mds-tabs__tab--selected{%- endif %} js-tabs-item" {{ MdsAttributes(item.linkCustomAttr) }}>
|
|
20
21
|
{{- item.label -}}
|
|
21
22
|
</a>
|
|
22
23
|
</li>
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
{%- endif -%}
|
|
43
44
|
{%- endset -%}
|
|
44
45
|
|
|
45
|
-
<section class="mds-tabs__panel{%- if not item.selected %} mds-tabs__panel--hidden {%- endif -%}{% if item.contentClasses %} {{ item.contentClasses }}{% endif %}" id="{{ tabId }}"
|
|
46
|
+
<section class="mds-tabs__panel{%- if not item.selected %} mds-tabs__panel--hidden {%- endif -%}{% if item.contentClasses %} {{ item.contentClasses }}{% endif %}" id="{{ tabId }}">
|
|
46
47
|
{# id is used in js to apply aria-labelledby, don't forget to update both if needed #}
|
|
47
48
|
<{{allHeadersTag}} class="mds-tabs__panel-header{{allHeadersDisplay}}">{{- headerText | safe -}}</{{allHeadersTag}}>
|
|
48
49
|
<div class="mds-tabs__panel__content">
|
|
@@ -117,14 +117,14 @@ module.exports = {
|
|
|
117
117
|
label: 'Plane',
|
|
118
118
|
selected: true,
|
|
119
119
|
headerText: 'Flying craft',
|
|
120
|
-
linkCustomAttr: 'data-metric
|
|
120
|
+
linkCustomAttr: { 'data-metric': 'wings' },
|
|
121
121
|
content:
|
|
122
122
|
'<p>Inspect the anchor tags inside each tab to see the custom attributes that are added. One use case of this is allowing us to add click metrics.</p>',
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
label: 'Helicopter',
|
|
126
126
|
headerText: 'Flying craft',
|
|
127
|
-
linkCustomAttr: 'data-metric
|
|
127
|
+
linkCustomAttr: { 'data-metric': 'rotor-blades' },
|
|
128
128
|
content:
|
|
129
129
|
'<p>Inspect the anchor tags inside each tab to see the custom attributes that are added. One use case of this is allowing us to add click metrics.</p>',
|
|
130
130
|
},
|
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
|
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders component attributes as string
|
|
3
|
+
|
|
4
|
+
* By default or using `optional: false`, attributes render as ` name="value"`
|
|
5
|
+
* Using `optional: true`, attributes with empty (`null`, `undefined` or `false`) values are omitted
|
|
6
|
+
* Using `optional: true`, attributes with `true` (boolean) values render `name` only without value
|
|
7
|
+
|
|
8
|
+
{@link https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML}
|
|
9
|
+
|
|
10
|
+
@example
|
|
11
|
+
Output attribute ` aria-hidden="true"` when `true` (boolean) or `"true"` (string)
|
|
12
|
+
|
|
13
|
+
```njk
|
|
14
|
+
MdsAttributes({
|
|
15
|
+
"aria-hidden": true
|
|
16
|
+
})
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
@example
|
|
20
|
+
Output attribute ` aria-hidden="false"` when `false` (boolean) or `"false"` (string)
|
|
21
|
+
|
|
22
|
+
```njk
|
|
23
|
+
MdsAttributes({
|
|
24
|
+
"aria-hidden": false
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
@example
|
|
29
|
+
Output attribute ` hidden=""` when `null`, `undefined` or empty `""` (string)
|
|
30
|
+
|
|
31
|
+
```njk
|
|
32
|
+
MdsAttributes({
|
|
33
|
+
"hidden": undefined
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
@example
|
|
38
|
+
Output attribute ` hidden` as boolean attribute when optional and `true`
|
|
39
|
+
|
|
40
|
+
```njk
|
|
41
|
+
MdsAttributes({
|
|
42
|
+
hidden: {
|
|
43
|
+
value: true,
|
|
44
|
+
optional: true
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
@example
|
|
50
|
+
Output empty string when optional and `null`, `undefined` or `false`
|
|
51
|
+
|
|
52
|
+
```njk
|
|
53
|
+
MdsAttributes({
|
|
54
|
+
hidden: {
|
|
55
|
+
value: undefined,
|
|
56
|
+
optional: true
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
@private
|
|
62
|
+
@param {{ [attribute: string]: string | { value: string, optional?: boolean } } | string} attributes - Component attributes param
|
|
63
|
+
#}
|
|
64
|
+
{% macro MdsAttributes(attributes) -%}
|
|
65
|
+
{#- Default attributes output -#}
|
|
66
|
+
{% set attributesHtml = attributes if attributes is string else "" %}
|
|
67
|
+
|
|
68
|
+
{#- Append attribute name/value pairs -#}
|
|
69
|
+
{%- if attributes is mapping %}
|
|
70
|
+
{% for name, value in attributes %}
|
|
71
|
+
{#- Detect if this is a `safe` filtered value. Just pass the value through if so. -#}
|
|
72
|
+
{#- https://github.com/alphagov/govuk-frontend/issues/4937 -#}
|
|
73
|
+
{% if value is mapping and not [undefined, null].includes(value.val) and value.length %}
|
|
74
|
+
{% set value = value.val %}
|
|
75
|
+
{% endif %}
|
|
76
|
+
|
|
77
|
+
{#- Set default attribute options -#}
|
|
78
|
+
{% set options = value if value is mapping else {
|
|
79
|
+
value: value,
|
|
80
|
+
optional: false
|
|
81
|
+
} %}
|
|
82
|
+
|
|
83
|
+
{#- Output ` name` only (no value) for boolean attributes -#}
|
|
84
|
+
{% if options.optional === true and options.value === true %}
|
|
85
|
+
{% set attributesHtml = attributesHtml + " " + name | escape %}
|
|
86
|
+
{#- Skip optional empty attributes or output ` name="value"` pair by default -#}
|
|
87
|
+
{% elif (options.optional === true and not [undefined, null, false].includes(options.value)) or options.optional !== true %}
|
|
88
|
+
{% set attributesHtml = attributesHtml + " " + name | escape + '="' + options.value | escape + '"' %}
|
|
89
|
+
{% endif %}
|
|
90
|
+
{% endfor %}
|
|
91
|
+
{% endif -%}
|
|
92
|
+
|
|
93
|
+
{{- attributesHtml | safe -}}
|
|
94
|
+
{%- endmacro %}
|
|
@@ -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;
|