Skip to content

Commit

Permalink
Move to compat
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Dec 30, 2024
1 parent 1006fd9 commit e471217
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 84 deletions.
12 changes: 9 additions & 3 deletions compat/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,21 @@ declare namespace React {
): T;

// Preact Defaults
export import Context = preact.Context;
export import ContextType = preact.ContextType;
export interface Context<T> extends preact.Provider<T> {
Consumer: preact.Consumer<T>;
Provider: preact.Provider<T>;
displayName?: string;
}
export function createContext<T>(defaultValue: T): Context<T>;
export type ContextType<C extends Context<any>> = C extends Context<infer T>
? T
: never;
export import RefObject = preact.RefObject;
export import Component = preact.Component;
export import FunctionComponent = preact.FunctionComponent;
export import ComponentType = preact.ComponentType;
export import ComponentClass = preact.ComponentClass;
export import FC = preact.FunctionComponent;
export import createContext = preact.createContext;
export import Ref = preact.Ref;
export import createRef = preact.createRef;
export import Fragment = preact.Fragment;
Expand Down
10 changes: 10 additions & 0 deletions compat/src/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,16 @@ function handleDomVNode(vnode) {

let oldVNodeHook = options.vnode;
options.vnode = vnode => {
// @ts-expect-error type can't be null, however TS is confused
if (
vnode.type != null &&
typeof vnode.type === 'object' &&
'Provider' in vnode.type
) {
// @ts-expect-error
vnode.type = vnode.type.Provider;
}

// only normalize props on Element nodes
if (typeof vnode.type === 'string') {
handleDomVNode(vnode);
Expand Down
34 changes: 34 additions & 0 deletions compat/test/browser/render.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,40 @@ describe('compat render', () => {
expect(scratch.textContent).to.equal('foo');
});

it('should allow context as a component', () => {
const Context = createContext(null);
const CONTEXT = { a: 'a' };

let receivedContext;

class Inner extends Component {
render(props) {
return <div>{props.a}</div>;
}
}

sinon.spy(Inner.prototype, 'render');

render(
<Context value={CONTEXT}>
<div>
<Context.Consumer>
{data => {
receivedContext = data;
return <Inner {...data} />;
}}
</Context.Consumer>
</div>
</Context>,
scratch
);

// initial render does not invoke anything but render():
expect(Inner.prototype.render).to.have.been.calledWithMatch(CONTEXT);
expect(receivedContext).to.equal(CONTEXT);
expect(scratch.innerHTML).to.equal('<div><div>a</div></div>');
});

it("should support recoils's usage of __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED", () => {
// Simplified version of: https://github.com/facebookexperimental/Recoil/blob/c1b97f3a0117cad76cbc6ab3cb06d89a9ce717af/packages/recoil/core/Recoil_ReactMode.js#L36-L44
function useStateWrapper(init) {
Expand Down
18 changes: 18 additions & 0 deletions compat/test/ts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,21 @@ React.unmountComponentAtNode(document.body.shadowRoot!);
React.createPortal(<div />, document.createElement('div'));
React.createPortal(<div />, document.createDocumentFragment());
React.createPortal(<div />, document.body.shadowRoot!);

const Ctx = React.createContext({ contextValue: '' });
class SimpleComponentWithContextAsProvider extends React.Component {
componentProp = 'componentProp';
render() {
// Render inside div to ensure standard JSX elements still work
return (
<Ctx value={{ contextValue: 'value' }}>
<div>
{/* Ensure context still works */}
<Ctx.Consumer>
{({ contextValue }) => contextValue.toLowerCase()}
</Ctx.Consumer>
</div>
</Ctx>
);
}
}
1 change: 0 additions & 1 deletion src/create-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export let i = 0;
export function createContext(defaultValue, contextId) {
contextId = '__cC' + i++;

/** @type {any} */
const context = {
_id: contextId,
_defaultValue: defaultValue,
Expand Down
9 changes: 1 addition & 8 deletions src/create-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let vnodeId = 0;
* Create an virtual node (used for JSX)
* @param {import('./internal').VNode["type"]} type The node name or Component constructor for this
* virtual node
* @param {object | null | undefined | import('.').Context} [props] The properties of the virtual node
* @param {object | null | undefined} [props] The properties of the virtual node
* @param {Array<import('.').ComponentChildren>} [children] The children of the
* virtual node
* @returns {import('./internal').VNode}
Expand All @@ -18,13 +18,6 @@ export function createElement(type, props, children) {
key,
ref,
i;

// @ts-expect-error type can't be null, however TS is confused
if (type != null && typeof type === 'object' && 'Provider' in type) {
// @ts-expect-error
type = type.Provider;
}

for (i in props) {
if (i == 'key') key = props[i];
else if (i == 'ref') ref = props[i];
Expand Down
2 changes: 1 addition & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ export type ContextType<C extends Context<any>> = C extends Context<infer T>
? T
: never;

export interface Context<T> extends Provider<T> {
export interface Context<T> {
Consumer: Consumer<T>;
Provider: Provider<T>;
displayName?: string;
Expand Down
34 changes: 0 additions & 34 deletions test/browser/createContext.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,40 +57,6 @@ describe('createContext', () => {
expect(scratch.innerHTML).to.equal('<div><div>a</div></div>');
});

it('should allow context as a component', () => {
const Context = createContext(null);
const CONTEXT = { a: 'a' };

let receivedContext;

class Inner extends Component {
render(props) {
return <div>{props.a}</div>;
}
}

sinon.spy(Inner.prototype, 'render');

render(
<Context value={CONTEXT}>
<div>
<Context.Consumer>
{data => {
receivedContext = data;
return <Inner {...data} />;
}}
</Context.Consumer>
</div>
</Context>,
scratch
);

// initial render does not invoke anything but render():
expect(Inner.prototype.render).to.have.been.calledWithMatch(CONTEXT);
expect(receivedContext).to.equal(CONTEXT);
expect(scratch.innerHTML).to.equal('<div><div>a</div></div>');
});

// This optimization helps
// to prevent a Provider from rerendering the children, this means
// we only propagate to children.
Expand Down
37 changes: 0 additions & 37 deletions test/ts/custom-elements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,41 +82,4 @@ class SimpleComponent extends Component {
}
}

class SimpleComponentWithContextAsProvider extends Component {
componentProp = 'componentProp';
render() {
// Render inside div to ensure standard JSX elements still work
return (
<Ctx value={{ contextValue: 'value' }}>
<div>
<clickable-ce
onClick={e => {
// `this` should be instance of SimpleComponent since this is an
// arrow function
console.log(this.componentProp);

// Validate `currentTarget` is HTMLElement
console.log('clicked ', e.currentTarget.style.display);
}}
></clickable-ce>
<color-picker space="rgb" dir="rtl"></color-picker>
<custom-whatever
dir="auto" // Inherited prop from HTMLAttributes
someattribute="string"
onsomeevent={function (e) {
// Validate `this` and `e` are the right type
console.log('clicked', this.instanceProp, e.eventProp);
}}
></custom-whatever>

{/* Ensure context still works */}
<Ctx.Consumer>
{({ contextValue }) => contextValue.toLowerCase()}
</Ctx.Consumer>
</div>
</Ctx>
);
}
}

const component = <SimpleComponent />;

0 comments on commit e471217

Please sign in to comment.