Skip to content

Commit c7a41d3

Browse files
Merge branch 'main' into dependabot/npm_and_yarn/tests/builds/webpack/http-proxy-middleware-2.0.7
2 parents 22999b3 + 4d2ca52 commit c7a41d3

12 files changed

Lines changed: 474 additions & 144 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
`@r2wc/react-to-web-component`:
88

99
- Works in all modern browsers. (Edge needs a [customElements polyfill](https://github.com/webcomponents/polyfills/tree/master/packages/custom-elements)).
10-
- Is `1.26KB` minified and gzipped.
10+
- Is `1.36KB` minified and gzipped.
1111

1212
## Setup
1313

docs/api.md

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
convert to a Web Component.
77
- `options` - An set of parameters.
88

9-
- `options.shadow` - Use shadow DOM rather than light DOM.
10-
- `options.props` - Array of camelCasedProps to watch as String values or { [camelCasedProps]: "string" | "number" | "boolean" | "function" | "json" }
9+
- `options.shadow` - ("open", "closed", or undefined) Use the specified shadow DOM mode rather than light DOM.
10+
- `options.events` - Array of camelCasedProps to dispatch as custom events or a Record of event names to their associated [Event constructor options](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event#options).
11+
- When dispatching events from named properties, "on" is stripped from the beginning of the property name if present, and the result is lowercased: the property `onMyCustomEvent` dispatches as "mycustomevent".
12+
- `options.props` - Array of camelCasedProps to watch as String values or { [camelCasedProps]: "string" | "number" | "boolean" | "function" | "method" | "json" }
1113

1214
- When specifying Array or Object as the type, the string passed into the attribute must pass `JSON.parse()` requirements.
1315
- When specifying Boolean as the type, "true", "1", "yes", "TRUE", and "t" are mapped to `true`. All strings NOT begining with t, T, 1, y, or Y will be `false`.
@@ -82,7 +84,7 @@ document.body.innerHTML =
8284
console.log(document.body.firstElementChild.innerHTML) // "<h1>Hello, Christopher</h1>"
8385
```
8486

85-
If `options.props` is specified, R2WC will use those props instead of the keys from propTypes. If it's an array, all corresponding kebob-cased attr values will be passed as strings to the underlying React component.
87+
If `options.props` is specified, R2WC will use those props instead of the keys from propTypes. If it's an array, all corresponding kebab-case attr values will be passed as strings to the underlying React component.
8688

8789
```js
8890
function Greeting({ camelCaseName }) {
@@ -107,11 +109,11 @@ console.log(document.body.firstElementChild.innerHTML) // "<h1>Hello, Jane</h1>"
107109
If `options.props` is an object, the keys are the camelCased React props and the values are any one of the following built in javascript types.
108110
This is the recommended way of passing props to r2wc.
109111

110-
`"string" | "number" | "boolean" | "function" | "json"`
112+
`"string" | "number" | "boolean" | "function" | "method" | "json"`
111113

112114
"json" can be an array or object. The string passed into the attribute must pass `JSON.parse()` requirements.
113115

114-
### "string" | "number" | "boolean" | "function" | "json" props
116+
### "string" | "number" | "boolean" | "function" | "method" | "json" props
115117

116118
```js
117119
function AttrPropTypeCasting(props) {
@@ -164,6 +166,8 @@ document.body.innerHTML = `
164166

165167
When `Function` is specified as the type, attribute values on the web component will be converted into function references when passed into the underlying React component. The string value of the attribute must be a valid reference to a function on `window` (or on `global`).
166168

169+
Note: If you want to avoid global functions, instead of passing an attribute you can pass an `events` object in options, and listen on events using `addEventListener` on the custom element. See below.
170+
167171
```js
168172
function ThemeSelect({ handleClick }) {
169173
return (
@@ -198,3 +202,83 @@ setTimeout(
198202
)
199203
// ^ calls globalFn, logs: true, "Jane"
200204
```
205+
206+
207+
### Method props
208+
209+
When `method` is specified as the type, the prop will be bound to a method that can be defined directly on the custom element instance. Unlike `function` props that reference global functions, `method` props allow you to define class methods directly on the web component element, providing better encapsulation and avoiding global namespace pollution.
210+
211+
This is particularly useful when you want to pass functions from parent components or when you need to define behavior specific to each web component instance.
212+
213+
```js
214+
function ClassGreeting({ name, sayHello }) {
215+
return (
216+
<div>
217+
<h1>Hello, {name}</h1>
218+
<button onClick={sayHello}>Click me</button>
219+
</div>
220+
)
221+
}
222+
223+
const WebClassGreeting = reactToWebComponent(ClassGreeting, {
224+
props: {
225+
name: "string",
226+
sayHello: "method",
227+
},
228+
})
229+
230+
customElements.define("class-greeting", WebClassGreeting)
231+
232+
233+
document.body.innerHTML = '<class-greeting name="Christopher"></class-greeting>'
234+
235+
const element = document.querySelector("class-greeting")
236+
237+
const myMethod = function(this: HTMLElement) {
238+
const nameElement = this.querySelector("h1") as HTMLElement;
239+
nameElement.textContent = "Hello, again rerendered";
240+
}
241+
242+
element.sayHello = myMethod.bind(element)
243+
244+
setTimeout(() => {
245+
document.querySelector("class-greeting button").click()
246+
}, 0)
247+
```
248+
249+
### Event dispatching
250+
251+
As an alternative to using function props, the `events` object insructs r2wc to dispatch a corresponding DOM event that can be listened to on the custom element itself, on ancestor elements using `bubbles`, and outside of any containing shadow DOM using `composed`.
252+
253+
```js
254+
function ThemeSelect({ onSelect }) {
255+
return (
256+
<div>
257+
<button onClick={() => onSelect("V")}>V</button>
258+
<button onClick={() => onSelect("Johnny")}>Johnny</button>
259+
<button onClick={() => onSelect("Jane")}>Jane</button>
260+
</div>
261+
)
262+
}
263+
264+
const WebThemeSelect = reactToWebComponent(ThemeSelect, {
265+
events: { onSelect: { bubbles: true } } // dispatches as "select", will bubble to ancestor elements but not escape a shadow DOM
266+
})
267+
268+
customElements.define("theme-select", WebThemeSelect)
269+
270+
document.body.innerHTML = "<theme-select></theme-select>"
271+
272+
setTimeout(() => {
273+
const element = document.querySelector("theme-select")
274+
element.addEventListener("select", (event) => {
275+
// "event.target" is the instance of the WebComponent / HTMLElement
276+
const thisIsEl = event.target === element
277+
console.log(thisIsEl, event.detail)
278+
})
279+
document.querySelector("theme-select button:last-child").click()
280+
}, 0)
281+
// ^ calls event listener, logs: true, "Jane"
282+
```
283+
284+
> Note: `events` and `props` entries should not be used for the same named property. During initial setup, the event handler will overwrite the function property handler, and if the attribute changes after construction, the new function property handler will overwrite the event handler.

0 commit comments

Comments
 (0)