@faststore/core 2.1.98 → 2.1.100

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 (167) hide show
  1. package/.turbo/turbo-build.log +13 -7
  2. package/.turbo/turbo-lint.log +2 -0
  3. package/cms/faststore/sections.json +12 -0
  4. package/package.json +9 -10
  5. package/src/components/product/NotAvailableButton/NotAvailableButton.tsx +13 -0
  6. package/src/components/product/NotAvailableButton/index.ts +1 -0
  7. package/src/components/sections/Newsletter/Overrides.tsx +4 -0
  8. package/src/components/sections/ProductDetails/Overrides.tsx +4 -0
  9. package/src/components/sections/ProductDetails/ProductDetails.tsx +21 -15
  10. package/src/components/ui/Newsletter/Newsletter.tsx +5 -43
  11. package/src/components/ui/Newsletter/NewsletterAddendum.tsx +86 -0
  12. package/src/components/ui/ProductDetails/ProductDetailsSettings.tsx +88 -68
  13. package/src/pages/api/graphql.ts +0 -4
  14. package/src/server/index.ts +2 -5
  15. package/src/typings/overrides.ts +3 -0
  16. package/.next/BUILD_ID +0 -1
  17. package/.next/build-manifest.json +0 -129
  18. package/.next/cache/.tsbuildinfo +0 -1
  19. package/.next/cache/eslint/.cache_abdhua +0 -1
  20. package/.next/cache/next-server.js.nft.json +0 -1
  21. package/.next/cache/webpack/client-production/0.pack +0 -0
  22. package/.next/cache/webpack/client-production/index.pack +0 -0
  23. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  24. package/.next/cache/webpack/server-production/0.pack +0 -0
  25. package/.next/cache/webpack/server-production/index.pack +0 -0
  26. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  27. package/.next/export-marker.json +0 -1
  28. package/.next/images-manifest.json +0 -1
  29. package/.next/next-server.js.nft.json +0 -1
  30. package/.next/package.json +0 -1
  31. package/.next/prerender-manifest.json +0 -1
  32. package/.next/react-loadable-manifest.json +0 -44
  33. package/.next/required-server-files.json +0 -1
  34. package/.next/routes-manifest.json +0 -1
  35. package/.next/server/chunks/177.js +0 -120
  36. package/.next/server/chunks/183.js +0 -95
  37. package/.next/server/chunks/184.js +0 -61
  38. package/.next/server/chunks/186.js +0 -113
  39. package/.next/server/chunks/289.js +0 -239
  40. package/.next/server/chunks/312.js +0 -697
  41. package/.next/server/chunks/350.js +0 -144
  42. package/.next/server/chunks/483.js +0 -650
  43. package/.next/server/chunks/487.js +0 -9142
  44. package/.next/server/chunks/53.js +0 -61
  45. package/.next/server/chunks/530.js +0 -648
  46. package/.next/server/chunks/574.js +0 -145
  47. package/.next/server/chunks/576.js +0 -95
  48. package/.next/server/chunks/650.js +0 -9142
  49. package/.next/server/chunks/676.js +0 -32
  50. package/.next/server/chunks/693.js +0 -58
  51. package/.next/server/chunks/71.js +0 -1252
  52. package/.next/server/chunks/74.js +0 -4087
  53. package/.next/server/chunks/753.js +0 -509
  54. package/.next/server/chunks/779.js +0 -58
  55. package/.next/server/chunks/825.js +0 -4039
  56. package/.next/server/chunks/854.js +0 -72
  57. package/.next/server/chunks/859.js +0 -959
  58. package/.next/server/chunks/907.js +0 -1933
  59. package/.next/server/chunks/933.js +0 -521
  60. package/.next/server/chunks/98.js +0 -163
  61. package/.next/server/chunks/988.js +0 -211
  62. package/.next/server/chunks/font-manifest.json +0 -1
  63. package/.next/server/font-manifest.json +0 -1
  64. package/.next/server/middleware-build-manifest.js +0 -1
  65. package/.next/server/middleware-manifest.json +0 -6
  66. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  67. package/.next/server/pages/404.js +0 -386
  68. package/.next/server/pages/404.js.nft.json +0 -1
  69. package/.next/server/pages/500.js +0 -388
  70. package/.next/server/pages/500.js.nft.json +0 -1
  71. package/.next/server/pages/[...slug].js +0 -1006
  72. package/.next/server/pages/[...slug].js.nft.json +0 -1
  73. package/.next/server/pages/[slug]/p.js +0 -2289
  74. package/.next/server/pages/[slug]/p.js.nft.json +0 -1
  75. package/.next/server/pages/_app.js +0 -280
  76. package/.next/server/pages/_app.js.nft.json +0 -1
  77. package/.next/server/pages/_document.js +0 -374
  78. package/.next/server/pages/_document.js.nft.json +0 -1
  79. package/.next/server/pages/_error.js +0 -164
  80. package/.next/server/pages/_error.js.nft.json +0 -1
  81. package/.next/server/pages/account.js +0 -363
  82. package/.next/server/pages/account.js.nft.json +0 -1
  83. package/.next/server/pages/api/graphql.js +0 -371
  84. package/.next/server/pages/api/graphql.js.nft.json +0 -1
  85. package/.next/server/pages/api/health/live.js +0 -31
  86. package/.next/server/pages/api/health/live.js.nft.json +0 -1
  87. package/.next/server/pages/api/health/ready.js +0 -31
  88. package/.next/server/pages/api/health/ready.js.nft.json +0 -1
  89. package/.next/server/pages/api/preview.js +0 -148
  90. package/.next/server/pages/api/preview.js.nft.json +0 -1
  91. package/.next/server/pages/checkout.js +0 -363
  92. package/.next/server/pages/checkout.js.nft.json +0 -1
  93. package/.next/server/pages/en-US/404.html +0 -81
  94. package/.next/server/pages/en-US/404.json +0 -1
  95. package/.next/server/pages/en-US/500.html +0 -81
  96. package/.next/server/pages/en-US/500.json +0 -1
  97. package/.next/server/pages/en-US/account.html +0 -81
  98. package/.next/server/pages/en-US/account.json +0 -1
  99. package/.next/server/pages/en-US/checkout.html +0 -81
  100. package/.next/server/pages/en-US/checkout.json +0 -1
  101. package/.next/server/pages/en-US/login.html +0 -81
  102. package/.next/server/pages/en-US/login.json +0 -1
  103. package/.next/server/pages/en-US/s.html +0 -81
  104. package/.next/server/pages/en-US/s.json +0 -1
  105. package/.next/server/pages/en-US.html +0 -81
  106. package/.next/server/pages/en-US.json +0 -1
  107. package/.next/server/pages/index.js +0 -439
  108. package/.next/server/pages/index.js.nft.json +0 -1
  109. package/.next/server/pages/login.js +0 -368
  110. package/.next/server/pages/login.js.nft.json +0 -1
  111. package/.next/server/pages/s.js +0 -466
  112. package/.next/server/pages/s.js.nft.json +0 -1
  113. package/.next/server/pages-manifest.json +0 -18
  114. package/.next/server/webpack-api-runtime.js +0 -229
  115. package/.next/server/webpack-runtime.js +0 -229
  116. package/.next/static/6S44gOq9FRmyEQybDi2Yj/_buildManifest.js +0 -1
  117. package/.next/static/6S44gOq9FRmyEQybDi2Yj/_ssgManifest.js +0 -1
  118. package/.next/static/chunks/148.582eaa81293ee470.js +0 -1
  119. package/.next/static/chunks/238-c6f8d07b669a1ec2.js +0 -1
  120. package/.next/static/chunks/243-fcf2a6c4691476a2.js +0 -1
  121. package/.next/static/chunks/530.2650771daa532c02.js +0 -1
  122. package/.next/static/chunks/548-19272804c3f0ff0d.js +0 -1
  123. package/.next/static/chunks/574.70612be06fd1365f.js +0 -1
  124. package/.next/static/chunks/603-7b26158c40b8a3e5.js +0 -1
  125. package/.next/static/chunks/651.7142f31ce1e052b3.js +0 -1
  126. package/.next/static/chunks/709.7bc5a25ce30abda6.js +0 -1
  127. package/.next/static/chunks/738-a5ff304828f20cbf.js +0 -1
  128. package/.next/static/chunks/741.52f7fb873418346f.js +0 -1
  129. package/.next/static/chunks/98.40c7e17d9de4eb8f.js +0 -1
  130. package/.next/static/chunks/988.afda042dd9ba11d1.js +0 -1
  131. package/.next/static/chunks/framework-dfd14d7ce6600b03.js +0 -1
  132. package/.next/static/chunks/main-fd466221927468fd.js +0 -1
  133. package/.next/static/chunks/pages/404-af78f7cd1d3c1f60.js +0 -1
  134. package/.next/static/chunks/pages/500-f6346ca5f9dc4fef.js +0 -1
  135. package/.next/static/chunks/pages/[...slug]-ca533c74c22cb787.js +0 -1
  136. package/.next/static/chunks/pages/[slug]/p-78699b0be521b30b.js +0 -1
  137. package/.next/static/chunks/pages/_app-40cff0982559d0b8.js +0 -1
  138. package/.next/static/chunks/pages/_error-a7a0c1d9bfbb4f38.js +0 -1
  139. package/.next/static/chunks/pages/account-05bd79fb78365e88.js +0 -1
  140. package/.next/static/chunks/pages/checkout-c973786e68f25a39.js +0 -1
  141. package/.next/static/chunks/pages/index-d521ce4f4e2b89a6.js +0 -1
  142. package/.next/static/chunks/pages/login-8deb9243376b6aa1.js +0 -1
  143. package/.next/static/chunks/pages/s-0935734d9ac3acb8.js +0 -1
  144. package/.next/static/chunks/polyfills-c67a75d1b6f99dc8.js +0 -1
  145. package/.next/static/chunks/webpack-34aa83cb25f3f792.js +0 -1
  146. package/.next/static/css/527e334fa69cf40a.css +0 -1
  147. package/.next/static/css/6e1a7434f061d0ef.css +0 -1
  148. package/.next/static/css/7ca374e5534a3f68.css +0 -1
  149. package/.next/static/css/9e76fef1c9ca89af.css +0 -1
  150. package/.next/static/css/a2eefb25a4608343.css +0 -1
  151. package/.next/static/css/cb7d1fcea42fab9c.css +0 -1
  152. package/.next/static/css/d5a515091be5db82.css +0 -1
  153. package/.next/static/css/df588bb98c0b0ca6.css +0 -1
  154. package/.next/static/css/e3b039e8f5daf95f.css +0 -1
  155. package/.next/static/css/f0e2d1b8832e935d.css +0 -1
  156. package/.next/trace +0 -80
  157. package/public/~partytown/debug/partytown-atomics.js +0 -556
  158. package/public/~partytown/debug/partytown-media.js +0 -374
  159. package/public/~partytown/debug/partytown-sandbox-sw.js +0 -543
  160. package/public/~partytown/debug/partytown-sw.js +0 -59
  161. package/public/~partytown/debug/partytown-ww-atomics.js +0 -1789
  162. package/public/~partytown/debug/partytown-ww-sw.js +0 -1781
  163. package/public/~partytown/debug/partytown.js +0 -72
  164. package/public/~partytown/partytown-atomics.js +0 -2
  165. package/public/~partytown/partytown-media.js +0 -2
  166. package/public/~partytown/partytown-sw.js +0 -2
  167. package/public/~partytown/partytown.js +0 -2
@@ -1,10 +1,16 @@
1
1
  $ yarn partytown && next build
2
2
  $ partytown copylib ./public/~partytown
3
- Partytown lib copied to: /Users/emersonlaurentino/develop/vtex/faststore/packages/core/public/~partytown
3
+ Partytown lib copied to: /home/runner/work/faststore/faststore/packages/core/public/~partytown
4
+ warn - No build cache found. Please configure build caching for faster rebuilds. Read more: https://nextjs.org/docs/messages/no-cache
5
+ Attention: Next.js now collects completely anonymous telemetry regarding usage.
6
+ This information is used to shape Next.js' roadmap and prioritize features.
7
+ You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
8
+ https://nextjs.org/telemetry
9
+
4
10
  info - Linting and checking validity of types...
5
11
  info - Creating an optimized production build...
6
12
  info - Disabled SWC as replacement for Babel because of custom Babel configuration ".babelrc" https://nextjs.org/docs/messages/swc-disabled
7
- info - Using external babel configuration from /Users/emersonlaurentino/develop/vtex/faststore/packages/core/.babelrc
13
+ info - Using external babel configuration from /home/runner/work/faststore/faststore/packages/core/.babelrc
8
14
  info - Compiled successfully
9
15
  info - Collecting page data...
10
16
  info - Generating static pages (0/7)
@@ -15,11 +21,11 @@ info - Generating static pages (7/7)
15
21
  info - Finalizing page optimization...
16
22
 
17
23
  Route (pages) Size First Load JS
18
- ┌ ● / 3.16 kB 197 kB
24
+ ┌ ● / 3.15 kB 130 kB
19
25
  ├ /_app 0 B 77.9 kB
20
- ├ ● /[...slug] 4.06 kB 209 kB
26
+ ├ ● /[...slug] 4.06 kB 141 kB
21
27
  ├ └ css/527e334fa69cf40a.css 1.85 kB
22
- ├ ● /[slug]/p 10.5 kB 205 kB
28
+ ├ ● /[slug]/p 10.6 kB 137 kB
23
29
  ├ └ css/7ca374e5534a3f68.css 11.3 kB
24
30
  ├ ○ /404 1.19 kB 114 kB
25
31
  ├ ● /500 1.21 kB 114 kB
@@ -31,11 +37,11 @@ Route (pages) Size First Load JS
31
37
  ├ ● /checkout 657 B 113 kB
32
38
  ├ ● /login 1.09 kB 114 kB
33
39
  └ ● /s 4.6 kB 128 kB
34
- + First Load JS shared by all 81 kB
40
+ + First Load JS shared by all 80.9 kB
35
41
  ├ chunks/framework-dfd14d7ce6600b03.js 45.3 kB
36
42
  ├ chunks/main-fd466221927468fd.js 23.9 kB
37
43
  ├ chunks/pages/_app-40cff0982559d0b8.js 6.46 kB
38
- ├ chunks/webpack-34aa83cb25f3f792.js 2.29 kB
44
+ ├ chunks/webpack-7bbdc4ae00f1fa43.js 2.22 kB
39
45
  └ css/9e76fef1c9ca89af.css 3.06 kB
40
46
 
41
47
  λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
@@ -0,0 +1,2 @@
1
+ $ next lint
2
+ ✔ No ESLint warnings or errors
@@ -1374,6 +1374,18 @@
1374
1374
  }
1375
1375
  }
1376
1376
  },
1377
+ "notAvailableButton": {
1378
+ "title": "Not Available Button",
1379
+ "description": "Shown when a SKU is not available",
1380
+ "type": "object",
1381
+ "properties": {
1382
+ "title": {
1383
+ "title": "Title",
1384
+ "type": "string",
1385
+ "default": "Not Available"
1386
+ }
1387
+ }
1388
+ },
1377
1389
  "shippingSimulator": {
1378
1390
  "title": "Shipping Simulation",
1379
1391
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "2.1.98",
3
+ "version": "2.1.100",
4
4
  "license": "MIT",
5
5
  "browserslist": "supports es6-module and not dead",
6
6
  "scripts": {
@@ -29,18 +29,17 @@
29
29
  "@envelop/graphql-jit": "^1.1.1",
30
30
  "@envelop/parser-cache": "^2.2.0",
31
31
  "@envelop/validation-cache": "^2.2.0",
32
- "@faststore/api": "^2.1.98",
33
- "@faststore/components": "^2.1.94",
34
- "@faststore/graphql-utils": "^2.1.82",
35
- "@faststore/sdk": "^2.1.82",
36
- "@faststore/ui": "^2.1.94",
32
+ "@faststore/api": "^2.1.99",
33
+ "@faststore/components": "^2.1.99",
34
+ "@faststore/graphql-utils": "^2.1.99",
35
+ "@faststore/sdk": "^2.1.99",
36
+ "@faststore/ui": "^2.1.99",
37
37
  "@types/react": "^18.0.14",
38
38
  "@vtex/client-cms": "^0.2.12",
39
39
  "autoprefixer": "^10.4.0",
40
40
  "chalk": "^5.2.0",
41
41
  "css-loader": "^6.7.1",
42
- "draft-js": "^0.11.7",
43
- "draft-js-export-html": "^1.4.1",
42
+ "draftjs-to-html": "^0.9.1",
44
43
  "graphql": "^15.0.0",
45
44
  "include-media": "^1.4.10",
46
45
  "msw": "^0.43.1",
@@ -61,7 +60,7 @@
61
60
  },
62
61
  "devDependencies": {
63
62
  "@cypress/code-coverage": "^3.12.1",
64
- "@faststore/eslint-config": "^2.1.82",
63
+ "@faststore/eslint-config": "^2.1.99",
65
64
  "@faststore/lighthouse": "^1.12.32",
66
65
  "@graphql-codegen/cli": "^2.6.2",
67
66
  "@graphql-codegen/typescript": "^2.5.1",
@@ -110,5 +109,5 @@
110
109
  "msw": {
111
110
  "workerDirectory": "public"
112
111
  },
113
- "gitHead": "5b295774a676946af022c70fb0d04914d3bd3a44"
112
+ "gitHead": "c1c5860557d7751c49caba0b8dae76f0f8926d85"
114
113
  }
@@ -0,0 +1,13 @@
1
+ import { PropsWithChildren } from 'react'
2
+ import { Button } from '@faststore/ui'
3
+
4
+ // TODO: Remove this component when <OutOfStock /> is ready to use
5
+ function NotAvailableButton({ children }: PropsWithChildren) {
6
+ return (
7
+ <Button variant="primary" disabled data-fs-buy-button>
8
+ {children}
9
+ </Button>
10
+ )
11
+ }
12
+
13
+ export default NotAvailableButton
@@ -0,0 +1 @@
1
+ export { default } from './NotAvailableButton'
@@ -3,6 +3,7 @@ import {
3
3
  InputField as UIInputField,
4
4
  Icon as UIIcon,
5
5
  } from '@faststore/ui'
6
+ import { NewsletterAddendum } from 'src/components/ui/Newsletter/NewsletterAddendum'
6
7
 
7
8
  import { getSectionOverrides } from 'src/utils/overrides'
8
9
  import { override } from 'src/customizations/components/overrides/Newsletter'
@@ -15,6 +16,7 @@ const {
15
16
  InputFieldName,
16
17
  InputFieldEmail,
17
18
  Button,
19
+ __experimentalNewsletterAddendum,
18
20
  } = getSectionOverrides(
19
21
  {
20
22
  ToastIconSuccess: UIIcon,
@@ -23,6 +25,7 @@ const {
23
25
  InputFieldName: UIInputField,
24
26
  InputFieldEmail: UIInputField,
25
27
  Button: UIButton,
28
+ __experimentalNewsletterAddendum: NewsletterAddendum,
26
29
  },
27
30
  override as NewsletterOverrideDefinition
28
31
  )
@@ -34,4 +37,5 @@ export {
34
37
  InputFieldName,
35
38
  InputFieldEmail,
36
39
  Button,
40
+ __experimentalNewsletterAddendum,
37
41
  }
@@ -14,6 +14,7 @@ import {
14
14
  import LocalImageGallery from 'src/components/ui/ImageGallery'
15
15
  import LocalShippingSimulation from 'src/components/ui/ShippingSimulation/ShippingSimulation'
16
16
  import { Image } from 'src/components/ui/Image'
17
+ import LocalNotAvailableButton from 'src/components/product/NotAvailableButton'
17
18
 
18
19
  import { getSectionOverrides } from 'src/utils/overrides'
19
20
  import { override } from 'src/customizations/components/overrides/ProductDetails'
@@ -33,6 +34,7 @@ const {
33
34
  __experimentalImageGalleryImage,
34
35
  __experimentalImageGallery,
35
36
  __experimentalShippingSimulation,
37
+ __experimentalNotAvailableButton,
36
38
  } = getSectionOverrides(
37
39
  {
38
40
  ProductTitle: UIProductTitle,
@@ -48,6 +50,7 @@ const {
48
50
  __experimentalImageGalleryImage: Image,
49
51
  __experimentalImageGallery: LocalImageGallery,
50
52
  __experimentalShippingSimulation: LocalShippingSimulation,
53
+ __experimentalNotAvailableButton: LocalNotAvailableButton,
51
54
  },
52
55
  override as ProductDetailsOverrideDefinition
53
56
  )
@@ -66,4 +69,5 @@ export {
66
69
  __experimentalImageGalleryImage,
67
70
  __experimentalImageGallery,
68
71
  __experimentalShippingSimulation,
72
+ __experimentalNotAvailableButton,
69
73
  }
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react'
1
+ import { useEffect, useState, useMemo } from 'react'
2
2
 
3
3
  import { gql } from '@faststore/graphql-utils'
4
4
  import { sendAnalyticsEvent } from '@faststore/sdk'
@@ -21,6 +21,7 @@ import {
21
21
  DiscountBadge,
22
22
  __experimentalImageGallery as ImageGallery,
23
23
  __experimentalShippingSimulation as ShippingSimulation,
24
+ __experimentalNotAvailableButton as NotAvailableButton,
24
25
  } from 'src/components/sections/ProductDetails/Overrides'
25
26
 
26
27
  interface ProductDetailsContextProps {
@@ -59,6 +60,9 @@ export interface ProductDetailsProps {
59
60
  imageGallery: {
60
61
  imagePosition: 'top' | 'center' | 'bottom'
61
62
  }
63
+ notAvailableButton: {
64
+ title: string
65
+ }
62
66
  }
63
67
 
64
68
  function ProductDetails({
@@ -91,6 +95,9 @@ function ProductDetails({
91
95
  displayDescription: shouldDisplayProductDescription,
92
96
  },
93
97
  imageGallery: { imagePosition = ImageGallery.props.imagePosition },
98
+ notAvailableButton: {
99
+ title: notAvailableButtonTitle = NotAvailableButton.props.title,
100
+ },
94
101
  }: ProductDetailsProps & ProductDetailsContextProps) {
95
102
  const { currency } = useSession()
96
103
  const [quantity, setQuantity] = useState(1)
@@ -155,7 +162,10 @@ function ProductDetails({
155
162
  gtin,
156
163
  ])
157
164
 
158
- const outOfStock = availability == 'https://schema.org/OutOfStock'
165
+ const outOfStock = useMemo(
166
+ () => availability === 'https://schema.org/OutOfStock',
167
+ [availability]
168
+ )
159
169
 
160
170
  return (
161
171
  <Section className={`${styles.section} section-product-details`}>
@@ -194,19 +204,15 @@ function ProductDetails({
194
204
  data-fs-product-details-settings
195
205
  data-fs-product-details-section
196
206
  >
197
- {!outOfStock ? (
198
- <ProductDetailsSettings
199
- product={data.product}
200
- isValidating={isValidating}
201
- buyButtonTitle={buyButtonTitle}
202
- quantity={quantity}
203
- setQuantity={setQuantity}
204
- buyButtonIcon={buyButtonIcon}
205
- />
206
- ) : (
207
- // TODO: Adds <OutOfStock /> when component is ready to use
208
- <p>Not Available</p>
209
- )}
207
+ <ProductDetailsSettings
208
+ product={data.product}
209
+ isValidating={isValidating}
210
+ buyButtonTitle={buyButtonTitle}
211
+ quantity={quantity}
212
+ setQuantity={setQuantity}
213
+ buyButtonIcon={buyButtonIcon}
214
+ notAvailableButtonTitle={notAvailableButtonTitle}
215
+ />
210
216
  </section>
211
217
 
212
218
  {!outOfStock && (
@@ -1,7 +1,5 @@
1
1
  import { ComponentPropsWithRef, FormEvent, useMemo } from 'react'
2
2
  import { forwardRef, useRef } from 'react'
3
- import { convertFromRaw } from 'draft-js'
4
- import { stateToHTML } from 'draft-js-export-html'
5
3
  import { useUI } from '@faststore/ui'
6
4
  import type { InputFieldProps } from '@faststore/ui'
7
5
 
@@ -13,39 +11,9 @@ import {
13
11
  InputFieldName,
14
12
  InputFieldEmail,
15
13
  Button,
14
+ __experimentalNewsletterAddendum as NewsletterAddendum,
16
15
  } from 'src/components/sections/Newsletter/Overrides'
17
16
 
18
- const cmsToHtml = (content) => {
19
- if (!content) {
20
- return ''
21
- }
22
-
23
- const rawDraftContentState = JSON.parse(content)
24
- const html = stateToHTML(convertFromRaw(rawDraftContentState), {
25
- entityStyleFn: (entity) => {
26
- const entityType = entity.get('type').toLowerCase()
27
-
28
- if (entityType === 'link') {
29
- const data = entity.getData()
30
-
31
- return {
32
- element: 'a',
33
- attributes: {
34
- 'data-fs-link': 'true',
35
- 'data-fs-link-variant': 'inline',
36
- 'data-fs-link-inverse': 'true',
37
- 'data-fs-link-size': 'regular',
38
- 'data-testid': 'fs-link',
39
- href: data.url,
40
- },
41
- }
42
- }
43
- },
44
- })
45
-
46
- return html
47
- }
48
-
49
17
  export type SubscribeMessage = {
50
18
  title: string
51
19
  message: string
@@ -124,10 +92,6 @@ const Newsletter = forwardRef<HTMLFormElement, NewsletterProps>(
124
92
  const { subscribeUser, loading, data } = useNewsletter()
125
93
  const nameInputRef = useRef<HTMLInputElement>(null)
126
94
  const emailInputRef = useRef<HTMLInputElement>(null)
127
- const subscriptionButtonLabel = useMemo(
128
- () => (loading ? subscribeButtonLoadingLabel : subscribeButtonLabel),
129
- [loading, subscribeButtonLabel, subscribeButtonLoadingLabel]
130
- )
131
95
 
132
96
  const { pushToast } = useUI()
133
97
 
@@ -217,12 +181,10 @@ const Newsletter = forwardRef<HTMLFormElement, NewsletterProps>(
217
181
  // This decision can be reviewed later if needed
218
182
  inputRef={emailInputRef}
219
183
  />
220
- <span
221
- data-fs-newsletter-addendum
222
- dangerouslySetInnerHTML={{
223
- __html: cmsToHtml(privacyPolicy),
224
- }}
225
- ></span>
184
+ <NewsletterAddendum.Component
185
+ addendum={privacyPolicy}
186
+ {...NewsletterAddendum.props}
187
+ />
226
188
  <Button.Component
227
189
  variant="secondary"
228
190
  inverse
@@ -0,0 +1,86 @@
1
+ /**
2
+ * TODO:
3
+ *
4
+ * This library hasn't been updated in 4 years at the time of writing.
5
+ *
6
+ * We're using it as a patch, and it is expected that some Rich Text use cases of draftjs are not supported by it.
7
+ * We have the dependency on draftjs because of the way the headless CMS send us the data.
8
+ *
9
+ * This library should be removed as soon as possible, which will probably be possible through one of two cases:
10
+ * 1. We support React Server Components and go back to using the official draftjs library for doing this, only on the server.
11
+ * This is still not the ideal solution, since it still relies on a unsupported, deprecated and archived library (draftjs).
12
+ * 2. CMS uses a new library or changes how it sends the data to not depend on draftjs.
13
+ *
14
+ * This is a limitation not only for this component, but for every native & custom component that makes use of Rich Text.
15
+ */
16
+ import draftToHtml from 'draftjs-to-html'
17
+
18
+ export interface NewsletterAddendumProps {
19
+ /**
20
+ * Expects a string of a JSON object in the form of draftjs's raw content state.
21
+ */
22
+ addendum: string
23
+ }
24
+
25
+ function getLinkElementAsString(url: string, text: string) {
26
+ return `
27
+ <a
28
+ data-fs-link="true"
29
+ data-fs-link-variant="inline"
30
+ data-fs-link-inverse="true"
31
+ data-fs-link-size="regular"
32
+ data-testid="fs-link"
33
+ href="${url}"
34
+ >${text}</a>`
35
+ }
36
+
37
+ function cmsToHtml(addendum: string) {
38
+ if (!addendum) {
39
+ return ''
40
+ }
41
+
42
+ let rawDraftContentState = null
43
+
44
+ try {
45
+ rawDraftContentState = JSON.parse(addendum)
46
+ } catch (e) {
47
+ throw new Error(
48
+ 'Newsletter\'s prop "addendum" is not a JSON string. This is happening because the overridden prop is malformed or the CMS is providing malformed content for that prop.',
49
+ {
50
+ cause: e,
51
+ }
52
+ )
53
+ }
54
+
55
+ if (!rawDraftContentState) {
56
+ return ''
57
+ }
58
+
59
+ return draftToHtml(
60
+ rawDraftContentState,
61
+ undefined,
62
+ undefined,
63
+ (entity: { type: string; data: { url: string } }, text: string) => {
64
+ if (entity.type !== 'LINK') {
65
+ return null
66
+ }
67
+
68
+ return getLinkElementAsString(entity.data.url, text)
69
+ }
70
+ )
71
+ }
72
+
73
+ export function NewsletterAddendum({
74
+ addendum,
75
+ ...otherProps
76
+ }: NewsletterAddendumProps) {
77
+ return (
78
+ <span
79
+ data-fs-newsletter-addendum
80
+ dangerouslySetInnerHTML={{
81
+ __html: cmsToHtml(addendum),
82
+ }}
83
+ {...otherProps}
84
+ ></span>
85
+ )
86
+ }
@@ -1,4 +1,5 @@
1
1
  import type { Dispatch, SetStateAction } from 'react'
2
+ import { useMemo } from 'react'
2
3
 
3
4
  import type { ProductDetailsFragment_ProductFragment } from '@generated/graphql'
4
5
 
@@ -13,6 +14,7 @@ import {
13
14
  Icon,
14
15
  Price,
15
16
  QuantitySelector,
17
+ __experimentalNotAvailableButton as NotAvailableButton,
16
18
  } from 'src/components/sections/ProductDetails/Overrides'
17
19
 
18
20
  interface ProductDetailsSettingsProps {
@@ -25,6 +27,7 @@ interface ProductDetailsSettingsProps {
25
27
  isValidating: boolean
26
28
  quantity: number
27
29
  setQuantity: Dispatch<SetStateAction<number>>
30
+ notAvailableButtonTitle: string
28
31
  }
29
32
 
30
33
  function ProductDetailsSettings({
@@ -37,6 +40,7 @@ function ProductDetailsSettings({
37
40
  icon: buyButtonIconName = Icon.props.name,
38
41
  alt: buyButtonIconAlt = Icon.props['aria-label'],
39
42
  },
43
+ notAvailableButtonTitle,
40
44
  }: ProductDetailsSettingsProps) {
41
45
  const {
42
46
  id,
@@ -54,8 +58,6 @@ function ProductDetailsSettings({
54
58
  },
55
59
  } = product
56
60
 
57
- const buyDisabled = availability !== 'https://schema.org/InStock'
58
-
59
61
  const buyProps = useBuyButton({
60
62
  id,
61
63
  price,
@@ -73,62 +75,94 @@ function ProductDetailsSettings({
73
75
  },
74
76
  })
75
77
 
76
- const shouldShowDiscountedPrice = lowPrice !== listPrice
78
+ const outOfStock = useMemo(
79
+ () => availability === 'https://schema.org/OutOfStock',
80
+ [availability]
81
+ )
82
+ const shouldShowDiscountedPrice = useMemo(
83
+ () => lowPrice !== listPrice,
84
+ [lowPrice, listPrice]
85
+ )
86
+
87
+ const AddToCartButton = () => {
88
+ return outOfStock ? (
89
+ // TODO: Adds <OutOfStock /> when component is ready to use
90
+ <NotAvailableButton.Component>
91
+ {notAvailableButtonTitle}
92
+ </NotAvailableButton.Component>
93
+ ) : (
94
+ <BuyButton.Component
95
+ {...BuyButton.props}
96
+ icon={
97
+ <Icon.Component
98
+ {...Icon.props}
99
+ name={buyButtonIconName}
100
+ aria-label={buyButtonIconAlt}
101
+ />
102
+ }
103
+ {...buyProps}
104
+ >
105
+ {buyButtonTitle || 'Add to Cart'}
106
+ </BuyButton.Component>
107
+ )
108
+ }
77
109
 
78
110
  return (
79
111
  <>
80
- <section data-fs-product-details-values>
81
- <div data-fs-product-details-prices>
82
- {shouldShowDiscountedPrice ? (
83
- <>
112
+ {!outOfStock && (
113
+ <section data-fs-product-details-values>
114
+ <div data-fs-product-details-prices>
115
+ {shouldShowDiscountedPrice ? (
116
+ <>
117
+ <Price.Component
118
+ formatter={useFormattedPrice}
119
+ testId="list-price"
120
+ variant="listing"
121
+ SRText="Original price:"
122
+ {...Price.props}
123
+ // Dynamic props shouldn't be overridable
124
+ // This decision can be reviewed later if needed
125
+ value={listPrice}
126
+ data-value={listPrice}
127
+ />
128
+ <Price.Component
129
+ formatter={useFormattedPrice}
130
+ testId="price"
131
+ variant="spot"
132
+ className="text__lead"
133
+ SRText="Sale Price:"
134
+ {...Price.props}
135
+ // Dynamic props shouldn't be overridable
136
+ // This decision can be reviewed later if needed
137
+ value={lowPrice}
138
+ data-value={lowPrice}
139
+ />
140
+ </>
141
+ ) : (
84
142
  <Price.Component
85
143
  formatter={useFormattedPrice}
86
144
  testId="list-price"
87
- variant="listing"
88
- SRText="Original price:"
89
- {...Price.props}
90
- // Dynamic props shouldn't be overridable
91
- // This decision can be reviewed later if needed
92
- value={listPrice}
93
- data-value={listPrice}
94
- />
95
- <Price.Component
96
- formatter={useFormattedPrice}
97
- testId="price"
98
145
  variant="spot"
99
146
  className="text__lead"
100
- SRText="Sale Price:"
147
+ SRText="Original price:"
101
148
  {...Price.props}
102
149
  // Dynamic props shouldn't be overridable
103
150
  // This decision can be reviewed later if needed
104
151
  value={lowPrice}
105
152
  data-value={lowPrice}
106
153
  />
107
- </>
108
- ) : (
109
- <Price.Component
110
- formatter={useFormattedPrice}
111
- testId="list-price"
112
- variant="spot"
113
- className="text__lead"
114
- SRText="Original price:"
115
- {...Price.props}
116
- // Dynamic props shouldn't be overridable
117
- // This decision can be reviewed later if needed
118
- value={lowPrice}
119
- data-value={lowPrice}
120
- />
121
- )}
122
- </div>
123
- <QuantitySelector.Component
124
- min={1}
125
- max={10}
126
- {...QuantitySelector.props}
127
- // Dynamic props shouldn't be overridable
128
- // This decision can be reviewed later if needed
129
- onChange={setQuantity}
130
- />
131
- </section>
154
+ )}
155
+ </div>
156
+ <QuantitySelector.Component
157
+ min={1}
158
+ max={10}
159
+ {...QuantitySelector.props}
160
+ // Dynamic props shouldn't be overridable
161
+ // This decision can be reviewed later if needed
162
+ onChange={setQuantity}
163
+ />
164
+ </section>
165
+ )}
132
166
  {skuVariants && (
133
167
  <Selectors
134
168
  slugsMap={skuVariants.slugsMap}
@@ -137,30 +171,16 @@ function ProductDetailsSettings({
137
171
  data-fs-product-details-selectors
138
172
  />
139
173
  )}
140
- {
141
- /* NOTE: A loading skeleton had to be used to avoid a Lighthouse's
142
- non-composited animation violation due to the button transitioning its
143
- background color when changing from its initial disabled to active state.
144
- See full explanation on commit https://git.io/JyXV5. */
145
- isValidating ? (
146
- <AddToCartLoadingSkeleton />
147
- ) : (
148
- <BuyButton.Component
149
- {...BuyButton.props}
150
- icon={
151
- <Icon.Component
152
- {...Icon.props}
153
- aria-label={buyButtonIconAlt}
154
- name={buyButtonIconName}
155
- />
156
- }
157
- disabled={buyDisabled}
158
- {...buyProps}
159
- >
160
- {buyButtonTitle || 'Add to Cart'}
161
- </BuyButton.Component>
162
- )
163
- }
174
+ {isValidating ? (
175
+ /* NOTE:
176
+ A loading skeleton had to be used to avoid a Lighthouse's
177
+ non-composited animation violation due to the button transitioning its
178
+ background color when changing from its initial disabled to active state.
179
+ See full explanation on commit https://git.io/JyXV5. */
180
+ <AddToCartLoadingSkeleton />
181
+ ) : (
182
+ <AddToCartButton />
183
+ )}
164
184
  </>
165
185
  )
166
186
  }
@@ -59,10 +59,6 @@ const handler: NextApiHandler = async (request, response) => {
59
59
  ? stringifyCacheControl(extensions.cacheControl)
60
60
  : 'no-cache, no-store'
61
61
 
62
- if (extensions.cookies && !hasErrors) {
63
- response.setHeader('set-cookie', extensions.cookies)
64
- }
65
-
66
62
  response.setHeader('cache-control', cacheControl)
67
63
  response.setHeader('content-type', 'application/json')
68
64
  response.send(JSON.stringify({ data, errors }))