Skip to content

Commit

Permalink
Fix/button accessibility error (#204)
Browse files Browse the repository at this point in the history
* fix(redmine 1306603): fixed button accessibility errors

* fix(redmine 1306603): fixed button accessibility errors

* fix(redmine 1306603): fixed test

* fix(redmine 1306603): fix pr review from Tony

* fix(redmine 1306603): fixed lint error

---------

Co-authored-by: Quentin Le Caignec <[email protected]>
  • Loading branch information
subraAntoine and MorganeLeCaignec authored Sep 5, 2024
1 parent d28d51d commit 2aead99
Show file tree
Hide file tree
Showing 21 changed files with 155 additions and 3 deletions.
6 changes: 6 additions & 0 deletions .changeset/early-games-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@smile/haring-react-table': minor
'@smile/haring-react': minor
---

fixed button accessibility erros
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,13 @@ export function CalendarHeader(props: ICalendarHeaderProps): ReactElement {
<Popover onChange={setOpened} opened={opened} radius={10} width="target">
<Popover.Target>
<MantineCalendarHeader
aria-label="calendar"
className={classes.header}
color="red"
label={label}
nextLabel="Next button"
onLevelClick={() => setOpened(!opened)}
previousLabel="Previous button"
role="region"
size="md"
{...calendarHeaderProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ exports[`CalendarHeader matches snapshot 1`] = `
aria-controls="mantine-f4bipx4bi-dropdown"
aria-expanded="false"
aria-haspopup="dialog"
aria-label="calendar"
class="m_730a79ed mantine-CalendarHeader-calendarHeader"
color="red"
data-size="md"
Expand All @@ -27,6 +28,7 @@ exports[`CalendarHeader matches snapshot 1`] = `
style="--dch-control-size: var(--dch-control-size-md); --dch-fz: var(--mantine-font-size-md);"
>
<button
aria-label="Previous button"
class="haring-focus m_2351eeb0 mantine-CalendarHeader-calendarHeaderControl m_87cf2631 mantine-UnstyledButton-root"
data-direction="previous"
tabindex="0"
Expand Down Expand Up @@ -56,6 +58,7 @@ exports[`CalendarHeader matches snapshot 1`] = `
August 2023
</button>
<button
aria-label="Next button"
class="haring-focus m_2351eeb0 mantine-CalendarHeader-calendarHeaderControl m_87cf2631 mantine-UnstyledButton-root"
data-direction="next"
tabindex="0"
Expand Down
1 change: 1 addition & 0 deletions packages/haring-react-shared/src/types/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface IActionConfirmModalProps<Item>
}

export interface IAction<Item> {
ariaLabel?: string | ((item: Item) => string);
color?: string;
componentProps?:
| Record<string, unknown>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { action } from '@storybook/addon-actions';
export const tableMock: ITableProps<Record<string, unknown>> = {
actions: [
{
ariaLabel: 'move',
icon: <FolderMove />,
id: 'move',
isMassAction: true,
Expand Down Expand Up @@ -69,6 +70,9 @@ export const tableMock: ITableProps<Record<string, unknown>> = {
onAction: action('Delete'),
},
],
ariaLabels: {
otherActions: 'Other actions',
},
columns: [
{
accessorKey: 'id',
Expand Down
15 changes: 14 additions & 1 deletion packages/haring-react-table/src/Components/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ import {
getActionComponentProps,
getActionIcon,
getActionLabel,
getAriaLabel,
} from '../../helpers';

import classes from './Table.module.css';

export interface ITableAriaLabels {
otherActions: string;
}

export interface ITableProps<Data extends Record<string, unknown>>
extends MRT_TableOptions<Data> {
actions?: ITableAction<Data>[];
ariaLabels?: ITableAriaLabels;
maxVisibleActions?: number;
menuLabel?: string;
paginationProps?: Partial<IPaginationProps>;
Expand All @@ -63,6 +69,7 @@ export function Table<Data extends Record<string, unknown>>(
): ReactElement {
const {
actions = [],
ariaLabels,
icons,
initialState,
menuLabel = 'Other actions',
Expand Down Expand Up @@ -191,10 +198,15 @@ export function Table<Data extends Record<string, unknown>>(
{visibleRowActions.map((action, index) => (
<Tooltip
key={`${index + index}`}
label={getActionLabel(action, row)}
label={getAriaLabel(action, row, 'actionLabel')}
{...tooltipProps}
>
<ActionIcon
aria-label={
getAriaLabel(action, row, 'ariaLabel')
? getAriaLabel(action, row, 'ariaLabel')
: getAriaLabel(action, row, 'actionLabel')
}
className={classes.action}
onClick={() => handleAction(row, index)}
radius={4}
Expand All @@ -218,6 +230,7 @@ export function Table<Data extends Record<string, unknown>>(
>
<Menu.Target>
<ActionIcon
aria-label={ariaLabels?.otherActions || 'Other actions'}
className={`${classes.menuButton} ${classes.action}`}
radius={4}
type="button"
Expand Down

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions packages/haring-react-table/src/helpers/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { ITableAction, ITableConfirmAction } from '../types';
import type { MRT_Row } from 'mantine-react-table';
import type { ReactNode } from 'react';

export type ILabelProperty = 'actionLabel' | 'ariaLabel';

export function isConfirmAction<Data extends Record<string, unknown>>(
action: ITableAction<Data>,
): action is ITableConfirmAction<Data> {
Expand All @@ -26,6 +28,28 @@ export function getActionLabel<Data extends Record<string, unknown>>(
return action.label;
}

export function getAriaLabel<Data extends Record<string, unknown>>(
action?: ITableAction<Data> | null,
rows?: MRT_Row<Data> | MRT_Row<Data>[],
property?: ILabelProperty,
): string | undefined {
if (property === 'actionLabel') {
return getActionLabel(action, rows);
}
if (!action) {
return '';
}
if (isConfirmAction(action)) {
return typeof action.ariaLabel === 'function'
? action.ariaLabel(action.item)
: action.ariaLabel;
}
if (typeof action.ariaLabel === 'function') {
return rows ? action.ariaLabel(rows) : '';
}
return action.ariaLabel;
}

export function getActionIcon<Data extends Record<string, unknown>>(
action?: ITableAction<Data> | null,
rows?: MRT_Row<Data> | MRT_Row<Data>[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ exports[`CardList matches snapshot 1`] = `
style="gap: calc(1rem * var(--mantine-scale)); align-items: center;"
>
<button
aria-label="export button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand All @@ -85,6 +86,7 @@ exports[`CardList matches snapshot 1`] = `
</span>
</button>
<button
aria-label="eye button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand Down Expand Up @@ -145,6 +147,7 @@ exports[`CardList matches snapshot 1`] = `
style="gap: calc(1rem * var(--mantine-scale)); align-items: center;"
>
<button
aria-label="export button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand All @@ -167,6 +170,7 @@ exports[`CardList matches snapshot 1`] = `
</span>
</button>
<button
aria-label="eye button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand Down Expand Up @@ -227,6 +231,7 @@ exports[`CardList matches snapshot 1`] = `
style="gap: calc(1rem * var(--mantine-scale)); align-items: center;"
>
<button
aria-label="export button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand All @@ -249,6 +254,7 @@ exports[`CardList matches snapshot 1`] = `
</span>
</button>
<button
aria-label="eye button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand Down Expand Up @@ -309,6 +315,7 @@ exports[`CardList matches snapshot 1`] = `
style="gap: calc(1rem * var(--mantine-scale)); align-items: center;"
>
<button
aria-label="export button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand All @@ -331,6 +338,7 @@ exports[`CardList matches snapshot 1`] = `
</span>
</button>
<button
aria-label="eye button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-variant="subtle"
style="--ai-size: calc(1.875rem * var(--mantine-scale)); --ai-bg: transparent; --ai-hover: var(--mantine-color-cyan-light-hover); --ai-color: var(--mantine-color-cyan-light-color); --ai-bd: calc(0.0625rem * var(--mantine-scale)) solid transparent;"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ import { CaretDown, CaretRight } from '@phosphor-icons/react';

import classes from './CollapseButton.module.css';

export interface ICollapseButtonAriaLabels {
expandButton?: string;
}

export interface ICollapseButtonControlledProps<
T extends number | string,
C extends ElementType,
> extends ICollapseButtonProps<T, C> {
ariaLabels?: ICollapseButtonAriaLabels;
collapseProps?: Partial<CollapseProps>;
/** Only in the Controlled version, use this prop to provide the setter function for the opened/collapsed state */
onCollapseChange?: (isOpened: boolean) => void;
Expand All @@ -25,6 +30,7 @@ export function CollapseButtonControlled<
C extends ElementType = 'button',
>(props: ICollapseButtonControlledProps<T, C>): ReactElement {
const {
ariaLabels,
children,
collapseProps,
component: Component = 'button',
Expand Down Expand Up @@ -110,6 +116,7 @@ export function CollapseButtonControlled<
rightSection={
Boolean(children) && (
<ActionIcon
aria-label={ariaLabels?.expandButton || 'expand button'}
data-testid="toggle"
onClick={handleClick}
radius="sm"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ exports[`CollapseButton matches snapshot 1`] = `
data-position="right"
>
<button
aria-label="expand button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-testid="toggle"
data-variant="transparent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ exports[`HeaderMenu matches snapshot in mobile mode 1`] = `
data-position="right"
>
<button
aria-label="expand button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-testid="toggle"
data-variant="transparent"
Expand Down Expand Up @@ -268,6 +269,7 @@ exports[`HeaderMenu matches snapshot in mobile mode 1`] = `
data-position="right"
>
<button
aria-label="expand button"
class="haring-focus haring-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
data-testid="toggle"
data-variant="transparent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ export const InfoBox: IStory = {
),
contentItems: [
{
arialLabel: 'user button',
icon: <User />,
label: 'Individual contract',
onAction: action('onAction'),
},
{
arialLabel: 'suitcase button',
icon: <Suitcase />,
label: '2 Lines text for example',
onAction: action('onAction'),
Expand Down
11 changes: 11 additions & 0 deletions packages/haring-react/src/Components/InfoBox/InfoBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@ import { Motif } from './Motif';

export type IMantineBreakpoint = 'lg' | 'md' | 'sm' | 'xl' | 'xs';

export interface IInfoBoxAriaLabels {
expandButton?: string;
}

export interface IContentItem {
arialLabel?: string;
icon?: ReactElement;
iconProps?: Partial<ActionIconProps>;
label?: string;
onAction?: (item: IContentItem) => void;
}

export interface IInfoCardProps extends PaperProps {
ariaLabels?: IInfoBoxAriaLabels;
children?: ReactElement;
collapse?: boolean;
content?: ReactElement;
Expand All @@ -35,6 +41,7 @@ export interface IInfoCardProps extends PaperProps {
export function InfoBox(props: IInfoCardProps): ReactElement {
const theme = useMantineTheme();
const {
ariaLabels,
children,
collapse = true,
content,
Expand Down Expand Up @@ -66,6 +73,9 @@ export function InfoBox(props: IInfoCardProps): ReactElement {
>
{Boolean(item.icon) && (
<ActionIcon
aria-label={
item.arialLabel ? item.arialLabel : item.label
}
className={classes.contentItem}
color={theme.primaryColor}
onClick={() => item.onAction?.(item)}
Expand Down Expand Up @@ -93,6 +103,7 @@ export function InfoBox(props: IInfoCardProps): ReactElement {
</Collapse>
{Boolean(collapse) && (
<ActionIcon
aria-label={ariaLabels?.expandButton || 'expand button'}
className={`${classes.collapseButton} ${
!opened && classes.collapseButtonCenter
}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ exports[`InfoBox matches snapshot 1`] = `
<div />
</div>
<button
aria-label="expand button"
class="haring-focus haring-active undefined false m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root"
style="--ai-color: var(--mantine-color-white);"
type="button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ export interface ISidebarFilter {
value: unknown;
}

export interface ISidebarFiltersAriaLabels {
expandIcon?: string;
}

type IId = number | string;

export interface ISidebarFiltersProps {
activeFilters?: ISidebarFilter[] | [];
ariaLabels?: ISidebarFiltersAriaLabels;
closeAllFiltersLabel?: string;
defaultOpenedActiveFilters?: boolean;
defaultOpenedMenuIds?: IId[];
Expand All @@ -48,6 +53,7 @@ export interface ISidebarFiltersProps {
export function SidebarFilters(props: ISidebarFiltersProps): ReactElement {
const {
activeFilters = [],
ariaLabels,
closeAllFiltersLabel = 'Close all',
title = 'Active filters',
menus = [],
Expand Down Expand Up @@ -134,6 +140,7 @@ export function SidebarFilters(props: ISidebarFiltersProps): ReactElement {
opened={activeFiltersCollapseOpened}
rightSection={
<ActionIcon
aria-label={ariaLabels?.expandIcon || 'expand icon'}
data-testid="toggle"
onClick={() => {
setActiveFiltersCollapseOpened(!activeFiltersCollapseOpened);
Expand Down
Loading

0 comments on commit 2aead99

Please sign in to comment.