@1024pix/pix-ui 13.0.0 → 13.3.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 +34 -4
- package/addon/components/pix-block.hbs +1 -1
- package/addon/components/pix-collapsible.hbs +4 -4
- package/addon/components/pix-dropdown.hbs +102 -0
- package/addon/components/pix-dropdown.js +181 -0
- package/addon/components/pix-icon-button.hbs +1 -1
- package/addon/components/pix-input-password.hbs +3 -3
- package/addon/components/pix-input.hbs +1 -1
- package/addon/components/pix-message.hbs +1 -1
- package/addon/components/pix-multi-select.hbs +5 -5
- package/addon/components/pix-progress-gauge.hbs +1 -1
- package/addon/components/pix-radio-button.hbs +1 -1
- package/addon/components/pix-select.hbs +2 -2
- package/addon/components/pix-selectable-tag.hbs +1 -1
- package/addon/components/pix-textarea.hbs +1 -1
- package/addon/components/pix-tooltip-deprecated.hbs +3 -3
- package/addon/components/pix-tooltip.hbs +12 -10
- package/addon/components/pix-tooltip.js +4 -0
- package/addon/styles/_form.scss +7 -0
- package/addon/styles/_pix-dropdown.scss +157 -0
- package/addon/styles/_reset-css.scss +0 -6
- package/addon/styles/addon.scss +1 -0
- package/app/components/pix-dropdown.js +1 -0
- package/app/stories/form.stories.js +13 -0
- package/app/stories/pix-dropdown.stories.js +191 -0
- package/app/stories/pix-dropdown.stories.mdx +70 -0
- package/app/stories/pix-tooltip.stories.js +15 -1
- package/app/stories/pix-tooltip.stories.mdx +19 -0
- package/docs/adr/0001-utiliser-les-balises-html-natives.md +53 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Pix-UI Changelog
|
|
2
2
|
|
|
3
|
+
## v13.3.1 (22/02/2022)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### :bug: Bug fix
|
|
7
|
+
- [#201](https://github.com/1024pix/pix-ui/pull/201) [BUGFIX] Corriger la couleur de la sélection et l'erreur d'accessibilité.
|
|
8
|
+
|
|
9
|
+
## v13.3.0 (21/02/2022)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### :rocket: Enhancement
|
|
13
|
+
- [#199](https://github.com/1024pix/pix-ui/pull/199) [FEATURE] Permettre de rendre obligatoire le PixDropdown
|
|
14
|
+
|
|
15
|
+
## v13.2.0 (17/02/2022)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### :rocket: Enhancement
|
|
19
|
+
- [#193](https://github.com/1024pix/pix-ui/pull/193) [FEATURE] Ajouter le composant dropdown (PIX-4295).
|
|
20
|
+
|
|
21
|
+
## v13.1.0 (16/02/2022)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### :building_construction: Tech
|
|
25
|
+
- [#194](https://github.com/1024pix/pix-ui/pull/194) [TECH] Mise a jour du package-lock.
|
|
26
|
+
- [#192](https://github.com/1024pix/pix-ui/pull/192) [TECH] Améliorer le blueprint pour auto-générer les nouveaux composants.
|
|
27
|
+
|
|
28
|
+
### :coffee: Various
|
|
29
|
+
- [#198](https://github.com/1024pix/pix-ui/pull/198) [FEAT] Permettre à la tooltip de ne pas s'afficher (PIX-4375)
|
|
30
|
+
- [#196](https://github.com/1024pix/pix-ui/pull/196) [FIX] Corriger les tests qui bloquent la pipeline.
|
|
31
|
+
- [#183](https://github.com/1024pix/pix-ui/pull/183) [DOC] Utiliser les champs de saisie natifs HTML plutôt que ceux d'Ember.
|
|
32
|
+
|
|
3
33
|
## v13.0.0 (27/01/2022)
|
|
4
34
|
|
|
5
35
|
|
|
@@ -13,7 +43,7 @@
|
|
|
13
43
|
- [#94](https://github.com/1024pix/pix-ui/pull/94) [FEATURE] Ajout du composant PixModal
|
|
14
44
|
|
|
15
45
|
### :bug: Bug fix
|
|
16
|
-
- [#186](https://github.com/1024pix/pix-ui/pull/186) [
|
|
46
|
+
- [#186](https://github.com/1024pix/pix-ui/pull/186) [BREAKING_CHANGES] Désactiver l'autocomplete sur le composant Pix Select (PIX-4158)
|
|
17
47
|
- [#187](https://github.com/1024pix/pix-ui/pull/187) [BUGFIX] Rendre tout le tag sélectionnable (PIX-4179)
|
|
18
48
|
|
|
19
49
|
## v11.2.0 (14/01/2022)
|
|
@@ -32,7 +62,7 @@
|
|
|
32
62
|
|
|
33
63
|
|
|
34
64
|
### :building_construction: Tech
|
|
35
|
-
- [#175](https://github.com/1024pix/pix-ui/pull/175) [
|
|
65
|
+
- [#175](https://github.com/1024pix/pix-ui/pull/175) [BREAKING_CHANGES] Faire en sorte que le message d'erreur soit situé à l'intérieur de l'élément qui le compose (PIX-3829)
|
|
36
66
|
|
|
37
67
|
## v11.0.1 (08/12/2021)
|
|
38
68
|
|
|
@@ -50,12 +80,12 @@
|
|
|
50
80
|
- [#177](https://github.com/1024pix/pix-ui/pull/177) [FEATURE] Création du composant Tag sélectionnable dans Pix UI (PIX-3757)
|
|
51
81
|
|
|
52
82
|
### :building_construction: Tech
|
|
53
|
-
- [#180](https://github.com/1024pix/pix-ui/pull/180) [
|
|
83
|
+
- [#180](https://github.com/1024pix/pix-ui/pull/180) [BREAKING_CHANGES] Ajouter la possibilité d'utiliser des composants Ember à l'intérieur de la tooltip (Pix-3925)
|
|
54
84
|
- [#179](https://github.com/1024pix/pix-ui/pull/179) [TECH] Mise à jour du template de pull request pour faire apparaître plus clairement les BREAKING_CHANGES
|
|
55
85
|
- [#148](https://github.com/1024pix/pix-ui/pull/148) [TECH] Formatter les fichiers avec prettier (PIX-3469)
|
|
56
86
|
|
|
57
87
|
### :bug: Bug fix
|
|
58
|
-
- [#147](https://github.com/1024pix/pix-ui/pull/147) [
|
|
88
|
+
- [#147](https://github.com/1024pix/pix-ui/pull/147) [BREAKING_CHANGES] Ajout de l'évènement onChange afin de supprimer le message d'erreur lorsque l'utilisateur modifie sa saisie (PIX-3476)
|
|
59
89
|
|
|
60
90
|
### :coffee: Various
|
|
61
91
|
- [#178](https://github.com/1024pix/pix-ui/pull/178) [DOC] Améliorer l'information sur les breaking changes dans Pix UI
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
<div class="pix-collapsible {{if this.isUnCollapsed
|
|
1
|
+
<div class="pix-collapsible {{if this.isUnCollapsed 'pix-collapsible--uncollapsed'}}">
|
|
2
2
|
|
|
3
3
|
<button
|
|
4
4
|
type="button"
|
|
5
5
|
{{on "click" this.toggleCollapsible}}
|
|
6
|
-
class="pix-collapsible__title {{if this.isUnCollapsed
|
|
6
|
+
class="pix-collapsible__title {{if this.isUnCollapsed 'pix-collapsible__title--uncollapsed'}}"
|
|
7
7
|
aria-controls={{this.contentId}}
|
|
8
8
|
aria-expanded={{if this.isUnCollapsed "true" "false"}}
|
|
9
9
|
...attributes
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
{{this.title}}
|
|
16
16
|
</span>
|
|
17
17
|
|
|
18
|
-
<FaIcon @icon="{{if this.isCollapsed
|
|
18
|
+
<FaIcon @icon="{{if this.isCollapsed 'plus' 'minus'}}" class="pix-collapsible-title__icon" />
|
|
19
19
|
</button>
|
|
20
20
|
|
|
21
21
|
<div
|
|
22
22
|
id={{this.contentId}}
|
|
23
23
|
aria-hidden={{if this.isCollapsed "true" "false"}}
|
|
24
24
|
class="pix-collapsible__content
|
|
25
|
-
{{if this.isUnCollapsed
|
|
25
|
+
{{if this.isUnCollapsed ' pix-collapsible__content--uncollapse'}}"
|
|
26
26
|
>
|
|
27
27
|
{{yield}}
|
|
28
28
|
</div>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<div class="pix-dropdown">
|
|
2
|
+
|
|
3
|
+
{{#if this.label}}
|
|
4
|
+
<label id={{@id}} class="pix-dropdown__label">
|
|
5
|
+
{{#if @requiredLabel}}
|
|
6
|
+
<abbr title={{@requiredLabel}} class="mandatory-mark" aria-hidden="true">*</abbr>
|
|
7
|
+
{{/if}}
|
|
8
|
+
{{this.label}}
|
|
9
|
+
</label>
|
|
10
|
+
{{/if}}
|
|
11
|
+
|
|
12
|
+
<div class="pix-dropdown__controller">
|
|
13
|
+
<button
|
|
14
|
+
type="button"
|
|
15
|
+
class="pix-dropdown__controller--placeholder{{if this.isExpanded ' expanded'}}"
|
|
16
|
+
aria-label={{if this.isExpanded @collapseLabel @expandLabel}}
|
|
17
|
+
{{on "click" this.toggleDropdown}}
|
|
18
|
+
{{on "keyup" this.navigateThroughOptions}}
|
|
19
|
+
...attributes
|
|
20
|
+
>
|
|
21
|
+
<p
|
|
22
|
+
title={{this.placeholder}}
|
|
23
|
+
class="pix-dropdown__controller--placeholder-text
|
|
24
|
+
{{unless this.hasSelectedOption ' default'}}"
|
|
25
|
+
aria-required={{if this.requiredLabel true false}}
|
|
26
|
+
>{{this.placeholder}}</p>
|
|
27
|
+
</button>
|
|
28
|
+
|
|
29
|
+
{{#if this.hasSelectedOption}}
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
class="pix-dropdown__controller--clear"
|
|
33
|
+
aria-label={{@clearLabel}}
|
|
34
|
+
{{on "click" this.clearSelection}}
|
|
35
|
+
>
|
|
36
|
+
<FaIcon @icon="times" />
|
|
37
|
+
</button>
|
|
38
|
+
{{/if}}
|
|
39
|
+
|
|
40
|
+
<div
|
|
41
|
+
role="button"
|
|
42
|
+
tabindex="-1"
|
|
43
|
+
class="pix-dropdown__controller--chevron"
|
|
44
|
+
{{on "click" this.toggleDropdown}}
|
|
45
|
+
>
|
|
46
|
+
<FaIcon @icon={{if this.isExpanded "chevron-up" "chevron-down"}} />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div
|
|
51
|
+
role="listbox"
|
|
52
|
+
id={{this.menuId}}
|
|
53
|
+
tabindex="-1"
|
|
54
|
+
class="pix-dropdown__menu{{if this.isExpanded ' expanded'}}"
|
|
55
|
+
aria-labelledby={{if this.label @id}}
|
|
56
|
+
aria-label={{unless this.label @placeholder}}
|
|
57
|
+
aria-hidden={{if this.isExpanded "false" "true"}}
|
|
58
|
+
aria-activedescendant={{if this.selectedOption this.selectedOption.value}}
|
|
59
|
+
{{on "keyup" this.navigateThroughOptions}}
|
|
60
|
+
{{on "scroll" this.incrementPage}}
|
|
61
|
+
>
|
|
62
|
+
|
|
63
|
+
{{#if @isSearchable}}
|
|
64
|
+
<div class="pix-dropdown__menu--search">
|
|
65
|
+
<FaIcon class="pix-dropdown__menu--search-icon" @icon="search" />
|
|
66
|
+
<label
|
|
67
|
+
class="pix-dropdown__menu--search-input-label"
|
|
68
|
+
for={{this.searchInputId}}
|
|
69
|
+
>{{@searchPlaceholder}}</label>
|
|
70
|
+
<input
|
|
71
|
+
class="pix-dropdown__menu--search-input"
|
|
72
|
+
id={{this.searchInputId}}
|
|
73
|
+
autocomplete="off"
|
|
74
|
+
tabindex={{unless this.isExpanded "-1"}}
|
|
75
|
+
placeholder={{@searchPlaceholder}}
|
|
76
|
+
{{on "input" this.filterOptions}}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
{{/if}}
|
|
80
|
+
|
|
81
|
+
{{#each this.options as |opt index|}}
|
|
82
|
+
{{#if (lt index this.showLimit)}}
|
|
83
|
+
<div
|
|
84
|
+
role="option"
|
|
85
|
+
id={{opt.value}}
|
|
86
|
+
tabindex="-1"
|
|
87
|
+
class="pix-dropdown__menu--option{{if
|
|
88
|
+
(eq this.selectedOption.value opt.value)
|
|
89
|
+
' selected'
|
|
90
|
+
}}"
|
|
91
|
+
aria-selected={{if (eq this.selectedOption.value opt.value) "true"}}
|
|
92
|
+
{{on "click" (fn this.selectOption opt)}}
|
|
93
|
+
{{on "mouseover" this.resetPreviouslyFocused}}
|
|
94
|
+
{{on "keypress" (fn this.onOptionKeyPress opt)}}
|
|
95
|
+
>
|
|
96
|
+
{{opt.label}}
|
|
97
|
+
</div>
|
|
98
|
+
{{/if}}
|
|
99
|
+
{{/each}}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
</div>
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
|
|
5
|
+
export default class PixDropdown extends Component {
|
|
6
|
+
defaultId = Math.floor(Math.random() * 100);
|
|
7
|
+
focusedOption = null;
|
|
8
|
+
focusedIndex = null;
|
|
9
|
+
@tracked page = 1;
|
|
10
|
+
@tracked selectedOption = null;
|
|
11
|
+
@tracked isExpanded = false;
|
|
12
|
+
@tracked options = this.args.options;
|
|
13
|
+
@tracked menuId = `menu-${this.defaultId}`;
|
|
14
|
+
@tracked searchInputId = `search-input-${this.defaultId}`;
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
|
|
19
|
+
if (!this.args.label && !this.args.placeholder) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'ERROR in PixDropdown component, you need to provide a label and/or a placeholder'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (this.args.selectedOption) {
|
|
26
|
+
this.selectedOption = this.args.options.find(
|
|
27
|
+
({ value }) => value === this.args.selectedOption
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get hasSelectedOption() {
|
|
33
|
+
return !!this.selectedOption;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get placeholder() {
|
|
37
|
+
return this.selectedOption?.label || this.args.placeholder;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get label() {
|
|
41
|
+
const labelIsDefined = this.args.label?.trim();
|
|
42
|
+
const idIsNotDefined = !this.args.id?.trim();
|
|
43
|
+
|
|
44
|
+
if (labelIsDefined && idIsNotDefined) {
|
|
45
|
+
throw new Error('ERROR in PixDropdown component, @id param is necessary when giving @label');
|
|
46
|
+
}
|
|
47
|
+
return this.args.label || null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get showLimit() {
|
|
51
|
+
if (!this.args.pageSize) return this.options.length;
|
|
52
|
+
|
|
53
|
+
const numberOfOptionsAccordingToPage = this.args.pageSize * this.page;
|
|
54
|
+
return Math.min(this.options.length, numberOfOptionsAccordingToPage);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@action
|
|
58
|
+
resetPreviouslyFocused() {
|
|
59
|
+
if (!this.focusedOption) return;
|
|
60
|
+
|
|
61
|
+
const previouslyFocusedElement = document.getElementById(this.focusedOption.value);
|
|
62
|
+
previouslyFocusedElement?.blur();
|
|
63
|
+
this.focusedOption = undefined;
|
|
64
|
+
this.focusedIndex = undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_navigateToOption(option, index) {
|
|
68
|
+
if (!option) return;
|
|
69
|
+
|
|
70
|
+
this.resetPreviouslyFocused();
|
|
71
|
+
this.focusedOption = option;
|
|
72
|
+
this.focusedIndex = index;
|
|
73
|
+
const focusedElement = document.getElementById(option.value);
|
|
74
|
+
focusedElement?.scrollIntoView({ block: 'nearest' });
|
|
75
|
+
focusedElement?.focus();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_computeNextIndex(isArrowDown, index) {
|
|
79
|
+
const length = this.showLimit;
|
|
80
|
+
let nextIndex;
|
|
81
|
+
if (index === undefined || index === null) {
|
|
82
|
+
nextIndex = isArrowDown ? 0 : length - 1;
|
|
83
|
+
} else {
|
|
84
|
+
const delta = isArrowDown ? 1 : -1;
|
|
85
|
+
nextIndex = index + delta;
|
|
86
|
+
}
|
|
87
|
+
// navigate through array in a circle
|
|
88
|
+
// (i.e. go back to first after last or last after first)
|
|
89
|
+
return ((nextIndex % length) + length) % length;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@action
|
|
93
|
+
navigateThroughOptions(event) {
|
|
94
|
+
const isArrowDown = event.code === 'ArrowDown';
|
|
95
|
+
if (event.code !== 'ArrowUp' && !isArrowDown) return;
|
|
96
|
+
const nextIndex = this._computeNextIndex(isArrowDown, this.focusedIndex);
|
|
97
|
+
this._navigateToOption(this.options[nextIndex], nextIndex);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@action
|
|
101
|
+
toggleDropdown() {
|
|
102
|
+
this.isExpanded = !this.isExpanded;
|
|
103
|
+
|
|
104
|
+
if (this.isExpanded) {
|
|
105
|
+
this.resetPreviouslyFocused();
|
|
106
|
+
const searchInput = document.getElementById(this.searchInputId);
|
|
107
|
+
if (searchInput) {
|
|
108
|
+
searchInput.focus();
|
|
109
|
+
} else {
|
|
110
|
+
this._navigateToOption(this.options[0], 0);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const menu = document.getElementById(this.menuId);
|
|
115
|
+
menu.scrollTop = 0;
|
|
116
|
+
this.page = 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@action
|
|
120
|
+
selectOption(option) {
|
|
121
|
+
if (this.selectedOption?.value !== option.value) {
|
|
122
|
+
this.args.onSelect?.(option.value);
|
|
123
|
+
this.selectedOption = option;
|
|
124
|
+
}
|
|
125
|
+
this.isExpanded = false;
|
|
126
|
+
|
|
127
|
+
const searchInput = document.getElementById(this.searchInputId);
|
|
128
|
+
if (searchInput) {
|
|
129
|
+
searchInput.value = '';
|
|
130
|
+
this.options = this.args.options;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@action
|
|
135
|
+
onOptionKeyPress(option, event) {
|
|
136
|
+
if (event.code === 'Enter' || event.code === 'Space') this.selectOption(option);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@action
|
|
140
|
+
clearSelection(event) {
|
|
141
|
+
event.stopPropagation();
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
this.selectedOption = null;
|
|
144
|
+
this._navigateToOption(this.options[0], 0);
|
|
145
|
+
this.args.onSelect?.();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@action
|
|
149
|
+
onClearKeyPress(event) {
|
|
150
|
+
if (event.code === 'Enter' || event.code === 'Space') this.clearSelection(event);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@action
|
|
154
|
+
filterOptions(event) {
|
|
155
|
+
this.resetPreviouslyFocused();
|
|
156
|
+
const filterValue = event.target?.value.toLowerCase();
|
|
157
|
+
this.page = 1;
|
|
158
|
+
this.options = filterValue
|
|
159
|
+
? this.args.options.filter(({ label }) => label.toLowerCase().includes(filterValue))
|
|
160
|
+
: this.args.options;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
_isLastOptionVisible(option) {
|
|
164
|
+
const lastVisibleOption = document.getElementById(option.value);
|
|
165
|
+
const menu = document.getElementById(this.menuId);
|
|
166
|
+
|
|
167
|
+
const { bottom, height, top } = lastVisibleOption.getBoundingClientRect();
|
|
168
|
+
const menuRect = menu.getBoundingClientRect();
|
|
169
|
+
|
|
170
|
+
return top <= menuRect.top ? menuRect.top - top <= height : bottom - menuRect.bottom <= height;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@action
|
|
174
|
+
incrementPage() {
|
|
175
|
+
if (!this.args.pageSize) return;
|
|
176
|
+
|
|
177
|
+
if (this._isLastOptionVisible(this.options[this.showLimit - 1])) {
|
|
178
|
+
this.page += 1;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
aria-label={{this.ariaLabel}}
|
|
4
4
|
class="pix-icon-button pix-icon-button--{{this.size}}
|
|
5
5
|
pix-icon-button--{{this.color}}
|
|
6
|
-
{{if @withBackground
|
|
6
|
+
{{if @withBackground ' pix-icon-button--background'}}"
|
|
7
7
|
{{on "click" this.triggerAction}}
|
|
8
8
|
...attributes
|
|
9
9
|
>
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
{{/if}}
|
|
8
8
|
<div
|
|
9
9
|
class="pix-input-password__container
|
|
10
|
-
{{if @errorMessage
|
|
11
|
-
{{if @prefix
|
|
10
|
+
{{if @errorMessage 'pix-input-password__error-container'}}
|
|
11
|
+
{{if @prefix 'pix-input-password__with-prefix'}}"
|
|
12
12
|
>
|
|
13
13
|
|
|
14
14
|
{{#if @prefix}}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
/>
|
|
25
25
|
|
|
26
26
|
<PixIconButton
|
|
27
|
-
class="pix-input-password__button {{if @errorMessage
|
|
27
|
+
class="pix-input-password__button {{if @errorMessage ' pix-input-password__error-button'}}"
|
|
28
28
|
@icon={{if this.isPasswordVisible "eye" "eye-slash"}}
|
|
29
29
|
@ariaLabel={{if this.isPasswordVisible "Masquer le mot de passe" "Afficher le mot de passe"}}
|
|
30
30
|
@triggerAction={{this.togglePasswordVisibility}}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
7
|
{{#if @isSearchable}}
|
|
8
|
-
<label class="pix-multi-select-header {{if this.isBig
|
|
8
|
+
<label class="pix-multi-select-header {{if this.isBig 'pix-multi-select-header--big'}}">
|
|
9
9
|
|
|
10
10
|
<span class="pix-multi-select-header__title">{{@title}}</span>
|
|
11
11
|
<FaIcon @icon="search" class="pix-multi-select-header__search-icon" />
|
|
@@ -26,25 +26,25 @@
|
|
|
26
26
|
<button
|
|
27
27
|
id={{@id}}
|
|
28
28
|
type="button"
|
|
29
|
-
class="pix-multi-select-header {{if this.isBig
|
|
29
|
+
class="pix-multi-select-header {{if this.isBig 'pix-multi-select-header--big'}}"
|
|
30
30
|
{{on "click" this.toggleDropDown}}
|
|
31
31
|
>
|
|
32
32
|
{{@title}}
|
|
33
33
|
<FaIcon
|
|
34
34
|
class="pix-multi-select-header__dropdown-icon
|
|
35
|
-
{{if this.isExpanded
|
|
35
|
+
{{if this.isExpanded ' pix-multi-select-header__dropdown-icon--expand'}}"
|
|
36
36
|
@icon={{if this.isExpanded "chevron-up" "chevron-down"}}
|
|
37
37
|
/>
|
|
38
38
|
</button>
|
|
39
39
|
{{/if}}
|
|
40
40
|
|
|
41
|
-
<ul class="pix-multi-select-list {{unless this.isExpanded
|
|
41
|
+
<ul class="pix-multi-select-list {{unless this.isExpanded 'pix-multi-select-list--hidden'}}">
|
|
42
42
|
{{#if (gt this.results.length 0)}}
|
|
43
43
|
{{#each this.results as |option|}}
|
|
44
44
|
<li class="pix-multi-select-list__item">
|
|
45
45
|
<input
|
|
46
46
|
class="pix-multi-select-list__checkbox
|
|
47
|
-
{{if @isSearchable
|
|
47
|
+
{{if @isSearchable ' pix-multi-select-list__checkbox--searchable'}}"
|
|
48
48
|
type="checkbox"
|
|
49
49
|
checked={{option.checked}}
|
|
50
50
|
id={{concat @id "-" option.value}}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
id={{@id}}
|
|
11
11
|
list="{{this.datalistId}}"
|
|
12
12
|
{{on "input" this.onChange}}
|
|
13
|
-
class="{{if this.isValid
|
|
13
|
+
class="{{if this.isValid 'pix-select--is-valid'}} {{if this.isBig 'pix-select--big'}}"
|
|
14
14
|
...attributes
|
|
15
15
|
autocomplete="off"
|
|
16
16
|
/>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
<select
|
|
27
27
|
id={{@id}}
|
|
28
|
-
class="{{if this.isBig
|
|
28
|
+
class="{{if this.isBig 'pix-select--big'}}"
|
|
29
29
|
{{on "change" this.onChange}}
|
|
30
30
|
...attributes
|
|
31
31
|
>
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
id={{@id}}
|
|
8
8
|
role="tooltip"
|
|
9
9
|
class="pix-tooltip__content pix-tooltip__content--{{this.position}}
|
|
10
|
-
{{if @isInline
|
|
11
|
-
{{if @isLight
|
|
12
|
-
{{if @isWide
|
|
10
|
+
{{if @isInline 'pix-tooltip__content--inline'}}
|
|
11
|
+
{{if @isLight 'pix-tooltip__content--light'}}
|
|
12
|
+
{{if @isWide 'pix-tooltip__content--wide'}}"
|
|
13
13
|
>
|
|
14
14
|
{{this.text}}
|
|
15
15
|
</span>
|
|
@@ -4,15 +4,17 @@
|
|
|
4
4
|
{{/if}}
|
|
5
5
|
|
|
6
6
|
{{#if (has-block "tooltip")}}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
{{#if this.display}}
|
|
8
|
+
<span
|
|
9
|
+
id={{@id}}
|
|
10
|
+
role="tooltip"
|
|
11
|
+
class="pix-tooltip__content pix-tooltip__content--{{this.position}}
|
|
12
|
+
{{if @isInline 'pix-tooltip__content--inline'}}
|
|
13
|
+
{{if @isLight 'pix-tooltip__content--light'}}
|
|
14
|
+
{{if @isWide 'pix-tooltip__content--wide'}}"
|
|
15
|
+
>
|
|
16
|
+
{{yield to="tooltip"}}
|
|
17
|
+
</span>
|
|
18
|
+
{{/if}}
|
|
17
19
|
{{/if}}
|
|
18
20
|
</span>
|
package/addon/styles/_form.scss
CHANGED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
.pix-dropdown {
|
|
2
|
+
@import 'reset-css';
|
|
3
|
+
width: 100%;
|
|
4
|
+
position: relative;
|
|
5
|
+
|
|
6
|
+
button {
|
|
7
|
+
margin: 0;
|
|
8
|
+
text-align: left;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&__label {
|
|
13
|
+
@include label();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&__controller {
|
|
17
|
+
width: 100%;
|
|
18
|
+
position: relative;
|
|
19
|
+
|
|
20
|
+
&--placeholder {
|
|
21
|
+
@include hoverFormElement();
|
|
22
|
+
@include focusFormElement();
|
|
23
|
+
width: 100%;
|
|
24
|
+
min-width: 250px;
|
|
25
|
+
min-height: 34px;
|
|
26
|
+
border: 1px solid $grey-40;
|
|
27
|
+
border-radius: $spacing-xxs;
|
|
28
|
+
padding: 0;
|
|
29
|
+
background: $white;
|
|
30
|
+
|
|
31
|
+
&.expanded {
|
|
32
|
+
border-bottom-left-radius: 0;
|
|
33
|
+
border-bottom-right-radius: 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&--placeholder-text {
|
|
38
|
+
color: $grey-90;
|
|
39
|
+
margin: 0;
|
|
40
|
+
font-size: 0.875rem;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
text-overflow: ellipsis;
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
padding: 8px 68px 8px 16px;
|
|
45
|
+
|
|
46
|
+
&.default {
|
|
47
|
+
color: $grey-60;
|
|
48
|
+
padding-right: 36px;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&--clear {
|
|
53
|
+
background: transparent;
|
|
54
|
+
font-size: 1rem;
|
|
55
|
+
color: $grey-50;
|
|
56
|
+
position: absolute;
|
|
57
|
+
padding: 8px 8px 6px;
|
|
58
|
+
border: none;
|
|
59
|
+
border-right: 2px solid $grey-40;
|
|
60
|
+
right: 38px;
|
|
61
|
+
top: 1px;
|
|
62
|
+
width: fit-content;
|
|
63
|
+
|
|
64
|
+
&:hover {
|
|
65
|
+
color: $grey-70;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&--chevron {
|
|
70
|
+
background: transparent;
|
|
71
|
+
border: none;
|
|
72
|
+
padding: 0;
|
|
73
|
+
position: absolute;
|
|
74
|
+
top: 9px;
|
|
75
|
+
right: 12px;
|
|
76
|
+
color: $grey-50;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
|
|
79
|
+
&:hover {
|
|
80
|
+
color: $grey-70;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&__menu {
|
|
86
|
+
background: white;
|
|
87
|
+
position: absolute;
|
|
88
|
+
top: 100%;
|
|
89
|
+
width: 100%;
|
|
90
|
+
max-height: 0;
|
|
91
|
+
transition: max-height ease-in 0.5s;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
overflow-y: auto;
|
|
94
|
+
z-index: 1;
|
|
95
|
+
|
|
96
|
+
&.expanded {
|
|
97
|
+
max-height: 300px;
|
|
98
|
+
border: 1px solid $grey-40;
|
|
99
|
+
border-top: none;
|
|
100
|
+
border-radius: 0 0 $spacing-xxs $spacing-xxs;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&--search {
|
|
104
|
+
display: flex;
|
|
105
|
+
position: relative;
|
|
106
|
+
|
|
107
|
+
&-icon {
|
|
108
|
+
color: $grey-30;
|
|
109
|
+
margin: 4px;
|
|
110
|
+
position: absolute;
|
|
111
|
+
left: 16px;
|
|
112
|
+
top: 12px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&-input {
|
|
116
|
+
border: none;
|
|
117
|
+
border-bottom: 1px solid $grey-40;
|
|
118
|
+
font-size: 0.875rem;
|
|
119
|
+
flex-grow: 1;
|
|
120
|
+
margin: 12px 12px 8px;
|
|
121
|
+
padding: 4px 4px 4px 32px;
|
|
122
|
+
outline: none;
|
|
123
|
+
|
|
124
|
+
&:hover {
|
|
125
|
+
box-shadow: inset 0 -0.7px 0 0 $grey-40;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&:focus {
|
|
129
|
+
box-shadow: inset 0 -0.7px 0 0 $blue;
|
|
130
|
+
border-color: $blue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
&-label {
|
|
134
|
+
height: 0;
|
|
135
|
+
width: 0;
|
|
136
|
+
overflow: hidden;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&--option {
|
|
142
|
+
padding: 8px 12px;
|
|
143
|
+
font-size: 0.875rem;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
|
|
146
|
+
&.selected {
|
|
147
|
+
color: $grey-70;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
&:focus,
|
|
151
|
+
&:hover {
|
|
152
|
+
outline: none;
|
|
153
|
+
background: $grey-15;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
package/addon/styles/addon.scss
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@1024pix/pix-ui/components/pix-dropdown';
|
|
@@ -33,6 +33,19 @@ export const form = (args) => {
|
|
|
33
33
|
/>
|
|
34
34
|
<br>
|
|
35
35
|
|
|
36
|
+
<PixDropdown
|
|
37
|
+
@id="form__searchable-pix-dropdown"
|
|
38
|
+
@options={{selectOptions}}
|
|
39
|
+
@placeholder="Choisir votre fruit"
|
|
40
|
+
@isSearchable={{true}}
|
|
41
|
+
@searchPlaceholder="Rechercher"
|
|
42
|
+
@label="Votre fruit préféré est : "
|
|
43
|
+
@clearLabel="Supprimer la sélection"
|
|
44
|
+
@expandLabel="Ouvrir la liste"
|
|
45
|
+
@collapseLabel="Réduire la liste"
|
|
46
|
+
/>
|
|
47
|
+
<br>
|
|
48
|
+
|
|
36
49
|
<PixTextarea
|
|
37
50
|
@id="form__pix-textarea"
|
|
38
51
|
@value=""
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { hbs } from 'ember-cli-htmlbars';
|
|
2
|
+
import { action } from '@storybook/addon-actions';
|
|
3
|
+
|
|
4
|
+
export const Template = (args) => {
|
|
5
|
+
return {
|
|
6
|
+
template: hbs`
|
|
7
|
+
<div style="width: 300px;">
|
|
8
|
+
<PixDropdown
|
|
9
|
+
@id={{id}}
|
|
10
|
+
@options={{options}}
|
|
11
|
+
@onSelect={{onSelect}}
|
|
12
|
+
@placeholder={{placeholder}}
|
|
13
|
+
@selectedOption={{selectedOption}}
|
|
14
|
+
@isSearchable={{isSearchable}}
|
|
15
|
+
@searchPlaceholder={{searchPlaceholder}}
|
|
16
|
+
@label={{label}}
|
|
17
|
+
@clearLabel={{clearLabel}}
|
|
18
|
+
@expandLabel={{expandLabel}}
|
|
19
|
+
@collapseLabel={{collapseLabel}}
|
|
20
|
+
@pageSize={{pageSize}}
|
|
21
|
+
@requiredLabel={{requiredLabel}}
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
context: args,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const Default = Template.bind({});
|
|
30
|
+
Default.args = {
|
|
31
|
+
options: [
|
|
32
|
+
{ value: '1', label: 'Figues' },
|
|
33
|
+
{ value: '2', label: 'Bananes' },
|
|
34
|
+
{ value: '3', label: 'Noix' },
|
|
35
|
+
{ value: '4', label: 'Papayes' },
|
|
36
|
+
{ value: '5', label: 'Fèves de chocolat' },
|
|
37
|
+
{ value: '6', label: 'Dattes' },
|
|
38
|
+
{ value: '7', label: 'Mangues' },
|
|
39
|
+
{ value: '8', label: 'Jujube' },
|
|
40
|
+
{ value: '9', label: 'Avocat' },
|
|
41
|
+
{ value: '10', label: 'Fraises' },
|
|
42
|
+
{ value: '11', label: 'Kaki' },
|
|
43
|
+
{ value: '12', label: 'Asiminier trilobé oblong vert (à ne pas confondre avec la papaye)' },
|
|
44
|
+
],
|
|
45
|
+
placeholder: 'Choisissez une option',
|
|
46
|
+
onSelect: action('onSelect'),
|
|
47
|
+
id: 'default-dropdown',
|
|
48
|
+
clearLabel: 'Supprimer la sélection',
|
|
49
|
+
expandLabel: 'Ouvrir la liste',
|
|
50
|
+
collapseLabel: 'Réduire la liste',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const withLabel = Template.bind({});
|
|
54
|
+
withLabel.args = {
|
|
55
|
+
...Default.args,
|
|
56
|
+
id: 'dropdown-with-label',
|
|
57
|
+
label: 'Ton fruit préféré: ',
|
|
58
|
+
requiredLabel: 'Champ requis',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const searchableDropdown = Template.bind({});
|
|
62
|
+
searchableDropdown.args = {
|
|
63
|
+
...Default.args,
|
|
64
|
+
isSearchable: true,
|
|
65
|
+
id: 'searchable-dropdown',
|
|
66
|
+
placeholder: 'Fraises, Mangues...',
|
|
67
|
+
searchPlaceholder: 'Rechercher',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const paginatedDropdown = Template.bind({});
|
|
71
|
+
paginatedDropdown.args = {
|
|
72
|
+
...Default.args,
|
|
73
|
+
id: 'paginated-dropdown',
|
|
74
|
+
placeholder: 'Quel est ton fruit préféré ?',
|
|
75
|
+
pageSize: 10,
|
|
76
|
+
isSearchable: true,
|
|
77
|
+
searchPlaceholder: 'Rechercher',
|
|
78
|
+
options: Array.from({ length: 100 }, (_, index) => ({
|
|
79
|
+
value: `${index}`,
|
|
80
|
+
label: `${index}abc`,
|
|
81
|
+
})),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const argTypes = {
|
|
85
|
+
options: {
|
|
86
|
+
name: 'options',
|
|
87
|
+
description:
|
|
88
|
+
'Les options sont représentées par un tableau d‘objet contenant les propriétés ``value`` et ``label``.',
|
|
89
|
+
type: { name: 'array', required: true },
|
|
90
|
+
},
|
|
91
|
+
selectedOption: {
|
|
92
|
+
name: 'selectedOption',
|
|
93
|
+
description: 'Option sélectionnée',
|
|
94
|
+
options: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'],
|
|
95
|
+
control: { type: 'select' },
|
|
96
|
+
type: { name: 'string', required: false },
|
|
97
|
+
table: {
|
|
98
|
+
type: { summary: 'string' },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
onSelect: {
|
|
102
|
+
name: 'onSelect',
|
|
103
|
+
description: 'Fonction à appeler quand une option est sélectionnée.',
|
|
104
|
+
type: { required: true },
|
|
105
|
+
control: { disable: true },
|
|
106
|
+
},
|
|
107
|
+
isSearchable: {
|
|
108
|
+
name: 'isSearchable',
|
|
109
|
+
description:
|
|
110
|
+
"Rend le champ cherchable, le paramètre @searchPlaceholder devient requis pour l'accessibilité.",
|
|
111
|
+
control: { type: 'boolean' },
|
|
112
|
+
type: { name: 'boolean', required: false },
|
|
113
|
+
table: {
|
|
114
|
+
type: { summary: 'boolean' },
|
|
115
|
+
defaultValue: { summary: false },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
searchPlaceholder: {
|
|
119
|
+
name: 'searchPlaceholder',
|
|
120
|
+
description: 'Placeholder du champ de recherche. Requis si le menu est cherchable.',
|
|
121
|
+
type: { name: 'string', required: false },
|
|
122
|
+
table: {
|
|
123
|
+
type: { summary: 'string' },
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
placeholder: {
|
|
127
|
+
name: 'placeholder',
|
|
128
|
+
description: "Un texte donnant une indication a l'utilisateur sur le choix des options.",
|
|
129
|
+
type: { name: 'string', required: false },
|
|
130
|
+
table: {
|
|
131
|
+
type: { summary: 'string' },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
label: {
|
|
135
|
+
name: 'label',
|
|
136
|
+
description: 'Label du menu déroulant, le paramètre @id devient obligatoire avec ce paramètre.',
|
|
137
|
+
type: { name: 'string', required: false },
|
|
138
|
+
table: {
|
|
139
|
+
type: { summary: 'string' },
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
requiredLabel: {
|
|
143
|
+
name: 'requiredLabel',
|
|
144
|
+
description:
|
|
145
|
+
'Label indiquant que le champ est requis, le paramètre @label devient obligatoire avec ce paramètre.',
|
|
146
|
+
type: { name: 'string', required: false },
|
|
147
|
+
table: {
|
|
148
|
+
type: { summary: 'string' },
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
id: {
|
|
152
|
+
name: 'id',
|
|
153
|
+
description: "L'id du label",
|
|
154
|
+
type: { name: 'string', required: false },
|
|
155
|
+
table: {
|
|
156
|
+
type: { summary: 'string' },
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
pageSize: {
|
|
160
|
+
name: 'pageSize',
|
|
161
|
+
description: "Le nombre d'élément à afficher dans la liste.",
|
|
162
|
+
type: { name: 'number', required: false },
|
|
163
|
+
table: {
|
|
164
|
+
type: { summary: 'number' },
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
clearLabel: {
|
|
168
|
+
name: 'clearLabel',
|
|
169
|
+
description: "Label de l'icône pour éliminer la sélection. Requis pour l'accessibilité.",
|
|
170
|
+
type: { name: 'string', required: false },
|
|
171
|
+
table: {
|
|
172
|
+
type: { summary: 'string' },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
expandLabel: {
|
|
176
|
+
name: 'expandLabel',
|
|
177
|
+
description: "Label de l'icône pour ouvrir le menu déroulant. Requis pour l'accessibilité.",
|
|
178
|
+
type: { name: 'string', required: false },
|
|
179
|
+
table: {
|
|
180
|
+
type: { summary: 'string' },
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
collapseLabel: {
|
|
184
|
+
name: 'collapseLabel',
|
|
185
|
+
description: "Label de l'icône pour réduire le menu déroulant. Requis pour l'accessibilité.",
|
|
186
|
+
type: { name: 'string', required: false },
|
|
187
|
+
table: {
|
|
188
|
+
type: { summary: 'string' },
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import centered from '@storybook/addon-centered/ember';
|
|
3
|
+
|
|
4
|
+
import * as stories from './pix-dropdown.stories.js';
|
|
5
|
+
|
|
6
|
+
<Meta
|
|
7
|
+
title='Form/Dropdown'
|
|
8
|
+
component='PixDropdown'
|
|
9
|
+
argTypes={stories.argTypes}
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
# Pix Dropdown
|
|
13
|
+
|
|
14
|
+
Affiche un menu déroulant avec la liste des options fournies.
|
|
15
|
+
|
|
16
|
+
Les options sont représentées par un tableau d'objet contenant les propriétés value et label.
|
|
17
|
+
|
|
18
|
+
> **⚠️** __Il est nécessaire d'avoir au moins un label ou un placeholder !__
|
|
19
|
+
|
|
20
|
+
> **⚠️** __N'oubliez pas de rajouter un searchLabel dans le cas ou le menu déroulant est cherchable pour le rendre accessible !__
|
|
21
|
+
|
|
22
|
+
> Pour aider l'utilisateur, rajoutez un placeholder cohérent !
|
|
23
|
+
|
|
24
|
+
## Default
|
|
25
|
+
|
|
26
|
+
<Canvas>
|
|
27
|
+
<Story name='Dropdown' story={stories.Default} height={200} />
|
|
28
|
+
</Canvas>
|
|
29
|
+
|
|
30
|
+
## With label
|
|
31
|
+
|
|
32
|
+
<Canvas>
|
|
33
|
+
<Story story={stories.withLabel} height={200} />
|
|
34
|
+
</Canvas>
|
|
35
|
+
|
|
36
|
+
## Searchable
|
|
37
|
+
|
|
38
|
+
<Canvas>
|
|
39
|
+
<Story name="Searchable" story={stories.searchableDropdown} height={200} />
|
|
40
|
+
</Canvas>
|
|
41
|
+
|
|
42
|
+
## Infinite scroll
|
|
43
|
+
|
|
44
|
+
<Canvas>
|
|
45
|
+
<Story name="Paginated" story={stories.paginatedDropdown} height={200} />
|
|
46
|
+
</Canvas>
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<PixDropdown
|
|
52
|
+
@id={{id}}
|
|
53
|
+
@options={{options}}
|
|
54
|
+
@onSelect={{onSelect}}
|
|
55
|
+
@selectedOption="1"
|
|
56
|
+
@isSearchable={{false}}
|
|
57
|
+
@placeholder="Choisissez une option"
|
|
58
|
+
@searchPlaceholder="Rechercher"
|
|
59
|
+
@label="Mon menu déroulant"
|
|
60
|
+
@requiredLabel="Requis"
|
|
61
|
+
@clearLabel="Supprimer la sélection"
|
|
62
|
+
@expandLabel="Ouvrir le menu déroulant"
|
|
63
|
+
@collapseLabel="Réduire le menu déroulant"
|
|
64
|
+
@pageSize={{10}}
|
|
65
|
+
/>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Arguments
|
|
69
|
+
|
|
70
|
+
<ArgsTable story="Dropdown" />
|
|
@@ -8,7 +8,8 @@ const Template = (args) => {
|
|
|
8
8
|
@position={{this.position}}
|
|
9
9
|
@isLight={{this.isLight}}
|
|
10
10
|
@isInline={{this.isInline}}
|
|
11
|
-
@isWide={{this.isWide}}
|
|
11
|
+
@isWide={{this.isWide}}
|
|
12
|
+
@hide={{this.hide}}>
|
|
12
13
|
<:triggerElement>
|
|
13
14
|
<PixButton aria-describedby={{this.id}}>
|
|
14
15
|
{{this.label}}
|
|
@@ -100,6 +101,13 @@ bottom.args = {
|
|
|
100
101
|
position: 'bottom',
|
|
101
102
|
};
|
|
102
103
|
|
|
104
|
+
export const hide = Template.bind({});
|
|
105
|
+
hide.args = {
|
|
106
|
+
label: 'À survoler pour voir la tooltip',
|
|
107
|
+
text: "Ne devrait pas s'afficher",
|
|
108
|
+
hide: true,
|
|
109
|
+
};
|
|
110
|
+
|
|
103
111
|
export const WithHTML = TemplateWithHTMLElement.bind({});
|
|
104
112
|
WithHTML.args = {
|
|
105
113
|
label: 'À survoler pour voir la tooltip',
|
|
@@ -154,4 +162,10 @@ export const argTypes = {
|
|
|
154
162
|
type: { name: 'boolean', required: false },
|
|
155
163
|
table: { defaultValue: { summary: false } },
|
|
156
164
|
},
|
|
165
|
+
hide: {
|
|
166
|
+
name: 'hide',
|
|
167
|
+
description: 'Masquer la tooltip',
|
|
168
|
+
type: { name: 'boolean', required: false },
|
|
169
|
+
table: { defaultValue: { summary: false } },
|
|
170
|
+
},
|
|
157
171
|
};
|
|
@@ -61,6 +61,13 @@ Les tooltips doivent prendre un `@id` et être référencées par leur élément
|
|
|
61
61
|
</PixTooltip>
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
## Hide
|
|
65
|
+
|
|
66
|
+
Cache la tooltip (par exemple si le contenu est vide).
|
|
67
|
+
|
|
68
|
+
<Canvas>
|
|
69
|
+
<Story name="Hide" story={stories.hide} height={200} />
|
|
70
|
+
</Canvas>
|
|
64
71
|
|
|
65
72
|
## Default
|
|
66
73
|
|
|
@@ -165,6 +172,18 @@ Infobulle contenant des éléments HTML
|
|
|
165
172
|
</:tooltip>
|
|
166
173
|
</PixTooltip>
|
|
167
174
|
|
|
175
|
+
<PixTooltip
|
|
176
|
+
@hide={{true}}
|
|
177
|
+
>
|
|
178
|
+
<:triggerElement>
|
|
179
|
+
<button>Tooltip n'apparaissant pas</button>
|
|
180
|
+
</:triggerElement>
|
|
181
|
+
|
|
182
|
+
<:tooltip>
|
|
183
|
+
Hey
|
|
184
|
+
</:tooltip>
|
|
185
|
+
</PixTooltip>
|
|
186
|
+
|
|
168
187
|
<PixTooltip
|
|
169
188
|
@isWide={{true}}
|
|
170
189
|
>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 1. Eviter les balises HTML natives
|
|
2
|
+
|
|
3
|
+
Date : 2022-01-04
|
|
4
|
+
|
|
5
|
+
## État
|
|
6
|
+
|
|
7
|
+
Accepté
|
|
8
|
+
|
|
9
|
+
## Contexte
|
|
10
|
+
|
|
11
|
+
Le framework Ember propose des composants helpers tels que `<Input>` ou `<Textarea>`.
|
|
12
|
+
Cependant leurs comportements diffèrent pas mal des éléments HTML natifs `<input>` et `<textarea>`.
|
|
13
|
+
|
|
14
|
+
Nous aimerions choisir une seule solution pour :
|
|
15
|
+
- simplifiier l'utilisation de Pix-UI;
|
|
16
|
+
- ne plus avoir à modifier les composants (exemple sur [PixInput](https://github.com/1024pix/pix-ui/pull/147#discussion_r724312399)).
|
|
17
|
+
|
|
18
|
+
Actuellement, tous les composants Pix-UI utilisent des balises HTML natives sauf le PixTextarea qui utilise le `<Textarea>` d'Ember.
|
|
19
|
+
|
|
20
|
+
Historique des PRs qui ont mené à cette problématique :
|
|
21
|
+
- https://github.com/1024pix/pix-ui/pull/149 a changé le PixInputPassword et PixInput pour utiliser `<Input>` plutôt que `<input>` (embarqué dans la version v8.4.0 de Pix-UI).
|
|
22
|
+
- https://github.com/1024pix/pix-ui/pull/147/files a changé le PixInputPassword pour utiliser `<input>` plutôt que `<Input>` (embarqué dans la version v11.0.0 de Pix-UI).
|
|
23
|
+
- https://github.com/1024pix/pix-ui/pull/175/files a changé le PixInput pour utiliser `<input>` plutôt que `<Input>` (embarqué dans la version v11.1.0 de Pix-UI).
|
|
24
|
+
|
|
25
|
+
#### Solution 1
|
|
26
|
+
Privilégier l'utilisation des composants helpers que propose Ember (`<Input>` / `<Textarea>`).
|
|
27
|
+
|
|
28
|
+
Ce qui impliquerait :
|
|
29
|
+
- une solution plus "ember way"
|
|
30
|
+
- une obligation d'utiliser le two-way-binding (les modifications de valeurs dans un composant enfant seront automatiquement propagées au composant parent) qui va à l'encontre du principe Data Down Action Up (les données descendent vers les composants enfants et les enfants remontent les actions aux composants parents).
|
|
31
|
+
- plus de magie et moins de contrôle, lorsqu'on passe l'attribut `@value` Ember met tout à jour tout seul (le changement est répercuté partout)
|
|
32
|
+
- une obligation de copier le modèle pour ne pas le modifier
|
|
33
|
+
- une difficulté pour la validation de formulaire, avec la librairie `ember-cp-validation` les erreurs s'affichent dans le formulaire au moindre changement (ce qui n'est pas souhaitable) mais de toute manière on est pas sûrs de vouloir continuer à utiliser cette librairie.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
#### Possibilité 2
|
|
37
|
+
Privilégier l'utilisation des éléments HTML natifs (`<input>` / `<textarea>`).
|
|
38
|
+
|
|
39
|
+
Ce qui impliquerait :
|
|
40
|
+
- de ne pas avoir le two-way-binding imposé
|
|
41
|
+
- une obligation de déclarer un `onChange` sur les champs afin d'interargir avec changements du champ
|
|
42
|
+
- une obligation de copier le modèle pour ne pas le modifier
|
|
43
|
+
- une plus grande maîtrise de la balise et ce qu'il s'y passe en coulisse
|
|
44
|
+
|
|
45
|
+
## Décision
|
|
46
|
+
|
|
47
|
+
Nous privilégierons les éléments HTML natifs et éviterons autant que possible le two-way-binding.
|
|
48
|
+
|
|
49
|
+
## Conséquences
|
|
50
|
+
|
|
51
|
+
Il faut mettre à jour le PixTextarea pour qu'il utilise une balise `<textarea>`.
|
|
52
|
+
|
|
53
|
+
⚠️ Pour ne pas casser le comportement existant. lors de la mise à jour des versions v11.0.0 / v11.1.0 de Pix-UI il faut mettre à jour tous les PixInput et PixInputPassword afin qu'il définissent un `onChange`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1024pix/pix-ui",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.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"
|
|
@@ -50,8 +50,9 @@
|
|
|
50
50
|
"ember-truth-helpers": "^3.0.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
+
"@1024pix/ember-testing-library": "^0.5.0",
|
|
53
54
|
"@ember/optional-features": "^2.0.0",
|
|
54
|
-
"@ember/test-helpers": "^2.
|
|
55
|
+
"@ember/test-helpers": "^2.6.0",
|
|
55
56
|
"@fortawesome/ember-fontawesome": "^0.2.3",
|
|
56
57
|
"@fortawesome/free-regular-svg-icons": "^5.15.4",
|
|
57
58
|
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|