@constructor-io/constructorio-ui-quizzes 1.8.3 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/constructorio-ui-quizzes-bundled.js +14 -14
  2. package/lib/cjs/package.json +1 -1
  3. package/lib/cjs/src/components/CioQuiz/actions.js +1 -0
  4. package/lib/cjs/src/components/CioQuiz/index.js +2 -1
  5. package/lib/cjs/src/components/CioQuiz/quizLocalReducer.js +21 -0
  6. package/lib/cjs/src/components/ControlBar/ControlBar.js +5 -2
  7. package/lib/cjs/src/components/ResultFavoritesButton/ResultFavoritesButton.js +2 -2
  8. package/lib/cjs/src/components/SkipButton/SkipButton.js +13 -0
  9. package/lib/cjs/src/constants.js +10 -1
  10. package/lib/cjs/src/hooks/usePropsGetters/index.js +10 -7
  11. package/lib/cjs/src/hooks/usePropsGetters/useAddToFavoritesButtonProps.js +10 -18
  12. package/lib/cjs/src/hooks/usePropsGetters/useSkipQuestionButtonProps.js +21 -0
  13. package/lib/cjs/src/hooks/useQuiz.js +2 -2
  14. package/lib/cjs/src/hooks/useQuizEvents/index.js +3 -0
  15. package/lib/cjs/src/hooks/useQuizEvents/useQuizSkipClick.js +31 -0
  16. package/lib/cjs/src/stories/Quiz/tests/mocks.js +1 -0
  17. package/lib/cjs/src/utils.js +10 -1
  18. package/lib/mjs/package.json +1 -1
  19. package/lib/mjs/src/components/CioQuiz/actions.js +1 -0
  20. package/lib/mjs/src/components/CioQuiz/index.js +2 -1
  21. package/lib/mjs/src/components/CioQuiz/quizLocalReducer.js +25 -0
  22. package/lib/mjs/src/components/ControlBar/ControlBar.js +5 -2
  23. package/lib/mjs/src/components/ResultFavoritesButton/ResultFavoritesButton.js +2 -2
  24. package/lib/mjs/src/components/SkipButton/SkipButton.js +10 -0
  25. package/lib/mjs/src/constants.js +9 -0
  26. package/lib/mjs/src/hooks/usePropsGetters/index.js +10 -7
  27. package/lib/mjs/src/hooks/usePropsGetters/useAddToFavoritesButtonProps.js +6 -10
  28. package/lib/mjs/src/hooks/usePropsGetters/useSkipQuestionButtonProps.js +17 -0
  29. package/lib/mjs/src/hooks/useQuiz.js +2 -2
  30. package/lib/mjs/src/hooks/useQuizEvents/index.js +3 -0
  31. package/lib/mjs/src/hooks/useQuizEvents/useQuizSkipClick.js +31 -0
  32. package/lib/mjs/src/stories/Quiz/tests/mocks.js +4 -0
  33. package/lib/mjs/src/utils.js +10 -1
  34. package/lib/styles.css +47 -3
  35. package/lib/types/src/components/CioQuiz/actions.d.ts +2 -1
  36. package/lib/types/src/components/CioQuiz/context.d.ts +2 -1
  37. package/lib/types/src/components/SkipButton/SkipButton.d.ts +6 -0
  38. package/lib/types/src/constants.d.ts +3 -2
  39. package/lib/types/src/hooks/usePropsGetters/index.d.ts +3 -2
  40. package/lib/types/src/hooks/usePropsGetters/useAddToFavoritesButtonProps.d.ts +1 -1
  41. package/lib/types/src/hooks/usePropsGetters/useSkipQuestionButtonProps.d.ts +3 -0
  42. package/lib/types/src/hooks/useQuizEvents/useQuizSkipClick.d.ts +6 -0
  43. package/lib/types/src/stories/Quiz/tests/mocks.d.ts +2 -2
  44. package/lib/types/src/types.d.ts +17 -4
  45. package/lib/types/src/utils.d.ts +1 -0
  46. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ import { useCallback } from 'react';
2
+ export default function useSkipQuestionButtonProps(skipQuestion, quizApiState) {
3
+ const getSkipQuestionButtonProps = useCallback(() => {
4
+ const currentQuestionId = quizApiState.quizCurrentQuestion?.next_question?.id;
5
+ const isHidden = currentQuestionId &&
6
+ (quizApiState.quizCurrentQuestion?.isCoverQuestion ||
7
+ !quizApiState.quizCurrentQuestion?.next_question.is_skippable);
8
+ return {
9
+ className: isHidden ? 'cio-question-skip-button hide' : 'cio-question-skip-button',
10
+ type: 'button',
11
+ onClick: () => {
12
+ skipQuestion();
13
+ },
14
+ };
15
+ }, [quizApiState.quizCurrentQuestion, skipQuestion]);
16
+ return getSkipQuestionButtonProps;
17
+ }
@@ -6,7 +6,7 @@ import usePrimaryColorStyles from './usePrimaryColorStyles';
6
6
  import useQuizEvents from './useQuizEvents';
7
7
  import useQuizState from './useQuizState';
8
8
  const useQuiz = (quizOptions) => {
9
- const { apiKey, cioJsClient, primaryColor } = quizOptions;
9
+ const { apiKey, cioJsClient, primaryColor, resultsPageOptions: { favoriteItems }, } = quizOptions;
10
10
  // Log console errors for required parameters quizId and resultsPageOptions
11
11
  useConsoleErrors(quizOptions);
12
12
  // Quiz Cio Client
@@ -18,7 +18,7 @@ const useQuiz = (quizOptions) => {
18
18
  // Props getters
19
19
  const { quizApiState, quizLocalState, quizSessionStorageState } = quizState;
20
20
  const { skipToResults } = quizSessionStorageState;
21
- const propGetters = usePropsGetters(quizEvents, quizApiState, quizLocalState);
21
+ const propGetters = usePropsGetters(quizEvents, quizApiState, quizLocalState, favoriteItems);
22
22
  const primaryColorStyles = usePrimaryColorStyles(primaryColor);
23
23
  useEffect(() => {
24
24
  if (skipToResults)
@@ -8,6 +8,7 @@ import useQuizResetClick from './useQuizResetClick';
8
8
  import useHydrateQuizLocalState from './useHydrateQuizLocalState';
9
9
  import { resetQuizSessionStorageState } from '../../utils';
10
10
  import useQuizAddToFavorites from './useQuizAddToFavorites';
11
+ import useQuizSkipClick from './useQuizSkipClick';
11
12
  const useQuizEvents = (quizOptions, cioClient, quizState) => {
12
13
  const { quizApiState, dispatchLocalState, dispatchApiState, quizLocalState, quizSessionStorageState, } = quizState;
13
14
  const { resultsPageOptions, callbacks } = quizOptions;
@@ -16,6 +17,7 @@ const useQuizEvents = (quizOptions, cioClient, quizState) => {
16
17
  const quizAnswerChanged = useQuizAnswerChangeHandler(quizApiState, dispatchLocalState);
17
18
  // Quiz Next button click callback
18
19
  const nextQuestion = useQuizNextClick(quizApiState, quizLocalState, dispatchLocalState, callbacks?.onQuizNextQuestion);
20
+ const skipQuestion = useQuizSkipClick(quizApiState, quizLocalState, dispatchLocalState, callbacks?.onQuizSkipQuestion);
19
21
  // Quiz Back button click callback
20
22
  const previousQuestion = useQuizBackClick(quizApiState, dispatchLocalState);
21
23
  // Quiz result add to cart callback
@@ -36,6 +38,7 @@ const useQuizEvents = (quizOptions, cioClient, quizState) => {
36
38
  quizAnswerChanged,
37
39
  previousQuestion,
38
40
  nextQuestion,
41
+ skipQuestion,
39
42
  resetQuiz,
40
43
  hydrateQuiz: hydrateQuizLocalState,
41
44
  resetSessionStorageState: resetQuizSessionStorageState(quizSessionStorageState.key),
@@ -0,0 +1,31 @@
1
+ import { useCallback } from 'react';
2
+ import { QuestionTypes } from '../../components/CioQuiz/actions';
3
+ import { isFunction } from '../../utils';
4
+ const useQuizSkipClick = (quizApiState, quizLocalState, dispatchLocalState, onQuizSkipQuestion) => {
5
+ const quizSkipClickHandler = useCallback(() => {
6
+ const currentQuestion = quizApiState.quizCurrentQuestion?.next_question;
7
+ const currentQuestionId = currentQuestion?.id;
8
+ if (dispatchLocalState && currentQuestionId) {
9
+ if (!(currentQuestion?.type === 'cover')) {
10
+ dispatchLocalState({
11
+ type: QuestionTypes.Skip,
12
+ payload: quizApiState.quizCurrentQuestion,
13
+ });
14
+ }
15
+ const currentAnswerInput = {
16
+ ...quizLocalState.answerInputs[currentQuestionId],
17
+ value: currentQuestion?.type === 'open' ? 'false' : null,
18
+ };
19
+ if (currentQuestion && isFunction(onQuizSkipQuestion)) {
20
+ onQuizSkipQuestion({ ...currentQuestion, answer: currentAnswerInput });
21
+ }
22
+ }
23
+ }, [
24
+ dispatchLocalState,
25
+ onQuizSkipQuestion,
26
+ quizApiState.quizCurrentQuestion,
27
+ quizLocalState.answerInputs,
28
+ ]);
29
+ return quizSkipClickHandler;
30
+ };
31
+ export default useQuizSkipClick;
@@ -176,6 +176,10 @@ export const useMockContextValue = (question) => {
176
176
  ...mockElementProps,
177
177
  className: 'cio-question-cta-button',
178
178
  }),
179
+ getSkipQuestionButtonProps: () => ({
180
+ ...mockElementProps,
181
+ className: 'cio-question-skip-button',
182
+ }),
179
183
  getPreviousQuestionButtonProps: () => ({
180
184
  ...mockElementProps,
181
185
  className: 'cio-question-back-button',
@@ -39,9 +39,18 @@ ${templateCode}
39
39
  export const functionStrings = {
40
40
  onAddToCartClick: `(item) => console.dir(item)`,
41
41
  onQuizResultClick: `(result, position) => console.dir(result, position)`,
42
- onAddToFavoritesClick: `(item) => console.dir(item)`,
42
+ onAddToFavoritesClick: `(result) => {
43
+ if (result.data) {
44
+ if (!favorites.includes(result.data.id)) {
45
+ setFavorites([...favorites, result.data.id]);
46
+ } else {
47
+ setFavorites(favorites.filter((id) => id !== result.data?.id));
48
+ }
49
+ }
50
+ }`,
43
51
  onQuizResultsLoaded: `(results) => console.dir(results)`,
44
52
  onQuizNextQuestion: `(question) => console.dir(question)`,
53
+ onQuizSkipQuestion: `(question) => console.dir(question)`,
45
54
  cioJsClient: `cioJsClient`,
46
55
  };
47
56
  export const stringifyWithDefaults = (obj) => {
package/lib/styles.css CHANGED
@@ -215,6 +215,14 @@
215
215
  background-color: rgba(255, 255, 255, 0.87);
216
216
  align-items: center;
217
217
  }
218
+ .cio-quiz .cio-question-buttons-group {
219
+ width: 20rem;
220
+ height: var(--bottom-bar-height);
221
+ padding: 8px 16px;
222
+ display: flex;
223
+ align-items: center;
224
+ margin-left: auto;
225
+ }
218
226
  .cio-quiz .cio-question-cta-button {
219
227
  background: var(--primary-color);
220
228
  color: #ffffff;
@@ -250,7 +258,27 @@
250
258
  cursor: auto;
251
259
  outline: none;
252
260
  }
253
-
261
+ .cio-quiz .cio-question-skip-button {
262
+ background: transparent;
263
+ color: var(--gray-dust-500);
264
+ border: none;
265
+ cursor: pointer;
266
+ padding: 0;
267
+ outline: none;
268
+ font-size: 1rem;
269
+ width: 100%;
270
+ text-decoration: underline;
271
+ text-decoration-color: transparent;
272
+ transition: text-decoration-color 0.2s ease-in-out;
273
+ }
274
+ .cio-quiz .cio-question-skip-button:hover,
275
+ .cio-quiz .cio-question-skip-button:focus {
276
+ text-decoration-color: var(--gray-dust-200);
277
+ }
278
+ .cio-quiz .cio-question-skip-button.hide {
279
+ visibility: hidden;
280
+ pointer-events: none;
281
+ }
254
282
  /* Back Button Component */
255
283
  .cio-quiz .cio-question-back-button {
256
284
  color: var(--primary-color);
@@ -585,11 +613,21 @@
585
613
  margin-bottom: 1rem;
586
614
  }
587
615
 
616
+ .cio-quiz .cio-result-card-favorites-button svg {
617
+ fill: none;
618
+ }
619
+
620
+ .cio-quiz .cio-result-card-favorites-button:hover svg {
621
+ fill: rgb(255, 51, 51);
622
+ stroke: rgb(255, 51, 51);
623
+ }
624
+
588
625
  .cio-quiz .cio-result-card-favorites-button.favorited svg {
589
- fill: red;
626
+ fill: rgb(255, 51, 51);
627
+ stroke: transparent;
590
628
  }
591
629
 
592
- .cio-quiz .cio-result-card-favorites-button svg:hover {
630
+ .cio-quiz .cio-result-card-favorites-button svg {
593
631
  stroke: black;
594
632
  stroke-width: 1px;
595
633
  }
@@ -639,6 +677,12 @@
639
677
 
640
678
  /* Container Queries */
641
679
 
680
+ @container quiz (max-width: 640px) {
681
+ .cio-quiz .cio-question-skip-button span {
682
+ display: none;
683
+ }
684
+ }
685
+
642
686
  /* Small Tablet */
643
687
  @container quiz (min-width: 640px) {
644
688
  .cio-quiz .cio-container {
@@ -6,6 +6,7 @@ export declare enum QuestionTypes {
6
6
  SingleSelect = "single",
7
7
  MultipleSelect = "multiple",
8
8
  Next = "next",
9
+ Skip = "skip",
9
10
  Back = "back",
10
11
  Reset = "reset",
11
12
  Hydrate = "hydrate",
@@ -24,7 +25,7 @@ interface Action<Type, Payload = {}> {
24
25
  payload?: Payload;
25
26
  }
26
27
  export type ActionAnswerInputQuestion = Action<QuestionTypes.OpenText, OpenTextQuestionPayload> | Action<QuestionTypes.SingleSelect, SelectQuestionPayload> | Action<QuestionTypes.MultipleSelect, SelectQuestionPayload> | Action<QuestionTypes.Cover, CoverQuestionPayload>;
27
- export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Next, CurrentQuestion> | Action<QuestionTypes.Back, CurrentQuestion> | Action<QuestionTypes.Reset> | Action<QuestionTypes.Complete> | Action<QuestionTypes.Hydrate, Partial<QuizLocalReducerState>>;
28
+ export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Next, CurrentQuestion> | Action<QuestionTypes.Skip, CurrentQuestion> | Action<QuestionTypes.Back, CurrentQuestion> | Action<QuestionTypes.Reset> | Action<QuestionTypes.Complete> | Action<QuestionTypes.Hydrate, Partial<QuizLocalReducerState>>;
28
29
  export declare enum QuizAPIActionTypes {
29
30
  SET_IS_LOADING = 0,
30
31
  SET_IS_ERROR = 1,
@@ -1,11 +1,12 @@
1
1
  import React from 'react';
2
2
  import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
3
- import { GetAddToCartButtonProps, GetAddToFavoritesButtonProps, GetCoverQuestionProps, GetHydrateQuizButtonProps, GetNextQuestionButtonProps, GetOpenTextInputProps, GetPreviousQuestionButtonProps, GetQuizImageProps, GetQuizResultButtonProps, GetQuizResultLinkProps, GetResetQuizButtonProps, GetSelectInputProps, PrimaryColorStyles, QuizReturnState } from '../../types';
3
+ import { GetAddToCartButtonProps, GetAddToFavoritesButtonProps, GetCoverQuestionProps, GetHydrateQuizButtonProps, GetNextQuestionButtonProps, GetSkipQuestionButtonProps, GetOpenTextInputProps, GetPreviousQuestionButtonProps, GetQuizImageProps, GetQuizResultButtonProps, GetQuizResultLinkProps, GetResetQuizButtonProps, GetSelectInputProps, PrimaryColorStyles, QuizReturnState } from '../../types';
4
4
  export interface QuizContextValue {
5
5
  cioClient?: ConstructorIOClient;
6
6
  state?: QuizReturnState;
7
7
  getOpenTextInputProps: GetOpenTextInputProps;
8
8
  getNextQuestionButtonProps: GetNextQuestionButtonProps;
9
+ getSkipQuestionButtonProps: GetSkipQuestionButtonProps;
9
10
  getPreviousQuestionButtonProps: GetPreviousQuestionButtonProps;
10
11
  getQuizImageProps: GetQuizImageProps;
11
12
  getSelectInputProps: GetSelectInputProps;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface SkipButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
3
+ propsGetters?: () => React.ButtonHTMLAttributes<HTMLButtonElement>;
4
+ }
5
+ declare function SkipButton(props: SkipButtonProps): JSX.Element;
6
+ export default SkipButton;
@@ -1,13 +1,14 @@
1
1
  export declare const apiKey = "key_wJSdZSiesX5hiVLt";
2
2
  export declare const quizId = "coffee-quiz";
3
3
  export declare const quizSessionStateKey = "constructorIOQuizState";
4
- export declare const componentDescription = "- import `CioQuiz` to render in your JSX.\n- This component handles state management, data fetching, and rendering logic.\n- To use this component, `quizId`, `resultsPageOptions`, and one of `apiKey` or `cioJsClient` are required.\n- `resultsPageOptions` lets you configure the results page\n - `onAddToCartClick` is a callback function that will be called when the \"Add to cart\" button is clicked\n - `onAddToFavoritesClick` is an optional callback function that will be called when the \"Add To Favorites\" heart icon is clicked\n - `onQuizResultClick` is an optional callback function that will be called when the result card is clicked. The default behavior is redirecting the user to the item's URL\n - `onQuizResultsLoaded` is an optional callback function that will be called when the quiz results are loaded\n - `resultCardRegularPriceKey` is a parameter that specifies the metadata field name for the regular price\n - `resultCardSalePriceKey` is an optional parameter that specifies the metadata field name for the sale price \n - `resultCardRatingCountKey` is an optional parameter that specifies the metadata field name for the ratings count \n - `resultCardRatingScoreKey` is an optional parameter that specifies the metadata field name for the ratings score \n - `renderResultCardPriceDetails` is an optional render function to render custom prices section in result card \n - `numResultsToDisplay` is an optional parameter that determines how many results should be displayed on results page\n- `callbacks` lets you pass callback functions that will be called on certain actions\n - `onQuizNextQuestion` is an optional callback function that will be called when user moves to the next question\n- `sessionStateOptions` lets you configure the session modal behavior\n - `showSessionModal` is a boolean used to decide whether to show the session modal. The default behavior is to show the session modal\n - `showSessionModalOnResults` is a boolean to decide whether to show the session modal after reaching the results page. The default behavior is to not show the session modal\n - `sessionStateKey` is a custom string that will be used as a session storage key\n- Use different props to configure the behavior of this component.\n- The following stories show how different props affect the component's behavior\n\n> Note: `cioJsClient` refers to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)\n";
5
- export declare const hookDescription = "- import `useCioQuiz` and call this custom hook in a functional component.\n- This hook leaves rendering logic up to you, while handling:\n - state management\n - data fetching\n - keyboard navigation\n - mouse interactions\n - focus and submit event handling\n- Since the markup is controlled by you, the default styles might not be applied if you have a different DOM structure\n- To use this hook, an `apiKey` and `quizId` are required, and `resultsPageOptions` must be passed to the `useCioQuiz` hook to configure behavior. All other values are optional.\n- use the <a href=\"https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters\" target=\"__blank\">prop getters</a> and other variables returned by this hook (below) to leverage the functionality described above with jsx elements in your react component definitions\n\nCalling the `useCioQuiz` hook returns an object with the following keys:\n\n```jsx\nconst {\n // must be used for a hooks integrations\n state: QuizReturnState, // Quiz state\n events: [{...}], // array of quiz events\n getAddToCartButtonProps: () => ({...})), // prop getter for jsx add to cart button for quiz result,\n getAddToFavoritesButtonProps: () => ({...})), // prop getter for jsx add to favorites button for quiz result,\n getCoverQuestionProps: () => ({...})), // prop getter for jsx quiz cover question,\n getHydrateQuizButtonProps: () => ({...})), // prop getter for jsx hydrate quiz button,\n getNextQuestionButtonProps: () => ({...})), // prop getter for jsx next button to traverse the quiz,\n getPreviousQuestionButtonProps: () => ({...})), // prop getter for jsx back button to traverse the quiz, \n getOpenTextInputProps: () => ({...})), // prop getter for jsx open text input,\n getSelectInputProps: () => ({...})), // prop getter for jsx select input for select type questions,\n getQuizImageProps: () => ({...})), // prop getter for jsx quiz image,\n getQuizResultButtonProps: () => ({...})), // prop getter for jsx result card click as a button,\n getQuizResultLinkProps: () => ({...})), // prop getter for jsx result card click as a link. Should be spread into <a> tags,\n getResetQuizButtonProps: () => ({...})), // prop getter for jsx reset quiz button,\n cioClient, // instance of constructorio-client-javascript\n } = useCioQuiz(args);\n```\n\n> Note: when we say `cioClient`, we are referring to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)\n";
4
+ export declare const componentDescription = "- import `CioQuiz` to render in your JSX.\n- This component handles state management, data fetching, and rendering logic.\n- To use this component, `quizId`, `resultsPageOptions`, and one of `apiKey` or `cioJsClient` are required.\n- `resultsPageOptions` lets you configure the results page\n - `onAddToCartClick` is a callback function that will be called when the \"Add to cart\" button is clicked\n - `onAddToFavoritesClick` is an optional callback function that will be called when the \"Add To Favorites\" heart icon is clicked\n - `onQuizResultClick` is an optional callback function that will be called when the result card is clicked. The default behavior is redirecting the user to the item's URL\n - `onQuizResultsLoaded` is an optional callback function that will be called when the quiz results are loaded\n - `resultCardRegularPriceKey` is a parameter that specifies the metadata field name for the regular price\n - `resultCardSalePriceKey` is an optional parameter that specifies the metadata field name for the sale price \n - `resultCardRatingCountKey` is an optional parameter that specifies the metadata field name for the ratings count \n - `resultCardRatingScoreKey` is an optional parameter that specifies the metadata field name for the ratings score \n - `renderResultCardPriceDetails` is an optional render function to render custom prices section in result card \n - `numResultsToDisplay` is an optional parameter that determines how many results should be displayed on results page\n- `callbacks` lets you pass callback functions that will be called on certain actions\n - `onQuizNextQuestion` is an optional callback function that will be called when user moves to the next question\n - `onQuizSkipQuestion` is an optional callback function that will be called when user skips a question\n- `sessionStateOptions` lets you configure the session modal behavior\n - `showSessionModal` is a boolean used to decide whether to show the session modal. The default behavior is to show the session modal\n - `showSessionModalOnResults` is a boolean to decide whether to show the session modal after reaching the results page. The default behavior is to not show the session modal\n - `sessionStateKey` is a custom string that will be used as a session storage key\n- Use different props to configure the behavior of this component.\n- The following stories show how different props affect the component's behavior\n\n> Note: `cioJsClient` refers to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)\n";
5
+ export declare const hookDescription = "- import `useCioQuiz` and call this custom hook in a functional component.\n- This hook leaves rendering logic up to you, while handling:\n - state management\n - data fetching\n - keyboard navigation\n - mouse interactions\n - focus and submit event handling\n- Since the markup is controlled by you, the default styles might not be applied if you have a different DOM structure\n- To use this hook, an `apiKey` and `quizId` are required, and `resultsPageOptions` must be passed to the `useCioQuiz` hook to configure behavior. All other values are optional.\n- use the <a href=\"https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters\" target=\"__blank\">prop getters</a> and other variables returned by this hook (below) to leverage the functionality described above with jsx elements in your react component definitions\n\nCalling the `useCioQuiz` hook returns an object with the following keys:\n\n```jsx\nconst {\n // must be used for a hooks integrations\n state: QuizReturnState, // Quiz state\n events: [{...}], // array of quiz events\n getAddToCartButtonProps: () => ({...})), // prop getter for jsx add to cart button for quiz result,\n getAddToFavoritesButtonProps: () => ({...})), // prop getter for jsx add to favorites button for quiz result,\n getCoverQuestionProps: () => ({...})), // prop getter for jsx quiz cover question,\n getHydrateQuizButtonProps: () => ({...})), // prop getter for jsx hydrate quiz button,\n getNextQuestionButtonProps: () => ({...})), // prop getter for jsx next button to traverse the quiz,\n getSkipQuestionButtonProps: () => ({...})), // prop getter for jsx skip button to traverse the quiz,\n getPreviousQuestionButtonProps: () => ({...})), // prop getter for jsx back button to traverse the quiz, \n getOpenTextInputProps: () => ({...})), // prop getter for jsx open text input,\n getSelectInputProps: () => ({...})), // prop getter for jsx select input for select type questions,\n getQuizImageProps: () => ({...})), // prop getter for jsx quiz image,\n getQuizResultButtonProps: () => ({...})), // prop getter for jsx result card click as a button,\n getQuizResultLinkProps: () => ({...})), // prop getter for jsx result card click as a link. Should be spread into <a> tags,\n getResetQuizButtonProps: () => ({...})), // prop getter for jsx reset quiz button,\n cioClient, // instance of constructorio-client-javascript\n } = useCioQuiz(args);\n```\n\n> Note: when we say `cioClient`, we are referring to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)\n";
6
6
  export declare const basicDescription = "Pass an `apiKey` and a `quizId` to request questions and quiz results from Constructor's servers";
7
7
  export declare const cioJsClientDescription = "If you are already using an instance of the `ConstructorIOClient`, you can pass a `cioJsClient` instead of an `apiKey` to request results from Constructor's servers\n\n> Note: `cioJsClient` refers to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)";
8
8
  export declare const smallContainerDescription = "If you are using the provided styles, CioQuiz component will respect the height and width of its parent container and use responsive styles based on the parent container's dimensions";
9
9
  export declare const changePrimaryColorDescription = "\nIf you would like to use a different primary color, pass a `primaryColor` string in RGB format ('R, G, B').\n\nAccent colors for borders and different states (hover, focus, active) will be calculated automatically based on the primary color you provided.\n\nBy default, the primary color has a value of \"35, 71, 199\" (Constructor Blue).\n\nIn the example below, the `primaryColor` prop has been used to override this color to \"255, 82, 48\" (Orange).\n\n> Advanced Option: Instead of passing a primaryColor prop, you can also override `--primary-color-h`, `--primary-color-s`, and `--primary-color-l` CSS variables within a `.cio-quiz` container element. If explicitly given a value in your CSS, then the values of these variables will be used as the HSL values for your quiz.\n";
10
10
  export declare const callbacksDescription = "Pass an `apiKey`, a `quizId`, and `callbacks`";
11
+ export declare const favoritesDescription = "\nAdd `const [favorites, setFavorites] = useState([]);` or equivalent to manage the favorite items' state.\n\nPass favorites as an array to `favoriteItems` in `resultsPageOptions` as a prop to CioQuiz.\n\nPass a callback function to `onAddToFavoritesClick` in `resultsPageOptions` as a prop to CioQuiz to handle favorites state changes to update `favorites` array. \n";
11
12
  export declare enum RequestStates {
12
13
  Stale = "STALE",
13
14
  Loading = "LOADING",
@@ -1,7 +1,7 @@
1
- import { GetResetQuizButtonProps, GetCoverQuestionProps, GetNextQuestionButtonProps, GetOpenTextInputProps, GetPreviousQuestionButtonProps, GetQuizImageProps, GetSelectInputProps, QuizEventsReturn, GetHydrateQuizButtonProps, GetAddToCartButtonProps, GetQuizResultButtonProps, GetQuizResultLinkProps, GetSelectQuestionImageProps, GetAddToFavoritesButtonProps } from '../../types';
1
+ import { GetResetQuizButtonProps, GetCoverQuestionProps, GetNextQuestionButtonProps, GetOpenTextInputProps, GetPreviousQuestionButtonProps, GetQuizImageProps, GetSelectInputProps, QuizEventsReturn, GetHydrateQuizButtonProps, GetAddToCartButtonProps, GetQuizResultButtonProps, GetQuizResultLinkProps, GetSelectQuestionImageProps, GetAddToFavoritesButtonProps, GetSkipQuestionButtonProps } from '../../types';
2
2
  import { QuizAPIReducerState } from '../../components/CioQuiz/quizApiReducer';
3
3
  import { QuizLocalReducerState } from '../../components/CioQuiz/quizLocalReducer';
4
- declare const usePropsGetters: (quizEvents: QuizEventsReturn, quizApiState: QuizAPIReducerState, quizLocalState: QuizLocalReducerState) => {
4
+ declare const usePropsGetters: (quizEvents: QuizEventsReturn, quizApiState: QuizAPIReducerState, quizLocalState: QuizLocalReducerState, favoriteItems?: string[]) => {
5
5
  getOpenTextInputProps: GetOpenTextInputProps;
6
6
  getNextQuestionButtonProps: GetNextQuestionButtonProps;
7
7
  getPreviousQuestionButtonProps: GetPreviousQuestionButtonProps;
@@ -15,5 +15,6 @@ declare const usePropsGetters: (quizEvents: QuizEventsReturn, quizApiState: Quiz
15
15
  getAddToFavoritesButtonProps: GetAddToFavoritesButtonProps;
16
16
  getQuizResultButtonProps: GetQuizResultButtonProps;
17
17
  getQuizResultLinkProps: GetQuizResultLinkProps;
18
+ getSkipQuestionButtonProps: GetSkipQuestionButtonProps;
18
19
  };
19
20
  export default usePropsGetters;
@@ -1,2 +1,2 @@
1
1
  import { GetAddToFavoritesButtonProps, QuizEventsReturn } from '../../types';
2
- export default function useAddToFavoritesButtonProps(addToFavorites: QuizEventsReturn.AddToFavorites): GetAddToFavoritesButtonProps;
2
+ export default function useAddToFavoritesButtonProps(addToFavorites: QuizEventsReturn.AddToFavorites, favoriteItems?: string[]): GetAddToFavoritesButtonProps;
@@ -0,0 +1,3 @@
1
+ import { QuizAPIReducerState } from '../../components/CioQuiz/quizApiReducer';
2
+ import { GetSkipQuestionButtonProps, QuizEventsReturn } from '../../types';
3
+ export default function useSkipQuestionButtonProps(skipQuestion: QuizEventsReturn.SkipQuestion, quizApiState: QuizAPIReducerState): GetSkipQuestionButtonProps;
@@ -0,0 +1,6 @@
1
+ import { ActionAnswerQuestion } from '../../components/CioQuiz/actions';
2
+ import { QuizAPIReducerState } from '../../components/CioQuiz/quizApiReducer';
3
+ import { QuizLocalReducerState } from '../../components/CioQuiz/quizLocalReducer';
4
+ import { QuizEventsReturn, OnQuizSkipQuestion } from '../../types';
5
+ declare const useQuizSkipClick: (quizApiState: QuizAPIReducerState, quizLocalState: QuizLocalReducerState, dispatchLocalState: React.Dispatch<ActionAnswerQuestion>, onQuizSkipQuestion?: OnQuizSkipQuestion) => QuizEventsReturn.NextQuestion;
6
+ export default useQuizSkipClick;
@@ -7,7 +7,7 @@ export declare const getMockQuestion: (type: `${QuestionTypes}`) => {
7
7
  title: string;
8
8
  description: string;
9
9
  cta_text: string;
10
- type: "reset" | "open" | "cover" | "single" | "multiple" | "next" | "back" | "hydrate" | "complete";
10
+ type: "reset" | "open" | "cover" | "single" | "multiple" | "next" | "skip" | "back" | "hydrate" | "complete";
11
11
  input_placeholder: string;
12
12
  };
13
13
  export declare const getMockQuestionWithImage: (type: QuestionTypes) => {
@@ -18,7 +18,7 @@ export declare const getMockQuestionWithImage: (type: QuestionTypes) => {
18
18
  title: string;
19
19
  description: string;
20
20
  cta_text: string;
21
- type: "reset" | "open" | "cover" | "single" | "multiple" | "next" | "back" | "hydrate" | "complete";
21
+ type: "reset" | "open" | "cover" | "single" | "multiple" | "next" | "skip" | "back" | "hydrate" | "complete";
22
22
  input_placeholder: string;
23
23
  };
24
24
  export declare const questionOptionsWithImages: {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- import { QuizResultData, NextQuestionResponse, QuizResultsResponse, Nullable, QuestionOption, Question } from '@constructor-io/constructorio-client-javascript/lib/types';
2
+ import { QuizResultData, NextQuestionResponse, QuizResultsResponse, QuestionOption, Question } from '@constructor-io/constructorio-client-javascript/lib/types';
3
3
  import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
4
4
  import { RequestStates } from './constants';
5
5
  import { QuestionTypes } from './components/CioQuiz/actions';
@@ -20,6 +20,7 @@ export declare namespace QuizResultsEventsProps {
20
20
  }
21
21
  export interface ResultsPageOptions extends ResultCardProps {
22
22
  numResultsToDisplay?: number;
23
+ favoriteItems?: string[];
23
24
  onQuizResultsLoaded?: QuizResultsEventsProps.OnQuizResultsLoaded;
24
25
  onQuizResultClick?: QuizResultsEventsProps.OnQuizResultClick;
25
26
  onAddToCartClick: QuizResultsEventsProps.OnAddToCartClick;
@@ -34,8 +35,10 @@ export type QuestionWithAnswer = Question & {
34
35
  answer: AnswerInput;
35
36
  };
36
37
  export type OnQuizNextQuestion = (question: QuestionWithAnswer) => void;
38
+ export type OnQuizSkipQuestion = (question: QuestionWithAnswer) => void;
37
39
  export interface Callbacks {
38
40
  onQuizNextQuestion?: OnQuizNextQuestion;
41
+ onQuizSkipQuestion?: OnQuizSkipQuestion;
39
42
  }
40
43
  export interface IQuizProps {
41
44
  apiKey?: string;
@@ -65,7 +68,7 @@ export interface QuizReturnState {
65
68
  }
66
69
  export type AnswerInput = {
67
70
  type: InputQuestionsTypes;
68
- value: string | string[];
71
+ value: string | string[] | null;
69
72
  };
70
73
  export type AnswerInputState = {
71
74
  [key: string]: AnswerInput;
@@ -87,6 +90,7 @@ export type CurrentQuestion = NextQuestionResponse & {
87
90
  export declare namespace QuizEventsReturn {
88
91
  type QuizAnswerChanged = (payload?: string | string[]) => void;
89
92
  type NextQuestion = () => void;
93
+ type SkipQuestion = () => void;
90
94
  type PreviousQuestion = () => void;
91
95
  type ResetQuiz = () => void;
92
96
  type ResultClick = (result: QuizResultDataPartial, position: number) => void;
@@ -97,6 +101,7 @@ export declare namespace QuizEventsReturn {
97
101
  }
98
102
  export interface QuizEventsReturn {
99
103
  nextQuestion: QuizEventsReturn.NextQuestion;
104
+ skipQuestion: QuizEventsReturn.SkipQuestion;
100
105
  quizAnswerChanged: QuizEventsReturn.QuizAnswerChanged;
101
106
  previousQuestion: QuizEventsReturn.PreviousQuestion;
102
107
  resetQuiz: QuizEventsReturn.ResetQuiz;
@@ -121,6 +126,12 @@ export interface NextQuestionButtonProps {
121
126
  disabled?: boolean;
122
127
  onClick: React.MouseEventHandler<HTMLElement>;
123
128
  }
129
+ export interface SkipQuestionButtonProps {
130
+ className: string;
131
+ type: 'submit' | 'reset' | 'button' | undefined;
132
+ disabled?: boolean;
133
+ onClick: React.MouseEventHandler<HTMLElement>;
134
+ }
124
135
  export interface PreviousQuestionButtonProps {
125
136
  className: string;
126
137
  type: 'submit' | 'reset' | 'button' | undefined;
@@ -147,8 +158,8 @@ export interface AddToCartButtonProps {
147
158
  }
148
159
  export interface QuizImageProps {
149
160
  className?: string;
150
- src?: Nullable<string>;
151
- alt?: Nullable<string>;
161
+ src?: string;
162
+ alt?: string;
152
163
  }
153
164
  export interface QuizResultPropsLink {
154
165
  href?: string;
@@ -175,6 +186,7 @@ export type GetOpenTextInputProps = () => OpenTextInputProps;
175
186
  export type GetCoverQuestionProps = () => CoverQuestionProps;
176
187
  export type GetSelectInputProps = (option: QuestionOption) => SelectInputProps;
177
188
  export type GetNextQuestionButtonProps = () => NextQuestionButtonProps;
189
+ export type GetSkipQuestionButtonProps = () => SkipQuestionButtonProps;
178
190
  export type GetPreviousQuestionButtonProps = () => PreviousQuestionButtonProps;
179
191
  export type GetResetQuizButtonProps = (stylesType?: 'primary' | 'secondary') => ResetQuizButtonProps;
180
192
  export type GetHydrateQuizButtonProps = () => HydrateQuizButtonProps;
@@ -200,6 +212,7 @@ export interface UseQuizReturn {
200
212
  events: QuizEventsReturn;
201
213
  getOpenTextInputProps: GetOpenTextInputProps;
202
214
  getNextQuestionButtonProps: GetNextQuestionButtonProps;
215
+ getSkipQuestionButtonProps: GetSkipQuestionButtonProps;
203
216
  getPreviousQuestionButtonProps: GetPreviousQuestionButtonProps;
204
217
  getQuizImageProps: GetQuizImageProps;
205
218
  getSelectQuestionImageProps: GetSelectQuestionImageProps;
@@ -18,6 +18,7 @@ export declare const functionStrings: {
18
18
  onAddToFavoritesClick: string;
19
19
  onQuizResultsLoaded: string;
20
20
  onQuizNextQuestion: string;
21
+ onQuizSkipQuestion: string;
21
22
  cioJsClient: string;
22
23
  };
23
24
  export declare const stringifyWithDefaults: (obj: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructor-io/constructorio-ui-quizzes",
3
- "version": "1.8.3",
3
+ "version": "1.9.1",
4
4
  "description": "Constructor.io Quizzes UI library for web applications",
5
5
  "author": "constructor.io",
6
6
  "license": "MIT",