-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dea94a8
commit eb275e8
Showing
19 changed files
with
536 additions
and
15 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-jsx-runtime-92a3c6b2-e767-4128-8320-7e237ef69eaa.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "prerelease", | ||
"comment": "feat: implements custom JSX pragma", | ||
"packageName": "@fluentui/react-jsx-runtime", | ||
"email": "bernardo.sunderhus@gmail.com", | ||
"dependentChangeType": "patch" | ||
} |
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-utilities-6ec7d77e-2bd1-4bcc-a0fb-0f71642d43bd.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "minor", | ||
"comment": "feat: ensure compatibility with custom JSX pragma", | ||
"packageName": "@fluentui/react-utilities", | ||
"email": "bernardo.sunderhus@gmail.com", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
// TODO: replace with real exports | ||
export {}; | ||
export { jsx } from './jsx'; | ||
export { Fragment } from 'react'; |
18 changes: 18 additions & 0 deletions
18
packages/react-components/react-jsx-runtime/src/jsx-dev-runtime.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/// <reference path="types.d.ts" /> | ||
|
||
import * as React from 'react'; | ||
import { jsx as createElement, extractChildrenFromProps } from './jsx'; | ||
|
||
export function jsxDEV<P extends {}>( | ||
type: React.ElementType<P>, | ||
props?: P | null, | ||
key?: string, | ||
isStaticChildren?: boolean, | ||
source?: unknown, | ||
self?: unknown, | ||
): React.ReactElement<P> | null { | ||
// extractChildrenFromProps is required since jsxDev signature differs from React.createElement signature | ||
return createElement(type, props, ...extractChildrenFromProps(props)); | ||
} | ||
|
||
export { Fragment } from 'react/jsx-dev-runtime'; |
24 changes: 24 additions & 0 deletions
24
packages/react-components/react-jsx-runtime/src/jsx-runtime.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/// <reference path="types.d.ts" /> | ||
|
||
import * as React from 'react'; | ||
import { jsx as createElement, extractChildrenFromProps } from './jsx'; | ||
|
||
export function jsx<P extends {}>( | ||
type: React.ElementType<P>, | ||
props?: P | null, | ||
key?: string, | ||
): React.ReactElement<P> | null { | ||
// extractChildrenFromProps is required since jsx signature differs from React.createElement signature | ||
return createElement(type, props, ...extractChildrenFromProps(props)); | ||
} | ||
|
||
export function jsxs<P extends {}>( | ||
type: React.ElementType<P>, | ||
props?: P | null, | ||
key?: string, | ||
): React.ReactElement<P> | null { | ||
// extractChildrenFromProps is required since jsxs signature differs from React.createElement signature | ||
return createElement(type, props, ...extractChildrenFromProps(props)); | ||
} | ||
|
||
export { Fragment } from 'react/jsx-runtime'; |
84 changes: 84 additions & 0 deletions
84
packages/react-components/react-jsx-runtime/src/jsx.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* eslint-disable jsdoc/check-tag-names */ | ||
/** @jsxRuntime classic */ | ||
/** @jsxFrag Fragment */ | ||
/** @jsx jsx */ | ||
/* eslint-enable jsdoc/check-tag-names */ | ||
|
||
import { | ||
ComponentProps, | ||
ComponentState, | ||
Slot, | ||
getSlots, | ||
getSlotsNext, | ||
resolveShorthand, | ||
} from '@fluentui/react-utilities'; | ||
import { jsx, Fragment } from './index'; | ||
import { render } from '@testing-library/react'; | ||
|
||
type TestComponentSlots = { slot: Slot<'div'> }; | ||
type TestComponentState = ComponentState<TestComponentSlots>; | ||
type TestComponentProps = ComponentProps<Partial<TestComponentSlots>> & { | ||
getSlots: typeof getSlots; | ||
}; | ||
|
||
const TestComponent = (props: TestComponentProps) => { | ||
const state: TestComponentState = { | ||
components: { | ||
slot: 'div', | ||
}, | ||
slot: resolveShorthand( | ||
props.slot ?? { | ||
children: (C, p) => ( | ||
<> | ||
<div>before</div> | ||
<C {...p} /> | ||
<div>after</div> | ||
</> | ||
), | ||
}, | ||
{ | ||
required: true, | ||
defaultProps: { | ||
children: <div>this is internal children</div>, | ||
}, | ||
}, | ||
), | ||
}; | ||
const { slots, slotProps } = props.getSlots<TestComponentSlots>(state); | ||
return <slots.slot {...slotProps.slot} />; | ||
}; | ||
|
||
describe('jsx', () => { | ||
it('should lose internal children while using getSlots', () => { | ||
const result = render(<TestComponent getSlots={getSlots} />); | ||
expect(result.container).toMatchInlineSnapshot(` | ||
<div> | ||
<div> | ||
before | ||
</div> | ||
<div /> | ||
<div> | ||
after | ||
</div> | ||
</div> | ||
`); | ||
}); | ||
it('should keep internal children while using getSlotsNext', () => { | ||
const result = render(<TestComponent getSlots={getSlotsNext} />); | ||
expect(result.container).toMatchInlineSnapshot(` | ||
<div> | ||
<div> | ||
before | ||
</div> | ||
<div> | ||
<div> | ||
this is internal children | ||
</div> | ||
</div> | ||
<div> | ||
after | ||
</div> | ||
</div> | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import * as React from 'react'; | ||
import { | ||
SlotRenderFunction, | ||
UnknownSlotProps, | ||
SLOT_EXTERNAL_CHILDREN_SYMBOL, | ||
SLOT_INTERNAL_CHILDREN_SYMBOL, | ||
} from '@fluentui/react-utilities'; | ||
|
||
type WithMetadata<Props extends {}> = Props & { | ||
[SLOT_EXTERNAL_CHILDREN_SYMBOL]: React.ReactNode | SlotRenderFunction<Props>; | ||
[SLOT_INTERNAL_CHILDREN_SYMBOL]: React.ReactNode | SlotRenderFunction<Props>; | ||
}; | ||
|
||
/** | ||
* Equivalent to React.createElement but supporting v9 slot API | ||
*/ | ||
export function jsx<P extends {}>( | ||
type: React.ElementType<P>, | ||
props?: P | null, | ||
...children: React.ReactNode[] | ||
): React.ReactElement<P> | null { | ||
return isSlotComponent(props) | ||
? jsxFromSlotComponent(type, props, ...children) | ||
: React.createElement(type, props, ...children); | ||
} | ||
|
||
function jsxFromSlotComponent<Props extends UnknownSlotProps>( | ||
type: React.ElementType<Props>, | ||
propsWithMetadata: WithMetadata<Props>, | ||
...overrideChildren: React.ReactNode[] | ||
): React.ReactElement<Props> | null { | ||
const { children, renderFn, props } = normalizeChildren(propsWithMetadata, overrideChildren); | ||
if (renderFn) { | ||
return React.createElement(React.Fragment, {}, renderFn(type, { ...props, children })) as React.ReactElement<Props>; | ||
} | ||
|
||
return React.createElement<Props>(type, { ...props, children } as Props); | ||
} | ||
|
||
export function extractChildrenFromProps<Props extends React.PropsWithChildren<{}>>( | ||
props?: Props | null, | ||
): React.ReactNode[] { | ||
if (!props) { | ||
return []; | ||
} | ||
return Array.isArray(props.children) ? props.children : [props.children]; | ||
} | ||
|
||
function normalizeChildren<Props extends UnknownSlotProps>( | ||
propsWithMetadata: WithMetadata<Props>, | ||
overrideChildren: React.ReactNode[] = [], | ||
): { | ||
props: Props; | ||
children: React.ReactNode; | ||
renderFn?: SlotRenderFunction<Props>; | ||
} { | ||
const { | ||
[SLOT_EXTERNAL_CHILDREN_SYMBOL]: slotExternalChildren, | ||
[SLOT_INTERNAL_CHILDREN_SYMBOL]: slotInternalChildren, | ||
...props | ||
} = propsWithMetadata; | ||
const isRenderFn = typeof slotExternalChildren === 'function'; | ||
const notNormalizedChildren: React.ReactNode | React.ReactNode[] = | ||
(overrideChildren.length > 0 ? overrideChildren : undefined) ?? | ||
(isRenderFn ? slotInternalChildren : slotExternalChildren) ?? | ||
slotInternalChildren ?? | ||
propsWithMetadata.children; | ||
const normalizedChildren = Array.isArray(notNormalizedChildren) | ||
? React.createElement(React.Fragment, {}, ...notNormalizedChildren) | ||
: notNormalizedChildren; | ||
const renderFn = isRenderFn ? (slotExternalChildren as SlotRenderFunction<Props>) : undefined; | ||
if (isRenderFn) { | ||
const { children: _, ...propsWithoutChildren } = props as UnknownSlotProps; | ||
return { | ||
children: normalizedChildren, | ||
renderFn, | ||
props: propsWithoutChildren as Props, | ||
}; | ||
} | ||
return { | ||
children: normalizedChildren, | ||
renderFn, | ||
props: props as UnknownSlotProps as Props, | ||
}; | ||
} | ||
|
||
export function isSlotComponent<Props extends {}>(props?: Props | null): props is WithMetadata<Props> { | ||
return Boolean(props?.hasOwnProperty(SLOT_INTERNAL_CHILDREN_SYMBOL)); | ||
} |
31 changes: 31 additions & 0 deletions
31
packages/react-components/react-jsx-runtime/src/types.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
declare module 'react/jsx-runtime' { | ||
import type * as React from 'react'; | ||
|
||
export const Fragment: symbol; | ||
|
||
export function jsx<P extends {}>( | ||
type: React.ElementType<P>, | ||
props?: P | null, | ||
key?: string, | ||
): React.ReactElement<P> | null; | ||
|
||
export function jsxs<P extends {}>( | ||
type: React.ElementType<P>, | ||
props?: P | null, | ||
key?: string, | ||
): React.ReactElement<P> | null; | ||
} | ||
declare module 'react/jsx-dev-runtime' { | ||
import type * as React from 'react'; | ||
|
||
export const Fragment: symbol; | ||
|
||
export function jsxDEV<P extends {}>( | ||
type: React.ElementType<P>, | ||
props?: P | null, | ||
key?: string, | ||
isStaticChildren?: boolean, | ||
source?: unknown, | ||
self?: unknown, | ||
): React.ReactElement<P> | null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
packages/react-components/react-utilities/src/compose/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* @internal | ||
* internal symbol used to keep defaultProps.children available | ||
*/ | ||
export const SLOT_INTERNAL_CHILDREN_SYMBOL = Symbol('fui.slotInternalChildren'); | ||
/** | ||
* @internal | ||
* internal symbol used to keep slot.children available | ||
*/ | ||
export const SLOT_EXTERNAL_CHILDREN_SYMBOL = Symbol('fui.slotExternalChildren'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.