Skip to content

Commit

Permalink
chore: removes import source files
Browse files Browse the repository at this point in the history
& restructure jsx to simplify
  • Loading branch information
bsunderhus committed Apr 12, 2023
1 parent 6accff9 commit 722dac4
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 279 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import { Fragment } from 'react';
import * as React_2 from 'react';

export { Fragment }
// @public (undocumented)
export function createElement<P extends {}>(type: React_2.ElementType<P>, props?: P | null, ...children: React_2.ReactNode[]): React_2.ReactElement<P> | null;

// @public
export function jsx<P extends {}>(type: React_2.ElementType<P>, props?: P | null, ...children: React_2.ReactNode[]): React_2.ReactElement<P> | null;
export { Fragment }

// (No @packageDocumentation comment for this package)

Expand Down
168 changes: 168 additions & 0 deletions packages/react-components/react-jsx-runtime/src/createElement.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/* eslint-disable jsdoc/check-tag-names */
/** @jsxRuntime classic */
/** @jsxFrag Fragment */
/** @jsx createElement */
/* eslint-enable jsdoc/check-tag-names */

import { render } from '@testing-library/react';
import { ComponentProps, ComponentState, Slot, getSlotsNext, resolveShorthand } from '@fluentui/react-utilities';
import { createElement } from './createElement';

describe('createElement', () => {
describe('general behavior tests', () => {
it('handles a string', () => {
const result = render(<div>Hello world</div>);

expect(result.container.firstChild).toMatchInlineSnapshot(`
<div>
Hello world
</div>
`);
});

it('handles an array', () => {
const result = render(
<div>
{Array.from({ length: 3 }, (_, i) => (
<div key={i}>{i}</div>
))}
</div>,
);

expect(result.container.firstChild).toMatchInlineSnapshot(`
<div>
<div>
0
</div>
<div>
1
</div>
<div>
2
</div>
</div>
`);
});

it('handles an array of children', () => {
const result = render(
<div>
<div>1</div>
<div>2</div>
</div>,
);

expect(result.container.firstChild).toMatchInlineSnapshot(`
<div>
<div>
1
</div>
<div>
2
</div>
</div>
`);
});
});

describe('custom behavior tests', () => {
it('keeps children from "defaultProps" in a render callback', () => {
type TestComponentSlots = { slot: Slot<'div'> };
type TestComponentState = ComponentState<TestComponentSlots>;
type TestComponentProps = ComponentProps<Partial<TestComponentSlots>>;

const TestComponent = (props: TestComponentProps) => {
const state: TestComponentState = {
components: { slot: 'div' },

slot: resolveShorthand(props.slot, {
defaultProps: { children: 'Default Children', id: 'slot' },
}),
};
const { slots, slotProps } = getSlotsNext<TestComponentSlots>(state);

return <slots.slot {...slotProps.slot} />;
};

const children = jest.fn().mockImplementation((Component, props) => (
<div id="render-fn">
<Component {...props} />
</div>
));
const result = render(<TestComponent slot={{ children }} />);

expect(children).toHaveBeenCalledTimes(1);
expect(children).toHaveBeenCalledWith('div', { children: 'Default Children', id: 'slot' });

expect(result.container.firstChild).toMatchInlineSnapshot(`
<div
id="render-fn"
>
<div
id="slot"
>
Default Children
</div>
</div>
`);
});

it('keeps children from a render template in a render callback', () => {
type TestComponentSlots = { outer: Slot<'div'>; inner: Slot<'div'> };
type TestComponentState = ComponentState<TestComponentSlots>;
type TestComponentProps = ComponentProps<Partial<TestComponentSlots>>;

const TestComponent = (props: TestComponentProps) => {
const state: TestComponentState = {
components: { inner: 'div', outer: 'div' },

inner: resolveShorthand(props.inner, { defaultProps: { id: 'inner' } }),
outer: resolveShorthand(props.outer, { defaultProps: { id: 'outer' } }),
};
const { slots, slotProps } = getSlotsNext<TestComponentSlots>(state);

return (
<slots.outer {...slotProps.outer}>
<slots.inner {...slotProps.inner} />
</slots.outer>
);
};

const children = jest.fn().mockImplementation((Component, props) => (
<div id="render-fn">
<Component {...props} />
</div>
));
const result = render(<TestComponent outer={{ children }} inner={{ children: 'Inner children' }} />);

expect(children).toHaveBeenCalledTimes(1);
expect(children.mock.calls[0][0]).toBe('div');
expect(children.mock.calls[0][1].id).toBe('outer');
expect(children.mock.calls[0][1].children).toMatchInlineSnapshot(`
<React.Fragment>
<div
id="inner"
>
Inner children
</div>
</React.Fragment>
`);

expect(result.container.firstChild).toMatchInlineSnapshot(`
<div
id="render-fn"
>
<div
id="outer"
>
<div
id="inner"
>
Inner children
</div>
</div>
</div>
`);
});
});
});
60 changes: 60 additions & 0 deletions packages/react-components/react-jsx-runtime/src/createElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as React from 'react';
import { SlotRenderFunction, UnknownSlotProps, SLOT_RENDER_FUNCTION_SYMBOL } from '@fluentui/react-utilities';

type WithMetadata<Props extends {}> = Props & {
[SLOT_RENDER_FUNCTION_SYMBOL]?: SlotRenderFunction<Props>;
};

export function createElement<P extends {}>(
type: React.ElementType<P>,
props?: P | null,
...children: React.ReactNode[]
): React.ReactElement<P> | null {
if (isSlotComponent(props)) {
const result = normalizeElementArguments(props, children);

if (result.renderFn) {
return React.createElement(
React.Fragment,
{},
result.renderFn(type, { ...result.props, children: result.children }),
) as React.ReactElement<P>;
}
}

return React.createElement(type, props, ...children);
}

function normalizeElementArguments<Props extends UnknownSlotProps>(
propsWithMetadata: WithMetadata<Props>,
overrideChildren?: React.ReactNode[],
): {
props: Props;
children: React.ReactNode;
renderFn?: SlotRenderFunction<Props>;
} {
const { [SLOT_RENDER_FUNCTION_SYMBOL]: renderFn, ...props } = propsWithMetadata;
if (overrideChildren?.length === 0) {
overrideChildren = undefined;
}
const children: React.ReactNode[] | React.ReactNode = overrideChildren ?? propsWithMetadata.children;
const normalizedChildren: React.ReactNode = Array.isArray(children)
? React.createElement(React.Fragment, {}, ...children)
: children;
if (renderFn) {
const { children: _, ...propsWithoutChildren } = props as UnknownSlotProps;
return {
renderFn,
children: normalizedChildren,
props: propsWithoutChildren as Props,
};
}
return {
children: normalizedChildren,
props: props as UnknownSlotProps as Props,
};
}

export function isSlotComponent<Props extends {}>(props?: Props | null): props is WithMetadata<Props> {
return Boolean(props?.hasOwnProperty(SLOT_RENDER_FUNCTION_SYMBOL));
}
2 changes: 1 addition & 1 deletion packages/react-components/react-jsx-runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { jsx } from './jsx';
export { createElement } from './createElement';
export { Fragment } from 'react';
18 changes: 0 additions & 18 deletions packages/react-components/react-jsx-runtime/src/jsx-dev-runtime.ts

This file was deleted.

24 changes: 0 additions & 24 deletions packages/react-components/react-jsx-runtime/src/jsx-runtime.ts

This file was deleted.

84 changes: 0 additions & 84 deletions packages/react-components/react-jsx-runtime/src/jsx.test.tsx

This file was deleted.

Loading

0 comments on commit 722dac4

Please sign in to comment.