@4alldigital/foundation-ui--core 3.10.1 → 3.11.0

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 (129) hide show
  1. package/package.json +2 -2
  2. package/src/components/Accordion/Accordion.tsx +3 -1
  3. package/src/components/AddressForm/AddressForm.tsx +4 -1
  4. package/src/components/AnimationCounter/AnimationCounter.tsx +3 -1
  5. package/src/components/AnimationSet1/AnimationSet1.tsx +3 -1
  6. package/src/components/Authenticator/Authenticator.tsx +2 -0
  7. package/src/components/Avatar/Avatar.tsx +2 -0
  8. package/src/components/Banner/Banner.tsx +1 -1
  9. package/src/components/Blockquote/Blockquote.tsx +1 -1
  10. package/src/components/Button/Button.tsx +1 -2
  11. package/src/components/ButtonDeprecated/Button.tsx +3 -1
  12. package/src/components/ButtonGroup/ButtonGroup.tsx +1 -1
  13. package/src/components/Card/Card.types.ts +3 -3
  14. package/src/components/Card/types/CardBasic/CardBasic.tsx +44 -133
  15. package/src/components/Card/types/CardData/CardData.tsx +3 -1
  16. package/src/components/Card/types/CardHorz/CardHorz.tsx +3 -1
  17. package/src/components/Card/types/CardIcon/CardIcon.tsx +3 -1
  18. package/src/components/Card/types/CardMedia/CardMedia.tsx +3 -1
  19. package/src/components/Card/types/CardVideo/CardVideo.tsx +3 -1
  20. package/src/components/Carousel/Carousel.tsx +3 -1
  21. package/src/components/Cart/Cart.tsx +15 -5
  22. package/src/components/Checkbox/Checkbox.tsx +4 -4
  23. package/src/components/Chip/Chip.tsx +3 -1
  24. package/src/components/Collapsible/Collapsible.tsx +1 -1
  25. package/src/components/Container/Container.tsx +1 -1
  26. package/src/components/ContentPageLayout/ContentPageLayout.tsx +1 -1
  27. package/src/components/ContentRowsLayout/ContentRowsLayout.tsx +1 -1
  28. package/src/components/Copy/Copy.tsx +1 -1
  29. package/src/components/DisplayHeading/DisplayHeading.tsx +1 -1
  30. package/src/components/ErrorBoundary/ErrorBoundary.tsx +44 -0
  31. package/src/components/ErrorBoundary/ErrorBoundary.types.ts +12 -0
  32. package/src/components/ErrorBoundary/index.ts +2 -0
  33. package/src/components/FileUpload/FileUpload.tsx +3 -1
  34. package/src/components/Footer/Footer.tsx +3 -1
  35. package/src/components/Form/Form.tsx +10 -4
  36. package/src/components/Form/Form.types.ts +4 -3
  37. package/src/components/FormField/FormField.tsx +12 -10
  38. package/src/components/FormSelect/FormSelect.tsx +2 -2
  39. package/src/components/FormSelect/FormSelect.types.ts +5 -11
  40. package/src/components/FullScreenVideoModal/FullScreenVideoModal.tsx +3 -1
  41. package/src/components/Header/Header.tsx +27 -9
  42. package/src/components/Heading/Heading.tsx +12 -19
  43. package/src/components/Heading/Heading.types.ts +1 -1
  44. package/src/components/Hero/Hero.tsx +1 -1
  45. package/src/components/Hr/Hr.tsx +1 -1
  46. package/src/components/HtmlContent/HtmlContent.tsx +1 -1
  47. package/src/components/Image/Image.tsx +1 -1
  48. package/src/components/Label/Label.tsx +1 -1
  49. package/src/components/Link/Link.tsx +3 -1
  50. package/src/components/List/List.tsx +1 -1
  51. package/src/components/ListItem/ListItem.tsx +3 -1
  52. package/src/components/Loader/Loader.tsx +1 -1
  53. package/src/components/Logo/Logo.tsx +1 -1
  54. package/src/components/Menu/Menu.tsx +1 -1
  55. package/src/components/Notice/Notice.tsx +3 -1
  56. package/src/components/OTPInput/OTPInput.tsx +4 -2
  57. package/src/components/PanelCards/PanelCards.tsx +3 -1
  58. package/src/components/PanelCustom/PanelCustom.tsx +1 -1
  59. package/src/components/PanelFeature/PanelFeature.tsx +1 -1
  60. package/src/components/PanelHero/PanelHero.tsx +3 -1
  61. package/src/components/PanelIntro/PanelIntro.tsx +3 -1
  62. package/src/components/PanelProduct/PanelProduct.tsx +1 -1
  63. package/src/components/PanelScroller/PanelScroller.tsx +3 -1
  64. package/src/components/PanelVideo/PanelVideo.tsx +3 -1
  65. package/src/components/ProductCard/ProductCard.tsx +2 -0
  66. package/src/components/ProductDetail/ProductDetail.tsx +2 -0
  67. package/src/components/Radio/Radio.tsx +1 -1
  68. package/src/components/Schedule/Schedule.tsx +2 -2
  69. package/src/components/ScheduleItem/ScheduleItem.tsx +2 -0
  70. package/src/components/Screen/Screen.tsx +1 -1
  71. package/src/components/ShadcnCarousel/ShadcnCarousel.tsx +2 -0
  72. package/src/components/Share/Share.tsx +2 -0
  73. package/src/components/SubscriptionManager/SubscriptionManager.tsx +3 -1
  74. package/src/components/Switch/Switch.tsx +1 -1
  75. package/src/components/Tabs/Tabs.tsx +3 -1
  76. package/src/components/TextInput/TextInput.tsx +6 -3
  77. package/src/components/TextInput/TextInput.types.ts +4 -55
  78. package/src/components/VariantSelector/VariantSelector.tsx +2 -0
  79. package/src/components/Video/Video.tsx +14 -13
  80. package/src/components/VisitUs/VisitUs.tsx +2 -0
  81. package/src/components/index.ts +7 -0
  82. package/src/context/Amplify/index.tsx +2 -1
  83. package/src/context/App/index.tsx +2 -0
  84. package/src/context/Cart/index.tsx +2 -0
  85. package/src/context/ExampleContext/index.tsx +3 -1
  86. package/src/context/Theme/index.tsx +2 -0
  87. package/src/context/index.ts +2 -0
  88. package/src/features/Search/Search.tsx +2 -0
  89. package/src/features/Search/config/config-helper.ts +2 -0
  90. package/src/features/Search/views/Layout/Layout.tsx +1 -1
  91. package/src/features/Search/views/MultiCheckboxFacet/MultiCheckboxFacet.tsx +3 -1
  92. package/src/features/Search/views/Paging/Paging.tsx +3 -1
  93. package/src/features/Search/views/PagingInfo/PagingInfo.tsx +1 -1
  94. package/src/features/Search/views/Result/Result.tsx +1 -1
  95. package/src/features/Search/views/Results/Results.tsx +1 -1
  96. package/src/features/Search/views/ResultsPerPage/ResultsPerPage.tsx +3 -1
  97. package/src/features/Search/views/SearchBox/SearchBox.tsx +3 -1
  98. package/src/features/Search/views/SingleLinksFacet/SingleLinksFacet.tsx +3 -1
  99. package/src/features/Search/views/SingleSelectFacet/SingleSelectFacet.tsx +3 -1
  100. package/src/features/Search/views/Sorting/Sorting.tsx +3 -1
  101. package/src/features/index.ts +2 -0
  102. package/src/forms/ForgotPasswordForm/ForgotPasswordForm.types.ts +1 -1
  103. package/src/forms/LoginForm/LoginForm.stories.tsx +1 -1
  104. package/src/forms/LoginForm/LoginForm.tsx +7 -5
  105. package/src/forms/LoginForm/LoginForm.types.ts +7 -1
  106. package/src/forms/PromoCodeForm/PromoCodeForm.types.ts +1 -1
  107. package/src/forms/RegisterForm/RegisterForm.stories.tsx +1 -1
  108. package/src/forms/RegisterForm/RegisterForm.tsx +7 -10
  109. package/src/forms/RegisterForm/RegisterForm.types.ts +4 -2
  110. package/src/forms/ResetPasswordAuthForm/ResetPasswordAuthForm.types.ts +1 -1
  111. package/src/forms/ResetPasswordForm/ResetPasswordForm.types.ts +1 -1
  112. package/src/forms/index.ts +2 -0
  113. package/src/hooks/index.ts +2 -0
  114. package/src/hooks/useLanguage.ts +2 -0
  115. package/src/hooks/useTheme.ts +3 -1
  116. package/src/index.ts +2 -0
  117. package/src/templates/AccountScreen/AccountScreen.tsx +2 -0
  118. package/src/templates/ChallengeScreen/ChallengeScreen.tsx +2 -0
  119. package/src/templates/MenuScreen/MenuScreen.tsx +2 -0
  120. package/src/templates/OrderDetailScreen/OrderDetailScreen.tsx +2 -0
  121. package/src/templates/OrdersHistoryScreen/OrdersHistoryScreen.tsx +2 -0
  122. package/src/templates/PasswordResetScreen/PasswordResetScreen.types.ts +1 -1
  123. package/src/templates/ProductListScreen/ProductListScreen.tsx +2 -0
  124. package/src/templates/PurchaseConfirmationScreen/PurchaseConfirmationScreen.tsx +2 -0
  125. package/src/templates/SubscriptionScreen/SubscriptionScreen.tsx +2 -0
  126. package/src/templates/WorkoutScreen/WorkoutScreen.tsx +3 -1
  127. package/src/templates/index.ts +2 -0
  128. package/src/utils/htmlParser/index.tsx +11 -5
  129. package/src/utils/index.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4alldigital/foundation-ui--core",
3
- "version": "3.10.1",
3
+ "version": "3.11.0",
4
4
  "description": "Foundation UI Core Component Library (source distribution)",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -35,5 +35,5 @@
35
35
  },
36
36
  "author": "Joe Mewes",
37
37
  "license": "MIT",
38
- "gitHead": "b113a4c018016b27fd3cdfa1d991b9aec006f086"
38
+ "gitHead": "f241bcb8eb542b8150a62af69d513687f870dd2b"
39
39
  }
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import { useState } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './Accordion.types';
4
6
  import Icon from '../Icon';
5
7
  import Copy from '../Copy';
@@ -1,8 +1,11 @@
1
+ "use client";
2
+
1
3
  import * as React from 'react';
2
4
  import { useState, useEffect } from 'react';
3
5
  import { cn } from '../../utils';
4
6
  import { AddressFormProps, ShippingAddress, AddressErrors } from './AddressForm.types';
5
7
  import TextInput from '../TextInput';
8
+ import { InputType } from '../TextInput/TextInput.types';
6
9
  import FormSelect from '../FormSelect';
7
10
 
8
11
  // Common countries
@@ -205,7 +208,7 @@ export const AddressForm = React.forwardRef<HTMLDivElement, AddressFormProps>(
205
208
  {/* Phone */}
206
209
  <TextInput
207
210
  id="shipping-phone"
208
- type="tel"
211
+ type={InputType.TEL}
209
212
  placeholder="Phone Number (optional)"
210
213
  value={address.phone || ''}
211
214
  onChange={(e) => handleChange('phone', e.target.value)}
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import { useRef } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './AnimationCounter.types';
4
6
  import { animate, useInView, useIsomorphicLayoutEffect } from 'framer-motion';
5
7
 
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { motion } from 'framer-motion';
4
6
  import { Props } from './AnimationSet1.types';
5
7
  import Image from '../Image';
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useState } from 'react';
2
4
  import { Props } from './Authenticator.types';
3
5
  import Tabs from '../Tabs';
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useEffect, useState } from 'react';
2
4
  import { Props } from './Avatar.types';
3
5
  import Image from '../Image';
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { Props } from './Banner.types';
3
3
  import Heading from '../Heading';
4
4
  import { HEADING_TAGS } from '../Heading/Heading.types';
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { Props } from './Blockquote.types';
3
3
  import Icon from '../Icon';
4
4
 
@@ -7,7 +7,7 @@ import Loader from '../Loader';
7
7
  import { BTN_VARIANTS, BTN_SIZES, type ButtonProps } from './Button.types';
8
8
 
9
9
  const buttonVariants = cva(
10
- 'inline-flex items-center cursor-pointer justify-center whitespace-nowrap rounded-sm text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
10
+ 'inline-flex items-center gap-2 cursor-pointer justify-center whitespace-nowrap rounded-sm text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
11
11
  {
12
12
  variants: {
13
13
  variant: {
@@ -248,7 +248,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
248
248
  type={type as 'button' | 'submit' | 'reset'}
249
249
  disabled={disabled}
250
250
  aria-label={ariaLabel}
251
- role="button"
252
251
  {...props}
253
252
  >
254
253
  {renderContent()}
@@ -1,4 +1,6 @@
1
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import { clsx as cx } from 'clsx';
2
4
  import { twMerge } from 'tailwind-merge';
3
5
  import { BTN_SIZES, BTN_TYPES, BTN_VARIANTS, Props } from './Button.types';
4
6
  import Icon from '../Icon';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './ButtonGroup.types';
4
4
  import Button from '../Button/Button';
5
5
 
@@ -20,10 +20,10 @@ export interface ImageData {
20
20
  aspectRatio?: 'aspect-[16/10]' | 'aspect-[16/9]' | 'aspect-[4/3]' | 'aspect-[1/1]' | 'aspect-[3/4]' | 'aspect-[9/16]';
21
21
  }
22
22
 
23
- type ActionItem = {
23
+ export type ActionItem = {
24
24
  count?: number;
25
25
  isActive: boolean;
26
- onClick: (event: any) => void;
26
+ onClick: (data: { id: string }) => void;
27
27
  };
28
28
 
29
29
  export interface Actions {
@@ -53,7 +53,7 @@ export interface CardData {
53
53
  moreTitle?: string;
54
54
  metaData?: string[];
55
55
  actions?: Actions;
56
- onClick?: (data: any) => void;
56
+ onClick?: (data: unknown) => void;
57
57
  playCallback?: (event: MouseEvent<HTMLButtonElement>) => void;
58
58
  isMuted?: boolean;
59
59
  icon?: string;
@@ -1,5 +1,7 @@
1
- import React, { MouseEvent, useContext } from 'react';
2
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import React, { MouseEvent, useCallback, useContext } from 'react';
4
+ import { clsx as cx } from 'clsx';
3
5
  import Image from '../../../Image';
4
6
  import { Props } from './CardBasic.types';
5
7
  import Heading from '../../../Heading';
@@ -8,6 +10,7 @@ import Copy from '../../../Copy';
8
10
  import { COPY_TAG } from '../../../Copy/Copy.types';
9
11
  import Button from '../../../Button';
10
12
  import { BTN_SIZES, BTN_VARIANTS } from '../../../Button/Button.types';
13
+ import { type ActionItem } from '../../Card.types';
11
14
  import { animateClasses, cardWrapperClasses, clickableCardWrapperClasses } from '../../constants';
12
15
  import { twMerge } from 'tailwind-merge';
13
16
  import HtmlContent from '../../../HtmlContent';
@@ -30,6 +33,17 @@ const CardBasic = ({
30
33
  }: Props) => {
31
34
  const appContext = useContext(AppContext);
32
35
  const cardClasses = onClick ? clickableCardWrapperClasses : cardWrapperClasses;
36
+
37
+ const handleAction = useCallback(
38
+ (actionKey: string) => (event: MouseEvent<HTMLButtonElement>) => {
39
+ event.stopPropagation();
40
+ const action = actions?.[actionKey as keyof Omit<typeof actions, 'custom'>] as ActionItem | undefined;
41
+ if (action?.onClick) {
42
+ action.onClick({ id });
43
+ }
44
+ },
45
+ [actions, id],
46
+ );
33
47
  return (
34
48
  <div
35
49
  id={id}
@@ -37,7 +51,10 @@ const CardBasic = ({
37
51
  className={twMerge(
38
52
  cx(animateClasses, cardClasses, { 'p-4': padded }, { 'hover:scale-100': !scaleOnHover }, className),
39
53
  )}
40
- onClick={onClick}>
54
+ onClick={onClick}
55
+ onKeyDown={onClick ? (e) => { if (e.key === 'Enter') onClick(e as unknown); } : undefined}
56
+ role={onClick ? 'button' : undefined}
57
+ tabIndex={onClick ? 0 : undefined}>
41
58
  {image && (
42
59
  <div
43
60
  className={twMerge(
@@ -115,136 +132,30 @@ const CardBasic = ({
115
132
  )}
116
133
  {actions && (
117
134
  <div className="flex items-center gap-1 flex-wrap flex-1 justify-end">
118
- {actions.like && (
119
- <Button
120
- variant={BTN_VARIANTS.PRIMARY}
121
- icon={actions.like.isActive ? 'mdi:like' : 'mdi:like-outline'}
122
- size={BTN_SIZES.SMALL}
123
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
124
- event.stopPropagation();
125
- if (actions?.like?.onClick && typeof actions.like.onClick === 'function') {
126
- actions.like.onClick({ id });
127
- }
128
- }}
129
- />
130
- )}
131
- {actions.share && (
132
- <Button
133
- variant={BTN_VARIANTS.PRIMARY}
134
- icon={actions.share.isActive ? 'mdi:share' : 'mdi:share-outline'}
135
- size={BTN_SIZES.SMALL}
136
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
137
- event.stopPropagation();
138
- if (actions?.share?.onClick && typeof actions.share.onClick === 'function') {
139
- actions.share.onClick({ id });
140
- }
141
- }}
142
- />
143
- )}
144
- {actions.save && (
145
- <Button
146
- variant={BTN_VARIANTS.PRIMARY}
147
- icon={actions.save.isActive ? 'mdi:bookmark' : 'mdi:bookmark-outline'}
148
- size={BTN_SIZES.SMALL}
149
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
150
- event.stopPropagation();
151
- if (actions?.save?.onClick && typeof actions.save.onClick === 'function') {
152
- actions.save.onClick({ id });
153
- }
154
- }}
155
- />
156
- )}
157
- {actions.comment && (
158
- <Button
159
- variant={BTN_VARIANTS.PRIMARY}
160
- icon={actions.comment.isActive ? 'mdi:comment' : 'mdi:comment-outline'}
161
- size={BTN_SIZES.SMALL}
162
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
163
- event.stopPropagation();
164
- if (actions?.comment?.onClick && typeof actions.comment.onClick === 'function') {
165
- actions.comment.onClick({ id });
166
- }
167
- }}
168
- />
169
- )}
170
- {actions.bookmark && (
171
- <Button
172
- variant={BTN_VARIANTS.PRIMARY}
173
- icon={actions.bookmark.isActive ? 'mdi:bookmark' : 'mdi:bookmark-outline'}
174
- size={BTN_SIZES.SMALL}
175
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
176
- event.stopPropagation();
177
- if (actions?.bookmark?.onClick && typeof actions.bookmark.onClick === 'function') {
178
- actions.bookmark.onClick({ id });
179
- }
180
- }}
181
- />
182
- )}
183
- {actions.report && (
184
- <Button
185
- variant={BTN_VARIANTS.PRIMARY}
186
- icon={actions.report.isActive ? 'mdi:flag' : 'mdi:flag-outline'}
187
- size={BTN_SIZES.SMALL}
188
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
189
- event.stopPropagation();
190
- if (actions?.report?.onClick && typeof actions.report.onClick === 'function') {
191
- actions.report.onClick({ id });
192
- }
193
- }}
194
- />
195
- )}
196
- {actions.follow && (
197
- <Button
198
- variant={BTN_VARIANTS.PRIMARY}
199
- icon={actions.follow.isActive ? 'mdi:account-plus' : 'mdi:account-plus-outline'}
200
- size={BTN_SIZES.SMALL}
201
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
202
- event.stopPropagation();
203
- if (actions?.follow?.onClick && typeof actions.follow.onClick === 'function') {
204
- actions.follow.onClick({ id });
205
- }
206
- }}
207
- />
208
- )}
209
- {actions.download && (
210
- <Button
211
- variant={BTN_VARIANTS.PRIMARY}
212
- icon={actions.download.isActive ? 'mdi:download' : 'mdi:download-outline'}
213
- size={BTN_SIZES.SMALL}
214
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
215
- event.stopPropagation();
216
- if (actions?.download?.onClick && typeof actions.download.onClick === 'function') {
217
- actions.download.onClick({ id });
218
- }
219
- }}
220
- />
221
- )}
222
- {actions.edit && (
223
- <Button
224
- variant={BTN_VARIANTS.PRIMARY}
225
- icon={actions.edit.isActive ? 'mdi:pencil' : 'mdi:pencil-outline'}
226
- size={BTN_SIZES.SMALL}
227
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
228
- event.stopPropagation();
229
- if (actions?.edit?.onClick && typeof actions.edit.onClick === 'function') {
230
- actions.edit.onClick({ id });
231
- }
232
- }}
233
- />
234
- )}
235
- {actions.delete && (
236
- <Button
237
- variant={BTN_VARIANTS.PRIMARY}
238
- icon={actions.delete.isActive ? 'mdi:delete' : 'mdi:delete-outline'}
239
- size={BTN_SIZES.SMALL}
240
- onClick={(event: MouseEvent<HTMLButtonElement>) => {
241
- event.stopPropagation();
242
- if (actions?.delete?.onClick && typeof actions.delete.onClick === 'function') {
243
- actions.delete.onClick({ id });
244
- }
245
- }}
246
- />
247
- )}
135
+ {([
136
+ { key: 'like', icon: 'mdi:like', iconOutline: 'mdi:like-outline' },
137
+ { key: 'share', icon: 'mdi:share', iconOutline: 'mdi:share-outline' },
138
+ { key: 'save', icon: 'mdi:bookmark', iconOutline: 'mdi:bookmark-outline' },
139
+ { key: 'comment', icon: 'mdi:comment', iconOutline: 'mdi:comment-outline' },
140
+ { key: 'bookmark', icon: 'mdi:bookmark', iconOutline: 'mdi:bookmark-outline' },
141
+ { key: 'report', icon: 'mdi:flag', iconOutline: 'mdi:flag-outline' },
142
+ { key: 'follow', icon: 'mdi:account-plus', iconOutline: 'mdi:account-plus-outline' },
143
+ { key: 'download', icon: 'mdi:download', iconOutline: 'mdi:download-outline' },
144
+ { key: 'edit', icon: 'mdi:pencil', iconOutline: 'mdi:pencil-outline' },
145
+ { key: 'delete', icon: 'mdi:delete', iconOutline: 'mdi:delete-outline' },
146
+ ] as const).map(({ key, icon, iconOutline }) => {
147
+ const action = actions[key as keyof Omit<typeof actions, 'custom'>] as ActionItem | undefined;
148
+ if (!action) return null;
149
+ return (
150
+ <Button
151
+ key={key}
152
+ variant={BTN_VARIANTS.PRIMARY}
153
+ icon={action.isActive ? icon : iconOutline}
154
+ size={BTN_SIZES.SMALL}
155
+ onClick={handleAction(key)}
156
+ />
157
+ );
158
+ })}
248
159
  {actions.custom && actions.custom.map((action, index) => <div key={index}>{action}</div>)}
249
160
  </div>
250
161
  )}
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useEffect } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { animate, motionValue } from 'framer-motion';
4
6
  import { Props } from './CardData.types';
5
7
  import Heading from '../../../Heading';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './CardHorz.types';
4
6
  import { twMerge } from 'tailwind-merge';
5
7
  import Image from '../../../Image';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './CardIcon.types';
4
6
  import { animateClasses, cardWrapperClasses, clickableCardWrapperClasses } from '../../constants';
5
7
  import Heading from '../../../Heading';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { MouseEvent, useContext } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './CardMedia.types';
4
6
  import { animateClasses, cardWrapperClasses, clickableCardWrapperClasses } from '../../constants';
5
7
  import Image from '../../../Image';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { MouseEvent } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import HoverVideoPlayer from 'react-hover-video-player';
4
6
  import { Props } from './CardVideo.types';
5
7
  import Image from '../../../Image';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import * as React from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './Carousel.types';
4
6
  import Card from '../Card';
5
7
  import {
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useEffect, useRef, useState } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './Cart.types';
4
6
  import { useCartContext } from '../../context';
5
7
  import DisplayHeading from '../DisplayHeading';
@@ -31,14 +33,20 @@ const Cart = ({ testID, isCheckout }: Props) => {
31
33
  };
32
34
 
33
35
  useEffect(() => {
34
- // Attach the event listener when the component mounts
36
+ const handleEscKey = (event: KeyboardEvent) => {
37
+ if (event.key === 'Escape' && showCart) {
38
+ setShowCart(false);
39
+ }
40
+ };
41
+
35
42
  document.addEventListener('mousedown', handleClickOutside);
43
+ document.addEventListener('keydown', handleEscKey);
36
44
 
37
- // Cleanup the event listener when the component unmounts
38
45
  return () => {
39
46
  document.removeEventListener('mousedown', handleClickOutside);
47
+ document.removeEventListener('keydown', handleEscKey);
40
48
  };
41
- }, []);
49
+ }, [showCart]);
42
50
 
43
51
  return (
44
52
  <div data-testid={testID || '"Cart"'} className="relative">
@@ -48,6 +56,8 @@ const Cart = ({ testID, isCheckout }: Props) => {
48
56
  <Button
49
57
  rounded
50
58
  icon="mdi:cart"
59
+ ariaLabel="Shopping cart"
60
+ aria-expanded={showCart}
51
61
  onClick={event => {
52
62
  event.stopPropagation();
53
63
  setShowCart(!showCart);
@@ -121,7 +131,7 @@ const Cart = ({ testID, isCheckout }: Props) => {
121
131
  {isCheckout && (
122
132
  <>
123
133
  <div className="flex items-start justify-start">
124
- <PromoCodeForm handleSubmit={(value: string) => context?.handleAddPromoCode?.(value)} />
134
+ <PromoCodeForm handleSubmit={(values) => context?.handleAddPromoCode?.(values.promoCode)} />
125
135
  </div>
126
136
  <Copy>
127
137
  {T?.UI?.TAX}: {context?.value?.taxRate?.percentage}%
@@ -1,11 +1,11 @@
1
1
  import { forwardRef } from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Checkbox.types';
4
4
  import Icon from '../Icon';
5
5
 
6
- const Checkbox = forwardRef(function MyInput(
7
- { id, label, required, className, disabled, ...rest }: Props,
8
- ref: any,
6
+ const Checkbox = forwardRef<HTMLInputElement, Props>(function MyInput(
7
+ { id, label, required, className, disabled, ...rest },
8
+ ref,
9
9
  ) {
10
10
  return (
11
11
  <div data-testid="Checkbox" className={cx('flex items-center', className)}>
@@ -1,6 +1,8 @@
1
+ "use client";
2
+
1
3
  import React, { useMemo } from 'react';
2
4
  import { twMerge } from 'tailwind-merge';
3
- import cx from 'classnames';
5
+ import { clsx as cx } from 'clsx';
4
6
  import { Props } from './Chip.types';
5
7
  import Icon from '../Icon';
6
8
  import Link from '../Link';
@@ -1,6 +1,6 @@
1
1
  import { forwardRef } from 'react';
2
2
  import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
3
- import cx from 'classnames';
3
+ import { clsx as cx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import { CollapsibleRootProps, CollapsibleTriggerProps, CollapsibleContentProps } from './Collapsible.types';
6
6
 
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { CONTAINER_DIRECTION, Props } from './Container.types';
3
3
  import { twMerge } from 'tailwind-merge';
4
4
 
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { Props, CPL_SPACING } from './ContentPageLayout.types';
3
3
 
4
4
  const ContentPageLayout = ({
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { Props, CRL_SIZE } from './ContentRowsLayout.types';
3
3
 
4
4
  const ContentRowsLayout = ({ className, children, size = CRL_SIZE.DEFAULT }: Props) => {
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import { Props, COPY_TAG } from './Copy.types';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './DisplayHeading.types';
4
4
  import Heading from '../Heading';
5
5
  import { HEADING_TAGS } from '../Heading/Heading.types';
@@ -0,0 +1,44 @@
1
+ "use client";
2
+
3
+ import React, { Component } from 'react';
4
+ import { Props, State } from './ErrorBoundary.types';
5
+
6
+ class ErrorBoundary extends Component<Props, State> {
7
+ static displayName = 'ErrorBoundary';
8
+
9
+ constructor(props: Props) {
10
+ super(props);
11
+ this.state = { hasError: false, error: null };
12
+ }
13
+
14
+ static getDerivedStateFromError(error: Error): State {
15
+ return { hasError: true, error };
16
+ }
17
+
18
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
19
+ this.props.onError?.(error, errorInfo);
20
+ }
21
+
22
+ render() {
23
+ if (this.state.hasError && this.state.error) {
24
+ if (typeof this.props.fallback === 'function') {
25
+ return this.props.fallback(this.state.error);
26
+ }
27
+
28
+ if (this.props.fallback) {
29
+ return this.props.fallback;
30
+ }
31
+
32
+ return (
33
+ <div role="alert" data-testid="ErrorBoundary" className="p-4 rounded border border-red-200 bg-red-50 dark:bg-red-950 dark:border-red-800">
34
+ <p className="text-sm text-red-800 dark:text-red-200">Something went wrong.</p>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ return this.props.children;
40
+ }
41
+ }
42
+
43
+ export default ErrorBoundary;
44
+ export type { Props };
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export interface Props {
4
+ children: ReactNode;
5
+ fallback?: ReactNode | ((error: Error) => ReactNode);
6
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
7
+ }
8
+
9
+ export interface State {
10
+ hasError: boolean;
11
+ error: Error | null;
12
+ }
@@ -0,0 +1,2 @@
1
+ export { default } from './ErrorBoundary';
2
+ export type { Props as ErrorBoundaryProps } from './ErrorBoundary.types';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './FileUpload.types';
4
6
 
5
7
  const FileUpload = React.forwardRef(function FileUploadInput(
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useContext } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './Footer.types';
4
6
  import Logo from '../Logo';
5
7
  import Copy from '../Copy';