@madgex/design-system 1.55.1 → 1.56.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.
- package/coverage/cobertura-coverage.xml +57 -3
- package/coverage/components/accordion/accordion.js.html +1 -1
- package/coverage/components/accordion/index.html +1 -1
- package/coverage/components/inputs/combobox/combobox.js.html +1 -1
- package/coverage/components/inputs/combobox/index.html +1 -1
- package/coverage/components/inputs/combobox/vue-components/Combobox.vue.html +1 -1
- package/coverage/components/inputs/combobox/vue-components/ListBoxOption.vue.html +1 -1
- package/coverage/components/inputs/combobox/vue-components/index.html +1 -1
- package/coverage/components/inputs/file-upload/file-upload.js.html +1 -1
- package/coverage/components/inputs/file-upload/index.html +1 -1
- package/coverage/components/inputs/multi-select/index.html +1 -1
- package/coverage/components/inputs/multi-select/multi-select.js.html +1 -1
- package/coverage/components/inputs/multi-select/vue-components/MultiSelect.vue.html +1 -1
- package/coverage/components/inputs/multi-select/vue-components/MultiSelectCheckbox.vue.html +1 -1
- package/coverage/components/inputs/multi-select/vue-components/MultiSelectCheckboxGroup.vue.html +1 -1
- package/coverage/components/inputs/multi-select/vue-components/index.html +1 -1
- package/coverage/components/inputs/textarea/character-count.js.html +190 -0
- package/coverage/components/inputs/textarea/index.html +110 -0
- package/coverage/components/modal/index.html +1 -1
- package/coverage/components/modal/modal.js.html +1 -1
- package/coverage/components/notification/index.html +1 -1
- package/coverage/components/notification/notification.js.html +1 -1
- package/coverage/components/popover/index.html +1 -1
- package/coverage/components/popover/popover.js.html +1 -1
- package/coverage/components/switch-state/index.html +1 -1
- package/coverage/components/switch-state/switch-state.js.html +1 -1
- package/coverage/components/tabs/index.html +1 -1
- package/coverage/components/tabs/tabs.js.html +1 -1
- package/coverage/index.html +26 -11
- package/coverage/js/common.js.html +1 -1
- package/coverage/js/fractal-scripts/combobox.js.html +1 -1
- package/coverage/js/fractal-scripts/index.html +1 -1
- package/coverage/js/fractal-scripts/notification.js.html +1 -1
- package/coverage/js/fractal-scripts/switch-state.js.html +1 -1
- package/coverage/js/index-fractal.js.html +1 -1
- package/coverage/js/index-polyfills.js.html +1 -1
- package/coverage/js/index-vue.js.html +1 -1
- package/coverage/js/index.html +5 -5
- package/coverage/js/index.js.html +10 -4
- package/coverage/js/polyfills/closest.js.html +1 -1
- package/coverage/js/polyfills/index.html +1 -1
- package/coverage/js/polyfills/remove.js.html +1 -1
- package/coverage/tokens/_config.js.html +1 -1
- package/coverage/tokens/index.html +1 -1
- package/dist/_tokens/css/_tokens.css +1 -1
- package/dist/_tokens/js/_tokens-module.js +1 -1
- package/dist/_tokens/scss/_tokens.scss +1 -1
- package/dist/css/index.css +1 -1
- package/dist/js/index.js +6 -6
- package/package.json +1 -1
- package/src/components/inputs/_form-elements.scss +19 -1
- package/src/components/inputs/file-upload/README.md +9 -0
- package/src/components/inputs/textarea/README.md +39 -0
- package/src/components/inputs/textarea/_macro.njk +3 -0
- package/src/components/inputs/textarea/_template.njk +55 -0
- package/src/components/inputs/textarea/character-count.js +37 -0
- package/src/components/inputs/textarea/textarea.config.js +10 -0
- package/src/components/inputs/textarea/textarea.njk +24 -0
- package/src/components/inputs/textarea/textarea.scss +4 -0
- package/src/js/index.js +2 -0
- package/src/scss/components/__index.scss +1 -1
- package/src/components/textarea/_macro.njk +0 -3
- package/src/components/textarea/_template.njk +0 -41
- package/src/components/textarea/textarea.config.js +0 -31
- package/src/components/textarea/textarea.njk +0 -11
- package/src/components/textarea/textarea.scss +0 -7
package/package.json
CHANGED
|
@@ -31,11 +31,29 @@
|
|
|
31
31
|
.mds-form-message {
|
|
32
32
|
@extend .mds-font-brevier;
|
|
33
33
|
margin-bottom: $mds-size-baseline * 4;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
34
36
|
|
|
35
37
|
&--error {
|
|
38
|
+
color: $mds-color-status-error-dark;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.mds-form-message--character-count {
|
|
43
|
+
display: none;
|
|
44
|
+
margin-top: $mds-size-baseline;
|
|
45
|
+
|
|
46
|
+
.js & {
|
|
36
47
|
display: flex;
|
|
37
48
|
align-items: center;
|
|
38
|
-
|
|
49
|
+
justify-content: flex-end;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
& .mds-icon {
|
|
53
|
+
display: none;
|
|
54
|
+
}
|
|
55
|
+
&.mds-form-message--error .mds-icon {
|
|
56
|
+
display: inline-block;
|
|
39
57
|
}
|
|
40
58
|
}
|
|
41
59
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- `state`: The current state of the input, currently the only allowed value is `error` **optional**,
|
|
16
16
|
- `tooltipMessage`: Toggles a tooltip with this message to appear on the input **optional**
|
|
17
17
|
- `classes`: add extra classes to the trigger - **optional**
|
|
18
|
+
- `fileTypes`: comma separated list of the different types of file you want to allow. It will add the `accept` attribute on the input - **optional ** (see notes on validation)
|
|
18
19
|
|
|
19
20
|
## Accessibility
|
|
20
21
|
|
|
@@ -31,6 +32,14 @@ For keyboard users as the file input itself remains visually hidden, when it rec
|
|
|
31
32
|
|
|
32
33
|
The styling falls back completely to a native file input with default design system form field styling if Javascript is not available.
|
|
33
34
|
|
|
35
|
+
## Validation
|
|
36
|
+
|
|
37
|
+
The `accept` attribute doesn't validate the types of the selected files; it simply provides hints for browsers to guide users towards selecting the correct file types. It is possible (in most cases) for users to toggle an option in the file chooser that makes it possible to override this and select any file they wish, which could have the incorrect type.
|
|
38
|
+
|
|
39
|
+
When using the drag and drop functionality, the user can also choose any file.
|
|
40
|
+
|
|
41
|
+
Because of this, you should make sure that expected requirements are validated server-side.
|
|
42
|
+
|
|
34
43
|
## Note
|
|
35
44
|
|
|
36
45
|
When a file is selected, the drag & drop/file selection area is disabled. When using cloud services such as Dropbox or Google Drive, the buttons to access those services will disappear as well. The user will need to click on the "remove file" button to be able to select a file again, either using the file input or the cloud services.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
## Parameters
|
|
2
|
+
|
|
3
|
+
- `labelText`: The label for the textarea **required**,
|
|
4
|
+
- `hideLabel`: true/false - Add this parameter if you need to visually hide the label. The text of the label will the used by default as a placeholder, unless overridden by the placeholder parameter **optional**
|
|
5
|
+
**(please see accessibility notes below regarding the use of this parameter)**
|
|
6
|
+
- `name`: The name of the textarea field, uses ID unless specified **optional**,
|
|
7
|
+
- `id`: The id attribute of the field, **required**
|
|
8
|
+
- `optional`: Is the textarea optional, otherwise required **optional**,
|
|
9
|
+
- `disabled`: Should the textarea be disabled **optional**,
|
|
10
|
+
- `helpText`: Helper text to display under the label **optional**,
|
|
11
|
+
- `validationError`: The error message provided by validation **optional**,
|
|
12
|
+
- `state`: The current state of the textarea, currently the only allowed value is `error` **optional**,
|
|
13
|
+
- `tooltipMessage`: Toggles a tooltip with this message to appear on the textarea **optional**
|
|
14
|
+
- `classes`: add extra classes to the trigger - **optional**
|
|
15
|
+
- `placeholder`: add a placeholder to the textarea **optional**
|
|
16
|
+
**(please see accessibility notes below regarding the use of this parameter)**
|
|
17
|
+
- `maxlength`: specify the maximum length (in characters) allowed in the textarea. Adding this attribute will automatically add a "character counter" at the bottom right of the textarea (see notes below) - **optional**
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Accessibility
|
|
21
|
+
|
|
22
|
+
This textarea relies entirely on the browser standard textarea and applies the accessibility provided by that.
|
|
23
|
+
Additional accessibility aria attributes are applied to the label `aria-live=polite` to inform users of help text or error messages attributed to a form
|
|
24
|
+
|
|
25
|
+
Avoid using `hideLabel` and `placeholder` if you can. Only hide the label when there can be no mistake as to what the textarea is for, probably when there is only one textarea in the form.
|
|
26
|
+
The placeholder can also be a problem given its lower contrast so don't rely on it to give important information. Use the `helpText` or the `tooltipMessage` instead, for example.
|
|
27
|
+
|
|
28
|
+
Useful article: https://www.nngroup.com/articles/form-design-placeholders/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## Character counter
|
|
32
|
+
Adding the `maxlength` attribute will add a "character counter" automatically if javascript is enabled. Please note that the attribute will be removed with javascript to allow for a negative count. This is to ensure that the text entered by the user is not cut off without warning. The negative count's message will look like an error to prompt the user to edit the text entered. The maximum length of the text should obviously be checked server side.
|
|
33
|
+
Note: Line breaks are counted as 2 characters as forms will encode them as such (`\r\n`)
|
|
34
|
+
|
|
35
|
+
`aria-live="polite"` and `aria-atomic="true"` has been added to the counter container so the updates can be read to the user after he has finished entering the text.
|
|
36
|
+
We are also using `aria-describedby` to get the number of characters left when a user enter the textarea.
|
|
37
|
+
|
|
38
|
+
Please note that, at the date of writing (28/07/20), `aria-describedby` doesn't work if it references a `role="alert/status"` (https://a11ysupport.io/tech/aria/aria-describedby_attribute) which is why we're only using the `aria-live` and `aria-atomic` on the live region.
|
|
39
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{% from "../label/_macro.njk" import MdsInputLabel %}
|
|
2
|
+
{% from "../_message/_macro.njk" import MdsInputMessages %}
|
|
3
|
+
{% from "../../icons/_macro.njk" import MdsIcon %}
|
|
4
|
+
|
|
5
|
+
{% if params.name %}
|
|
6
|
+
{% set name = params.name %}
|
|
7
|
+
{% else %}
|
|
8
|
+
{% set name = params.id %}
|
|
9
|
+
{% endif %}
|
|
10
|
+
|
|
11
|
+
<div class="mds-form-element mds-form-element--textarea{% if params.state %} mds-form-element--{{params.state}}{% endif %}{% if params.classes %} {{params.classes}}{% endif %}{% if params.maxlength %} mds-form-element--character-count{% endif %}" id="{{ params.id }}-container" data-test="textarea">
|
|
12
|
+
{{ MdsInputLabel({
|
|
13
|
+
labelText: params.labelText,
|
|
14
|
+
hideLabel: params.hideLabel,
|
|
15
|
+
id: params.id,
|
|
16
|
+
optional: params.optional,
|
|
17
|
+
tooltipMessage: params.tooltipMessage
|
|
18
|
+
}) }}
|
|
19
|
+
{{ MdsInputMessages({
|
|
20
|
+
id: params.id,
|
|
21
|
+
helpText: params.helpText,
|
|
22
|
+
validationError: params.validationError
|
|
23
|
+
}) }}
|
|
24
|
+
<textarea
|
|
25
|
+
class="mds-form-control"
|
|
26
|
+
name="{{ name }}"
|
|
27
|
+
id="{{ params.id }}"
|
|
28
|
+
{% if not params.optional %}
|
|
29
|
+
required="required"
|
|
30
|
+
{% endif %}
|
|
31
|
+
{% if params.disabled %}
|
|
32
|
+
disabled="disabled"
|
|
33
|
+
{% endif %}
|
|
34
|
+
{% if params.hideLabel or params.placeholder %}
|
|
35
|
+
placeholder="{% if params.placeholder %}{{ params.placeholder }}{% else %}{{ params.labelText }}{% endif %}"
|
|
36
|
+
{% endif %}
|
|
37
|
+
{% if params.maxlength %}
|
|
38
|
+
maxlength="{{ params.maxlength }}" aria-describedby="{{ params.id }}-character-count-text"
|
|
39
|
+
{% endif %}
|
|
40
|
+
>{#
|
|
41
|
+
#}{% if params.value %}{{params.value}}{% endif %}{#
|
|
42
|
+
#}</textarea>
|
|
43
|
+
{% if params.maxlength %}
|
|
44
|
+
<div class="mds-form-message mds-form-message--character-count" id="{{ params.id }}-character-count-text" aria-live="polite" aria-atomic="true">
|
|
45
|
+
{{- MdsIcon({
|
|
46
|
+
iconName: 'cross',
|
|
47
|
+
classes: 'mds-icon--sm',
|
|
48
|
+
hasContainer: true,
|
|
49
|
+
containerClasses: 'mds-icon-container--circle mds-icon-container--error mds-icon-container--before'
|
|
50
|
+
})
|
|
51
|
+
-}}
|
|
52
|
+
<span><span class="js-character-count-number">{{ params.maxlength }}</span> characters remaining</span>
|
|
53
|
+
</div>
|
|
54
|
+
{% endif %}
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
const characterCountClass = '.mds-form-element--character-count';
|
|
3
|
+
const messageClass = '.mds-form-message--character-count';
|
|
4
|
+
const characterCountNumberClass = '.js-character-count-number';
|
|
5
|
+
const formControlClass = '.mds-form-control';
|
|
6
|
+
|
|
7
|
+
const characterCount = {
|
|
8
|
+
init: () => {
|
|
9
|
+
const elementsWithCharacterCount = Array.from(document.querySelectorAll(characterCountClass));
|
|
10
|
+
elementsWithCharacterCount.forEach((element) => {
|
|
11
|
+
const formControl = element.querySelector(formControlClass);
|
|
12
|
+
const maxLength = formControl.getAttribute('maxlength');
|
|
13
|
+
const message = element.querySelector(messageClass);
|
|
14
|
+
const characterCountNumber = element.querySelector(characterCountNumberClass);
|
|
15
|
+
formControl.removeAttribute('maxlength');
|
|
16
|
+
characterCount.updateCounter(formControl, maxLength, characterCountNumber, message);
|
|
17
|
+
formControl.addEventListener('keyup', (e) => {
|
|
18
|
+
e.stopPropagation();
|
|
19
|
+
characterCount.updateCounter(formControl, maxLength, characterCountNumber, message);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
updateCounter: (formControl, maxLength, characterCountNumber, message) => {
|
|
24
|
+
// Counting new lines as 2 characters to plan for \r\n encoding
|
|
25
|
+
const newLines = formControl.value.match(/(\r\n|\n|\r)/g);
|
|
26
|
+
const currentCount = formControl.value.length + (newLines ? newLines.length : 0);
|
|
27
|
+
const remainingCount = maxLength - currentCount;
|
|
28
|
+
characterCountNumber.textContent = remainingCount;
|
|
29
|
+
if (remainingCount < 0) {
|
|
30
|
+
message.classList.add('mds-form-message--error');
|
|
31
|
+
} else {
|
|
32
|
+
message.classList.remove('mds-form-message--error');
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default characterCount;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{% from "./inputs/textarea/_macro.njk" import MdsTextarea %}
|
|
2
|
+
|
|
3
|
+
<div class="mds-grid-row">
|
|
4
|
+
<div class="mds-grid-col-12 mds-grid-col-md-6">
|
|
5
|
+
<div class="mds-form-field">
|
|
6
|
+
{{ MdsTextarea({
|
|
7
|
+
labelText: labelText,
|
|
8
|
+
hideLabel: hideLabel,
|
|
9
|
+
name: name,
|
|
10
|
+
id: id,
|
|
11
|
+
value:value,
|
|
12
|
+
optional: optional,
|
|
13
|
+
disabled: disabled,
|
|
14
|
+
placeholder: placeholder,
|
|
15
|
+
helpText: helpText,
|
|
16
|
+
validationError: validationError,
|
|
17
|
+
state: state,
|
|
18
|
+
classes: classes,
|
|
19
|
+
tooltipMessage: tooltipMessage,
|
|
20
|
+
maxlength: maxlength
|
|
21
|
+
}) }}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
package/src/js/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import accordion from '../components/accordion/accordion';
|
|
|
13
13
|
import popovers from '../components/popover/popover';
|
|
14
14
|
import modals from '../components/modal/modal';
|
|
15
15
|
import fileUpload from '../components/inputs/file-upload/file-upload';
|
|
16
|
+
import characterCount from '../components/inputs/textarea/character-count';
|
|
16
17
|
|
|
17
18
|
document.addEventListener('DOMContentLoaded', () => {
|
|
18
19
|
tabs.init();
|
|
@@ -20,4 +21,5 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
20
21
|
popovers.init();
|
|
21
22
|
modals.init();
|
|
22
23
|
fileUpload.init();
|
|
24
|
+
characterCount.init();
|
|
23
25
|
});
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
@import '../../components/card/card';
|
|
4
4
|
@import '../../components/section-title/section-title';
|
|
5
5
|
@import '../../components/tabs/tabs';
|
|
6
|
-
@import '../../components/textarea/textarea';
|
|
7
6
|
@import '../../components/accordion/accordion';
|
|
8
7
|
@import '../../components/pagination/pagination';
|
|
9
8
|
@import '../../components/switch-state/switch-state';
|
|
@@ -13,5 +12,6 @@
|
|
|
13
12
|
@import '../../components/inputs/combobox/combobox';
|
|
14
13
|
@import '../../components/inputs/multi-select/multi-select';
|
|
15
14
|
@import '../../components/inputs/file-upload/file-upload';
|
|
15
|
+
@import '../../components/inputs/textarea/textarea';
|
|
16
16
|
@import '../../components/modal/modal';
|
|
17
17
|
@import '../../components/skip-link/skip-link';
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
<div data-test="textarea">
|
|
2
|
-
<label class="mds-display-block mds-input__label mds-font-long-primer mds-margin-bottom-b1" for="{{ params.id }}">
|
|
3
|
-
{{ params.labelText }}
|
|
4
|
-
{% if params.required %}
|
|
5
|
-
<span>*</span>
|
|
6
|
-
{% endif %}
|
|
7
|
-
</label>
|
|
8
|
-
{% if params.labelHint %}
|
|
9
|
-
<span
|
|
10
|
-
class="mds-display-block mds-input__label--hint mds-font-long-primer mds-margin-bottom-b1"
|
|
11
|
-
id="{{ params.id }}-hint"
|
|
12
|
-
>
|
|
13
|
-
{{ params.labelHint }}
|
|
14
|
-
</span>
|
|
15
|
-
{% endif %}
|
|
16
|
-
<textarea
|
|
17
|
-
class="mds-textarea mds-border mds-border-radius"
|
|
18
|
-
name="{{ params.name }}"
|
|
19
|
-
id="{{ params.id }}"
|
|
20
|
-
cols="{{ params.cols | default(30) }}"
|
|
21
|
-
rows="{{ params.rows | default(10) }}"
|
|
22
|
-
{% if params.labelHint or params.validationMessage %}
|
|
23
|
-
aria-describedby="{% if params.labelHint %}{{ params.id }}-hint {% endif %}{% if params.validationMessage %}{{ params.id }}-validation-message{% endif %}"
|
|
24
|
-
{% endif %}
|
|
25
|
-
{% if params.disabled %}
|
|
26
|
-
disabled="disabled"
|
|
27
|
-
{% endif %}
|
|
28
|
-
{% if params.required %}
|
|
29
|
-
required="required"
|
|
30
|
-
{% endif %}
|
|
31
|
-
></textarea>
|
|
32
|
-
{% if params.validationMessage %}
|
|
33
|
-
<span
|
|
34
|
-
aria-live="polite"
|
|
35
|
-
id="{{ params.id }}-validation-message"
|
|
36
|
-
class="mds-input__validation-message mds-font-brevier"
|
|
37
|
-
>
|
|
38
|
-
{{ params.validationMessage }}
|
|
39
|
-
</span>
|
|
40
|
-
{% endif %}
|
|
41
|
-
</div>
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
title: 'Text Area',
|
|
3
|
-
status: 'prototype',
|
|
4
|
-
context: {
|
|
5
|
-
labelText: 'Text area',
|
|
6
|
-
name: 'Example text area',
|
|
7
|
-
id: 'exampleTextArea',
|
|
8
|
-
},
|
|
9
|
-
variants: [
|
|
10
|
-
{
|
|
11
|
-
name: 'With label hint',
|
|
12
|
-
context: {
|
|
13
|
-
labelText: 'Hint input',
|
|
14
|
-
name: 'Hint input',
|
|
15
|
-
id: 'hintInput',
|
|
16
|
-
type: 'text',
|
|
17
|
-
labelHint: 'This is a text area',
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: 'With validation message',
|
|
22
|
-
context: {
|
|
23
|
-
labelText: 'Validated input',
|
|
24
|
-
name: 'Validated input',
|
|
25
|
-
id: 'validatedInput',
|
|
26
|
-
type: 'text',
|
|
27
|
-
validationMessage: '100 characters remaining',
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
};
|