Skip to content

Commit

Permalink
docs(web-components): implementation notes for React integration (#5000)
Browse files Browse the repository at this point in the history
### Related Ticket(s)

Refs #4975.

### Description

Add notes on how React integration for `@carbon/ibmdotcom-web-components` works, and how build procedure automates in this area.

### Changelog

**New**

- Notes on how React integration for `@carbon/ibmdotcom-web-components` works, and how build procedure automates in this area.
  • Loading branch information
asudoh authored Jan 26, 2021
1 parent cc9709d commit 3e5c259
Showing 1 changed file with 68 additions and 0 deletions.
68 changes: 68 additions & 0 deletions packages/web-components/IMPLEMENTATION_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
- [Composite components](#composite-components)
- [Components rendering modal](#components-rendering-modal)
- [CTA components](#cta-components)
- [React integration](#react-integration)
- [React wrapper generator](#react-wrapper-generator)
- [Build procedure to generate React wrapper](#build-procedure-to-generate-react-wrapper)
- [Limited components to generate React wrapper](#limited-components-to-generate-react-wrapper)
- [Non-React APIs for React integration](#non-react-apis-for-react-integration)
- [Container components](#container-components)
- [Triggering action dispatcher](#triggering-action-dispatcher)
- [Masthead search](#masthead-search)
Expand Down Expand Up @@ -114,6 +119,69 @@ There are some common behaviors in CTA components, that is implemented by [`CTAM
- Send an event (`dds-cta-request-video-data`) when user clicks on CTA so that the lightbox video player code can launch the light box
- (Video caption/duration/thumbnail)

## React integration

While Angular/Vue require limited/no change in application to use Web Components, React has some problems with Web Components support. React heavily relies on its knowledge with regard to how intrinsic HTML elements work.

For example, if you have `<dds-foo custom-boolean={true}>`, the DOM becomes `<dds-foo custom-boolean="true">` that is different from how `<button disabled={true}>` is rendered to DOM, which is, `<button disabled>` (empty string in `disabled` attribute value).

Another example is `<dds-foo onFoo={handleFoo}>` that doesn't attach an event handler, whereas `<div onClick={handleClick}>` does.

### React wrapper generator

Web Components community [made React core team aware of above issue](https://github.com/facebook/react/issues/11347), and created a [proposal](https://github.com/reactjs/rfcs/pull/15) to address that.

`carbon-web-components` library implements a variant of the proposal, with [`createReactCustomElementType()` function](https://github.com/carbon-design-system/carbon-web-components/blob/v1.11.0/src/globals/wrappers/createReactCustomElementType.ts). Below example create a React component, `BXDropdown`, from `<bx-dropdown>` custom element:

```javascript
import createCustomElementType, { booleanSerializer } from 'carbon-web-components/es/globals/wrappers/createCustomElementType';

const BXDropdown = createCustomElementType('bx-dropdown', {
disabled: {
serialize: booleanSerializer,
},
helperText: {
attribute: 'helper-text',
},
onBeforeSelect: {
event: 'bx-dropdown-beingselected',
},
});
```

We call React components that `createCustomElementType()` creates, e.g. `<BXDropdown>`, _React wrappers_. React wrapper provides the following enhancement over directly using `<bx-dropdown>` in JSX:

- `<BXDropdown disabled={true}>` yeilds to `<bx-dropdown disabled>` and `<BXDropdown disabled={false}>` causes `disabled` attribute removed from `<bx-dropdown>`.
- `<BXDropdown helperText="The helper text">` yields to `<bx-dropdown helper-text="The helper text">`.
- `<BXDropdown onBeforeSelect={handleBeforeSelect}>` attaches `bx-dropdown-beingselected` event to `<bx-dropdown>`.

### Build procedure to generate React wrapper

`carbon-web-components` as well as `@carbon/ibmdotcom-web-components` have a build procedure to generate the React wrapper using `createReactCustomElementType`. It uses a [Babel plugin](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.1/packages/web-components/tools/babel-plugin-create-react-custom-element-type.js) that transforms the original custom element type code to a corresponding React wrapper, by looking at `lit-element`'s `@property` decorator and `eventXXX` static properties in the custom element class. The build procedure can be found at [here](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.1/packages/web-components/gulp-tasks/build.js#L159-L170).

There is [another Babel plugin](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.1/packages/web-components/tools/babel-plugin-create-react-custom-element-type-def.js) that transforms the original custom element type code to a corresponding TypeScript definition to above React wrapper, leveraging the same `@property` and `eventXXX` in the custom element class. The build procedure can be found at [here](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.1/packages/web-components/gulp-tasks/build.js#L385-L397).

#### Limited components to generate React wrapper

At the point of `v1.15.0` release, we decided that we generate React wrappers only for components without pure-React counterpart, for example, `<dds-carousel>` and `<dds-leaving-ibm-modal>`.

Those kind of components should have `/* @__GENERATE_REACT_CUSTOM_ELEMENT_TYPE__ */` annotation at the default export. Such annotation is read by a [Babel plugin](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.1/packages/web-components/tools/babel-plugin-scan-create-react-custom-element-type-candidates.js) so the build procedure can [harvest the list of files to generate React wrappers for](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.1/packages/web-components/gulp-tasks/build.js#L126-L148).

### Non-React APIs for React integration

There are a couple of types of non-React APIs that application can use with React integration code:

- Constant/enum definitions, e.g. `PICTOGRAM_PLACEMENT` for `<dds-card>`
- `mapStateToProps()`/`mapDispatchToProps()` for Redux integration

Because of non-React nature, the implementation is identical to the original Web Components code, while we want to make them available to React users. Therefore, we create modules in `es/components-react` directory that proxies to one in `es/components`, like:

```javascript
export * from '../../components/card/defs.js';
```

The build procedure can be found at [here](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.1/packages/web-components/gulp-tasks/build.js#L257-L282).

## Container components

Container components are inheritances of composite components that connects to `@carbon/ibmdotcom-service`. Connecting to `@carbon/ibmdotcom-service` is done via a Redux store, [`@carbon/ibmdotcom-services-store`](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/tree/v1.13.0/packages/services-store). Connecting to `@carbon/ibmdotcom-services-store` is done by [`ConnectMixin`](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.13.0/packages/web-components/src/globals/mixins/connect.ts) that has a similar feature set to [`react-redux`](https://react-redux.js.org).
Expand Down

0 comments on commit 3e5c259

Please sign in to comment.