From 8a5470dd019594c391e99e347712e61574ec731f Mon Sep 17 00:00:00 2001 From: Reece Browne <74901996+reecebrowne@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:28:24 +0100 Subject: [PATCH] Add an accessibility regression gate for Storybook (#7086) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Follow-up to #7073. Turns the story scan into an accessibility gate: stories run axe in a real browser, and CI flags a change that adds a **new** violation. The app has plenty of existing a11y problems (mostly theme-level colour contrast), so rather than block everything on those, they're recorded in `.storybook/a11y-baseline.json` and grandfathered. The gate cares about three things: - a story breaking a rule it wasn't already breaking - a story that fails to render at all - a scan that didn't cover everything it was asked to Starting point: 839 stories carry a known violation, 1058 story-rule pairs. ## Where it runs - **Pull requests** scan only the stories the branch touches — usually seconds. A full sweep is ~30 minutes, too slow to sit in front of every merge, and the `frontend` path filter is broad enough that unrelated changes would pay for it. - **Nightly** scans every story, so a violation introduced somewhere other than the story itself — a shared component, a theme token — still surfaces within a day. - Both upload their scan reports as artifacts; the reports carry the offending selector and help text, without which a red run can only be understood by reproducing it locally. - **Advisory to start with.** It is deliberately not in `all-checks-passed`, so it reports without blocking. Worth promoting once a few weeks of runs show the pass/fail is stable. ## Using it - **Fixed some violations?** `task frontend:storybook:a11y:record` re-records so the gate locks the improvement in. - **Locally:** `task frontend:storybook:a11y:changed` for your branch, `task frontend:storybook:a11y` for everything. - **New component?** Its story is picked up automatically. ## Testing - Every story — 526 files, ~1,450 stories — runs in a real browser with no render failures, and the gate reports no regressions against the baseline. - Running the gate over a single changed story takes seconds, which is the pull-request path. - The gate's own behaviour is covered against synthetic scan reports: a new rule fails, the same rule on more nodes does not, a crashed story fails, an incomplete scan refuses to report, and re-recording refuses while anything is crashing. - Typecheck (all build variants), ESLint and Prettier pass. ## Notes for reviewers Some of this PR is making the mechanism trustworthy rather than adding features, so it's worth knowing what changed and why: - Rule ids come from the axe docs URL in each violation, not a hand-maintained list of rule names — the old list silently ignored 39 of axe's 104 rules, including `object-alt`, `target-size` and the table rules. - The baseline records **which** rules a story breaks, not how many nodes break them. Node counts drift between runs because stories fetch asynchronously and axe samples whatever has rendered, which made unrelated changes look like regressions. For the same reason the baseline is the union of repeated scans, so a run can only be a subset of it. - A story that fails for a non-a11y reason used to yield no rule id and was recorded as clean, which hid crashes and could mask real violations. Those now fail, and re-recording refuses to run while any story is crashing. - The scan writes a manifest of every story file it intends to cover and the check fails unless all of them reported, so a dropped batch can't read as "no violations". - Vite was pre-bundling the JSX runtime mid-run and reloading the page, which crashed whichever stories were loading; those deps are now named up front and the per-story timeout is above the 5s default. Colour contrast dominates the baseline and is theme-level, tracked separately from this. --- .github/config/.files.yaml | 1 + .github/workflows/build.yml | 11 + .github/workflows/frontend-a11y.yml | 57 + .github/workflows/nightly.yml | 40 + .taskfiles/frontend.yml | 44 +- frontend/.gitignore | 1 + frontend/.storybook/a11y-baseline.json | 2622 ++++++++++++++++++++++++ frontend/.storybook/a11y-check.mjs | 202 ++ frontend/.storybook/a11y-scan.sh | 84 + frontend/.storybook/preview.tsx | 10 +- frontend/.storybook/vitest.config.ts | 20 +- frontend/.storybook/vitest.setup.ts | 11 +- 12 files changed, 3083 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/frontend-a11y.yml create mode 100644 frontend/.storybook/a11y-baseline.json create mode 100644 frontend/.storybook/a11y-check.mjs create mode 100644 frontend/.storybook/a11y-scan.sh diff --git a/.github/config/.files.yaml b/.github/config/.files.yaml index 64454c5fe1..a9d4d3550e 100644 --- a/.github/config/.files.yaml +++ b/.github/config/.files.yaml @@ -84,6 +84,7 @@ frontend: &frontend - .taskfiles/frontend.yml - .taskfiles/e2e.yml - .github/workflows/frontend-validation.yml + - .github/workflows/frontend-a11y.yml - .github/workflows/e2e-stubbed.yml - .github/workflows/e2e-live.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d4130f214..c8275c023b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,6 +99,17 @@ jobs: uses: ./.github/workflows/frontend-validation.yml secrets: inherit + # Advisory: deliberately NOT in all-checks-passed. It reports on the stories a + # branch touches so a regression is visible in review, but a browser scan is + # too new here to block merges on. Promote it once its pass/fail proves stable. + frontend-a11y: + if: needs.files-changed.outputs.frontend == 'true' + needs: [files-changed] + permissions: + contents: read + uses: ./.github/workflows/frontend-a11y.yml + secrets: inherit + playwright-e2e: if: needs.files-changed.outputs.frontend == 'true' needs: [files-changed] diff --git a/.github/workflows/frontend-a11y.yml b/.github/workflows/frontend-a11y.yml new file mode 100644 index 0000000000..d763447048 --- /dev/null +++ b/.github/workflows/frontend-a11y.yml @@ -0,0 +1,57 @@ +name: Frontend a11y regression gate + +# Reusable workflow called from build.yml when frontend sources change. +# +# Scans the stories this branch touches in real Chromium and runs axe against +# each. Existing violations are grandfathered in .storybook/a11y-baseline.json; +# the check fails on a NEW violation — a story breaking a rule it wasn't already +# breaking — or on a story that fails to render at all. +# +# Only changed stories, because a full sweep is ~30 minutes: far too slow to sit +# in front of every merge. The whole suite is scanned nightly instead +# (nightly.yml), which catches anything a branch didn't touch. +# +# Advisory for now: this is not in build.yml's all-checks-passed list, so a +# failure reports without blocking. Promote it once a few weeks of runs show the +# pass/fail is stable. +on: + workflow_call: + +permissions: + contents: read + +jobs: + frontend-a11y: + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + - name: Harden Runner + uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3 + with: + egress-policy: audit + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # Need the base branch too, to diff against it. + fetch-depth: 0 + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "22" + cache: "npm" + cache-dependency-path: frontend/package-lock.json + - name: Install Task + uses: go-task/setup-task@01a4adf9db2d14c1de7a560f09170b6e0df736aa # v2.1.0 + - name: a11y gate (changed stories) + run: task frontend:storybook:a11y:changed -- origin/${{ github.base_ref || 'main' }} + - name: Upload scan reports + # The reports carry the offending selector and help text for each + # violation; without them a red run can only be understood by + # reproducing the whole scan locally. + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: a11y-scan-${{ github.run_id }} + path: frontend/.a11y-scan/ + retention-days: 7 + if-no-files-found: ignore diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 4047157a45..301a1fc8df 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -53,6 +53,46 @@ jobs: path: frontend/playwright-report/ retention-days: 14 + # Whole-suite accessibility sweep. Pull requests only scan the stories they + # touch (frontend-a11y.yml) because a full pass takes ~30 minutes; this covers + # everything else, so a violation introduced by a change somewhere other than + # the story itself — a shared component, a theme token — still surfaces within + # a day. + a11y-all-stories: + name: a11y (every story) + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "22" + cache: "npm" + cache-dependency-path: frontend/package-lock.json + + - name: Install Task + uses: go-task/setup-task@01a4adf9db2d14c1de7a560f09170b6e0df736aa # v2.1.0 + + - name: a11y gate (every story) + run: task frontend:storybook:a11y + + - name: Upload scan reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: a11y-scan-nightly-${{ github.run_id }} + path: frontend/.a11y-scan/ + retention-days: 14 + if-no-files-found: ignore + # Builds all desktop platforms on a schedule so the Rust dependency cache is # written on main, where PR and merge-queue tauri builds can restore it. warm-tauri-cache: diff --git a/.taskfiles/frontend.yml b/.taskfiles/frontend.yml index 3e608855f3..1338aae8e1 100644 --- a/.taskfiles/frontend.yml +++ b/.taskfiles/frontend.yml @@ -203,13 +203,55 @@ tasks: - npx playwright install chromium storybook:test: - desc: "Scan every story in real Chromium — each must mount without throwing" + desc: "Scan every story in real Chromium: it must render and pass axe" deps: [install, storybook:browser] cmds: # Runs each story as a browser test. Pass a filter through, e.g. # task frontend:storybook:test -- Button - npx vitest run --config .storybook/vitest.config.ts {{.CLI_ARGS}} + storybook:a11y: + desc: "a11y regression gate over every story: fail only on NEW axe violations" + deps: [install, storybook:browser] + cmds: + - bash .storybook/a11y-scan.sh + - node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt + + storybook:a11y:changed: + desc: "a11y gate over stories changed vs a base ref (default origin/main)" + summary: | + Scans only the stories this branch touches, which is what pull requests + run — a full scan takes ~30 minutes, far too long to sit in front of every + merge. The nightly job covers the rest of the suite. + + Pass a base ref through CLI_ARGS, e.g. + task frontend:storybook:a11y:changed -- origin/release + deps: [install, storybook:browser] + vars: + BASE: '{{.CLI_ARGS | default "origin/main"}}' + # Stories touched by this branch, plus any not yet committed. + CHANGED: + sh: | + { git diff --name-only --diff-filter=d {{.CLI_ARGS | default "origin/main"}}...HEAD -- '*.stories.ts' '*.stories.tsx'; + git diff --name-only --diff-filter=d -- '*.stories.ts' '*.stories.tsx'; + git ls-files --others --exclude-standard -- '*.stories.ts' '*.stories.tsx'; } \ + | sed 's|^frontend/||' | sort -u | tr '\n' ' ' + cmds: + - cmd: | + if [ -z "{{.CHANGED}}" ]; then + echo "a11y: no story files changed vs {{.BASE}} — nothing to check" + exit 0 + fi + bash .storybook/a11y-scan.sh {{.CHANGED}} + node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt + + storybook:a11y:record: + desc: "Re-record the a11y baseline (run after intentionally fixing/adding violations)" + deps: [install, storybook:browser] + cmds: + - bash .storybook/a11y-scan.sh + - node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt --record + # ============================================================ # Code quality # ============================================================ diff --git a/frontend/.gitignore b/frontend/.gitignore index 0605e6e79f..c0c467073d 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -48,3 +48,4 @@ test-results /scripts/dev-update-test/.update-dist/ /scripts/dev-update-test/screenshots/ /editor/src-tauri/tauri.conf.dev-update.json +.a11y-scan/ diff --git a/frontend/.storybook/a11y-baseline.json b/frontend/.storybook/a11y-baseline.json new file mode 100644 index 0000000000..10fc530c77 --- /dev/null +++ b/frontend/.storybook/a11y-baseline.json @@ -0,0 +1,2622 @@ +{ + "editor/src/core/components/StorageStatsCard.stories.tsx :: Default": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/core/components/StorageStatsCard.stories.tsx :: Nearing Quota": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/core/components/StorageStatsCard.stories.tsx :: No Quota": [ + "color-contrast" + ], + "editor/src/core/components/annotation/shared/ColorPicker.stories.tsx :: Default": [ + "aria-input-field-name", + "button-name", + "color-contrast" + ], + "editor/src/core/components/annotation/shared/ColorPicker.stories.tsx :: With Opacity": [ + "aria-input-field-name", + "button-name", + "color-contrast" + ], + "editor/src/core/components/annotation/shared/DrawingCanvas.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/annotation/shared/DrawingCanvas.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/annotation/shared/DrawingControls.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/annotation/shared/ImageUploader.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/annotation/shared/ImageUploader.stories.tsx :: With Background Removal": [ + "color-contrast" + ], + "editor/src/core/components/annotation/shared/ImageUploader.stories.tsx :: With Label And Hint": [ + "color-contrast" + ], + "editor/src/core/components/annotation/tools/DrawingTool.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/annotation/tools/DrawingTool.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/annotation/tools/ImageTool.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/annotation/tools/ImageTool.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/fileManager/CompactFileDetails.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/fileManager/CompactFileDetails.stories.tsx :: Multiple Files": [ + "color-contrast" + ], + "editor/src/core/components/fileManager/FileDetails.stories.tsx :: Compact": [ + "color-contrast" + ], + "editor/src/core/components/fileManager/FileDetails.stories.tsx :: Default": [ + "color-contrast", + "scrollable-region-focusable" + ], + "editor/src/core/components/fileManager/FileDetails.stories.tsx :: Empty": [ + "color-contrast", + "scrollable-region-focusable" + ], + "editor/src/core/components/filesPage/DeleteFilesDialog.stories.tsx :: Cloud Only": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/DeleteFilesDialog.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/DeleteFilesDialog.stories.tsx :: Local And Cloud Choice": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/DeleteFolderDialog.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/DeleteFolderDialog.stories.tsx :: With Files": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/FileDetailsPanel.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/FileDetailsPanel.stories.tsx :: In Folder": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/FileDetailsPanel.stories.tsx :: Local Only With Save To Server": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/FileDetailsPanel.stories.tsx :: Multi Select": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/FileGrid.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/FileGrid.stories.tsx :: List Mode": [ + "aria-required-children" + ], + "editor/src/core/components/filesPage/FileOriginBadge.stories.tsx :: Cloud": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/FileOriginBadge.stories.tsx :: Shared With Me": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/FolderNameDialog.stories.tsx :: Default": [ + "button-name" + ], + "editor/src/core/components/filesPage/FolderNameDialog.stories.tsx :: Rename": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/FolderThumbnail.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/MoveToFolderDialog.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/MoveToFolderDialog.stories.tsx :: Empty": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/MoveToFolderDialog.stories.tsx :: With Create Folder": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/MoveToFolderDialog.stories.tsx :: With Disabled Descendant": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/VersionHistoryModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/VersionHistoryModal.stories.tsx :: No File Selected": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/filesPage/VersionTimeline.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/VersionTimeline.stories.tsx :: Long Chain Collapsed": [ + "color-contrast" + ], + "editor/src/core/components/filesPage/VersionTimeline.stories.tsx :: No Header": [ + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Admin Overview Login Disabled": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Admin Overview Login Enabled": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Analytics Choice": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Analytics Choice Error": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Desktop Install": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: First Login": [ + "aria-dialog-name" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: First Login Default Credentials": [ + "aria-dialog-name" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Mfa Setup": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Security Check": [ + "aria-dialog-name" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Server License": [ + "aria-dialog-name" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Server License Over Limit": [ + "aria-dialog-name" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Stepped Flow Example": [ + "aria-dialog-name", + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Tour Overview": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingModalSlide.stories.tsx :: Welcome": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingSlideShell.stories.tsx :: Default": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/OnboardingSlideShell.stories.tsx :: Not Dismissible": [ + "aria-dialog-name", + "aria-progressbar-name" + ], + "editor/src/core/components/onboarding/OnboardingSlideShell.stories.tsx :: Stepped With Back": [ + "aria-dialog-name", + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/core/components/onboarding/slides/WelcomeSlide.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/pageEditor/bulkSelectionPanel/SelectedPagesDisplay.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/BulkShareModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/BulkShareModal.stories.tsx :: Links Enabled": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/BulkUploadToServerModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/BulkUploadToServerModal.stories.tsx :: Single File": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/ButtonToggle.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/ButtonToggle.stories.tsx :: Small": [ + "color-contrast" + ], + "editor/src/core/components/shared/DropdownListWithFooter.stories.tsx :: Default": [ + "aria-allowed-attr" + ], + "editor/src/core/components/shared/DropdownListWithFooter.stories.tsx :: Empty": [ + "aria-allowed-attr" + ], + "editor/src/core/components/shared/DropdownListWithFooter.stories.tsx :: Multi Select With Footer": [ + "aria-allowed-attr" + ], + "editor/src/core/components/shared/EditableSecretField.stories.tsx :: Masked": [ + "label" + ], + "editor/src/core/components/shared/EditableSecretField.stories.tsx :: Masked Disabled": [ + "label" + ], + "editor/src/core/components/shared/EditableSecretField.stories.tsx :: With Error": [ + "color-contrast", + "label-title-only" + ], + "editor/src/core/components/shared/EncryptedPdfUnlockModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/EncryptedPdfUnlockModal.stories.tsx :: Incorrect Password": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/EncryptedPdfUnlockModal.stories.tsx :: Multiple Files Remaining": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/EncryptedPdfUnlockModal.stories.tsx :: Processing": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/ErrorBoundary.stories.tsx :: Caught Error": [ + "color-contrast" + ], + "editor/src/core/components/shared/ErrorBoundary.stories.tsx :: Custom Fallback": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileCard.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileCard.stories.tsx :: Selected": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileCard.stories.tsx :: Unsupported": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileDropdownMenu.stories.tsx :: Default": [ + "aria-allowed-attr" + ], + "editor/src/core/components/shared/FileDropdownMenu.stories.tsx :: No Remove": [ + "aria-allowed-attr" + ], + "editor/src/core/components/shared/FileDropdownMenu.stories.tsx :: Switching": [ + "aria-allowed-attr" + ], + "editor/src/core/components/shared/FileGrid.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileGrid.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileGrid.stories.tsx :: Search And Sort": [ + "color-contrast", + "label" + ], + "editor/src/core/components/shared/FilePickerModal.stories.tsx :: Default": [ + "button-name", + "color-contrast", + "label" + ], + "editor/src/core/components/shared/FilePickerModal.stories.tsx :: Empty": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/FileSelectorPicker.stories.tsx :: Custom Placeholder": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileSelectorPicker.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/FileSelectorPicker.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/shared/Footer.stories.tsx :: All Links And Cookie Banner": [ + "color-contrast" + ], + "editor/src/core/components/shared/Footer.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/InfoBanner.stories.tsx :: Warning": [ + "color-contrast" + ], + "editor/src/core/components/shared/MobileUploadModal.stories.tsx :: Default": [ + "button-name", + "color-contrast", + "svg-img-alt" + ], + "editor/src/core/components/shared/MultiSelectControls.stories.tsx :: All Actions": [ + "color-contrast" + ], + "editor/src/core/components/shared/NavigationWarningModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/NavigationWarningModal.stories.tsx :: With Apply And Continue": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/NavigationWarningModal.stories.tsx :: With Export And Continue": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/ShareFileModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/ShareFileModal.stories.tsx :: Links Enabled": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/ShareManagementModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/ShareManagementModal.stories.tsx :: Links Enabled": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/UpdateModal.stories.tsx :: Default": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/shared/UpdateModal.stories.tsx :: Desktop Install Blocked": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/shared/UpdateModal.stories.tsx :: Desktop Install Ready To Restart": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/core/components/shared/UploadToServerModal.stories.tsx :: Already Uploaded": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/UploadToServerModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/UserSelector.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/ZipWarningModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/ZipWarningModal.stories.tsx :: Single File": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/config/LoginRequiredBanner.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/OverviewHeader.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/PendingBadge.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/PendingBadge.stories.tsx :: Large Size": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/RestartConfirmationModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/shared/config/SettingsStickyFooter.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/SettingsStickyFooter.stories.tsx :: Saving": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/GeneralSection.stories.tsx :: Admin Banner": [ + "label" + ], + "editor/src/core/components/shared/config/configSections/GeneralSection.stories.tsx :: Default": [ + "label" + ], + "editor/src/core/components/shared/config/configSections/GeneralSection.stories.tsx :: With Backend Version": [ + "label" + ], + "editor/src/core/components/shared/config/configSections/HelpSection.stories.tsx :: Admin": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/HelpSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/HotkeysSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/LegalSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/LegalSection.stories.tsx :: Minimal Links": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/LegalSection.stories.tsx :: With Analytics Enabled": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/Overview.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/Overview.stories.tsx :: Loaded": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/Overview.stories.tsx :: With Warning": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/ProviderCard.stories.tsx :: Configured": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/ProviderCard.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/ProviderCard.stories.tsx :: Read Only": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/ThirdPartyLicensesSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/config/configSections/ThirdPartyLicensesSection.stories.tsx :: Frontend": [ + "color-contrast" + ], + "editor/src/core/components/shared/filePreview/DocumentThumbnail.stories.tsx :: Encrypted": [ + "color-contrast" + ], + "editor/src/core/components/shared/filePreview/DocumentThumbnail.stories.tsx :: Loading": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/CreateSessionFlow.stories.tsx :: Creating": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/CreateSessionFlow.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/CreateSessionFlow.stories.tsx :: No File Selected": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/SharedSigningLauncher.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/SharedSigningLauncher.stories.tsx :: Pending Requests": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/ConfigureSignatureDefaultsStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/ConfigureSignatureDefaultsStep.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/ConfigureSignatureDefaultsStep.stories.tsx :: Invisible Signature": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/ReviewSessionStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/ReviewSessionStep.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/ReviewSessionStep.stories.tsx :: Invisible Signature": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/SelectDocumentStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/SelectDocumentStep.stories.tsx :: Multiple Files Selected": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/SelectDocumentStep.stories.tsx :: No File Selected": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/SelectParticipantsStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/SelectParticipantsStep.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/shared/signing/steps/SelectParticipantsStep.stories.tsx :: With Selection": [ + "color-contrast" + ], + "editor/src/core/components/shared/sliderWithInput/SliderWithInput.stories.tsx :: Default": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/shared/sliderWithInput/SliderWithInput.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/shared/wetSignature/DrawSignatureCanvas.stories.tsx :: Default": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/shared/wetSignature/DrawSignatureCanvas.stories.tsx :: Disabled": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/shared/wetSignature/TypeSignatureText.stories.tsx :: Default": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/shared/wetSignature/TypeSignatureText.stories.tsx :: Disabled": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/shared/wetSignature/TypeSignatureText.stories.tsx :: Empty": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/shared/wetSignature/UploadSignatureImage.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/shared/wetSignature/UploadSignatureImage.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/shared/wetSignature/UploadSignatureImage.stories.tsx :: With Signature": [ + "color-contrast" + ], + "editor/src/core/components/tools/ToolLoadingFallback.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/ToolLoadingFallback.stories.tsx :: With Tool Name": [ + "color-contrast" + ], + "editor/src/core/components/tools/ToolPanelModePrompt.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/ToolRenderer.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/addPageNumbers/PageNumberPreview.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/addPageNumbers/PageNumberPreview.stories.tsx :: With Quick Grid": [ + "color-contrast" + ], + "editor/src/core/components/tools/addStamp/StampPreview.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/addStamp/StampPreview.stories.tsx :: With Quick Grid": [ + "color-contrast" + ], + "editor/src/core/components/tools/addStamp/StampPreview.stories.tsx :: With Text": [ + "color-contrast" + ], + "editor/src/core/components/tools/addStamp/StampSetupSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/addStamp/StampSetupSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/addStamp/StampSetupSettings.stories.tsx :: Image Stamp": [ + "color-contrast" + ], + "editor/src/core/components/tools/addStamp/StampSetupSettings.stories.tsx :: Text Stamp With Preview": [ + "color-contrast" + ], + "editor/src/core/components/tools/addWatermark/AddWatermarkSingleStepSettings.stories.tsx :: Disabled": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/addWatermark/AddWatermarkSingleStepSettings.stories.tsx :: Text Only": [ + "button-name", + "color-contrast", + "label" + ], + "editor/src/core/components/tools/addWatermark/AddWatermarkSingleStepSettings.stories.tsx :: Text Watermark": [ + "button-name", + "color-contrast", + "label" + ], + "editor/src/core/components/tools/addWatermark/WatermarkFormatting.stories.tsx :: Default": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/addWatermark/WatermarkFormatting.stories.tsx :: Disabled": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/addWatermark/WatermarkFormatting.stories.tsx :: Without Flatten Option": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/addWatermark/WatermarkStyleSettings.stories.tsx :: Default": [ + "label" + ], + "editor/src/core/components/tools/addWatermark/WatermarkStyleSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/addWatermark/WatermarkTextStyle.stories.tsx :: Default": [ + "button-name", + "label" + ], + "editor/src/core/components/tools/addWatermark/WatermarkTextStyle.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastBasicSettings.stories.tsx :: Adjusted": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastBasicSettings.stories.tsx :: Default": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastBasicSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastColorSettings.stories.tsx :: Adjusted": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastColorSettings.stories.tsx :: Default": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastColorSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastSingleStepSettings.stories.tsx :: Adjusted": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastSingleStepSettings.stories.tsx :: Default": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/adjustContrast/AdjustContrastSingleStepSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/automate/AutomationCreation.stories.tsx :: Edit Existing": [ + "color-contrast" + ], + "editor/src/core/components/tools/automate/AutomationImportModal.stories.tsx :: Default": [ + "button-name", + "color-contrast", + "label" + ], + "editor/src/core/components/tools/automate/ToolConfigurationModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/automate/ToolConfigurationModal.stories.tsx :: With Settings": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/bookletImposition/BookletImpositionSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/bookletImposition/BookletImpositionSettings.stories.tsx :: Manual Duplex": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertSignAutomationSettings.stories.tsx :: Auto Sign Mode": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertSignAutomationSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertificateFilesSettings.stories.tsx :: Auto Sign Mode": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertificateFormatSettings.stories.tsx :: Selected": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertificateSelector.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertificateSelector.stories.tsx :: Pem Format": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertificateTypeSettings.stories.tsx :: All Sources Available": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/CertificateTypeSettings.stories.tsx :: Server Selected": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/HardwareCertificateSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureAppearanceSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureAppearanceSettings.stories.tsx :: Visible Signature": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureSettingsDisplay.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureSettingsDisplay.stories.tsx :: Invisible": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureSettingsDisplay.stories.tsx :: Minimal Details": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureSettingsInput.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureSettingsInput.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/SignatureSettingsInput.stories.tsx :: Visible Signature": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/WetSignatureInput.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/WetSignatureInput.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/WetSignatureInput.stories.tsx :: Upload Certificate": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/modals/AddParticipantsFlow.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/modals/CertificateConfigModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/modals/CertificateConfigModal.stories.tsx :: Disabled": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/modals/CertificateConfigModal.stories.tsx :: Multiple Signatures": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/modals/SelectSignatureModal.stories.tsx :: Default": [ + "button-name" + ], + "editor/src/core/components/tools/certSign/panels/ParticipantListPanel.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/ParticipantListPanel.stories.tsx :: Finalized": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/SessionActionsPanel.stories.tsx :: All Signed": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/SessionActionsPanel.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/SessionActionsPanel.stories.tsx :: Finalized": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/SessionDetailPanel.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/SessionDetailPanel.stories.tsx :: Finalized": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/SignControlsPanel.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/panels/SignControlsPanel.stories.tsx :: No Signature Chosen": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/AddSignaturesStep.stories.tsx :: Default": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/AddSignaturesStep.stories.tsx :: Disabled": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/AddSignaturesStep.stories.tsx :: Placement Mode": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/CertificateSelectionStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/CertificateSelectionStep.stories.tsx :: Upload Ready": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/CertificateSelectionStep.stories.tsx :: User Certificate": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/ReviewSignatureStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/ReviewSignatureStep.stories.tsx :: Multiple Signatures": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/ReviewSignatureStep.stories.tsx :: Uploaded Certificate Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/SignatureCreationStep.stories.tsx :: Default": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/SignatureCreationStep.stories.tsx :: Disabled": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/SignatureCreationStep.stories.tsx :: Type Mode": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/SignatureCreationStep.stories.tsx :: With Signature": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/SignaturePlacementStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/SignaturePlacementStep.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/certSign/steps/SignaturePlacementStep.stories.tsx :: Placed": [ + "color-contrast" + ], + "editor/src/core/components/tools/changeMetadata/steps/AdvancedOptionsStep.stories.tsx :: With Custom Metadata": [ + "color-contrast" + ], + "editor/src/core/components/tools/changeMetadata/steps/CustomMetadataStep.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/changeMetadata/steps/CustomMetadataStep.stories.tsx :: With Entries": [ + "color-contrast" + ], + "editor/src/core/components/tools/changeMetadata/steps/DocumentDatesStep.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/changeMetadata/steps/DocumentDatesStep.stories.tsx :: Filled": [ + "button-name" + ], + "editor/src/core/components/tools/compare/CompareDocumentPane.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/compare/ComparePixelWorkbenchView.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/compare/ComparePixelWorkbenchView.stories.tsx :: No Differences": [ + "color-contrast" + ], + "editor/src/core/components/tools/compare/ComparePixelWorkbenchView.stories.tsx :: With Warnings": [ + "color-contrast" + ], + "editor/src/core/components/tools/compress/CompressSettings.stories.tsx :: Default": [ + "aria-input-field-name", + "color-contrast", + "label" + ], + "editor/src/core/components/tools/compress/CompressSettings.stories.tsx :: Disabled": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/compress/CompressSettings.stories.tsx :: File Size Method": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/compress/CompressSettings.stories.tsx :: Line Art Enabled": [ + "aria-input-field-name", + "color-contrast", + "label" + ], + "editor/src/core/components/tools/convert/ConvertFromEbookSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertFromEbookSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertFromEmailSettings.stories.tsx :: Default": [ + "label" + ], + "editor/src/core/components/tools/convert/ConvertFromEmailSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/convert/ConvertFromImageSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertFromSvgSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertFromSvgSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertFromWebSettings.stories.tsx :: Default": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/convert/ConvertFromWebSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/convert/ConvertToEpubSettings.stories.tsx :: Azw 3 Output": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertToEpubSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertToEpubSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/convert/ConvertToPdfaSettings.stories.tsx :: Default": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/convert/ConvertToPdfaSettings.stories.tsx :: Disabled": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/convert/ConvertToPdfaSettings.stories.tsx :: Strict Mode": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/convert/GroupedFormatDropdown.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/crop/CropAutomationSettings.stories.tsx :: Custom Area": [ + "color-contrast" + ], + "editor/src/core/components/tools/crop/CropAutomationSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/crop/CropAutomationSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/crop/CropCoordinateInputs.stories.tsx :: Automation Info": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/BookmarkEditor.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/BookmarkEditor.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/BookmarkEditor.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/EditTableOfContentsSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/EditTableOfContentsSettings.stories.tsx :: Loading With Error": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/EditTableOfContentsSettings.stories.tsx :: No File Selected": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/EditTableOfContentsWorkbenchView.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/EditTableOfContentsWorkbenchView.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/EditTableOfContentsWorkbenchView.stories.tsx :: With Error": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/editTableOfContents/EditTableOfContentsWorkbenchView.stories.tsx :: With Results": [ + "color-contrast" + ], + "editor/src/core/components/tools/flatten/FlattenSettings.stories.tsx :: Custom Render Dpi": [ + "color-contrast" + ], + "editor/src/core/components/tools/flatten/FlattenSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/flatten/FlattenSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/flatten/FlattenSettings.stories.tsx :: Flatten Only Forms": [ + "color-contrast" + ], + "editor/src/core/components/tools/fullscreen/DetailedToolItem.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/fullscreen/DetailedToolItem.stories.tsx :: Selected": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/GetPdfInfoReportView.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/GetPdfInfoReportView.stories.tsx :: No Data": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/GetPdfInfoResults.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/GetPdfInfoResults.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/GetPdfInfoResults.stories.tsx :: Partial Error": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/ComplianceSection.stories.tsx :: All Passed": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/ComplianceSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/ComplianceSection.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/KeyValueSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/KeyValueSection.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/OtherSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/OtherSection.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/PerPageSection.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/SummarySection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/SummarySection.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/SummarySection.stories.tsx :: Hidden Title": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/TableOfContentsSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/sections/TableOfContentsSection.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/shared/KeyValueList.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/shared/KeyValueList.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/shared/ScrollableCodeBlock.stories.tsx :: Custom Empty Message": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/shared/ScrollableCodeBlock.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/getPdfInfo/shared/SectionBlock.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/ocr/LanguagePicker.stories.tsx :: Default": [ + "aria-allowed-attr" + ], + "editor/src/core/components/tools/ocr/OCRSettings.stories.tsx :: Default": [ + "aria-allowed-attr" + ], + "editor/src/core/components/tools/redact/RedactAdvancedSettings.stories.tsx :: Default": [ + "button-name" + ], + "editor/src/core/components/tools/redact/RedactSingleStepSettings.stories.tsx :: Automatic With Words": [ + "button-name" + ], + "editor/src/core/components/tools/redact/RedactSingleStepSettings.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/redact/WordsToRedactInput.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/removeBlanks/RemoveBlanksSettings.stories.tsx :: Default": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/removeBlanks/RemoveBlanksSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/removeBlanks/RemoveBlanksSettings.stories.tsx :: Include Blank Pages": [ + "aria-input-field-name", + "label" + ], + "editor/src/core/components/tools/removePages/RemovePagesSettings.stories.tsx :: Invalid Input": [ + "color-contrast" + ], + "editor/src/core/components/tools/replaceColor/ReplaceColorSettings.stories.tsx :: Custom Color": [ + "button-name", + "label" + ], + "editor/src/core/components/tools/replaceColor/ReplaceColorSettings.stories.tsx :: Default": [ + "label" + ], + "editor/src/core/components/tools/replaceColor/ReplaceColorSettings.stories.tsx :: Disabled": [ + "label" + ], + "editor/src/core/components/tools/sanitize/SanitizeSettings.stories.tsx :: All Selected": [ + "color-contrast" + ], + "editor/src/core/components/tools/sanitize/SanitizeSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/sanitize/SanitizeSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/scannerImageSplit/ScannerImageSplitSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/scannerImageSplit/ScannerImageSplitSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/ErrorNotification.stories.tsx :: Custom Title": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/shared/ErrorNotification.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/components/tools/shared/FileMetadata.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/FileMetadata.stories.tsx :: Unknown Type": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/NavigationControls.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/NavigationControls.stories.tsx :: Last File": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/NoToolsFound.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/NumberInputWithUnit.stories.tsx :: Default": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/shared/NumberInputWithUnit.stories.tsx :: Disabled": [ + "color-contrast", + "label" + ], + "editor/src/core/components/tools/shared/NumberInputWithUnit.stories.tsx :: With Min Max": [ + "label" + ], + "editor/src/core/components/tools/shared/ResultsPreview.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/ResultsPreview.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/ResultsPreview.stories.tsx :: Loading": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/ResultsPreview.stories.tsx :: Single File": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/ToolStep.stories.tsx :: Collapsed": [ + "color-contrast" + ], + "editor/src/core/components/tools/shared/ToolStep.stories.tsx :: With Help Text And Number": [ + "color-contrast" + ], + "editor/src/core/components/tools/showJS/ShowJSView.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/showJS/ShowJSView.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/showJS/ShowJSView.stories.tsx :: With Download": [ + "color-contrast" + ], + "editor/src/core/components/tools/sign/SavedSignaturesSection.stories.tsx :: Admin With Shared Delete": [ + "color-contrast" + ], + "editor/src/core/components/tools/sign/SavedSignaturesSection.stories.tsx :: At Capacity": [ + "color-contrast" + ], + "editor/src/core/components/tools/sign/SavedSignaturesSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/sign/SavedSignaturesSection.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/tools/split/SplitSettings.stories.tsx :: By Poster": [ + "color-contrast" + ], + "editor/src/core/components/tools/split/SplitSettings.stories.tsx :: By Sections": [ + "color-contrast" + ], + "editor/src/core/components/tools/split/SplitSettings.stories.tsx :: No Method Selected": [ + "color-contrast" + ], + "editor/src/core/components/tools/timestampPdf/TimestampPdfSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/timestampPdf/TimestampPdfSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/toolPicker/FavoriteStar.stories.tsx :: Default": [ + "aria-prohibited-attr" + ], + "editor/src/core/components/tools/toolPicker/FavoriteStar.stories.tsx :: Favorited": [ + "aria-prohibited-attr" + ], + "editor/src/core/components/tools/toolPicker/FavoriteStar.stories.tsx :: Sizes": [ + "aria-prohibited-attr" + ], + "editor/src/core/components/tools/validateSignature/ValidateSignatureReportView.stories.tsx :: Default": [ + "aria-allowed-attr", + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/ValidateSignatureReportView.stories.tsx :: Error": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/ValidateSignatureReportView.stories.tsx :: Multiple Signatures": [ + "aria-allowed-attr", + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/ValidateSignatureReportView.stories.tsx :: No Signatures": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/ValidateSignatureSettings.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/ValidateSignatureSettings.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/ValidateSignatureSettings.stories.tsx :: With Cert File": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/FieldBlock.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/FieldBlock.stories.tsx :: Empty Value": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/FileSummaryHeader.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/FileSummaryHeader.stories.tsx :: Missing Metadata": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/FileSummaryHeader.stories.tsx :: No Signatures": [ + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/SignatureSection.stories.tsx :: Default": [ + "aria-allowed-attr", + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/SignatureSection.stories.tsx :: Invalid With Error": [ + "aria-allowed-attr", + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/SignatureSection.stories.tsx :: Self Signed Minimal Data": [ + "aria-allowed-attr", + "color-contrast" + ], + "editor/src/core/components/tools/validateSignature/reportView/SignatureStatusBadge.stories.tsx :: Default": [ + "aria-allowed-attr" + ], + "editor/src/core/components/tools/validateSignature/reportView/SignatureStatusBadge.stories.tsx :: Invalid": [ + "aria-allowed-attr" + ], + "editor/src/core/components/tools/validateSignature/reportView/SignatureStatusBadge.stories.tsx :: Untrusted Signer": [ + "aria-allowed-attr" + ], + "editor/src/core/components/viewer/DocumentReadyWrapper.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/viewer/LocalEmbedPDF.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/viewer/LocalEmbedPDF.stories.tsx :: Unsupported File": [ + "color-contrast" + ], + "editor/src/core/components/viewer/NonPdfViewer.stories.tsx :: Csv": [ + "color-contrast" + ], + "editor/src/core/components/viewer/NonPdfViewer.stories.tsx :: Unsupported": [ + "color-contrast" + ], + "editor/src/core/components/viewer/SearchInterface.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/viewer/SearchInterface.stories.tsx :: Hidden": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/CsvViewer.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/CsvViewer.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/CsvViewer.stories.tsx :: Tsv": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/HtmlViewer.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/JsonViewer.stories.tsx :: Invalid Json": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/NonPdfBanner.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/TextViewer.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/core/components/viewer/nonpdf/TextViewer.stories.tsx :: Markdown": [ + "color-contrast" + ], + "editor/src/core/tokens/Tokens.stories.tsx :: Colours": ["color-contrast"], + "editor/src/core/ui/Banner.stories.tsx :: Playground": ["color-contrast"], + "editor/src/core/ui/Banner.stories.tsx :: Success": ["color-contrast"], + "editor/src/core/ui/Banner.stories.tsx :: Tone Matrix": ["color-contrast"], + "editor/src/core/ui/Banner.stories.tsx :: With Action": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: Accents": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: Disabled Dark": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: Loading": ["button-name"], + "editor/src/core/ui/Button.stories.tsx :: Padding": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: Playground": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: Shape": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: Sizes": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: Variants": ["color-contrast"], + "editor/src/core/ui/Button.stories.tsx :: With Icons": ["color-contrast"], + "editor/src/core/ui/ChatFABButton.stories.tsx :: Default": ["button-name"], + "editor/src/core/ui/ChatFABButton.stories.tsx :: Loading": ["button-name"], + "editor/src/core/ui/ChatFABButton.stories.tsx :: Tick": ["button-name"], + "editor/src/core/ui/ChatFABButton.stories.tsx :: Tick While Loading": [ + "button-name" + ], + "editor/src/core/ui/ChatFABWindow.stories.tsx :: Open": ["color-contrast"], + "editor/src/core/ui/ChatFABWindow.stories.tsx :: Toggle": ["color-contrast"], + "editor/src/core/ui/Chip.stories.tsx :: Accents": ["color-contrast"], + "editor/src/core/ui/Chip.stories.tsx :: Dashed Add": ["nested-interactive"], + "editor/src/core/ui/Chip.stories.tsx :: In Context Op Chain": [ + "color-contrast" + ], + "editor/src/core/ui/Chip.stories.tsx :: Playground": [ + "color-contrast", + "nested-interactive" + ], + "editor/src/core/ui/CodeBlock.stories.tsx :: In Context Quickstart": [ + "color-contrast" + ], + "editor/src/core/ui/CodeBlock.stories.tsx :: In Context Two Up Comparison": [ + "color-contrast" + ], + "editor/src/core/ui/CodeBlock.stories.tsx :: Long Scrolling": [ + "color-contrast", + "scrollable-region-focusable" + ], + "editor/src/core/ui/CodeBlock.stories.tsx :: Playground": ["color-contrast"], + "editor/src/core/ui/Drawer.stories.tsx :: Playground": [ + "aria-allowed-role", + "color-contrast" + ], + "editor/src/core/ui/Drawer.stories.tsx :: With Footer": [ + "aria-allowed-role", + "color-contrast" + ], + "editor/src/core/ui/EmptyState.stories.tsx :: With CT As": ["color-contrast"], + "editor/src/core/ui/FilePicker.stories.tsx :: Accept Pdf": ["color-contrast"], + "editor/src/core/ui/FilePicker.stories.tsx :: Default": ["color-contrast"], + "editor/src/core/ui/FilePicker.stories.tsx :: Multiple": ["color-contrast"], + "editor/src/core/ui/Forms.stories.tsx :: Checkbox Grid Of Categories": [ + "color-contrast" + ], + "editor/src/core/ui/Forms.stories.tsx :: Full Form": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/ui/Forms.stories.tsx :: Input Default": ["color-contrast"], + "editor/src/core/ui/Forms.stories.tsx :: Input Error": ["color-contrast"], + "editor/src/core/ui/Forms.stories.tsx :: Input With Icon": ["color-contrast"], + "editor/src/core/ui/Forms.stories.tsx :: Radio Group": ["color-contrast"], + "editor/src/core/ui/Forms.stories.tsx :: Radio Horizontal": [ + "color-contrast" + ], + "editor/src/core/ui/Forms.stories.tsx :: Slider Confidence": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/ui/Forms.stories.tsx :: Slider Retention": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/ui/Inline.stories.tsx :: Default": ["color-contrast"], + "editor/src/core/ui/Inline.stories.tsx :: Space Between": ["color-contrast"], + "editor/src/core/ui/Inline.stories.tsx :: Wrap": ["color-contrast"], + "editor/src/core/ui/ListRow.stories.tsx :: In Card": ["color-contrast"], + "editor/src/core/ui/MantineForms.stories.tsx :: Color Input Default": [ + "button-name", + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Color Input Error": [ + "button-name", + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Color Input Preselected": [ + "button-name", + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Color Input Sm Size": [ + "button-name", + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Multi Select Default": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Multi Select Error": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Multi Select Sm Size": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Multi Select With Values": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Number Input Decimal": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Number Input Default": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Number Input Error": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Number Input Sm Size": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Number Input With Unit": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Select Default": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Select Error": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Select Searchable": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Select Sm Size": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Slider Default": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Slider Disabled": [ + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Slider No Label": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Slider With Marks": [ + "aria-input-field-name", + "color-contrast" + ], + "editor/src/core/ui/MantineForms.stories.tsx :: Watermark Form": [ + "button-name", + "color-contrast" + ], + "editor/src/core/ui/MethodBadge.stories.tsx :: Default": ["color-contrast"], + "editor/src/core/ui/MethodBadge.stories.tsx :: In Row": ["color-contrast"], + "editor/src/core/ui/MethodBadge.stories.tsx :: Matrix": ["color-contrast"], + "editor/src/core/ui/MetricCard.stories.tsx :: Playground": ["color-contrast"], + "editor/src/core/ui/MetricCard.stories.tsx :: Pro Tier Strip": [ + "color-contrast" + ], + "editor/src/core/ui/MetricStrip.stories.tsx :: Default": ["color-contrast"], + "editor/src/core/ui/NavItem.stories.tsx :: In Context Sidebar Group": [ + "color-contrast" + ], + "editor/src/core/ui/PanelHeader.stories.tsx :: With Actions": [ + "color-contrast" + ], + "editor/src/core/ui/ProgressBar.stories.tsx :: In Context Usage Meter": [ + "color-contrast" + ], + "editor/src/core/ui/ProgressBar.stories.tsx :: Playground": [ + "aria-progressbar-name" + ], + "editor/src/core/ui/ProgressBar.stories.tsx :: Threshold Ladder": [ + "aria-progressbar-name" + ], + "editor/src/core/ui/SegmentedControl.stories.tsx :: Variants": [ + "color-contrast" + ], + "editor/src/core/ui/SegmentedControl.stories.tsx :: With Icons": [ + "color-contrast" + ], + "editor/src/core/ui/SettingsRow.stories.tsx :: List": ["label"], + "editor/src/core/ui/SettingsRow.stories.tsx :: Select Control": ["label"], + "editor/src/core/ui/SettingsRow.stories.tsx :: Toggle": ["label"], + "editor/src/core/ui/SettingsRow.stories.tsx :: With Description": ["label"], + "editor/src/core/ui/SettingsShell.stories.tsx :: Default": ["color-contrast"], + "editor/src/core/ui/StatTile.stories.tsx :: Tone Row": ["color-contrast"], + "editor/src/core/ui/StatusBadge.stories.tsx :: All Tones": ["color-contrast"], + "editor/src/core/ui/StatusBadge.stories.tsx :: Default": ["color-contrast"], + "editor/src/core/ui/StatusBadge.stories.tsx :: Live": ["color-contrast"], + "editor/src/core/ui/StatusBadge.stories.tsx :: Sizes": ["color-contrast"], + "editor/src/core/ui/StepIndicator.stories.tsx :: Small": [ + "aria-progressbar-name" + ], + "editor/src/core/ui/StepIndicator.stories.tsx :: Step 1": [ + "aria-progressbar-name" + ], + "editor/src/core/ui/StepIndicator.stories.tsx :: Step 2": [ + "aria-progressbar-name" + ], + "editor/src/core/ui/StepIndicator.stories.tsx :: Step 3": [ + "aria-progressbar-name" + ], + "editor/src/core/ui/Table.stories.tsx :: Basic": ["color-contrast"], + "editor/src/core/ui/Table.stories.tsx :: Interactive": ["color-contrast"], + "editor/src/core/ui/Tabs.stories.tsx :: In Context Document Verticals": [ + "color-contrast" + ], + "editor/src/core/ui/Tabs.stories.tsx :: Playground": ["color-contrast"], + "editor/src/core/ui/Tabs.stories.tsx :: With Disabled Tab": [ + "color-contrast" + ], + "editor/src/core/ui/Toast.stories.tsx :: Triggers": ["color-contrast"], + "editor/src/portal/components/AppShell.stories.tsx :: Mobile": [ + "color-contrast" + ], + "editor/src/portal/components/AppShell.stories.tsx :: With Home View": [ + "color-contrast" + ], + "editor/src/portal/components/AssistantPanel.stories.tsx :: Reply Fails": [ + "aria-allowed-role" + ], + "editor/src/portal/components/AssistantPanel.stories.tsx :: Slow Reply": [ + "aria-allowed-role" + ], + "editor/src/portal/components/AssistantPanel.stories.tsx :: Suggestions Only": [ + "aria-allowed-role", + "color-contrast" + ], + "editor/src/portal/components/ChatFABWidget.stories.tsx :: Closed": [ + "aria-hidden-focus" + ], + "editor/src/portal/components/ChatFABWidget.stories.tsx :: Full Flow": [ + "aria-hidden-focus", + "color-contrast" + ], + "editor/src/portal/components/ChatFABWidget.stories.tsx :: Loading While Closed": [ + "aria-hidden-focus" + ], + "editor/src/portal/components/ChatFABWidget.stories.tsx :: Unread Result": [ + "aria-hidden-focus" + ], + "editor/src/portal/components/EditorStatusCard.stories.tsx :: Deployment Unavailable": [ + "color-contrast" + ], + "editor/src/portal/components/EditorStatusCard.stories.tsx :: With Setup Checklist": [ + "color-contrast" + ], + "editor/src/portal/components/ErrorBoundary.stories.tsx :: Caught Error": [ + "color-contrast" + ], + "editor/src/portal/components/HomeHero.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/HomeHero.stories.tsx :: Free Tier": [ + "color-contrast" + ], + "editor/src/portal/components/PortalChrome.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/ProcessorFlow.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/ProcessorFlow.stories.tsx :: Idle Empty": [ + "color-contrast" + ], + "editor/src/portal/components/ProcessorFlow.stories.tsx :: Playground": [ + "color-contrast" + ], + "editor/src/portal/components/SearchModal.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/SearchModal.stories.tsx :: Empty Catalogue": [ + "color-contrast" + ], + "editor/src/portal/components/SetupChecklist.stories.tsx :: Almost Done": [ + "color-contrast" + ], + "editor/src/portal/components/SetupChecklist.stories.tsx :: In Progress": [ + "color-contrast" + ], + "editor/src/portal/components/SetupChecklist.stories.tsx :: Not Started": [ + "color-contrast" + ], + "editor/src/portal/components/Sidebar.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/Sidebar.stories.tsx :: Enterprise Tier": [ + "color-contrast" + ], + "editor/src/portal/components/Sidebar.stories.tsx :: Free Tier": [ + "color-contrast" + ], + "editor/src/portal/components/WelcomeBanner.stories.tsx :: With Setup Checklist": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/AccountLinkPanel.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/portal/components/account-link/AccountLinkPanel.stories.tsx :: Load Forbidden": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/AccountLinkPanel.stories.tsx :: Not Linked": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/LinkAccountCard.stories.tsx :: Error": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/LinkAccountCard.stories.tsx :: Linked": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/LinkAccountCard.stories.tsx :: Linking": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/LinkAccountCard.stories.tsx :: Not Linked": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/LinkAccountCard.stories.tsx :: Unconfigured": [ + "color-contrast" + ], + "editor/src/portal/components/account-link/LinkedInstancesTable.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/portal/components/billing/ActivationChoiceModal.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/billing/BundleCheckoutModal.stories.tsx :: First Purchase": [ + "color-contrast" + ], + "editor/src/portal/components/billing/BundleCheckoutModal.stories.tsx :: Top Up": [ + "color-contrast" + ], + "editor/src/portal/components/billing/CardPlaceholder.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/billing/FreePlanView.stories.tsx :: Leader": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/FreePlanView.stories.tsx :: Member": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/InvoicesList.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/billing/PrepaidCapacityCard.stories.tsx :: Bundle Healthy": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/PrepaidCapacityCard.stories.tsx :: Bundle Low": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/SpendLimitCard.stories.tsx :: Approaching Cap": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/SpendLimitCard.stories.tsx :: Editing": [ + "color-contrast" + ], + "editor/src/portal/components/billing/SpendLimitCard.stories.tsx :: Within Cap": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/SpendThisMonthCard.stories.tsx :: With Free Remaining": [ + "color-contrast" + ], + "editor/src/portal/components/billing/StripeCheckoutModal.stories.tsx :: Set Cap": [ + "color-contrast" + ], + "editor/src/portal/components/billing/StripeCheckoutModal.stories.tsx :: Uncapped": [ + "color-contrast" + ], + "editor/src/portal/components/billing/SubscribedPlanView.stories.tsx :: Approaching Cap": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/SubscribedPlanView.stories.tsx :: Leader": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/SubscribedPlanView.stories.tsx :: With Prepaid": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/WalletMeter.stories.tsx :: Free Approaching Limit": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/WalletMeter.stories.tsx :: Free Limit Reached": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/billing/WalletMeter.stories.tsx :: Free Plenty Left": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/docs/AuthenticationSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/docs/ComponentsSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/docs/DocsNav.stories.tsx :: Badged Leaf Active": [ + "color-contrast" + ], + "editor/src/portal/components/docs/DocsNav.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/docs/DocsSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/docs/DocsSection.stories.tsx :: Without Lead": [ + "color-contrast" + ], + "editor/src/portal/components/docs/EndpointReferenceSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/docs/ErrorsSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/docs/GettingStartedSection.stories.tsx :: Default": [ + "color-contrast", + "heading-order" + ], + "editor/src/portal/components/docs/LangSnippet.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/docs/LangSnippet.stories.tsx :: Single Language": [ + "color-contrast" + ], + "editor/src/portal/components/docs/PlaybooksSection.stories.tsx :: Default": [ + "color-contrast", + "heading-order" + ], + "editor/src/portal/components/docs/RateLimitsSection.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/portal/components/docs/RateLimitsSection.stories.tsx :: Free": [ + "color-contrast" + ], + "editor/src/portal/components/docs/RateLimitsSection.stories.tsx :: Pro": [ + "color-contrast" + ], + "editor/src/portal/components/docs/SdksSection.stories.tsx :: Default": [ + "color-contrast", + "heading-order" + ], + "editor/src/portal/components/docs/SdksSection.stories.tsx :: Ga Only": [ + "color-contrast", + "heading-order" + ], + "editor/src/portal/components/docs/SkillsSection.stories.tsx :: Default": [ + "color-contrast", + "heading-order" + ], + "editor/src/portal/components/docs/WebhooksSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/documents/DocumentAudit.stories.tsx :: Approved": [ + "color-contrast" + ], + "editor/src/portal/components/documents/DocumentAudit.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/documents/DocumentDrawer.stories.tsx :: Default": [ + "aria-allowed-role", + "color-contrast" + ], + "editor/src/portal/components/documents/DocumentDrawer.stories.tsx :: Sensitive": [ + "aria-allowed-role", + "color-contrast" + ], + "editor/src/portal/components/documents/DocumentExtractions.stories.tsx :: Masked": [ + "color-contrast" + ], + "editor/src/portal/components/documents/ElevationBanner.stories.tsx :: Granted": [ + "color-contrast" + ], + "editor/src/portal/components/documents/ElevationBanner.stories.tsx :: Granted Four Eyes": [ + "color-contrast" + ], + "editor/src/portal/components/documents/ElevationBanner.stories.tsx :: Locked": [ + "color-contrast" + ], + "editor/src/portal/components/documents/ElevationBanner.stories.tsx :: Locked Four Eyes": [ + "color-contrast" + ], + "editor/src/portal/components/documents/ReviewQueue.stories.tsx :: Default": [ + "aria-prohibited-attr", + "color-contrast", + "empty-table-header", + "nested-interactive" + ], + "editor/src/portal/components/documents/ReviewQueue.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/portal/components/documents/ReviewQueueTable.stories.tsx :: Default": [ + "aria-prohibited-attr", + "color-contrast", + "empty-table-header", + "nested-interactive" + ], + "editor/src/portal/components/documents/ReviewQueueTable.stories.tsx :: Empty": [ + "empty-table-header" + ], + "editor/src/portal/components/documents/ReviewQueueTable.stories.tsx :: Needs Review": [ + "color-contrast", + "empty-table-header", + "nested-interactive" + ], + "editor/src/portal/components/editor-admin/CredentialRotationCard.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/CredentialRotationCard.stories.tsx :: Recently Rotated": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/DeploymentSummaryStrip.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/DeploymentSummaryStrip.stories.tsx :: Pro": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/DeploymentTargets.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/DeploymentTargets.stories.tsx :: Free": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/DeploymentTargets.stories.tsx :: Pro": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/InstanceHealthTable.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/InstanceHealthTable.stories.tsx :: Pro": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/OfflineActivationCard.stories.tsx :: Locked": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/PairingPanel.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/portal/components/editor-admin/PairingPanel.stories.tsx :: Pro": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ApiKeyCard.stories.tsx :: Personal": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ApiKeyCard.stories.tsx :: Revoked": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ApiKeysTab.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ApiKeysTab.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ApiKeysTab.stories.tsx :: Loading": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/AuditExportModal.stories.tsx :: Export Fails": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/AuditExportModal.stories.tsx :: Open": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/AuditTab.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/AuditTab.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/AuditTab.stories.tsx :: Loading": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/AuditTab.stories.tsx :: Non Lead Forbidden": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/AuditTab.stories.tsx :: Team Lead Scoped": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/CreateKeyModal.stories.tsx :: Form": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/DeploymentsTab.stories.tsx :: Default": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ModelsTab.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ModelsTab.stories.tsx :: Free": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/ModelsTab.stories.tsx :: Pro": [ + "aria-progressbar-name", + "color-contrast" + ], + "editor/src/portal/components/infrastructure/SecurityTab.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/infrastructure/StorageTab.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/pipelines/PipelineStepSettings.stories.tsx :: No Settings": [ + "color-contrast" + ], + "editor/src/portal/components/pipelines/PipelineStepSettings.stories.tsx :: Unsupported": [ + "color-contrast" + ], + "editor/src/portal/components/pipelines/PipelinesTable.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/portal/components/pipelines/PipelinesTable.stories.tsx :: Empty": [ + "empty-table-header" + ], + "editor/src/portal/components/policies/PolicyCategoryCard.stories.tsx :: Coming Soon": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyCategoryCard.stories.tsx :: Configured": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyCategoryCard.stories.tsx :: Not Set Up": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyDetailPanel.stories.tsx :: Active": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyDetailPanel.stories.tsx :: Custom No Activity": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyDetailPanel.stories.tsx :: Paused": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyDetailPanel.stories.tsx :: With Flagged Items": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyExternalApiConfig.stories.tsx :: Custom Api Escape Hatch": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyExternalApiConfig.stories.tsx :: Notify Configured": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyFieldRow.stories.tsx :: Chips": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyFieldRow.stories.tsx :: Select": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyFieldRow.stories.tsx :: Text": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyPurviewConfig.stories.tsx :: Configured": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyPurviewConfig.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyPurviewReadConfig.stories.tsx :: Connection Selected": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicyPurviewReadConfig.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicySetupWizard.stories.tsx :: Classification": [ + "color-contrast" + ], + "editor/src/portal/components/policies/PolicySetupWizard.stories.tsx :: Create": [ + "color-contrast", + "label" + ], + "editor/src/portal/components/policies/PolicySetupWizard.stories.tsx :: Edit": [ + "color-contrast", + "label" + ], + "editor/src/portal/components/procurement/DealJourney.stories.tsx :: At Trial": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DealJourney.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DealJourney.stories.tsx :: Live": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DealStatusHero.stories.tsx :: Agreement": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DealStatusHero.stories.tsx :: Live": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DealStatusHero.stories.tsx :: Payment": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DealStatusHero.stories.tsx :: Quote": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DealStatusHero.stories.tsx :: Trial": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DocRow.stories.tsx :: Complete": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DocRow.stories.tsx :: Download": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DocRow.stories.tsx :: Locked": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DocRow.stories.tsx :: Paid Addon": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DocRow.stories.tsx :: Sign Action": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DocumentLedger.stories.tsx :: At Trial": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/DocumentLedger.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/LockedState.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementAgreement.stories.tsx :: Agreeing": [ + "color-contrast", + "scrollable-region-focusable" + ], + "editor/src/portal/components/procurement/ProcurementAgreement.stories.tsx :: Default": [ + "color-contrast", + "scrollable-region-focusable" + ], + "editor/src/portal/components/procurement/ProcurementAgreement.stories.tsx :: Downloading": [ + "color-contrast", + "scrollable-region-focusable" + ], + "editor/src/portal/components/procurement/ProcurementBanner.stories.tsx :: Deal Underway": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementBanner.stories.tsx :: Upsell": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementExtras.stories.tsx :: License": [ + "aria-dialog-name" + ], + "editor/src/portal/components/procurement/ProcurementExtras.stories.tsx :: License Trial": [ + "aria-dialog-name" + ], + "editor/src/portal/components/procurement/ProcurementExtras.stories.tsx :: Schedule Call": [ + "aria-dialog-name" + ], + "editor/src/portal/components/procurement/ProcurementExtras.stories.tsx :: Trial Manage": [ + "aria-dialog-name" + ], + "editor/src/portal/components/procurement/ProcurementExtras.stories.tsx :: Trial Manage Maxed": [ + "aria-dialog-name" + ], + "editor/src/portal/components/procurement/ProcurementExtras.stories.tsx :: Trial Setup": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementFlow.stories.tsx :: Default": [ + "color-contrast", + "scrollable-region-focusable" + ], + "editor/src/portal/components/procurement/ProcurementFlow.stories.tsx :: Unlinked": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementHome.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementStages.stories.tsx :: License": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementStages.stories.tsx :: License Downloading": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/ProcurementStages.stories.tsx :: License Online Only": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/QuoteBuilder.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/procurement/StageStepper.stories.tsx :: Locked": [ + "color-contrast" + ], + "editor/src/portal/components/sources/ConnectionForm.stories.tsx :: Conditional Fields Revealed": [ + "color-contrast" + ], + "editor/src/portal/components/sources/ConnectionForm.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/sources/ConnectionForm.stories.tsx :: Filled": [ + "color-contrast" + ], + "editor/src/portal/components/sources/ConnectionModal.stories.tsx :: Edit": [ + "color-contrast" + ], + "editor/src/portal/components/sources/ConnectionModal.stories.tsx :: Fixed Type": [ + "color-contrast" + ], + "editor/src/portal/components/sources/SourceModal.stories.tsx :: Edit Folder": [ + "color-contrast" + ], + "editor/src/portal/components/sources/SourceModal.stories.tsx :: Edit Webhook": [ + "color-contrast" + ], + "editor/src/portal/components/sources/SourcesTable.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/portal/components/users/ConfirmModal.stories.tsx :: Danger": [ + "color-contrast" + ], + "editor/src/portal/components/users/ConfirmModal.stories.tsx :: Neutral": [ + "color-contrast" + ], + "editor/src/portal/components/users/InviteMemberModal.stories.tsx :: Create Account Form": [ + "color-contrast" + ], + "editor/src/portal/components/users/InviteMemberModal.stories.tsx :: No Admin Role": [ + "color-contrast" + ], + "editor/src/portal/components/users/InviteMemberModal.stories.tsx :: Open": [ + "color-contrast" + ], + "editor/src/portal/components/users/InviteMemberModal.stories.tsx :: Scoped To Team": [ + "color-contrast" + ], + "editor/src/portal/components/users/InviteMemberModal.stories.tsx :: Self Hosted Direct Create": [ + "color-contrast" + ], + "editor/src/portal/components/users/InviteMemberModal.stories.tsx :: Self Hosted No Mail": [ + "color-contrast" + ], + "editor/src/portal/components/users/MoveToTeamModal.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/users/NewTeamModal.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/users/PendingInvitations.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/users/PendingInvitations.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/portal/components/users/PendingInvitations.stories.tsx :: Expires Today": [ + "color-contrast" + ], + "editor/src/portal/components/users/RenameTeamModal.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/users/ResetPasswordModal.stories.tsx :: Default": [ + "color-contrast", + "label" + ], + "editor/src/portal/components/users/ResetPasswordModal.stories.tsx :: With Email": [ + "color-contrast", + "label" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: Large Team": [ + "color-contrast" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: Member States": [ + "color-contrast" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: Org Only": [ + "color-contrast" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: Saas Team Leader": [ + "color-contrast" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: Single Team": [ + "color-contrast" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: Team Wide Processor": [ + "color-contrast" + ], + "editor/src/portal/components/users/UsersDirectory.stories.tsx :: With Guests": [ + "color-contrast" + ], + "editor/src/portal/data/Endpoints.stories.tsx :: By Vertical": [ + "color-contrast" + ], + "editor/src/portal/data/Ops.stories.tsx :: Agents": ["color-contrast"], + "editor/src/portal/data/Ops.stories.tsx :: Library By Category": [ + "color-contrast" + ], + "editor/src/portal/data/Ops.stories.tsx :: Pipeline Ops": ["color-contrast"], + "editor/src/portal/theme/MantineIntegration.stories.tsx :: Side By Side": [ + "color-contrast" + ], + "editor/src/portal/views/DeveloperDocs.stories.tsx :: Default": [ + "color-contrast", + "landmark-unique" + ], + "editor/src/portal/views/Documents.stories.tsx :: Default": [ + "aria-prohibited-attr", + "color-contrast", + "empty-table-header", + "nested-interactive" + ], + "editor/src/portal/views/Documents.stories.tsx :: Empty": [ + "aria-prohibited-attr", + "color-contrast", + "empty-table-header", + "nested-interactive" + ], + "editor/src/portal/views/Home.stories.tsx :: Enterprise Tier": [ + "color-contrast", + "landmark-no-duplicate-banner", + "landmark-unique" + ], + "editor/src/portal/views/Home.stories.tsx :: Free Tier": ["color-contrast"], + "editor/src/portal/views/Home.stories.tsx :: Pro Tier": [ + "color-contrast", + "landmark-no-duplicate-banner", + "landmark-unique" + ], + "editor/src/portal/views/Home.stories.tsx :: Subscribed In Procurement": [ + "color-contrast", + "landmark-no-duplicate-banner", + "landmark-unique" + ], + "editor/src/portal/views/Integrations.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/views/Integrations.stories.tsx :: No Connections": [ + "color-contrast" + ], + "editor/src/portal/views/PipelineBuilder.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/portal/views/Pipelines.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/portal/views/Pipelines.stories.tsx :: Empty": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/portal/views/Policies.stories.tsx :: Default": ["color-contrast"], + "editor/src/portal/views/Policies.stories.tsx :: Empty": ["color-contrast"], + "editor/src/portal/views/Sources.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/portal/views/Sources.stories.tsx :: Empty": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/proprietary/auth/ui/AuthScreens.stories.tsx :: Signup": [ + "aria-hidden-focus" + ], + "editor/src/proprietary/auth/ui/EmailPasswordForm.stories.tsx :: With Errors": [ + "color-contrast" + ], + "editor/src/proprietary/components/policies/PolicyPiiField.stories.tsx :: With Selection": [ + "color-contrast" + ], + "editor/src/proprietary/components/policies/PolicyRedactConfig.stories.tsx :: With Selection": [ + "color-contrast" + ], + "editor/src/proprietary/components/policies/PolicyWatermarkConfig.stories.tsx :: Default": [ + "button-name", + "color-contrast", + "label" + ], + "editor/src/proprietary/components/policies/PolicyWatermarkConfig.stories.tsx :: Disabled": [ + "color-contrast", + "label" + ], + "editor/src/proprietary/components/shared/ChangeUserPasswordModal.stories.tsx :: Default": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/ChangeUserPasswordModal.stories.tsx :: Mail Disabled": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/ChangeUserPasswordModal.stories.tsx :: Non Email Username": [ + "aria-dialog-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/UpdateSeatsModal.stories.tsx :: At Minimum": [ + "button-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/UpdateSeatsModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/EnterpriseRequiredBanner.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/GeneralWithLoginLanding.stories.tsx :: Admin Banner": [ + "label" + ], + "editor/src/proprietary/components/shared/config/GeneralWithLoginLanding.stories.tsx :: Default": [ + "label" + ], + "editor/src/proprietary/components/shared/config/GeneralWithLoginLanding.stories.tsx :: With Backend Version": [ + "label" + ], + "editor/src/proprietary/components/shared/config/OverviewHeader.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/OverviewHeader.stories.tsx :: Signed In": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AccountSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AccountSection.stories.tsx :: Mfa Enabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AccountSection.stories.tsx :: Sso User": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAiDocumentsSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAiGeneralSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAiLimitsSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAiModelsSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAuditSection.stories.tsx :: Audit Logging Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAuditSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAuditSection.stories.tsx :: Enabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminAuditSection.stories.tsx :: Login Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminFolderAccessSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/AdminFolderAccessSection.stories.tsx :: Login Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/ApiKeys.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/ApiKeys.stories.tsx :: Load Error": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/ApiKeys.stories.tsx :: Loading": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/LoginAgreementEditor.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/LoginAgreementEditor.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/TeamDetailsSection.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/proprietary/components/shared/config/configSections/TeamDetailsSection.stories.tsx :: Empty": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/proprietary/components/shared/config/configSections/TeamsSection.stories.tsx :: Default": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/proprietary/components/shared/config/configSections/TeamsSection.stories.tsx :: Empty": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/proprietary/components/shared/config/configSections/TeamsSection.stories.tsx :: Login Disabled": [ + "color-contrast", + "empty-table-header" + ], + "editor/src/proprietary/components/shared/config/configSections/apiKeys/RefreshModal.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditClearDataSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditClearDataSection.stories.tsx :: Login Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditEventsTable.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditEventsTable.stories.tsx :: With File Metadata Columns": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditExportSection.stories.tsx :: All Fields Enabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditExportSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditExportSection.stories.tsx :: Login Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditFiltersForm.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditFiltersForm.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditFiltersForm.stories.tsx :: Interactive": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditFiltersForm.stories.tsx :: With Filters Applied": [ + "button-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditStatsCards.stories.tsx :: Day": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditStatsCards.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditStatsCards.stories.tsx :: Month": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditSystemStatus.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/audit/AuditSystemStatus.stories.tsx :: Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/plan/AvailablePlansSection.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/plan/AvailablePlansSection.stories.tsx :: Login Disabled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/plan/AvailablePlansSection.stories.tsx :: With Currency Selector": [ + "color-contrast", + "label" + ], + "editor/src/proprietary/components/shared/config/configSections/plan/PlanCard.stories.tsx :: Current Tier": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/plan/PlanCard.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/plan/PlanCard.stories.tsx :: Enterprise Plan": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/plan/PlanCard.stories.tsx :: Free Plan": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/usage/UsageAnalyticsChart.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/usage/UsageAnalyticsChart.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/usage/UsageAnalyticsTable.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/config/configSections/usage/UsageAnalyticsTable.stories.tsx :: Empty": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/StripeCheckout.stories.tsx :: Default": [ + "button-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/StripeCheckout.stories.tsx :: Hosted Checkout Success": [ + "button-name", + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/components/PriceDisplay.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/components/PriceDisplay.stories.tsx :: Enterprise With Total": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/components/PriceDisplay.stories.tsx :: Simple": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/components/PricingBadge.stories.tsx :: Current": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/components/PricingBadge.stories.tsx :: Popular": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/components/PricingBadge.stories.tsx :: Savings": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/EmailStage.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/EmailStage.stories.tsx :: Filled": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/EmailStage.stories.tsx :: With Error": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/ErrorStage.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/ErrorStage.stories.tsx :: Network Error": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/PaymentStage.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/PaymentStage.stories.tsx :: Redirecting": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/PlanSelectionStage.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/PlanSelectionStage.stories.tsx :: Enterprise": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/PlanSelectionStage.stories.tsx :: With Savings": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/SuccessStage.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/SuccessStage.stories.tsx :: Polling": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/SuccessStage.stories.tsx :: Timeout": [ + "color-contrast" + ], + "editor/src/proprietary/components/shared/stripeCheckout/stages/SuccessStage.stories.tsx :: Upgrade Complete": [ + "color-contrast" + ], + "editor/src/proprietary/components/workflow/ParticipantView.stories.tsx :: Completed": [ + "color-contrast" + ], + "editor/src/proprietary/components/workflow/ParticipantView.stories.tsx :: Default": [ + "color-contrast" + ], + "editor/src/proprietary/components/workflow/ParticipantView.stories.tsx :: Expired": [ + "color-contrast" + ], + "editor/src/proprietary/routes/login/OAuthButtons.stories.tsx :: Vertical": [ + "image-redundant-alt" + ] +} diff --git a/frontend/.storybook/a11y-check.mjs b/frontend/.storybook/a11y-check.mjs new file mode 100644 index 0000000000..7eef5e079e --- /dev/null +++ b/frontend/.storybook/a11y-check.mjs @@ -0,0 +1,202 @@ +// a11y regression gate — baseline diff. +// +// Consumes the Storybook Vitest scan's JSON reporter output (one or more files +// in --in) and compares each story's axe rule violations against +// .storybook/a11y-baseline.json. Existing violations are grandfathered; the gate +// fails when a story breaks a rule it wasn't already breaking, when a story +// crashes, or when the scan didn't cover everything it was supposed to. +// +// node a11y-check.mjs --in --manifest # diff (the gate) +// node a11y-check.mjs --in --manifest --record # (re)write baseline +// node a11y-check.mjs --in --merge # union into baseline +// +// Exit codes: 0 pass · 1 regression · 2 unusable scan (incomplete/no input). +// +// The baseline records WHICH rules a story breaks, not how many nodes break +// them. Node counts drift between runs (stories fetch data asynchronously, so +// axe sometimes samples the DOM before late content lands), which made a +// count-based gate cry wolf on unrelated changes. Rule presence is stable, and +// "story now breaks a rule it didn't before" is the regression worth blocking. +// +// Run from frontend/. Baseline path defaults next to this script. + +import { readFileSync, readdirSync, writeFileSync, existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const here = dirname(fileURLToPath(import.meta.url)); +const args = process.argv.slice(2); +const opt = (n, d) => { + const i = args.indexOf(n); + const v = i >= 0 ? args[i + 1] : undefined; + // A flag given without a value is a mistake, not a request for the default. + if (i >= 0 && (v === undefined || v.startsWith("--"))) { + console.error(`a11y-check: ${n} requires a value`); + process.exit(2); + } + return v ?? d; +}; +const record = args.includes("--record"); +const merge = args.includes("--merge"); +const inDir = opt("--in", ".a11y-scan"); +const manifestFile = opt("--manifest", ""); +const baselineFile = opt("--baseline", join(here, "a11y-baseline.json")); + +// Every axe violation block in the matcher's message ends with a link to the +// rule's docs, e.g. .../rules/axe/4.11/color-contrast?application=axeAPI. That +// URL is the only machine-stable rule id in the message — the human-readable +// help text around it is free to change between addon and axe versions. +const RULE_URL = /dequeuniversity\.com\/rules\/axe\/[\d.]+\/([a-z0-9-]+)/g; + +/** Reads every scan report in `dir`, keyed by " :: ". */ +function collect(dir) { + const rules = {}; // storyKey -> Set(ruleId) + const crashed = []; // storyKey[] — failed for a non-a11y reason + const seenFiles = new Set(); + let scanned = 0; + + for (const cf of readdirSync(dir).filter((f) => /\.json$/.test(f))) { + let report; + try { + report = JSON.parse(readFileSync(join(dir, cf), "utf8")); + } catch { + console.error(`a11y-check: unreadable scan report: ${cf}`); + process.exit(2); + } + for (const tf of report.testResults || []) { + const norm = tf.name.replace(/\\/g, "/"); + const idx = norm.search(/editor\/src\//); + const file = idx >= 0 ? norm.slice(idx) : norm; + seenFiles.add(file); + for (const a of tf.assertionResults || []) { + scanned++; + if (a.status === "passed") continue; + const key = `${file} :: ${a.title || a.fullName || "?"}`; + const found = new Set(); + for (const m of a.failureMessages || []) + for (const hit of m.matchAll(RULE_URL)) found.add(hit[1]); + if (found.size) { + rules[key] = rules[key] || new Set(); + for (const id of found) rules[key].add(id); + } else { + // No rule link anywhere in the message: the story threw, timed out or + // otherwise failed. Silently dropping these is how a broken scan can + // look clean — and a crashed story reports no violations at all, + // which would also mask any it really has. + crashed.push(key); + } + } + } + } + return { rules, crashed, seenFiles, scanned }; +} + +if (!existsSync(inDir)) { + console.error(`a11y-check: scan dir not found: ${inDir}`); + process.exit(2); +} +const { rules, crashed, seenFiles, scanned } = collect(inDir); +const observed = {}; +for (const [k, set] of Object.entries(rules)) observed[k] = [...set].sort(); + +// Completeness: every story file the scan was asked to cover must appear in the +// reports. A dropped batch (killed process, empty report) would otherwise read +// as "those stories have no violations" and pass. +if (manifestFile) { + if (!existsSync(manifestFile)) { + console.error(`a11y-check: manifest not found: ${manifestFile}`); + process.exit(2); + } + const expected = readFileSync(manifestFile, "utf8") + .split("\n") + .map((l) => l.trim().replace(/\\/g, "/")) + .filter(Boolean); + const missing = expected.filter((f) => !seenFiles.has(f)); + if (missing.length) { + console.error( + `a11y-check: scan is incomplete — ${missing.length} of ${expected.length} story files produced no results:`, + ); + missing.slice(0, 20).forEach((f) => console.error(` ${f}`)); + if (missing.length > 20) + console.error(` … and ${missing.length - 20} more`); + console.error("Refusing to report on a partial scan."); + process.exit(2); + } +} + +if (record || merge) { + const base = + merge && existsSync(baselineFile) + ? JSON.parse(readFileSync(baselineFile, "utf8")) + : {}; + if (crashed.length) { + console.error( + `a11y-check: refusing to record — ${crashed.length} story(ies) failed for a non-a11y reason:`, + ); + crashed.slice(0, 20).forEach((k) => console.error(` ${k}`)); + console.error("Fix those first; recording now would bake in blind spots."); + process.exit(2); + } + // Union, so a re-record can only widen what's grandfathered — it never drops + // a rule that a slower run happened to miss and then reports it as new. + for (const [k, list] of Object.entries(observed)) + base[k] = [...new Set([...(base[k] || []), ...list])].sort(); + const sorted = {}; + for (const k of Object.keys(base).sort()) sorted[k] = base[k]; + writeFileSync(baselineFile, JSON.stringify(sorted, null, 2) + "\n"); + const pairs = Object.values(sorted).reduce((s, l) => s + l.length, 0); + console.log( + `baseline ${merge ? "merged" : "recorded"}: ${scanned} stories scanned, ` + + `${Object.keys(sorted).length} with violations, ${pairs} story-rule pairs.`, + ); + process.exit(0); +} + +if (!existsSync(baselineFile)) { + console.error("a11y-check: no baseline file:", baselineFile); + process.exit(2); +} +const baseline = JSON.parse(readFileSync(baselineFile, "utf8")); + +const regressions = []; +for (const [key, list] of Object.entries(observed)) { + const base = baseline[key] || []; + for (const id of list) + if (!base.includes(id)) regressions.push(`NEW ${key} ${id}`); +} +// Stories that improved — reported so the baseline can be ratcheted down, never +// a failure. +const fixed = Object.keys(baseline).filter( + (k) => seenFiles.has(k.split(" :: ")[0]) && !observed[k], +); + +const pairs = Object.values(observed).reduce((s, l) => s + l.length, 0); +console.log( + `a11y scan: ${scanned} stories, ${Object.keys(observed).length} with violations, ` + + `${pairs} story-rule pairs (baselined).`, +); + +if (crashed.length) { + console.error(`\n✖ ${crashed.length} story(ies) failed to render:`); + crashed.slice(0, 50).forEach((k) => console.error(` ${k}`)); + if (crashed.length > 50) console.error(` … and ${crashed.length - 50} more`); +} +if (regressions.length) { + console.error(`\n✖ ${regressions.length} a11y regression(s) vs baseline:`); + regressions.slice(0, 100).forEach((r) => console.error(` ${r}`)); + if (regressions.length > 100) + console.error(` … and ${regressions.length - 100} more`); + console.error( + "\nFix the new violation(s). If a story was renamed or moved its old " + + "baseline key no longer matches — re-record: task frontend:storybook:a11y:record", + ); +} +if (crashed.length || regressions.length) process.exit(1); + +if (fixed.length) + console.log( + `✓ no a11y regressions. ${fixed.length} baselined story(ies) now clean — ` + + `re-record to lock that in: task frontend:storybook:a11y:record`, + ); +else console.log("✓ no a11y regressions vs baseline."); +process.exit(0); diff --git a/frontend/.storybook/a11y-scan.sh b/frontend/.storybook/a11y-scan.sh new file mode 100644 index 0000000000..855a4449dc --- /dev/null +++ b/frontend/.storybook/a11y-scan.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# Run the Storybook Vitest scan in batches and emit one JSON report per batch +# into .a11y-scan/, plus a manifest of every story file the run was supposed to +# cover and a log of the raw output. Consumed by a11y-check.mjs, which fails if +# any manifest entry produced no results. Run from frontend/. +# +# a11y-scan.sh scan every story +# a11y-scan.sh [file…] scan only these story files +# +# Batching keeps each browser session small: a single run over the whole story +# set holds one Chromium context open for the entire scan, so one crash in it +# costs every story after that point. +set -uo pipefail +cd "$(dirname "$0")/.." || exit 1 + +OUT=".a11y-scan" +rm -rf "$OUT" +mkdir -p "$OUT" +MANIFEST="$OUT/manifest.txt" +LOG="$OUT/scan.log" + +if [ "$#" -gt 0 ]; then + # Explicit list (the pull-request path passes just the stories a branch + # touched). Anything that no longer exists is dropped, so a deleted story + # doesn't fail the manifest check. + for f in "$@"; do [ -f "$f" ] && printf '%s\n' "$f"; done | sort -u >"$MANIFEST" +else + # Tracked story files plus any not yet committed, so a new story can be + # checked before it is added to the index. + { + git ls-files 'editor/src/**/*.stories.ts' 'editor/src/**/*.stories.tsx' + git ls-files --others --exclude-standard 'editor/src/**/*.stories.ts' \ + 'editor/src/**/*.stories.tsx' + } | sort -u >"$MANIFEST" +fi + +mapfile -t FILES <"$MANIFEST" +TOTAL=${#FILES[@]} +if [ "$TOTAL" -eq 0 ]; then + if [ "$#" -gt 0 ]; then + echo "a11y-scan: no existing story files in the given list — nothing to scan" + exit 0 + fi + echo "a11y-scan: no story files found — check the glob" >&2 + exit 2 +fi + +CHUNK=20 +NB=$(((TOTAL + CHUNK - 1) / CHUNK)) +echo "a11y-scan: $TOTAL story files, $NB batches of $CHUNK" + +failed=() +i=0 +ci=0 +while [ "$i" -lt "$TOTAL" ]; do + ci=$((ci + 1)) + batch=("${FILES[@]:i:CHUNK}") + i=$((i + CHUNK)) + out="$OUT/chunk-$ci.json" + filters=() + for f in "${batch[@]}"; do filters+=("${f%.tsx}"); done + # The scan exits non-zero whenever a story has a violation — expected here, so + # the report is what matters, not the status. Output is teed to the log so a red + # CI run still has the offending selectors and help text to work from. + for attempt in 1 2; do + timeout 300 npx vitest run --config .storybook/vitest.config.ts \ + --reporter=json --outputFile="$out" "${filters[@]}" >>"$LOG" 2>&1 + [ -s "$out" ] && break + echo "a11y-scan: batch $ci produced no report (attempt $attempt)" >&2 + done + if [ -s "$out" ]; then + echo " batch $ci/$NB done" + else + failed+=("$ci") + echo " batch $ci/$NB FAILED — no report" >&2 + fi +done + +echo "a11y-scan: $(ls "$OUT"/chunk-*.json 2>/dev/null | wc -l)/$NB batches produced reports" +if [ ${#failed[@]} -gt 0 ]; then + echo "a11y-scan: ${#failed[@]} batch(es) produced no report: ${failed[*]}" >&2 + echo "a11y-scan: see $LOG. Not reporting on a partial scan." >&2 + exit 2 +fi diff --git a/frontend/.storybook/preview.tsx b/frontend/.storybook/preview.tsx index cd2e53153d..a5cc4161c4 100644 --- a/frontend/.storybook/preview.tsx +++ b/frontend/.storybook/preview.tsx @@ -242,12 +242,10 @@ const preview: Preview = { ], }, a11y: { - // Run axe automatically against the story root; violations show in the - // Accessibility panel. `context` replaced `element` in addon-a11y 9.x. - context: "#storybook-root", - config: {}, - options: {}, - test: "todo", + // Run axe against the rendered story; `test: "error"` fails the scan on + // any violation. Context is left at the addon default (the document root) + // so it resolves under both the Storybook UI and the Vitest browser mount. + test: "error", }, }, globalTypes: { diff --git a/frontend/.storybook/vitest.config.ts b/frontend/.storybook/vitest.config.ts index 9535f63a47..8c0d0e81f2 100644 --- a/frontend/.storybook/vitest.config.ts +++ b/frontend/.storybook/vitest.config.ts @@ -4,10 +4,9 @@ import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; /** * Dedicated Vitest config that turns every story into a browser test: it mounts - * the story in real Chromium as a render/smoke check (a story must mount without - * throwing). a11y is currently report-only (preview's `a11y.test: "todo"`) and is - * not yet enforced here — flipping it to pass/fail is a follow-up. Kept separate - * from editor/vitest.config.ts (the jsdom unit tests) so the two suites don't collide. + * the story in real Chromium and runs axe against it, so a story fails if it + * throws on mount or trips an accessibility rule. Kept separate from + * editor/vitest.config.ts (the jsdom unit tests) so the two suites don't collide. * * The storybook test must live in a `test.projects[]` entry (not a flat config) * so Vitest wires up the browser test runner correctly. @@ -23,8 +22,8 @@ export default defineConfig({ // JSX runtime import, so Vite optimizes them lazily mid-run and emits // "optimized dependencies changed, reloading". That reload tears down the // browser worker and whichever stories were mid-load fail with a bogus - // "Failed to fetch dynamically imported module" — a result that looks real. - // Naming them here keeps a run deterministic. + // "Failed to fetch dynamically imported module" — a scan that then looks + // like a real result. Naming them here keeps a run deterministic. include: [ "react", "react/jsx-runtime", @@ -42,10 +41,11 @@ export default defineConfig({ plugins: [storybookTest({ configDir: resolve(__dirname) })], test: { name: "storybook", - // Mounting a story takes well over Vitest's 5s default on the heavier - // screens, and a story that trips the timeout is reported as a failure - // with no message — which reads like a crash. Give it room; a - // genuinely hung story still fails, just later. + // Mounting a story and running a full axe pass over it takes well + // over Vitest's 5s default on the heavier screens, and a story that + // trips the timeout is reported as a failure with no message — which + // reads like a crash and makes the run non-reproducible. Give it + // room; a genuinely hung story still fails, just later. testTimeout: 60_000, hookTimeout: 60_000, browser: { diff --git a/frontend/.storybook/vitest.setup.ts b/frontend/.storybook/vitest.setup.ts index 6265c9bf54..22fa4c027e 100644 --- a/frontend/.storybook/vitest.setup.ts +++ b/frontend/.storybook/vitest.setup.ts @@ -1,10 +1,15 @@ import { beforeAll } from "vitest"; import { setProjectAnnotations } from "@storybook/react-vite"; +import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; // eslint-disable-next-line no-restricted-imports -- Storybook-only: the sibling preview config has no @-alias. import * as projectAnnotations from "./preview"; -// Apply the same decorators/parameters/globals the Storybook UI uses (providers, -// i18n, theme) so stories run under Vitest render identically to the browser. -const project = setProjectAnnotations([projectAnnotations]); +// Include addon-a11y's annotations so its axe checks run under Vitest, not only +// in the Storybook UI panel. projectAnnotations supplies the same +// decorators/parameters/globals (providers, i18n, theme) as the browser. +const project = setProjectAnnotations([ + a11yAddonAnnotations, + projectAnnotations, +]); beforeAll(project.beforeAll);