diff --git a/plugins/woocommerce-blocks/assets/js/base/README.MD b/plugins/woocommerce-blocks/assets/js/base/README.MD index 64302efd50b..f7f51087d1d 100644 --- a/plugins/woocommerce-blocks/assets/js/base/README.MD +++ b/plugins/woocommerce-blocks/assets/js/base/README.MD @@ -1,11 +1,12 @@ # Base Components/Context/Hooks -Base components are designed to be used on the frontend of a store. Due to this, we need to avoid using heavy WordPress externals as dependencies (wp-components, wp-block-editor, etc). To get around this, import from a local package instead. +Base components are designed to be used on the frontend of a store. Due to this, we need to avoid using heavy WordPress externals as dependencies (`@wordpress/blocks`, `@wordpress/block-editor`, etc). -e.g. Instead of importing from `@wordpress/components`, use: +Note 2 exceptions, we do use the `Slot` and `Fill` which we [import and bundle](../../../packages/checkout/slot/index.js) from `@wordpress/components`. Otherwise you should avoid importing anything else from that package. -```js -import { Component } from 'wordpress-components'; -``` +The other exception is the `FormTokenField`, which is used in deprecated blocks and use of [that import](../base/components/form-token-field/) is also deprecated. + +If you need primitive/low-level components to build components in this library, please use [Ariakit](https://ariakit.org/) to build them. +See [Button](components/button/index.tsx) for an example. Check the built `*.assets.php` files to ensure extra dependencies aren't being added to the build. diff --git a/plugins/woocommerce-blocks/docs/contributors/javascript-build-system.md b/plugins/woocommerce-blocks/docs/contributors/javascript-build-system.md index f1bd44bbc2c..5684d1d0072 100644 --- a/plugins/woocommerce-blocks/docs/contributors/javascript-build-system.md +++ b/plugins/woocommerce-blocks/docs/contributors/javascript-build-system.md @@ -43,11 +43,13 @@ In practice, that means the dependency version is: [`@wordpress/dependency-extraction-webpack-plugin`](https://developer.wordpress.org/block-editor/packages/packages-dependency-extraction-webpack-plugin/) script is applied to each of the build processes: Core, Main, Frontend, Payments, Extensions. -### `wordpress-components` alias +### `wordpress-components` and `wordpress-components-slotfill` alias -`@wordpress/components` is one of the dependencies that gets externalized by the plugin. You can see the external `components.min.js` file being loaded on the Editor pages containing blocks that depend on the `@wordpress/components`, while the code is not bundled within WooCommerce Blocks files. +We have an alias to the 14.2.0 version of `@wordpress/components` called `wordpress-components`. This alias was used to import a limited set of components in front-end components. This alias is deprecated and **should no longer be used**. When the `FormTokenField` component is removed from the codebase, the alias will be removed as well. -For optimization purposes, there's a `wordpress-components` alias created, which is used in the frontend code. Thanks to that, the dependency omits the Dependency Extraction Plugin and behaves like any other dependency: it's tree-shaken and only the necessary code is bundled with the block. That allows us to avoid loading the whole `@wordpress/components` on the frontend pages. +We also have one other alias `wordpress-components-slotfill` which is used to import the `SlotFill` and `Fill` components from `@wordpress/components`. This alias **should not be used to import any other dependencies from `@wordpress/components`**. Note that it is tree-shaken so should only bundle the small amount of code directly needed to support slot fill functionality. + +If you need to build front-end components in future, you should use Ariakit to build them. See examples like the [Button](../../assets/js/base/components/button/index.tsx) component for how to build components using Ariakit. ## Aliases @@ -84,4 +86,3 @@ Webpack config is split between several files: 🐞 Found a mistake, or have a suggestion? [Leave feedback about this document here.](https://github.com/woocommerce/woocommerce-blocks/issues/new?assignees=&labels=type%3A+documentation&template=--doc-feedback.md&title=Feedback%20on%20./docs/contributors/javascript-build-system.md) - diff --git a/plugins/woocommerce-blocks/package.json b/plugins/woocommerce-blocks/package.json index 05a1fce9275..1fac577b36b 100644 --- a/plugins/woocommerce-blocks/package.json +++ b/plugins/woocommerce-blocks/package.json @@ -315,7 +315,8 @@ "trim-html": "0.1.9", "use-debounce": "9.0.4", "usehooks-ts": "^2.9.1", - "wordpress-components": "npm:@wordpress/components@14.2.0" + "wordpress-components": "npm:@wordpress/components@14.2.0", + "wordpress-components-slotfill": "npm:@wordpress/components@wp-6.5" }, "peerDependencies": { "react": "^18.3.0", diff --git a/plugins/woocommerce-blocks/packages/checkout/components/discounts-meta/index.js b/plugins/woocommerce-blocks/packages/checkout/components/discounts-meta/index.js index b0cf6eb3ee8..24b69c6dcc5 100644 --- a/plugins/woocommerce-blocks/packages/checkout/components/discounts-meta/index.js +++ b/plugins/woocommerce-blocks/packages/checkout/components/discounts-meta/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * Internal dependencies */ -import { createSlotFill, hasValidFills, useSlot } from '../../slot'; +import { createSlotFill, hasValidFills, useSlotFills } from '../../slot'; import TotalsWrapper from '../../../components/totals-wrapper'; const slotName = '__experimentalDiscountsMeta'; @@ -15,7 +15,7 @@ const { Fill: ExperimentalDiscountsMeta, Slot: DiscountsMetaSlot } = createSlotFill( slotName ); const Slot = ( { className, extensions, cart, context } ) => { - const { fills } = useSlot( slotName ); + const fills = useSlotFills( slotName ); return ( hasValidFills( fills ) && ( diff --git a/plugins/woocommerce-blocks/packages/checkout/components/order-meta/index.js b/plugins/woocommerce-blocks/packages/checkout/components/order-meta/index.js index 6426b7f203a..765b7957c75 100644 --- a/plugins/woocommerce-blocks/packages/checkout/components/order-meta/index.js +++ b/plugins/woocommerce-blocks/packages/checkout/components/order-meta/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * Internal dependencies */ -import { createSlotFill, hasValidFills, useSlot } from '../../slot'; +import { createSlotFill, hasValidFills, useSlotFills } from '../../slot'; import TotalsWrapper from '../../../components/totals-wrapper'; const slotName = '__experimentalOrderMeta'; @@ -15,7 +15,8 @@ const { Fill: ExperimentalOrderMeta, Slot: OrderMetaSlot } = createSlotFill( slotName ); const Slot = ( { className, extensions, cart, context } ) => { - const { fills } = useSlot( slotName ); + const fills = useSlotFills( slotName ); + return ( hasValidFills( fills ) && ( diff --git a/plugins/woocommerce-blocks/packages/checkout/index.js b/plugins/woocommerce-blocks/packages/checkout/index.js index c23f7c07fa4..f80118f2759 100644 --- a/plugins/woocommerce-blocks/packages/checkout/index.js +++ b/plugins/woocommerce-blocks/packages/checkout/index.js @@ -3,4 +3,7 @@ export * from './utils'; export * from './slot'; export * from './filter-registry'; export * from './blocks-registry'; -export { SlotFillProvider } from 'wordpress-components'; + +// It is very important to export this directly from the build module to avoid introducing side-effects +// from importing the index of the @wordpress/components package. +export { Provider as SlotFillProvider } from 'wordpress-components-slotfill/build-module/slot-fill'; diff --git a/plugins/woocommerce-blocks/packages/checkout/slot/index.js b/plugins/woocommerce-blocks/packages/checkout/slot/index.js index 1d6bf6d7672..a668a98ffa7 100644 --- a/plugins/woocommerce-blocks/packages/checkout/slot/index.js +++ b/plugins/woocommerce-blocks/packages/checkout/slot/index.js @@ -1,41 +1,22 @@ /** * External dependencies */ -import deprecated from '@wordpress/deprecated'; import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings'; import { Children, cloneElement } from '@wordpress/element'; +// It is very important to export this directly from the build module to avoid introducing side-effects +// from importing the index of the @wordpress/components package. +// eslint-disable-next-line -- When adding comments to imports it breaks the external/internal dependencies lint. import { createSlotFill as baseCreateSlotFill, - __experimentalUseSlot, - useSlot as __useSlot, //eslint-disable-line -} from 'wordpress-components'; + useSlot, + useSlotFills, +} from 'wordpress-components-slotfill/build-module/slot-fill'; /** * Internal dependencies */ import BlockErrorBoundary from '../components/error-boundary'; -/** - * This function is used in case __experimentalUseSlot is removed and useSlot is not released, it tries to mock - * the return value of that slot. - * - * @return {Object} The hook mocked return, currently: - * fills, a null array of length 2. - */ -const mockedUseSlot = () => { - /** - * If we're here, it means useSlot was never graduated and __experimentalUseSlot is removed, so we should change our code. - * - */ - deprecated( '__experimentalUseSlot', { - plugin: 'woocommerce-gutenberg-products-block', - } ); - // We're going to mock its value - return { - fills: new Array( 2 ), - }; -}; - /** * Checks if this slot has any valid fills. A valid fill is one that isn't falsy. * @@ -45,23 +26,7 @@ const mockedUseSlot = () => { export const hasValidFills = ( fills ) => Array.isArray( fills ) && fills.filter( Boolean ).length > 0; -/** - * A hook that is used inside a slotFillProvider to return information on the a slot. - * - * @param {string} slotName The slot name to be hooked into. - * @return {Object} slot data. - */ -let useSlot; - -if ( typeof __useSlot === 'function' ) { - useSlot = __useSlot; -} else if ( typeof __experimentalUseSlot === 'function' ) { - useSlot = __experimentalUseSlot; -} else { - useSlot = mockedUseSlot; -} - -export { useSlot }; +export { useSlot, useSlotFills }; /** * Abstracts @wordpress/components createSlotFill, wraps Fill in an error boundary and passes down fillProps. @@ -76,7 +41,7 @@ export const createSlotFill = ( slotName, onError = null ) => { /** * A Fill that will get rendered inside associate slot. - * If the code inside has a error, it would be caught ad removed. + * If the code inside has a error, it would be caught and removed. * The error is only visible to admins. * * @param {Object} props Items props. diff --git a/plugins/woocommerce-blocks/tests/js/setup-globals.js b/plugins/woocommerce-blocks/tests/js/setup-globals.js index 198bcbc24a0..8124b840452 100644 --- a/plugins/woocommerce-blocks/tests/js/setup-globals.js +++ b/plugins/woocommerce-blocks/tests/js/setup-globals.js @@ -254,8 +254,13 @@ global.jQuery = () => ( { global.IntersectionObserver = function () { return { + root: null, + rootMargin: '', + thresholds: [], observe: () => void null, unobserve: () => void null, + disconnect: () => void null, + takeRecords: () => [], }; }; diff --git a/plugins/woocommerce/changelog/47105-dev-update-slotfill b/plugins/woocommerce/changelog/47105-dev-update-slotfill new file mode 100644 index 00000000000..81d32f15027 --- /dev/null +++ b/plugins/woocommerce/changelog/47105-dev-update-slotfill @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Update cart/checkout usage of the @wordpress/components Slot Fill \ No newline at end of file diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json index 732d2c826ab..145f4a88675 100644 --- a/plugins/woocommerce/package.json +++ b/plugins/woocommerce/package.json @@ -418,7 +418,6 @@ "node_modules/@woocommerce/e2e-core-tests/CHANGELOG.md", "node_modules/@woocommerce/api/dist/", "node_modules/@woocommerce/admin-e2e-tests/build", - "node_modules/@woocommerce/classic-assets/build", "node_modules/@woocommerce/block-library/build", "node_modules/@woocommerce/block-library/blocks.ini", "node_modules/@woocommerce/admin-library/build", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3d7b828b16..ae33c56d8fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3997,6 +3997,9 @@ importers: wordpress-components: specifier: npm:@wordpress/components@14.2.0 version: '@wordpress/components@14.2.0(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react-with-direction@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(reakit-utils@0.15.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(redux@4.2.1)' + wordpress-components-slotfill: + specifier: npm:@wordpress/components@wp-6.5 + version: '@wordpress/components@26.0.6(@babel/helper-module-imports@7.22.15)(@babel/types@7.23.6)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' optionalDependencies: ndb: specifier: 1.1.5 @@ -10515,6 +10518,13 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + '@wordpress/components@26.0.6': + resolution: {integrity: sha512-1e3UY7OCrShVlC3VkX3oolADmQyrybIXNWYki1B0yQk/mBhJXiQTscYmR7UNW2MlOxMM+uOPIvtSfacur7TdWA==} + engines: {node: '>=12'} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + '@wordpress/components@27.5.0': resolution: {integrity: sha512-RIF1RfBI/SsBYy6wB+DsEfTq24QCLO6Mz+cGTE0mOpiNim2Bi7g9ULlp/gBNRKRuMG3V6hcJklacoMH7gVWk7Q==} engines: {node: '>=12'} @@ -11321,6 +11331,10 @@ packages: resolution: {integrity: sha512-P7nxI/bGMDQhtlTfSe1Y2SDfrd20K5UMnTHbq+hmIkzBGRpNPbdGeNu2bZaZtIvmXk1OCR0Fkef+e6QqkOfYPg==} engines: {node: '>=12'} + '@wordpress/private-apis@0.33.1': + resolution: {integrity: sha512-I7nxWUtZJ243vBC7cRRTId7FK0+c82RlIUZ1DVzutojJlg5a66RfFlMygWg/jVBWEmQqfcGSB4zPiGhi7JVBAg==} + engines: {node: '>=12'} + '@wordpress/private-apis@0.39.0': resolution: {integrity: sha512-JGn7ngLHjbIWcaRNiZP2githQXcLRYS55UpnWa8WemM5vM/Lfql+mBwo1B9/GVvxiGgTy0K3Fv4SsGhZwMjCMg==} engines: {node: '>=12'} @@ -25034,9 +25048,9 @@ snapshots: '@automattic/puppeteer-utils@https://codeload.github.com/Automattic/puppeteer-utils/tar.gz/0f3ec50(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1))': dependencies: - '@babel/cli': 7.23.4(@babel/core@7.23.6) - '@babel/core': 7.23.6 - '@babel/preset-env': 7.23.6(@babel/core@7.23.6) + '@babel/cli': 7.23.4(@babel/core@7.23.5) + '@babel/core': 7.23.5 + '@babel/preset-env': 7.23.5(@babel/core@7.23.5) '@slack/web-api': 5.15.0 '@wordpress/e2e-test-utils': 3.0.0(jest@24.9.0)(puppeteer@2.1.1)(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1)) config: 3.3.7 @@ -25140,20 +25154,6 @@ snapshots: '@nicolo-ribaudo/chokidar-2': 2.1.8-no-fsevents.3 chokidar: 3.5.3 - '@babel/cli@7.23.4(@babel/core@7.23.6)': - dependencies: - '@babel/core': 7.23.6 - '@jridgewell/trace-mapping': 0.3.20 - commander: 4.1.1 - convert-source-map: 2.0.0 - fs-readdir-recursive: 1.1.0 - glob: 7.2.3 - make-dir: 2.1.0 - slash: 2.0.0 - optionalDependencies: - '@nicolo-ribaudo/chokidar-2': 2.1.8-no-fsevents.3 - chokidar: 3.5.3 - '@babel/code-frame@7.12.11': dependencies: '@babel/highlight': 7.23.4 @@ -29240,6 +29240,12 @@ snapshots: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) + '@floating-ui/react-dom@2.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.5.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@floating-ui/utils@0.1.6': {} '@gar/promisify@1.1.3': {} @@ -38905,6 +38911,67 @@ snapshots: - babel-plugin-macros - vite + '@wordpress/components@26.0.6(@babel/helper-module-imports@7.22.15)(@babel/types@7.23.6)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ariakit/react': 0.3.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/runtime': 7.23.6 + '@emotion/cache': 11.11.0 + '@emotion/css': 11.11.2 + '@emotion/react': 11.11.1(@types/react@17.0.71)(react@18.3.1) + '@emotion/serialize': 1.1.2 + '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@18.3.1))(@types/react@17.0.71)(react@18.3.1) + '@emotion/utils': 1.2.1 + '@floating-ui/react-dom': 2.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/gradient-parser': 0.1.3 + '@types/highlight-words-core': 1.2.1 + '@use-gesture/react': 10.3.0(react@18.3.1) + '@wordpress/a11y': 3.57.0 + '@wordpress/compose': 6.34.0(react@18.3.1) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/escape-html': 2.57.0 + '@wordpress/hooks': 3.57.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 + '@wordpress/icons': 9.48.0 + '@wordpress/is-shallow-equal': 4.57.0 + '@wordpress/keycodes': 3.57.0 + '@wordpress/primitives': 3.55.0 + '@wordpress/private-apis': 0.33.1 + '@wordpress/rich-text': 6.34.0(react@18.3.1) + '@wordpress/warning': 2.57.0 + change-case: 4.1.2 + classnames: 2.3.2 + colord: 2.9.3 + date-fns: 2.30.0 + deepmerge: 4.3.1 + dom-scroll-into-view: 1.2.1 + downshift: 6.1.12(react@18.3.1) + fast-deep-equal: 3.1.3 + framer-motion: 10.16.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + gradient-parser: 0.1.5 + highlight-words-core: 1.2.2 + is-plain-object: 5.0.0 + memize: 2.1.0 + path-to-regexp: 6.2.1 + re-resizable: 6.9.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + remove-accents: 0.5.0 + use-lilius: 2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + uuid: 9.0.1 + valtio: 1.7.0(@babel/helper-module-imports@7.22.15)(@babel/types@7.23.6)(babel-plugin-macros@3.1.0)(react@18.3.1) + transitivePeerDependencies: + - '@babel/helper-module-imports' + - '@babel/types' + - '@types/react' + - aslemammad-vite-plugin-macro + - babel-plugin-macros + - vite + '@wordpress/components@27.5.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@ariakit/react': 0.3.14(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -41126,6 +41193,10 @@ snapshots: dependencies: '@babel/runtime': 7.23.6 + '@wordpress/private-apis@0.33.1': + dependencies: + '@babel/runtime': 7.23.6 + '@wordpress/private-apis@0.39.0': dependencies: '@babel/runtime': 7.23.6 @@ -60493,6 +60564,12 @@ snapshots: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) + use-lilius@2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + date-fns: 3.6.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + use-memo-one@1.1.3(react@17.0.2): dependencies: react: 17.0.2