npm package: @justeattakeaway/pie-wrapper-react
Description: Common react wrapper for use for PIE custom elements
Author: Just Eat Takeaway.com - Design System Team
Latest version: 0.14.4

# pie-wrapper-react

## Required dependencies

In order to use this package, your component must have the following devDependencies:

```bash
yarn add -D @custom-elements-manifest/analyzer \
@justeattakeaway/pie-components-config \
@justeattakeaway/pie-wrapper-react \
cem-plugin-module-file-extensions
```

## Required npm scripts

### create:manifest
This package assumes that a `custom-elements.json` file exists in the components root directory. This can be generated by defining a new script in your component `package.json`.

```json
"create:manifest": "yarn cem analyze --litelement",
```

This script utilises the `@custom-elements-manifest/analyzer` dependency to search through the component `src` for any custom elements and condenses it's information, such as events and attributes, into a large json object - making it easier to use for wrappers. Therefore, any changes made to a web component will automatically be reflected in the `custom-elements.json` file during the build process.

Changes to this file should be committed as part of your PR. These are necessary for the react-wrapper to be generated with the most up to date version of the component so should be committed along with your other file changes.

### build:react-wrapper
In order to create the react wrapper, this package exposes a `build-react-wrapper` executable that can be added to your component `package.json`:

```json
"build:react-wrapper": "npx build-react-wrapper"
```

### Script execution order

These scripts should be executed **before** your main `build` script. If your repo uses Turborepo, you can define these tasks as dependencies of your `build` task in `turbo.json` like so:

```json
{
  "$schema": "https://turborepo.org/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": [
        "^build",
        "build:react-wrapper"
      ],
      "outputs": [
        "dist/**"
      ]
    },
    "build:react-wrapper": {
        "dependsOn": [
            "create:manifest"
        ],
      "outputs": [
        "src/react.ts"
      ]
    },
    "create:manifest": {
      "outputs": [
        "custom-elements.json"
      ]
    },
```

Alternatively, simply modify your `build` script in `package.json`:

```json
"build": "yarn create:manifest && yarn build:react-wrapper && <existing-build-command>",
"build:react-wrapper": "npx build-react-wrapper",
"create:manifest": "yarn cem analyze --litelement",
```

## Required configuration

**package.json**
In order to allow tools to find npm packages with custom element manifests without having to download package tarballs, packages should have a "customElements" field in their package.json that points to the manifest and should also be included as part of your published package:

```json
"files": [
    ...
    "custom-elements.json"
],
"customElements": "custom-elements.json"
```

**custom-elements-manifest.config.js**

You should also create `custom-elements-manifest.config.js` in the root of your component directory. At a minimum, this configuration should extend the shared config from `@justeattakeaway/pie-components-config`.

```js
import customElementsManifestConfig from '@justeattakeaway/pie-components-config/custom-elements-manifest.config.js';

export default customElementsManifestConfig;

```

## Usage

This package is for generating a React wrapper during the build process of a Lit component. Previously, React developers would need to install the component, then manually wrap it within a `createComponent` function before it could be used. This is because React 18 and previous versions don't handle web components and custom elements out of the box correctly, due to how React treats custom props and events.

With this package, the below code (example of `pie-button`) is automatically generated into a `react.ts` file within the component's `src` directory. The wrapper then gets saved to the component's `dist` folder during the build.

```js
import * as React from 'react';
import { createComponent } from '@lit/react';
import { PieButton as PieButtonLit } from './index';
import { type ButtonProps } from './defs';

const PieButtonReact = createComponent({
        displayName: 'PieButton',
        elementClass: PieButtonLit,
        react: React,
        tagName: 'pie-button',
        events: {}
    });

// Provides missing contextual types for a React button component
// Use the React IntrinsicElements interface as reference for mapping standard HTML elements to existing React Interfaces
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0bb210867d16170c4a08d9ce5d132817651a0f80/types/react/index.d.ts#L2829
type ReactBaseType = React.ButtonHTMLAttributes<HTMLButtonElement>;

// Workaround for `createComponent` setting all props set as optional with the additional contextual types declared above
export const PieButton = PieButtonReact as React.ForwardRefExoticComponent<React.PropsWithoutRef<ButtonProps> & React.RefAttributes<PieButtonLit> & ReactBaseType>;
```

To use the React wrapper in an application, import `Pie{Component}` from the package of the component, with the additional import of `dist/react`. For example:

```js
import { PieButton } from '@justeattakeaway/pie-button/dist/react'
```

When constructing your web component, please reference the `componentSelector` variable and component class to define your custom element to have consistent identifiers across the component. For example:

`customElements.define(componentSelector, PieIconButton);`

Alternatively, you can define your custom element with a string and component class directly. For example

`customElements.define('pie-icon-button', PieIconButton);`

The first parameter must be a reference to **componentSelector** or a **string** - this will be used to construct the tag name in the react component.

Note: In order for the `custom-elements-manifest/analyzer` to recognise events inside your component, please declare the event inside the `this.dispatchEvent` function. For example:

```js
this.dispatchEvent(new CustomEvent('CustomEvent', { detail: 'WC event dispatched' }))
```

## Credits

This package was heavily inspired by [`spectrum-web-components`](https://github.com/adobe/spectrum-web-components).