Skip to content

Commit 9fb0bb6

Browse files
committed
Add support for variations on quiz results page
1 parent 610a435 commit 9fb0bb6

File tree

13 files changed

+185
-16
lines changed

13 files changed

+185
-16
lines changed

spec/__tests__/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const defaultContextMocks = {
1313
getAddToFavoritesButtonProps: jest.fn(),
1414
getQuizResultButtonProps: jest.fn(),
1515
getQuizResultLinkProps: jest.fn(),
16+
getQuizResultSwatchProps: jest.fn(),
1617
customClickItemCallback: true,
1718
customAddToFavoritesCallback: true,
1819
primaryColorStyles: {

src/components/CioQuiz/context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
PrimaryColorStyles,
1818
QuizReturnState,
1919
GetShareResultsButtonProps,
20+
GetQuizResultSwatchProps,
2021
} from '../../types';
2122

2223
export interface QuizContextValue {
@@ -36,6 +37,7 @@ export interface QuizContextValue {
3637
getAddToFavoritesButtonProps: GetAddToFavoritesButtonProps;
3738
getQuizResultButtonProps: GetQuizResultButtonProps;
3839
getQuizResultLinkProps: GetQuizResultLinkProps;
40+
getQuizResultSwatchProps: GetQuizResultSwatchProps;
3941
primaryColorStyles: PrimaryColorStyles;
4042
customClickItemCallback: boolean;
4143
customAddToFavoritesCallback: boolean;

src/components/CioQuiz/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default function CioQuiz(props: IQuizProps) {
3030
getQuizResultLinkProps,
3131
getResetQuizButtonProps,
3232
getSelectInputProps,
33+
getQuizResultSwatchProps,
3334
primaryColorStyles,
3435
getShareResultsButtonProps,
3536
} = useQuiz(props);
@@ -76,6 +77,7 @@ export default function CioQuiz(props: IQuizProps) {
7677
getResetQuizButtonProps,
7778
getShareResultsButtonProps,
7879
getSelectInputProps,
80+
getQuizResultSwatchProps,
7981
customClickItemCallback: !!callbacks?.onQuizResultClick,
8082
customAddToFavoritesCallback: !!callbacks?.onAddToFavoritesClick,
8183
primaryColorStyles,

src/components/ResultCard/ResultCard.tsx

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import ResultFavoritesButton from '../ResultFavoritesButton/ResultFavoritesButto
44
import QuizContext from '../CioQuiz/context';
55
import { QuizResultDataPartial } from '../../types';
66
import { getNestedValueUsingDotNotation, validateNumberOrString } from '../../utils';
7+
import ResultCardSwatches from '../ResultCardSwatches/ResultCardSwatches';
8+
import useResult from './useResult';
79

810
interface ResultCardOptions {
911
result: QuizResultDataPartial;
1012
salePriceKey?: string;
1113
regularPriceKey?: string;
1214
ratingCountKey?: string;
1315
ratingScoreKey?: string;
16+
swatchImageKey?: string;
1417
resultPosition: number;
1518
renderResultCardPriceDetails?: (result: QuizResultDataPartial) => JSX.Element;
1619
getResultCardImageUrl?: (result: QuizResultDataPartial) => string;
@@ -24,6 +27,7 @@ export default function ResultCard(props: ResultCardOptions) {
2427
resultPosition,
2528
ratingCountKey,
2629
ratingScoreKey,
30+
swatchImageKey,
2731
renderResultCardPriceDetails,
2832
getResultCardImageUrl,
2933
} = props;
@@ -33,30 +37,40 @@ export default function ResultCard(props: ResultCardOptions) {
3337
getQuizResultButtonProps,
3438
getQuizResultLinkProps,
3539
} = useContext(QuizContext);
40+
const { faceOutResult, onVariationClick } = useResult(result);
3641

3742
const salePrice = validateNumberOrString(
38-
getNestedValueUsingDotNotation(result?.data, salePriceKey)
43+
getNestedValueUsingDotNotation(faceOutResult?.data, salePriceKey)
3944
);
4045
const regularPrice = validateNumberOrString(
41-
getNestedValueUsingDotNotation(result?.data, regularPriceKey)
46+
getNestedValueUsingDotNotation(faceOutResult?.data, regularPriceKey)
4247
);
4348
const ratingCount = validateNumberOrString(
44-
getNestedValueUsingDotNotation(result?.data, ratingCountKey)
49+
getNestedValueUsingDotNotation(faceOutResult?.data, ratingCountKey)
4550
);
4651
const ratingScore = validateNumberOrString(
47-
getNestedValueUsingDotNotation(result?.data, ratingScoreKey)
52+
getNestedValueUsingDotNotation(faceOutResult?.data, ratingScoreKey)
4853
);
4954

5055
const resultCardContent = () => (
5156
<>
5257
<div className='cio-result-card-image'>
5358
<img
54-
src={getResultCardImageUrl ? getResultCardImageUrl(result) : result.data?.image_url}
59+
src={
60+
getResultCardImageUrl
61+
? getResultCardImageUrl(faceOutResult)
62+
: faceOutResult.data?.image_url
63+
}
5564
alt='product'
5665
/>
5766
</div>
67+
<ResultCardSwatches
68+
swatchImageKey={swatchImageKey}
69+
faceOutResult={faceOutResult}
70+
onVariationClick={onVariationClick}
71+
/>
5872
<div className='cio-result-card-text'>
59-
<p className='cio-result-card-title'>{result.value}</p>
73+
<p className='cio-result-card-title'>{faceOutResult.value}</p>
6074
<div className='cio-result-card-details'>
6175
<div className='cio-result-card-rating'>
6276
{!!ratingScore && (
@@ -68,7 +82,7 @@ export default function ResultCard(props: ResultCardOptions) {
6882
{!!ratingCount && <span className='cio-result-card-rating-count'>({ratingCount})</span>}
6983
</div>
7084
{renderResultCardPriceDetails ? (
71-
renderResultCardPriceDetails(result)
85+
renderResultCardPriceDetails(faceOutResult)
7286
) : (
7387
<div className='cio-result-card-prices'>
7488
{!!salePrice && <span className='cio-result-card-sale-price'>${salePrice}</span>}
@@ -87,7 +101,12 @@ export default function ResultCard(props: ResultCardOptions) {
87101

88102
const resultCardContentWithoutLink = () =>
89103
getQuizResultButtonProps && (
90-
<div {...getQuizResultButtonProps({ result, position: resultPosition, type: 'button' })}>
104+
<div
105+
{...getQuizResultButtonProps({
106+
result: faceOutResult,
107+
position: resultPosition,
108+
type: 'button',
109+
})}>
91110
{resultCardContent()}
92111
</div>
93112
);
@@ -98,18 +117,22 @@ export default function ResultCard(props: ResultCardOptions) {
98117
className='cio-result-card-anchor'
99118
rel='noreferrer'
100119
target='_blank'
101-
{...getQuizResultLinkProps({ result, position: resultPosition, type: 'link' })}>
120+
{...getQuizResultLinkProps({
121+
result: faceOutResult,
122+
position: resultPosition,
123+
type: 'link',
124+
})}>
102125
{resultCardContent()}
103126
</a>
104127
);
105128

106129
return (
107130
<div className='cio-result-card'>
108131
{customAddToFavoritesCallback && (
109-
<ResultFavoritesButton item={result} price={salePrice || regularPrice} />
132+
<ResultFavoritesButton item={faceOutResult} price={salePrice || regularPrice} />
110133
)}
111134
{!customClickItemCallback ? resultCardContentWithLink() : resultCardContentWithoutLink()}
112-
<ResultCtaButton item={result} price={salePrice || regularPrice} />
135+
<ResultCtaButton item={faceOutResult} price={salePrice || regularPrice} />
113136
</div>
114137
);
115138
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useState } from 'react';
2+
import { QuizResultDataPartial } from '../../types';
3+
4+
const useResult = (result?: QuizResultDataPartial) => {
5+
const [faceOutResult, setFaceOutResult] = useState<QuizResultDataPartial>({
6+
...result,
7+
...result?.variations?.find(
8+
(variation) => variation.data.variation_id === result?.data?.variation_id
9+
),
10+
});
11+
12+
const onVariationClick = (variation: QuizResultDataPartial) => {
13+
setFaceOutResult((prev) => ({
14+
...prev,
15+
...variation,
16+
...variation?.variations?.find(
17+
(item) => item.data.variation_id === variation?.data?.variation_id
18+
),
19+
}));
20+
};
21+
return {
22+
faceOutResult,
23+
onVariationClick,
24+
};
25+
};
26+
export default useResult;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { useContext } from 'react';
2+
import { QuizResultDataPartial } from '../../types';
3+
import QuizContext from '../CioQuiz/context';
4+
5+
interface ResultCardSwatchesOptions {
6+
swatchImageKey?: string;
7+
faceOutResult: QuizResultDataPartial;
8+
onVariationClick: (variation: QuizResultDataPartial) => void;
9+
}
10+
11+
export default function ResultCardSwatches(props: ResultCardSwatchesOptions) {
12+
const { faceOutResult, onVariationClick } = props;
13+
const { getQuizResultSwatchProps } = useContext(QuizContext);
14+
15+
return (
16+
<div className='result-card-swatches-container'>
17+
{faceOutResult?.variations?.map((variation) => {
18+
const isSelected = faceOutResult?.data?.variation_id === variation.data.variation_id;
19+
20+
return (
21+
getQuizResultSwatchProps && (
22+
// eslint-disable-next-line react/button-has-type
23+
<button {...getQuizResultSwatchProps(variation, onVariationClick, faceOutResult)}>
24+
{isSelected && <div className='cio-swatch-selected' />}
25+
</button>
26+
)
27+
);
28+
})}
29+
</div>
30+
);
31+
}

src/components/ResultContainer/ResultContainer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default function ResultContainer(props: IResultContainerProps) {
2626
renderResultCardPriceDetails,
2727
renderResultCard,
2828
getResultCardImageUrl,
29+
swatchImageKey,
2930
} = resultCardOptions || {};
3031
const numberOfResults = state?.quiz.results?.response?.results?.length;
3132
const resultsConfig = state?.quiz.resultsConfig;
@@ -48,6 +49,7 @@ export default function ResultContainer(props: IResultContainerProps) {
4849
resultCardRegularPriceKey={resultCardRegularPriceKey}
4950
resultCardRatingCountKey={resultCardRatingCountKey}
5051
resultCardRatingScoreKey={resultCardRatingScoreKey}
52+
swatchImageKey={swatchImageKey}
5153
renderResultCardPriceDetails={renderResultCardPriceDetails}
5254
renderResultCard={renderResultCard}
5355
getResultCardImageUrl={getResultCardImageUrl}

src/components/Results/Results.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ function Results(props: ResultsProps) {
1111
resultCardRegularPriceKey,
1212
resultCardRatingCountKey,
1313
resultCardRatingScoreKey,
14+
swatchImageKey,
1415
renderResultCardPriceDetails,
1516
renderResultCard,
1617
getResultCardImageUrl,
@@ -32,6 +33,7 @@ function Results(props: ResultsProps) {
3233
regularPriceKey={resultCardRegularPriceKey}
3334
ratingCountKey={resultCardRatingCountKey}
3435
ratingScoreKey={resultCardRatingScoreKey}
36+
swatchImageKey={swatchImageKey}
3537
renderResultCardPriceDetails={renderResultCardPriceDetails}
3638
getResultCardImageUrl={getResultCardImageUrl}
3739
resultPosition={index + 1}

src/hooks/usePropsGetters/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
GetAddToFavoritesButtonProps,
1818
GetSkipQuestionButtonProps,
1919
GetShareResultsButtonProps,
20+
GetQuizResultSwatchProps,
2021
} from '../../types';
2122
import { QuizAPIReducerState } from '../../components/CioQuiz/quizApiReducer';
2223
import { QuizLocalReducerState } from '../../components/CioQuiz/quizLocalReducer';
@@ -149,6 +150,34 @@ const usePropsGetters = (
149150
[quizResultClickDown, resultClick]
150151
);
151152

153+
const getQuizResultSwatchProps: GetQuizResultSwatchProps = useCallback(
154+
(variation, onVariationClick, faceOutResult, swatchImageKey) => {
155+
const isSelected = variation?.data?.variation_id === faceOutResult?.data?.variation_id;
156+
const style = {
157+
background: `url(${
158+
swatchImageKey ? variation?.data?.[swatchImageKey] : variation?.data?.image_url
159+
})`,
160+
backgroundSize: 'fit-object',
161+
backgroundPosition: 'center',
162+
backgroundRepeat: 'no-repeat',
163+
// outline: isSelected ? '3px solid black' : undefined,
164+
};
165+
166+
const onClick = (e: React.MouseEvent<HTMLElement>) => {
167+
e.stopPropagation();
168+
onVariationClick(variation);
169+
};
170+
return {
171+
key: variation?.data?.variation_id,
172+
className: `cio-result-card-swatch ${isSelected ? 'selected' : ''}`,
173+
type: 'button',
174+
onClick,
175+
style,
176+
};
177+
},
178+
[]
179+
);
180+
152181
const getQuizImageProps: GetQuizImageProps = useCallback(
153182
() => ({
154183
src: quizApiState.quizCurrentQuestion?.next_question?.images?.primary_url || undefined,
@@ -182,6 +211,7 @@ const usePropsGetters = (
182211
getQuizResultButtonProps,
183212
getQuizResultLinkProps,
184213
getSkipQuestionButtonProps,
214+
getQuizResultSwatchProps,
185215
};
186216
};
187217

src/services/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export const trackQuizResultClick = (
9898
quizSessionId: quiz_session_id,
9999
itemId: result.data?.id,
100100
itemName: result?.value,
101-
variationId: result.data?.variation_id,
101+
variationId: result?.data?.variation_id,
102102
section,
103103
resultCount: total_num_results,
104104
resultPage: page,

0 commit comments

Comments
 (0)