Skip to content

Commit 3cca31c

Browse files
torounitmirkaciampo
authored andcommitted
SlotFill: Migrate to Typescript. (#51350)
* convert typescript slot-fill-context.ts and slot-fill-provider.tsx * fix portalContainer type. * convert hooks to ts. * fix types * fix fillProps * convert slot.tsx * update Fill * fix typename. * fix dropdown v2 portal container type. * fix ref type * refactor * refactor SlotFillProvider * migrate SlotComponent to TS * convert useSlot * add update * fix ProviderProps * refactor type * refactor type * allow symbol to name prop. * refactor SlotComponentProps * refactor stroies and README * fix typo * remove comments. remove children from Slot. * refactor SlotComponentProps * Apply suggestions from code review Co-authored-by: Lena Morita <lena@jaguchi.com> * Apply suggestions from code review Co-authored-by: Lena Morita <lena@jaguchi.com> * refactor: newChildren to inline. * Remove unnecessary type variables. * fix type comments * Remove unnecessary Type Guards. Remove unnecessary Type Guards. And use `React.Key`. * remove type from jsdoc * refactor types * refactor types * Simplify the type of `slot`. * Remove the type specification and leave it to type inference. * fix types * remove story.js * update changelog * remove ts-nocheck * fix story filename. remove override bubblesVirtually attribute * replace useState to useMemo * switch to ts-expect-error in story. * fix mssing changelog https://github.com/WordPress/gutenberg/pull/53272/files/362b6d4405edd476df1f6479bb264dc9f51d6789#r1319926144 * add `children?: never` #51350 (comment) * use Record * use Record / Enable className only when bubblesVirtually: true. * Update packages/components/src/slot-fill/types.ts Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com> * update changelog * use optional chain * fix WordPressComponentProps import --------- Co-authored-by: Lena Morita <lena@jaguchi.com> Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>
1 parent 82fc70d commit 3cca31c

File tree

21 files changed

+682
-377
lines changed

21 files changed

+682
-377
lines changed

packages/components/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- `Tooltip`, `Shortcut`: Remove unused `ui/` components from the codebase ([#54573](https://github.com/WordPress/gutenberg/pull/54573))
1919
- Update `uuid` package to 9.0.1 ([#54725](https://github.com/WordPress/gutenberg/pull/54725)).
2020
- `ContextSystemProvider`: Move out of `ui/` ([#54847](https://github.com/WordPress/gutenberg/pull/54847)).
21+
- `SlotFill`: Migrate to TypeScript and Convert to Functional Component `<Slot bubblesVirtually />`. ([#51350](https://github.com/WordPress/gutenberg/pull/51350)).
22+
2123

2224
## 25.8.0 (2023-09-20)
2325

@@ -130,6 +132,7 @@
130132

131133
- `ColorPalette`, `BorderControl`: Don't hyphenate hex value in `aria-label` ([#52932](https://github.com/WordPress/gutenberg/pull/52932)).
132134
- `MenuItemsChoice`, `MenuItem`: Support a `disabled` prop on a menu item ([#52737](https://github.com/WordPress/gutenberg/pull/52737)).
135+
- `TabPanel`: Introduce a new version of `TabPanel` with updated internals and improved adherence to ARIA guidance on `tabpanel` focus behavior while maintaining the same functionality and API surface.([#52133](https://github.com/WordPress/gutenberg/pull/52133)).
133136

134137
### Bug Fix
135138

packages/components/src/dropdown-menu-v2/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,5 +261,5 @@ export type DropdownMenuPrivateContext = Pick<
261261
DropdownMenuInternalContext,
262262
'variant'
263263
> & {
264-
portalContainer: HTMLElement | null;
264+
portalContainer?: HTMLElement | null;
265265
};

packages/components/src/popover/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,6 @@ function PopoverSlot(
497497
) {
498498
return (
499499
<Slot
500-
// @ts-expect-error Need to type `SlotFill`
501500
bubblesVirtually
502501
name={ name }
503502
className="popover-slot"

packages/components/src/slot-fill/bubbles-virtually/fill.js renamed to packages/components/src/slot-fill/bubbles-virtually/fill.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// @ts-nocheck
21
/**
32
* WordPress dependencies
43
*/
@@ -9,6 +8,7 @@ import { useRef, useState, useEffect, createPortal } from '@wordpress/element';
98
*/
109
import useSlot from './use-slot';
1110
import StyleProvider from '../../style-provider';
11+
import type { FillComponentProps } from '../types';
1212

1313
function useForceUpdate() {
1414
const [ , setState ] = useState( {} );
@@ -28,7 +28,8 @@ function useForceUpdate() {
2828
};
2929
}
3030

31-
export default function Fill( { name, children } ) {
31+
export default function Fill( props: FillComponentProps ) {
32+
const { name, children } = props;
3233
const { registerFill, unregisterFill, ...slot } = useSlot( name );
3334
const rerender = useForceUpdate();
3435
const ref = useRef( { rerender } );
@@ -47,17 +48,15 @@ export default function Fill( { name, children } ) {
4748
return null;
4849
}
4950

50-
if ( typeof children === 'function' ) {
51-
children = children( slot.fillProps );
52-
}
53-
5451
// When using a `Fill`, the `children` will be rendered in the document of the
5552
// `Slot`. This means that we need to wrap the `children` in a `StyleProvider`
5653
// to make sure we're referencing the right document/iframe (instead of the
5754
// context of the `Fill`'s parent).
5855
const wrappedChildren = (
5956
<StyleProvider document={ slot.ref.current.ownerDocument }>
60-
{ children }
57+
{ typeof children === 'function'
58+
? children( slot.fillProps ?? {} )
59+
: children }
6160
</StyleProvider>
6261
);
6362

packages/components/src/slot-fill/bubbles-virtually/slot-fill-context.js renamed to packages/components/src/slot-fill/bubbles-virtually/slot-fill-context.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// @ts-nocheck
21
/**
32
* External dependencies
43
*/
@@ -8,8 +7,12 @@ import { proxyMap } from 'valtio/utils';
87
*/
98
import { createContext } from '@wordpress/element';
109
import warning from '@wordpress/warning';
10+
/**
11+
* Internal dependencies
12+
*/
13+
import type { SlotFillBubblesVirtuallyContext } from '../types';
1114

12-
const SlotFillContext = createContext( {
15+
const initialContextValue: SlotFillBubblesVirtuallyContext = {
1316
slots: proxyMap(),
1417
fills: proxyMap(),
1518
registerSlot: () => {
@@ -25,6 +28,8 @@ const SlotFillContext = createContext( {
2528

2629
// This helps the provider know if it's using the default context value or not.
2730
isDefault: true,
28-
} );
31+
};
32+
33+
const SlotFillContext = createContext( initialContextValue );
2934

3035
export default SlotFillContext;

packages/components/src/slot-fill/bubbles-virtually/slot-fill-provider.js

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { ref as valRef } from 'valtio';
5+
import { proxyMap } from 'valtio/utils';
6+
7+
/**
8+
* WordPress dependencies
9+
*/
10+
import { useMemo } from '@wordpress/element';
11+
import isShallowEqual from '@wordpress/is-shallow-equal';
12+
13+
/**
14+
* Internal dependencies
15+
*/
16+
import SlotFillContext from './slot-fill-context';
17+
import type {
18+
SlotFillProviderProps,
19+
SlotFillBubblesVirtuallyContext,
20+
} from '../types';
21+
22+
function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
23+
const slots: SlotFillBubblesVirtuallyContext[ 'slots' ] = proxyMap();
24+
const fills: SlotFillBubblesVirtuallyContext[ 'fills' ] = proxyMap();
25+
26+
const registerSlot: SlotFillBubblesVirtuallyContext[ 'registerSlot' ] = (
27+
name,
28+
ref,
29+
fillProps
30+
) => {
31+
const slot = slots.get( name );
32+
33+
slots.set(
34+
name,
35+
valRef( {
36+
...slot,
37+
ref: ref || slot?.ref,
38+
fillProps: fillProps || slot?.fillProps || {},
39+
} )
40+
);
41+
};
42+
43+
const unregisterSlot: SlotFillBubblesVirtuallyContext[ 'unregisterSlot' ] =
44+
( name, ref ) => {
45+
// Make sure we're not unregistering a slot registered by another element
46+
// See https://github.com/WordPress/gutenberg/pull/19242#issuecomment-590295412
47+
if ( slots.get( name )?.ref === ref ) {
48+
slots.delete( name );
49+
}
50+
};
51+
52+
const updateSlot: SlotFillBubblesVirtuallyContext[ 'updateSlot' ] = (
53+
name,
54+
fillProps
55+
) => {
56+
const slot = slots.get( name );
57+
if ( ! slot ) {
58+
return;
59+
}
60+
61+
if ( isShallowEqual( slot.fillProps, fillProps ) ) {
62+
return;
63+
}
64+
65+
slot.fillProps = fillProps;
66+
const slotFills = fills.get( name );
67+
if ( slotFills ) {
68+
// Force update fills.
69+
slotFills.map( ( fill ) => fill.current.rerender() );
70+
}
71+
};
72+
73+
const registerFill: SlotFillBubblesVirtuallyContext[ 'registerFill' ] = (
74+
name,
75+
ref
76+
) => {
77+
fills.set( name, valRef( [ ...( fills.get( name ) || [] ), ref ] ) );
78+
};
79+
80+
const unregisterFill: SlotFillBubblesVirtuallyContext[ 'registerFill' ] = (
81+
name,
82+
ref
83+
) => {
84+
const fillsForName = fills.get( name );
85+
if ( ! fillsForName ) {
86+
return;
87+
}
88+
89+
fills.set(
90+
name,
91+
valRef( fillsForName.filter( ( fillRef ) => fillRef !== ref ) )
92+
);
93+
};
94+
95+
return {
96+
slots,
97+
fills,
98+
registerSlot,
99+
updateSlot,
100+
unregisterSlot,
101+
registerFill,
102+
unregisterFill,
103+
};
104+
}
105+
106+
export default function SlotFillProvider( {
107+
children,
108+
}: SlotFillProviderProps ) {
109+
const registry = useMemo( createSlotRegistry, [] );
110+
return (
111+
<SlotFillContext.Provider value={ registry }>
112+
{ children }
113+
</SlotFillContext.Provider>
114+
);
115+
}

packages/components/src/slot-fill/bubbles-virtually/slot.js renamed to packages/components/src/slot-fill/bubbles-virtually/slot.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
// @ts-nocheck
1+
/**
2+
* External dependencies
3+
*/
4+
import type { ForwardedRef } from 'react';
5+
26
/**
37
* WordPress dependencies
48
*/
@@ -15,21 +19,30 @@ import { useMergeRefs } from '@wordpress/compose';
1519
*/
1620
import { View } from '../../view';
1721
import SlotFillContext from './slot-fill-context';
22+
import type { WordPressComponentProps } from '../../context';
23+
import type { SlotComponentProps } from '../types';
1824

19-
function Slot( props, forwardedRef ) {
25+
function Slot(
26+
props: WordPressComponentProps<
27+
Omit< SlotComponentProps, 'bubblesVirtually' >,
28+
'div'
29+
>,
30+
forwardedRef: ForwardedRef< any >
31+
) {
2032
const {
2133
name,
2234
fillProps = {},
2335
as,
2436
// `children` is not allowed. However, if it is passed,
2537
// it will be displayed as is, so remove `children`.
38+
// @ts-ignore
2639
children,
2740
...restProps
2841
} = props;
2942

3043
const { registerSlot, unregisterSlot, ...registry } =
3144
useContext( SlotFillContext );
32-
const ref = useRef();
45+
const ref = useRef< HTMLElement >( null );
3346

3447
useLayoutEffect( () => {
3548
registerSlot( name, ref, fillProps );

packages/components/src/slot-fill/bubbles-virtually/use-slot-fills.js renamed to packages/components/src/slot-fill/bubbles-virtually/use-slot-fills.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// @ts-nocheck
21
/**
32
* External dependencies
43
*/
@@ -13,8 +12,9 @@ import { useContext } from '@wordpress/element';
1312
* Internal dependencies
1413
*/
1514
import SlotFillContext from './slot-fill-context';
15+
import type { SlotKey } from '../types';
1616

17-
export default function useSlotFills( name ) {
17+
export default function useSlotFills( name: SlotKey ) {
1818
const registry = useContext( SlotFillContext );
1919
const fills = useSnapshot( registry.fills, { sync: true } );
2020
// The important bit here is that this call ensures that the hook

0 commit comments

Comments
 (0)