fix(wcag): Legend panel issues#3349
fix(wcag): Legend panel issues#3349kenchase wants to merge 1 commit intoCanadian-Geospatial-Platform:developfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements WCAG-driven accessibility improvements centered on the Legend panel (Issue #3327), while also standardizing IDs, improving focus management, and adding reusable a11y styling utilities across geoview-core (and one plugin touchpoint).
Changes:
- Improves Legend/Legend fullscreen accessibility (ARIA semantics, focus restoration, loading announcements, remove non-interactive tooltips/truncation).
- Standardizes/uniquifies DOM IDs and strengthens focus-trap + dialog semantics across panels/modals/app-bar controls.
- Updates shared UI primitives for a11y (Switch now requires a label + uses unique ids; reusable
visuallyHidden; aria-disabled styling for icon buttons; ProgressBar supportsaria-label).
Reviewed changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/geoview-custom-legend/src/components/layer-item.tsx | Passes mapId/containerType into core LegendLayer for consistent IDs/ARIA. |
| packages/geoview-core/src/ui/switch/switch.tsx | Makes label required and links label↔control via unique ids. |
| packages/geoview-core/src/ui/switch/switch-style.ts | Improves focus indication styling for switch + minor palette tweak. |
| packages/geoview-core/src/ui/style/themeOptionsGenerator.ts | Adds global styling for [aria-disabled="true"] icon buttons (buttonOutline). |
| packages/geoview-core/src/ui/style/default.ts | Introduces shared visuallyHidden sr-only style utility. |
| packages/geoview-core/src/ui/panel/panel.tsx | Updates dialog semantics/labeling and changes panel container id format. |
| packages/geoview-core/src/ui/linear-progress/linear-progress.tsx | Allows passing an aria-label to ProgressBar for SRs. |
| packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx | Requires containerType, adds unique ids, improves tooltip semantics. |
| packages/geoview-core/src/core/components/notifications/notifications.tsx | Updates app-bar notification button id to new naming scheme. |
| packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx | Updates Switch usage to satisfy required label typing. |
| packages/geoview-core/src/core/components/legend/legend.tsx | Wires mapId/containerType into LegendLayer, adds fullscreen button ref for focus restore, updates ToggleAll usage. |
| packages/geoview-core/src/core/components/legend/legend-styles.ts | Removes title truncation styling and adopts shared visuallyHidden. |
| packages/geoview-core/src/core/components/legend/legend-layer.tsx | Adds unique ids + ARIA relationships, removes non-interactive tooltip, adds SR live announcements for load/error, passes props into collapsible content. |
| packages/geoview-core/src/core/components/legend/legend-layer-items.tsx | Improves visibility toggle labeling/pressed state + stabilizes item ids for focus restoration. |
| packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx | Adjusts tooltips/ARIA labels and introduces aria-disabled/pressed patterns for icon buttons. |
| packages/geoview-core/src/core/components/legend/legend-layer-container.tsx | Makes WMS legend image interactive (ButtonBase), adds region semantics + links collapse to header, adds focus restoration hooks. |
| packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx | Improves fullscreen dialog semantics/ids/title, switches to IconButton, restores focus on close, marks background inert. |
| packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx | Updates Switch usage to satisfy required label typing. |
| packages/geoview-core/src/core/components/layers/layers-toolbar.tsx | Adds containerType + unique ids; passes containerType into ToggleAll. |
| packages/geoview-core/src/core/components/layers/layers-panel.tsx | Propagates containerType into LayersToolbar. |
| packages/geoview-core/src/core/components/geolocator/geolocator.tsx | Adjusts aria-modal semantics for WCAG mode; minor focus-trap handling change. |
| packages/geoview-core/src/core/components/geolocator/geolocator-style.ts | Replaces local sr-only pattern with shared visuallyHidden. |
| packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx | Adds aria-disabled behavior to clear-filters button based on active filters/results. |
| packages/geoview-core/src/core/components/export/export-modal.tsx | Updates ids to be map-scoped and dialog-consistent. |
| packages/geoview-core/src/core/components/export/export-modal-button.tsx | Simplifies Export button ARIA (dialog semantics) + trims unused props. |
| packages/geoview-core/src/core/components/details/coordinate-info.tsx | Updates Switch usage to satisfy required label typing. |
| packages/geoview-core/src/core/components/data-table/filter-map.tsx | Updates Switch usage to satisfy required label typing. |
| packages/geoview-core/src/core/components/common/layer-list.tsx | Removes redundant aria-disabled when disabled is already used. |
| packages/geoview-core/src/core/components/app-bar/buttons/version.tsx | Adopts shared visuallyHidden and standardizes ids for focus trap + dialog. |
| packages/geoview-core/src/core/components/app-bar/app-bar.tsx | Improves app-bar button ARIA based on WCAG mode and avoids ESC conflicts with lightbox. |
| packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts | Minor import ordering / type import cleanup. |
| packages/geoview-core/public/locales/fr/translation.json | Adds/updates strings for new labels and SR announcements. |
| packages/geoview-core/public/locales/en/translation.json | Adds/updates strings for new labels and SR announcements. |
| docs/app/accessibility.md | Expands internal a11y best-practices documentation (WIP). |
You can also share your feedback on Copilot code review. Take the survey.
| setActiveAppBarTab(DEFAULT_APPBAR_CORE.GEOLOCATOR, false, false); | ||
| setTimeout(() => { | ||
| disableFocusTrap(`${mapId}-${CONTAINER_TYPE.APP_BAR}-${DEFAULT_APPBAR_CORE.GEOLOCATOR}-panel-btn`); | ||
| disableFocusTrap(`${mapId}-${CONTAINER_TYPE.APP_BAR}-${DEFAULT_APPBAR_CORE.GEOLOCATOR}-panel-btn`); |
| <ButtonBase | ||
| id={buttonId} | ||
| sx={styles.imageButton} | ||
| onClick={() => initLightBox(imgSrc, buttonId, 0, 2)} |
There was a problem hiding this comment.
Fixed. Now uses separate props for altText and returnFocusId
There was a problem hiding this comment.
Fixed. Now uses separate props for altText and returnFocusId
| aria-label={`${t('layers.zoomVisibleScale')} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips | ||
| aria-disabled={!isZoomToVisibleScaleCapable} | ||
| aria-pressed={!isZoomToVisibleScaleCapable} | ||
| className={`buttonOutline`} | ||
| onClick={controls.handleZoomToLayerVisibleScale} |
| aria-disabled={!isInVisibleRange || parentHidden || !isVisible || layerStatus === 'error'} // WCAG - used instead of disabled to allow button to retain focus after keyboard press | ||
| aria-pressed={!isInVisibleRange} |
| * | ||
| * @param item - The legend item to generate an ID for | ||
| * @returns A stable, unique ID in format: "legend-item-{randomId}-{mapId}" | ||
| * @returns A stable, unique ID in format: "{mapId}-legend-item-{randomId}-" |
docs/app/accessibility.md
Outdated
| ${mapId}-${containerType}-${panel.panelId)-[element] | ||
|
|
||
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); | ||
| ${mapId}-${containerType}-[element]-${uniqueId} | ||
| ${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId} |
There was a problem hiding this comment.
Documentation is WIP. Will be reviewed later.
There was a problem hiding this comment.
Documentation is WIP. Will be reviewed later.
| }} | ||
| ref={panelContainerRef} | ||
| id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-${panel.panelId || ''}-panel`} | ||
| id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-panel-${panel.panelId || ''}`} |
| @@ -150,7 +154,7 @@ export function GeolocatorResult({ geoLocationData, searchValue, error }: Geoloc | |||
| className="buttonOutline" | |||
| aria-label={t('geolocator.clearFilters')} | |||
| onClick={handleClearFilters} | |||
| interface CollapsibleContentProps { | ||
| layerPath: string; | ||
| initLightBox: (imgSrc: string, title: string, index: number, total: number) => void; | ||
| LegendLayerComponent: typeof LegendLayer; | ||
| showControls: boolean; |
docs/app/accessibility.md
Outdated
| ${mapId}-${containerType}-${panel.panelId)-[element] | ||
|
|
||
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); | ||
| ${mapId}-${containerType}-[element]-${uniqueId} | ||
| ${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId} |
There was a problem hiding this comment.
Documentation is WIP. Will be reviewed later.
There was a problem hiding this comment.
Documentation is WIP. Will be reviewed later.
f3da7f2 to
656d2dd
Compare
656d2dd to
8091658
Compare
There was a problem hiding this comment.
Pull request overview
This PR focuses on WCAG-driven accessibility fixes across GeoView’s legend panel and related UI controls, improving ARIA semantics, focus management, and DOM ID uniqueness across core and plugin surfaces.
Changes:
- Improve legend panel accessibility (ARIA semantics, live region announcements, focus restoration, tooltip/labeling adjustments).
- Standardize/uniquify DOM IDs and add shared
visuallyHiddenutility for sr-only content. - Strengthen switch and icon-button accessibility patterns (required labels, focus styling, aria-disabled usage).
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/geoview-custom-legend/src/components/layer-item.tsx | Pass mapId/containerType into core LegendLayer for unique IDs and ARIA relationships. |
| packages/geoview-core/src/ui/switch/switch.tsx | Make label required and generate a unique id to associate label and control. |
| packages/geoview-core/src/ui/switch/switch-style.ts | Add stronger focus indication styling for switch labels/controls. |
| packages/geoview-core/src/ui/style/themeOptionsGenerator.ts | Add styling for aria-disabled="true" icon buttons (buttonOutline). |
| packages/geoview-core/src/ui/style/default.ts | Introduce shared visuallyHidden (sr-only) style utility. |
| packages/geoview-core/src/ui/panel/panel.tsx | Update panel ARIA semantics for focus-trapped (dialog) mode and labels. |
| packages/geoview-core/src/ui/linear-progress/linear-progress.tsx | Allow passing aria-label to progress bar for screen readers. |
| packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx | Require source/containerType and make toggle-all IDs unique per map/container. |
| packages/geoview-core/src/core/components/notifications/notifications.tsx | Update notification button IDs to match naming conventions / unique IDs. |
| packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx | Update Switch usage to align with required label typing. |
| packages/geoview-core/src/core/components/legend/legend.tsx | Pass mapId/containerType to LegendLayer; wire fullscreen focus restoration refs; update ToggleAll usage. |
| packages/geoview-core/src/core/components/legend/legend-styles.ts | Remove title truncation; reuse shared visuallyHidden; adjust toggle bar padding. |
| packages/geoview-core/src/core/components/legend/legend-layer.tsx | Add stable IDs for ARIA relationships and ARIA live region announcements for loading states. |
| packages/geoview-core/src/core/components/legend/legend-layer-items.tsx | Improve ARIA labeling (aria-pressed, descriptive labels) and update item ID format. |
| packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx | Improve tooltips/ARIA labels; add aria-disabled patterns for zoom controls. |
| packages/geoview-core/src/core/components/legend/legend-layer-container.tsx | Use interactive element (ButtonBase) for WMS legend images; link collapsibles via aria-labelledby; pass through IDs. |
| packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx | Improve fullscreen dialog semantics/IDs; restore focus to triggering button; update title copy. |
| packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx | Update Switch labels to match new required typing. |
| packages/geoview-core/src/core/components/layers/layers-toolbar.tsx | Add unique IDs; thread containerType; use ToggleAll with required props. |
| packages/geoview-core/src/core/components/layers/layers-panel.tsx | Pass containerType into LayersToolbar. |
| packages/geoview-core/src/core/components/geolocator/geolocator.tsx | Add ARIA live region for loading announcements; progress bar labeling; adjust aria-modal usage. |
| packages/geoview-core/src/core/components/geolocator/geolocator-style.ts | Replace duplicated sr-only CSS with shared visuallyHidden. |
| packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx | Use aria-disabled for clear-filters button and prevent action when disabled. |
| packages/geoview-core/src/core/components/export/export-modal.tsx | Make dialog/menu/input IDs unique per map instance. |
| packages/geoview-core/src/core/components/export/export-modal-button.tsx | Simplify export button ARIA attributes; mark as dialog trigger. |
| packages/geoview-core/src/core/components/details/feature-info-table.tsx | Update lightbox API usage to support focus restoration + alt text parameterization. |
| packages/geoview-core/src/core/components/details/coordinate-info.tsx | Update Switch labels to match new required typing. |
| packages/geoview-core/src/core/components/data-table/filter-map.tsx | Update Switch labels to match new required typing. |
| packages/geoview-core/src/core/components/data-table/data-table.tsx | Update lightbox API usage for new signature (altText + returnFocusId). |
| packages/geoview-core/src/core/components/common/layer-list.tsx | Remove redundant aria-disabled when using disabled. |
| packages/geoview-core/src/core/components/common/hooks/use-light-box.tsx | Add focus restoration by element id; update init signature to include alt text + return focus id. |
| packages/geoview-core/src/core/components/app-bar/buttons/version.tsx | Update IDs to match ${mapId}-${CONTAINER_TYPE...} conventions; reuse shared visuallyHidden. |
| packages/geoview-core/src/core/components/app-bar/app-bar.tsx | Adjust ARIA semantics for WCAG (dialog vs region); avoid handling ESC when lightbox is open. |
| packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts | Import ordering cleanup. |
| packages/geoview-core/public/locales/en/translation.json | Add new keys for panel labeling and legend/geolocator loading announcements; adjust legend fullscreen title. |
| packages/geoview-core/public/locales/fr/translation.json | Same as EN: add new keys and update legend fullscreen title. |
| docs/app/accessibility.md | Expand accessibility best practices guidance, especially around IDs, aria-disabled, and live regions. |
You can also share your feedback on Copilot code review. Take the survey.
docs/app/accessibility.md
Outdated
| ${mapId}-${containerType}-${panel.panelId)-[element] | ||
|
|
||
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); | ||
| ${mapId}-${containerType}-[element]-${uniqueId} | ||
| ${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId} |
docs/app/accessibility.md
Outdated
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); |
docs/app/accessibility.md
Outdated
| When a button has keyboard focus and becomes disabled on press, focus is lost and jumps unpredictably to another element, disorienting keyboard users who lose track of their position in the interface. | ||
|
|
||
| - Use aria-disabled instead of disabled | ||
| - Style the the aria-disabled element to look like it would if disabled |
| tooltip={t('layers.zoomVisibleScale')} | ||
| aria-label={`${t('layers.zoomVisibleScale')} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips | ||
| aria-disabled={!isZoomToVisibleScaleCapable} | ||
| aria-pressed={!isZoomToVisibleScaleCapable} |
| tooltip={t('legend.zoomTo')} | ||
| aria-label={`${t('legend.zoomTo')} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips | ||
| aria-disabled={isZoomToLayerDisabled} // WCAG - used instead of disabled to allow button to retain focus after keyboard press | ||
| aria-pressed={!isInVisibleRange} | ||
| className="buttonOutline" |
| '&[aria-disabled="true"]': { | ||
| color: `${geoViewColors.bgColor.dark[450]}`, | ||
| backgroundColor: 'transparent', | ||
| cursor: 'not-allowed', | ||
| pointerEvents: 'none', | ||
| }, |
| component="section" | ||
| role={open ? 'dialog' : undefined} | ||
| aria-modal={open ? 'true' : undefined} | ||
| role={activeTrapGeoView ? 'dialog' : undefined} | ||
| aria-label={t('general.panelLabel', { title: t(panel.title) })!} | ||
| aria-hidden={!open} | ||
| aria-label={`${t(panel.title)} panel`} | ||
| aria-modal={activeTrapGeoView || undefined} | ||
| sx={{ |
| component="section" | ||
| role={isPanelOpen ? 'dialog' : undefined} | ||
| aria-modal={isPanelOpen ? 'true' : undefined} | ||
| aria-modal={activeTrapGeoView || undefined} |
8091658 to
3b1e284
Compare
3b1e284 to
a4daa30
Compare
There was a problem hiding this comment.
Pull request overview
WCAG/accessibility fixes centered on the Legend panel (and related UI primitives) within GeoView’s React+TypeScript viewer, improving labeling, focus management, unique IDs, and screen-reader announcements.
Changes:
- Improve Legend (and fullscreen Legend) semantics, focus restoration, and screen reader support (live regions, better ARIA labels, more interactive affordances).
- Standardize/uniquify element IDs across panels, toolbars, modals, and buttons to avoid duplicate IDs and strengthen ARIA relationships.
- Update shared UI primitives/styles for accessibility (Switch requires a label + unique id association, aria-disabled styling, shared
visuallyHiddenutility, ProgressBar aria-label support).
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/geoview-custom-legend/src/components/layer-item.tsx | Passes mapId/containerType into core LegendLayer from the custom legend plugin. |
| packages/geoview-core/src/ui/switch/switch.tsx | Makes label required and associates label↔switch via unique id. |
| packages/geoview-core/src/ui/switch/switch-style.ts | Improves focus indication styling for Switch. |
| packages/geoview-core/src/ui/style/themeOptionsGenerator.ts | Adds global styling for [aria-disabled="true"] icon buttons (buttonOutline). |
| packages/geoview-core/src/ui/style/default.ts | Adds shared visuallyHidden sr-only style utility. |
| packages/geoview-core/src/ui/panel/panel.tsx | Improves dialog semantics/labeling and standardizes panel IDs. |
| packages/geoview-core/src/ui/linear-progress/linear-progress.tsx | Allows aria-label passthrough for ProgressBar accessibility. |
| packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx | Requires container context, unique IDs, and improves tooltip semantics. |
| packages/geoview-core/src/core/components/notifications/notifications.tsx | Updates button IDs and dialog semantics for notifications popper. |
| packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx | Adjusts Switch labels to align with required-label change. |
| packages/geoview-core/src/core/components/legend/legend.tsx | Passes mapId/containerType into LegendLayer; improves fullscreen button focus management and ToggleAll usage. |
| packages/geoview-core/src/core/components/legend/legend-styles.ts | Removes truncation rules for legend titles and reuses shared visuallyHidden. |
| packages/geoview-core/src/core/components/legend/legend-layer.tsx | Adds ARIA live region announcements, improves collapse button ARIA, and threads mapId/containerType. |
| packages/geoview-core/src/core/components/legend/legend-layer-items.tsx | Improves toggle button ARIA, adjusts tooltip text, and stabilizes unique IDs with mapId prefix. |
| packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx | Improves icon button labels/state with aria-pressed/aria-disabled and click-guarding for focus retention. |
| packages/geoview-core/src/core/components/legend/legend-layer-container.tsx | Makes legend images open lightbox via an interactive element (ButtonBase) and adds region semantics for collapsible content. |
| packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx | Improves fullscreen dialog IDs/title, uses IconButton, restores focus on close, and sets inert content behind dialog. |
| packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx | Updates Switch labels to match required-label API. |
| packages/geoview-core/src/core/components/layers/layers-toolbar.tsx | Adds containerType + unique IDs and passes containerType into ToggleAll. |
| packages/geoview-core/src/core/components/layers/layers-panel.tsx | Threads containerType into LayersToolbar. |
| packages/geoview-core/src/core/components/geolocator/geolocator.tsx | Adds live region announcements for loading status and improves aria-modal behavior in WCAG mode. |
| packages/geoview-core/src/core/components/geolocator/geolocator-style.ts | Reuses shared visuallyHidden utility. |
| packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx | Uses aria-disabled (with early return) for clear-filters button based on active filters. |
| packages/geoview-core/src/core/components/export/export-modal.tsx | Standardizes dialog/input/menu IDs with mapId prefix. |
| packages/geoview-core/src/core/components/export/export-modal-button.tsx | Simplifies Export button API and sets aria-haspopup="dialog". |
| packages/geoview-core/src/core/components/details/feature-info-table.tsx | Updates lightbox API usage to include alt text + focus restoration id. |
| packages/geoview-core/src/core/components/details/coordinate-info.tsx | Updates Switch label usage to match required-label API. |
| packages/geoview-core/src/core/components/data-table/filter-map.tsx | Updates Switch label usage to match required-label API. |
| packages/geoview-core/src/core/components/data-table/data-table.tsx | Updates lightbox init signature to include alt text + focus restoration id. |
| packages/geoview-core/src/core/components/common/layer-list.tsx | Removes redundant aria-disabled where disabled already applies. |
| packages/geoview-core/src/core/components/common/hooks/use-light-box.tsx | Enhances lightbox API to support alt text and focus restoration to triggering element. |
| packages/geoview-core/src/core/components/app-bar/buttons/version.tsx | Standardizes IDs and focus trap ids; updates popper labeling. |
| packages/geoview-core/src/core/components/app-bar/app-bar.tsx | Improves ARIA semantics for buttons/panels in WCAG mode and avoids ESC conflicts when lightbox is open. |
| packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts | Minor import organization. |
| packages/geoview-core/public/locales/fr/translation.json | Adds new WCAG-related strings (panel label, legend fullscreen title, live announcements, etc.). |
| packages/geoview-core/public/locales/en/translation.json | Adds new WCAG-related strings (panel label, legend fullscreen title, live announcements, etc.). |
| docs/app/accessibility.md | Expands accessibility best-practices documentation (WIP). |
Comments suppressed due to low confidence (2)
packages/geoview-core/src/core/components/notifications/notifications.tsx:257
- The notifications IconButton no longer exposes its expanded state or controlled element relationship. Since it toggles a
role="dialog"popper (id="notification-dialog-${mapId}"), consider restoringaria-expanded={open}andaria-controlspointing at that dialog id.
<IconButton
id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-notifications-btn`}
aria-label={t('appbar.notifications')}
aria-haspopup="dialog"
tooltipPlacement="right"
onClick={handleOpenPopover}
packages/geoview-core/src/core/components/app-bar/buttons/version.tsx:129
- The version IconButton toggles a
role="dialog"popper (id="${mapId}-${CONTAINER_TYPE.APP_BAR}-version-dialog") but no longer setsaria-expanded/aria-controls. Re-adding these would preserve the button→dialog relationship and state for assistive tech.
<IconButton
id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-version-btn`}
aria-haspopup="dialog"
aria-label={t('appbar.version')}
tooltipPlacement="right"
onClick={handleOpenPopover}
You can also share your feedback on Copilot code review. Take the survey.
|
|
||
| _Work in progress_ | ||
|
|
||
| The viewer needs to be accessible for keyboard and screen reader. It's should follow WCAG 2.1 requirements: https://www.w3.org/TR/WCAG21 |
| edge="end" | ||
| size="small" | ||
| tooltip={tooltip} | ||
| aria-label={`${tooltip} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips |
| <IconButton | ||
| aria-label={t('legend.selectLayerAndScroll')} | ||
| tooltip={t('legend.selectLayerAndScroll')} | ||
| aria-label={`${t('legend.selectLayerAndScroll')} - ${layerName}`} | ||
| className="buttonOutline" |
| '&:focus-visible': { | ||
| outline: '2px solid', | ||
| outlineColor: 'primary.main', | ||
| }, |
a4daa30 to
1c45047
Compare
1c45047 to
f0f73e6
Compare
kenchase
left a comment
There was a problem hiding this comment.
@kenchase made 22 comments.
Reviewable status: 0 of 37 files reviewed, 22 unresolved discussions.
docs/app/accessibility.md
Outdated
| ${mapId}-${containerType}-${panel.panelId)-[element] | ||
|
|
||
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); | ||
| ${mapId}-${containerType}-[element]-${uniqueId} | ||
| ${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId} |
There was a problem hiding this comment.
Documentation is WIP. Will be reviewed later.
docs/app/accessibility.md
Outdated
| ${mapId}-${containerType}-${panel.panelId)-[element] | ||
|
|
||
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); | ||
| ${mapId}-${containerType}-[element]-${uniqueId} | ||
| ${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId} |
There was a problem hiding this comment.
Documentation is WIP. Will be reviewed later.
docs/app/accessibility.md
Outdated
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); |
docs/app/accessibility.md
Outdated
| ${mapId}-${containerType}-${panel.panelId)-[element] | ||
|
|
||
| // If a unique ID is required | ||
| // uniqueId could be generated from: | ||
| // const id = useId(); | ||
| // const id generateId(8); | ||
| ${mapId}-${containerType}-[element]-${uniqueId} | ||
| ${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId} |
docs/app/accessibility.md
Outdated
| When a button has keyboard focus and becomes disabled on press, focus is lost and jumps unpredictably to another element, disorienting keyboard users who lose track of their position in the interface. | ||
|
|
||
| - Use aria-disabled instead of disabled | ||
| - Style the the aria-disabled element to look like it would if disabled |
| <IconButton | ||
| aria-label={t('legend.selectLayerAndScroll')} | ||
| tooltip={t('legend.selectLayerAndScroll')} | ||
| aria-label={`${t('legend.selectLayerAndScroll')} - ${layerName}`} | ||
| className="buttonOutline" |
| * | ||
| * @param item - The legend item to generate an ID for | ||
| * @returns A stable, unique ID in format: "legend-item-{randomId}-{mapId}" | ||
| * @returns A stable, unique ID in format: "{mapId}-legend-item-{randomId}-" |
| }} | ||
| ref={panelContainerRef} | ||
| id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-${panel.panelId || ''}-panel`} | ||
| id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-panel-${panel.panelId || ''}`} |
| component="section" | ||
| role={open ? 'dialog' : undefined} | ||
| aria-modal={open ? 'true' : undefined} | ||
| role={activeTrapGeoView ? 'dialog' : undefined} | ||
| aria-label={t('general.panelLabel', { title: t(panel.title) })!} | ||
| aria-hidden={!open} | ||
| aria-label={`${t(panel.title)} panel`} | ||
| aria-modal={activeTrapGeoView || undefined} | ||
| sx={{ |
| '&[aria-disabled="true"]': { | ||
| color: `${geoViewColors.bgColor.dark[450]}`, | ||
| backgroundColor: 'transparent', | ||
| cursor: 'not-allowed', | ||
| pointerEvents: 'none', | ||
| }, |
There was a problem hiding this comment.
Pull request overview
This PR addresses WCAG-related accessibility issues in GeoView’s legend panel and related UI primitives (switches, panels, dialogs/lightbox), improving keyboard navigation, ARIA semantics, focus management, and ID uniqueness across the app.
Changes:
- Improve legend accessibility: semantic markup, descriptive ARIA labels, live-region announcements for loading, focus restoration for fullscreen/lightbox, and removal of non-interactive tooltips/truncation.
- Standardize unique ID generation/usage and update components to pass
mapId/containerTypewhere needed. - Strengthen shared UI primitives for accessibility (required
Switchlabels + label association,aria-disabledstyling, progress bar labeling, panel/dialog ARIA updates).
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/geoview-custom-legend/src/components/layer-item.tsx | Passes mapId/containerType into core LegendLayer for consistent IDs/ARIA relationships. |
| packages/geoview-core/src/ui/switch/switch.tsx | Makes label required and associates label↔switch via generated unique IDs. |
| packages/geoview-core/src/ui/switch/switch-style.ts | Improves focus indication styling for the switch wrapper and adjusts focus-visible styling. |
| packages/geoview-core/src/ui/style/themeOptionsGenerator.ts | Adds visual styling for aria-disabled="true" icon buttons (cursor + muted colors). |
| packages/geoview-core/src/ui/style/default.ts | Adds shared visuallyHidden sr-only utility style. |
| packages/geoview-core/src/ui/panel/panel.tsx | Updates panel ARIA semantics for focus-trapped (dialog) vs non-trapped modes; standardizes IDs. |
| packages/geoview-core/src/ui/linear-progress/linear-progress.tsx | Allows aria-label to be passed through to progress bar for SR labeling. |
| packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx | Requires explicit source and containerType and updates IDs/tooltips/labels for accessibility. |
| packages/geoview-core/src/core/components/notifications/notifications.tsx | Updates notification button ID to match global conventions; dialog semantics remain. |
| packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx | Updates Switch usage to align with required label typing. |
| packages/geoview-core/src/core/components/legend/legend.tsx | Passes mapId/containerType to LegendLayer; adds fullscreen button ref for focus restoration; updates ToggleAll usage. |
| packages/geoview-core/src/core/components/legend/legend-styles.ts | Removes truncation/out-of-range hiding, tweaks layout padding, and reuses shared visuallyHidden. |
| packages/geoview-core/src/core/components/legend/legend-layer.tsx | Adds IDs for ARIA relationships, live-region announcements for layer status changes, and propagates mapId/containerType. |
| packages/geoview-core/src/core/components/legend/legend-layer-items.tsx | Makes visibility toggle buttons more descriptive with aria-label/aria-pressed and ID convention updates. |
| packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx | Refines legend control button ARIA/tooltip behavior and uses aria-disabled patterns for focus retention. |
| packages/geoview-core/src/core/components/legend/legend-layer-container.tsx | Makes WMS legend images open lightbox via an interactive element (ButtonBase) and links collapsibles via ARIA attributes. |
| packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx | Improves fullscreen dialog ID/title semantics and restores focus to triggering button on exit. |
| packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx | Updates Switch label usage to match required label typing. |
| packages/geoview-core/src/core/components/layers/layers-toolbar.tsx | Adds containerType/map-scoped IDs and updates ToggleAll usage. |
| packages/geoview-core/src/core/components/layers/layers-panel.tsx | Wires containerType down into LayersToolbar. |
| packages/geoview-core/src/core/components/geolocator/geolocator.tsx | Adds live-region loading announcements and improves aria-modal handling when focus-trapped. |
| packages/geoview-core/src/core/components/geolocator/geolocator-style.ts | Reuses shared visuallyHidden style instead of duplicating sr-only CSS. |
| packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx | Uses aria-disabled + early return for “clear filters” to preserve focus behavior. |
| packages/geoview-core/src/core/components/export/export-modal.tsx | Makes dialog/menu/input IDs map-scoped and more consistent. |
| packages/geoview-core/src/core/components/export/export-modal-button.tsx | Simplifies export button ARIA props and indicates dialog semantics via aria-haspopup. |
| packages/geoview-core/src/core/components/details/feature-info-table.tsx | Updates lightbox init signature to support alt text and focus restoration ID. |
| packages/geoview-core/src/core/components/details/coordinate-info.tsx | Updates Switch label usage to match required label typing. |
| packages/geoview-core/src/core/components/data-table/filter-map.tsx | Updates Switch label usage to match required label typing. |
| packages/geoview-core/src/core/components/data-table/data-table.tsx | Updates lightbox init signature to include alt text + focus restoration ID. |
| packages/geoview-core/src/core/components/common/layer-list.tsx | Removes redundant aria-disabled where native disabled is already set. |
| packages/geoview-core/src/core/components/common/hooks/use-light-box.tsx | Updates lightbox API to accept alt text and return-focus element ID; restores focus on exit. |
| packages/geoview-core/src/core/components/app-bar/buttons/version.tsx | Updates IDs to match conventions and reuses shared visuallyHidden. |
| packages/geoview-core/src/core/components/app-bar/app-bar.tsx | Adjusts ARIA attributes based on focus-trap mode and avoids ESC conflicts when lightbox is open. |
| packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts | Import cleanup/reordering. |
| packages/geoview-core/public/locales/fr/translation.json | Adds new accessibility strings (panel label, legend fullscreen title, loading announcements, etc.). |
| packages/geoview-core/public/locales/en/translation.json | Adds new accessibility strings (panel label, legend fullscreen title, loading announcements, etc.). |
| docs/app/accessibility.md | Expands accessibility best-practices documentation (unique IDs, ARIA labels, focus management, live regions). |
You can also share your feedback on Copilot code review. Take the survey.
| const parentHidden = useMapSelectorLayerParentHidden(layerPath); | ||
| const highlightedLayer = useLayerHighlightedLayer(); | ||
| const isFocusTrap = useUIActiveTrapGeoView(); | ||
| st layerName = useLayerSelectorName(layerPath) ?? layerPath; |
|
|
||
| _Work in progress_ | ||
|
|
||
| The viewer needs to be accessible for keyboard and screen reader. It's should follow WCAG 2.1 requirements: https://www.w3.org/TR/WCAG21 |
* documentation: update accessibility best practices md file (work in progress) * geolocator: add aria-disabled to clear filters button when no filters selected * geolocator: updated loading status region for A11Y (aria-live) (ProgressBar) * global: updates to use unique ids * global styles: add css rule for aria-disabled icon buttons (buttonOutline) * global styles: add reusable style utility for visuallyHidden * legend layer: remove truncation from legend title * legend layer: remove tooltips from non interactive elements (legend title) * legend layer: add accessible code for loading layer * legend panel: fix fullscreen icon button focus management * legend panel: fix to allow both zoom icon buttons to retain focus after being pressed * legend panel: improve aria and semantic HTML implementation * legend panel: fix images that open a lightbox to use an interactive element (button) * legend panel: fix focus management on lightbox close (returns focus to triggering item) * legend panel fullscreen: update panel title to be more descriptive (read only) * panel and other modals: improve aria and semantic HTML implementation * panel and appBar and related: updated to use consistent ID naming conventions (mapId-containerType...) * switch: make label required for accessibility * switch: make focus indicators more noticeable * switch: add unique id to associate the label to the switch * use-light-box: update to use separate props for image alt test and focus management element ID * use-light-box (global): update to set alt text to "" where descriptive alt text is unavailable
f0f73e6 to
426b432
Compare
|
Replaced with PR #3353 |
Description
A11Y fixes related to WCAG review of the legend panel.
Fixes #3327
Type of change
How Has This Been Tested?
Tested manually using keyboard navigation, Chrome dev tools, W3C HTML validator.
Add the URL for your deploy!
Checklist:
I have made corresponding changes to the documentationI have added tests that prove my fix is effective or that my feature worksNew and existing unit tests pass locally with my changesThis change is