origam 2.18.2 → 2.18.4
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 +242 -0
- package/dist/src/components/ColorPickerField/OrigamColorPickerField.vue +24 -2
- package/dist/src/components/DatePickerField/OrigamDatePickerField.vue +22 -2
- package/dist/src/components/TextField/OrigamTextField.vue +2 -0
- package/dist/src/directives/Contrast/contrast.directive.cjs +15 -1
- package/dist/src/directives/Contrast/contrast.directive.js +15 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,71 @@ 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"
|
|
@@ -156,6 +221,105 @@ conclure qu'elle ne l'est plus par défaut.
|
|
|
156
221
|
|
|
157
222
|
### Fixed
|
|
158
223
|
|
|
224
|
+
- **#871 (clôture) — les 7 dernières violations AA étaient UN défaut de
|
|
225
|
+
palette répété, pas sept défauts de composant.** Mesuré avec
|
|
226
|
+
`packages/tests/audit/dark-contrast.audit.mjs` : **7 / 1 664 → 0 / 1 664**,
|
|
227
|
+
les 8 identités × 2 modes × 2 portées, quatre contrôles verts des deux
|
|
228
|
+
côtés. Exactement **7 lignes changent, aucune ne se dégrade**, et le ratio
|
|
229
|
+
minimum de toute la surface passe de 3.65 à **4.70**.
|
|
230
|
+
|
|
231
|
+
Les 7 se répartissaient en deux causes, toutes deux dans
|
|
232
|
+
`packages/marketing/src/themes` — **aucune dans le DS**, qui était déjà à
|
|
233
|
+
zéro :
|
|
234
|
+
|
|
235
|
+
| cause | marque · mode | couple | avant | après |
|
|
236
|
+
|---|---|---|---|---|
|
|
237
|
+
| encre héritée du mode clair | `apple` sombre | `#ffffff` → `#000000` sur `#0a84ff` | 3.65 | **5.76** |
|
|
238
|
+
| encre héritée du mode clair | `ecom` sombre | `#ffffff` → `#1a0f0a` sur `#f43f5e` | 3.67 | **5.12** |
|
|
239
|
+
| jeton de rôle inversé | `ecom` clair | `action.primary.bg` → `.fgSubtle` sur la crème | 4.43 | **5.93** |
|
|
240
|
+
|
|
241
|
+
**La cause commune.** Le mode sombre ÉCLAIRCIT l'accent de marque — c'est
|
|
242
|
+
la bonne pratique pour une grande surface (`#0071e3` → `#0a84ff` chez
|
|
243
|
+
`apple`, rose-600 → rose-500 chez `ecom`) — mais les deux palettes gardaient
|
|
244
|
+
l'encre `#ffffff` de leur jumeau clair. Or chacune applique **déjà** la règle
|
|
245
|
+
inverse à ses intentions `feedback` : les 4 d'`ecom` et 3 des 4 d'`apple`
|
|
246
|
+
encrent leur accent éclairci avec le `text.inverse` de la palette.
|
|
247
|
+
`action.primary` était la dernière exception. Le badge n'était pas en cause :
|
|
248
|
+
`--origam-badge__badge---{background-color,color}` lit exactement ce couple —
|
|
249
|
+
tout comme `--origam-btn--primary---*`, qui n'est pas dans la sonde et était
|
|
250
|
+
donc en défaut sans être mesuré.
|
|
251
|
+
|
|
252
|
+
⚠️ **On n'a PAS foncé l'accent**, bien que ce soit l'autre levier : chez
|
|
253
|
+
`apple`, `action.primary.bg` sert aussi d'ENCRE (le bloc `components` passe
|
|
254
|
+
ce `var()` en `color` sur `origam-breadcrumb-item`). Le ramener à `#0071e3`
|
|
255
|
+
donnait 4.70 sous du blanc mais faisait tomber le fil d'Ariane à **4.47** sur
|
|
256
|
+
le fond noir — un défaut échangé contre un autre. Vérifié par calcul avant
|
|
257
|
+
d'écrire la moindre ligne.
|
|
258
|
+
|
|
259
|
+
La 3ᵉ cause est une erreur de RÔLE : `bg` est un jeton de surface, `fgSubtle`
|
|
260
|
+
la teinte prévue pour être LUE sur une surface neutre. `ecomDarkTheme` posait
|
|
261
|
+
déjà `fgSubtle` ; seul le bloc clair se trompait de jeton. Les deux modes sont
|
|
262
|
+
désormais symétriques.
|
|
263
|
+
|
|
264
|
+
- **#871 — une 8ᵉ violation, que la sonde ne pouvait pas voir** :
|
|
265
|
+
`material` sombre, `feedback.success`, `#1b5e20` sur `#81c784` = **3.91**
|
|
266
|
+
→ `#0d3b10` = **6.32**. La sonde de `dark-contrast.audit.mjs` ne rend qu'UNE
|
|
267
|
+
intention par famille de composant, donc elle ne peint jamais
|
|
268
|
+
`feedback.success`. Trouvée par la spec statique ajoutée ci-dessous. C'est
|
|
269
|
+
l'encre qui était l'intruse, pas le conteneur : les trois autres intentions
|
|
270
|
+
sombres de `material` tiennent 5.79 / 7.70 / 7.71 avec des encres bespoke
|
|
271
|
+
très profondes, `success` était la seule à se contenter du green-900 de la
|
|
272
|
+
rampe.
|
|
273
|
+
|
|
274
|
+
- **#871 — l'audit lui-même ne démarrait plus.** Son `npx vite build` résolvait
|
|
275
|
+
vite par le PATH, via un lien `node_modules/.bin` qu'un peer auto-installé
|
|
276
|
+
ne garantit pas : sur un `pnpm install --frozen-lockfile` propre,
|
|
277
|
+
`packages/tests/node_modules/.bin/` ne contient que `playwright` et `vitest`,
|
|
278
|
+
et la commande mourait en `127 — sh: vite: command not found`. **L'audit ne
|
|
279
|
+
rendait plus aucun chiffre**, et rien ne le signalait puisque rien ne le
|
|
280
|
+
rejoue en CI. `resolveViteBin()` passe désormais par le realpath de
|
|
281
|
+
`@vitejs/plugin-vue`, ce qui ne dépend ni du PATH ni du hoisting et
|
|
282
|
+
sélectionne la copie de vite que le plugin utilise réellement (deux majeures
|
|
283
|
+
coexistent dans le magasin). Les commentaires qui affirmaient le contraire
|
|
284
|
+
dans les deux fichiers ont été corrigés.
|
|
285
|
+
|
|
286
|
+
- **#871 — l'état SURVOL était en défaut plus grave encore, et le correctif
|
|
287
|
+
d'encre l'a réglé sans qu'on y touche.** Le survol n'a pas d'encre à lui :
|
|
288
|
+
`--origam-btn--primary---background-color-hover` bascule la surface, mais il
|
|
289
|
+
n'existe **aucun** `--origam-btn--primary---color-hover` (ni de
|
|
290
|
+
`--origam-badge__badge---color-hover`) — l'encre reste `action.primary.fg`
|
|
291
|
+
dans les deux états. Sous l'encre blanche, `apple` sombre y tombait à
|
|
292
|
+
**2.19:1** et `ecom` sombre à **2.69:1** : *pires que n'importe laquelle des
|
|
293
|
+
7 violations comptées*, et invisibles parce que la sonde de
|
|
294
|
+
`dark-contrast.audit.mjs` ne rend que l'état au repos.
|
|
295
|
+
|
|
296
|
+
**Aucun `bgHover` n'a été modifié.** La direction du survol n'était pas le
|
|
297
|
+
problème — l'encre l'était. Mesuré sur les deux marques :
|
|
298
|
+
|
|
299
|
+
| mode · encre | direction du survol | ratio repos → survol |
|
|
300
|
+
|---|---|---|
|
|
301
|
+
| clair · blanche | fonce | 4.70 → **6.95** / **6.29** ↑ |
|
|
302
|
+
| sombre · blanche *(avant)* | éclaircit | 3.65 → **2.19** / 3.67 → **2.69** ↓ |
|
|
303
|
+
| sombre · foncée *(après)* | éclaircit | 5.76 → **9.60** / 5.12 → **6.99** ↑ |
|
|
304
|
+
|
|
305
|
+
La règle n'est donc pas *« le survol doit foncer »* mais **« le survol doit
|
|
306
|
+
déplacer l'accent à l'opposé de la clarté de son encre »**. Les palettes la
|
|
307
|
+
respectaient déjà dans 3 quadrants sur 4 ; le seul cassé l'était par son
|
|
308
|
+
encre. L'idiome « le survol éclaircit », qui est le bon en mode sombre, est
|
|
309
|
+
conservé dans les deux marques.
|
|
310
|
+
|
|
311
|
+
- **#871 — non-régression : `packages/tests/TU/marketing/brand-palette-contrast.spec.ts`.**
|
|
312
|
+
Les couples `{bg, fg}` littéraux des 7 palettes de marque (14 thèmes) doivent
|
|
313
|
+
tenir AA. L'audit de #871 est un script qu'on lance à la main : rien ne le
|
|
314
|
+
rejoue, donc rien n'empêchait une retouche de palette de ramener ces couples
|
|
315
|
+
en silence. La spec lit les hex **dans l'objet `IOrigamTheme`**, avant toute
|
|
316
|
+
indirection CSS — la règle jsdom/`var()` du `CLAUDE.md` ne s'y applique pas,
|
|
317
|
+
et le navigateur reste l'arbitre de ce qui est peint. Elle embarque ses
|
|
318
|
+
propres garde-fous : les 3 témoins de la formule, un contrôle négatif sur les
|
|
319
|
+
3 couples corrigés, et un plancher de couverture qui rend bruyante une dérive
|
|
320
|
+
du filtre. **Vérifiée ROUGE sur `origin/develop`** : 3 échecs / 14 succès.
|
|
321
|
+
C'est elle qui a trouvé la 8ᵉ violation ci-dessus.
|
|
322
|
+
|
|
159
323
|
- **#871 — le mode sombre ne peignait qu'à moitié dans un sous-arbre thémé.**
|
|
160
324
|
Les feuilles de tokens portent ~1 761 déclarations **dérivées**
|
|
161
325
|
(`--origam-title---color: var(--origam-color__text---primary)`). Une custom
|
|
@@ -301,6 +465,84 @@ conclure qu'elle ne l'est plus par défaut.
|
|
|
301
465
|
|
|
302
466
|
---
|
|
303
467
|
|
|
468
|
+
#### `v-contrast` était silencieusement inerte sur fond opaque (#869)
|
|
469
|
+
|
|
470
|
+
La directive `v-contrast`, câblée sur 30 composants, n'émettait **jamais**
|
|
471
|
+
rien — ni classe, ni `color: !important`, ni `console.warn` — dès que fond
|
|
472
|
+
**et** texte étaient tous deux OPAQUES, c'est-à-dire la configuration par
|
|
473
|
+
défaut du DS. Deux ruptures indépendantes dans le code partagé, corrigeant
|
|
474
|
+
l'une ne suffisait pas sans l'autre :
|
|
475
|
+
|
|
476
|
+
1. **`toRgb()`** faisait **toujours** passer la couleur par un aller-retour
|
|
477
|
+
`canvas.fillStyle`, même quand `getComputedStyle` avait déjà rendu une
|
|
478
|
+
chaîne `rgb()/rgba()` exploitable. Le getter `fillStyle` de Chromium
|
|
479
|
+
sérialise toute couleur OPAQUE en hexadécimal `#rrggbb` (seul l'alpha < 1
|
|
480
|
+
ressort en `rgba(…)`), ce qui transformait une valeur déjà utilisable en
|
|
481
|
+
une valeur morte pour `rgbaParts`/`channelsOf`.
|
|
482
|
+
2. **`channelsOf()`** extrayait les canaux via `match(/[\d.]+/g)`, taillé
|
|
483
|
+
pour `rgb(r, g, b)` — une suite de chiffres hexadécimaux SANS séparateur
|
|
484
|
+
(ex. `#777777`, six chiffres) s'agrège en UN seul grand nombre au lieu de
|
|
485
|
+
trois, donc `channelsOf` rend `null` sur exactement la forme hex que la
|
|
486
|
+
rupture 1 pouvait encore produire (couleur nommée, `hsl()`, …
|
|
487
|
+
nécessitant légitimement le détour par le canvas).
|
|
488
|
+
|
|
489
|
+
**Correctif** : `toRgb()` court-circuite désormais le canvas quand l'entrée
|
|
490
|
+
est déjà `rgb()/rgba()` (le cas de la quasi-totalité des appels, puisqu'ils
|
|
491
|
+
lisent `getComputedStyle`), et le chemin canvas restant convertit son
|
|
492
|
+
résultat hexadécimal en `rgb()/rgba()` via un nouveau `hexToRgb()` avant de
|
|
493
|
+
le rendre — `toRgb()` ne rend plus jamais de hex. La mécanique de correction
|
|
494
|
+
elle-même (forcer noir/blanc, poser `data-origam-contrast-fixed`, journaliser
|
|
495
|
+
le ratio) n'est pas modifiée : elle fonctionnait déjà, elle n'était
|
|
496
|
+
simplement jamais atteinte.
|
|
497
|
+
|
|
498
|
+
**Preuve** — `packages/tests/e2e/contrast-directive.spec.ts` (Playwright,
|
|
499
|
+
Chromium réel — jsdom n'a pas de `canvas.getContext('2d')` fonctionnel dans
|
|
500
|
+
ce dépôt, donc ne peut ni reproduire ni vérifier ce bug) : contrôle positif
|
|
501
|
+
(`#777777` sur blanc, 4.48:1, opaque des deux côtés) déclenche désormais la
|
|
502
|
+
correction + le `console.warn` ; contrôle négatif (noir sur blanc, 21:1) ne
|
|
503
|
+
déclenche rien ; non-régression du chemin translucide (fond composé via
|
|
504
|
+
`resolvePaintedBackground`) inchangée. **A/B contre le commit parent** : 4
|
|
505
|
+
des 5 tests rougissent sur le code d'avant (le contrôle négatif reste vert
|
|
506
|
+
des deux côtés, comme attendu).
|
|
507
|
+
|
|
508
|
+
⚠️ **Rayon mesuré séparément, pas hérité de #871 — et le chiffre publié par
|
|
509
|
+
#871 contenait un artefact, corrigé depuis par #883.** Le chiffrage initial
|
|
510
|
+
du ticket (346 violations en mode sombre) datait d'avant #876. #876 avait
|
|
511
|
+
d'abord publié **189 → 11** ; en rejouant `audit/dark-contrast.audit.mjs`
|
|
512
|
+
avec la VRAIE directive (redirect du stub désactivé) au lieu de sa
|
|
513
|
+
reformulation mathématique, seuls **7 `console.warn` réels** se
|
|
514
|
+
déclenchaient sur les 32 configurations (30 composants × 8 identités × 2
|
|
515
|
+
modes, portées racine + sous-arbre) — écart signalé à l'auteur de #876, qui
|
|
516
|
+
l'a reproduit, creusé et corrigé sous #883 : **les bornes AVANT et APRÈS
|
|
517
|
+
étaient toutes les deux fausses (185 → 7, pas 189 → 11)**, la même erreur de
|
|
518
|
+
mesure existant déjà dans le comptage "avant". Le delta réel (178) n'a
|
|
519
|
+
jamais changé.
|
|
520
|
+
|
|
521
|
+
L'écart venait d'`origam-tooltip__content` (identité `apple`, root + subtree,
|
|
522
|
+
light + dark) et, découvert par #883, également d'`origam-bottom-nav` sur la
|
|
523
|
+
même identité. Fond réel : `color(srgb 0.898039 0.898039 0.905882 / 0.94)`
|
|
524
|
+
(translucide) ; `toRgb()` le résout correctement en `rgb(215, 215, 217)` une
|
|
525
|
+
fois composé sur son ancêtre opaque, contre du texte noir : **ratio 14.61:1,
|
|
526
|
+
conforme** — vérifié en appelant directement `toRgb()`/
|
|
527
|
+
`resolvePaintedBackground()` de la vraie directive sur cette page. La
|
|
528
|
+
fonction `parse()` de `dark-contrast.audit.mjs` (une réimplémentation
|
|
529
|
+
distincte, pas la directive) ne reconnaissait que `rgba?\(…\)` — elle
|
|
530
|
+
ignorait silencieusement cette couche `color(srgb …)`, sautait jusqu'à
|
|
531
|
+
l'ancêtre opaque suivant et rapportait à tort un faux "noir sur noir"
|
|
532
|
+
(ratio 1.00). **Ce n'était pas un défaut du DS ni de `v-contrast`** — la
|
|
533
|
+
même classe de lacune que ce ticket corrige, mais dans un autre fichier. Les
|
|
534
|
+
7 restantes (4 `origam-badge__badge`, 3 `origam-breadcrumb-item`) sont
|
|
535
|
+
confirmées identiques entre les deux mesures, et `sous 2:1` passe à **0** une
|
|
536
|
+
fois l'artefact retiré : plus rien de ce qui reste n'est proche d'invisible.
|
|
537
|
+
|
|
538
|
+
Non corrigé ici : `dark-contrast.audit.mjs` appartenait à #871/#876, pas à ce
|
|
539
|
+
ticket — signalé, reproduit et corrigé séparément sous #883 (ne pas citer
|
|
540
|
+
189 → 11, c'était faux des deux côtés).
|
|
541
|
+
|
|
542
|
+
**Rupture d'API** : aucune — `toRgb`/`channelsOf`/`hexToRgb` restent des
|
|
543
|
+
fonctions privées du module, non exportées ; la surface publique
|
|
544
|
+
(`setContrastConfig`, `v-contrast` par défaut) est inchangée.
|
|
545
|
+
|
|
304
546
|
## [2.18.0] - 2026-09-19
|
|
305
547
|
|
|
306
548
|
80 commits depuis `v2.17.1`. L'essentiel est du **correctif** : des tokens qui
|
|
@@ -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,
|
|
@@ -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,
|
|
@@ -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 },
|
|
@@ -34,10 +34,24 @@ function srgbToRgb(color) {
|
|
|
34
34
|
const a = m[4] !== void 0 ? parseFloat(m[4]) : 1;
|
|
35
35
|
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
36
36
|
}
|
|
37
|
+
function hexToRgb(hex) {
|
|
38
|
+
const m = hex.match(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
|
|
39
|
+
if (!m) return null;
|
|
40
|
+
const h = m[1].length <= 4 ? [...m[1]].map(c => c + c).join("") : m[1];
|
|
41
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
42
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
43
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
44
|
+
if (h.length === 8) {
|
|
45
|
+
const a = parseInt(h.slice(6, 8), 16) / 255;
|
|
46
|
+
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
47
|
+
}
|
|
48
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
49
|
+
}
|
|
37
50
|
function toRgb(color) {
|
|
38
51
|
if (!color) return null;
|
|
39
52
|
const direct = srgbToRgb(color);
|
|
40
53
|
if (direct) return direct;
|
|
54
|
+
if (/^rgba?\(/i.test(color)) return color;
|
|
41
55
|
if (typeof document === "undefined") return color;
|
|
42
56
|
if (normCtx === void 0) normCtx = document.createElement("canvas").getContext("2d");
|
|
43
57
|
if (!normCtx) return color;
|
|
@@ -48,7 +62,7 @@ function toRgb(color) {
|
|
|
48
62
|
normCtx.fillStyle = color;
|
|
49
63
|
const onWhite = normCtx.fillStyle;
|
|
50
64
|
if (onBlack !== onWhite) return null;
|
|
51
|
-
return srgbToRgb(onBlack) ?? onBlack;
|
|
65
|
+
return srgbToRgb(onBlack) ?? hexToRgb(onBlack) ?? onBlack;
|
|
52
66
|
}
|
|
53
67
|
function rgbaParts(rgb) {
|
|
54
68
|
if (!rgb) return null;
|
|
@@ -22,10 +22,24 @@ function srgbToRgb(color) {
|
|
|
22
22
|
const a = m[4] !== void 0 ? parseFloat(m[4]) : 1;
|
|
23
23
|
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
24
24
|
}
|
|
25
|
+
function hexToRgb(hex) {
|
|
26
|
+
const m = hex.match(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
|
|
27
|
+
if (!m) return null;
|
|
28
|
+
const h = m[1].length <= 4 ? [...m[1]].map((c) => c + c).join("") : m[1];
|
|
29
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
30
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
31
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
32
|
+
if (h.length === 8) {
|
|
33
|
+
const a = parseInt(h.slice(6, 8), 16) / 255;
|
|
34
|
+
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
35
|
+
}
|
|
36
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
37
|
+
}
|
|
25
38
|
function toRgb(color) {
|
|
26
39
|
if (!color) return null;
|
|
27
40
|
const direct = srgbToRgb(color);
|
|
28
41
|
if (direct) return direct;
|
|
42
|
+
if (/^rgba?\(/i.test(color)) return color;
|
|
29
43
|
if (typeof document === "undefined") return color;
|
|
30
44
|
if (normCtx === void 0) normCtx = document.createElement("canvas").getContext("2d");
|
|
31
45
|
if (!normCtx) return color;
|
|
@@ -36,7 +50,7 @@ function toRgb(color) {
|
|
|
36
50
|
normCtx.fillStyle = color;
|
|
37
51
|
const onWhite = normCtx.fillStyle;
|
|
38
52
|
if (onBlack !== onWhite) return null;
|
|
39
|
-
return srgbToRgb(onBlack) ?? onBlack;
|
|
53
|
+
return srgbToRgb(onBlack) ?? hexToRgb(onBlack) ?? onBlack;
|
|
40
54
|
}
|
|
41
55
|
function rgbaParts(rgb) {
|
|
42
56
|
if (!rgb) return null;
|