Skip to content

Commit 05c14a2

Browse files
authored
Support scrolling clue into view when selected (#1980)
* Fix height of single column clue list and make scrollable * Scroll currently selected clue into view * Add missing closing bracket * Determine whether cluelist is scrollable * Allow automatic scrolling to selected clue
1 parent 1a66ca1 commit 05c14a2

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

libs/@guardian/react-crossword/src/components/Clue.tsx

+17-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isString } from '@guardian/libs';
33
import { visuallyHidden } from '@guardian/source/foundations';
44
import { SvgTickRound } from '@guardian/source/react-components';
55
import type { HTMLAttributes } from 'react';
6-
import { memo } from 'react';
6+
import { memo, useEffect, useRef } from 'react';
77
import type { CAPIEntry } from '../@types/CAPI';
88
import { useTheme } from '../context/Theme';
99

@@ -52,6 +52,7 @@ type Props = {
5252
isSelected?: boolean;
5353
isComplete?: boolean;
5454
isValid?: boolean;
55+
scrollToSelected?: boolean;
5556
selectClue: (entry: CAPIEntry) => void;
5657
} & HTMLAttributes<HTMLDivElement>;
5758

@@ -61,10 +62,24 @@ const ClueComponent = ({
6162
isSelected,
6263
isComplete,
6364
isValid,
65+
scrollToSelected,
6466
selectClue,
6567
...props
6668
}: Props) => {
6769
const theme = useTheme();
70+
const clueRef = useRef<HTMLDivElement>(null);
71+
72+
useEffect(() => {
73+
const clue = clueRef.current;
74+
75+
if (!clue) {
76+
return;
77+
}
78+
79+
if (isSelected && scrollToSelected) {
80+
clue.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
81+
}
82+
}, [isSelected, scrollToSelected]);
6883

6984
return (
7085
<div
@@ -88,6 +103,7 @@ const ClueComponent = ({
88103
}
89104
`}
90105
onClick={() => selectClue(entry)}
106+
ref={clueRef}
91107
{...props}
92108
>
93109
<span

libs/@guardian/react-crossword/src/components/Clues.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Clue } from './Clue';
1212

1313
type Props = {
1414
direction: Direction;
15+
scrollToSelected?: boolean;
1516
/** Use this to provide a custom header component for the list of clues. If
1617
* undefined, the word 'across' or 'down' will be displayed, unstyled. */
1718
Header?: ComponentType<{
@@ -38,7 +39,7 @@ const Label = memo(({ direction }: { direction: Direction }) => {
3839
);
3940
});
4041

41-
export const Clues = ({ direction, Header }: Props) => {
42+
export const Clues = ({ direction, scrollToSelected, Header }: Props) => {
4243
const { entries, getId, cells } = useData();
4344
const { progress } = useProgress();
4445
const { currentEntryId, setCurrentEntryId } = useCurrentClue();
@@ -208,6 +209,7 @@ export const Clues = ({ direction, Header }: Props) => {
208209
isSelected={isSelected}
209210
isComplete={complete}
210211
isValid={isValid}
212+
scrollToSelected={scrollToSelected}
211213
key={entry.id}
212214
id={getId(entry.id)}
213215
tabIndex={-1}

0 commit comments

Comments
 (0)