Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite: use internal context to consume composite store #64493

Merged
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
4 changes: 3 additions & 1 deletion packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@

- `Composite`: add stable version of the component ([#63564](https://github.com/WordPress/gutenberg/pull/63564)).
- `Composite`: add `Hover` and `Typeahead` subcomponents ([#64399](https://github.com/WordPress/gutenberg/pull/64399)).
- `Composite`: export `useCompositeStore, add focus-related props to `Composite`and`Composite.Item` subcomponents ([#64450](https://github.com/WordPress/gutenberg/pull/64450)).
- `Composite`: export `useCompositeStore`, add focus-related props to `Composite`and`Composite.Item` subcomponents ([#64450](https://github.com/WordPress/gutenberg/pull/64450)).
- `Composite`: add `Context` subcomponent ([#64493](https://github.com/WordPress/gutenberg/pull/64493)).

### Enhancements

- `Composite`: improve Storybook examples and add interactive controls ([#64397](https://github.com/WordPress/gutenberg/pull/64397)).
- `Composite`: use internal context to forward the composite store to sub-components ([#64493](https://github.com/WordPress/gutenberg/pull/64493)).
- `QueryControls`: Default to new 40px size ([#64457](https://github.com/WordPress/gutenberg/pull/64457)).
- `TimePicker`: add `hideLabelFromVision` prop ([#64267](https://github.com/WordPress/gutenberg/pull/64267)).
- `DropdownMenuV2`: adopt elevation scale ([#64432](https://github.com/WordPress/gutenberg/pull/64432)).
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/composite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,7 @@ Allows the component to be rendered as a different HTML element or React compone
The contents of the component.

- Required: no

### `Composite.Context`

The React context used by the composite components. It can be used by to access the composite store, and to forward the context when composite sub-components are rendered across portals (ie. `SlotFill` components) that would not otherwise forward the context to the `Fill` children.
14 changes: 14 additions & 0 deletions packages/components/src/composite/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* WordPress dependencies
*/
import { createContext, useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import type { CompositeContextProps } from './types';

export const CompositeContext =
createContext< CompositeContextProps >( undefined );

export const useCompositeContext = () => useContext( CompositeContext );
95 changes: 86 additions & 9 deletions packages/components/src/composite/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import * as Ariakit from '@ariakit/react';
/**
* WordPress dependencies
*/
import { forwardRef } from '@wordpress/element';
import { useMemo, forwardRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import type { WordPressComponentProps } from '../context';
import { CompositeContext, useCompositeContext } from './context';
import type {
CompositeStoreProps,
CompositeProps,
Expand Down Expand Up @@ -72,47 +73,89 @@ const Group = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeGroupProps, 'div', false >
>( function CompositeGroup( props, ref ) {
return <Ariakit.CompositeGroup { ...props } ref={ ref } />;
const context = useCompositeContext();
return (
<Ariakit.CompositeGroup
store={ context?.store }
{ ...props }
ref={ ref }
/>
);
} );
Group.displayName = 'Composite.Group';

const GroupLabel = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeGroupLabelProps, 'div', false >
>( function CompositeGroupLabel( props, ref ) {
return <Ariakit.CompositeGroupLabel { ...props } ref={ ref } />;
const context = useCompositeContext();
return (
<Ariakit.CompositeGroupLabel
store={ context?.store }
{ ...props }
ref={ ref }
/>
);
} );
GroupLabel.displayName = 'Composite.GroupLabel';

const Item = forwardRef<
HTMLButtonElement,
WordPressComponentProps< CompositeItemProps, 'button', false >
>( function CompositeItem( props, ref ) {
return <Ariakit.CompositeItem { ...props } ref={ ref } />;
const context = useCompositeContext();
return (
<Ariakit.CompositeItem
store={ context?.store }
{ ...props }
ref={ ref }
/>
);
} );
Item.displayName = 'Composite.Item';

const Row = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeRowProps, 'div', false >
>( function CompositeRow( props, ref ) {
return <Ariakit.CompositeRow { ...props } ref={ ref } />;
const context = useCompositeContext();
return (
<Ariakit.CompositeRow
store={ context?.store }
{ ...props }
ref={ ref }
/>
);
} );
Row.displayName = 'Composite.Row';

const Hover = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeHoverProps, 'div', false >
>( function CompositeHover( props, ref ) {
return <Ariakit.CompositeHover { ...props } ref={ ref } />;
const context = useCompositeContext();
return (
<Ariakit.CompositeHover
store={ context?.store }
{ ...props }
ref={ ref }
/>
);
} );
Hover.displayName = 'Composite.Hover';

const Typeahead = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeTypeaheadProps, 'div', false >
>( function CompositeTypeahead( props, ref ) {
return <Ariakit.CompositeTypeahead { ...props } ref={ ref } />;
const context = useCompositeContext();
return (
<Ariakit.CompositeTypeahead
store={ context?.store }
{ ...props }
ref={ ref }
/>
);
} );
Typeahead.displayName = 'Composite.Typeahead';

Expand All @@ -136,9 +179,28 @@ export const Composite = Object.assign(
forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeProps, 'div', false >
>( function Composite( { disabled = false, ...props }, ref ) {
>( function Composite(
{ children, store, disabled = false, ...props },
ref
) {
const contextValue = useMemo(
() => ( {
store,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's document this so folks know that if they want to access the store inside a Composite, they can do it with the context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added JSDocs and README section

} ),
[ store ]
);

return (
<Ariakit.Composite disabled={ disabled } { ...props } ref={ ref } />
<Ariakit.Composite
disabled={ disabled }
store={ store }
{ ...props }
ref={ ref }
>
<CompositeContext.Provider value={ contextValue }>
{ children }
</CompositeContext.Provider>
</Ariakit.Composite>
);
} ),
{
Expand Down Expand Up @@ -260,5 +322,20 @@ export const Composite = Object.assign(
* ```
*/
Typeahead,
/**
* The React context used by the composite components. It can be used by
* to access the composite store, and to forward the context when composite
* sub-components are rendered across portals (ie. `SlotFill` components)
* that would not otherwise forward the context to the `Fill` children.
*
* @example
* ```jsx
* import { Composite } from '@wordpress/components';
* import { useContext } from '@wordpress/element';
*
* const compositeContext = useContext( Composite.Context );
* ```
*/
Context: CompositeContext,
}
);
9 changes: 9 additions & 0 deletions packages/components/src/composite/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
*/
import type * as Ariakit from '@ariakit/react';

export type CompositeContextProps =
| {
/**
* Object returned by the `useCompositeStore` hook.
*/
store: Ariakit.CompositeStore;
}
| undefined;

export type CompositeStoreProps = {
/**
* The current active item `id`. The active item is the element within the
Expand Down
Loading