@tristan2828/ui-foundation 3.27.2 → 3.27.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.
@@ -19,11 +19,12 @@ export declare function forceLoggedOut(page: Page): Promise<void>;
19
19
  */
20
20
  export declare const POPUP_FOCUS_GUARDS = "[data-base-ui-focus-guard]";
21
21
  /**
22
- * axe on the page as it is now. The options are for a state whose
23
- * structure a rule doesn't fit, said where they're used: a list open in a
24
- * portal sits outside the page's landmarks by design (`disableRules:
25
- * ['region']`) and brings focus guards (`exclude: [POPUP_FOCUS_GUARDS]`),
26
- * while every other rule, contrast included, still checks the list.
22
+ * axe on the page as it is now, once its transitions have finished. The
23
+ * options are for a state whose structure a rule doesn't fit, said where
24
+ * they're used: a list open in a portal sits outside the page's landmarks
25
+ * by design (`disableRules: ['region']`) and brings focus guards
26
+ * (`exclude: [POPUP_FOCUS_GUARDS]`), while every other rule, contrast
27
+ * included, still checks the list.
27
28
  */
28
29
  export declare function expectNoAxeViolations(page: Page, options?: {
29
30
  disableRules?: string[];
@@ -44,13 +44,33 @@ export function forceLoggedOut(page) {
44
44
  */
45
45
  export const POPUP_FOCUS_GUARDS = '[data-base-ui-focus-guard]';
46
46
  /**
47
- * axe on the page as it is now. The options are for a state whose
48
- * structure a rule doesn't fit, said where they're used: a list open in a
49
- * portal sits outside the page's landmarks by design (`disableRules:
50
- * ['region']`) and brings focus guards (`exclude: [POPUP_FOCUS_GUARDS]`),
51
- * while every other rule, contrast included, still checks the list.
47
+ * Resolves once the page's open and close transitions have finished. A
48
+ * popup fading in is already "visible" to Playwright, and its text is
49
+ * blended with the page behind: axe measured a sheet's muted labels at
50
+ * 4.37:1 mid-fade, 4.74:1 once shown, and failed CI on correct colours.
51
+ * Two frames first, so a transition that starts on the next frame (Base
52
+ * UI drops `data-starting-style` there) is running by the time it's read.
53
+ * Endless animations (a spinner, a skeleton's pulse) are left out.
54
+ */
55
+ async function waitForTransitions(page) {
56
+ await page.evaluate(async () => {
57
+ await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
58
+ const ending = document
59
+ .getAnimations()
60
+ .filter((animation) => animation.effect?.getComputedTiming().endTime !== Infinity);
61
+ await Promise.all(ending.map((animation) => animation.finished.catch(() => undefined)));
62
+ });
63
+ }
64
+ /**
65
+ * axe on the page as it is now, once its transitions have finished. The
66
+ * options are for a state whose structure a rule doesn't fit, said where
67
+ * they're used: a list open in a portal sits outside the page's landmarks
68
+ * by design (`disableRules: ['region']`) and brings focus guards
69
+ * (`exclude: [POPUP_FOCUS_GUARDS]`), while every other rule, contrast
70
+ * included, still checks the list.
52
71
  */
53
72
  export async function expectNoAxeViolations(page, options = {}) {
73
+ await waitForTransitions(page);
54
74
  const builder = new AxeBuilder({ page });
55
75
  if (options.disableRules?.length)
56
76
  builder.disableRules(options.disableRules);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tristan2828/ui-foundation",
3
- "version": "3.27.2",
3
+ "version": "3.27.4",
4
4
  "description": "Shared UI foundation for contract-first, database-backed React apps: shadcn primitives, DataTable/EntityForm composites, auth, the gateway error seam, design tokens, lint rules and the AI playbook.",
5
5
  "license": "MIT",
6
6
  "repository": {