Skip to content

Commit

Permalink
Improve a11y of autofill assessment table (#1019)
Browse files Browse the repository at this point in the history
* Update Project Enrollment tables

* fix a11y

* More accessibility fixes

* Simplify and resolve todos

* Add storybook story

* Remove errant comment

* Add comments

* Update api of TableRowActions

* Fix issue with fulColSpan

* Implement Table Actions pattern and default columns

* Add random comment

* Add storybook for table actions and fix cde story

* Right-align table row actions

Co-authored-by: Gig <[email protected]>

* Remove comment

* Replace overrideTableBody with more understandable API

* Implement PR suggestion of moving table row to column def

* Implement PR suggestion of moving table row to column def

* Cleanup and add comments

* add todo

* Port over relevant changes from other PR

* Refactor to use renderCellContents

* Revert out-of-scope change

* Add todo

* Remove circular dependency

* Clean up

* Centralize action column base attrs

* Finish refactor to use column def instead of getTableAction

* Fix bulk services loading

* Provide a visually hidden header when header isnt provided

* Keep existing behavior of individual/household assessment viewing

* PR feedback

* Fix typescript problems

* Fix typescript

* Add accessible text for RelativeDateDisplay

* implement one possible approach for client ID typescript

* Improve a11y of autofill assessment table

* Improve and clean up

* PR feedback

* Revert unneeded

---------

Co-authored-by: Gig Ashton <[email protected]>
  • Loading branch information
martha and gigxz authored Jan 15, 2025
1 parent f860bba commit 737d6ed
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 40 deletions.
43 changes: 24 additions & 19 deletions src/components/elements/table/GenericTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface Props<T> {
actionRow?: ReactNode;
tableProps?: TableProps;
vertical?: boolean;
verticalHiddenHeader?: string; // For vertically oriented tables, this is the hidden header of the first column
noHead?: boolean;
renderVerticalHeaderCell?: RenderFunction<T>;
rowSx?: (row: T) => SxProps<Theme>;
Expand All @@ -80,24 +81,16 @@ const clickableRowStyles = {
cursor: 'pointer',
};

const HeaderCell = ({
children,
sx,
padding,
}: {
children: ReactNode;
padding?: TableCellProps['padding'];
sx?: SxProps<Theme>;
}) => (
const HeaderCell = ({ children, sx, ...rest }: TableCellProps) => (
<TableCell
padding={padding}
sx={{
borderBottomColor: 'borders.dark',
borderBottomWidth: 2,
borderBottomStyle: 'solid',
pb: 1,
...sx,
}}
{...rest}
>
{children}
</TableCell>
Expand All @@ -115,6 +108,17 @@ export const renderCellContents = <T extends { id: string }>(
return null;
};

export const renderHeaderCellContents = <T extends { id: string }>(
def: ColumnDef<T>
) => {
return def.header ? (
<strong>{def.header}</strong>
) : (
// If header isn't provided, add a visually hidden header with the column key for accessibility
<Box sx={visuallyHidden}>{def.key}</Box>
);
};

const GenericTable = <T extends { id: string }>({
rows,
handleRowClick,
Expand All @@ -123,6 +127,7 @@ const GenericTable = <T extends { id: string }>({
paginated = false,
loading = false,
vertical = false,
verticalHiddenHeader,
renderVerticalHeaderCell,
tablePaginationProps,
tableContainerProps,
Expand Down Expand Up @@ -203,7 +208,12 @@ const GenericTable = <T extends { id: string }>({
<TableHead sx={{ '.MuiTableCell-head': { verticalAlign: 'bottom' } }}>
{renderVerticalHeaderCell && (
<TableRow>
<TableCell key='empty' sx={{ backgroundColor: 'background.paper' }} />
<TableCell
key='empty'
sx={{ ...verticalCellSx(0), backgroundColor: 'background.paper' }}
>
<Box sx={visuallyHidden}>{verticalHiddenHeader}</Box>
</TableCell>
{rows.map((row, idx) => (
<TableCell key={row.id} sx={verticalCellSx(idx)}>
{renderVerticalHeaderCell(row)}
Expand Down Expand Up @@ -243,12 +253,7 @@ const GenericTable = <T extends { id: string }>({
width: def.width,
}}
>
{def.header ? (
<strong>{def.header}</strong>
) : (
// If header isn't provided, add a visually hidden header with the column key for accessibility
<Box sx={visuallyHidden}>{def.key}</Box>
)}
{renderHeaderCellContents(def)}
</HeaderCell>
))}
</TableRow>
Expand Down Expand Up @@ -308,9 +313,9 @@ const GenericTable = <T extends { id: string }>({
<HeaderCell
sx={{ ...verticalCellSx(1), width: '350px' }}
key={key(def)}
role='rowheader'
>
{' '}
<strong>{def.header}</strong>
{renderHeaderCellContents(def)}
</HeaderCell>
{rows.map((row, idx) => (
<TableCell key={row.id} sx={{ ...verticalCellSx(idx) }}>
Expand Down
50 changes: 33 additions & 17 deletions src/modules/assessments/components/RecordPickerDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Stack } from '@mui/material';
import { Box } from '@mui/material';
import Button from '@mui/material/Button';
import { DialogProps } from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import { visuallyHidden } from '@mui/utils';
import { startCase } from 'lodash-es';
import { ReactNode, useMemo } from 'react';

Expand All @@ -16,7 +17,10 @@ import {
getFieldOnAssessment,
getPopulatableChildren,
} from '@/modules/form/util/formUtil';
import { assessmentColumns } from '@/modules/form/util/recordPickerUtil';
import {
ASSESSMENT_COLUMNS,
assessmentColumns,
} from '@/modules/form/util/recordPickerUtil';
import HmisField from '@/modules/hmis/components/HmisField';
import { AssessmentRole, FormItem, FormRole } from '@/types/gqlTypes';

Expand All @@ -40,10 +44,28 @@ const RecordPickerDialog = ({
clientId,
...other
}: Props) => {
const actionColumnDef: ColumnDef<AssessmentForPopulation> = useMemo(() => {
return {
key: 'Action',
render: (record: AssessmentForPopulation) => (
<Button
onClick={() => onSelected(record)}
variant='outlined'
size='small'
sx={{ backgroundColor: 'white' }}
fullWidth
aria-label={`Select record from ${record.assessmentDate}`}
>
Select
</Button>
),
};
}, [onSelected]);

const columns: ColumnDef<AssessmentForPopulation>[] = useMemo(() => {
// If no item was passed, that means we're pre-filling the entire assessment.
// Only metadata columns are shown in that case.
if (!item) return assessmentColumns;
if (!item) return [actionColumnDef, ...assessmentColumns];

// Select additional fields to show in table based on child items in the group
const dataColumns = getPopulatableChildren(item)
Expand All @@ -69,8 +91,8 @@ const RecordPickerDialog = ({
);
},
}));
return [...assessmentColumns, ...dataColumns];
}, [item]);
return [actionColumnDef, ...assessmentColumns, ...dataColumns];
}, [actionColumnDef, item]);

const hudRoles = [
AssessmentRole.Intake,
Expand Down Expand Up @@ -123,7 +145,11 @@ const RecordPickerDialog = ({
}}
renderVerticalHeaderCell={(record) => {
return (
<Stack spacing={2} sx={{ py: 1 }}>
<>
<Box sx={visuallyHidden}>
{/* Additional visually hidden info about this assessment to help with accessibility when navigating the table */}
{ASSESSMENT_COLUMNS.CollectionStage.render(record)}
</Box>
<RelativeDate
dateString={record.assessmentDate}
dateUpdated={record.dateUpdated || undefined}
Expand All @@ -133,17 +159,7 @@ const RecordPickerDialog = ({
withTooltip
prefix='Assessment Date: '
/>
<Button
onClick={() => onSelected(record)}
variant='outlined'
size='small'
sx={{ backgroundColor: 'white' }}
fullWidth
aria-label={`Select record from ${record.assessmentDate}`}
>
Select
</Button>
</Stack>
</>
);
}}
/>
Expand Down
12 changes: 11 additions & 1 deletion src/modules/dataFetching/components/GenericTableWithData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ export interface Props<
SortOptionsType = Record<string, string>,
> extends Omit<
GenericTableProps<RowDataType>,
'rows' | 'tablePaginationProps' | 'loading' | 'paginated' | 'noData'
| 'rows'
| 'tablePaginationProps'
| 'loading'
| 'paginated'
| 'noData'
| 'verticalHiddenHeader'
> {
getColumnDefs?: (
rows: RowDataType[],
Expand Down Expand Up @@ -130,6 +135,7 @@ const GenericTableWithData = <
onCompleted,
paginationItemName,
filterRows,
vertical,
...props
}: Props<
Query,
Expand Down Expand Up @@ -333,6 +339,10 @@ const GenericTableWithData = <
}
columns={showColumnDefs}
noData={loading ? 'Loading...' : noDataValue}
vertical={vertical}
verticalHiddenHeader={
vertical ? `${recordType} attributes` : undefined
}
filterToolbar={
showTopToolbar && (
<>
Expand Down
11 changes: 8 additions & 3 deletions src/modules/form/util/recordPickerUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ const dataCollectionVerb = {
const COLLECTION_DATE = 'Date Collected';
const COLLECTION_STAGE = 'Collection Point';

export const assessmentColumns = [
{
export const ASSESSMENT_COLUMNS = {
CollectionDate: {
header: COLLECTION_DATE,
render: (record: AssessmentForPopulation) =>
[parseAndFormatDate(record.assessmentDate), record.user?.name]
.filter((s) => !!s)
.join(' by '),
},
{
CollectionStage: {
header: COLLECTION_STAGE,
render: (record: AssessmentForPopulation) =>
[
Expand All @@ -34,4 +34,9 @@ export const assessmentColumns = [
.filter((s) => !!s)
.join(' '),
},
};

export const assessmentColumns = [
ASSESSMENT_COLUMNS.CollectionDate,
ASSESSMENT_COLUMNS.CollectionStage,
];

0 comments on commit 737d6ed

Please sign in to comment.