Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/seven-fans-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lit-labs/react': patch
---

Update `WebComponentProps` type to allow providing `ref` prop in JSX.
30 changes: 20 additions & 10 deletions packages/labs/react/src/create-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ const DEV_MODE = true;
* }
* ```
*/
export type WebComponentProps<I extends HTMLElement> = React.HTMLAttributes<I> &
// TODO(augustjk) Consider omitting keyof LitElement to remove "internal"
// lifecycle methods or allow user to explicitly set the prop type instead
// of grabbing from class
Partial<Omit<I, keyof HTMLElement>>;
export type WebComponentProps<I extends HTMLElement> = React.DetailedHTMLProps<
React.HTMLAttributes<I>,
I
> &
ElementProps<I>;

/**
* Type of the React component wrapping the web component. This is the return
Expand All @@ -39,10 +39,20 @@ export type ReactWebComponent<
I extends HTMLElement,
E extends EventNames = {}
> = React.ForwardRefExoticComponent<
React.PropsWithoutRef<WebComponentProps<I> & EventListeners<E>> &
React.RefAttributes<I>
React.PropsWithoutRef<ComponentProps<I, E>> & React.RefAttributes<I>
>;

// Props derived from custom element class. Currently has limitations of making
// all properties optional and also surfaces life cycle methods in autocomplete.
// TODO(augustjk) Consider omitting keyof LitElement to remove "internal"
// lifecycle methods or allow user to explicitly provide props.
type ElementProps<I> = Partial<Omit<I, keyof HTMLElement>>;

// Acceptable props to the React component.
type ComponentProps<I, E extends EventNames = {}> = React.HTMLAttributes<I> &
ElementProps<I> &
EventListeners<E>;

/**
* Type used to cast an event name with an event type when providing the
* `events` option to `createComponent` for better typing of the event handler
Expand Down Expand Up @@ -218,10 +228,10 @@ export const createComponent = <
}
}

type ComponentProps = WebComponentProps<I> & EventListeners<E>;
type Props = ComponentProps<I, E>;

const ReactComponent = React.forwardRef<I, ComponentProps>((props, ref) => {
const prevPropsRef = React.useRef<ComponentProps | null>(null);
const ReactComponent = React.forwardRef<I, Props>((props, ref) => {
const prevPropsRef = React.useRef<Props | null>(null);
const elementRef = React.useRef<I | null>(null);

// Props to be passed to React.createElement
Expand Down
13 changes: 9 additions & 4 deletions packages/labs/react/src/test/create-component_test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,18 @@ suite('createComponent', () => {
});

// Type only test to be caught at build time.
test.skip('renders element with expected type', async () => {
type TypedComponent = ReactWebComponent<BasicElement>;
test.skip('Wrapped component should accept props with correct types', async () => {
const TypedBasicElementComponent = {} as ReactWebComponent<BasicElement>;

let TypedBasicElement: TypedComponent;
<TypedBasicElementComponent str="str" bool={true} num={1} />;

// @ts-expect-error bool prop only accepts boolean
<TypedBasicElement bool={'string'}></TypedBasicElement>;
<TypedBasicElementComponent bool={'string'} />;
});

// Type only test to be caught at build time.
test.skip('WebComponentProps type allows "ref"', async () => {
<x-foo ref={React.createRef()}></x-foo>;
});

test('works with text children', async () => {
Expand Down