@adobe-commerce/elsie 1.0.0-alpha04071347

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 (340) hide show
  1. package/.elsie.js +21 -0
  2. package/.eslintrc.js +18 -0
  3. package/README.md +52 -0
  4. package/__mocks__/svg.js +11 -0
  5. package/bin/builders/build/index.js +20 -0
  6. package/bin/builders/generate/api/index.js +65 -0
  7. package/bin/builders/generate/api/templates/function.js +9 -0
  8. package/bin/builders/generate/api/templates/index.js +7 -0
  9. package/bin/builders/generate/api/templates/story.js +23 -0
  10. package/bin/builders/generate/api/templates/unit-test.js +15 -0
  11. package/bin/builders/generate/component/index.js +87 -0
  12. package/bin/builders/generate/component/templates/Component.js +43 -0
  13. package/bin/builders/generate/component/templates/css.js +24 -0
  14. package/bin/builders/generate/component/templates/index.js +8 -0
  15. package/bin/builders/generate/component/templates/stories.js +46 -0
  16. package/bin/builders/generate/component/templates/unit-test.js +19 -0
  17. package/bin/builders/generate/config/index.js +54 -0
  18. package/bin/builders/generate/config/templates/elsie.js +29 -0
  19. package/bin/builders/generate/container/index.js +65 -0
  20. package/bin/builders/generate/container/templates/Component.js +18 -0
  21. package/bin/builders/generate/container/templates/index.js +8 -0
  22. package/bin/builders/generate/container/templates/stories.js +34 -0
  23. package/bin/builders/generate/container/templates/unit-test.js +19 -0
  24. package/bin/builders/generate/index.js +283 -0
  25. package/bin/builders/gql/createOrClearDirectory.js +33 -0
  26. package/bin/builders/gql/getSchemaRef.js +25 -0
  27. package/bin/builders/gql/index.js +71 -0
  28. package/bin/builders/lint/index.js +5 -0
  29. package/bin/builders/serve/index.js +44 -0
  30. package/bin/builders/storybook/index.js +5 -0
  31. package/bin/builders/test/index.js +5 -0
  32. package/bin/index.js +26 -0
  33. package/bin/lib/cli.js +8 -0
  34. package/bin/lib/config.js +12 -0
  35. package/bin/lib/log-message.js +11 -0
  36. package/bin/lib/string.js +26 -0
  37. package/bin/lib/validate-typeof.js +28 -0
  38. package/bin/lib/write-file.js +30 -0
  39. package/bin/lib/write-parent-index.js +45 -0
  40. package/config/eslint.js +113 -0
  41. package/config/jest.js +90 -0
  42. package/config/prettier.js +16 -0
  43. package/config/setEnvVars.js +14 -0
  44. package/config/storybook/addon.js +130 -0
  45. package/config/storybook/components/FileTree/FileTree.jsx +192 -0
  46. package/config/storybook/components/FileTree/index.js +10 -0
  47. package/config/storybook/components/Flex/Flex.jsx +21 -0
  48. package/config/storybook/components/Flex/Flex.module.css +29 -0
  49. package/config/storybook/components/Flex/index.js +10 -0
  50. package/config/storybook/components/OptionsTable/OptionsTable.jsx +88 -0
  51. package/config/storybook/components/OptionsTable/OptionsTable.module.css +104 -0
  52. package/config/storybook/components/OptionsTable/index.js +10 -0
  53. package/config/storybook/components/Panel/Panel.module.css +56 -0
  54. package/config/storybook/components/Panel/Panel.tsx +46 -0
  55. package/config/storybook/components/Panel/index.ts +10 -0
  56. package/config/storybook/components/Screenshot/Screenshot.jsx +23 -0
  57. package/config/storybook/components/Screenshot/Screenshot.module.css +28 -0
  58. package/config/storybook/components/Screenshot/index.js +10 -0
  59. package/config/storybook/components/Steps/Steps.jsx +21 -0
  60. package/config/storybook/components/Steps/Steps.module.css +43 -0
  61. package/config/storybook/components/Steps/index.js +10 -0
  62. package/config/storybook/components/StoryWrapper/StoryWrapper.jsx +18 -0
  63. package/config/storybook/components/StoryWrapper/StoryWrapper.module.css +22 -0
  64. package/config/storybook/components/StoryWrapper/index.js +10 -0
  65. package/config/storybook/components/Summary/Summary.jsx +19 -0
  66. package/config/storybook/components/Summary/Summary.module.css +20 -0
  67. package/config/storybook/components/Summary/index.js +10 -0
  68. package/config/storybook/components/Variants/Variants.js +57 -0
  69. package/config/storybook/components/Variants/docs.css +48 -0
  70. package/config/storybook/components/Variants/index.js +10 -0
  71. package/config/storybook/components/video/index.jsx +28 -0
  72. package/config/storybook/manager.js +23 -0
  73. package/config/storybook/preview.jsx +88 -0
  74. package/config/storybook/theming/fonts.css +68 -0
  75. package/config/storybook/theming/logo.svg +19 -0
  76. package/config/storybook/theming/manager.css +63 -0
  77. package/config/storybook/theming/preview.css +93 -0
  78. package/config/storybook/theming/theme.js +61 -0
  79. package/config/tsconfig-base.json +16 -0
  80. package/config/tsconfig-preact.json +15 -0
  81. package/config/vite.mjs +306 -0
  82. package/package.json +113 -0
  83. package/post-release.sh +5 -0
  84. package/src/components/Accordion/Accordion.css +88 -0
  85. package/src/components/Accordion/Accordion.stories.tsx +582 -0
  86. package/src/components/Accordion/Accordion.tsx +177 -0
  87. package/src/components/Accordion/index.ts +11 -0
  88. package/src/components/ActionButton/ActionButton.css +100 -0
  89. package/src/components/ActionButton/ActionButton.stories.tsx +169 -0
  90. package/src/components/ActionButton/ActionButton.tsx +53 -0
  91. package/src/components/ActionButton/index.ts +10 -0
  92. package/src/components/ActionButtonGroup/ActionButtonGroup.css +77 -0
  93. package/src/components/ActionButtonGroup/ActionButtonGroup.stories.tsx +97 -0
  94. package/src/components/ActionButtonGroup/ActionButtonGroup.tsx +91 -0
  95. package/src/components/ActionButtonGroup/index.ts +10 -0
  96. package/src/components/AlertBanner/AlertBanner.css +144 -0
  97. package/src/components/AlertBanner/AlertBanner.stories.tsx +165 -0
  98. package/src/components/AlertBanner/AlertBanner.tsx +90 -0
  99. package/src/components/AlertBanner/index.ts +11 -0
  100. package/src/components/Breadcrumbs/Breadcrumbs.css +60 -0
  101. package/src/components/Breadcrumbs/Breadcrumbs.stories.tsx +195 -0
  102. package/src/components/Breadcrumbs/Breadcrumbs.tsx +71 -0
  103. package/src/components/Breadcrumbs/index.ts +11 -0
  104. package/src/components/Button/Button.css +213 -0
  105. package/src/components/Button/Button.mdx +133 -0
  106. package/src/components/Button/Button.stories.tsx +398 -0
  107. package/src/components/Button/Button.tsx +121 -0
  108. package/src/components/Button/index.ts +11 -0
  109. package/src/components/Card/Card.css +34 -0
  110. package/src/components/Card/Card.stories.tsx +76 -0
  111. package/src/components/Card/Card.tsx +34 -0
  112. package/src/components/Card/index.ts +10 -0
  113. package/src/components/CartItem/CartItem.css +509 -0
  114. package/src/components/CartItem/CartItem.stories.tsx +628 -0
  115. package/src/components/CartItem/CartItem.tsx +467 -0
  116. package/src/components/CartItem/CartItemSkeleton.tsx +38 -0
  117. package/src/components/CartItem/index.ts +12 -0
  118. package/src/components/CartList/CartList.css +35 -0
  119. package/src/components/CartList/CartList.stories.tsx +111 -0
  120. package/src/components/CartList/CartList.tsx +40 -0
  121. package/src/components/CartList/index.ts +11 -0
  122. package/src/components/Checkbox/Checkbox.css +255 -0
  123. package/src/components/Checkbox/Checkbox.stories.tsx +290 -0
  124. package/src/components/Checkbox/Checkbox.tsx +138 -0
  125. package/src/components/Checkbox/index.ts +10 -0
  126. package/src/components/ColorSwatch/ColorSwatch.css +132 -0
  127. package/src/components/ColorSwatch/ColorSwatch.stories.tsx +274 -0
  128. package/src/components/ColorSwatch/ColorSwatch.tsx +127 -0
  129. package/src/components/ColorSwatch/index.ts +11 -0
  130. package/src/components/ContentGrid/ContentGrid.css +54 -0
  131. package/src/components/ContentGrid/ContentGrid.stories.tsx +137 -0
  132. package/src/components/ContentGrid/ContentGrid.tsx +57 -0
  133. package/src/components/ContentGrid/index.ts +11 -0
  134. package/src/components/Divider/Divider.css +22 -0
  135. package/src/components/Divider/Divider.stories.tsx +62 -0
  136. package/src/components/Divider/Divider.tsx +33 -0
  137. package/src/components/Divider/index.ts +11 -0
  138. package/src/components/Field/Field.css +83 -0
  139. package/src/components/Field/Field.stories.tsx +238 -0
  140. package/src/components/Field/Field.tsx +84 -0
  141. package/src/components/Field/index.ts +10 -0
  142. package/src/components/Header/Header.css +56 -0
  143. package/src/components/Header/Header.stories.tsx +180 -0
  144. package/src/components/Header/Header.tsx +81 -0
  145. package/src/components/Header/index.ts +11 -0
  146. package/src/components/Icon/Icon.css +26 -0
  147. package/src/components/Icon/Icon.stories.helpers.jsx +21 -0
  148. package/src/components/Icon/Icon.stories.tsx +98 -0
  149. package/src/components/Icon/Icon.tsx +112 -0
  150. package/src/components/Icon/index.ts +10 -0
  151. package/src/components/IllustratedMessage/IllustratedMessage.css +61 -0
  152. package/src/components/IllustratedMessage/IllustratedMessage.stories.tsx +126 -0
  153. package/src/components/IllustratedMessage/IllustratedMessage.tsx +78 -0
  154. package/src/components/IllustratedMessage/index.ts +11 -0
  155. package/src/components/Image/Image.css +52 -0
  156. package/src/components/Image/Image.stories.tsx +89 -0
  157. package/src/components/Image/Image.tsx +66 -0
  158. package/src/components/Image/index.ts +10 -0
  159. package/src/components/ImageSwatch/ImageSwatch.css +154 -0
  160. package/src/components/ImageSwatch/ImageSwatch.stories.tsx +310 -0
  161. package/src/components/ImageSwatch/ImageSwatch.tsx +123 -0
  162. package/src/components/ImageSwatch/index.ts +11 -0
  163. package/src/components/InLineAlert/InLineAlert.css +116 -0
  164. package/src/components/InLineAlert/InLineAlert.stories.tsx +326 -0
  165. package/src/components/InLineAlert/InLineAlert.tsx +128 -0
  166. package/src/components/InLineAlert/index.ts +11 -0
  167. package/src/components/Incrementer/Incrementer.css +165 -0
  168. package/src/components/Incrementer/Incrementer.stories.tsx +172 -0
  169. package/src/components/Incrementer/Incrementer.tsx +192 -0
  170. package/src/components/Incrementer/index.ts +10 -0
  171. package/src/components/Input/Input.css +304 -0
  172. package/src/components/Input/Input.stories.tsx +155 -0
  173. package/src/components/Input/Input.tsx +166 -0
  174. package/src/components/Input/index.ts +11 -0
  175. package/src/components/InputDate/InputDate.css +56 -0
  176. package/src/components/InputDate/InputDate.stories.tsx +117 -0
  177. package/src/components/InputDate/InputDate.tsx +120 -0
  178. package/src/components/InputDate/index.ts +11 -0
  179. package/src/components/InputPassword/InputPassword.css +31 -0
  180. package/src/components/InputPassword/InputPassword.stories.tsx +148 -0
  181. package/src/components/InputPassword/InputPassword.tsx +135 -0
  182. package/src/components/InputPassword/PasswordStatusIndicator/PasswordStatusIndicator.css +31 -0
  183. package/src/components/InputPassword/PasswordStatusIndicator/PasswordStatusIndicator.tsx +96 -0
  184. package/src/components/InputPassword/PasswordStatusIndicator/index.ts +11 -0
  185. package/src/components/InputPassword/index.ts +11 -0
  186. package/src/components/Modal/Modal.css +125 -0
  187. package/src/components/Modal/Modal.stories.tsx +250 -0
  188. package/src/components/Modal/Modal.tsx +157 -0
  189. package/src/components/Modal/index.ts +10 -0
  190. package/src/components/Pagination/Pagination.css +95 -0
  191. package/src/components/Pagination/Pagination.stories.tsx +117 -0
  192. package/src/components/Pagination/Pagination.tsx +149 -0
  193. package/src/components/Pagination/index.ts +11 -0
  194. package/src/components/Picker/Picker.css +220 -0
  195. package/src/components/Picker/Picker.stories.tsx +318 -0
  196. package/src/components/Picker/Picker.tsx +203 -0
  197. package/src/components/Picker/index.ts +10 -0
  198. package/src/components/Price/Price.css +57 -0
  199. package/src/components/Price/Price.stories.tsx +110 -0
  200. package/src/components/Price/Price.tsx +75 -0
  201. package/src/components/Price/index.ts +10 -0
  202. package/src/components/PriceRange/PriceRange.css +66 -0
  203. package/src/components/PriceRange/PriceRange.stories.tsx +240 -0
  204. package/src/components/PriceRange/PriceRange.tsx +248 -0
  205. package/src/components/PriceRange/index.ts +11 -0
  206. package/src/components/ProgressSpinner/ProgressSpinner.css +91 -0
  207. package/src/components/ProgressSpinner/ProgressSpinner.stories.tsx +300 -0
  208. package/src/components/ProgressSpinner/ProgressSpinner.tsx +86 -0
  209. package/src/components/ProgressSpinner/index.ts +11 -0
  210. package/src/components/RadioButton/RadioButton.css +134 -0
  211. package/src/components/RadioButton/RadioButton.stories.tsx +126 -0
  212. package/src/components/RadioButton/RadioButton.tsx +86 -0
  213. package/src/components/RadioButton/index.ts +11 -0
  214. package/src/components/Skeleton/Skeleton.css +145 -0
  215. package/src/components/Skeleton/Skeleton.stories.tsx +265 -0
  216. package/src/components/Skeleton/Skeleton.tsx +133 -0
  217. package/src/components/Skeleton/index.ts +10 -0
  218. package/src/components/Tag/Tag.css +26 -0
  219. package/src/components/Tag/Tag.stories.tsx +103 -0
  220. package/src/components/Tag/Tag.tsx +38 -0
  221. package/src/components/Tag/index.ts +11 -0
  222. package/src/components/TextArea/TextArea.css +140 -0
  223. package/src/components/TextArea/TextArea.stories.tsx +130 -0
  224. package/src/components/TextArea/TextArea.tsx +89 -0
  225. package/src/components/TextArea/index.ts +11 -0
  226. package/src/components/TextSwatch/TextSwatch.css +152 -0
  227. package/src/components/TextSwatch/TextSwatch.stories.tsx +277 -0
  228. package/src/components/TextSwatch/TextSwatch.tsx +131 -0
  229. package/src/components/TextSwatch/index.ts +11 -0
  230. package/src/components/ToggleButton/ToggleButton.css +95 -0
  231. package/src/components/ToggleButton/ToggleButton.stories.tsx +190 -0
  232. package/src/components/ToggleButton/ToggleButton.tsx +75 -0
  233. package/src/components/ToggleButton/index.ts +11 -0
  234. package/src/components/UIProvider/UIProvider.css +140 -0
  235. package/src/components/UIProvider/UIProvider.tsx +61 -0
  236. package/src/components/UIProvider/debugger.css +47 -0
  237. package/src/components/UIProvider/index.ts +10 -0
  238. package/src/components/UIProvider/normalize.css +26 -0
  239. package/src/components/index.ts +49 -0
  240. package/src/docs/API/event-bus.mdx +52 -0
  241. package/src/docs/API/graphql.mdx +214 -0
  242. package/src/docs/API/initializer.mdx +119 -0
  243. package/src/docs/API/render.mdx +125 -0
  244. package/src/docs/Design/colors.mdx +202 -0
  245. package/src/docs/Design/designBlocks.jsx +87 -0
  246. package/src/docs/Design/getTokenData.ts +28 -0
  247. package/src/docs/Design/grid.mdx +365 -0
  248. package/src/docs/Design/overview.mdx +69 -0
  249. package/src/docs/Design/shapes.mdx +100 -0
  250. package/src/docs/Design/spacing.mdx +22 -0
  251. package/src/docs/Design/typography.mdx +126 -0
  252. package/src/docs/Utilities/classList.mdx +52 -0
  253. package/src/docs/Utilities/debounce.mdx +49 -0
  254. package/src/docs/Utilities/deepmerge.mdx +12 -0
  255. package/src/docs/Utilities/getFormErrors.mdx +41 -0
  256. package/src/docs/Utilities/getFormValues.mdx +38 -0
  257. package/src/docs/assets/Banner.png +0 -0
  258. package/src/docs/assets/Colors.png +0 -0
  259. package/src/docs/assets/DropinBanner.png +0 -0
  260. package/src/docs/assets/ShapeStyles.png +0 -0
  261. package/src/docs/assets/Spacing.png +0 -0
  262. package/src/docs/assets/Typography.png +0 -0
  263. package/src/docs/cli-usage.mdx +181 -0
  264. package/src/docs/components/overview.mdx +124 -0
  265. package/src/docs/quick-start.mdx +245 -0
  266. package/src/docs/slots.mdx +211 -0
  267. package/src/docs/welcome.mdx +52 -0
  268. package/src/i18n/en_US.json +146 -0
  269. package/src/i18n/index.ts +26 -0
  270. package/src/icons/Add.svg +9 -0
  271. package/src/icons/AddressBook.svg +3 -0
  272. package/src/icons/Bulk.svg +24 -0
  273. package/src/icons/Burger.svg +5 -0
  274. package/src/icons/Card.svg +7 -0
  275. package/src/icons/Cart.svg +11 -0
  276. package/src/icons/Check.svg +8 -0
  277. package/src/icons/CheckWithCircle.svg +4 -0
  278. package/src/icons/ChevronDown.svg +3 -0
  279. package/src/icons/ChevronRight.svg +8 -0
  280. package/src/icons/ChevronUp.svg +3 -0
  281. package/src/icons/Close.svg +4 -0
  282. package/src/icons/Coupon.svg +3 -0
  283. package/src/icons/Date.svg +4 -0
  284. package/src/icons/Delivery.svg +11 -0
  285. package/src/icons/EmptyBox.svg +3 -0
  286. package/src/icons/Eye.svg +3 -0
  287. package/src/icons/EyeClose.svg +3 -0
  288. package/src/icons/Gift.svg +3 -0
  289. package/src/icons/GiftCard.svg +3 -0
  290. package/src/icons/Heart.svg +3 -0
  291. package/src/icons/HeartFilled.svg +3 -0
  292. package/src/icons/InfoFilled.svg +3 -0
  293. package/src/icons/Locker.svg +11 -0
  294. package/src/icons/Minus.svg +3 -0
  295. package/src/icons/Order.svg +6 -0
  296. package/src/icons/OrderError.svg +15 -0
  297. package/src/icons/OrderSuccess.svg +15 -0
  298. package/src/icons/PaymentError.svg +16 -0
  299. package/src/icons/Placeholder.svg +3 -0
  300. package/src/icons/PlaceholderFilled.svg +4 -0
  301. package/src/icons/Search.svg +9 -0
  302. package/src/icons/SearchFilled.svg +10 -0
  303. package/src/icons/Sort.svg +12 -0
  304. package/src/icons/Star.svg +8 -0
  305. package/src/icons/Trash.svg +7 -0
  306. package/src/icons/User.svg +5 -0
  307. package/src/icons/View.svg +14 -0
  308. package/src/icons/Wallet.svg +6 -0
  309. package/src/icons/Warning.svg +12 -0
  310. package/src/icons/WarningFilled.svg +3 -0
  311. package/src/icons/WarningWithCircle.svg +4 -0
  312. package/src/icons/index.ts +42 -0
  313. package/src/lib/classes.ts +34 -0
  314. package/src/lib/config.ts +24 -0
  315. package/src/lib/debounce.ts +16 -0
  316. package/src/lib/deepmerge.ts +45 -0
  317. package/src/lib/deviceUtils.ts +16 -0
  318. package/src/lib/form-values.ts +31 -0
  319. package/src/lib/i18n.ts +18 -0
  320. package/src/lib/image-params-keymap.ts +36 -0
  321. package/src/lib/index.ts +24 -0
  322. package/src/lib/initializer.ts +134 -0
  323. package/src/lib/is-number.ts +12 -0
  324. package/src/lib/render.tsx +138 -0
  325. package/src/lib/resolve-image.ts +101 -0
  326. package/src/lib/signals.ts +11 -0
  327. package/src/lib/slot.tsx +434 -0
  328. package/src/lib/tests.tsx +47 -0
  329. package/src/lib/types.ts +16 -0
  330. package/src/lib/vcomponent.tsx +42 -0
  331. package/static/assets/images/Card.png +0 -0
  332. package/static/assets/images/example.jpg +0 -0
  333. package/static/assets/images/index.ts +11 -0
  334. package/static/dropin.png +0 -0
  335. package/static/favicon.svg +14 -0
  336. package/storybook-stories.js +21 -0
  337. package/tests/__mocks__/browserMocks.ts +28 -0
  338. package/tests/__mocks__/fileMocks.ts +12 -0
  339. package/tests/__mocks__/styleMock.ts +0 -0
  340. package/types/icons.d.ts +18 -0
@@ -0,0 +1,66 @@
1
+ /********************************************************************
2
+ * Copyright 2024 Adobe
3
+ * All Rights Reserved.
4
+ *
5
+ * NOTICE: Adobe permits you to use, modify, and distribute this
6
+ * file in accordance with the terms of the Adobe license agreement
7
+ * accompanying it.
8
+ *******************************************************************/
9
+
10
+ import { FunctionComponent } from 'preact';
11
+ import { HTMLAttributes, useMemo, useState } from 'preact/compat';
12
+ import {
13
+ classes,
14
+ generateSrcset,
15
+ ResolveImageUrlOptions,
16
+ } from '@adobe-commerce/elsie/lib';
17
+ import '@adobe-commerce/elsie/components/Image/Image.css';
18
+
19
+ export interface ImageProps extends HTMLAttributes<HTMLDivElement> {
20
+ params?: ResolveImageUrlOptions;
21
+ src: string;
22
+ onLoad?: (e: any) => void;
23
+ }
24
+ export const Image: FunctionComponent<ImageProps> = ({
25
+ className,
26
+ src,
27
+ params,
28
+ loading = 'lazy',
29
+ srcSet,
30
+ onLoad,
31
+ ...props
32
+ }) => {
33
+ const [loaded, setLoaded] = useState(false);
34
+
35
+ const _srcSet = useMemo(() => {
36
+ // If srcSet is provided, use it
37
+ if (srcSet) return srcSet;
38
+
39
+ // If src is not provided, return "undefined"
40
+ if (!src || !params) return;
41
+
42
+ // Otherwise, generate srcSet based on params
43
+ return generateSrcset(src, { ...params });
44
+ }, [params, src, srcSet]);
45
+
46
+ // Add class at image onLoad
47
+ const onLoadHandler = (e: any) => {
48
+ setLoaded(true);
49
+ onLoad?.(e);
50
+ };
51
+
52
+ return (
53
+ <img
54
+ {...props}
55
+ className={classes([
56
+ 'dropin-image',
57
+ ['dropin-image--loaded', loaded],
58
+ className,
59
+ ])}
60
+ loading={loading}
61
+ onLoad={onLoadHandler}
62
+ src={src}
63
+ srcSet={_srcSet}
64
+ />
65
+ );
66
+ };
@@ -0,0 +1,10 @@
1
+ /********************************************************************
2
+ * Copyright 2024 Adobe
3
+ * All Rights Reserved.
4
+ *
5
+ * NOTICE: Adobe permits you to use, modify, and distribute this
6
+ * file in accordance with the terms of the Adobe license agreement
7
+ * accompanying it.
8
+ *******************************************************************/
9
+
10
+ export * from '@adobe-commerce/elsie/components/Image/Image';
@@ -0,0 +1,154 @@
1
+ /********************************************************************
2
+ * Copyright 2024 Adobe
3
+ * All Rights Reserved.
4
+ *
5
+ * NOTICE: Adobe permits you to use, modify, and distribute this
6
+ * file in accordance with the terms of the Adobe license agreement
7
+ * accompanying it.
8
+ *******************************************************************/
9
+
10
+ /* https://cssguidelin.es/#bem-like-naming */
11
+
12
+ /* Hide default the radio/checkbox button */
13
+ .dropin-image-swatch__container input[type='radio'],
14
+ .dropin-image-swatch__container input[type='checkbox'] {
15
+ opacity: 0;
16
+ position: absolute;
17
+ width: 0;
18
+ height: 0;
19
+ }
20
+
21
+ .dropin-image-swatch__container .dropin-image-swatch__span {
22
+ display: inline-block;
23
+ --bg-color: var(--color-neutral-300);
24
+ box-shadow: 0 0 0 var(--shape-border-width-1) var(--color-neutral-400);
25
+ position: relative;
26
+ cursor: pointer;
27
+ background-color: var(--bg-color);
28
+ background-image: var(--background-image);
29
+ background-repeat: no-repeat;
30
+ background-size: cover;
31
+ height: 100px;
32
+ width: 100px;
33
+ overflow: hidden;
34
+ }
35
+
36
+ .dropin-image-swatch__content {
37
+ width: inherit;
38
+ position: absolute;
39
+ top: -9999px;
40
+ bottom: -9999px;
41
+ left: -9999px;
42
+ right: -9999px;
43
+ margin: auto;
44
+ }
45
+
46
+ .dropin-image-swatch__span:hover {
47
+ box-shadow: 0 0 0 var(--shape-border-width-3) var(--color-neutral-50),
48
+ 0 0 0 var(--shape-border-width-4) var(--color-neutral-500);
49
+ outline: none;
50
+ }
51
+
52
+ .dropin-image-swatch__container
53
+ input[type='radio']:not(:checked):active
54
+ ~ .dropin-image-swatch__span,
55
+ .dropin-image-swatch__container
56
+ input[type='checkbox']:not(:checked):active
57
+ ~ .dropin-image-swatch__span {
58
+ box-shadow: 0 0 0 var(--shape-border-width-1) var(--color-neutral-500);
59
+ outline: none;
60
+ }
61
+
62
+ .dropin-image-swatch__container
63
+ input[type='radio']:checked
64
+ ~ .dropin-image-swatch__span,
65
+ .dropin-image-swatch__container
66
+ input[type='checkbox']:checked
67
+ ~ .dropin-image-swatch__span,
68
+ .dropin-image-swatch--selected {
69
+ box-shadow: 0 0 0 var(--shape-border-width-3) var(--color-neutral-50),
70
+ 0 0 0 var(--shape-border-width-4) var(--color-neutral-800);
71
+ }
72
+
73
+ .dropin-image-swatch__container
74
+ input[type='radio']:checked:active
75
+ ~ .dropin-image-swatch__span,
76
+ .dropin-image-swatch__container
77
+ input[type='checkbox']:checked:active
78
+ ~ .dropin-image-swatch__span {
79
+ box-shadow: 0 0 0 var(--shape-border-width-3) var(--color-neutral-50),
80
+ 0 0 0 var(--shape-border-width-4) var(--color-neutral-600);
81
+ }
82
+
83
+ .dropin-image-swatch__container
84
+ input[type='radio']:focus-visible
85
+ + .dropin-image-swatch__span,
86
+ .dropin-image-swatch__container
87
+ input[type='checkbox']:focus-visible
88
+ + .dropin-image-swatch__span {
89
+ box-shadow: 0 0 0 var(--shape-border-width-3) var(--color-neutral-900),
90
+ 0 0 0 6px var(--color-neutral-400);
91
+ }
92
+
93
+ .dropin-image-swatch__container
94
+ input[type='radio']:disabled
95
+ ~ .dropin-image-swatch__span,
96
+ .dropin-image-swatch__container
97
+ input[type='checkbox']:disabled
98
+ ~ .dropin-image-swatch__span,
99
+ .dropin-image-swatch--disabled,
100
+ .dropin-image-swatch__container
101
+ input[type='radio']:disabled:active
102
+ ~ .dropin-image-swatch__span,
103
+ .dropin-image-swatch__container
104
+ input[type='checkbox']:disabled:active
105
+ ~ .dropin-image-swatch__span {
106
+ box-shadow: 0 0 0 var(--shape-border-width-4) var(--color-neutral-500);
107
+ background-color: rgba(255, 255, 255, 0.76);
108
+ pointer-events: none;
109
+ }
110
+
111
+ .dropin-image-swatch__container .dropin-image-swatch__span--out-of-stock {
112
+ background: linear-gradient(
113
+ to top left,
114
+ rgba(0, 0, 0, 0) 0%,
115
+ rgba(0, 0, 0, 0) calc(50% - 0.8px),
116
+ var(--color-neutral-700) 50%,
117
+ rgba(0, 0, 0, 0) calc(50% + 0.8px),
118
+ rgba(0, 0, 0, 0) 100%
119
+ );
120
+ background-color: rgba(255, 255, 255, 0.6);
121
+ }
122
+
123
+ .dropin-image-swatch__span--out-of-stock > .dropin-image-swatch__content,
124
+ .dropin-image-swatch__container
125
+ input[type='radio']:disabled
126
+ ~ .dropin-image-swatch__span
127
+ > .dropin-image-swatch__content,
128
+ .dropin-image-swatch__container
129
+ input[type='checkbox']:disabled
130
+ ~ .dropin-image-swatch__span
131
+ > .dropin-image-swatch__content {
132
+ z-index: -1;
133
+ }
134
+
135
+ .dropin-image-swatch__container
136
+ input[type='radio']:not(:disabled)
137
+ ~ .dropin-image-swatch__span,
138
+ .dropin-image-swatch__container
139
+ input[type='checkbox']:not(:disabled)
140
+ ~ .dropin-image-swatch__span {
141
+ cursor: pointer;
142
+ }
143
+
144
+ /* Medium (portrait tablets and large phones, 768px and up) */
145
+ /* @media only screen and (min-width: 768px) { } */
146
+
147
+ /* Large (landscape tablets, 1024px and up) */
148
+ /* @media only screen and (min-width: 1024px) { } */
149
+
150
+ /* XLarge (laptops/desktops, 1366px and up) */
151
+ /* @media only screen and (min-width: 1366px) { } */
152
+
153
+ /* XXlarge (large laptops and desktops, 1920px and up) */
154
+ /* @media only screen and (min-width: 1920px) { } */
@@ -0,0 +1,310 @@
1
+ /********************************************************************
2
+ * Copyright 2024 Adobe
3
+ * All Rights Reserved.
4
+ *
5
+ * NOTICE: Adobe permits you to use, modify, and distribute this
6
+ * file in accordance with the terms of the Adobe license agreement
7
+ * accompanying it.
8
+ *******************************************************************/
9
+
10
+ // https://storybook.js.org/docs/7.0/preact/writing-stories/introduction
11
+ import type { Meta, StoryObj } from '@storybook/preact';
12
+ import { expect, within } from '@storybook/test';
13
+ import { action } from '@storybook/addon-actions';
14
+ import {
15
+ ImageSwatch,
16
+ ImageSwatchProps,
17
+ } from '@adobe-commerce/elsie/components/ImageSwatch';
18
+
19
+ const defaultWidth = 200;
20
+ const defaultHeight = 200;
21
+
22
+ /**
23
+ * Use Image Swatches to display thumbnails of photos or illustrations.
24
+ */
25
+ const meta: Meta<ImageSwatchProps> = {
26
+ title: 'Components/ImageSwatch',
27
+ component: ImageSwatch,
28
+ argTypes: {
29
+ name: {
30
+ description: 'Field name (used for mapping the value in a form)',
31
+ type: {
32
+ required: false,
33
+ name: 'string',
34
+ },
35
+ },
36
+ label: {
37
+ description: 'Field label',
38
+ type: {
39
+ required: true,
40
+ name: 'string',
41
+ },
42
+ },
43
+ id: {
44
+ description: 'Field id',
45
+ type: {
46
+ required: false,
47
+ name: 'string',
48
+ },
49
+ },
50
+ alt: {
51
+ description: 'Alt Text',
52
+ type: {
53
+ required: false,
54
+ name: 'string',
55
+ },
56
+ },
57
+ value: {
58
+ description: 'Field value',
59
+ type: {
60
+ required: false,
61
+ name: 'string',
62
+ },
63
+ },
64
+ disabled: {
65
+ description: 'ImageSwatch disabled',
66
+ type: {
67
+ required: false,
68
+ name: 'boolean',
69
+ },
70
+ },
71
+ selected: {
72
+ description: 'ImageSwatch active',
73
+ type: {
74
+ required: false,
75
+ name: 'boolean',
76
+ },
77
+ },
78
+ multi: {
79
+ description: 'ImageSwatch multi',
80
+ type: {
81
+ required: false,
82
+ name: 'boolean',
83
+ },
84
+ },
85
+ src: {
86
+ description: 'Image path',
87
+ type: {
88
+ required: true,
89
+ name: 'string',
90
+ },
91
+ },
92
+ groupAriaLabel: {
93
+ description: 'Label name for the swatch group',
94
+ type: {
95
+ name: 'string',
96
+ },
97
+ },
98
+ outOfStock: {
99
+ description: 'Whether or not the image swatch is out of stock',
100
+ type: {
101
+ required: false,
102
+ name: 'boolean',
103
+ },
104
+ },
105
+ onValue: {
106
+ description: 'Function to handle value changes',
107
+ type: {
108
+ required: false,
109
+ name: 'function',
110
+ },
111
+ },
112
+ onUpdateError: {
113
+ description: 'Function to handle errors',
114
+ type: {
115
+ required: false,
116
+ name: 'function',
117
+ },
118
+ },
119
+ },
120
+ };
121
+
122
+ export default meta;
123
+
124
+ type Story = StoryObj<ImageSwatchProps>;
125
+
126
+ /**
127
+ * ```ts
128
+ * import { ImageSwatch } from '@adobe-commerce/elsie/components/ImageSwatch';
129
+ * ```
130
+ */
131
+
132
+ export const DefaultImageSwatch: Story = {
133
+ args: {
134
+ name: 'imageSwatchField',
135
+ id: 'imageSwatch1',
136
+ label: 'imageSwatch1Example',
137
+ groupAriaLabel: 'Image Swatches',
138
+ value: 'imageSwatchExample',
139
+ src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`,
140
+ alt: 'Some alternative text',
141
+ selected: false,
142
+ disabled: false,
143
+ outOfStock: false,
144
+ onValue: action('onValue'),
145
+ },
146
+ play: async ({ canvasElement }) => {
147
+ const canvas = within(canvasElement);
148
+ const imageSwatch = await canvas.findByRole('radio');
149
+ const spanElement = document.querySelector(
150
+ '.dropin-image-swatch__span'
151
+ ) as HTMLElement;
152
+ expect(spanElement).toBeVisible();
153
+ await expect(imageSwatch).not.toBeChecked();
154
+ },
155
+ };
156
+
157
+ export const ImageSwatchSelected: Story = {
158
+ args: {
159
+ name: 'imageSwatchField',
160
+ id: 'imageSwatch1',
161
+ label: 'imageSwatch1Example',
162
+ groupAriaLabel: 'Image Swatches',
163
+ value: 'imageSwatchExample',
164
+ src: `https://picsum.photos/400/400`,
165
+ alt: 'imageSwatch1Example',
166
+ selected: true,
167
+ disabled: false,
168
+ outOfStock: false,
169
+ onValue: action('onValue'),
170
+ },
171
+ play: async ({ canvasElement }) => {
172
+ const canvas = within(canvasElement);
173
+ const imageSwatch = await canvas.findByRole('radio');
174
+ const spanElement = document.querySelector(
175
+ '.dropin-image-swatch__span'
176
+ ) as HTMLElement;
177
+ expect(spanElement).toBeVisible();
178
+ await expect(imageSwatch).toBeChecked();
179
+ await expect(
180
+ canvas.getByLabelText(
181
+ 'Image Swatches: imageSwatch1Example swatch selected'
182
+ )
183
+ ).toBeChecked();
184
+ },
185
+ };
186
+
187
+ export const ImageSwatchDisabled: Story = {
188
+ args: {
189
+ name: 'imageSwatchField',
190
+ id: 'imageSwatch1',
191
+ label: 'imageSwatch1Example',
192
+ groupAriaLabel: 'Image Swatches',
193
+ value: 'imageSwatchExample',
194
+ src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`,
195
+ alt: 'Some alternative text',
196
+ selected: false,
197
+ disabled: true,
198
+ outOfStock: false,
199
+ onValue: action('onValue'),
200
+ },
201
+ play: async ({ canvasElement }) => {
202
+ const canvas = within(canvasElement);
203
+ await expect(await canvas.findByRole('radio')).toBeDisabled();
204
+ },
205
+ };
206
+
207
+ export const OutOfStockImageSwatch: Story = {
208
+ args: {
209
+ name: 'imageSwatchField',
210
+ id: 'imageSwatch1',
211
+ label: 'imageSwatch1Example',
212
+ groupAriaLabel: 'Image Swatches',
213
+ value: 'imageSwatchExample',
214
+ src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`,
215
+ alt: 'Some alternative text',
216
+ selected: false,
217
+ disabled: false,
218
+ outOfStock: true,
219
+ onValue: action('onValue'),
220
+ },
221
+ };
222
+
223
+ export const SelectedOutOfStockImageSwatch: Story = {
224
+ args: {
225
+ name: 'imageSwatchField',
226
+ id: 'imageSwatch1',
227
+ label: 'imageSwatch1Example',
228
+ groupAriaLabel: 'Image Swatches',
229
+ value: 'imageSwatchExample',
230
+ src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`,
231
+ alt: 'Some alternative text',
232
+ selected: true,
233
+ disabled: false,
234
+ outOfStock: true,
235
+ onValue: action('onValue'),
236
+ 'aria-label': 'image swatch example',
237
+ },
238
+ };
239
+
240
+ export const MultiImageSwatch: Story = {
241
+ args: {
242
+ name: 'imageSwatchField',
243
+ id: 'imageSwatch1',
244
+ value: 'imageSwatchExample',
245
+ src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`,
246
+ alt: 'Some alternative text',
247
+ selected: false,
248
+ disabled: false,
249
+ outOfStock: false,
250
+ multi: true,
251
+ onValue: action('onValue'),
252
+ 'aria-label': 'image swatch example',
253
+ },
254
+ render: (args) => (
255
+ <div style="display: flex; flex-wrap:wrap; gap: 25px">
256
+ <ImageSwatch {...args} id={'imageSwatch1'} value={'image1'} />
257
+ <ImageSwatch {...args} id={'imageSwatch2'} value={'image2'} />
258
+ <ImageSwatch {...args} id={'imageSwatch3'} value={'image1'} />
259
+ </div>
260
+ ),
261
+ };
262
+
263
+ export const CustomImageNodeSwatch: Story = {
264
+ args: {
265
+ name: 'customImageSwatch',
266
+ id: 'customImageSwatch1',
267
+ label: 'Custom Image Node Example',
268
+ groupAriaLabel: 'Custom Image Swatches',
269
+ value: 'customImageNode',
270
+ src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`, // fallback, not used with imageNode
271
+ alt: 'Custom Image Node',
272
+ selected: false,
273
+ disabled: false,
274
+ outOfStock: false,
275
+ onValue: action('onValue'),
276
+ imageNode: (
277
+ <div style="position: relative; width: 100%; height: 100%;">
278
+ <img
279
+ src={`https://picsum.photos/${defaultWidth}/${defaultHeight}?grayscale`}
280
+ alt="Custom grayscale image"
281
+ style="width: 100%; height: 100%; object-fit: cover;"
282
+ />
283
+ <div style="position: absolute; top: 0; left: 0; background: rgba(255,255,255,0.7); padding: 4px 8px; border-radius: 0 0 8px 0;">
284
+ <span style="font-size: 12px; font-weight: bold; color: #333;">
285
+ Custom
286
+ </span>
287
+ </div>
288
+ </div>
289
+ ),
290
+ },
291
+ play: async ({ canvasElement }) => {
292
+ const canvas = within(canvasElement);
293
+ const imageSwatch = await canvas.findByRole('radio');
294
+ const customImageContainer = canvasElement.querySelector(
295
+ 'div[style*="position: relative"]'
296
+ );
297
+ const customImage = canvasElement.querySelector(
298
+ 'img[alt="Custom grayscale image"]'
299
+ );
300
+ const customLabel = canvasElement.querySelector(
301
+ 'span[style*="font-weight: bold"]'
302
+ );
303
+
304
+ expect(imageSwatch).toBeInTheDocument();
305
+ expect(customImageContainer).toBeInTheDocument();
306
+ expect(customImage).toBeInTheDocument();
307
+ expect(customLabel).toBeInTheDocument();
308
+ expect(customLabel?.textContent).toBe('Custom');
309
+ },
310
+ };
@@ -0,0 +1,123 @@
1
+ /********************************************************************
2
+ * Copyright 2024 Adobe
3
+ * All Rights Reserved.
4
+ *
5
+ * NOTICE: Adobe permits you to use, modify, and distribute this
6
+ * file in accordance with the terms of the Adobe license agreement
7
+ * accompanying it.
8
+ *******************************************************************/
9
+
10
+ import { FunctionComponent, VNode } from 'preact';
11
+ import { HTMLAttributes, useCallback } from 'preact/compat';
12
+ import { classes } from '@adobe-commerce/elsie/lib';
13
+ import '@adobe-commerce/elsie/components/ImageSwatch/ImageSwatch.css';
14
+ import { Image } from '@adobe-commerce/elsie/components/Image';
15
+ import { useText } from '@adobe-commerce/elsie/i18n';
16
+ export interface ImageSwatchProps
17
+ extends Omit<HTMLAttributes<HTMLInputElement>, 'label'> {
18
+ name?: string;
19
+ label?: string;
20
+ groupAriaLabel?: string;
21
+ id?: string;
22
+ src: string;
23
+ alt?: string;
24
+ value?: string;
25
+ disabled?: boolean;
26
+ selected?: boolean;
27
+ outOfStock?: boolean;
28
+ multi?: boolean;
29
+ imageNode?: VNode;
30
+ onValue?: (value: any) => void;
31
+ onUpdateError?: (error: Error) => void;
32
+ }
33
+
34
+ export const ImageSwatch: FunctionComponent<ImageSwatchProps> = ({
35
+ className,
36
+ name,
37
+ value,
38
+ id,
39
+ label,
40
+ groupAriaLabel,
41
+ src,
42
+ alt,
43
+ disabled = false,
44
+ selected = false,
45
+ outOfStock = false,
46
+ multi = false,
47
+ imageNode,
48
+ onValue,
49
+ onUpdateError,
50
+ ...props
51
+ }) => {
52
+ const outOfStockLabel = useText('Dropin.Swatches.outOfStock.label').label;
53
+ const selectedLabel = useText('Dropin.Swatches.selected.label').label;
54
+ const swatchLabel = useText('Dropin.Swatches.swatch.label').label;
55
+
56
+ const onValueHandler = useCallback(
57
+ async (newValue: any) => {
58
+ if (onValue) {
59
+ try {
60
+ await onValue(newValue);
61
+ } catch (e) {
62
+ if (onUpdateError) {
63
+ onUpdateError(e as Error);
64
+ }
65
+ }
66
+ }
67
+ },
68
+ [onValue, onUpdateError]
69
+ );
70
+
71
+ const handleOnValue = (e: Event) => {
72
+ const input = e.target as HTMLInputElement;
73
+
74
+ onValueHandler(input.value);
75
+ };
76
+
77
+ const handleAriaLabel = () => {
78
+ if (outOfStock) return `${groupAriaLabel}: ${label} ${outOfStockLabel}`;
79
+ if (selected) return `${groupAriaLabel}: ${label} ${selectedLabel}`;
80
+ return `${groupAriaLabel}: ${label} ${swatchLabel}`;
81
+ };
82
+
83
+ return (
84
+ <label className={classes(['dropin-image-swatch__container', className])}>
85
+ <input
86
+ type={multi ? 'checkbox' : 'radio'}
87
+ name={name}
88
+ id={id}
89
+ value={value}
90
+ aria-label={handleAriaLabel()}
91
+ checked={selected}
92
+ disabled={disabled}
93
+ onChange={handleOnValue}
94
+ {...props}
95
+ className={classes([
96
+ 'dropin-image-swatch',
97
+ ['dropin-image-swatch--selected', selected],
98
+ ['dropin-image-swatch--disabled', disabled],
99
+ className,
100
+ ])}
101
+ />
102
+
103
+ <span
104
+ className={classes([
105
+ 'dropin-image-swatch__span',
106
+ ['dropin-image-swatch__span--out-of-stock', outOfStock],
107
+ className,
108
+ ])}
109
+ >
110
+ {imageNode || (
111
+ <Image
112
+ src={src}
113
+ className={classes(['dropin-image-swatch__content'])}
114
+ params={{ width: 100, fit: 'bounds', crop: true }}
115
+ alt={alt}
116
+ loading={'lazy'}
117
+ onError={(e: any) => (e.target.style.display = 'none')}
118
+ />
119
+ )}
120
+ </span>
121
+ </label>
122
+ );
123
+ };
@@ -0,0 +1,11 @@
1
+ /********************************************************************
2
+ * Copyright 2024 Adobe
3
+ * All Rights Reserved.
4
+ *
5
+ * NOTICE: Adobe permits you to use, modify, and distribute this
6
+ * file in accordance with the terms of the Adobe license agreement
7
+ * accompanying it.
8
+ *******************************************************************/
9
+
10
+ export * from '@adobe-commerce/elsie/components/ImageSwatch/ImageSwatch';
11
+ export { ImageSwatch as default } from '@adobe-commerce/elsie/components/ImageSwatch/ImageSwatch';