Skip to content

Commit

Permalink
Merge branch 'main' into react-crossword-v3
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverabrahams authored Jan 2, 2025
2 parents fa896e7 + f98e690 commit de727b3
Show file tree
Hide file tree
Showing 20 changed files with 936 additions and 289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ export const ENDPOINT = isGuardianDomain()
? 'https://sourcepoint.theguardian.com'
: 'https://cdn.privacy-mgmt.com';
export type EndPoint = typeof ENDPOINT;

// https://docs.sourcepoint.com/hc/en-us/articles/4405397484307-Event-callbacks-CMP#h_01FTY32EGZ7H3SS192MEE6SNCP
export const SourcePointChoiceTypes = {
AcceptAll: 11,
RejectAll: 13,
Dismiss: 15,
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ENDPOINT,
PROPERTY_ID,
PROPERTY_ID_AUSTRALIA,
SourcePointChoiceTypes,
} from './lib/sourcepointConfig';
import { invokeCallbacks } from './onConsentChange';
import { stub } from './stub';
Expand Down Expand Up @@ -134,11 +135,11 @@ export const init = (framework: ConsentFramework, pubData = {}): void => {

log('cmp', `onMessageChoiceSelect choice_id: ${choice_id}`);
log('cmp', `onMessageChoiceSelect choice_type_id: ${choiceTypeID}`);
// https://documentation.sourcepoint.com/web-implementation/web-implementation/multi-campaign-web-implementation/event-callbacks#choice-type-id-descriptions
if (
// https://documentation.sourcepoint.com/web-implementation/web-implementation/multi-campaign-web-implementation/event-callbacks#choice-type-id-descriptions
choiceTypeID === 11 ||
choiceTypeID === 13 ||
choiceTypeID === 15
Object.values(SourcePointChoiceTypes).some(
(spChoiceType) => spChoiceType === choiceTypeID,
)
) {
setTimeout(invokeCallbacks, 0);
}
Expand Down
4 changes: 2 additions & 2 deletions libs/@guardian/react-crossword/src/@types/crossword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ export type Theme = {
focusColor: string;
/** The colour of cells/clues that are currently selected clue */
selectedColor: string;
/** The colour of cells/clues that are related to the currently selected clue */
relatedColor: string;
/** The colour of cells/clues that are connected to the currently selected clue */
connectedColor: string;

/** The background colour of clue-helper buttons */
buttonBackgroundColor: string;
Expand Down
34 changes: 21 additions & 13 deletions libs/@guardian/react-crossword/src/components/AnagramHelper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCurrentClue } from '../context/CurrentClue';
import { useData } from '../context/Data';
import { useProgress } from '../context/Progress';
import { useShowAnagramHelper } from '../context/ShowAnagramHelper';
import { useTheme } from '../context/Theme';
import { useUIState } from '../context/UI';
import { biasedShuffle } from '../utils/biasedShuffle';
import { getCellsWithProgressForGroup } from '../utils/getCellsWithProgressForGroup';
import { Button } from './Button';
import { Clue } from './Clue';
import { CrosswordButton } from './CrosswordButton';
import { SolutionDisplay } from './SolutionDisplay';
import { WordWheel } from './WordWheel';

Expand All @@ -21,7 +21,7 @@ export const AnagramHelper = () => {
const [solving, setSolving] = useState(false);
const [shuffledLetters, setShuffledLetters] = useState<string[]>([]);
const theme = useTheme();
const { setShowAnagramHelper } = useUIState();
const { setShowAnagramHelper } = useShowAnagramHelper();
const { entries, cells } = useData();
const { currentEntryId } = useCurrentClue();
const { progress } = useProgress();
Expand Down Expand Up @@ -82,13 +82,13 @@ export const AnagramHelper = () => {
margin-bottom: ${space[4]}px;
`}
>
<Button
onSuccess={() => setShowAnagramHelper(false)}
<CrosswordButton
onClick={() => setShowAnagramHelper(false)}
size="small"
priority="tertiary"
>
<SvgCross size="xsmall" />
</Button>
</CrosswordButton>
</div>
<div
css={css`
Expand Down Expand Up @@ -118,17 +118,17 @@ export const AnagramHelper = () => {
maxLength={cellsWithProgress.length}
/>
</div>
<Button
<CrosswordButton
cssOverrides={css`
margin: ${space[1]}px 0 0 ${space[1]}px;
`}
onSuccess={start}
onClick={start}
disabled={letters.length < 1}
priority="primary"
size="default"
>
Start
</Button>
</CrosswordButton>
<span>
{letters.length}/{cellsWithProgress.length}
</span>
Expand All @@ -145,12 +145,20 @@ export const AnagramHelper = () => {
}
`}
>
<Button onSuccess={reset} size={'default'} priority="secondary">
<CrosswordButton
onClick={reset}
size={'default'}
priority="secondary"
>
Back
</Button>
<Button onSuccess={shuffle} size={'default'} priority="primary">
</CrosswordButton>
<CrosswordButton
onClick={shuffle}
size={'default'}
priority="primary"
>
Shuffle
</Button>
</CrosswordButton>
</div>
</>
)}
Expand Down
2 changes: 1 addition & 1 deletion libs/@guardian/react-crossword/src/components/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const CellComponent = ({
: isHighlighted
? isActive
? theme.selectedColor
: theme.relatedColor
: theme.connectedColor
: theme.gridForegroundColor;

return (
Expand Down
12 changes: 6 additions & 6 deletions libs/@guardian/react-crossword/src/components/Clue.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,28 @@ export const Complete: Story = {

export const Highlighted: Story = {
args: {
isHighlighted: true,
isConnected: true,
},
};

export const HighlightedAndComplete: Story = {
args: {
isHighlighted: true,
isConnected: true,
isComplete: true,
},
};

export const Active: Story = {
args: {
isHighlighted: true,
isActive: true,
isConnected: true,
isSelected: true,
},
};

export const ActiveAndComplete: Story = {
args: {
isHighlighted: true,
isActive: true,
isConnected: true,
isSelected: true,
isComplete: true,
},
};
86 changes: 66 additions & 20 deletions libs/@guardian/react-crossword/src/components/Clue.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,92 @@
import { css } from '@emotion/react';
import { isString } from '@guardian/libs';
import { visuallyHidden } from '@guardian/source/foundations';
import { SvgTickRound } from '@guardian/source/react-components';
import type { HTMLAttributes } from 'react';
import { memo } from 'react';
import type { CAPIEntry } from '../@types/CAPI';
import { useData } from '../context/Data';
import { useTheme } from '../context/Theme';
import { useValidAnswers } from '../context/ValidAnswers';

interface Props {
const punctuateString = (string: string) => {
const trimmed = string.trim();
return /[!?.]$/.test(trimmed) ? trimmed : `${trimmed}.`;
};

const formatClueForScreenReader = (clueString: string) => {
const [, clue, lengths] = /(.+)\((.+?)\)$/gm.exec(clueString) ?? [];

// If we can't find the clue or lengths, just return the original string
if (!isString(clue) || !isString(lengths)) {
return punctuateString(clueString);
}

const [last, ...rest] = lengths
.split(',')
.map((_) => _.trim() + ' letters')
.reverse();

const lengthsToSentence = [rest.reverse().join(', '), last?.trim()]
.filter(Boolean)
.join(' and ');

const clueWithPunctuation = punctuateString(clue);

return `${clueWithPunctuation} ${lengthsToSentence}.`;
};

const formatNumberForScreenReader = (
humanNumber: string,
direction: string,
) => {
return (
humanNumber
.split(',')
.map((number) => `${number.trim()} ${direction}`)
.join(', ') + '.'
);
};

type Props = {
entry: CAPIEntry;
isHighlighted?: boolean;
isActive?: boolean;
isConnected?: boolean;
isSelected?: boolean;
isComplete?: boolean;
}
} & HTMLAttributes<HTMLDivElement>;

const ClueComponent = ({
entry,
isHighlighted,
isActive,
isConnected,
isSelected,
isComplete,
...props
}: Props) => {
const theme = useTheme();
const { getId } = useData();
const { validAnswers } = useValidAnswers();

return (
<div
tabIndex={-1}
id={
// this must match the format used for aria-activedescendant in ./Clues.tsx
getId(`${entry.id}`)
}
data-entry-id={entry.id}
role="option"
aria-selected={isHighlighted}
css={css`
background-color: ${isActive
background-color: ${isSelected
? theme.selectedColor
: isHighlighted
? theme.relatedColor
: isConnected
? theme.connectedColor
: 'transparent'};
cursor: ${isHighlighted ? 'default' : 'pointer'};
cursor: ${isConnected ? 'default' : 'pointer'};
opacity: ${isComplete ? 0.5 : 1};
padding: 0.5em 0;
color: currentColor;
.visuallyHidden {
${visuallyHidden}
}
`}
{...props}
>
<span
aria-hidden="true"
css={css`
font-weight: bold;
display: table-cell;
Expand All @@ -57,11 +97,17 @@ const ClueComponent = ({
{entry.humanNumber}
</span>
<span
aria-hidden="true"
css={css`
display: table-cell;
`}
dangerouslySetInnerHTML={{ __html: entry.clue }}
dangerouslySetInnerHTML={{
__html: entry.clue,
}}
></span>
<span css={css(visuallyHidden)}>
{`${formatNumberForScreenReader(entry.humanNumber, entry.direction)} ${formatClueForScreenReader(entry.clue)}`}
</span>
{validAnswers.has(entry.id) && (
<span
css={css`
Expand Down
Loading

0 comments on commit de727b3

Please sign in to comment.