@akinon/pz-checkout-gift-pack 2.0.0-beta.2 → 2.0.0-beta.21

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.
package/CHANGELOG.md CHANGED
@@ -1,18 +1,134 @@
1
1
  # @akinon/pz-checkout-gift-pack
2
2
 
3
- ## 2.0.0-beta.2
3
+ ## 2.0.0-beta.21
4
4
 
5
- ## 2.0.0-beta.1
5
+ ## 2.0.0-beta.20
6
+
7
+ ## 1.126.0
8
+
9
+ ## 1.125.2
10
+
11
+ ## 1.125.1
12
+
13
+ ## 1.125.0
14
+
15
+ ## 1.124.0
16
+
17
+ ## 1.123.0
18
+
19
+ ## 1.122.0
20
+
21
+ ## 1.121.0
22
+
23
+ ## 1.120.0
24
+
25
+ ## 1.119.0
26
+
27
+ ## 1.118.0
28
+
29
+ ## 1.117.0
30
+
31
+ ## 1.116.0
32
+
33
+ ## 1.115.0
34
+
35
+ ## 1.114.0
36
+
37
+ ## 1.113.0
38
+
39
+ ## 1.112.0
40
+
41
+ ## 1.111.0
42
+
43
+ ## 1.110.0
44
+
45
+ ## 1.109.0
46
+
47
+ ## 1.108.0
48
+
49
+ ## 1.107.0
50
+
51
+ ## 1.106.0
52
+
53
+ ### Minor Changes
54
+
55
+ - 8bc82f0: ZERO-3405: Update Readme
56
+
57
+ ## 1.105.0
58
+
59
+ ## 1.104.0
60
+
61
+ ## 1.103.0
62
+
63
+ ## 1.102.0
64
+
65
+ ## 1.101.0
66
+
67
+ ## 1.100.0
68
+
69
+ ## 1.99.0
70
+
71
+ ### Minor Changes
72
+
73
+ - d58538b: ZERO-3638: Enhance RC pipeline: add fetch, merge, and pre-release setup with conditional commit
74
+
75
+ ## 1.98.0
76
+
77
+ ## 1.97.0
78
+
79
+ ## 1.96.0
80
+
81
+ ## 1.95.0
82
+
83
+ ## 1.94.0
84
+
85
+ ## 1.93.0
86
+
87
+ ## 1.92.0
88
+
89
+ ## 1.91.0
90
+
91
+ ## 1.90.0
6
92
 
7
93
  ### Minor Changes
8
94
 
9
- - ZERO-3091: Upgrade Next.js to v15 and React to v19
95
+ - ec9ff89: ZERO-3368: Add customization to package
96
+
97
+ ## 1.89.0
98
+
99
+ ## 1.88.0
100
+
101
+ ### Minor Changes
102
+
103
+ - ce64181: ZERO-3333: Add data-testid for checkout gift pack
104
+
105
+ ## 1.87.0
106
+
107
+ ## 1.86.0
108
+
109
+ ## 1.85.0
110
+
111
+ ## 1.84.0
112
+
113
+ ### Minor Changes
114
+
115
+ - 624a4eb: ZERO-3276: Update installation instructions across multiple README files to standardize format and improve clarity
116
+
117
+ ## 1.83.0
118
+
119
+ ## 1.82.0
120
+
121
+ ## 1.81.0
122
+
123
+ ## 1.80.0
124
+
125
+ ## 1.79.0
10
126
 
11
- ## 2.0.0-beta.0
127
+ ## 1.78.0
12
128
 
13
- ### Major Changes
129
+ ## 1.77.0
14
130
 
15
- - be6c09d: ZERO-3114: Create beta version.
131
+ ## 1.76.0
16
132
 
17
133
  ## 1.75.0
18
134
 
package/README.md CHANGED
@@ -1,10 +1,13 @@
1
1
  # @akinon/pz-checkout-gift-package
2
2
 
3
- ### Install the npm package
3
+ ## Installation
4
+
5
+ You can use the following command to install the extension with the latest plugins:
4
6
 
5
7
  ```bash
6
- # For latest version
7
- yarn add @akinon/pz-checkout-gift-package
8
+
9
+ npx @akinon/projectzero@latest --plugins
10
+
8
11
  ```
9
12
 
10
13
  ### Props
@@ -15,3 +18,124 @@ yarn add @akinon/pz-checkout-gift-package
15
18
  | modalClassName | `string` | This prop is used to customize the overall style of the modal. |
16
19
  | modalTitle | `string` | This prop sets the title of the modal. |
17
20
  | modalContentClassName | `string` | This prop is used to customize the style of the modal content. |
21
+ | maxNoteLength? | `number` | Sets the maximum number of characters allowed for the gift note. If not provided, a default value is used. |
22
+ | customUIRender | `(props: { hasGiftPack: boolean; hasNote: { state: boolean }; note: string; onAddGiftPack: () => void; onRemoveGiftPack: () => void; onAddNote: () => void; formContent: React.ReactNode; translations: Record<string, string> }) => React.ReactNode` | A custom render function to fully override the default gift pack UI. This gives complete control over the display and interactions. |
23
+ | customGiftNoteFormUIRender | `(props: { register: any; errors: any; note: string; textAreaCount: number; onSubmit: (data: any) => void; removeNote: () => void; handleSubmit: any; translations: Record<string, string> }) => React.ReactNode` | A render function that replaces the default gift note form UI. Useful for providing a customized textarea, button layout, or validation display. |
24
+
25
+ ### Default Usage
26
+
27
+ ```js
28
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module';
29
+
30
+ <PluginModule
31
+ component={Component.CheckoutGiftPack}
32
+ props={{
33
+ className: 'flex flex-col w-full mb-4 border border-solid border-gray-400'
34
+ }}
35
+ />;
36
+ ```
37
+
38
+ ### Customized sample code
39
+
40
+ ```js
41
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module';
42
+
43
+ <PluginModule
44
+ component={Component.CheckoutGiftPack}
45
+ props={{
46
+ className: 'flex flex-col w-full mb-4 border border-solid border-gray-400',
47
+ translations: {
48
+ addGiftPackText: 'Add Gift Pack',
49
+ giftPackAdded: 'Gift Pack Added',
50
+ removeGiftPackText: 'Remove Gift Pack',
51
+ informationText: 'This order will be gift packaged*',
52
+ updateNote: 'Update Note',
53
+ removeGiftNoteText: 'Remove Gift Note',
54
+ charactersLength: 'characters left',
55
+ placeholderInput:
56
+ 'You can leave empty this area. However it will be a gift package without note.',
57
+ accordionTitle: 'Gift Note',
58
+ warningMessage:
59
+ 'Make sure that this field is not longer than the character limit.',
60
+ save: 'SAVE',
61
+ close: 'Close'
62
+ },
63
+ customUIRender: ({
64
+ hasGiftPack,
65
+ hasNote,
66
+ note,
67
+ onAddGiftPack,
68
+ onRemoveGiftPack,
69
+ onAddNote,
70
+ formContent,
71
+ translations
72
+ }) => (
73
+ <div className="custom-gift-pack">
74
+ {hasGiftPack ? (
75
+ <div className="gift-pack-added p-4 border border-gray-200">
76
+ <div className="flex justify-between items-center mb-4">
77
+ <span className="font-medium">{translations.giftPackAdded}</span>
78
+ <button
79
+ onClick={onRemoveGiftPack}
80
+ className="text-red-500 hover:text-red-700"
81
+ >
82
+ {translations.removeGiftPackText}
83
+ </button>
84
+ </div>
85
+
86
+ <div className="gift-note-info bg-gray-50 p-3 rounded">
87
+ <span className="text-sm text-gray-600">
88
+ {translations.informationText}
89
+ </span>
90
+ {note && <p className="mt-2 text-sm">{note}</p>}
91
+ <button
92
+ onClick={onAddNote}
93
+ className="mt-2 text-blue-500 hover:text-blue-700"
94
+ >
95
+ {translations.updateNote}
96
+ </button>
97
+ </div>
98
+
99
+ {hasNote.state && <div className="mt-4">{formContent}</div>}
100
+ </div>
101
+ ) : (
102
+ <button
103
+ onClick={onAddGiftPack}
104
+ className="w-full p-2 text-center border border-gray-200 hover:bg-gray-50"
105
+ >
106
+ {translations.addGiftPackText}
107
+ </button>
108
+ )}
109
+ </div>
110
+ ),
111
+ customGiftNoteFormUIRender: ({
112
+ register,
113
+ errors,
114
+ note,
115
+ textAreaCount,
116
+ onSubmit,
117
+ removeNote,
118
+ handleSubmit,
119
+ translations: formTranslations
120
+ }) => (
121
+ <form onSubmit={handleSubmit(onSubmit)}>
122
+ <textarea
123
+ {...register('message')}
124
+ value={note}
125
+ placeholder={formTranslations.placeholderInput}
126
+ />
127
+ {errors.message && <span>{errors.message.message}</span>}
128
+ <div className="character-count">
129
+ {textAreaCount}/160 {formTranslations.charactersLength}
130
+ </div>
131
+ <div className="buttons">
132
+ <button type="button" onClick={removeNote}>
133
+ {formTranslations.removeGiftNoteText}
134
+ </button>
135
+ <button type="submit">{formTranslations.save}</button>
136
+ </div>
137
+ </form>
138
+ )
139
+ }}
140
+ />;
141
+ ```
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@akinon/pz-checkout-gift-pack",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.21",
4
4
  "license": "MIT",
5
5
  "main": "src/index.tsx",
6
6
  "peerDependencies": {
7
- "react": "^19.0.0",
8
- "react-dom": "^19.0.0"
7
+ "react": "^18.0.0 || ^19.0.0",
8
+ "react-dom": "^18.0.0 || ^19.0.0"
9
9
  },
10
10
  "devDependencies": {
11
- "@types/node": "^22.10.2",
12
- "@types/react": "^19.0.2",
13
- "@types/react-dom": "^19.0.2",
14
- "react": "^19.0.0",
15
- "react-dom": "^19.0.0",
16
- "typescript": "^5.7.2"
11
+ "@types/node": "^18.7.8",
12
+ "@types/react": "^18.0.17",
13
+ "@types/react-dom": "^18.0.6",
14
+ "react": "19.2.5",
15
+ "react-dom": "19.2.5",
16
+ "typescript": "^4.7.4"
17
17
  }
18
18
  }
package/src/index.tsx CHANGED
@@ -1,37 +1,76 @@
1
+ import React from 'react';
1
2
  import { useAppSelector } from '@akinon/next/redux/hooks';
2
3
  import { yupResolver } from '@hookform/resolvers/yup';
3
4
  import clsx from 'clsx';
4
5
  import { twMerge } from 'tailwind-merge';
5
6
  import { Accordion, Button, Icon, Modal } from '@akinon/next/components';
6
7
  import { useEffect, useState } from 'react';
7
- import { SubmitHandler, useForm } from 'react-hook-form';
8
+ import {
9
+ SubmitHandler,
10
+ useForm,
11
+ UseFormRegister,
12
+ FieldErrors,
13
+ UseFormHandleSubmit
14
+ } from 'react-hook-form';
8
15
  import { RootState } from 'redux/store';
9
16
  import * as yup from 'yup';
10
17
  import { useAddGiftPackMutation, useRemoveGiftPackMutation } from './endpoints';
11
18
 
19
+ const DEFAULT_NOTE = 'NO-GIFT-MESSAGE';
20
+
21
+ const defaultTranslations = {
22
+ addGiftPackText: 'Add Gift Pack',
23
+ giftPackAdded: 'Gift Pack Added',
24
+ removeGiftPackText: 'Remove Gift Pack',
25
+ informationText: 'This order will be gift packaged*',
26
+ updateNote: 'Update Note',
27
+ removeGiftNoteText: 'Remove Gift Note',
28
+ charactersLength: 'characters left',
29
+ placeholderInput:
30
+ 'You can leave empty this area. However it will be a gift package without note.',
31
+ accordionTitle: 'Gift Note',
32
+ warningMessage:
33
+ 'Make sure that this field is not longer than the character limit.',
34
+ save: 'SAVE',
35
+ close: 'Close'
36
+ };
37
+
12
38
  interface GiftPackForm {
13
39
  message: string;
14
40
  }
15
41
 
42
+ interface CustomUIRenderParams {
43
+ hasGiftPack: boolean;
44
+ hasNote: { title: string; state: boolean };
45
+ note: string;
46
+ textAreaCount: number;
47
+ onAddGiftPack: () => void;
48
+ onRemoveGiftPack: () => void;
49
+ onAddNote: () => void;
50
+ onRemoveNote: () => void;
51
+ formContent: React.ReactNode;
52
+ translations: typeof defaultTranslations;
53
+ }
54
+
16
55
  interface CheckoutGiftPackProps {
17
56
  className?: string;
18
57
  useModal?: boolean;
19
58
  modalClassName?: string;
20
59
  modalTitle?: string;
60
+ maxNoteLength?: number;
21
61
  modalContentClassName?: string;
22
- translations: {
23
- addGiftPackText: string;
24
- giftPackAdded: string;
25
- removeGiftPackText: string;
26
- informationText: string;
27
- updateNote: string;
28
- removeGiftNoteText: string;
29
- charactersLength: string;
30
- placeholderInput: string;
31
- accordionTitle: string;
32
- warningMessage: string;
33
- save: string;
34
- };
62
+ translations?: Partial<typeof defaultTranslations>;
63
+ customUIRender?: (params: CustomUIRenderParams) => React.ReactNode;
64
+ customGiftNoteFormUIRender?: (params: {
65
+ register: UseFormRegister<GiftPackForm>;
66
+ errors: FieldErrors<GiftPackForm>;
67
+ note: string;
68
+ textAreaCount: number;
69
+ onSubmit: () => void;
70
+ removeNote: () => void;
71
+ handleSubmit: UseFormHandleSubmit<GiftPackForm>;
72
+ translations: typeof defaultTranslations;
73
+ }) => React.ReactNode;
35
74
  }
36
75
 
37
76
  export const CheckoutGiftPack = ({
@@ -40,49 +79,32 @@ export const CheckoutGiftPack = ({
40
79
  modalClassName,
41
80
  modalContentClassName,
42
81
  modalTitle = 'Gift Note',
43
- translations
82
+ maxNoteLength = 160,
83
+ translations,
84
+ customUIRender,
85
+ customGiftNoteFormUIRender
44
86
  }: CheckoutGiftPackProps) => {
45
87
  const [isNoteFormOpen, setIsNoteFormOpen] = useState(false);
88
+ const [hasNote, setHasNote] = useState({
89
+ title: translations?.updateNote || defaultTranslations.updateNote,
90
+ state: false
91
+ });
92
+ const [textAreaCount, setTextAreaCount] = useState(0);
93
+ const [note, setNote] = useState('');
46
94
 
47
95
  const { preOrder, hasGiftBox } = useAppSelector(
48
96
  (state: RootState) => state.checkout
49
97
  );
50
98
 
51
- const defaultTranslations = {
52
- addGiftPackText: 'Add Gift Pack',
53
- giftPackAdded: 'Gift Pack Added',
54
- removeGiftPackText: 'Remove Gift Pack',
55
- informationText: 'This order will be gift packaged*',
56
- updateNote: 'Update Note',
57
- removeGiftNoteText: 'Remove Gift Note',
58
- charactersLength: 'characters left',
59
- placeholderInput:
60
- 'You can leave empty this area. However it will be a gift package without note.',
61
- accordionTitle: 'Gift Note',
62
- warningMessage: 'Ensure this field has no more than 160 characters.',
63
- save: 'SAVE'
64
- };
65
-
66
- const _translations = {
67
- ...defaultTranslations,
68
- ...translations
69
- };
99
+ const _translations = { ...defaultTranslations, ...translations };
70
100
 
71
101
  const giftPackFormSchema = () =>
72
102
  yup.object().shape({
73
- message: yup.string().max(160, _translations.warningMessage)
103
+ message: yup.string().max(maxNoteLength, _translations.warningMessage)
74
104
  });
75
105
 
76
- if (!hasGiftBox) {
77
- console.warn(
78
- 'Gift box for checkout is disabled. Please enable it from Omnitron.'
79
- );
80
- return null;
81
- }
82
-
83
106
  const [addGiftPack] = useAddGiftPackMutation();
84
107
  const [removeGiftPack] = useRemoveGiftPackMutation();
85
- const DEFAULT_NOTE = 'NO-GIFT-MESSAGE';
86
108
 
87
109
  const {
88
110
  register,
@@ -92,89 +114,147 @@ export const CheckoutGiftPack = ({
92
114
  setValue,
93
115
  formState: { errors }
94
116
  } = useForm<GiftPackForm>({
95
- resolver: yupResolver(giftPackFormSchema())
117
+ resolver: yupResolver(giftPackFormSchema()) as any
96
118
  });
97
119
 
98
- const subscribeNote = watch('message');
99
- const noteLength = subscribeNote?.length ?? 0;
100
- const giftBox = preOrder?.gift_box;
120
+ useEffect(() => {
121
+ if (preOrder?.gift_box?.note !== DEFAULT_NOTE) {
122
+ setValue('message', preOrder?.gift_box?.note);
123
+ setNote(preOrder?.gift_box?.note);
124
+ setTextAreaCount(preOrder?.gift_box?.note?.length || 0);
125
+ }
126
+ }, [preOrder?.gift_box]);
127
+
128
+ useEffect(() => {
129
+ const subscription = watch(({ message }) => {
130
+ setNote(message);
131
+ setTextAreaCount(message?.length || 0);
132
+ });
133
+
134
+ return () => subscription.unsubscribe();
135
+ }, [watch]);
136
+
137
+ if (!hasGiftBox) {
138
+ console.warn(
139
+ 'Gift box for checkout is disabled. Please enable it from Omnitron.'
140
+ );
141
+ return null;
142
+ }
101
143
 
102
144
  const onSubmit: SubmitHandler<GiftPackForm> = async (data) => {
103
145
  await addGiftPack({
104
- note: noteLength > 0 ? data.message : DEFAULT_NOTE
146
+ note: textAreaCount > 0 ? data.message : DEFAULT_NOTE
105
147
  }).unwrap();
148
+
106
149
  setIsNoteFormOpen(false);
150
+ setHasNote((prevstate) => ({ ...prevstate, state: false }));
107
151
  };
108
152
 
109
153
  const removeGiftNote = async () => {
110
154
  await addGiftPack({ note: DEFAULT_NOTE }).unwrap();
111
155
  resetField('message');
156
+ setNote('');
157
+ setTextAreaCount(0);
112
158
  };
113
159
 
114
- const formContent = (
115
- <form
116
- className={clsx({
117
- hidden: !isNoteFormOpen
118
- })}
119
- onSubmit={handleSubmit(onSubmit)}
120
- >
160
+ const formContent = customGiftNoteFormUIRender ? (
161
+ customGiftNoteFormUIRender({
162
+ register,
163
+ errors,
164
+ note,
165
+ textAreaCount,
166
+ onSubmit: () => handleSubmit(onSubmit)(),
167
+ removeNote: removeGiftNote,
168
+ handleSubmit,
169
+ translations: _translations
170
+ })
171
+ ) : (
172
+ <form onSubmit={handleSubmit(onSubmit)}>
173
+ {!useModal && (
174
+ <div className="flex justify-between mb-3">
175
+ <span className="text-[0.75rem] font-bold">
176
+ {_translations.accordionTitle}
177
+ </span>
178
+ <Button
179
+ appearance="ghost"
180
+ className="text-[#000000] cursor-pointer border-0 px-0 py-0 text-[0.75rem] select-none underline h-auto hover:bg-transparent hover:text-[#000000]"
181
+ onClick={(e) => {
182
+ e.preventDefault();
183
+ setHasNote((prevstate) => ({ ...prevstate, state: false }));
184
+ }}
185
+ >
186
+ {_translations.close}
187
+ </Button>
188
+ </div>
189
+ )}
121
190
  <textarea
122
191
  className={clsx(
123
192
  'w-full border border-solid p-4 placeholder:text-[0.75rem] placeholder:text-[#9c9d9d] outline-none text-[0.75rem]',
124
- noteLength > 160 ? 'border-[#e85150]' : 'border-[#7b9d75]'
193
+ textAreaCount > maxNoteLength
194
+ ? 'border-[#e85150]'
195
+ : 'border-[#7b9d75]'
125
196
  )}
126
197
  rows={4}
198
+ value={note !== DEFAULT_NOTE ? note : ' '}
127
199
  placeholder={_translations.placeholderInput}
128
200
  {...register('message')}
201
+ data-testid="gift-note-input"
129
202
  />
130
203
  {errors.message && (
131
- <span className="text-sm text-[#d72a04] text-[0.875rem]">
132
- {errors.message.message}
204
+ <span
205
+ className="text-sm text-[#d72a04] text-[0.875rem]"
206
+ data-testid="error-message"
207
+ >
208
+ {String(errors.message.message)}
133
209
  </span>
134
210
  )}
135
- <div className="text-[0.75rem]">
136
- {noteLength} / 160 {_translations.charactersLength}
137
- </div>
138
- <div className="flex justify-end items-end gap-2">
139
- {giftBox?.note !== DEFAULT_NOTE && (
211
+ <div className="flex justify-between items-center mt-2">
212
+ <span
213
+ className={clsx(
214
+ 'text-[0.75rem]',
215
+ textAreaCount > maxNoteLength ? 'text-[#e85150]' : 'text-[#82a27c]'
216
+ )}
217
+ data-testid="characters-length"
218
+ >
219
+ {textAreaCount}/{maxNoteLength} {_translations.charactersLength}
220
+ </span>
221
+ <div className="flex items-center gap-3">
222
+ {preOrder?.gift_box?.note !== DEFAULT_NOTE && (
223
+ <Button
224
+ appearance="ghost"
225
+ className="text-[0.75rem] underline cursor-pointer border-0 px-0 py-0 h-auto hover:bg-transparent hover:text-[#e95151]"
226
+ onClick={removeGiftNote}
227
+ data-testid="remove-gift-note-button"
228
+ >
229
+ {_translations.removeGiftNoteText}
230
+ </Button>
231
+ )}
140
232
  <Button
141
- appearance="ghost"
142
- className="text-[0.75rem] underline cursor-pointer border-0 px-0 py-0 h-auto hover:bg-transparent hover:text-[#e95151]"
143
- onClick={removeGiftNote}
233
+ type="submit"
234
+ className="w-[7rem] h-[1.75rem] font-[0.75rem] uppercase"
235
+ data-testid="save-gift-pack-button"
144
236
  >
145
- {_translations.removeGiftNoteText}
237
+ {_translations.save}
146
238
  </Button>
147
- )}
148
- <Button
149
- type="submit"
150
- className="w-[7rem] h-[1.75rem] font-[0.75rem] uppercase"
151
- appearance="outlined"
152
- >
153
- {_translations.save}
154
- </Button>
239
+ </div>
155
240
  </div>
156
241
  </form>
157
242
  );
158
243
 
159
- useEffect(() => {
160
- if (giftBox?.note !== DEFAULT_NOTE) {
161
- setValue('message', giftBox?.note);
162
- }
163
- }, [giftBox]);
164
-
165
- return (
244
+ const defaultRender = () => (
166
245
  <div className={className}>
167
246
  <div className="flex justify-between items-center py-2 px-5">
168
247
  <div className="flex gap-2 items-center">
169
248
  <Icon name="giftbox" size={16} className="fill-[#000000]" />
170
249
  <span className="text-[1rem] font-light">
171
- {giftBox ? (
250
+ {preOrder?.gift_box ? (
172
251
  _translations.giftPackAdded
173
252
  ) : (
174
253
  <Button
175
254
  appearance="ghost"
176
255
  className="cursor-pointer border-0 px-0 py-0 text-[1rem] font-light h-auto hover:bg-transparent hover:text-[#000000]"
177
256
  onClick={() => addGiftPack({ note: DEFAULT_NOTE })}
257
+ data-testid="add-gift-pack-button"
178
258
  >
179
259
  {_translations.addGiftPackText}
180
260
  </Button>
@@ -186,13 +266,15 @@ export const CheckoutGiftPack = ({
186
266
  className={clsx(
187
267
  'text-[0.75rem] underline cursor-pointer border-0 px-0 py-0 h-auto hover:bg-transparent hover:text-[#e95151]',
188
268
  {
189
- hidden: !giftBox
269
+ hidden: !preOrder?.gift_box
190
270
  }
191
271
  )}
192
272
  onClick={async () => {
193
273
  await removeGiftPack().unwrap();
194
274
  setIsNoteFormOpen(false);
275
+ setHasNote((prevstate) => ({ ...prevstate, state: false }));
195
276
  }}
277
+ data-testid="remove-gift-pack-button"
196
278
  >
197
279
  {_translations.removeGiftPackText}
198
280
  </Button>
@@ -201,20 +283,30 @@ export const CheckoutGiftPack = ({
201
283
  className={clsx(
202
284
  'flex flex-col gap-2 bg-[#f7f7f7] text-[#58585a] text-[0.875rem] py-2 px-5',
203
285
  {
204
- hidden: !giftBox
286
+ hidden: !preOrder?.gift_box
205
287
  }
206
288
  )}
207
289
  >
208
290
  <div className="flex justify-between items-center">
209
- <span>{_translations.informationText}</span>
291
+ <span data-testid="information-text">
292
+ {_translations.informationText}
293
+ </span>
210
294
  <Icon name="giftbox" size={14} className="fill-[#58585a]" />
211
295
  </div>
212
296
  <div className="flex justify-between items-center gap-8">
213
- <span>{giftBox?.note !== DEFAULT_NOTE ? giftBox?.note : ''}</span>
297
+ <span data-testid="saved-gift-note">
298
+ {preOrder?.gift_box?.note !== DEFAULT_NOTE
299
+ ? preOrder?.gift_box?.note
300
+ : ''}
301
+ </span>
214
302
  <Button
215
303
  appearance="ghost"
216
304
  className="underline cursor-pointer flex-shrink-0 border-0 px-0 py-0 h-auto hover:bg-transparent hover:text-[#000000]"
217
- onClick={() => setIsNoteFormOpen(true)}
305
+ onClick={() => {
306
+ setIsNoteFormOpen(true);
307
+ setHasNote((prevstate) => ({ ...prevstate, state: true }));
308
+ }}
309
+ data-testid="update-gift-pack-button"
218
310
  >
219
311
  {_translations.updateNote}
220
312
  </Button>
@@ -229,15 +321,17 @@ export const CheckoutGiftPack = ({
229
321
  'w-full sm:w-[28rem] max-h-[90vh] overflow-y-auto',
230
322
  modalClassName
231
323
  )}
232
- open={isNoteFormOpen}
233
- setOpen={setIsNoteFormOpen}
324
+ open={hasNote.state}
325
+ setOpen={(state) =>
326
+ setHasNote((prevstate) => ({ ...prevstate, state }))
327
+ }
234
328
  >
235
329
  <div className={twMerge('px-6 py-4', modalContentClassName)}>
236
330
  {formContent}
237
331
  </div>
238
332
  </Modal>
239
333
  ) : (
240
- <div className={clsx('py-2 px-5', { hidden: !isNoteFormOpen })}>
334
+ <div className={clsx('py-2 px-5', { hidden: !hasNote.state })}>
241
335
  <Accordion
242
336
  title={_translations.accordionTitle}
243
337
  titleClassName="text-[0.75rem]"
@@ -249,4 +343,22 @@ export const CheckoutGiftPack = ({
249
343
  )}
250
344
  </div>
251
345
  );
346
+
347
+ return customUIRender
348
+ ? customUIRender({
349
+ hasGiftPack: !!preOrder?.gift_box,
350
+ hasNote,
351
+ note,
352
+ textAreaCount,
353
+ onAddGiftPack: () => addGiftPack({ note: DEFAULT_NOTE }),
354
+ onRemoveGiftPack: () => removeGiftPack().unwrap(),
355
+ onAddNote: () => {
356
+ setIsNoteFormOpen(true);
357
+ setHasNote((prevstate) => ({ ...prevstate, state: true }));
358
+ },
359
+ onRemoveNote: removeGiftNote,
360
+ formContent,
361
+ translations: _translations
362
+ })
363
+ : defaultRender();
252
364
  };