Skip to content

Commit

Permalink
fix(pn-13257): tables accessibility (#1462)
Browse files Browse the repository at this point in the history
  • Loading branch information
leleOFA authored Feb 20, 2025
1 parent f335b19 commit 81a7208
Show file tree
Hide file tree
Showing 27 changed files with 263 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ const FormattedDateAndTime: React.FC<{ date: string; inTwoLines?: boolean }> = (
<Typography variant="body2">{formatDateTime(date)}</Typography>
);
}
return <Typography variant="body2">-</Typography>;
return (
<Typography variant="body2">
{getLocalizedOrDefaultLabel('appStatus', 'appStatus.missed-endDate')}
</Typography>
);
};

const DowntimeLogDataSwitch: React.FC<{
Expand Down
1 change: 0 additions & 1 deletion packages/pn-commons/src/components/CustomTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const CustomTooltip: React.FC<Props> = ({
onOpen,
tooltipProps,
}) => {
// tooltip state
const [open, setOpen] = useState(false);
const handleTooltipClose = () => {
if (openOnClick) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Box, SxProps, TableCell } from '@mui/material';
import { SxProps, TableCell } from '@mui/material';
import { ButtonNaked } from '@pagopa/mui-italia';

import { buttonNakedInheritStyle } from '../../../utility';

export type PnTableBodyCellProps = {
Expand All @@ -23,17 +22,14 @@ const PnTableBodyCell: React.FC<PnTableBodyCellProps> = ({
...cellProps!,
borderBottom: 'none',
}}
onClick={onClick}
>
{onClick && (
<>
{/* Even there is a onClick function on the TableCell, leave ButtonNaked below as is.
This makes spacebar key with accessibility to trigger the onClick function.
The ButtonNaked "inherits" the onClick action from the outer TableCell, so that is not necessary to replicate it. */}
<ButtonNaked sx={buttonNakedInheritStyle}>{children}</ButtonNaked>
</>
{onClick ? (
<ButtonNaked onClick={onClick} sx={buttonNakedInheritStyle}>
{children}
</ButtonNaked>
) : (
<>{children}</>
)}
{!onClick && <Box>{children}</Box>}
</TableCell>
);
export default PnTableBodyCell;
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
import { vi } from 'vitest';

import { disableConsoleLogging, fireEvent, render } from '../../../../test-utils';
import { disableConsoleLogging, fireEvent, render, within } from '../../../../test-utils';
import PnTableBodyCell from '../PnTableBodyCell';

describe('PnTableBodyCell', () => {
disableConsoleLogging('error');

const mockFn = vi.fn();

it('render component', () => {
const { container, queryByTestId } = render(
<PnTableBodyCell>mocke-cell-content</PnTableBodyCell>
<PnTableBodyCell>mocked-cell-content</PnTableBodyCell>
);
expect(container).toHaveTextContent(/mocke-cell-content/);
expect(container).toHaveTextContent(/mocked-cell-content/);
const buttons = queryByTestId('cell.button');
expect(buttons).not.toBeInTheDocument();
});

it('click cell event', () => {
const { getByTestId } = render(
<PnTableBodyCell testId="cell" onClick={() => mockFn()}>
mocke-cell-content
mocked-cell-content
</PnTableBodyCell>
);
const cell = getByTestId('cell');
fireEvent.click(cell);
expect(mockFn).toBeCalledTimes(1);
const button = within(cell).getByRole('button');
fireEvent.click(button);
expect(mockFn).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@ const RenderSmartData: React.FC<{
<SmartBodyCell
key={column.id.toString()}
columnId={column.id}
tableProps={{
onClick: column.tableConfiguration.onClick,
}}
tableProps={{ onClick: column.tableConfiguration.onClick }}
cardProps={column.cardConfiguration}
isCardHeader={column.cardConfiguration?.isCardHeader}
testId="rowCell"
Expand Down Expand Up @@ -160,13 +158,14 @@ describe('SmartData', () => {
const sortableColumn = smartCfg.find((cfg) => cfg.tableConfiguration.sortable);
const sortToggle = within(table).getByTestId(`headerCellDesktop.sort.${sortableColumn!.id}`);
fireEvent.click(sortToggle);
expect(handleSort).toBeCalledTimes(1);
expect(handleSort).toHaveBeenCalledTimes(1);
const clickableColumnIdx = smartCfg.findIndex((cfg) => cfg.tableConfiguration.onClick);
const rows = within(table).getAllByTestId('bodyRowDesktop');
// we can take the row we want
const cells = within(rows[0]).getAllByTestId('rowCellDesktop');
fireEvent.click(cells[clickableColumnIdx]);
expect(handleColumnClick).toBeCalledTimes(1);
const button = within(cells[clickableColumnIdx]).getByRole('button');
fireEvent.click(button);
expect(handleColumnClick).toHaveBeenCalledTimes(1);
});

it('no sort available (desktop version)', () => {
Expand All @@ -193,6 +192,6 @@ describe('SmartData', () => {
const action = cardActions[0];
expect(action).toHaveTextContent('Mocked action');
fireEvent.click(action);
expect(clickActionMockFn).toBeCalledTimes(1);
expect(clickActionMockFn).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ describe('PnTable Component', () => {
const sortButton = within(firstColumn).getByRole('button');
expect(sortButton).toBeInTheDocument();
fireEvent.click(sortButton);
expect(handleSort).toBeCalledTimes(1);
expect(handleSort).toBeCalledWith({ order: 'desc', orderBy: 'column-1' });
expect(handleSort).toHaveBeenCalledTimes(1);
expect(handleSort).toHaveBeenCalledWith({ order: 'desc', orderBy: 'column-1' });
});

it('click on a column', () => {
Expand All @@ -121,8 +121,8 @@ describe('PnTable Component', () => {
const firstRow = within(tableBody).getAllByTestId('table-test.body.row')[0];
const tableColumns = within(firstRow).getAllByTestId('table-test.body.row.cell');
fireEvent.click(tableColumns[2].querySelectorAll('button')[0]);
expect(handleColumnClick).toBeCalledTimes(1);
expect(handleColumnClick).toBeCalledWith(rows[0], columns[2].id);
expect(handleColumnClick).toHaveBeenCalledTimes(1);
expect(handleColumnClick).toHaveBeenCalledWith(rows[0], columns[2].id);
});

it('render component - multiple PnTableBody', () => {
Expand Down Expand Up @@ -153,7 +153,7 @@ describe('PnTable Component', () => {
</PnTableBody>
</PnTable>
)
).toThrowError('PnTable can have only 1 child of type PnTableHeader');
).toThrow('PnTable can have only 1 child of type PnTableHeader');
});

it('render component - multiple PnTableHeader', () => {
Expand Down Expand Up @@ -188,7 +188,7 @@ describe('PnTable Component', () => {
</PnTableHeader>
</PnTable>
)
).toThrowError('PnTable can have only 1 child of type PnTableHeader');
).toThrow('PnTable can have only 1 child of type PnTableHeader');
});

it('render component - incorrect child', () => {
Expand All @@ -211,7 +211,7 @@ describe('PnTable Component', () => {
<Box>Incorrect child</Box>
</PnTable>
)
).toThrowError(
).toThrow(
'PnTable can have only 1 child of type PnTableHeader and 1 child of type PnTableBody'
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,7 @@ const RenderSmartable: React.FC<{
<SmartBodyCell
key={column.id.toString()}
columnId={column.id}
tableProps={{
onClick: column.tableConfiguration.onClick,
}}
tableProps={{ onClick: column.tableConfiguration.onClick }}
cardProps={column.cardConfiguration}
isCardHeader={column.cardConfiguration?.isCardHeader}
>
Expand Down Expand Up @@ -153,10 +151,10 @@ describe('Smart Table Component', () => {
const itemsPerPageSelectorBtn = itemsPerPageSelector.querySelector('button');
fireEvent.click(itemsPerPageSelectorBtn!);
const itemsPerPageList = getAllByRole('menuitem');
fireEvent.click(itemsPerPageList[1]!);
fireEvent.click(itemsPerPageList[1]);
await waitFor(() => {
expect(handleChangePagination).toBeCalledTimes(1);
expect(handleChangePagination).toBeCalledWith({
expect(handleChangePagination).toHaveBeenCalledTimes(1);
expect(handleChangePagination).toHaveBeenCalledWith({
size: 20,
page: 0,
totalElements: 100,
Expand All @@ -168,8 +166,8 @@ describe('Smart Table Component', () => {
// the buttons are < 1 2 3 >
fireEvent.click(pageButtons[2]);
await waitFor(() => {
expect(handleChangePagination).toBeCalledTimes(2);
expect(handleChangePagination).toBeCalledWith({
expect(handleChangePagination).toHaveBeenCalledTimes(2);
expect(handleChangePagination).toHaveBeenCalledWith({
size: 20,
page: 1,
totalElements: 100,
Expand Down Expand Up @@ -208,9 +206,7 @@ describe('Smart Table Component', () => {
<SmartBodyCell
key={column.id.toString()}
columnId={column.id}
tableProps={{
onClick: column.tableConfiguration.onClick,
}}
tableProps={{ onClick: column.tableConfiguration.onClick }}
cardProps={column.cardConfiguration}
isCardHeader={column.cardConfiguration?.isCardHeader}
>
Expand All @@ -227,9 +223,7 @@ describe('Smart Table Component', () => {
<SmartBodyCell
key={column.id.toString()}
columnId={column.id}
tableProps={{
onClick: column.tableConfiguration.onClick,
}}
tableProps={{ onClick: column.tableConfiguration.onClick }}
cardProps={column.cardConfiguration}
isCardHeader={column.cardConfiguration?.isCardHeader}
>
Expand All @@ -241,7 +235,7 @@ describe('Smart Table Component', () => {
</SmartBody>
</SmartTable>
)
).toThrowError('SmartTable can have only 1 child of type SmartBody');
).toThrow('SmartTable can have only 1 child of type SmartBody');
});

it('render component - multiple SmartHeader', () => {
Expand Down Expand Up @@ -272,7 +266,7 @@ describe('Smart Table Component', () => {
</SmartHeader>
</SmartTable>
)
).toThrowError('SmartTable can have only 1 child of type SmartHeader');
).toThrow('SmartTable can have only 1 child of type SmartHeader');
});

it('render component - multiple SmartFilter', () => {
Expand Down Expand Up @@ -314,7 +308,7 @@ describe('Smart Table Component', () => {
</SmartHeader>
</SmartTable>
)
).toThrowError('SmartTable can have only 1 child of type SmartFilter');
).toThrow('SmartTable can have only 1 child of type SmartFilter');
});

it('render component - incorrect child', () => {
Expand All @@ -335,7 +329,7 @@ describe('Smart Table Component', () => {
<Box>Incorrect child</Box>
</SmartTable>
)
).toThrowError(
).toThrow(
'SmartTable can have only 1 child of type SmartFilter, 1 child of type SmartHeader and 1 child of type SmartBody'
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
import { Typography } from '@mui/material';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import { Box, Typography } from '@mui/material';
import { ButtonNaked } from '@pagopa/mui-italia';

import { useIsMobile } from '../../hooks';
import { Notification, NotificationColumnData, Row } from '../../models';
import { formatDate, getNotificationStatusInfos } from '../../utility';
import { getLocalizedOrDefaultLabel } from '../../utility/localization.utility';
import NewNotificationBadge, { isNewNotification } from './NewNotificationBadge';
import StatusTooltip from './StatusTooltip';

const NotificationStatusChip: React.FC<{
data: Row<Notification>;
}> = ({ data }) => {
const NotificationStatusChip: React.FC<{ data: Row<Notification> }> = ({ data }) => {
const { label, tooltip, color } = getNotificationStatusInfos(data.notificationStatus, {
recipients: data.recipients,
});
return <StatusTooltip label={label} tooltip={tooltip} color={color} />;
};

const ActionButton: React.FC<{
mandateId?: string;
iun: string;
handleRowClick?: (iun: string, mandateId?: string) => void;
}> = ({ mandateId, iun, handleRowClick }) => (
<ButtonNaked
data-testid="goToNotificationDetail"
onClick={() => handleRowClick && handleRowClick(iun, mandateId)}
aria-label={getLocalizedOrDefaultLabel('notifications', 'table.aria-action-table', undefined, {
iun,
})}
>
<ChevronRightIcon color="primary" />
</ButtonNaked>
);

const NotificationsDataSwitch: React.FC<{
data: Row<Notification>;
type: keyof NotificationColumnData;
}> = ({ data, type }) => {
handleRowClick?: (iun: string, mandateId?: string) => void;
}> = ({ data, type, handleRowClick }) => {
const isMobile = useIsMobile();

if (type === 'badge') {
Expand Down Expand Up @@ -46,7 +64,18 @@ const NotificationsDataSwitch: React.FC<{
return <>{data.sender}</>;
}
if (type === 'subject') {
return <>{data.subject.length > 65 ? data.subject.substring(0, 65) + '...' : data.subject}</>;
return (
<Box
sx={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
color: 'inherit',
}}
>
{data.subject}
</Box>
);
}
if (type === 'iun') {
return <>{data.iun}</>;
Expand All @@ -65,6 +94,11 @@ const NotificationsDataSwitch: React.FC<{
</>
);
}
if (type === 'action') {
return (
<ActionButton iun={data.iun} mandateId={data?.mandateId} handleRowClick={handleRowClick} />
);
}

return <></>;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const StatusTooltip = ({
cursor: 'default',
}}
data-testid={`statusChip-${label}`}
aria-label={isMobile ? `${label}: ${tooltip}` : undefined}
aria-label={`${label}: ${tooltip}`}
/>
</CustomTooltip>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { vi } from 'vitest';

import { NotificationStatus } from '../../../models';
import { createMatchMedia, render } from '../../../test-utils';
import { createMatchMedia, fireEvent, render } from '../../../test-utils';
import { formatDate, getNotificationStatusInfos } from '../../../utility';
import NotificationsDataSwitch from '../NotificationsDataSwitch';

Expand Down Expand Up @@ -100,4 +100,15 @@ describe('NotificationsDataSwitch Component', () => {
const regexp = new RegExp(`^${data.recipients.join('')}$`, 'ig');
expect(container).toHaveTextContent(regexp);
});

it('renders component - action', () => {
const clickFn = vi.fn();
const { getByTestId } = render(
<NotificationsDataSwitch data={data} type="action" handleRowClick={clickFn} />
);
const button = getByTestId('goToNotificationDetail');
expect(button).toBeInTheDocument();
fireEvent.click(button);
expect(clickFn).toHaveBeenCalledTimes(1);
});
});
3 changes: 2 additions & 1 deletion packages/pn-commons/src/models/Notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ export interface GetNotificationsParams<TDate extends string | Date> {
group?: string;
}

export type NotificationColumnData = Notification & { badge: string };
export type NotificationColumnData = Notification & { badge?: string; action:string } ;

Loading

0 comments on commit 81a7208

Please sign in to comment.