@constructor-io/constructorio-ui-quizzes 1.3.2 → 1.3.4

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 (38) hide show
  1. package/dist/constructorio-ui-quizzes-bundled.js +15 -15
  2. package/lib/cjs/components/CioQuiz/actions.js +1 -0
  3. package/lib/cjs/components/CioQuiz/index.js +16 -3
  4. package/lib/cjs/components/CioQuiz/quizApiReducer.js +5 -5
  5. package/lib/cjs/components/CioQuiz/quizLocalReducer.js +2 -0
  6. package/lib/cjs/components/CoverTypeQuestion/CoverTypeQuestion.js +1 -1
  7. package/lib/cjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +1 -1
  8. package/lib/cjs/components/SelectTypeQuestion/SelectTypeQuestion.js +2 -2
  9. package/lib/cjs/components/SessionPromptModal/SessionPromptModal.js +24 -0
  10. package/lib/cjs/constants.js +3 -1
  11. package/lib/cjs/hooks/useQuiz.js +8 -5
  12. package/lib/cjs/hooks/useQuizApiState.js +16 -13
  13. package/lib/cjs/hooks/useQuizEvents/index.js +5 -1
  14. package/lib/cjs/hooks/useQuizLocalState.js +35 -1
  15. package/lib/mjs/components/CioQuiz/actions.js +1 -0
  16. package/lib/mjs/components/CioQuiz/index.js +16 -3
  17. package/lib/mjs/components/CioQuiz/quizApiReducer.js +0 -2
  18. package/lib/mjs/components/CioQuiz/quizLocalReducer.js +5 -0
  19. package/lib/mjs/components/CoverTypeQuestion/CoverTypeQuestion.js +1 -1
  20. package/lib/mjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +1 -1
  21. package/lib/mjs/components/SelectTypeQuestion/SelectTypeQuestion.js +2 -2
  22. package/lib/mjs/components/SessionPromptModal/SessionPromptModal.js +20 -0
  23. package/lib/mjs/constants.js +2 -0
  24. package/lib/mjs/hooks/useQuiz.js +8 -5
  25. package/lib/mjs/hooks/useQuizApiState.js +17 -14
  26. package/lib/mjs/hooks/useQuizEvents/index.js +5 -1
  27. package/lib/mjs/hooks/useQuizLocalState.js +34 -3
  28. package/lib/styles.css +43 -0
  29. package/lib/types/components/CioQuiz/actions.d.ts +4 -2
  30. package/lib/types/components/CioQuiz/quizApiReducer.d.ts +0 -2
  31. package/lib/types/components/CioQuiz/quizLocalReducer.d.ts +2 -0
  32. package/lib/types/components/SessionPromptModal/SessionPromptModal.d.ts +9 -0
  33. package/lib/types/constants.d.ts +1 -0
  34. package/lib/types/hooks/useQuizApiState.d.ts +2 -1
  35. package/lib/types/hooks/useQuizEvents/index.d.ts +3 -0
  36. package/lib/types/hooks/useQuizLocalState.d.ts +6 -2
  37. package/lib/types/types.d.ts +11 -0
  38. package/package.json +1 -1
@@ -4,7 +4,7 @@ import useQuizAddToCart from './useQuizAddToCart';
4
4
  import useQuizNextClick from './useQuizNextClick';
5
5
  import useQuizBackClick from './useQuizBackClick';
6
6
  const useQuizEvents = (options) => {
7
- const { cioClient, quizApiState, resultsPageOptions, dispatchLocalState, resetQuizApiState, resetQuizLocalState, } = options;
7
+ const { cioClient, quizApiState, resultsPageOptions, dispatchLocalState, resetQuizApiState, resetQuizLocalState, hydrateQuizLocalState, resetQuizStoredState, hasQuizStoredState, } = options;
8
8
  const { onAddToCartClick, onQuizResultClick, onQuizResultsLoaded } = resultsPageOptions;
9
9
  // Quiz Next button click
10
10
  const nextQuestion = useQuizNextClick(quizApiState, dispatchLocalState);
@@ -20,6 +20,7 @@ const useQuizEvents = (options) => {
20
20
  if (quizApiState.quizResults) {
21
21
  resetQuizApiState();
22
22
  resetQuizLocalState();
23
+ resetQuizStoredState();
23
24
  }
24
25
  };
25
26
  return {
@@ -28,6 +29,9 @@ const useQuizEvents = (options) => {
28
29
  nextQuestion,
29
30
  previousQuestion,
30
31
  resetQuiz,
32
+ hydrateQuiz: hydrateQuizLocalState,
33
+ hasStoredState: hasQuizStoredState,
34
+ resetStoredState: resetQuizStoredState,
31
35
  };
32
36
  };
33
37
  export default useQuizEvents;
@@ -1,16 +1,47 @@
1
- import { useReducer } from 'react';
1
+ import { useEffect, useReducer } from 'react';
2
2
  import { QuestionTypes } from '../components/CioQuiz/actions';
3
- import quizLocalReducer, { initialState } from '../components/CioQuiz/quizLocalReducer';
4
- const useQuizLocalState = () => {
3
+ import quizLocalReducer, { initialState, } from '../components/CioQuiz/quizLocalReducer';
4
+ import { quizSessionStateKey } from '../constants';
5
+ const useQuizLocalState = (sessionStateKey) => {
5
6
  const [quizLocalState, dispatch] = useReducer(quizLocalReducer, initialState);
7
+ const quizStateKey = sessionStateKey || quizSessionStateKey;
8
+ const getStateFromSessionStorage = () => {
9
+ const state = window?.sessionStorage?.getItem(quizStateKey);
10
+ if (state) {
11
+ return JSON.parse(state);
12
+ }
13
+ return null;
14
+ };
15
+ useEffect(() => {
16
+ // don't save state if initial state
17
+ if (quizLocalState?.answers?.length) {
18
+ window?.sessionStorage?.setItem(quizStateKey, JSON.stringify(quizLocalState));
19
+ }
20
+ }, [quizLocalState, quizStateKey]);
6
21
  const resetQuizLocalState = () => {
7
22
  dispatch({
8
23
  type: QuestionTypes.Reset,
9
24
  });
10
25
  };
26
+ const hydrateQuizLocalState = () => {
27
+ const quizState = getStateFromSessionStorage();
28
+ if (quizState) {
29
+ dispatch({
30
+ type: QuestionTypes.Hydrate,
31
+ payload: quizState,
32
+ });
33
+ }
34
+ };
35
+ const resetQuizStoredState = () => {
36
+ window?.sessionStorage?.removeItem(quizStateKey);
37
+ };
38
+ const hasQuizStoredState = () => getStateFromSessionStorage() !== null;
11
39
  return {
12
40
  quizLocalState,
13
41
  resetQuizLocalState,
42
+ hydrateQuizLocalState,
43
+ hasQuizStoredState,
44
+ resetQuizStoredState,
14
45
  dispatchLocalState: dispatch,
15
46
  };
16
47
  };
package/lib/styles.css CHANGED
@@ -73,6 +73,49 @@
73
73
  padding-bottom: var(--bottom-bar-height);
74
74
  }
75
75
 
76
+ /* Session Prompt Modal */
77
+ .cio-session-prompt-modal {
78
+ position: absolute;
79
+ display: flex;
80
+ z-index: 100;
81
+ width: 100%;
82
+ height: 100%;
83
+ background-color: rgba(0,0,0,0.5);
84
+ justify-content: space-between;
85
+ }
86
+
87
+ .cio-session-prompt-container {
88
+ margin: auto;
89
+ }
90
+
91
+ .cio-session-prompt-content {
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: 1rem;
95
+ height: fit-content;
96
+ margin: 2rem;
97
+ padding: 2rem;
98
+ border-radius: 8px;
99
+ background: #ffffff;
100
+ line-height: 1.5;
101
+ }
102
+
103
+ .cio-session-prompt-controls-container {
104
+ display: flex;
105
+ justify-content: space-between;
106
+ gap: 1rem;
107
+ }
108
+
109
+ .cio-session-prompt-content .cio-button-container {
110
+ margin: 0px;
111
+ padding: 0px;
112
+ }
113
+
114
+ .cio-session-prompt-controls-container .cio-button-container:nth-child(1) .cio-question-cta-button {
115
+ color: var(--primary-color);
116
+ background-color: #ffffff;
117
+ }
118
+
76
119
  /* Content */
77
120
  .cio-question-content {
78
121
  display: flex;
@@ -1,11 +1,13 @@
1
1
  import { NextQuestionResponse, QuizResultsResponse } from '../../types';
2
+ import type { QuizLocalReducerState } from './quizLocalReducer';
2
3
  export declare enum QuestionTypes {
3
4
  OpenText = "open",
4
5
  Cover = "cover",
5
6
  SingleSelect = "single",
6
7
  MultipleSelect = "multiple",
7
8
  Back = "back",
8
- Reset = "reset"
9
+ Reset = "reset",
10
+ Hydrate = "hydrate"
9
11
  }
10
12
  export interface QuestionAnswer<Value> {
11
13
  questionId: number;
@@ -22,7 +24,7 @@ interface Action<Type, Payload = {}> {
22
24
  payload?: Payload;
23
25
  }
24
26
  export type ActionAnswerInputQuestion = Action<QuestionTypes.OpenText, OpenTextQuestionPayload> | Action<QuestionTypes.SingleSelect, SelectQuestionPayload> | Action<QuestionTypes.MultipleSelect, SelectQuestionPayload>;
25
- export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Cover, CoverQuestionPayload> | Action<QuestionTypes.Back> | Action<QuestionTypes.Reset>;
27
+ export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Cover, CoverQuestionPayload> | Action<QuestionTypes.Back> | Action<QuestionTypes.Reset> | Action<QuestionTypes.Hydrate, Partial<QuizLocalReducerState>>;
26
28
  export declare enum QuizAPIActionTypes {
27
29
  SET_IS_LOADING = 0,
28
30
  SET_IS_ERROR = 1,
@@ -3,8 +3,6 @@ import { NextQuestionResponse, QuizResultsResponse } from '../../types';
3
3
  import { ActionQuizAPI } from './actions';
4
4
  export type QuizAPIReducerState = {
5
5
  quizRequestState: RequestStates;
6
- quizVersionId?: string;
7
- quizSessionId?: string;
8
6
  quizFirstQuestion?: NextQuestionResponse;
9
7
  quizCurrentQuestion?: NextQuestionResponse;
10
8
  quizResults?: QuizResultsResponse;
@@ -4,6 +4,8 @@ export type QuizLocalReducerState = {
4
4
  answers: Answers;
5
5
  answerInputs: {};
6
6
  isLastAnswer: boolean;
7
+ quizVersionId?: string;
8
+ quizSessionId?: string;
7
9
  };
8
10
  export type AnswerInputState = {
9
11
  [key: string]: OpenTextQuestionPayload | SelectQuestionPayload;
@@ -0,0 +1,9 @@
1
+ /// <reference types="react" />
2
+ interface SessionPromptModalProps {
3
+ continueSession: () => void;
4
+ resetStoredState: () => void;
5
+ setShowSessionPrompt: (showSessionPrompt: boolean) => void;
6
+ showSessionPrompt: boolean;
7
+ }
8
+ export default function SessionPromptModal({ continueSession, resetStoredState, setShowSessionPrompt, showSessionPrompt, }: SessionPromptModalProps): JSX.Element | null;
9
+ export {};
@@ -1,5 +1,6 @@
1
1
  export declare const apiKey = "key_wJSdZSiesX5hiVLt";
2
2
  export declare const quizId = "coffee-quiz";
3
+ export declare const quizSessionStateKey = "constructorIOQuizState";
3
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 - `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- 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";
4
5
  export declare const basicDescription = "Pass an `apiKey` and a `quizId` to request questions and quiz results from Constructor's servers";
5
6
  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)";
@@ -1,7 +1,8 @@
1
1
  import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
2
+ import { ActionAnswerQuestion } from '../components/CioQuiz/actions';
2
3
  import { QuizLocalReducerState } from '../components/CioQuiz/quizLocalReducer';
3
4
  import { ResultsPageOptions } from '../types';
4
- declare const useFetchQuiz: (quizId: string, quizLocalState: QuizLocalReducerState, resultsPageOptions: ResultsPageOptions, quizVersionIdProp: string | undefined, cioClient: ConstructorIOClient) => {
5
+ declare const useFetchQuiz: (quizId: string, quizLocalState: QuizLocalReducerState, dispatchLocalState: React.Dispatch<ActionAnswerQuestion>, resultsPageOptions: ResultsPageOptions, quizVersionIdProp: string | undefined, cioClient: ConstructorIOClient) => {
5
6
  cioClient: ConstructorIOClient;
6
7
  quizApiState: import("../components/CioQuiz/quizApiReducer").QuizAPIReducerState;
7
8
  isFirstQuestion: boolean;
@@ -10,6 +10,9 @@ export type UseQuizEventOptions = {
10
10
  dispatchLocalState: React.Dispatch<ActionAnswerQuestion>;
11
11
  resetQuizApiState: () => void;
12
12
  resetQuizLocalState: () => void;
13
+ hydrateQuizLocalState: () => void;
14
+ resetQuizStoredState: () => void;
15
+ hasQuizStoredState: () => boolean;
13
16
  };
14
17
  declare const useQuizEvents: (options: UseQuizEventOptions) => QuizEventsReturn;
15
18
  export default useQuizEvents;
@@ -1,6 +1,10 @@
1
- declare const useQuizLocalState: () => {
2
- quizLocalState: import("../components/CioQuiz/quizLocalReducer").QuizLocalReducerState;
1
+ import { QuizLocalReducerState } from '../components/CioQuiz/quizLocalReducer';
2
+ declare const useQuizLocalState: (sessionStateKey?: string) => {
3
+ quizLocalState: QuizLocalReducerState;
3
4
  resetQuizLocalState: () => void;
5
+ hydrateQuizLocalState: () => void;
6
+ hasQuizStoredState: () => boolean;
7
+ resetQuizStoredState: () => void;
4
8
  dispatchLocalState: import("react").Dispatch<import("../components/CioQuiz/actions").ActionAnswerQuestion>;
5
9
  };
6
10
  export default useQuizLocalState;
@@ -19,12 +19,17 @@ export interface ResultsPageOptions extends ResultsProps {
19
19
  onQuizResultClick?: QuizResultsEventsProps.OnQuizResultClick;
20
20
  onAddToCartClick: QuizResultsEventsProps.OnAddToCartClick;
21
21
  }
22
+ export interface SessionStateOptions {
23
+ showSessionModal?: boolean;
24
+ sessionStateKey?: string;
25
+ }
22
26
  export interface IQuizProps {
23
27
  apiKey?: string;
24
28
  cioJsClient?: ConstructorIOClient;
25
29
  quizId: string;
26
30
  quizVersionId?: string;
27
31
  resultsPageOptions: ResultsPageOptions;
32
+ sessionStateOptions?: SessionStateOptions;
28
33
  }
29
34
  export interface QuizReturnState {
30
35
  answers: {
@@ -48,6 +53,9 @@ export declare namespace QuizEventsReturn {
48
53
  type ResetQuiz = () => void;
49
54
  type ResultClick = (result: QuizResultDataPartial, position: number) => void;
50
55
  type AddToCart = (e: React.MouseEvent<HTMLElement>, result: QuizResultDataPartial, price?: number) => void;
56
+ type HydrateQuiz = () => void;
57
+ type HasStoredState = () => boolean;
58
+ type ResetStoredState = () => void;
51
59
  }
52
60
  export interface QuizEventsReturn {
53
61
  nextQuestion: QuizEventsReturn.NextQuestion;
@@ -55,6 +63,9 @@ export interface QuizEventsReturn {
55
63
  resetQuiz: QuizEventsReturn.ResetQuiz;
56
64
  resultClick: QuizEventsReturn.ResultClick;
57
65
  addToCart: QuizEventsReturn.AddToCart;
66
+ hydrateQuiz: QuizEventsReturn.HydrateQuiz;
67
+ hasStoredState: QuizEventsReturn.HasStoredState;
68
+ resetStoredState: QuizEventsReturn.ResetStoredState;
58
69
  }
59
70
  export interface UseQuizReturn {
60
71
  cioClient?: ConstructorIOClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructor-io/constructorio-ui-quizzes",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "Constructor.io Quizzes UI library for web applications",
5
5
  "author": "constructor.io",
6
6
  "license": "MIT",