Skip to content

Commit

Permalink
add label to the cell itself so the function only has to be called on…
Browse files Browse the repository at this point in the history
… the initial parsing of the data
  • Loading branch information
oliverabrahams committed Jan 23, 2025
1 parent 7f39063 commit 2993fd5
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 44 deletions.
3 changes: 3 additions & 0 deletions libs/@guardian/react-crossword/src/@types/crossword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export type Cell = Coords & {
/** Array of entries that this solution is part of */
group?: CrosswordEntry['group'];

/** The cell's description */
description?: string;

/** The cell's solution */
solution?: string;
};
Expand Down
45 changes: 1 addition & 44 deletions libs/@guardian/react-crossword/src/components/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import { isUndefined } from '@guardian/libs';
import { textSans12 } from '@guardian/source/foundations';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import type { FocusEvent, KeyboardEvent } from 'react';
import type { CAPIEntry } from '../@types/CAPI';
import type {
Cell as CellType,
Coords,
Entries,
Separator,
Theme,
} from '../@types/crossword';
Expand All @@ -19,52 +17,11 @@ import { useProgress } from '../context/Progress';
import { useTheme } from '../context/Theme';
import { useCheatMode } from '../hooks/useCheatMode';
import { useUpdateCell } from '../hooks/useUpdateCell';
import { formatClueForScreenReader } from '../utils/formatClueForScreenReader';
import { keyDownRegex } from '../utils/keydownRegex';
import { Cell } from './Cell';

const noop = () => {};

const getCellDescription = (cell: CellType, entries: Entries) => {
const cellEntryIds = cell.group ?? [];
const cellRelevantEntryId =
cell.group?.length === 1
? cell.group[0]
: cellEntryIds.find((id) => id.endsWith('across'));
if (isUndefined(cellRelevantEntryId)) {
return 'Blank cell.';
}
const additionalEntries = cellEntryIds
.filter((id) => !id.endsWith('across') && id !== cellRelevantEntryId)
.map((id) => entries.get(id))
.filter((entry) => !isUndefined(entry));
const relevantEntry = entries.get(cellRelevantEntryId);

return (
`` +
// ('Letter 2 of 4-across: Life is in a mess (5 letters).) | ('Blank cell.')
`${relevantEntry ? `${getReadableLabelForCellAndEntry({ entry: relevantEntry, cell: cell })}. ` : 'Blank. '}` +
// (Also, letter 1 of 5-down Life is always in a mess (2 letters).)
`${additionalEntries.map((entry) => getReadableLabelForCellAndEntry({ entry, cell: cell, additionalEntry: true })).join('. ')}`
);
};

const getReadableLabelForCellAndEntry = ({
entry,
cell,
additionalEntry = false,
}: {
entry: CAPIEntry;
cell: CellType;
additionalEntry?: boolean;
}): string => {
const cellPosition =
entry.direction === 'across'
? String(cell.x + 1 - entry.position.x)
: String(cell.y + 1 - entry.position.y);
return `${additionalEntry ? 'Also, letter' : 'Letter'} ${cellPosition} of ${entry.length}. ${entry.id}. ${formatClueForScreenReader(entry.clue)}`;
};

const getCellPosition = (
index: number,
{ gridCellSize, gridGutterSize }: Theme,
Expand Down Expand Up @@ -508,7 +465,7 @@ export const Grid = () => {
}
tabIndex={isCurrentCell ? 0 : -1}
aria-label="Crossword cell"
aria-description={getCellDescription(cell, entries)}
aria-description={cell.description ?? ''}
css={css`
position: absolute;
top: 0;
Expand Down
44 changes: 44 additions & 0 deletions libs/@guardian/react-crossword/src/utils/getCellDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { isUndefined } from '@guardian/libs';
import type { CAPIEntry } from '../@types/CAPI';
import type { Cell, Entries } from '../@types/crossword';
import { formatClueForScreenReader } from './formatClueForScreenReader';

export const getCellDescription = (cell: Cell, entries: Entries) => {
const cellEntryIds = cell.group ?? [];
const cellRelevantEntryId =
cell.group?.length === 1
? cell.group[0]
: cellEntryIds.find((id) => id.endsWith('across'));
if (isUndefined(cellRelevantEntryId)) {
return 'Blank cell.';
}
const additionalEntries = cellEntryIds
.filter((id) => !id.endsWith('across') && id !== cellRelevantEntryId)
.map((id) => entries.get(id))
.filter((entry) => !isUndefined(entry));
const relevantEntry = entries.get(cellRelevantEntryId);

return (
`` +
// ('Letter 2 of 4-across: Life is in a mess (5 letters).) | ('Blank cell.')
`${relevantEntry ? `${getReadableLabelForCellAndEntry({ entry: relevantEntry, cell: cell })}. ` : 'Blank. '}` +
// (Also, letter 1 of 5-down Life is always in a mess (2 letters).)
`${additionalEntries.map((entry) => getReadableLabelForCellAndEntry({ entry, cell: cell, additionalEntry: true })).join('. ')}`
);
};

const getReadableLabelForCellAndEntry = ({
entry,
cell,
additionalEntry = false,
}: {
entry: CAPIEntry;
cell: Cell;
additionalEntry?: boolean;
}): string => {
const cellPosition =
entry.direction === 'across'
? String(cell.x + 1 - entry.position.x)
: String(cell.y + 1 - entry.position.y);
return `${additionalEntry ? 'Also, letter' : 'Letter'} ${cellPosition} of ${entry.length}. ${entry.id}. ${formatClueForScreenReader(entry.clue)}`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
Entries,
Separators,
} from '../@types/crossword';
import { getCellDescription } from './getCellDescription';

/**
* Takes the crossword data from the CAPI and returns some things we can use.
Expand Down Expand Up @@ -98,6 +99,12 @@ export const parseCrosswordData = (data: {
}
}

// Map over cells and add descriptions.
// We need the entries map for this so have to do it after the loop
cells.forEach((cell) => {
cell.description = getCellDescription(cell, entries);
});

return {
cells,
entries,
Expand Down

0 comments on commit 2993fd5

Please sign in to comment.