fix(a11y): add ARIA progressbar to QuotaBar component (#1878)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Operators run those agents against paid model providers, so the web UI has a Costs surface that reports spend and quota utilisation > - Those figures are drawn as horizontal bars by the shared `QuotaBar` component (`ui/src/components/QuotaBar.tsx`), consumed by `BillerSpendCard` and `ProviderQuotaCard` > - `QuotaBar` renders its fill as a plain `<div>` whose CSS width is the only encoding of the percentage — no `role`, no value attributes, no accessible name > - A screen reader therefore announces nothing at all for these bars, so the spend and quota numbers they convey are unavailable to assistive-technology users (WCAG 2.1 SC 4.1.2, Name/Role/Value) > - The same gap is being closed for the other bars in this area by #1805 (BudgetPolicyCard) and #1869 (ProviderQuotaCard's inline bars); `QuotaBar` is the remaining shared component with no ARIA semantics > - This pull request adds the standard ARIA progressbar attributes to `QuotaBar`'s fill element, reusing the `label` prop the component already takes > - The benefit is that every progress bar on the Costs screen exposes its name and current value to assistive technology, with no visual or behavioural change for sighted users ## Linked Issues or Issue Description No existing GitHub issue — the problem is described in-PR below, following [`bug_report.yml`](.github/ISSUE_TEMPLATE/bug_report.yml). Related PRs from the same accessibility sweep (each covers a *different* component, so these are companions rather than duplicates — all three are currently open): - Refs #1805 — ARIA attributes for the BudgetPolicyCard progress bar - Refs #1869 — ARIA attributes for the ProviderQuotaCard inline bars **What happened?** On the Costs screen, the spend/quota bars rendered by `QuotaBar` (via `BillerSpendCard` and `ProviderQuotaCard`) are non-semantic `<div>` elements. Screen readers skip them entirely: no role, no value, no label is announced, so the percentage information is available only visually. **Expected behavior** Each bar should be exposed as a progress bar with an accessible name and its current value — e.g. announced as "Weekly spend: 45%, progress bar". **Steps to reproduce** 1. Run the app and open the Costs page. 2. Expand any provider or biller card so a quota/spend bar is visible. 3. Navigate to the bar with a screen reader (VoiceOver, NVDA, or Chrome DevTools → Accessibility pane). 4. Observe that the fill element has no role, no value, and no accessible name. **Paperclip version or commit** Reproduces on `master`; `ui/src/components/QuotaBar.tsx` has carried no ARIA attributes since the component was introduced. **Deployment mode** Not deployment-specific — the missing markup is in the shipped component. Verified in local dev (`pnpm dev`). **Agent adapter(s) involved** Not adapter-specific (core UI). ## What Changed - `ui/src/components/QuotaBar.tsx`: added `role="progressbar"` to the fill `<div>`. - Added `aria-valuenow={Math.round(clampedPct)}` with `aria-valuemin={0}` / `aria-valuemax={100}`, using the already-clamped percentage so the reported value can never fall outside 0–100. - Added an `aria-label` of the form `<label>: <pct>%`, reusing the existing `label` prop for the accessible name. - No changes to props, styling, layout, or rendering logic: 1 file, 5 added lines, 0 deleted. ## Verification - Manual: open Costs → expand a provider/biller card, inspect the bar in Chrome DevTools → Accessibility pane. The fill node now reports role `progressbar`, value `45`, min `0`, max `100`, and name "Weekly spend: 45%". - Manual: with VoiceOver/NVDA, the bar announces "Weekly spend: 45%, progress bar" instead of being skipped. - Visual regression check: the bar is unchanged for sighted users — only ARIA attributes were added, no class or style changes. - CI (lint, typecheck, build, tests) is green on this branch. - No unit test is added: the change is a set of static ARIA attributes on one element, and `QuotaBar` currently has no test file. Happy to add one if maintainers would like coverage here. ## Risks Low risk. Presentation-only accessibility metadata on a single element; no props, state, or styling change, and no other component is touched. The one debatable point is that the percentage appears in both `aria-label` and `aria-valuenow`, so some screen readers may announce it twice; both forms are valid, and the label is kept because it carries the bar's name alongside the value. Happy to drop the percentage from the label if reviewers prefer the terser announcement. ## Model Used <!-- @bluzername: please replace this line with the provider + exact model ID (and context window / reasoning mode if relevant), or "None — human-authored". Required by CONTRIBUTING.md. --> ## Checklist - [x] I have included a thinking path that traces from project context to this change - [ ] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [ ] I have run tests locally and they pass - [ ] I have added or updated tests where applicable - [ ] I have updated relevant documentation to reflect my changes (no docs cover this component's markup) - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge
This commit is contained in:
parent
204c416478
commit
b431d4eca1
|
|
@ -46,6 +46,11 @@ export function QuotaBar({
|
|||
<div className="relative h-2 w-full border border-border overflow-hidden">
|
||||
{/* fill */}
|
||||
<div
|
||||
role="progressbar"
|
||||
aria-valuenow={Math.round(clampedPct)}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
aria-label={`${label}: ${Math.round(clampedPct)}%`}
|
||||
className={cn(
|
||||
"absolute inset-y-0 left-0 transition-(--tp-width-background-color) duration-150",
|
||||
fillColor(clampedPct),
|
||||
|
|
|
|||
Loading…
Reference in New Issue