From 7e93e0790f7c79665a14acfa88ceb6ff0a2f1ff3 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 9 Aug 2021 17:55:48 +0200 Subject: [PATCH 01/35] README: add notes --- packages/components/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/components/README.md b/packages/components/README.md index dfff2847c56bf..0e7569661e994 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -10,6 +10,7 @@ Install the module npm install @wordpress/components --save ``` +[Note: is the following paragraph still relevant?] _This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as IE browsers then using [core-js](https://github.com/zloirock/core-js) will add polyfills for these methods._ ## Usage @@ -27,6 +28,7 @@ export default function MyButton() { } ``` +[Note: is the following paragraph still relevant?] Many components include CSS to add style, you will need to add in order to appear correctly. Within WordPress, add the `wp-components` stylesheet as a dependency of your plugin's stylesheet. See [wp_enqueue_style documentation](https://developer.wordpress.org/reference/functions/wp_enqueue_style/#parameters) for how to specify dependencies. In non-WordPress projects, link to the `build-style/style.css` file directly, it is located at `node_modules/@wordpress/components/build-style/style.css`. From 37e175976770d648f1d32a68de81cde5e2ee704e Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 9 Aug 2021 17:56:23 +0200 Subject: [PATCH 02/35] README: add link to `CONTRIBUTING.md` --- packages/components/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/components/README.md b/packages/components/README.md index 0e7569661e994..5560d130a3ea5 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -34,3 +34,7 @@ Many components include CSS to add style, you will need to add in order to appea In non-WordPress projects, link to the `build-style/style.css` file directly, it is located at `node_modules/@wordpress/components/build-style/style.css`.

Code is Poetry.

+ +## Contributing + +See [CONTRIBUTING.md](/packages/components/CONTRIBUTING.md) for the contributing guidelines for the `@wordpress/components` package. From cefad1f2146aa6c7b4a8215bda675c2cb5799b9a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 9 Aug 2021 18:13:40 +0200 Subject: [PATCH 03/35] CONTRIBUTING: Add initial notes --- packages/components/CONTRIBUTING.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index f032f25c4a92c..767a56145cea6 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -4,6 +4,26 @@ Thank you for taking the time to contribute. The following is a set of guidelines for contributing to the `@wordpress/components` package to be considered in addition to the general ones described in our [Contributing Policy](/CONTRIBUTING.md). +## Core principles + +- Mix of legacy and newer components +- Avoid breaking changes as much as possible +- Set of components mainly focused on building Gutenberg-specific UI (toolbars and sidebars) +- Primitive vs High-level vs Utility (https://github.com/WordPress/gutenberg/issues/33111) +- Composition and API consistency (https://github.com/WordPress/gutenberg/issues/33391) (e.g. boolean props begin with `is` or `has`, event callbacks begin with `on`, etc) + +Note: didn't we say that we'd remove the `createComponent` function? + +### Technical requirements for new components + +- TypeScript (Polymorphic component props) +- Emotion +- Context system +- Unit tests +- Storybook +- Folder structure (sub-components folders with hook and component) +- Docs + ## Examples Each component needs to include an example in its README.md file to demonstrate the usage of the component. @@ -13,3 +33,8 @@ These examples can be consumed automatically from other projects in order to vis - It has to be included in a `jsx` code block. - It has to work out-of-the-box. No additional code should be needed to have working the example. - It has to define a React component called `My` which renders the example (i.e.: `MyButton`). Examples for the Higher Order Components should define a `MyComponent` component (i.e.: `MyComponentWithNotices`). + + +## Pull Request Process + +TBD From 02114ce5b7af50af42ac195b4fff5da00c401565 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 10 Aug 2021 12:00:09 +0200 Subject: [PATCH 04/35] More notes into CONTRIBUTING --- packages/components/CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 767a56145cea6..590bde2fc9115 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -11,6 +11,8 @@ The following is a set of guidelines for contributing to the `@wordpress/compone - Set of components mainly focused on building Gutenberg-specific UI (toolbars and sidebars) - Primitive vs High-level vs Utility (https://github.com/WordPress/gutenberg/issues/33111) - Composition and API consistency (https://github.com/WordPress/gutenberg/issues/33391) (e.g. boolean props begin with `is` or `has`, event callbacks begin with `on`, etc) +- Layout: Each component should worry only about the layout of its children (e.g. no margins around itself). Layout components separate from +- Styled components should be suffixed with "*View", to make it clear that they're styled components in other files Note: didn't we say that we'd remove the `createComponent` function? From 3d665fbfc631157448a23e5d3528d999e265ed54 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 11 Aug 2021 19:11:24 +0200 Subject: [PATCH 05/35] Update CONTRIBUTING --- packages/components/CONTRIBUTING.md | 140 +++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 23 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 590bde2fc9115..8d855cb335e2a 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -6,37 +6,131 @@ The following is a set of guidelines for contributing to the `@wordpress/compone ## Core principles -- Mix of legacy and newer components -- Avoid breaking changes as much as possible -- Set of components mainly focused on building Gutenberg-specific UI (toolbars and sidebars) -- Primitive vs High-level vs Utility (https://github.com/WordPress/gutenberg/issues/33111) -- Composition and API consistency (https://github.com/WordPress/gutenberg/issues/33391) (e.g. boolean props begin with `is` or `has`, event callbacks begin with `on`, etc) -- Layout: Each component should worry only about the layout of its children (e.g. no margins around itself). Layout components separate from -- Styled components should be suffixed with "*View", to make it clear that they're styled components in other files +Contributions to the `@wordpress/components` package should follow a set of core principles and technical requirements. -Note: didn't we say that we'd remove the `createComponent` function? +This set of guidelines should apply especially to newly introduced components. It is, in fact, possible that some of the older component don't respect some of these guidelines for legacy/compat reasons. + +### Compatibility + +The `@wordpress/components` package includes several components which are relied upon by many developers across different projects. It is, therefore, very important to avoid introducing breaking changes as much as possible. + +In these situations, an alternative approach is to "soft-deprecate" the given legacy API. This is achieved by removing traces of the API from the docs, while the code keeps supporing it in the background (with or without a warning) + +### Components structure + +The contents of the `@wordpress/components` package can be divided into three groups: UI Primitives, UI Blocks, and Utilities. + +#### UI Primitives + +Lower-level components, which are usually composed together to build the second type of components: UI Blocks. UI Primitives usually don't contain translated text and aim at being highly configurable (at the expense of exposing more props and therefore more complexity). + +UI Primitives can be useful when in need of building an app with all new UI that looks and feels like WordPress but isn't powered by other WordPress technologies (like `@wordpress/i18n`). + +_[Note: we need to discuss how to properly classify/label these components. Should we introduce `primitives`, `blocks` and `utils` folders in the `src` folder? Should we include a standard way in each component's README to show which category it belongs to?]_ + +#### UI Blocks + +Higher-level components, which usually offer fewer props compared to UI Primitives and contain translated text. These components use other WordPress technologies and are ready to be used in Gutenberg's UI. + +#### Utilities + +Components and other utilities function that don't necessarily render a piece of UI to screen, but are instead useful when implementing a given piece of functionality. + +### Components composition + +[To be expanded] E.g.: + +- Polymorphic Components +- Using `children` vs custom render props vs arbitrary "data" props +- Controlled and semi-controlled components +- Composition patterns +- Sub-components naming conventions +- Components' layout responsibilities and boundaries (i.e., a component should only affect the layout of its children, not its own) +- ... + +### APIs Consinstency + +[To be expanded] E.g.: + +- Boolean component props should be prefixed with `is*` (e.g. `isChecked`) or `has*` (`hasValue`). +- Event callback props should be prefixed with `on*` (e.g. `onChanged`) +- `styled` components should be suffixed with `*View` (e.g. `BoxView`) +- ... ### Technical requirements for new components -- TypeScript (Polymorphic component props) -- Emotion -- Context system -- Unit tests -- Storybook -- Folder structure (sub-components folders with hook and component) -- Docs +The following are a set of technical requirements for all newly introduced components. These requirements are also retroactively being applied to existing components. + +For an example of a component that follows these requirements, take a look at [`ItemGroup`](/packages/components/src/item-group); + +#### TypeScript + +All new components should be written in TypeScript and should be typed using the `PolymorphicComponent` type (more details about polymorphism can be found above in the "Components composition" section). + +#### Emotion + +All new component should be styled using [Emotion](https://emotion.sh/docs/introduction). + +Note: Instead of using Emotion's standard `cx` function, the custom [`useCx` hook](/packages/components/src/utils/hooks/use-cx.ts) should be used instead. + +#### Context system + +[To be expanded] + +#### Hooks vs Components + +[To be expanded] + +#### Unit tests + +All new components should include unit tests using [testing library](https://testing-library.com/). Ideally, the component's main piece of functionality would be tested explicitely (rather than using snapshots). + +#### Storybook + +All new components should include Storybook examples. + +#### Documentation -## Examples +All components, in addition to being typed, should be using JSDoc when necessary. -Each component needs to include an example in its README.md file to demonstrate the usage of the component. +Each component that is exported from the `@wordpress/components` package should include a `README.md` file, explaining how to use the component, showing examples, and documenting all the props. -These examples can be consumed automatically from other projects in order to visualize them in their documentation. To ensure these examples are extractable, compilable and renderable, they should be structured in the following way: +#### Folder structure -- It has to be included in a `jsx` code block. -- It has to work out-of-the-box. No additional code should be needed to have working the example. -- It has to define a React component called `My` which renders the example (i.e.: `MyButton`). Examples for the Higher Order Components should define a `MyComponent` component (i.e.: `MyComponentWithNotices`). +Following all previous guidelines, all new components should follow this folder structure: +``` +component-parent-folder/ +├── sub-component-folder/ +├── component.tsx +├── context.ts +├── hook.ts +├── index.ts +├── README.md +├── styles.ts +└── types.ts +``` -## Pull Request Process +In case of a family of components (e.g. `Card` and `CardBody`, `CardFooter`, `CardHeader` ...), each component's implementation should be added in a separate subfolder: -TBD +``` +component-parent-folder/ +├── sub-component-folder/ +│ ├── index.ts +│ ├── component.tsx +│ ├── hook.ts +│ └── README.md +├── sub-component-folder/ +│ ├── index.ts +│ ├── component.tsx +│ ├── hook.ts +│ └── README.md +├── stories +│ └── index.js +├── test +│ └── index.js +├── context.ts +├── index.ts +├── styles.ts +└── types.ts +``` From 0fb47534884fd7ce32985ca241b30e323167a877 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 12 Aug 2021 11:03:26 +0200 Subject: [PATCH 06/35] Add `enable` to boolean prefixes --- packages/components/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 8d855cb335e2a..2c6ab213f198b 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -52,7 +52,7 @@ Components and other utilities function that don't necessarily render a piece of [To be expanded] E.g.: -- Boolean component props should be prefixed with `is*` (e.g. `isChecked`) or `has*` (`hasValue`). +- Boolean component props should be prefixed with `is*` (e.g. `isChecked`), `has*` (e.g. `hasValue`) or `enable*` (e.g. `enableScroll`) - Event callback props should be prefixed with `on*` (e.g. `onChanged`) - `styled` components should be suffixed with `*View` (e.g. `BoxView`) - ... From 00e2b35166ce26372ba29d64eb79dd92b44b56db Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 12 Aug 2021 11:03:48 +0200 Subject: [PATCH 07/35] Remove `View` suffix for styled components --- packages/components/CONTRIBUTING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 2c6ab213f198b..d8f845b7b7dd8 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -54,7 +54,6 @@ Components and other utilities function that don't necessarily render a piece of - Boolean component props should be prefixed with `is*` (e.g. `isChecked`), `has*` (e.g. `hasValue`) or `enable*` (e.g. `enableScroll`) - Event callback props should be prefixed with `on*` (e.g. `onChanged`) -- `styled` components should be suffixed with `*View` (e.g. `BoxView`) - ... ### Technical requirements for new components From 17ccabe005327f7eb3a3ca1f0997200654c03792 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 12 Aug 2021 11:11:34 +0200 Subject: [PATCH 08/35] Update folder structure: move styles and types into each subfolder --- packages/components/CONTRIBUTING.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index d8f845b7b7dd8..a5081ce7e5c47 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -118,18 +118,20 @@ component-parent-folder/ │ ├── index.ts │ ├── component.tsx │ ├── hook.ts -│ └── README.md +│ ├── README.md +│ ├── styles.ts +│ └── types.ts ├── sub-component-folder/ │ ├── index.ts │ ├── component.tsx │ ├── hook.ts -│ └── README.md +│ ├── README.md +│ ├── styles.ts +│ └── types.ts ├── stories │ └── index.js ├── test │ └── index.js ├── context.ts -├── index.ts -├── styles.ts -└── types.ts +└── index.ts ``` From 1459214824439f106be137729799c1911a30b2f3 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Aug 2021 19:25:46 +0200 Subject: [PATCH 09/35] Less strict working about typescript, rename PolymorphicComponent to WordPressComponent --- packages/components/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index a5081ce7e5c47..4ae4ab230665e 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -64,7 +64,7 @@ For an example of a component that follows these requirements, take a look at [` #### TypeScript -All new components should be written in TypeScript and should be typed using the `PolymorphicComponent` type (more details about polymorphism can be found above in the "Components composition" section). +We strongly encourage using TypeScript for all new components. Components should be typed using the `WordPressComponent` type (more details about polymorphism can be found above in the "Components composition" section). #### Emotion From c6f6a244d80539978dc14d74ccdf0bf195d962af Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Aug 2021 19:39:54 +0200 Subject: [PATCH 10/35] Update packages/components/CONTRIBUTING.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Greg Ziółkowski --- packages/components/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 4ae4ab230665e..2abcf2703f1e2 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -12,7 +12,7 @@ This set of guidelines should apply especially to newly introduced components. I ### Compatibility -The `@wordpress/components` package includes several components which are relied upon by many developers across different projects. It is, therefore, very important to avoid introducing breaking changes as much as possible. +The `@wordpress/components` package includes components that are relied upon by many developers across different projects. It is, therefore, very important to avoid introducing breaking changes. In these situations, an alternative approach is to "soft-deprecate" the given legacy API. This is achieved by removing traces of the API from the docs, while the code keeps supporing it in the background (with or without a warning) From f53385799413647af35b848a1d3ca57a56c0fcef Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 12:30:34 +0200 Subject: [PATCH 11/35] Refer to the repo testing overview docs in the unit test section --- packages/components/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 2abcf2703f1e2..41dd45c28d2a9 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -82,7 +82,7 @@ Note: Instead of using Emotion's standard `cx` function, the custom [`useCx` hoo #### Unit tests -All new components should include unit tests using [testing library](https://testing-library.com/). Ideally, the component's main piece of functionality would be tested explicitely (rather than using snapshots). +Please refer to the [JavaScript Testing Overview docs](/docs/contributors/code/testing-overview.md) #### Storybook From 4c1c20089d2358191e053753cfc838456ca52713 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 12:40:53 +0200 Subject: [PATCH 12/35] Update polyfill info --- packages/components/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/components/README.md b/packages/components/README.md index 5560d130a3ea5..02a18c36597aa 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -10,8 +10,7 @@ Install the module npm install @wordpress/components --save ``` -[Note: is the following paragraph still relevant?] -_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as IE browsers then using [core-js](https://github.com/zloirock/core-js) will add polyfills for these methods._ +_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](/packages/babel-preset-default#polyfill) in your code._ ## Usage From 89a89d1fbd0439f83aa16d79293bca741284b9fb Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 16:06:09 +0200 Subject: [PATCH 13/35] Remove "Components Structure" paragraph --- packages/components/CONTRIBUTING.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 41dd45c28d2a9..675f916de429a 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -16,26 +16,6 @@ The `@wordpress/components` package includes components that are relied upon by In these situations, an alternative approach is to "soft-deprecate" the given legacy API. This is achieved by removing traces of the API from the docs, while the code keeps supporing it in the background (with or without a warning) -### Components structure - -The contents of the `@wordpress/components` package can be divided into three groups: UI Primitives, UI Blocks, and Utilities. - -#### UI Primitives - -Lower-level components, which are usually composed together to build the second type of components: UI Blocks. UI Primitives usually don't contain translated text and aim at being highly configurable (at the expense of exposing more props and therefore more complexity). - -UI Primitives can be useful when in need of building an app with all new UI that looks and feels like WordPress but isn't powered by other WordPress technologies (like `@wordpress/i18n`). - -_[Note: we need to discuss how to properly classify/label these components. Should we introduce `primitives`, `blocks` and `utils` folders in the `src` folder? Should we include a standard way in each component's README to show which category it belongs to?]_ - -#### UI Blocks - -Higher-level components, which usually offer fewer props compared to UI Primitives and contain translated text. These components use other WordPress technologies and are ready to be used in Gutenberg's UI. - -#### Utilities - -Components and other utilities function that don't necessarily render a piece of UI to screen, but are instead useful when implementing a given piece of functionality. - ### Components composition [To be expanded] E.g.: From 5238a8867695f8e72c93d7d4a353c5f3967b7c1f Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 16:09:57 +0200 Subject: [PATCH 14/35] Reword text in "Folder structure" section, exclude shared utilities --- packages/components/CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 675f916de429a..2f365631f8060 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -76,7 +76,7 @@ Each component that is exported from the `@wordpress/components` package should #### Folder structure -Following all previous guidelines, all new components should follow this folder structure: +As a result of the above guidelines, all new components (execpt for shared utilities) should generally follow this folder structure: ``` component-parent-folder/ @@ -90,7 +90,7 @@ component-parent-folder/ └── types.ts ``` -In case of a family of components (e.g. `Card` and `CardBody`, `CardFooter`, `CardHeader` ...), each component's implementation should be added in a separate subfolder: +In case of a family of components (e.g. `Card` and `CardBody`, `CardFooter`, `CardHeader` ...), each component's implementation should live in a separate subfolder: ``` component-parent-folder/ From 7acc92535bc6ba594a5ed9a98496a5aad440d673 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 16:10:38 +0200 Subject: [PATCH 15/35] Folder structure schemes: name parent folders, remove useless sub-component folder --- packages/components/CONTRIBUTING.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 2f365631f8060..c63abb729ae20 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -79,8 +79,7 @@ Each component that is exported from the `@wordpress/components` package should As a result of the above guidelines, all new components (execpt for shared utilities) should generally follow this folder structure: ``` -component-parent-folder/ -├── sub-component-folder/ +component-name/ ├── component.tsx ├── context.ts ├── hook.ts @@ -93,15 +92,15 @@ component-parent-folder/ In case of a family of components (e.g. `Card` and `CardBody`, `CardFooter`, `CardHeader` ...), each component's implementation should live in a separate subfolder: ``` -component-parent-folder/ -├── sub-component-folder/ +component-family-name/ +├── sub-component-name/ │ ├── index.ts │ ├── component.tsx │ ├── hook.ts │ ├── README.md │ ├── styles.ts │ └── types.ts -├── sub-component-folder/ +├── sub-component-name/ │ ├── index.ts │ ├── component.tsx │ ├── hook.ts From 6621f1e7bb4250cc1a55cbb8fed0c427354d9efe Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 16:34:49 +0200 Subject: [PATCH 16/35] Expand Storybook section --- packages/components/CONTRIBUTING.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index c63abb729ae20..60307475464ad 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -66,7 +66,25 @@ Please refer to the [JavaScript Testing Overview docs](/docs/contributors/code/t #### Storybook -All new components should include Storybook examples. +All new components should include [Storybook](https://storybook.js.org/) stories, in order to make it easier to work on and review each component in isolation. + +A component's stories should be showcasing its different states — for example, a `Button`'s different variants: + +```jsx +import Button from '../'; + +export default { title: 'Components/Button', component: Button }; + +export const _default = () => ; + +export const primary = () => ; + +export const secondary = () => ; +``` + +For even more flexibility while writing stories, the [`@storybook/addons-controls`](https://storybook.js.org/addons/@storybook/addon-controls) addon is a great way to interact with a component's arguments dynamically and in real time. + +Storybook can be started on a local maching by running `npm run storybook:dev`. Alternatively, the components' catalogue (up to date with the latest code on `trunk`) can be found at [wordpress.github.io/gutenberg/](https://wordpress.github.io/gutenberg/). #### Documentation From 6f6d8defa10a0222f9fceeecf0448c45cbff8234 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 17:29:47 +0200 Subject: [PATCH 17/35] Add details to soft deprecation strategy --- packages/components/CONTRIBUTING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 60307475464ad..f01371d52a385 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -14,7 +14,11 @@ This set of guidelines should apply especially to newly introduced components. I The `@wordpress/components` package includes components that are relied upon by many developers across different projects. It is, therefore, very important to avoid introducing breaking changes. -In these situations, an alternative approach is to "soft-deprecate" the given legacy API. This is achieved by removing traces of the API from the docs, while the code keeps supporing it in the background (with or without a warning) +In these situations, one possible approach is to "soft-deprecate" a given legacy API. This is achieved by: + +1. Removing traces of the API from the docs, while still supporing it in code; +2. Updating all places in Gutenberg that use that API; +3. Adding deprecation warnings (only after the previous point is completed, otherwide the Browser Console will be polluted by all those warnings and some e2e tests may fail). ### Components composition From 9d0ece79dd1de69950599bf334d72edd2302649a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 17:30:20 +0200 Subject: [PATCH 18/35] Misc smaller improvements --- packages/components/CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index f01371d52a385..6eb9e2bb41ac4 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -44,7 +44,7 @@ In these situations, one possible approach is to "soft-deprecate" a given legacy The following are a set of technical requirements for all newly introduced components. These requirements are also retroactively being applied to existing components. -For an example of a component that follows these requirements, take a look at [`ItemGroup`](/packages/components/src/item-group); +For an example of a component that follows these requirements, take a look at [`ItemGroup`](/packages/components/src/item-group) as an example. #### TypeScript @@ -66,7 +66,7 @@ Note: Instead of using Emotion's standard `cx` function, the custom [`useCx` hoo #### Unit tests -Please refer to the [JavaScript Testing Overview docs](/docs/contributors/code/testing-overview.md) +Please refer to the [JavaScript Testing Overview docs](/docs/contributors/code/testing-overview.md). #### Storybook From d394f5db2a5a557c5f81e56f9807f38f71f889b2 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 17:40:44 +0200 Subject: [PATCH 19/35] Fix typo --- packages/components/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 6eb9e2bb41ac4..d83d07010ae43 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -98,7 +98,7 @@ Each component that is exported from the `@wordpress/components` package should #### Folder structure -As a result of the above guidelines, all new components (execpt for shared utilities) should generally follow this folder structure: +As a result of the above guidelines, all new components (except for shared utilities) should _generally_ follow this folder structure: ``` component-name/ From 3359502c251715b2779843e0b0621aba0c33e097 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 18:32:14 +0200 Subject: [PATCH 20/35] Expand hooks vs components section, move under composition --- packages/components/CONTRIBUTING.md | 48 ++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index d83d07010ae43..844ee43461bcf 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -32,6 +32,50 @@ In these situations, one possible approach is to "soft-deprecate" a given legacy - Components' layout responsibilities and boundaries (i.e., a component should only affect the layout of its children, not its own) - ... +#### Components & Hooks + +One way to enable reusability and composition is to extract a component's underlying logic into a hook (living in a separate `hook.ts` file). The actual component (usually defined in a `component.tsx` file) can then invoke the hook and use its output to render the required DOM elements. For example: + +```tsx +// in `hook.ts` +function useExampleComponent( props: PolymorphicComponentProps< ExampleProps, 'div' > ) { + // Merge received props with the context system. + const { isVisible, className, ...otherProps } = useContextSystem( props, 'Example' ); + + // Any other reusable rendering logic (e.g. computing className, state, event listeners...) + const cx = useCx(); + const classes = useMemo( + () => + cx( + styles.example, + isVisible && styles.visible, + className + ), + [ className, isVisible ] + ); + + return { + ...otherProps, + className: classes + }; +} + +// in `component.tsx` +function Example( + props: PolymorphicComponentProps< ExampleProps, 'div' >, + forwardedRef: Ref< any > +) { + const exampleProps = useExampleComponent( props ); + + return ; +} +``` + +A couple of good examples of how hooks are used for composition are: + +- the `Card` component, which builds on top of the `Surface` component by [calling the `useSurface` hook inside its own hook](/packages/components/src/card/card/hook.js); +- the `HStack` component, which builds on top of the `Flex` component and [calls the `useFlex` hook inside its own hook](/packages/components/src/h-stack/hook.js). + ### APIs Consinstency [To be expanded] E.g.: @@ -60,10 +104,6 @@ Note: Instead of using Emotion's standard `cx` function, the custom [`useCx` hoo [To be expanded] -#### Hooks vs Components - -[To be expanded] - #### Unit tests Please refer to the [JavaScript Testing Overview docs](/docs/contributors/code/testing-overview.md). From cfc843d43fc1a6814cfbe2a8513d93b0e8368e47 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 18:32:55 +0200 Subject: [PATCH 21/35] Move sub components naming convention under "APIs consistency" section --- packages/components/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 844ee43461bcf..ae4f7e5da8e4b 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -28,7 +28,6 @@ In these situations, one possible approach is to "soft-deprecate" a given legacy - Using `children` vs custom render props vs arbitrary "data" props - Controlled and semi-controlled components - Composition patterns -- Sub-components naming conventions - Components' layout responsibilities and boundaries (i.e., a component should only affect the layout of its children, not its own) - ... @@ -82,6 +81,7 @@ A couple of good examples of how hooks are used for composition are: - Boolean component props should be prefixed with `is*` (e.g. `isChecked`), `has*` (e.g. `hasValue`) or `enable*` (e.g. `enableScroll`) - Event callback props should be prefixed with `on*` (e.g. `onChanged`) +- Sub-components naming conventions (e.g `CardBody` instead of `Card.Body`) - ... ### Technical requirements for new components From abf50ad967a7985a459080a81655aa147ed90a40 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 1 Sep 2021 19:00:26 +0200 Subject: [PATCH 22/35] Add Polymorphic components sub-section, reorganise "Components composition" section into sub-sections --- packages/components/CONTRIBUTING.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index ae4f7e5da8e4b..7ce5def32eede 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -22,14 +22,28 @@ In these situations, one possible approach is to "soft-deprecate" a given legacy ### Components composition -[To be expanded] E.g.: -- Polymorphic Components -- Using `children` vs custom render props vs arbitrary "data" props -- Controlled and semi-controlled components -- Composition patterns -- Components' layout responsibilities and boundaries (i.e., a component should only affect the layout of its children, not its own) -- ... +### Polymorphic Components (i.e. the `as` prop) + +The primary way to compose components is through the `as` prop. This prop can be used to change the underlying element used to render a component, e.g.: + +```tsx +function LinkButton( { href, children } ) { + return ; +} +``` + +### Composition patterns + +TBD — E.g. Using `children` vs custom render props vs arbitrary "data" props + +### (Semi-)Controlled components + +TBD + +### Layout "responsibilities" + +TBD — Components' layout responsibilities and boundaries (i.e., a component should only affect the layout of its children, not its own) #### Components & Hooks From 0021dfc00efe4425980a938daa2a67707f1f42ab Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 2 Sep 2021 15:53:20 +0200 Subject: [PATCH 23/35] Add more details about listing props through knobs in storybook --- packages/components/CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 7ce5def32eede..efac2ac691863 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -142,6 +142,8 @@ export const secondary = () => ; export const secondary = () => ; ``` -For even more flexibility while writing stories, the [`@storybook/addons-controls`](https://storybook.js.org/addons/@storybook/addon-controls) addon is a great way to interact with a component's arguments dynamically and in real time. +A great tool to use when writing stories is the [Storybook Controls addon](https://storybook.js.org/addons/@storybook/addon-controls). Ideally props should be exposed by using this addon, which provides a graphical UI to interact dynamically with the component without needing to write code. -Ideally, all of the props for the story's components should be exposed by using knobs. The default value of the knobs should coincide with the default value of the props (i.e. it should be `undefined` if a prop is not required). A story should, therefore, also explicitly show how values from the Context System are applied to (sub)components. A good example of how this may look like is the [`Card` story](https://wordpress.github.io/gutenberg/?path=/story/components-card--default) (code [here](/packages/components/src/card/stories/index.js)). +The default value of each control should coincide with the default value of the props (i.e. it should be `undefined` if a prop is not required). A story should, therefore, also explicitly show how values from the Context System are applied to (sub)components. A good example of how this may look like is the [`Card` story](https://wordpress.github.io/gutenberg/?path=/story/components-card--default) (code [here](/packages/components/src/card/stories/index.js)). Storybook can be started on a local maching by running `npm run storybook:dev`. Alternatively, the components' catalogue (up to date with the latest code on `trunk`) can be found at [wordpress.github.io/gutenberg/](https://wordpress.github.io/gutenberg/). From e8bb18d572c23644a0d35173fa45abaa8038211b Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 8 Sep 2021 17:53:14 +0200 Subject: [PATCH 32/35] Link to the Coding Guidelines in the "Documentation" section --- packages/components/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index d0ef1fb248245..11ad49f27e2fc 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -166,7 +166,7 @@ Storybook can be started on a local maching by running `npm run storybook:dev`. #### Documentation -All components, in addition to being typed, should be using JSDoc when necessary. +All components, in addition to being typed, should be using JSDoc when necessary — as explained in the [Coding Guidelines](/docs/contributors/code/coding-guidelines.md#javascript-documentation-using-jsdoc). Each component that is exported from the `@wordpress/components` package should include a `README.md` file, explaining how to use the component, showing examples, and documenting all the props. From 08cd3568541b4a63fc814d9c99b2c47e1c89ed32 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 8 Sep 2021 17:54:42 +0200 Subject: [PATCH 33/35] remove note about relevance of SCSS-related docs in README --- packages/components/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/components/README.md b/packages/components/README.md index 02a18c36597aa..3c89d0092dc4f 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -27,7 +27,6 @@ export default function MyButton() { } ``` -[Note: is the following paragraph still relevant?] Many components include CSS to add style, you will need to add in order to appear correctly. Within WordPress, add the `wp-components` stylesheet as a dependency of your plugin's stylesheet. See [wp_enqueue_style documentation](https://developer.wordpress.org/reference/functions/wp_enqueue_style/#parameters) for how to specify dependencies. In non-WordPress projects, link to the `build-style/style.css` file directly, it is located at `node_modules/@wordpress/components/build-style/style.css`. From c34f73168aafb25ab9412d3f0c4f5ed59c3c1358 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 8 Sep 2021 19:23:20 +0200 Subject: [PATCH 34/35] Update context section with example snippets --- packages/components/CONTRIBUTING.md | 72 ++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 11ad49f27e2fc..8edb9be3e92ab 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -134,7 +134,77 @@ Components can use this system via a couple of functions: - they can connect to the Context via `contextConnect` - they can read the "computed" values from the context via `useContextSystem` -An example of how this is used is the [`Card` component](https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/card/card/component.js#L44-L54) — all of its subcomponents (e.g. `CardBody`, `CardHeader`...) will [use, by default, the same value for the `size` as the parent `Card` component](https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/card/card-body/hook.js#L23) — which results in all the components having all the correct spacing "auto-magically". +An example of how this is used can be found in the [`Card` component family](https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/card). For example, this is how the `Card` component injects the `size` and `isBorderless` props down to its `CardBody` subcomponent — which makes it use the correct spacing and border settings "auto-magically". + +```jsx +//========================================================================= +// Simplified snippet from `packages/components/src/card/card/hook.js` +//========================================================================= +import { useContextSystem } from '../../ui/context'; + +export function useCard( props ) { + // Read any derived registered prop from the Context System in the `Card` namespace + const derivedProps = useContextSystem( props, 'Card' ); + + // [...] + + return computedHookProps; +} + +//========================================================================= +// Simplified snippet from `packages/components/src/card/card/component.js` +//========================================================================= +import { contextConnect, ContextSystemProvider } from '../../ui/context'; + +function Card( props, forwardedRef ) { + const { + size, + isBorderless, + ...otherComputedHookProps + } = useCard( props ); + + // [...] + + // Prepare the additional props that should be passed to subcomponents via the Context System. + const contextProviderValue = useMemo( () => { + return { + // Each key in this object should match a component's registered namespace. + CardBody: { + size, + isBorderless, + }, + }; + }, [ isBorderless, size ] ); + + return ( + { /* Write additional values to the Context System */ } + + { /* [...] */ } + + ); +} + +// Connect to the Context System under the `Card` namespace +const ConnectedCard = contextConnect( Card, 'Card' ); +export default ConnectedCard; + +//========================================================================= +// Simplified snippet from `packages/components/src/card/card-body/hook.js` +//========================================================================= +import { useContextSystem } from '../../ui/context'; + +export function useCardBody( props ) { + // Read any derived registered prop from the Context System in the `CardBody` namespace. + // If a `CardBody` component is rendered as a child of a `Card` component, the value of + // the `size` prop will be the one set by the parent `Card` component via the Context + // System (unless the prop gets explicitely set on the `CardBody` component). + const { size = 'medium', ...otherDerivedProps } = useContextSystem( props, 'CardBody' ); + + // [...] + + return computedHookProps; +} +``` #### Unit tests From 8c494c83282cb1a9cf68f0bb51597d91d5f1abb5 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 8 Sep 2021 19:25:07 +0200 Subject: [PATCH 35/35] Format internal links to absolute path format --- packages/components/CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 8edb9be3e92ab..3bed2829216a8 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -22,7 +22,7 @@ In these situations, one possible approach is to "soft-deprecate" a given legacy When adding new components or new props to existing components, it's recommended to prefix them with `__unstable` or `__experimental` until they're stable enough to be exposed as part of the public API. -Learn more on [How to preserve backward compatibility for a React Component](https://github.com/WordPress/gutenberg/blob/trunk/docs/how-to-guides/backward-compatibility/README.md#how-to-preserve-backward-compatibility-for-a-react-component) and [Experimental and Unstable APIs](https://github.com/WordPress/gutenberg/blob/trunk/docs/contributors/code/coding-guidelines.md#experimental-and-unstable-apis). +Learn more on [How to preserve backward compatibility for a React Component](/docs/how-to-guides/backward-compatibility/README.md#how-to-preserve-backward-compatibility-for-a-react-component) and [Experimental and Unstable APIs](/docs/contributors/code/coding-guidelines.md#experimental-and-unstable-apis). ### Components composition @@ -134,7 +134,7 @@ Components can use this system via a couple of functions: - they can connect to the Context via `contextConnect` - they can read the "computed" values from the context via `useContextSystem` -An example of how this is used can be found in the [`Card` component family](https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/card). For example, this is how the `Card` component injects the `size` and `isBorderless` props down to its `CardBody` subcomponent — which makes it use the correct spacing and border settings "auto-magically". +An example of how this is used can be found in the [`Card` component family](/packages/components/src/card). For example, this is how the `Card` component injects the `size` and `isBorderless` props down to its `CardBody` subcomponent — which makes it use the correct spacing and border settings "auto-magically". ```jsx //=========================================================================