Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,6 @@
>{{ clampedProgress }}%</span>
</div>

<!-- Overage ARIA signal (visually hidden, aria-live region for assistive tech) -->
<div
v-if="overage > 0"
class="billing-meter-progress__overage d-flex align-center mb-1"
aria-live="assertive"
aria-atomic="true"
/>

<!-- Bar variant -->
<template v-if="variant === 'bar'">
<v-progress-linear
Expand Down
2 changes: 2 additions & 0 deletions src/modules/billing/lang/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ export const billingFr = {
remaining: '(aucun restant) | ({count} restant) | ({count} restants)',
/** i18n key: billing.meterProgress.extras — interpolate {count} */
extras: '+{count} extras',
/** i18n key: billing.meterProgress.overDetail — interpolate {overage} — shown in tooltip when in overage */
overDetail: 'dépassement de {overage} compute',
/** i18n key: billing.meterProgress.ariaOver — interpolate {base}, {used}, {quota}, {overage} */
ariaOver: '{base}{used} sur {quota} utilisés, {overage} de dépassement',
/** i18n key: billing.meterProgress.ariaUsed — interpolate {base}, {used}, {quota}, {percent} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ exports[`BillingMeterProgressComponent > matches the overage snapshot 1`] = `
"<div data-v-f7f25334="" class="billing-meter-progress" aria-label="120 of 100 used, 20 over quota">
<!-- Label row -->
<!--v-if-->
<!-- Overage ARIA signal (visually hidden, aria-live region for assistive tech) -->
<div data-v-f7f25334="" class="billing-meter-progress__overage d-flex align-center mb-1" aria-live="assertive" aria-atomic="true"></div><!-- Bar variant -->
<!-- Bar variant -->
<div data-v-f7f25334="" class="v-progress-linear v-progress-linear--rounded v-progress-linear--rounded v-theme--light v-locale--is-ltr" style="top: 0px; height: 10px; --v-progress-linear-height: 10px;" role="progressbar" aria-hidden="false" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100">
<!---->
<div class="v-progress-linear__background bg-surface-variant" style="opacity: NaN;"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ exports[`BillingUsageBarComponent > matches the meter overage snapshot 1`] = `
<div data-v-f7f25334="" data-v-621d4e81="" class="billing-meter-progress" aria-label="1100 of 1000 used, 100 over quota">
<!-- Label row -->
<!--v-if-->
<!-- Overage ARIA signal (visually hidden, aria-live region for assistive tech) -->
<div data-v-f7f25334="" class="billing-meter-progress__overage d-flex align-center mb-1" aria-live="assertive" aria-atomic="true"></div><!-- Bar variant -->
<!-- Bar variant -->
<div data-v-f7f25334="" class="v-progress-linear v-progress-linear--rounded v-progress-linear--rounded v-theme--light v-locale--is-ltr" style="top: 0px; height: 10px; --v-progress-linear-height: 10px;" role="progressbar" aria-hidden="false" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100">
<!---->
<div class="v-progress-linear__background bg-surface-variant" style="opacity: NaN;"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,20 @@ describe('BillingMeterProgressComponent', () => {
expect(summary.text()).toContain('100%');
});

it('adds aria-live regions for summary and overage updates', () => {
it('summary row carries the assertive aria-live announcement when in overage', () => {
const wrapper = mountComponent({ used: 120, quota: 100, overage: 20, netRemainingRaw: -20 });
// Task 4: summary is assertive when in overage (merged into one region)
expect(wrapper.find('.billing-meter-progress__summary').attributes('aria-live')).toBe('assertive');
// Hidden overage div still exists for backward compat (aria-live assertive + aria-atomic)
expect(wrapper.find('.billing-meter-progress__overage').attributes('aria-live')).toBe('assertive');
expect(wrapper.find('.billing-meter-progress__overage').attributes('aria-atomic')).toBe('true');
// Task 4: a single summary aria-live region announces overage state changes
// (the previously empty overage div had no content and was removed to avoid
// dual assertive regions racing each other in assistive tech).
const summary = wrapper.find('.billing-meter-progress__summary');
expect(summary.attributes('aria-live')).toBe('assertive');
// Summary text carries the % primary value and overage tooltip is reachable
expect(summary.text()).toContain('100%');
});

it('summary aria-live is polite (non-interrupting) when not in overage', () => {
const wrapper = mountComponent({ used: 50, quota: 100 });
expect(wrapper.find('.billing-meter-progress__summary').attributes('aria-live')).toBe('polite');
});

it('matches the overage snapshot', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,16 +356,18 @@ describe('BillingUsageBarComponent', () => {
expect(meterProgress.props('netRemainingRaw')).toBe(-100);
});

it('adds aria-live regions for standard meter summary and overage badge', () => {
it('adds aria-live regions for standard meter summary and overage announcement', () => {
authState.user = { roles: ['user'] };
const store = useBillingStore();
store.subscription = { status: 'active', plan: 'starter' };
store.usageMeter = { meterUsed: 1100, meterQuota: 1000, extrasRemaining: 0 };
const wrapper = mountComponent({ mode: 'meter' });

// Task 4: usage-bar summary stays polite; the meterProgress child summary
// turns assertive when in overage. Single assertive region (no empty
// duplicate overage div) avoids dual-region race in assistive tech.
expect(wrapper.find('.billing-usage-bar__summary').attributes('aria-live')).toBe('polite');
expect(wrapper.find('.billing-meter-progress__overage').attributes('aria-live')).toBe('assertive');
expect(wrapper.find('.billing-meter-progress__overage').attributes('aria-atomic')).toBe('true');
expect(wrapper.find('.billing-meter-progress__summary').attributes('aria-live')).toBe('assertive');
});

it('matches the meter overage snapshot', () => {
Expand Down
Loading