@setsquare/ui 0.2.4 → 0.5.0

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/NOTICE +1 -1
  3. package/README.md +17 -7
  4. package/dist/index.d.ts +11 -4
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +5 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/page-items.d.ts +10 -0
  9. package/dist/lib/page-items.d.ts.map +1 -0
  10. package/dist/lib/page-items.js +26 -0
  11. package/dist/lib/page-items.js.map +1 -0
  12. package/dist/lint/index.d.ts +5 -0
  13. package/dist/lint/index.d.ts.map +1 -1
  14. package/dist/lint/index.js +84 -3
  15. package/dist/lint/index.js.map +1 -1
  16. package/dist/shell/sidebar.d.ts +23 -0
  17. package/dist/shell/sidebar.d.ts.map +1 -1
  18. package/dist/shell/sidebar.js +22 -3
  19. package/dist/shell/sidebar.js.map +1 -1
  20. package/dist/styles/theme.css +550 -0
  21. package/dist/styles/tokens.css +17 -0
  22. package/dist/testing/index.d.ts +57 -2
  23. package/dist/testing/index.d.ts.map +1 -1
  24. package/dist/testing/index.js +276 -11
  25. package/dist/testing/index.js.map +1 -1
  26. package/dist/tokens.json +45 -0
  27. package/dist/ui/breadcrumbs.d.ts +9 -1
  28. package/dist/ui/breadcrumbs.d.ts.map +1 -1
  29. package/dist/ui/breadcrumbs.js +3 -3
  30. package/dist/ui/breadcrumbs.js.map +1 -1
  31. package/dist/ui/button-styles.d.ts.map +1 -1
  32. package/dist/ui/button-styles.js +16 -6
  33. package/dist/ui/button-styles.js.map +1 -1
  34. package/dist/ui/combo-box.d.ts +6 -1
  35. package/dist/ui/combo-box.d.ts.map +1 -1
  36. package/dist/ui/combo-box.js +2 -2
  37. package/dist/ui/combo-box.js.map +1 -1
  38. package/dist/ui/drop-indicator.d.ts +21 -0
  39. package/dist/ui/drop-indicator.d.ts.map +1 -0
  40. package/dist/ui/drop-indicator.js +20 -0
  41. package/dist/ui/drop-indicator.js.map +1 -0
  42. package/dist/ui/feature-card.d.ts.map +1 -1
  43. package/dist/ui/feature-card.js.map +1 -1
  44. package/dist/ui/grid-list.d.ts +10 -3
  45. package/dist/ui/grid-list.d.ts.map +1 -1
  46. package/dist/ui/grid-list.js +18 -7
  47. package/dist/ui/grid-list.js.map +1 -1
  48. package/dist/ui/pagination.d.ts +69 -14
  49. package/dist/ui/pagination.d.ts.map +1 -1
  50. package/dist/ui/pagination.js +177 -32
  51. package/dist/ui/pagination.js.map +1 -1
  52. package/dist/ui/switch.d.ts +2 -1
  53. package/dist/ui/switch.d.ts.map +1 -1
  54. package/dist/ui/switch.js +5 -2
  55. package/dist/ui/switch.js.map +1 -1
  56. package/dist/ui/tabs.d.ts +82 -15
  57. package/dist/ui/tabs.d.ts.map +1 -1
  58. package/dist/ui/tabs.js +274 -16
  59. package/dist/ui/tabs.js.map +1 -1
  60. package/dist/ui/time-of-day-icon.d.ts.map +1 -1
  61. package/dist/ui/time-of-day-icon.js.map +1 -1
  62. package/dist/ui/toolbar.d.ts +22 -0
  63. package/dist/ui/toolbar.d.ts.map +1 -0
  64. package/dist/ui/toolbar.js +22 -0
  65. package/dist/ui/toolbar.js.map +1 -0
  66. package/dist/ui/tree.d.ts +98 -0
  67. package/dist/ui/tree.d.ts.map +1 -0
  68. package/dist/ui/tree.js +75 -0
  69. package/dist/ui/tree.js.map +1 -0
  70. package/package.json +21 -25
  71. package/templates/next/page-transition.tsx +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,mGAAmG;IACnG,IAAI,EAAE,cAAc,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IACzD,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,GAAE,UAAqB,GAAG,kBAAkB,EAAE,CAmE5F;AAED,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,GAAE,UAAqB,GAAG,oBAAoB,EAAE,CAkG7F;AAED,kDAAkD;AAClD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;CAC/C;AAED,mCAAmC;AACnC,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAUlF;AAED;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB/E;AAMD,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,0FAA0F;IAC1F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,kBAAuB,EAChC,IAAI,GAAE,UAAqB,GAC1B,aAAa,EAAE,CA4DjB;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,UAAqB,GAAG,cAAc,EAAE,CA+EpF;AAED,uGAAuG;AACvG,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAcf;AAID,sFAAsF;AACtF,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,QAAQ,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAChD,UAAU,CAAC,OAAO,EAAE;QAClB,IAAI,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC9D,UAAU,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;QAClC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,mBAAmB;IAClC,6FAA6F;IAC7F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAWD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,cAAc,EAAE,CAAC,CAqK3B;AAED,mHAAmH;AACnH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CAaf;AAID,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,wFAAwF;IACxF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,MAAM,EAAE,aAAa,GAAG,cAAc,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,WAAgB,EACzB,IAAI,GAAE,UAAqB,GAC1B,WAAW,EAAE,CAuGf;AAED,yGAAyG;AACzG,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAaf;AAID,wFAAwF;AACxF,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,KAAK,EAAE;QAAE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;CACtD;AAED;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAClB,OAAO,GACP,MAAM,GACN,MAAM,GACN,UAAU,GACV,SAAS,GACT,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;IACvD,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mGAAmG;IACnG,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC;CACrD;AAQD;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,aAAa,EAAE,CAAC,CAoJ1B;AAED,wFAAwF;AACxF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAYf;AAID,MAAM,WAAW,YAAY;IAC3B,+FAA+F;IAC/F,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,mBAAwB,EACjC,IAAI,GAAE,UAAqB,GAC1B,YAAY,EAAE,CA8EhB;AAED,+GAA+G;AAC/G,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CAaf;AAID,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAChC,4GAA4G;IAC5G,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,OAAO,GAAE,kBAAuB,EAChC,IAAI,GAAE,UAAqB,GAC1B,WAAW,EAAE,CAyEf;AAED,wGAAwG;AACxG,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAYf;AAID,MAAM,WAAW,gBAAgB;IAC/B,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,6GAA6G;IAC7G,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,gBAAqB,EAC9B,IAAI,GAAE,UAAqB,GAC1B,gBAAgB,EAAE,CAgEpB;AAED,uGAAuG;AACvG,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAYf"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
@@ -404,6 +404,21 @@ export async function expectTargetsSized(page, options = {}) {
404
404
  * This is the check that finds a ring styled on an element that never receives focus.
405
405
  */
406
406
  export async function findInvisibleFocus(page, options = {}) {
407
+ const out = [];
408
+ await probeFocus(page, options, async ({ box, away, here }) => {
409
+ if (!box || !away || !here)
410
+ return;
411
+ const same = away.length === here.length && away.every((byte, k) => byte === here[k]);
412
+ if (same)
413
+ out.push({ element: box.described, reason: 'unchanged' });
414
+ }, (finding) => out.push(finding));
415
+ return out;
416
+ }
417
+ /**
418
+ * Focuses every control a keyboard can reach, one at a time, with focus moved away first, and hands `compare` a
419
+ * screenshot of it (and `pad` px around it) without focus and with it. Shared by the focus checks.
420
+ */
421
+ async function probeFocus(page, options, compare, skipped) {
407
422
  const pad = options.pad ?? 8;
408
423
  const count = await page.evaluate((ignore) => {
409
424
  const style = document.createElement('style');
@@ -436,7 +451,6 @@ export async function findInvisibleFocus(page, options = {}) {
436
451
  // Keyboard modality, for React Aria's data-focus-visible and the browser's :focus-visible. (React Aria
437
452
  // ignores Shift and the other modifiers, so it has to be a real key.)
438
453
  await page.keyboard.press('Tab');
439
- const out = [];
440
454
  for (let i = 0; i < count; i++) {
441
455
  // 1. Put focus on another control, far from this one, and measure this one where it now sits.
442
456
  const box = await page.evaluate(async (args) => {
@@ -461,9 +475,38 @@ export async function findInvisibleFocus(page, options = {}) {
461
475
  shown = p;
462
476
  break;
463
477
  }
478
+ // The component, as 2.4.13 measures it: its visible presentation, which the focus indicator follows (W3C's
479
+ // Understanding page uses the indicator itself as the gauge). That's the first part, the control or something
480
+ // inside it, whose outline or box shadow changes when it takes focus; the control itself if none does.
481
+ const parts = [shown, ...shown.querySelectorAll('*')].slice(0, 60);
482
+ // Unfocused first: the first control has focus from the Tab that set keyboard modality.
483
+ if (shown.contains(document.activeElement)) {
484
+ document.activeElement.blur();
485
+ await frames();
486
+ }
487
+ const ring = (e) => [undefined, '::before', '::after'].map((pseudo) => {
488
+ const st = getComputedStyle(e, pseudo);
489
+ // An outline that isn't drawn has a width and colour that mean nothing.
490
+ const outline = st.outlineStyle === 'none'
491
+ ? 'none'
492
+ : `${st.outlineStyle} ${st.outlineWidth} ${st.outlineColor}`;
493
+ return `${outline}|${st.boxShadow}`;
494
+ });
495
+ const unfocused = parts.map(ring);
464
496
  // Where it is when focused (a skip link only appears then) and when not: compare the union of both.
465
497
  el.focus({ preventScroll: true });
466
498
  await frames();
499
+ let bearer = shown;
500
+ let bearerPseudo;
501
+ find: for (const [k, part] of parts.entries()) {
502
+ const now = ring(part);
503
+ for (const [j, pseudo] of [undefined, '::before', '::after'].entries())
504
+ if (now[j] !== unfocused[k][j]) {
505
+ bearer = part;
506
+ bearerPseudo = pseudo;
507
+ break find;
508
+ }
509
+ }
467
510
  const r1 = shown.getBoundingClientRect();
468
511
  const clip = {
469
512
  left: r1.left - args.pad,
@@ -490,6 +533,25 @@ export async function findInvisibleFocus(page, options = {}) {
490
533
  margin = 0;
491
534
  others = all.filter((x) => apart(x.r, r1));
492
535
  }
536
+ // Moving focus away can scroll the page (React Aria scrolls a focused row into view), taking this control
537
+ // out of the frame the screenshots compare. Put every scroll position back once focus has moved.
538
+ const scrolled = [
539
+ document.scrollingElement,
540
+ ...(function* () {
541
+ for (let p = el.parentElement; p; p = p.parentElement)
542
+ yield p;
543
+ })(),
544
+ ].filter((x) => !!x &&
545
+ (x.scrollTop !== 0 ||
546
+ x.scrollLeft !== 0 ||
547
+ x.scrollHeight > x.clientHeight ||
548
+ x.scrollWidth > x.clientWidth));
549
+ const positions = scrolled.map((x) => [x, x.scrollLeft, x.scrollTop]);
550
+ const restore = async () => {
551
+ for (const [x, left, top] of positions)
552
+ x.scrollTo({ left, top, behavior: 'instant' });
553
+ await frames();
554
+ };
493
555
  let away = false;
494
556
  for (const { o } of others.slice(0, 8)) {
495
557
  o.focus({ preventScroll: true });
@@ -504,6 +566,7 @@ export async function findInvisibleFocus(page, options = {}) {
504
566
  await frames();
505
567
  away = document.activeElement !== el && !shown.contains(document.activeElement);
506
568
  }
569
+ await restore();
507
570
  const r2 = shown.getBoundingClientRect();
508
571
  const left = Math.min(r1.left, r2.left) - margin;
509
572
  const top = Math.min(r1.top, r2.top) - margin;
@@ -523,20 +586,41 @@ export async function findInvisibleFocus(page, options = {}) {
523
586
  height,
524
587
  trapped: !away,
525
588
  described: `${el.tagName.toLowerCase()}${role ? `[role=${role}]` : ''}${name ? ` "${name}"` : ''}`,
589
+ own: (() => {
590
+ const b = bearer.getBoundingClientRect();
591
+ const st = getComputedStyle(bearer, bearerPseudo);
592
+ const corners = [
593
+ st.borderTopLeftRadius,
594
+ st.borderTopRightRadius,
595
+ st.borderBottomLeftRadius,
596
+ st.borderBottomRightRadius,
597
+ ].map((v) => parseFloat(v) || 0);
598
+ return {
599
+ width: b.width,
600
+ height: b.height,
601
+ radius: Math.min(Math.min(...corners), b.width / 2, b.height / 2),
602
+ };
603
+ })(),
526
604
  };
527
605
  }, { i, pad });
528
606
  if (!box)
529
607
  continue;
530
608
  if (box.trapped) {
531
- out.push({ element: box.described, reason: 'trapped' });
609
+ skipped({ element: box.described, reason: 'trapped' });
532
610
  continue;
533
611
  }
534
612
  if (box.width < 1 || box.height < 1) {
535
- out.push({ element: box.described, reason: 'offscreen' });
613
+ skipped({ element: box.described, reason: 'offscreen' });
536
614
  continue;
537
615
  }
538
616
  const clip = { x: box.x, y: box.y, width: box.width, height: box.height };
539
- const away = await page.screenshot({ clip, animations: 'disabled', caret: 'hide' });
617
+ const shoot = () => page.screenshot({
618
+ clip,
619
+ animations: 'disabled',
620
+ caret: 'hide',
621
+ ...(options.scale ? { scale: options.scale } : {}),
622
+ });
623
+ const away = await shoot();
540
624
  // 2. Focus this one. If it hands focus to something inside it (a grid to its first row), that element is
541
625
  // probed on its own turn.
542
626
  const kept = await page.evaluate(async (n) => {
@@ -549,10 +633,8 @@ export async function findInvisibleFocus(page, options = {}) {
549
633
  }, i);
550
634
  if (!kept)
551
635
  continue;
552
- const here = await page.screenshot({ clip, animations: 'disabled', caret: 'hide' });
553
- const same = away.length === here.length && away.every((byte, k) => byte === here[k]);
554
- if (same)
555
- out.push({ element: box.described, reason: 'unchanged' });
636
+ const here = await shoot();
637
+ await compare({ box, away, here });
556
638
  }
557
639
  await page.evaluate(() => {
558
640
  document.querySelector('style[data-setsquare-focus-probe-style]')?.remove();
@@ -561,7 +643,6 @@ export async function findInvisibleFocus(page, options = {}) {
561
643
  el.removeAttribute('data-setsquare-focus-skip');
562
644
  }
563
645
  }, undefined);
564
- return out;
565
646
  }
566
647
  /** Throws, listing every control whose focus cannot be seen, unless there are none. `trapped` ones are skipped. */
567
648
  export async function expectFocusVisible(page, options = {}) {
@@ -575,6 +656,182 @@ export async function expectFocusVisible(page, options = {}) {
575
656
  : ` ${f.element}: took focus but could not be seen`),
576
657
  ].join('\n'));
577
658
  }
659
+ /** A PNG's pixels, decoded by the page itself (no image library): RGBA, row by row. */
660
+ const decodeInPage = (page, shots) => {
661
+ const base64 = (bytes) => {
662
+ let text = '';
663
+ for (let i = 0; i < bytes.length; i += 0x8000)
664
+ text += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
665
+ return btoa(text);
666
+ };
667
+ return page.evaluate(async (pngs) => {
668
+ const decode = async (b64) => {
669
+ const blob = await (await fetch(`data:image/png;base64,${b64}`)).blob();
670
+ const bitmap = await createImageBitmap(blob);
671
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
672
+ const ctx = canvas.getContext('2d');
673
+ ctx.drawImage(bitmap, 0, 0);
674
+ return {
675
+ width: bitmap.width,
676
+ height: bitmap.height,
677
+ data: [...ctx.getImageData(0, 0, bitmap.width, bitmap.height).data],
678
+ };
679
+ };
680
+ const [a, b] = await Promise.all(pngs.map(decode));
681
+ // Count the pixels whose colour changed by at least 3:1 between the two states.
682
+ const lum = (r, g, bl) => {
683
+ const c = [r, g, bl].map((v) => {
684
+ const s = v / 255;
685
+ return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
686
+ });
687
+ return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
688
+ };
689
+ if (!a || !b || a.width !== b.width || a.height !== b.height)
690
+ return 0;
691
+ let changed = 0;
692
+ for (let k = 0; k < a.data.length; k += 4) {
693
+ const la = lum(a.data[k], a.data[k + 1], a.data[k + 2]);
694
+ const lb = lum(b.data[k], b.data[k + 1], b.data[k + 2]);
695
+ const [hi, lo] = la > lb ? [la, lb] : [lb, la];
696
+ if ((hi + 0.05) / (lo + 0.05) >= 3)
697
+ changed++;
698
+ }
699
+ return changed;
700
+ }, shots.map(base64));
701
+ };
702
+ /**
703
+ * Focus appearance (WCAG 2.4.13, AAA): for every control a keyboard can reach, the pixels that change colour by at
704
+ * least 3:1 when it takes focus must cover at least the area of a 2 CSS px thick perimeter of the unfocused
705
+ * component: 4h + 4w − (16 − 4π)r CSS px² for a rectangle with corner radius r (W3C's formulas, read on 9 October
706
+ * 2026). The component is its visible presentation, which W3C gauges by the indicator itself: the part whose outline
707
+ * or box shadow changes on focus (a three-layer control's paint), or the control when none does. Screenshots are
708
+ * taken at CSS scale, with
709
+ * focus away and then on the control, as `findInvisibleFocus` takes them. Controls the browser draws the indicator
710
+ * for and the author hasn't touched are exempt in WCAG, and aren't told apart here.
711
+ */
712
+ export async function findWeakFocusIndicators(page, options = {}) {
713
+ const out = [];
714
+ await probeFocus(page, { ...options, scale: 'css' }, async ({ box, away, here }) => {
715
+ // W3C's area for a 2 CSS px perimeter of a rounded rectangle: 4h + 4w − (16 − 4π)r (a rectangle when r is 0).
716
+ const { width: w, height: h, radius: r } = box.own;
717
+ const needed = Math.round(4 * (w + h) - (16 - 4 * Math.PI) * r);
718
+ const area = await decodeInPage(page, [away, here]);
719
+ if (area < needed)
720
+ out.push({ element: box.described, area, needed });
721
+ }, () => { });
722
+ return out;
723
+ }
724
+ /** Throws, listing every control whose focus indicator is too small or too faint for 2.4.13, unless there are none. */
725
+ export async function expectFocusAppearance(page, options = {}) {
726
+ const found = await findWeakFocusIndicators(page, options);
727
+ if (!found.length)
728
+ return;
729
+ throw new Error([
730
+ `The focus indicator is too small or too faint on ${found.length} control${found.length === 1 ? '' : 's'} (2.4.13):`,
731
+ ...found.map((f) => ` ${f.element}: ${f.area} CSS px² changed by 3:1, ${f.needed} needed`),
732
+ ].join('\n'));
733
+ }
734
+ /**
735
+ * Focus not obscured (WCAG 2.4.11 at AA, 2.4.12 at AAA): presses Tab, and at each stop samples a grid of points
736
+ * across the focused control. A point is covered when what's on top there is neither the control, something inside
737
+ * it, nor something it's inside. At AA a control fails when every point is covered; at AAA when any is. A visually
738
+ * hidden input is measured by the control around it, and only the part of a control that's in view is sampled:
739
+ * what the window or a scroll container cuts off isn't covered by other content. Elements with
740
+ * `pointer-events: none` can't be seen by the sampling, so a covering layer that lets clicks through goes unnoticed.
741
+ */
742
+ export async function findObscuredFocus(page, options = {}) {
743
+ const level = options.level ?? 'aa';
744
+ const out = [];
745
+ const seen = new Set();
746
+ for (let i = 0; i < (options.stops ?? 80); i++) {
747
+ await page.keyboard.press('Tab');
748
+ const found = await page.evaluate(async () => {
749
+ await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
750
+ let el = document.activeElement;
751
+ // Into a same-origin frame: the frame's own focused element is what's focused.
752
+ while (el?.tagName === 'IFRAME') {
753
+ const inner = el.contentDocument
754
+ ?.activeElement;
755
+ if (!inner || inner === inner.ownerDocument.body)
756
+ break;
757
+ el = inner;
758
+ }
759
+ if (!el || el === document.body)
760
+ return null;
761
+ const doc = el.ownerDocument;
762
+ // A visually hidden input (React Aria's switch, checkbox, radio) stands for the control around it.
763
+ const tiny = (e) => {
764
+ const b = e.getBoundingClientRect();
765
+ return b.width <= 2 || b.height <= 2;
766
+ };
767
+ let shown = el;
768
+ while (tiny(shown) && shown.parentElement)
769
+ shown = shown.parentElement;
770
+ // Only the part that's in view: what a scroll container or the window cuts off isn't covered by anything,
771
+ // and a control larger than its scroller can't be shown whole.
772
+ const r0 = shown.getBoundingClientRect();
773
+ const r = { left: r0.left, top: r0.top, right: r0.right, bottom: r0.bottom };
774
+ const view = doc.defaultView;
775
+ r.left = Math.max(r.left, 0);
776
+ r.top = Math.max(r.top, 0);
777
+ r.right = Math.min(r.right, view.innerWidth);
778
+ r.bottom = Math.min(r.bottom, view.innerHeight);
779
+ for (let p = shown.parentElement; p; p = p.parentElement) {
780
+ const cs = view.getComputedStyle(p);
781
+ if (cs.overflowX === 'visible' && cs.overflowY === 'visible')
782
+ continue;
783
+ const c = p.getBoundingClientRect();
784
+ r.left = Math.max(r.left, c.left);
785
+ r.top = Math.max(r.top, c.top);
786
+ r.right = Math.min(r.right, c.right);
787
+ r.bottom = Math.min(r.bottom, c.bottom);
788
+ }
789
+ const width = r.right - r.left;
790
+ const height = r.bottom - r.top;
791
+ if (width < 1 || height < 1)
792
+ return null;
793
+ let total = 0;
794
+ let covered = 0;
795
+ for (let gx = 1; gx <= 5; gx++)
796
+ for (let gy = 1; gy <= 3; gy++) {
797
+ const x = r.left + (width * gx) / 6;
798
+ const y = r.top + (height * gy) / 4;
799
+ total++;
800
+ const top = doc.elementFromPoint(x, y);
801
+ if (!top || !(shown.contains(top) || top.contains(shown)))
802
+ covered++;
803
+ }
804
+ const role = el.getAttribute('role');
805
+ const name = (el.getAttribute('aria-label') ?? el.textContent ?? '')
806
+ .trim()
807
+ .replace(/\s+/g, ' ')
808
+ .slice(0, 40);
809
+ return {
810
+ key: `${el.tagName}|${el.id}|${name}|${Math.round(r0.left)}|${Math.round(r0.top)}`,
811
+ element: `${el.tagName.toLowerCase()}${role ? `[role=${role}]` : ''}${name ? ` "${name}"` : ''}`,
812
+ covered: covered / total,
813
+ };
814
+ }, undefined);
815
+ if (!found)
816
+ continue;
817
+ if (seen.has(found.key))
818
+ break;
819
+ seen.add(found.key);
820
+ if (level === 'aa' ? found.covered >= 1 : found.covered > 0)
821
+ out.push({ element: found.element, covered: Math.round(found.covered * 100) / 100 });
822
+ }
823
+ return out;
824
+ }
825
+ /** Throws, listing every tab stop whose focused control is hidden by other content, unless there are none. */
826
+ export async function expectFocusNotObscured(page, options = {}) {
827
+ const found = await findObscuredFocus(page, options);
828
+ if (!found.length)
829
+ return;
830
+ throw new Error([
831
+ `Focus is hidden by other content on ${found.length} control${found.length === 1 ? '' : 's'}:`,
832
+ ...found.map((f) => ` ${f.element}: ${Math.round(f.covered * 100)}% covered`),
833
+ ].join('\n'));
834
+ }
578
835
  /**
579
836
  * Every link in running text that people who can't tell the colours apart could not find. A link counts as
580
837
  * in running text when it is laid out inline and the block around it has words of its own — so navigation,
@@ -984,8 +1241,9 @@ export async function expectPageReflows(page, options = {}) {
984
1241
  * reports every box whose content now runs out of it further than it did before. Measuring the difference
985
1242
  * leaves alone anything that was already clipped on purpose (visually hidden text, a decorative bleed).
986
1243
  * Scroll containers are left out too, since their content can still be scrolled to, and so is text that
987
- * spills from a box inside a scroller that scrolls that way (code in a code block). Only the innermost box
988
- * is reported. Takes the spacing off again before returning. Returns [] when everything still fits.
1244
+ * spills from a box inside a scroller that scrolls that way (code in a code block), or inside a pane marked
1245
+ * `data-setsquare-pannable`: one people move with pan controls instead of scrollbars, such as a React Flow
1246
+ * diagram, which lays itself out again once its nodes have grown. Only the innermost box is reported. Takes the spacing off again before returning. Returns [] when everything still fits.
989
1247
  */
990
1248
  export function findClippedText(options = {}, root = document) {
991
1249
  const describe = (el) => {
@@ -1014,6 +1272,8 @@ export function findClippedText(options = {}, root = document) {
1014
1272
  return false;
1015
1273
  if (/(auto|scroll)/.test(cs.overflowX + cs.overflowY))
1016
1274
  return false;
1275
+ if (el.hasAttribute('data-setsquare-pannable'))
1276
+ return false;
1017
1277
  const r = el.getBoundingClientRect();
1018
1278
  // 1px visually hidden boxes clip on purpose.
1019
1279
  return r.width > 2 && r.height > 2 && !!el.textContent?.trim();
@@ -1030,7 +1290,12 @@ export function findClippedText(options = {}, root = document) {
1030
1290
  document.head.append(style);
1031
1291
  /** Which ways the nearest ancestor that doesn't show its overflow scrolls: text spilled that way is reachable. */
1032
1292
  const scrollsTo = (el) => {
1293
+ // Anything inside a pannable pane can be panned to, whatever clips it on the way.
1294
+ if (el.parentElement?.closest('[data-setsquare-pannable]'))
1295
+ return { x: true, y: true };
1033
1296
  for (let p = el.parentElement; p && p !== document.body; p = p.parentElement) {
1297
+ if (p.hasAttribute('data-setsquare-pannable'))
1298
+ return { x: true, y: true };
1034
1299
  const cs = getComputedStyle(p);
1035
1300
  if (cs.overflowX === 'visible' && cs.overflowY === 'visible')
1036
1301
  continue;