origam 2.18.3 → 2.18.5
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 +241 -0
- package/dist/src/assets/locales/en.json +9 -0
- package/dist/src/assets/locales/fr.json +9 -0
- package/dist/src/components/ColorPickerField/OrigamColorPickerField.vue +24 -2
- package/dist/src/components/DataTable/OrigamDataTableGroupHeaderRow.vue +4 -0
- package/dist/src/components/DatePicker/OrigamDatePickerControls.vue +5 -0
- package/dist/src/components/DatePickerField/OrigamDatePickerField.vue +22 -2
- package/dist/src/components/InfiniteScroll/OrigamInfiniteScroll.vue +17 -0
- package/dist/src/components/Slide/OrigamSlideGroup.vue +5 -0
- package/dist/src/components/TextField/OrigamTextField.vue +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,12 +18,154 @@ This project follows [Semantic Versioning](https://semver.org).
|
|
|
18
18
|
|
|
19
19
|
## [Unreleased]
|
|
20
20
|
|
|
21
|
+
### Fixed — #818 baseline shrink : `aria-allowed-attr`/`aria-prohibited-attr`/`aria-valid-attr-value` (12 des 25 entrées)
|
|
22
|
+
|
|
23
|
+
Recompté sur `a11y-violations.baseline.json` (pas repris d'un chiffre cité
|
|
24
|
+
ailleurs) : la baseline #818 portait **25 clés / 33 violations** sur 24
|
|
25
|
+
composants, dont **12 `aria-allowed-attr`** concentrées sur la famille
|
|
26
|
+
`OrigamDataTable*`. Cause commune, pas 12 défauts distincts :
|
|
27
|
+
`OrigamTextField` ne posait pas `inheritAttrs: false` alors qu'il
|
|
28
|
+
redistribue déjà explicitement chaque attr de fall-through
|
|
29
|
+
(`filterInputAttrs` → `rootAttrs` sur `<origam-input>`, `inputAttrs` sur le
|
|
30
|
+
vrai `<input>`). Sans ce flag, Vue appliquait EN PLUS le `$attrs` brut sur
|
|
31
|
+
la racine `<origam-input>`, qui ne s'y soustrayait pas non plus — doublant
|
|
32
|
+
`aria-haspopup`/`aria-expanded`/`aria-controls`/`aria-valuenow`/… sur le
|
|
33
|
+
`<div>` wrapper, qui ne porte aucun rôle les autorisant. `role` (prop
|
|
34
|
+
déclarée sur `OrigamTextField`) n'atteignait par ailleurs que
|
|
35
|
+
`<origam-field>`, jamais le vrai `<input>` — c'est ce défaut exact que le
|
|
36
|
+
commentaire `KNOWN_FAILURES` documentait déjà pour `OrigamSelect` (test
|
|
37
|
+
`fixme`).
|
|
38
|
+
|
|
39
|
+
**Correctif à la source** (`packages/ds/src/components/TextField/OrigamTextField.vue`) :
|
|
40
|
+
`defineOptions({ inheritAttrs: false })` + `role` désormais aussi lié sur le
|
|
41
|
+
`<input>` réel. Élimine, mesuré axe-core en navigateur réel (Playwright +
|
|
42
|
+
Histoire statique) :
|
|
43
|
+
- `OrigamSelect` — sort de `KNOWN_FAILURES` (`components.spec.ts`), test
|
|
44
|
+
`fixme` → vert.
|
|
45
|
+
- `OrigamDataTable`, `OrigamDataTableFooter`, `OrigamDataTableHeaderCell`,
|
|
46
|
+
`OrigamDataTableHeaders`, `OrigamDataTableHeadersCell`,
|
|
47
|
+
`OrigamDataTableRow` — `aria-allowed-attr` disparaît, entrée de baseline
|
|
48
|
+
retirée entièrement.
|
|
49
|
+
- `OrigamDataTableGroupHeaderRow`, `OrigamDataTableRows`, `OrigamNumberField`
|
|
50
|
+
— `aria-allowed-attr` disparaît, l'entrée reste pour leur AUTRE violation
|
|
51
|
+
(`button-name` / `color-contrast`), non touchée par ce lot.
|
|
52
|
+
- `OrigamDataTableHeadersCellMobile` — `aria-allowed-attr` ET
|
|
53
|
+
`aria-prohibited-attr` disparaissent (même fuite, deux règles axe
|
|
54
|
+
différentes), entrée retirée entièrement.
|
|
55
|
+
|
|
56
|
+
**Deuxième cause, distincte, deux composants** —
|
|
57
|
+
`packages/ds/src/components/ColorPickerField/OrigamColorPickerField.vue` et
|
|
58
|
+
`.../DatePickerField/OrigamDatePickerField.vue` posaient un
|
|
59
|
+
`aria-haspopup` avec une valeur inventée (`'colorpickerbox'` /
|
|
60
|
+
`'datepickerbox'`, jamais un token ARIA valide) sur l'élément
|
|
61
|
+
`.origam-field` (via `activator="parent"` d'`OrigamMenu`), sans `role` le
|
|
62
|
+
justifiant. Remplacé par `aria-haspopup: 'dialog'` (motif WAI-ARIA « Date
|
|
63
|
+
Picker Dialog ») + `role: 'combobox'` sur le même élément — même principe
|
|
64
|
+
que `comboboxAriaAttrs` d'`OrigamSelect`. `aria-allowed-attr` ET
|
|
65
|
+
`aria-valid-attr-value` disparaissent pour les deux ; `OrigamColorPickerField`
|
|
66
|
+
est intégralement retiré de la baseline, `OrigamDatePickerField` y reste
|
|
67
|
+
pour son `color-contrast` (non touché).
|
|
68
|
+
|
|
69
|
+
**Bilan mesuré** : baseline #818 25 clés/33 violations → **17 clés/18
|
|
70
|
+
violations**. Suite `pnpm -F @origam/tests test:a11y` complète (226 tests,
|
|
71
|
+
catalogue 218/218) : **226 passed**, aucune régression sur les 36
|
|
72
|
+
composants à baseline vide. Contrôle positif exécuté : réintroduire l'ancien
|
|
73
|
+
comportement fait échouer `a11y — OrigamSelect Default Variant` avec
|
|
74
|
+
`aria-allowed-attr`/`aria-prohibited-attr` NON baselinés — la porte capte
|
|
75
|
+
la régression, pas seulement l'édition du JSON.
|
|
76
|
+
|
|
77
|
+
**Laissé de côté, délibérément** (17 clés / 18 violations restent dans
|
|
78
|
+
`a11y-violations.baseline.json`, cf. #818) : `color-contrast` (8 instances —
|
|
79
|
+
plusieurs agents dédiés au contraste travaillent déjà sur ce périmètre),
|
|
80
|
+
`button-name` (3, boutons icône sans libellé — famille distincte),
|
|
81
|
+
`scrollable-region-focusable` (3), `aria-required-parent`/`aria-required-children`/
|
|
82
|
+
`listitem`/`aria-prohibited-attr` (`OrigamChartMap`, sans lien avec ce
|
|
83
|
+
correctif) — un défaut chacun, cause non partagée avec ce lot, non
|
|
84
|
+
instruit ici.
|
|
85
|
+
|
|
21
86
|
Récolte de deux dépréciations posées "pour la prochaine majeure" avant que le
|
|
22
87
|
`CLAUDE.md` n'acte que `3.0.0` est réservé à la séparation en modules et que
|
|
23
88
|
les ruptures, elles, ne le sont pas — voir "Work priorities and versioning"
|
|
24
89
|
du `CLAUDE.md`. `origam` n'a aucun consommateur : les deux ruptures
|
|
25
90
|
ci-dessous partent en **mineure**, sans shim ni période de grâce.
|
|
26
91
|
|
|
92
|
+
### Fixed — `button-name` et `scrollable-region-focusable` (6 des 18 violations laissées de côté par le lot précédent)
|
|
93
|
+
|
|
94
|
+
Recompté sur `a11y-violations.baseline.json` avant de commencer (pas repris
|
|
95
|
+
d'un chiffre cité ailleurs) : **18 violations / 17 clés**, dont deux familles
|
|
96
|
+
explicitement laissées de côté par le lot ci-dessus.
|
|
97
|
+
|
|
98
|
+
**`button-name` (3, cause commune)** — `OrigamDataTableGroupHeaderRow`,
|
|
99
|
+
`OrigamDatePicker`, `OrigamDatePickerControls` rendaient chacun un
|
|
100
|
+
`<origam-btn icon="…">` sans `aria-label`/`aria-labelledby`/`title` : un
|
|
101
|
+
bouton icône réel, annoncé "button" et rien d'autre par un lecteur d'écran
|
|
102
|
+
(`OrigamBtn` avertit déjà de ce cas précis en dev depuis #653, mais ne
|
|
103
|
+
fabrique jamais de libellé — cf. son commentaire `isUnnamedIconOnly`, à
|
|
104
|
+
raison : un libellé deviné tairait axe sans rien dire à l'utilisateur).
|
|
105
|
+
Corrigé à la source de chaque bouton (pas dans `OrigamBtn`, qui n'a pas
|
|
106
|
+
l'information sémantique) :
|
|
107
|
+
- `OrigamDataTableGroupHeaderRow` — le bouton d'expand/collapse du groupe
|
|
108
|
+
reçoit `aria-expanded` + `aria-label` dynamique (`origam.data_table_group_header_row.{expand_group,collapse_group}`),
|
|
109
|
+
même motif que le correctif `data_table_row.{expand_row,collapse_row}`
|
|
110
|
+
déjà en place sur `OrigamDataTableRow`.
|
|
111
|
+
- `OrigamDatePickerControls` — les 3 boutons icône (bascule mois/année,
|
|
112
|
+
précédent, suivant) reçoivent chacun un `aria-label` via `useLocale()`
|
|
113
|
+
(`origam.date_picker_controls.{toggle_year,prev,next}`).
|
|
114
|
+
- `OrigamDatePicker::default` en héritait sans bouton icône propre — sa
|
|
115
|
+
Variant par défaut rend `OrigamDatePickerControls`, donc le correctif
|
|
116
|
+
ci-dessus l'assainit aussi ; son AUTRE violation (`color-contrast`,
|
|
117
|
+
hors périmètre) reste dans la baseline.
|
|
118
|
+
|
|
119
|
+
Nouvelles clés ajoutées à `en.json` ET `fr.json` dans le même commit,
|
|
120
|
+
`snake_case` minuscule, aucune chaîne en dur dans les templates.
|
|
121
|
+
|
|
122
|
+
**`scrollable-region-focusable` (3, cause commune)** — `OrigamInfiniteScroll`,
|
|
123
|
+
`OrigamInfiniteScrollIntersect` (dont la story monte elle-même un
|
|
124
|
+
`OrigamInfiniteScroll`) et `OrigamSlideGroup` rendent chacun un conteneur
|
|
125
|
+
`overflow: auto` sans jamais être atteignables au clavier quand leur contenu
|
|
126
|
+
n'a aucun descendant focusable — axe-core l'exige (technique WAI WCAG
|
|
127
|
+
SCR29).
|
|
128
|
+
|
|
129
|
+
⚠️ `tabindex="0"` inconditionnel a été essayé et écarté : `OrigamSlideGroup`
|
|
130
|
+
a un seul consommateur réel (`OrigamChipGroup`), qui slotte toujours des
|
|
131
|
+
puces focusables — y poser `tabindex` sans condition aurait inséré un arrêt
|
|
132
|
+
de tabulation redondant AVANT les items, dégradant le parcours clavier réel
|
|
133
|
+
pour corriger un cas de story synthétique. Le correctif retenu conditionne
|
|
134
|
+
donc le `tabindex` sur l'ABSENCE de descendant focusable (`focusableChildren()`,
|
|
135
|
+
déjà utilisé ailleurs dans le DS) :
|
|
136
|
+
- `OrigamInfiniteScroll` — `hasFocusableDescendant` (recalculé par
|
|
137
|
+
`MutationObserver` sur la racine, le contenu s'accumulant en continu via
|
|
138
|
+
`@load`) pilote un `:tabindex` conditionnel sur la racine.
|
|
139
|
+
- `OrigamSlideGroup` — `hasFocusableContent` (recalculé dans le même
|
|
140
|
+
`requestAnimationFrame` qui mesure déjà `isOverflowing`) pilote un
|
|
141
|
+
`:tabindex` conditionnel sur `.origam-slide-group__container`, l'élément
|
|
142
|
+
RÉELLEMENT scrollable — distinct de la racine, qui porte déjà son propre
|
|
143
|
+
`tabindex` conditionnel pour un mécanisme sans rapport (proxy de focus
|
|
144
|
+
vers le premier item, cf. `handleFocus`) qu'axe ne peut pas lire ici
|
|
145
|
+
puisque `__container` n'est pas un ancêtre de la racine.
|
|
146
|
+
- `OrigamInfiniteScrollIntersect::default` en héritait sans conteneur
|
|
147
|
+
scrollable propre — sa Variant par défaut monte un `OrigamInfiniteScroll`,
|
|
148
|
+
donc le correctif ci-dessus l'assainit aussi.
|
|
149
|
+
|
|
150
|
+
**Bilan mesuré** : baseline **18 clés/17 violations → 12 clés/12 violations**
|
|
151
|
+
(6 clés retirées : `OrigamDataTableGroupHeaderRow`, `OrigamDatePickerControls`,
|
|
152
|
+
`OrigamInfiniteScroll`, `OrigamInfiniteScrollIntersect`, `OrigamSlideGroup`,
|
|
153
|
+
et `button-name` retiré de l'entrée `OrigamDatePicker` qui garde son
|
|
154
|
+
`color-contrast`). Suite `pnpm -F @origam/tests test:a11y` complète (226
|
|
155
|
+
tests, catalogue 218/218) : **226 passed**, aucune régression sur les 36
|
|
156
|
+
composants à baseline vide. Contrôle positif exécuté sur
|
|
157
|
+
`OrigamDataTableGroupHeaderRow` : retirer `aria-expanded`/`aria-label` fait
|
|
158
|
+
échouer la porte avec `1 violation(s) a11y NOUVELLE(S) non baselinée(s) —
|
|
159
|
+
button-name` (`REAL_EXIT=1`) ; le correctif remis, la suite ciblée repasse
|
|
160
|
+
au vert. `pnpm -F origam guards` (28/28), `guards:self` (15/15),
|
|
161
|
+
`pnpm -F @origam/tests test:e2e:audit` (3/3) et `pnpm run lint`
|
|
162
|
+
(`--max-warnings 0`) verts sur le dernier commit.
|
|
163
|
+
|
|
164
|
+
**Laissé de côté, délibérément** (12 clés / 12 violations restent dans
|
|
165
|
+
`a11y-violations.baseline.json`) : `color-contrast` (8 instances, périmètre
|
|
166
|
+
d'autres agents), `aria-required-parent`/`aria-required-children`/`listitem`/
|
|
167
|
+
`aria-prohibited-attr` (un défaut chacun, cause non partagée avec ce lot).
|
|
168
|
+
|
|
27
169
|
### ⚠️ BREAKING — `click:prepend` / `click:append` retirés d'`IBtnEmits` (#443, #577)
|
|
28
170
|
|
|
29
171
|
`<OrigamBtn>` n'émet plus `click:prepend` ni `click:append` — ni au niveau du
|
|
@@ -156,6 +298,105 @@ conclure qu'elle ne l'est plus par défaut.
|
|
|
156
298
|
|
|
157
299
|
### Fixed
|
|
158
300
|
|
|
301
|
+
- **#871 (clôture) — les 7 dernières violations AA étaient UN défaut de
|
|
302
|
+
palette répété, pas sept défauts de composant.** Mesuré avec
|
|
303
|
+
`packages/tests/audit/dark-contrast.audit.mjs` : **7 / 1 664 → 0 / 1 664**,
|
|
304
|
+
les 8 identités × 2 modes × 2 portées, quatre contrôles verts des deux
|
|
305
|
+
côtés. Exactement **7 lignes changent, aucune ne se dégrade**, et le ratio
|
|
306
|
+
minimum de toute la surface passe de 3.65 à **4.70**.
|
|
307
|
+
|
|
308
|
+
Les 7 se répartissaient en deux causes, toutes deux dans
|
|
309
|
+
`packages/marketing/src/themes` — **aucune dans le DS**, qui était déjà à
|
|
310
|
+
zéro :
|
|
311
|
+
|
|
312
|
+
| cause | marque · mode | couple | avant | après |
|
|
313
|
+
|---|---|---|---|---|
|
|
314
|
+
| encre héritée du mode clair | `apple` sombre | `#ffffff` → `#000000` sur `#0a84ff` | 3.65 | **5.76** |
|
|
315
|
+
| encre héritée du mode clair | `ecom` sombre | `#ffffff` → `#1a0f0a` sur `#f43f5e` | 3.67 | **5.12** |
|
|
316
|
+
| jeton de rôle inversé | `ecom` clair | `action.primary.bg` → `.fgSubtle` sur la crème | 4.43 | **5.93** |
|
|
317
|
+
|
|
318
|
+
**La cause commune.** Le mode sombre ÉCLAIRCIT l'accent de marque — c'est
|
|
319
|
+
la bonne pratique pour une grande surface (`#0071e3` → `#0a84ff` chez
|
|
320
|
+
`apple`, rose-600 → rose-500 chez `ecom`) — mais les deux palettes gardaient
|
|
321
|
+
l'encre `#ffffff` de leur jumeau clair. Or chacune applique **déjà** la règle
|
|
322
|
+
inverse à ses intentions `feedback` : les 4 d'`ecom` et 3 des 4 d'`apple`
|
|
323
|
+
encrent leur accent éclairci avec le `text.inverse` de la palette.
|
|
324
|
+
`action.primary` était la dernière exception. Le badge n'était pas en cause :
|
|
325
|
+
`--origam-badge__badge---{background-color,color}` lit exactement ce couple —
|
|
326
|
+
tout comme `--origam-btn--primary---*`, qui n'est pas dans la sonde et était
|
|
327
|
+
donc en défaut sans être mesuré.
|
|
328
|
+
|
|
329
|
+
⚠️ **On n'a PAS foncé l'accent**, bien que ce soit l'autre levier : chez
|
|
330
|
+
`apple`, `action.primary.bg` sert aussi d'ENCRE (le bloc `components` passe
|
|
331
|
+
ce `var()` en `color` sur `origam-breadcrumb-item`). Le ramener à `#0071e3`
|
|
332
|
+
donnait 4.70 sous du blanc mais faisait tomber le fil d'Ariane à **4.47** sur
|
|
333
|
+
le fond noir — un défaut échangé contre un autre. Vérifié par calcul avant
|
|
334
|
+
d'écrire la moindre ligne.
|
|
335
|
+
|
|
336
|
+
La 3ᵉ cause est une erreur de RÔLE : `bg` est un jeton de surface, `fgSubtle`
|
|
337
|
+
la teinte prévue pour être LUE sur une surface neutre. `ecomDarkTheme` posait
|
|
338
|
+
déjà `fgSubtle` ; seul le bloc clair se trompait de jeton. Les deux modes sont
|
|
339
|
+
désormais symétriques.
|
|
340
|
+
|
|
341
|
+
- **#871 — une 8ᵉ violation, que la sonde ne pouvait pas voir** :
|
|
342
|
+
`material` sombre, `feedback.success`, `#1b5e20` sur `#81c784` = **3.91**
|
|
343
|
+
→ `#0d3b10` = **6.32**. La sonde de `dark-contrast.audit.mjs` ne rend qu'UNE
|
|
344
|
+
intention par famille de composant, donc elle ne peint jamais
|
|
345
|
+
`feedback.success`. Trouvée par la spec statique ajoutée ci-dessous. C'est
|
|
346
|
+
l'encre qui était l'intruse, pas le conteneur : les trois autres intentions
|
|
347
|
+
sombres de `material` tiennent 5.79 / 7.70 / 7.71 avec des encres bespoke
|
|
348
|
+
très profondes, `success` était la seule à se contenter du green-900 de la
|
|
349
|
+
rampe.
|
|
350
|
+
|
|
351
|
+
- **#871 — l'audit lui-même ne démarrait plus.** Son `npx vite build` résolvait
|
|
352
|
+
vite par le PATH, via un lien `node_modules/.bin` qu'un peer auto-installé
|
|
353
|
+
ne garantit pas : sur un `pnpm install --frozen-lockfile` propre,
|
|
354
|
+
`packages/tests/node_modules/.bin/` ne contient que `playwright` et `vitest`,
|
|
355
|
+
et la commande mourait en `127 — sh: vite: command not found`. **L'audit ne
|
|
356
|
+
rendait plus aucun chiffre**, et rien ne le signalait puisque rien ne le
|
|
357
|
+
rejoue en CI. `resolveViteBin()` passe désormais par le realpath de
|
|
358
|
+
`@vitejs/plugin-vue`, ce qui ne dépend ni du PATH ni du hoisting et
|
|
359
|
+
sélectionne la copie de vite que le plugin utilise réellement (deux majeures
|
|
360
|
+
coexistent dans le magasin). Les commentaires qui affirmaient le contraire
|
|
361
|
+
dans les deux fichiers ont été corrigés.
|
|
362
|
+
|
|
363
|
+
- **#871 — l'état SURVOL était en défaut plus grave encore, et le correctif
|
|
364
|
+
d'encre l'a réglé sans qu'on y touche.** Le survol n'a pas d'encre à lui :
|
|
365
|
+
`--origam-btn--primary---background-color-hover` bascule la surface, mais il
|
|
366
|
+
n'existe **aucun** `--origam-btn--primary---color-hover` (ni de
|
|
367
|
+
`--origam-badge__badge---color-hover`) — l'encre reste `action.primary.fg`
|
|
368
|
+
dans les deux états. Sous l'encre blanche, `apple` sombre y tombait à
|
|
369
|
+
**2.19:1** et `ecom` sombre à **2.69:1** : *pires que n'importe laquelle des
|
|
370
|
+
7 violations comptées*, et invisibles parce que la sonde de
|
|
371
|
+
`dark-contrast.audit.mjs` ne rend que l'état au repos.
|
|
372
|
+
|
|
373
|
+
**Aucun `bgHover` n'a été modifié.** La direction du survol n'était pas le
|
|
374
|
+
problème — l'encre l'était. Mesuré sur les deux marques :
|
|
375
|
+
|
|
376
|
+
| mode · encre | direction du survol | ratio repos → survol |
|
|
377
|
+
|---|---|---|
|
|
378
|
+
| clair · blanche | fonce | 4.70 → **6.95** / **6.29** ↑ |
|
|
379
|
+
| sombre · blanche *(avant)* | éclaircit | 3.65 → **2.19** / 3.67 → **2.69** ↓ |
|
|
380
|
+
| sombre · foncée *(après)* | éclaircit | 5.76 → **9.60** / 5.12 → **6.99** ↑ |
|
|
381
|
+
|
|
382
|
+
La règle n'est donc pas *« le survol doit foncer »* mais **« le survol doit
|
|
383
|
+
déplacer l'accent à l'opposé de la clarté de son encre »**. Les palettes la
|
|
384
|
+
respectaient déjà dans 3 quadrants sur 4 ; le seul cassé l'était par son
|
|
385
|
+
encre. L'idiome « le survol éclaircit », qui est le bon en mode sombre, est
|
|
386
|
+
conservé dans les deux marques.
|
|
387
|
+
|
|
388
|
+
- **#871 — non-régression : `packages/tests/TU/marketing/brand-palette-contrast.spec.ts`.**
|
|
389
|
+
Les couples `{bg, fg}` littéraux des 7 palettes de marque (14 thèmes) doivent
|
|
390
|
+
tenir AA. L'audit de #871 est un script qu'on lance à la main : rien ne le
|
|
391
|
+
rejoue, donc rien n'empêchait une retouche de palette de ramener ces couples
|
|
392
|
+
en silence. La spec lit les hex **dans l'objet `IOrigamTheme`**, avant toute
|
|
393
|
+
indirection CSS — la règle jsdom/`var()` du `CLAUDE.md` ne s'y applique pas,
|
|
394
|
+
et le navigateur reste l'arbitre de ce qui est peint. Elle embarque ses
|
|
395
|
+
propres garde-fous : les 3 témoins de la formule, un contrôle négatif sur les
|
|
396
|
+
3 couples corrigés, et un plancher de couverture qui rend bruyante une dérive
|
|
397
|
+
du filtre. **Vérifiée ROUGE sur `origin/develop`** : 3 échecs / 14 succès.
|
|
398
|
+
C'est elle qui a trouvé la 8ᵉ violation ci-dessus.
|
|
399
|
+
|
|
159
400
|
- **#871 — le mode sombre ne peignait qu'à moitié dans un sous-arbre thémé.**
|
|
160
401
|
Les feuilles de tokens portent ~1 761 déclarations **dérivées**
|
|
161
402
|
(`--origam-title---color: var(--origam-color__text---primary)`). Une custom
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
"placeholder": "Enter date"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
+
"date_picker_controls": {
|
|
55
|
+
"prev": "Previous",
|
|
56
|
+
"next": "Next",
|
|
57
|
+
"toggle_year": "Toggle year selection"
|
|
58
|
+
},
|
|
54
59
|
"no_data_text": "No data available",
|
|
55
60
|
"bracket": {
|
|
56
61
|
"aria_label": "Tournament bracket",
|
|
@@ -199,6 +204,10 @@
|
|
|
199
204
|
"expand_row": "Expand row",
|
|
200
205
|
"collapse_row": "Collapse row"
|
|
201
206
|
},
|
|
207
|
+
"data_table_group_header_row": {
|
|
208
|
+
"expand_group": "Expand group",
|
|
209
|
+
"collapse_group": "Collapse group"
|
|
210
|
+
},
|
|
202
211
|
"rating": {
|
|
203
212
|
"aria_label": {
|
|
204
213
|
"item": "Rating {0} of {1}"
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
"placeholder": "Entrer une date"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
+
"date_picker_controls": {
|
|
55
|
+
"prev": "Précédent",
|
|
56
|
+
"next": "Suivant",
|
|
57
|
+
"toggle_year": "Basculer la sélection de l'année"
|
|
58
|
+
},
|
|
54
59
|
"no_data_text": "Aucune donnée disponible",
|
|
55
60
|
"bracket": {
|
|
56
61
|
"aria_label": "Tableau du tournoi",
|
|
@@ -199,6 +204,10 @@
|
|
|
199
204
|
"expand_row": "Développer la ligne",
|
|
200
205
|
"collapse_row": "Réduire la ligne"
|
|
201
206
|
},
|
|
207
|
+
"data_table_group_header_row": {
|
|
208
|
+
"expand_group": "Développer le groupe",
|
|
209
|
+
"collapse_group": "Réduire le groupe"
|
|
210
|
+
},
|
|
202
211
|
"rating": {
|
|
203
212
|
"aria_label": {
|
|
204
213
|
"item": "Note de {0} sur {1}"
|
|
@@ -331,9 +331,31 @@ const menuProps = computed(() => {
|
|
|
331
331
|
return {
|
|
332
332
|
...props.menuProps,
|
|
333
333
|
activatorProps: {
|
|
334
|
+
/*********************************************************
|
|
335
|
+
* role + aria-haspopup — #818
|
|
336
|
+
*
|
|
337
|
+
* @description
|
|
338
|
+
* `'colorpickerbox'` was never a valid `aria-haspopup` token
|
|
339
|
+
* (spec allows `false|true|menu|listbox|tree|grid|dialog`) —
|
|
340
|
+
* axe-core's `aria-valid-attr-value` flagged it, measured
|
|
341
|
+
* against a real browser. The popup is a free-form panel
|
|
342
|
+
* (a color canvas + swatches), not a list of options, so
|
|
343
|
+
* `'dialog'` is the correct token — same family as the
|
|
344
|
+
* WAI-ARIA "Date Picker Dialog" pattern `OrigamDatePickerField`
|
|
345
|
+
* uses right below.
|
|
346
|
+
*
|
|
347
|
+
* @description
|
|
348
|
+
* `role: 'combobox'` is ALSO required here: `activator="parent"`
|
|
349
|
+
* (below) lands these ARIA attrs on the `.origam-field` wrapper
|
|
350
|
+
* `<div>`, which carries no role by default — `aria-allowed-attr`
|
|
351
|
+
* rejects `aria-expanded` on an element whose role doesn't
|
|
352
|
+
* support it. Same fix, same rationale as
|
|
353
|
+
* `OrigamSelect.comboboxAriaAttrs` (`role` MUST sit on the same
|
|
354
|
+
* element as `aria-haspopup`/`aria-expanded`).
|
|
355
|
+
********************************************************/
|
|
356
|
+
role: "combobox",
|
|
334
357
|
...props.menuProps?.activatorProps || {},
|
|
335
|
-
"aria-haspopup": "
|
|
336
|
-
// Set aria-haspopup to 'listbox'
|
|
358
|
+
"aria-haspopup": "dialog"
|
|
337
359
|
},
|
|
338
360
|
contentProps: {
|
|
339
361
|
...consumerContentProps,
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
>
|
|
16
16
|
<origam-data-table-column-cell class="origam-data-table-group-header-row__column">
|
|
17
17
|
<origam-btn
|
|
18
|
+
:aria-expanded="isGroupOpen(item)"
|
|
19
|
+
:aria-label="isGroupOpen(item) ? t('origam.data_table_group_header_row.collapse_group') : t('origam.data_table_group_header_row.expand_group')"
|
|
18
20
|
:icon="groupIcon"
|
|
19
21
|
size="small"
|
|
20
22
|
variant="text"
|
|
@@ -54,6 +56,7 @@ import OrigamCheckboxBtn from "../Checkbox/OrigamCheckboxBtn.vue";
|
|
|
54
56
|
import OrigamDataTableColumnCell from "./OrigamDataTableColumnCell.vue";
|
|
55
57
|
import { useGroupBy } from "../../composables/DataTable/group.composable";
|
|
56
58
|
import { useHeaders } from "../../composables/DataTable/headers.composable";
|
|
59
|
+
import { useLocale } from "../../composables/Commons/locale.composable";
|
|
57
60
|
import { useProps } from "../../composables/Commons/props.composable";
|
|
58
61
|
import { useSelection } from "../../composables/DataTable/select.composable";
|
|
59
62
|
import { useStyle } from "../../composables/Commons/style.composable";
|
|
@@ -74,6 +77,7 @@ const props = defineProps({
|
|
|
74
77
|
defineEmits([]);
|
|
75
78
|
defineSlots();
|
|
76
79
|
const { filterProps } = useProps(props);
|
|
80
|
+
const { t } = useLocale();
|
|
77
81
|
const { isGroupOpen, toggleGroup, extractRows } = useGroupBy();
|
|
78
82
|
const { isSelected, isSomeSelected, select } = useSelection();
|
|
79
83
|
const { columns } = useHeaders();
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
<origam-btn
|
|
19
19
|
key="mode-btn"
|
|
20
|
+
:aria-label="t('origam.date_picker_controls.toggle_year')"
|
|
20
21
|
:disabled="disableYear"
|
|
21
22
|
:icon="modeIcon"
|
|
22
23
|
class="origam-date-picker-controls__mode-btn"
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
<origam-btn-group>
|
|
36
37
|
<template #default>
|
|
37
38
|
<origam-btn
|
|
39
|
+
:aria-label="t('origam.date_picker_controls.prev')"
|
|
38
40
|
:disabled="disablePrev"
|
|
39
41
|
:icon="prevIcon"
|
|
40
42
|
:rounded="0"
|
|
@@ -42,6 +44,7 @@
|
|
|
42
44
|
/>
|
|
43
45
|
|
|
44
46
|
<origam-btn
|
|
47
|
+
:aria-label="t('origam.date_picker_controls.next')"
|
|
45
48
|
:disabled="disableNext"
|
|
46
49
|
:icon="nextIcon"
|
|
47
50
|
:rounded="0"
|
|
@@ -58,6 +61,7 @@ import OrigamBtn from "../Btn/OrigamBtn.vue";
|
|
|
58
61
|
import OrigamBtnGroup from "../Btn/OrigamBtnGroup.vue";
|
|
59
62
|
import OrigamSpacer from "../Grids/OrigamSpacer.vue";
|
|
60
63
|
import { useUnsupportedProp } from "../../composables/Commons/unsupportedProp.composable";
|
|
64
|
+
import { useLocale } from "../../composables/Commons/locale.composable";
|
|
61
65
|
import { useProps } from "../../composables/Commons/props.composable";
|
|
62
66
|
import { useStyle } from "../../composables/Commons/style.composable";
|
|
63
67
|
import { DATE_MODE } from "../../enums/DatePicker/date-picker.enum";
|
|
@@ -94,6 +98,7 @@ useUnsupportedProp(
|
|
|
94
98
|
const emits = defineEmits(["click:year", "click:month", "click:prev", "click:next"]);
|
|
95
99
|
defineSlots();
|
|
96
100
|
const { filterProps } = useProps(props);
|
|
101
|
+
const { t } = useLocale();
|
|
97
102
|
const disableMonth = computed(() => {
|
|
98
103
|
return props.disabled || props.disabledMonth;
|
|
99
104
|
});
|
|
@@ -405,9 +405,29 @@ const menuProps = computed(() => {
|
|
|
405
405
|
return {
|
|
406
406
|
...props.menuProps,
|
|
407
407
|
activatorProps: {
|
|
408
|
+
/*********************************************************
|
|
409
|
+
* role + aria-haspopup — #818
|
|
410
|
+
*
|
|
411
|
+
* @description
|
|
412
|
+
* `'datepickerbox'` was never a valid `aria-haspopup` token
|
|
413
|
+
* (spec allows `false|true|menu|listbox|tree|grid|dialog`) —
|
|
414
|
+
* axe-core's `aria-valid-attr-value` flagged it, measured
|
|
415
|
+
* against a real browser. `'dialog'` matches the WAI-ARIA
|
|
416
|
+
* "Date Picker Dialog" pattern: a button/combobox opening a
|
|
417
|
+
* calendar dialog.
|
|
418
|
+
*
|
|
419
|
+
* @description
|
|
420
|
+
* `role: 'combobox'` is ALSO required here: `activator="parent"`
|
|
421
|
+
* (below) lands these ARIA attrs on the `.origam-field` wrapper
|
|
422
|
+
* `<div>`, which carries no role by default — `aria-allowed-attr`
|
|
423
|
+
* rejects `aria-expanded` on an element whose role doesn't
|
|
424
|
+
* support it. Same fix, same rationale as
|
|
425
|
+
* `OrigamSelect.comboboxAriaAttrs` (`role` MUST sit on the same
|
|
426
|
+
* element as `aria-haspopup`/`aria-expanded`).
|
|
427
|
+
********************************************************/
|
|
428
|
+
role: "combobox",
|
|
408
429
|
...props.menuProps?.activatorProps || {},
|
|
409
|
-
"aria-haspopup": "
|
|
410
|
-
// Set aria-haspopup to 'listbox'
|
|
430
|
+
"aria-haspopup": "dialog"
|
|
411
431
|
},
|
|
412
432
|
contentProps: {
|
|
413
433
|
...consumerContentProps,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
ref="rootEl"
|
|
6
6
|
:class="infiniteScrollClasses"
|
|
7
7
|
:style="infiniteScrollStyles"
|
|
8
|
+
:tabindex="hasFocusableDescendant ? undefined : 0"
|
|
8
9
|
>
|
|
9
10
|
<div class="origam-infinite-scroll__side" role="status" aria-live="polite" :style="typographyStyles">
|
|
10
11
|
<template v-if="hasStartIntersect">
|
|
@@ -129,6 +130,7 @@ import { IN_BROWSER } from "../../consts/Commons/commons.const";
|
|
|
129
130
|
import { DIRECTION } from "../../enums/Commons/direction.enum";
|
|
130
131
|
import { INFINITE_SCROLL_MODE, INFINITE_SCROLL_SIDE, INFINITE_SCROLL_STATUS } from "../../enums/InfiniteScroll/infinite-scroll.enum";
|
|
131
132
|
import { PROGRESS_TYPE } from "../../enums/Progress/progress.enum";
|
|
133
|
+
import { focusableChildren } from "../../utils/Commons/commons.util";
|
|
132
134
|
const props = defineProps({
|
|
133
135
|
side: { type: null, required: false, default: INFINITE_SCROLL_SIDE.END },
|
|
134
136
|
mode: { type: null, required: false, default: INFINITE_SCROLL_MODE.INTERSECT },
|
|
@@ -159,6 +161,21 @@ const { typographyStyles } = useTypography(props, "infinite-scroll__loader");
|
|
|
159
161
|
const { colorClasses, colorStyles } = useBothColor(toRef(props, "bgColor"), toRef(props, "color"));
|
|
160
162
|
const rootEl = ref();
|
|
161
163
|
const isIntersecting = shallowRef(false);
|
|
164
|
+
const hasFocusableDescendant = shallowRef(false);
|
|
165
|
+
let focusableObserver;
|
|
166
|
+
const refreshHasFocusableDescendant = () => {
|
|
167
|
+
if (!rootEl.value) return;
|
|
168
|
+
hasFocusableDescendant.value = focusableChildren(rootEl.value).length > 0;
|
|
169
|
+
};
|
|
170
|
+
onMounted(() => {
|
|
171
|
+
if (!IN_BROWSER || !rootEl.value) return;
|
|
172
|
+
refreshHasFocusableDescendant();
|
|
173
|
+
focusableObserver = new MutationObserver(refreshHasFocusableDescendant);
|
|
174
|
+
focusableObserver.observe(rootEl.value, { childList: true, subtree: true });
|
|
175
|
+
});
|
|
176
|
+
onBeforeUnmount(() => {
|
|
177
|
+
focusableObserver?.disconnect();
|
|
178
|
+
});
|
|
162
179
|
const propertyDirection = computed(() => {
|
|
163
180
|
return props.direction === DIRECTION.VERTICAL ? "scrollTop" : "scrollLeft";
|
|
164
181
|
});
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
key="container"
|
|
26
26
|
ref="containerRef"
|
|
27
27
|
class="origam-slide-group__container"
|
|
28
|
+
:tabindex="isOverflowing && !hasFocusableContent ? 0 : undefined"
|
|
28
29
|
@scroll="handleScroll"
|
|
29
30
|
>
|
|
30
31
|
<div
|
|
@@ -138,6 +139,7 @@ const { marginClasses, marginStyles } = useMargin(props);
|
|
|
138
139
|
const { displayClasses, mobile } = useDisplay(props);
|
|
139
140
|
const group = useGroup(props, ORIGAM_SLIDE_GROUP_KEY);
|
|
140
141
|
const isOverflowing = shallowRef(false);
|
|
142
|
+
const hasFocusableContent = shallowRef(false);
|
|
141
143
|
const scrollOffset = shallowRef(0);
|
|
142
144
|
const containerSize = shallowRef(0);
|
|
143
145
|
const contentSize = shallowRef(0);
|
|
@@ -172,6 +174,9 @@ if (IN_BROWSER) {
|
|
|
172
174
|
contentSize.value = contentRect.value[sizeProperty];
|
|
173
175
|
isOverflowing.value = containerSize.value + 1 < contentSize.value;
|
|
174
176
|
}
|
|
177
|
+
if (contentRef.value) {
|
|
178
|
+
hasFocusableContent.value = focusableChildren(contentRef.value).length > 0;
|
|
179
|
+
}
|
|
175
180
|
if (firstSelectedIndex.value >= 0 && contentRef.value) {
|
|
176
181
|
const selectedElement = contentRef.value.children[lastSelectedIndex.value];
|
|
177
182
|
scrollToChildren(selectedElement, props.centerActive);
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
:readonly="isReadonly"
|
|
92
92
|
:required="required || undefined"
|
|
93
93
|
:aria-required="required ? 'true' : undefined"
|
|
94
|
+
:role="role"
|
|
94
95
|
:size="1"
|
|
95
96
|
:type="resolvedInputType"
|
|
96
97
|
:value="displayValue"
|
|
@@ -208,6 +209,7 @@ import { applyMask } from "../../utils/Commons/apply-mask.util";
|
|
|
208
209
|
import { filterInputAttrs } from "../../utils/Input/input.util";
|
|
209
210
|
import { forwardRefs } from "../../utils/Commons/forwardRefs.util";
|
|
210
211
|
import { resolveMaskConfig } from "../../utils/Commons/resolve-mask-config.util";
|
|
212
|
+
defineOptions({ inheritAttrs: false });
|
|
211
213
|
const props = defineProps({
|
|
212
214
|
autofocus: { type: Boolean, required: false },
|
|
213
215
|
counter: { type: [Boolean, Number, String], required: false },
|