@1024pix/pix-ui 18.2.0 → 19.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Pix-UI Changelog
2
2
 
3
+ ## v19.0.1 (04/10/2022)
4
+
5
+
6
+ ### :rocket: Amélioration
7
+ - [#258](https://github.com/1024pix/pix-ui/pull/258) [FEATURE] Centre la PixModal en hauteur
8
+ - [#257](https://github.com/1024pix/pix-ui/pull/257) [FEATURE] Suppression du `padding` de la Sidebar pour permettre de mieux customiser
9
+
10
+ ### :coffee: Autre
11
+ - [#256](https://github.com/1024pix/pix-ui/pull/256) [BUFGIX] Mettre à jour la page d'utilisation de la PixModal (PIX-5735)
12
+
13
+ ## v19.0.0 (16/09/2022)
14
+
15
+
16
+ ### :rocket: Amélioration
17
+ - [#252](https://github.com/1024pix/pix-ui/pull/252) [FEATURE] Rendre accessible (a11y) le composant PixMultiSelect (PIX-5555)
18
+
3
19
  ## v18.2.0 (16/09/2022)
4
20
 
5
21
 
@@ -1,15 +1,20 @@
1
1
  <div class="pix-checkbox">
2
- <label
3
- class="pix-checkbox__label {{this.labelSizeClass}} {{if @screenReaderOnly 'sr-only'}}"
4
- for={{this.id}}
5
- >
6
- {{this.label}}
7
- </label>
8
2
  <input
9
- id={{this.id}}
3
+ id={{@id}}
10
4
  type="checkbox"
11
5
  class="pix-checkbox__input {{if @isIndeterminate ' pix-checkbox__input--indeterminate'}}"
12
6
  checked={{@checked}}
13
7
  ...attributes
14
8
  />
9
+ <label
10
+ class="pix-checkbox__label {{this.labelSizeClass}} {{if @screenReaderOnly 'sr-only'}}"
11
+ for={{@id}}
12
+ >
13
+ {{#if (has-block)}}
14
+ {{yield}}
15
+ {{else}}
16
+ yield required to give a label for PixCheckBox
17
+ {{@id}}.
18
+ {{/if}}
19
+ </label>
15
20
  </div>
@@ -1,5 +1,4 @@
1
1
  import Component from '@glimmer/component';
2
- import { htmlSafe } from '@ember/string';
3
2
 
4
3
  const labelSizeToClass = new Map([
5
4
  ['small', 'pix-checkbox__label--small'],
@@ -11,17 +10,9 @@ export default class PixCheckbox extends Component {
11
10
  constructor() {
12
11
  super(...arguments);
13
12
 
14
- if (!this.args.label) {
15
- throw new Error('ERROR in PixCheckbox component, you must provide @label params');
16
- }
17
- this.label = htmlSafe(this.args.label);
18
- }
19
-
20
- get id() {
21
13
  if (!this.args.id || !this.args.id.toString().trim()) {
22
14
  throw new Error('ERROR in PixCheckbox component, @id param is not provided');
23
15
  }
24
- return this.args.id;
25
16
  }
26
17
 
27
18
  get labelSizeClass() {
@@ -1,35 +1,51 @@
1
- <div class="pix-multi-select" ...attributes {{on-click-outside this.hideDropDown capture=true}}>
2
-
3
- {{#if this.label}}
4
- <label for={{@id}} class="pix-multi-select__label">{{this.label}}</label>
5
- {{/if}}
1
+ <div
2
+ class="pix-multi-select"
3
+ {{on-click-outside this.hideDropDown}}
4
+ {{on-arrow-down-up-action this.listId this.showDropDown this.isExpanded}}
5
+ {{on-escape-action this.hideDropDown @id}}
6
+ >
6
7
 
7
8
  {{#if @isSearchable}}
8
- <label class="pix-multi-select-header {{if this.isBig 'pix-multi-select-header--big'}}">
9
-
10
- <span class="pix-multi-select-header__title">{{@title}}</span>
9
+ <label
10
+ for={{@id}}
11
+ class="pix-multi-select__label{{if @screenReaderOnly ' screen-reader-only'}}"
12
+ >{{@label}}</label>
13
+ <span class="pix-multi-select-header">
11
14
  <FaIcon @icon="magnifying-glass" class="pix-multi-select-header__search-icon" />
12
15
 
13
16
  <input
17
+ class="pix-multi-select-header__search-input"
14
18
  id={{@id}}
15
19
  type="text"
16
20
  name={{@id}}
17
- placeholder={{this.placeholder}}
21
+ placeholder={{this.innerText}}
18
22
  autocomplete="off"
19
23
  {{on "input" this.updateSearch}}
20
- {{on "focus" this.focusDropdown}}
21
- class="pix-multi-select-header__search-input"
24
+ {{on "click" this.toggleDropDown}}
25
+ aria-expanded={{this.isAriaExpanded}}
26
+ aria-controls={{this.listId}}
27
+ aria-haspopup="menu"
28
+ ...attributes
22
29
  />
23
-
24
- </label>
30
+ </span>
25
31
  {{else}}
32
+ <span
33
+ id={{this.labelId}}
34
+ class="pix-multi-select__label{{if @screenReaderOnly ' screen-reader-only'}}"
35
+ >{{@label}}</span>
36
+
26
37
  <button
38
+ aria-labelledby={{this.labelId}}
27
39
  id={{@id}}
28
40
  type="button"
29
- class="pix-multi-select-header {{if this.isBig 'pix-multi-select-header--big'}}"
41
+ aria-expanded={{this.isAriaExpanded}}
42
+ aria-controls={{this.listId}}
43
+ aria-haspopup="menu"
44
+ class="pix-multi-select-header"
30
45
  {{on "click" this.toggleDropDown}}
46
+ ...attributes
31
47
  >
32
- {{@title}}
48
+ {{this.innerText}}
33
49
  <FaIcon
34
50
  class="pix-multi-select-header__dropdown-icon
35
51
  {{if this.isExpanded ' pix-multi-select-header__dropdown-icon--expand'}}"
@@ -38,23 +54,25 @@
38
54
  </button>
39
55
  {{/if}}
40
56
 
41
- <ul class="pix-multi-select-list {{unless this.isExpanded 'pix-multi-select-list--hidden'}}">
57
+ <ul
58
+ class="pix-multi-select-list {{unless this.isExpanded 'pix-multi-select-list--hidden'}}"
59
+ id={{this.listId}}
60
+ aria-multiselectable="true"
61
+ role="menu"
62
+ >
42
63
  {{#if (gt this.results.length 0)}}
43
64
  {{#each this.results as |option|}}
44
- <li class="pix-multi-select-list__item">
45
- <input
46
- class="pix-multi-select-list__checkbox
47
- {{if @isSearchable ' pix-multi-select-list__checkbox--searchable'}}"
48
- type="checkbox"
49
- checked={{option.checked}}
50
- id={{concat @id "-" option.value}}
51
- name={{option.label}}
65
+ <li class="pix-multi-select-list__item" role="menuitem">
66
+ <PixCheckbox
67
+ @id={{concat @id "-" option.value}}
68
+ @checked={{option.checked}}
52
69
  value={{option.value}}
53
70
  {{on "change" this.onSelect}}
54
- />
55
- <label for={{concat @id "-" option.value}}>
71
+ {{on-enter-action this.hideDropDown @id}}
72
+ tabindex={{if this.isExpanded "0" "-1"}}
73
+ >
56
74
  {{yield option}}
57
- </label>
75
+ </PixCheckbox>
58
76
  </li>
59
77
  {{/each}}
60
78
  {{else if this.isLoadingOptions}}
@@ -26,35 +26,47 @@ export default class PixMultiSelect extends Component {
26
26
 
27
27
  constructor(...args) {
28
28
  super(...args);
29
- const { onLoadOptions, selected } = this.args;
29
+ const { onLoadOptions, id, label, innerText } = this.args;
30
+
31
+ const idIsNotDefined = !id || !id.trim();
32
+ const labelIsNotDefined = !label || !label.trim();
33
+ const innerTextIsNotDefined = !innerText || !innerText.trim();
34
+
35
+ if (idIsNotDefined || labelIsNotDefined || innerTextIsNotDefined) {
36
+ const missingParams = [];
37
+
38
+ if (idIsNotDefined) missingParams.push('@id');
39
+ if (labelIsNotDefined) missingParams.push('@label');
40
+ if (innerTextIsNotDefined) missingParams.push('@innerText');
41
+
42
+ throw new Error(
43
+ `ERROR in PixMultiSelect component, ${missingParams.join(', ')} ${
44
+ missingParams.length > 1 ? 'params are' : 'param is'
45
+ } necessary`
46
+ );
47
+ }
30
48
 
31
49
  if (onLoadOptions) {
32
50
  this.isLoadingOptions = true;
33
51
  onLoadOptions().then((options = []) => {
34
52
  this.options = options;
35
- this._setDisplayedOptions(selected, true);
36
53
  this.isLoadingOptions = false;
37
54
  });
38
55
  } else {
39
56
  this.options = [...(this.args.options || [])];
40
- this._setDisplayedOptions(selected, true);
41
57
  }
42
58
  }
43
59
 
44
- get label() {
45
- const labelIsDefined = this.args.label && this.args.label.trim();
46
- const idIsNotDefined = this.args.id && !this.args.id.trim();
60
+ get listId() {
61
+ return `list-${this.args.id}`;
62
+ }
47
63
 
48
- if (labelIsDefined && idIsNotDefined) {
49
- throw new Error(
50
- 'ERROR in PixMultiSelect component, @id param is necessary when giving @label'
51
- );
52
- }
53
- return this.args.label || null;
64
+ get labelId() {
65
+ return `label-${this.args.id}`;
54
66
  }
55
67
 
56
- get isBig() {
57
- return this.args.size === 'big';
68
+ get isAriaExpanded() {
69
+ return this.isExpanded ? 'true' : 'false';
58
70
  }
59
71
 
60
72
  get results() {
@@ -64,8 +76,8 @@ export default class PixMultiSelect extends Component {
64
76
  return this.options;
65
77
  }
66
78
 
67
- get placeholder() {
68
- const { selected, placeholder } = this.args;
79
+ get innerText() {
80
+ const { selected, innerText } = this.args;
69
81
  if (selected?.length > 0) {
70
82
  const selectedOptionLabels = this.options
71
83
  .filter(({ value, label }) => {
@@ -76,7 +88,7 @@ export default class PixMultiSelect extends Component {
76
88
  .join(', ');
77
89
  return selectedOptionLabels;
78
90
  }
79
- return placeholder;
91
+ return innerText;
80
92
  }
81
93
 
82
94
  _setDisplayedOptions(selected, shouldSort) {
@@ -108,8 +120,6 @@ export default class PixMultiSelect extends Component {
108
120
  selected = selected.filter((value) => value !== event.target.value);
109
121
  }
110
122
 
111
- this._setDisplayedOptions(selected, false);
112
-
113
123
  if (this.args.onSelect) {
114
124
  this.args.onSelect(selected);
115
125
  }
@@ -142,13 +152,6 @@ export default class PixMultiSelect extends Component {
142
152
  this.isExpanded = false;
143
153
  }
144
154
 
145
- @action
146
- focusDropdown() {
147
- if (this.args.isSearchable && this.args.showOptionsOnInput) {
148
- this.showDropDown();
149
- }
150
- }
151
-
152
155
  @action
153
156
  updateSearch(event) {
154
157
  this.searchData = this.args.strictSearch
@@ -0,0 +1,10 @@
1
+ .screen-reader-only {
2
+ position: absolute;
3
+ width: 1px;
4
+ height: 1px;
5
+ padding: 0;
6
+ margin: -1px;
7
+ overflow: hidden;
8
+ clip: rect(0, 0, 0, 0);
9
+ border: 0;
10
+ }
@@ -13,7 +13,7 @@
13
13
  }
14
14
 
15
15
  @mixin focusWithinFormElement() {
16
- &:focus {
16
+ &:focus-within {
17
17
  border-color: $pix-primary;
18
18
  box-shadow: inset 0px 0px 0px 0.6px $pix-primary;
19
19
  outline: none;
@@ -1,9 +1,7 @@
1
1
  .pix-checkbox {
2
2
  @import 'reset-css';
3
- display: flex;
4
3
  align-items: center;
5
4
  display: flex;
6
- flex-direction: row-reverse;
7
5
 
8
6
  label, input {
9
7
  cursor: pointer;
@@ -31,9 +29,7 @@
31
29
  min-height: 18px;
32
30
  border: 1.2px solid $pix-neutral-90;
33
31
  border-radius: 2px;
34
- display: flex;
35
- justify-content: center;
36
- align-items: center;
32
+ position: relative;
37
33
 
38
34
  &:hover, &:focus {
39
35
  box-shadow: inset 0 1px 3px rgba(0,0,0, .1), 0 0 0 6px $pix-neutral-15;
@@ -54,6 +50,9 @@
54
50
  background-position: center;
55
51
  width: 16px;
56
52
  height: 16px;
53
+ position: absolute;
54
+ top: 0;
55
+ left: 0;
57
56
 
58
57
  }
59
58
  }
@@ -1,14 +1,27 @@
1
1
  .pix-modal__overlay {
2
- background-color: rgba(52, 69, 99, 0.7);
2
+ position: fixed;
3
+ z-index: 1000;
4
+ top: 0;
3
5
  bottom: 0;
4
6
  left: 0;
5
- overflow-y: scroll;
6
- position: fixed;
7
7
  right: 0;
8
- top: 0;
9
- z-index: 1000;
8
+ overflow-y: auto;
9
+ text-align: center; // Used to center horizontally the inline-block modal content
10
+ padding: $spacing-xs 0;
11
+ background-color: rgba(52, 69, 99, 0.7);
10
12
  transition: all 0.3s ease-in-out;
11
13
 
14
+ // This block is used to center vertically the modal
15
+ // if the content is less than 100vh
16
+ // Inspired by https://mui.com/material-ui/react-dialog/#scrolling-long-content
17
+ &::after {
18
+ content: "";
19
+ display: inline-block;
20
+ vertical-align: middle;
21
+ height: 100%;
22
+ width: 0px;
23
+ }
24
+
12
25
  &--hidden {
13
26
  visibility: hidden;
14
27
  opacity: 0;
@@ -23,16 +36,19 @@ $button-margin: 16px;
23
36
 
24
37
  .pix-modal {
25
38
  @import 'reset-css';
39
+ display: inline-block;
40
+ vertical-align: middle; // Centered vertically with the .pix-modal__overlay::after which is 100% height
41
+ width: 512px;
42
+ max-width: calc(100% - #{2 * $spacing-xs}); // Horizontal margin sets here to have extra space for the .pix-modal__overlay::after on mobile
43
+ text-align: initial;
44
+ background-color: $pix-neutral-10;
26
45
  box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
27
- margin: 20vh auto;
28
- max-width: 512px;
29
- width: calc(100% - 24px);
46
+ border-radius: 4px;
47
+ overflow: hidden;
30
48
 
31
49
  &__header {
32
50
  background: $pix-neutral-0;
33
51
  border-bottom: 1px solid $pix-neutral-20;
34
- border-top-left-radius: 4px;
35
- border-top-right-radius: 4px;
36
52
  padding: $modal-padding;
37
53
  display: flex;
38
54
  align-items: flex-start;
@@ -64,7 +80,6 @@ $button-margin: 16px;
64
80
  }
65
81
 
66
82
  &__content {
67
- background-color: $pix-neutral-10;
68
83
  padding: $modal-padding;
69
84
  font-size: 0.875rem;
70
85
  color: $pix-neutral-90;
@@ -78,10 +93,7 @@ $button-margin: 16px;
78
93
  }
79
94
 
80
95
  &__footer {
81
- background-color: $pix-neutral-10;
82
96
  padding: 0 $modal-padding $modal-padding - $button-margin;
83
- border-bottom-left-radius: 4px;
84
- border-bottom-right-radius: 4px;
85
97
 
86
98
  @include device-is('tablet') {
87
99
  display: flex;
@@ -71,7 +71,7 @@
71
71
 
72
72
  .pix-multi-select-list {
73
73
  width: 100%;
74
- margin: 0px;
74
+ margin: 0;
75
75
  z-index: 200;
76
76
  background-color: $pix-neutral-0;
77
77
  position: absolute;
@@ -82,9 +82,11 @@
82
82
  list-style-type: none;
83
83
  padding: 0;
84
84
  box-shadow: 0px 8px 24px 0px rgba(23, 43, 77, 0.1);
85
+ transition: all .1s ease-in-out;
85
86
 
86
87
  &--hidden {
87
- display: none;
88
+ visibility: hidden;
89
+ opacity: 0;
88
90
  }
89
91
 
90
92
  &::-webkit-scrollbar {
@@ -109,9 +111,10 @@
109
111
  border-bottom: 1px solid $pix-neutral-15;
110
112
  list-style: none;
111
113
  font-size: 0.9rem;
114
+ padding: 8px;
112
115
 
113
116
  @include hoverFormElement();
114
-
117
+
115
118
  &--no-result {
116
119
  text-align: center;
117
120
  padding: 12px 0;
@@ -121,58 +124,4 @@
121
124
  border-bottom: none;
122
125
  }
123
126
  }
124
-
125
- &__checkbox {
126
- position: absolute;
127
- opacity: 0;
128
-
129
- & + label {
130
- position: relative;
131
- display: flex;
132
- align-items: center;
133
- padding: 11px 16px;
134
- cursor: pointer;
135
- }
136
-
137
- & + label:before {
138
- content: '';
139
- margin-right: 12px;
140
- display: inline-block;
141
- vertical-align: text-top;
142
- min-width: 16px;
143
- min-height: 16px;
144
- border-radius: 4px;
145
- background: $pix-neutral-0;
146
- border: 1px solid $pix-neutral-20;
147
- }
148
-
149
- &:checked + label:before {
150
- background: $pix-primary;
151
- border-color: $pix-primary;
152
- }
153
-
154
- &:checked + label:after {
155
- position: absolute;
156
- top: calc(50% - 5px);
157
- left: 21px;
158
- width: 7px;
159
- height: 5px;
160
- background: transparent;
161
- border: 2px solid $pix-neutral-0;
162
- border-top: none;
163
- border-right: none;
164
- transform: rotate(-45deg);
165
- content: '';
166
- }
167
-
168
- &--searchable {
169
- & + label {
170
- padding: 11px 36px;
171
- }
172
-
173
- &:checked + label:after {
174
- left: 41px;
175
- }
176
- }
177
- }
178
127
  }
@@ -69,10 +69,6 @@ $button-margin: 16px;
69
69
  }
70
70
  }
71
71
 
72
- &__content {
73
- padding: $sidebar-padding 0;
74
- }
75
-
76
72
  &__footer {
77
73
  padding: $sidebar-padding;
78
74
  border-top: 1px solid $pix-neutral-20;
@@ -1,5 +1,6 @@
1
1
  .pix-stars {
2
2
  @import 'reset-css';
3
+ line-height: 0;
3
4
 
4
5
  > svg {
5
6
  width: 36px;
@@ -30,14 +31,3 @@
30
31
  fill: $pix-neutral-15;
31
32
  }
32
33
  }
33
-
34
- .sr-only {
35
- position: absolute;
36
- width: 1px;
37
- height: 1px;
38
- padding: 0;
39
- margin: -1px;
40
- overflow: hidden;
41
- clip: rect(0, 0, 0, 0);
42
- border: 0;
43
- }
@@ -1,4 +1,5 @@
1
1
  @import 'pix-design-tokens';
2
+ @import 'a11y';
2
3
  @import 'pix-background-header';
3
4
  @import 'pix-banner';
4
5
  @import 'pix-block';
@@ -0,0 +1,82 @@
1
+ import { modifier } from 'ember-modifier';
2
+
3
+ export default modifier((element, [elementId, callback, isExpanded]) => {
4
+ const elementToTarget = document.getElementById(elementId);
5
+
6
+ element.addEventListener('keydown', handleKeyDown);
7
+
8
+ return () => {
9
+ element.removeEventListener('keydown', handleKeyDown);
10
+ };
11
+
12
+ function handleKeyDown(event) {
13
+ const ARROW_UP_KEY = 'ArrowUp';
14
+ const ARROW_DOWN_KEY = 'ArrowDown';
15
+
16
+ if (![ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {
17
+ return;
18
+ }
19
+ event.preventDefault();
20
+
21
+ const focusElement = () => {
22
+ const focusableElements = findFocusableElements(elementToTarget);
23
+
24
+ const [firstFocusableElement] = focusableElements;
25
+ const lastFocusableElement = focusableElements[focusableElements.length - 1];
26
+
27
+ const activeIndexElement = focusableElements.findIndex((elementToTarget) => {
28
+ return document.activeElement === elementToTarget;
29
+ });
30
+
31
+ const handleArrowDown = () => {
32
+ if (
33
+ !isExpanded ||
34
+ document.activeElement === lastFocusableElement ||
35
+ activeIndexElement === -1
36
+ ) {
37
+ firstFocusableElement.focus();
38
+ } else {
39
+ focusableElements[activeIndexElement + 1].focus();
40
+ }
41
+ };
42
+
43
+ const handleArrowUp = () => {
44
+ if (
45
+ !isExpanded ||
46
+ document.activeElement === firstFocusableElement ||
47
+ activeIndexElement === -1
48
+ ) {
49
+ lastFocusableElement.focus();
50
+ } else {
51
+ focusableElements[activeIndexElement - 1].focus();
52
+ }
53
+ };
54
+
55
+ if (ARROW_UP_KEY === event.key) {
56
+ handleArrowUp();
57
+ } else if (ARROW_DOWN_KEY === event.key) {
58
+ handleArrowDown();
59
+ }
60
+ };
61
+
62
+ if (!isExpanded) {
63
+ elementToTarget.addEventListener('transitionend', focusElement);
64
+
65
+ callback();
66
+
67
+ return () => {
68
+ elementToTarget.removeEventListener('transitionend', focusElement);
69
+ };
70
+ } else {
71
+ focusElement(elementToTarget);
72
+ }
73
+ }
74
+ });
75
+
76
+ function findFocusableElements(element) {
77
+ return [
78
+ ...element.querySelectorAll(
79
+ 'a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])'
80
+ ),
81
+ ].filter((el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'));
82
+ }
@@ -0,0 +1,28 @@
1
+ import { modifier } from 'ember-modifier';
2
+
3
+ export default modifier((element, [callback, focusId = null]) => {
4
+ function handleKeyUp(event) {
5
+ const ENTER_KEY = 'Enter';
6
+
7
+ if (event.key !== ENTER_KEY) {
8
+ return;
9
+ }
10
+
11
+ if (element.type === 'checkbox') {
12
+ element.checked = !element.checked;
13
+ element.dispatchEvent(new Event('change'));
14
+ }
15
+
16
+ if (focusId) {
17
+ document.getElementById(focusId).focus();
18
+ }
19
+
20
+ callback(event);
21
+ }
22
+
23
+ element.addEventListener('keydown', handleKeyUp);
24
+
25
+ return () => {
26
+ element.removeEventListener('keydown', handleKeyUp);
27
+ };
28
+ });
@@ -1,14 +1,18 @@
1
1
  import { modifier } from 'ember-modifier';
2
2
 
3
- export default modifier((element, [callback]) => {
3
+ export default modifier((element, [callback, focusId = null]) => {
4
4
  function handleKeyUp(event) {
5
- const TAB_KEY = 'Escape';
5
+ const ESCAPE_KEY = 'Escape';
6
6
 
7
- if (event.key !== TAB_KEY) {
7
+ if (event.key !== ESCAPE_KEY) {
8
8
  return;
9
9
  }
10
10
 
11
11
  callback(event);
12
+
13
+ if (focusId) {
14
+ document.getElementById(focusId).focus();
15
+ }
12
16
  }
13
17
 
14
18
  element.addEventListener('keyup', handleKeyUp);
@@ -81,12 +81,12 @@ function hasDurationByKey(element, key) {
81
81
  }
82
82
 
83
83
  function handleKeyDown(event, element) {
84
- const TAB_KEY = 9;
84
+ const TAB_KEY = 'Tab';
85
85
  const focusableElements = findFocusableElements(element);
86
86
  const [firstFocusableElement] = focusableElements;
87
87
  const lastFocusableElement = focusableElements[focusableElements.length - 1];
88
88
 
89
- if (event.keyCode !== TAB_KEY) {
89
+ if (event.key !== TAB_KEY) {
90
90
  return;
91
91
  }
92
92
 
@@ -11,7 +11,7 @@ export const form = (args) => {
11
11
  <br>
12
12
 
13
13
  <PixMultiSelect
14
- @title="Votre notation en étoiles..."
14
+ @innerText="Votre notation en étoiles..."
15
15
  @id="form__pix-mutli-select"
16
16
  @label="A quel point aimez vous Pix UI ?"
17
17
  @onSelect={{onSelect}}
@@ -22,6 +22,19 @@ export const form = (args) => {
22
22
  </PixMultiSelect>
23
23
  <br><br>
24
24
 
25
+ <PixMultiSelect
26
+ @innerText="Mes condiements"
27
+ @id="form__pix-mutli-select-searchable"
28
+ @label="Choississez vos condiments"
29
+ @onSelect={{onSelect}}
30
+ @selected={{selected}}
31
+ @isSearchable={{true}}
32
+ @options={{optionsSearch}} as |condiment|
33
+ >
34
+ {{condiment.label}}
35
+ </PixMultiSelect>
36
+ <br><br>
37
+
25
38
  <PixSelect
26
39
  @id="form__searchable-pix-select"
27
40
  @label="Votre fruit préféré est : "
@@ -90,6 +103,13 @@ form.args = {
90
103
  { value: '2', total: 3 },
91
104
  { value: '3', total: 3 },
92
105
  ],
106
+ optionsSearch: [
107
+ { value: 'Cornichon', label: 'Cornichon' },
108
+ { value: 'Ail', label: 'Ail' },
109
+ { value: 'Oignon', label: 'Oignon' },
110
+ { value: 'Aigre-Doux', label: 'Aigre-douc' },
111
+ { value: 'Soja sucré', label: 'Soja sucré' },
112
+ ],
93
113
  cancel: action('cancel'),
94
114
  onSelect: action('onSelect'),
95
115
  selectOptions: [
@@ -5,12 +5,12 @@ export const Template = (args) => {
5
5
  template: hbs`
6
6
  <PixCheckbox
7
7
  @id={{id}}
8
- @label={{label}}
9
8
  @screenReaderOnly={{screenReaderOnly}}
10
9
  @isIndeterminate={{isIndeterminate}}
11
10
  @labelSize={{labelSize}}
12
- @checked={{checked}}
13
- />
11
+ @checked={{checked}}>
12
+ {{label}}
13
+ </PixCheckbox>
14
14
  `,
15
15
  context: args,
16
16
  };
@@ -53,7 +53,6 @@ export const argTypes = {
53
53
  label: {
54
54
  name: 'label',
55
55
  description: "Le label de l'input",
56
- type: { name: 'string', required: false },
57
56
  defaultValue: null,
58
57
  },
59
58
  screenReaderOnly: {
@@ -38,11 +38,12 @@ La PixCheckbox permet de créer des checkbox basiques ou des checkbox mixées (c
38
38
  ```html
39
39
  <PixCheckbox
40
40
  @id="superId"
41
- @label="Recevoir la newsletter"
42
41
  @screenReaderOnly={{false}}
43
42
  @isIndeterminate={{false}}
44
43
  @labelSize="small"
45
- />
44
+ >
45
+ Recevoir la newsletter
46
+ </PixCheckBox>
46
47
  ```
47
48
 
48
49
  ## Arguments
@@ -49,7 +49,7 @@ export const argTypes = {
49
49
  showModal: {
50
50
  name: 'showModal',
51
51
  description: "Gérer l'ouverture de la modale",
52
- type: { name: 'boolean', required: false },
52
+ type: { name: 'boolean', required: true },
53
53
  control: { type: 'boolean' },
54
54
  table: {
55
55
  type: { summary: 'boolean' },
@@ -30,6 +30,7 @@ Ce composant possède deux `yield` :
30
30
  ```html
31
31
  <PixModal
32
32
  @title="Qu'est-ce qu'une modale ?"
33
+ @showModal={{this.showModal}}
33
34
  @onCloseButtonClick={{this.closeModal}}
34
35
  >
35
36
  <:content>
@@ -20,14 +20,18 @@ export const multiSelectWithChildComponent = (args) => {
20
20
  template: hbs`
21
21
  <h4>⚠️ La sélection des éléments ne fonctionne pas dans Storybook.</h4>
22
22
  <PixMultiSelect
23
- @title={{titleStars}}
24
- @id={{id}}
25
- @onSelect={{onSelect}}
26
- @emptyMessage={{emptyMessage}}
27
- @label={{label}}
28
- @options={{options}} as |star|
23
+ @id={{this.id}}
24
+ @label={{this.label}}
25
+ @innerText={{this.titleStars}}
26
+ @screenReaderOnly={{this.screenReaderOnly}}
27
+
28
+ @onSelect={{this.onSelect}}
29
+ @emptyMessage={{this.emptyMessage}}
30
+
31
+ @options={{this.options}} as |star|
29
32
  >
30
33
  <PixStars
34
+ @alt={{concat "Étoiles " star.value " sur " star.total}}
31
35
  @count={{star.value}}
32
36
  @total={{star.total}}
33
37
  />
@@ -39,7 +43,9 @@ export const multiSelectWithChildComponent = (args) => {
39
43
 
40
44
  multiSelectWithChildComponent.args = {
41
45
  titleStars: 'Sélectionner le niveau souhaité',
46
+ label: 'Résultat évaluation',
42
47
  options: [
48
+ { value: '0', total: 3 },
43
49
  { value: '1', total: 3 },
44
50
  { value: '2', total: 3 },
45
51
  { value: '3', total: 3 },
@@ -52,17 +58,19 @@ export const multiSelectSearchable = (args) => {
52
58
  <h4>⚠️ La sélection des éléments ne fonctionne pas dans Storybook.</h4>
53
59
  <PixMultiSelect
54
60
  style="width:350px"
55
- @id={{id}}
56
- @title={{title}}
57
- @placeholder={{placeholder}}
58
- @isSearchable={{isSearchable}}
59
- @showOptionsOnInput={{showOptionsOnInput}}
60
- @strictSearch={{strictSearch}}
61
- @onSelect={{doSomething}}
62
- @emptyMessage={{emptyMessage}}
63
- @size={{size}}
64
- @selected={{selected}}
65
- @options={{options}} as |option|
61
+ @id={{this.id}}
62
+ @label={{this.label}}
63
+ @screenReaderOnly={{this.screenReaderOnly}}
64
+ @innerText={{this.innerText}}
65
+
66
+ @isSearchable={{this.isSearchable}}
67
+ @strictSearch={{this.strictSearch}}
68
+
69
+ @onSelect={{this.doSomething}}
70
+ @selected={{this.selected}}
71
+
72
+ @emptyMessage={{this.emptyMessage}}
73
+ @options={{this.options}} as |option|
66
74
  >
67
75
  {{option.label}}
68
76
  </PixMultiSelect>
@@ -78,17 +86,19 @@ export const multiSelectAsyncOptions = (args) => {
78
86
  <h4>⚠️ La sélection des éléments ne fonctionne pas dans Storybook.</h4>
79
87
  <PixMultiSelect
80
88
  style="width:350px"
81
- @id={{id}}
82
- @title={{title}}
83
- @placeholder={{placeholder}}
84
- @isSearchable={{isSearchable}}
85
- @showOptionsOnInput={{showOptionsOnInput}}
86
- @strictSearch={{strictSearch}}
87
- @onSelect={{doSomething}}
88
- @emptyMessage={{emptyMessage}}
89
- @size={{size}}
90
- @selected={{selected}}
91
- @onLoadOptions={{onLoadOptions}} as |option|
89
+ @id={{this.id}}
90
+ @label={{this.label}}
91
+ @screenReaderOnly={{this.screenReaderOnly}}
92
+ @innerText={{this.innerText}}
93
+
94
+ @isSearchable={{this.isSearchable}}
95
+ @strictSearch={{this.strictSearch}}
96
+
97
+ @onSelect={{this.doSomething}}
98
+ @selected={{this.selected}}
99
+
100
+ @emptyMessage={{this.emptyMessage}}
101
+ @onLoadOptions={{this.onLoadOptions}} as |option|
92
102
  >
93
103
  {{option.label}}
94
104
  </PixMultiSelect>
@@ -104,20 +114,18 @@ export const argTypes = {
104
114
  type: { name: 'string', required: true },
105
115
  defaultValue: 'aromate',
106
116
  },
107
- title: {
108
- name: 'title',
109
- description: 'Donne un titre à sa liste de choix multiple.',
117
+ innerText: {
118
+ name: 'innerText',
119
+ description:
120
+ 'Donne un titre à sa liste de choix multiple, utilisé comme placeholder lorsque ``isSearchable`` à ``true``',
110
121
  type: { name: 'string', required: true },
111
122
  defaultValue: 'Rechercher un condiment',
112
123
  },
113
124
  label: {
114
125
  name: 'label',
115
- description: 'Donne un label au champ, le paramètre @id devient obligatoire avec ce paramètre.',
116
- type: { name: 'string', required: false },
117
- table: {
118
- type: { summary: 'string' },
119
- defaultValue: { summary: null },
120
- },
126
+ description: 'Donne un label au champ',
127
+ type: { name: 'string', required: true },
128
+ defaultValue: 'Label du champ',
121
129
  },
122
130
  emptyMessage: {
123
131
  name: 'emptyMessage',
@@ -133,13 +141,6 @@ export const argTypes = {
133
141
  type: { name: 'string', required: false },
134
142
  defaultValue: 'Chargement...',
135
143
  },
136
- placeholder: {
137
- name: 'placeholder',
138
- description:
139
- 'Donner une liste d‘exemple pour la recherche utilisateur dans le cas ``isSearchable`` à ``true``',
140
- type: { name: 'string', required: true },
141
- defaultValue: 'Curcuma, Thym, ...',
142
- },
143
144
  options: {
144
145
  name: 'options',
145
146
  description:
@@ -165,13 +166,6 @@ export const argTypes = {
165
166
  type: { name: 'array', required: false },
166
167
  defaultValue: ['1', '4'],
167
168
  },
168
- showOptionsOnInput: {
169
- name: 'showOptionsOnInput',
170
- description:
171
- 'Afficher la liste au focus du champs de saisie lorsque ``isSearchable`` à ``true``',
172
- type: { name: 'boolean', required: false },
173
- defaultValue: true,
174
- },
175
169
  isSearchable: {
176
170
  name: 'isSearchable',
177
171
  description: 'Permet de rajouter une saisie utilisateur pour faciliter la recherche',
@@ -185,15 +179,10 @@ export const argTypes = {
185
179
  type: { name: 'boolean', required: false },
186
180
  defaultValue: false,
187
181
  },
188
- size: {
189
- name: 'size',
190
- description:
191
- '⚠️ **Propriété dépréciée** ⚠️ , désormais tous les éléments de formulaires feront 36px de hauteur.',
192
- options: ['big', 'small'],
193
- type: { name: 'string', required: false },
194
- table: {
195
- type: { summary: 'string' },
196
- defaultValue: { summary: 'small' },
197
- },
182
+ screenReaderOnly: {
183
+ name: 'screenReaderOnly',
184
+ description: "Permet de cacher à l'écran le label tout en restant vocalisable",
185
+ type: { name: 'boolean', required: false },
186
+ defaultValue: false,
198
187
  },
199
188
  };
@@ -40,11 +40,14 @@ Il est possible de donner un message via `loadingMessage` à afficher à la plac
40
40
 
41
41
  ```html
42
42
  <PixMultiSelect
43
- @title={{title}}
44
- @emptyMessage={{emptyMessage}}
45
43
  @id={{id}}
44
+ @label={{label}}
45
+ @screenReaderOnly={{screenReaderOnly}}
46
+ @innerText={{innerText}}
47
+
46
48
  @onSelect={{doSomething}}
47
49
  @selected={{selected}}
50
+ @emptyMessage={{emptyMessage}}
48
51
  @options={{options}} as |option|>
49
52
  {{option.label}}
50
53
  </PixMultiSelect>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1024pix/pix-ui",
3
- "version": "18.2.0",
3
+ "version": "19.0.1",
4
4
  "description": "Pix-UI is the implementation of Pix design principles and guidelines for its products.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -72,6 +72,7 @@
72
72
  "@storybook/ember-cli-storybook": "^0.4.0",
73
73
  "@storybook/storybook-deployer": "^2.8.10",
74
74
  "@storybook/theming": "^6.3.7",
75
+ "@testing-library/user-event": "^14.4.3",
75
76
  "axios": "^0.21.1",
76
77
  "babel-eslint": "^10.1.0",
77
78
  "babel-plugin-ember-modules-api-polyfill": "^2.13.4",