Skip to content

Commit

Permalink
update for React 18 (#103)
Browse files Browse the repository at this point in the history
* update for React 18

* move tests

* fxx tests

* update tests

* prettier fix

* add prop-types dev dep

* socs update

* update codesandbox example for legacy in docs

* update header example link

* link updates in docs

* Publish @r2wc/react-to-web-component v2.0.0-alpha.1

* update links

* update

* update main documentation api link

* update ReactDOMRenderType for legacy

* prettier

---------

Co-authored-by: Bavin Edwards <[email protected]>
Co-authored-by: @r2wc/react-to-web-component[bot]@workflow <Workflow: @r2wc/react-to-web-component[bot]>
  • Loading branch information
3 people authored May 19, 2023
1 parent 60d3eca commit f3670fa
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 243 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,13 @@ npm install @r2wc/react-to-web-component

## External Examples

Greeting example in a [CodePen](https://codepen.io/bavinedwards/pen/jOveaGm)

Greeting example in [CodeSandbox](https://codesandbox.io/s/sample-greeting-app-ts-qwidh9)

Hello, world example (React17) in [CodeSandbox](https://codesandbox.io/s/hello-world-react17-u4l3x1)

Example with all prop types in [CodeSandbox](https://codesandbox.io/p/sandbox/vite-example-with-numerous-types-gjf87o)

R2WC With Vite Header Example in [CodeSandbox](https://codesandbox.io/p/sandbox/header-example-e4x25q)
R2WC With Vite Header Example in [CodeSandbox](https://codesandbox.io/p/sandbox/r2wc-header-example-vqzfgo)

## External Blog Posts

Expand All @@ -109,9 +107,9 @@ R2WC with Create React App (CRA) [View Post](https://www.bitovi.com/blog/how-to-

## How it works

Check out our [full API documentation](../../docs/api.md).
Check out our [full API documentation](https://github.com/bitovi/react-to-web-component/blob/main/docs/api.md).

`r2wc` creates a constructor function whose prototype is a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This acts as a trap for any property set on instances of the custom element. When a property is set, the proxy:
Under the hood, `r2wc` creates a `CustomElementConstructor` with custom getters/setters and life cycle methods that keep track of the props that you have defined. When a property is set, its custom setter:

- re-renders the React component inside the custom element.
- creates an enumerable getter / setter on the instance
Expand Down
59 changes: 39 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/legacy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ R2WC with Create React App (CRA) [View Post](https://www.bitovi.com/blog/how-to-

## How it works

Check out our [full API documentation](../../docs/api.md).
Check out our [full API documentation](https://github.com/bitovi/react-to-web-component/blob/main/docs/api.md).

# We want to hear from you.

Expand Down
10 changes: 3 additions & 7 deletions packages/legacy/src/react-to-webcomponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ import type { R2WCOptions } from "@r2wc/core"
import r2wcCore from "@r2wc/core"

interface ReactType {
createElement: (
type: any,
data: any,
children?: any,
) => React.ReactElement | null
createElement: (type: any, data: any, children?: any) => React.ReactElement
}

interface ReactDOMRootRootType {
Expand All @@ -25,7 +21,7 @@ interface ReactDOMRootType {
interface ReactDOMRenderType {
unmountComponentAtNode: (container: Element | DocumentFragment) => boolean
render: (
element: React.ReactElement | null,
element: React.ReactElement,
container: ReactDOM.Container | null,
) => unknown
}
Expand All @@ -37,7 +33,7 @@ interface Context<Props> {
}

/**
* Converts a React component into a webcomponent by wrapping it in a Proxy object.
* Converts a React component into a webcomponent by mounting it into an HTMLElement container.
* @param {ReactComponent}
* @param {React}
* @param {ReactDOM}
Expand Down
19 changes: 16 additions & 3 deletions packages/react-to-web-component/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ npm install @r2wc/react-to-web-component

## Examples

* [Greeting](https://codesandbox.io/s/greeting-react-17-u4l3x1)
* [All the Props](https://codesandbox.io/s/all-the-props-react-17-x09rxo)
* [Greeting](https://codesandbox.io/s/greeting-md5oih)
* [All the Props](https://codesandbox.io/s/all-the-props-n8z5hv)
* [Header Demo](https://codesandbox.io/s/example-header-blog-7k313l)
* [MUI Button](https://codesandbox.io/s/example-mui-button-qwidh9)
* [Checklist Demo](https://codesandbox.io/s/example-checklist-blog-y3nqwx)

## Blog Posts

Expand All @@ -102,7 +105,17 @@ R2WC with Create React App (CRA) [View Post](https://www.bitovi.com/blog/how-to-

## How it works

Check out our [full API documentation](../../docs/api.md).
Check out our [full API documentation](https://github.com/bitovi/react-to-web-component/blob/main/docs/api.md).

Under the hood, `r2wc` creates a `CustomElementConstructor` with custom getters/setters and life cycle methods that keep track of the props that you have defined. When a property is set, its custom setter:

- re-renders the React component inside the custom element.
- creates an enumerable getter / setter on the instance to save the set value and avoid hitting the proxy in the future.

Also:

- Enumerable properties and values on the custom element are used as the `props` passed to the React component.
- The React component is not rendered until the custom element is inserted into the page.

# We want to hear from you.

Expand Down
9 changes: 5 additions & 4 deletions packages/react-to-web-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@
"@r2wc/core": "^1.0.0"
},
"devDependencies": {
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0"
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"prop-types": "^15.8.1"
},
"peerDependencies": {
"react": "^16.0.0 || ^17.0.0",
"react-dom": "^16.0.0 || ^17.0.0"
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, assert } from "vitest"
import matchers from "@testing-library/jest-dom/matchers"
import React from "react"
import PropTypes from "prop-types"

import r2wc from "./react-to-web-component"

Expand All @@ -14,7 +16,7 @@ const Greeting: React.FC<{ name: string }> = ({ name }) => (
<h1>Hello, {name}</h1>
)

describe("react-to-web-component", () => {
describe("react-to-web-component 1", () => {
it("basics with react", () => {
const MyWelcome = r2wc(Greeting)
customElements.define("my-welcome", MyWelcome)
Expand All @@ -26,6 +28,71 @@ describe("react-to-web-component", () => {
expect(myWelcome.nodeName).toEqual("MY-WELCOME")
})

it("works with props array", async () => {
function TestComponent({ name }: { name: string }) {
return <div>hello, {name}</div>
}

const TestElement = r2wc(TestComponent, { props: ["name"] })

customElements.define("test-hello", TestElement)

const body = document.body
body.innerHTML = "<test-hello name='Bavin'></test-hello>"

await flushPromises()

const div = body.querySelector("div")
expect(div?.textContent).toBe("hello, Bavin")
})

it("works with proptypes", async () => {
function WithProptypes({ name }: { name: string }) {
return <div>hello, {name}</div>
}

WithProptypes.propTypes = {
name: PropTypes.string.isRequired,
}

const WithPropTypesElement = r2wc(WithProptypes)

customElements.define("with-proptypes", WithPropTypesElement)

const body = document.body
body.innerHTML = "<with-proptypes name='Bavin'></with-proptypes>"

await flushPromises()

const div = body.querySelector("div")
expect(div?.textContent).toBe("hello, Bavin")
})

it("works with class components", async () => {
class TestClassComponent extends React.Component<{ name: string }> {
render() {
return <div>hello, {this.props.name}</div>
}
}

class TestClassElement extends r2wc(TestClassComponent, {
props: ["name"],
}) {}

customElements.define("test-class", TestClassElement)

const body = document.body
body.innerHTML = "<test-class name='Bavin'></test-class>"

await flushPromises()

const div = body.querySelector("div")
const testClassEl = body.querySelector("test-class")

expect(testClassEl).toBeInstanceOf(TestClassElement)
expect(div?.textContent).toBe("hello, Bavin")
})

it("works with shadow DOM `options.shadow === 'open'`", async () => {
expect.assertions(5)

Expand Down
21 changes: 11 additions & 10 deletions packages/react-to-web-component/src/react-to-web-component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { R2WCOptions } from "@r2wc/core"
import type { Root } from "react-dom/client"

import React from "react"
import ReactDOM from "react-dom"
import { createRoot } from "react-dom/client"

import r2wcCore from "@r2wc/core"

interface Context<Props extends object> {
container: HTMLElement
root: Root
ReactComponent: React.ComponentType<Props>
}

Expand All @@ -15,27 +16,27 @@ function mount<Props extends object>(
ReactComponent: React.ComponentType<Props>,
props: Props,
): Context<Props> {
const element = React.createElement(ReactComponent, props)
const root = createRoot(container)

ReactDOM.render(element, container)
const element = React.createElement(ReactComponent, props)
root.render(element)

return {
container,
root,
ReactComponent,
}
}

function update<Props extends object>(
{ container, ReactComponent }: Context<Props>,
{ root, ReactComponent }: Context<Props>,
props: Props,
): void {
const element = React.createElement(ReactComponent, props)

ReactDOM.render(element, container)
root.render(element)
}

function unmount<Props extends object>({ container }: Context<Props>): void {
ReactDOM.unmountComponentAtNode(container)
function unmount<Props extends object>({ root }: Context<Props>): void {
root.unmount()
}

export default function r2wc<Props extends object>(
Expand Down
Loading

0 comments on commit f3670fa

Please sign in to comment.