From 4a95e8cde83df31e3d45d85df2a72849824de1a3 Mon Sep 17 00:00:00 2001 From: Huub <50170696+huubl@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:34:19 +0200 Subject: [PATCH] Revert "Navigator: stabilize and export APIs (#64613)" This reverts commit cedb4d4b06e09d92b52b7fe7ddb7732dfc44496a. --- docs/manifest.json | 30 +++- packages/components/CHANGELOG.md | 1 - packages/components/src/index.ts | 1 - packages/components/src/navigator/README.md | 168 ------------------ packages/components/src/navigator/index.tsx | 131 -------------- packages/components/src/navigator/legacy.ts | 15 +- .../navigator/navigator-back-button/README.md | 15 ++ .../navigator-back-button/component.tsx | 2 +- .../navigator/navigator-back-button/hook.ts | 2 +- .../src/navigator/navigator-button/README.md | 38 ++++ .../navigator/navigator-button/component.tsx | 2 +- .../src/navigator/navigator-button/hook.ts | 2 +- .../navigator/navigator-provider/README.md | 98 ++++++++++ .../component.tsx | 44 ++++- .../src/navigator/navigator-screen/README.md | 33 ++++ .../navigator/navigator-screen/component.tsx | 6 +- .../navigator-to-parent-button/README.md | 17 ++ .../navigator-to-parent-button/component.tsx | 7 +- .../src/navigator/stories/index.story.tsx | 98 +++++----- packages/components/src/navigator/styles.ts | 2 +- .../components/src/navigator/test/index.tsx | 94 +++++----- packages/components/src/navigator/types.ts | 18 -- .../components/src/navigator/use-navigator.ts | 5 +- storybook/manager-head.html | 1 - 24 files changed, 385 insertions(+), 445 deletions(-) delete mode 100644 packages/components/src/navigator/README.md delete mode 100644 packages/components/src/navigator/index.tsx create mode 100644 packages/components/src/navigator/navigator-back-button/README.md create mode 100644 packages/components/src/navigator/navigator-button/README.md create mode 100644 packages/components/src/navigator/navigator-provider/README.md rename packages/components/src/navigator/{navigator => navigator-provider}/component.tsx (84%) create mode 100644 packages/components/src/navigator/navigator-screen/README.md create mode 100644 packages/components/src/navigator/navigator-to-parent-button/README.md diff --git a/docs/manifest.json b/docs/manifest.json index 8387b9079694c..d76717fbdedfc 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1098,9 +1098,33 @@ "parent": "components" }, { - "title": "Navigator", - "slug": "navigator", - "markdown_source": "../packages/components/src/navigator/README.md", + "title": "NavigatorBackButton", + "slug": "navigator-back-button", + "markdown_source": "../packages/components/src/navigator/navigator-back-button/README.md", + "parent": "components" + }, + { + "title": "NavigatorButton", + "slug": "navigator-button", + "markdown_source": "../packages/components/src/navigator/navigator-button/README.md", + "parent": "components" + }, + { + "title": "NavigatorProvider", + "slug": "navigator-provider", + "markdown_source": "../packages/components/src/navigator/navigator-provider/README.md", + "parent": "components" + }, + { + "title": "NavigatorScreen", + "slug": "navigator-screen", + "markdown_source": "../packages/components/src/navigator/navigator-screen/README.md", + "parent": "components" + }, + { + "title": "NavigatorToParentButton", + "slug": "navigator-to-parent-button", + "markdown_source": "../packages/components/src/navigator/navigator-to-parent-button/README.md", "parent": "components" }, { diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 8ef1c7c3bd200..d34b411833468 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -19,7 +19,6 @@ - `Navigator`: add support for exit animation ([#64777](https://github.com/WordPress/gutenberg/pull/64777)). - `Guide`: Update finish button to use the new default size ([#65680](https://github.com/WordPress/gutenberg/pull/65680)). - `BorderControl`: Use `__next40pxDefaultSize` prop for Reset button ([#65682](https://github.com/WordPress/gutenberg/pull/65682)). -- `Navigator`: stabilize APIs ([#64613](https://github.com/WordPress/gutenberg/pull/64613)). ## 28.8.0 (2024-09-19) diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index cc3c0265c4220..37ba116a7f679 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -132,7 +132,6 @@ export { NavigatorToParentButton as __experimentalNavigatorToParentButton, useNavigator as __experimentalUseNavigator, } from './navigator/legacy'; -export { Navigator, useNavigator } from './navigator'; export { default as Notice } from './notice'; export { default as __experimentalNumberControl } from './number-control'; export { default as NoticeList } from './notice/list'; diff --git a/packages/components/src/navigator/README.md b/packages/components/src/navigator/README.md deleted file mode 100644 index 00b1cfaeebe0f..0000000000000 --- a/packages/components/src/navigator/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# `Navigator` - -`Navigator` is a collection components that allow rendering nested views/panels/menus (via the `Navigator.Screen` component) and navigate between them (via the `Navigator.Button` and `Navigator.BackButton` components). - -## Usage - -```jsx -import { Navigator } from '@wordpress/components'; - -const MyNavigation = () => ( - - -

This is the home screen.

- - Navigate to child screen. - -
- -

This is the child screen.

- Go back -
-
-); -``` - -### Hierarchical `path`s - -`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. - -`Navigator` will treat "back" navigations as going to the parent screen — it is, therefore, the responsibility of the consumer of the component to create the correct screen hierarchy. - -For example: - -- `/` is the root of all paths. There should always be a screen with `path="/"`; -- `/parent/child` is a child of `/parent`; -- `/parent/child/grand-child` is a child of `/parent/child`; -- `/parent/:param` is a child of `/parent` as well; -- if the current screen has a `path="/parent/child/grand-child"`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. - -### Height and animations - -Due to how `Navigator.Screen` animations work, it is recommended that the `Navigator` component is assigned a `height` to prevent some potential UI jumps while moving across screens. - -### Individual components - -`Navigator` is comprised of four individual components: - -- `Navigator`: a wrapper component and context provider. It holds the main logic for hiding and showing screens. -- `Navigator.Screen`: represents a single view/screen/panel; -- `Navigator.Button`: renders a button that allows navigating to a different `Navigator.Screen`; -- `Navigator.BackButton`: renders a button that allows navigating to the parent `Navigator.Screen` (see the section above about hierarchical paths). - -For advanced usages, consumers can use the `useNavigator` hook. - -#### `Navigator` - -##### Props - -###### `initialPath`: `string` - -The initial active path. - -- Required: Yes - -###### `children`: `string` - -The children elements. - -- Required: Yes - -#### `Navigator.Screen` - -###### `path`: `string` - -The screen's path, matched against the current path stored in the navigator. - -`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. - -`Navigator` will treat "back" navigations as going to the parent screen — it is, therefore, the responsibility of the consumer of the component to create the correct screen hierarchy. - -For example: - -- `/` is the root of all paths. There should always be a screen with `path="/"`. -- `/parent/child` is a child of `/parent`. -- `/parent/child/grand-child` is a child of `/parent/child`. -- `/parent/:param` is a child of `/parent` as well. -- if the current screen has a `path` with value `/parent/child/grand-child`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. - -- Required: Yes - -###### `children`: `string` - -The children elements. - -- Required: Yes - -##### `Navigator.Button` - -###### `path`: `string` - -The path of the screen to navigate to. The value of this prop needs to be [a valid value for an HTML attribute](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2). - -- Required: Yes - -###### `attributeName`: `string` - -The HTML attribute used to identify the `Navigator.Button`, which is used by `Navigator` to restore focus. - -- Required: No -- Default: `id` - -###### `children`: `string` - -The children elements. - -- Required: No - -###### Inherited props - -`Navigator.Button` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. - -##### `Navigator.BackButton` - -###### `children`: `string` - -The children elements. - -- Required: No - -###### Inherited props - -`Navigator.BackButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. - -###### `useNavigator` - -You can retrieve a `navigator` instance by using the `useNavigator` hook. - -The `navigator` instance has a few properties: - -###### `goTo`: `( path: string, options: NavigateOptions ) => void` - -The `goTo` function allows navigating to a given path. The second argument can augment the navigation operations with different options. - -The available options are: - -- `focusTargetSelector`: `string`. An optional property used to specify the CSS selector used to restore focus on the matching element when navigating back; -- `isBack`: `boolean`. An optional property used to specify whether the navigation should be considered as backwards (thus enabling focus restoration when possible, and causing the animation to be backwards too); -- `skipFocus`: `boolean`. An optional property used to opt out of `Navigator`'s focus management, useful when the consumer of the component wants to manage focus themselves; - -###### `goBack`: `( path: string, options: NavigateOptions ) => void` - -The `goBack` function allows navigating to the parent screen. Parent/child navigation only works if the paths you define are hierarchical (see note above). - -When a match is not found, the function will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. - -The available options are the same as for the `goTo` method, except for the `isBack` property, which is not available for the `goBack` method. - -###### `location`: `NavigatorLocation` - -The `location` object represents the current location, and has a few properties: - -- `path`: `string`. The path associated to the location. -- `isBack`: `boolean`. A flag that is `true` when the current location was reached by navigating backwards. -- `isInitial`: `boolean`. A flag that is `true` only for the initial location. - -###### `params`: `Record< string, string | string[] >` - -The parsed record of parameters from the current location. For example if the current screen path is `/product/:productId` and the location is `/product/123`, then `params` will be `{ productId: '123' }`. diff --git a/packages/components/src/navigator/index.tsx b/packages/components/src/navigator/index.tsx deleted file mode 100644 index 1d9ae95441e01..0000000000000 --- a/packages/components/src/navigator/index.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Internal dependencies - */ -import { Navigator as TopLevelNavigator } from './navigator/component'; -import { NavigatorScreen } from './navigator-screen/component'; -import { NavigatorButton } from './navigator-button/component'; -import { NavigatorBackButton } from './navigator-back-button/component'; -export { useNavigator } from './use-navigator'; - -/** - * The `Navigator` component allows rendering nested views/panels/menus - * (via the `Navigator.Screen` component) and navigate between them - * (via the `Navigator.Button` and `Navigator.BackButton` components). - * - * ```jsx - * import { Navigator } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back - * - *
- *
- * ); - * ``` - */ -export const Navigator = Object.assign( TopLevelNavigator, { - /** - * The `Navigator.Screen` component represents a single view/screen/panel and - * should be used in combination with the `Navigator`, the `Navigator.Button` - * and the `Navigator.BackButton` components. - * - * @example - * ```jsx - * import { Navigator } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back - * - *
- *
- * ); - * ``` - */ - Screen: Object.assign( NavigatorScreen, { - displayName: 'Navigator.Screen', - } ), - /** - * The `Navigator.Button` component can be used to navigate to a screen and - * should be used in combination with the `Navigator`, the `Navigator.Screen` - * and the `Navigator.BackButton` components. - * - * @example - * ```jsx - * import { Navigator } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back - * - *
- *
- * ); - * ``` - */ - Button: Object.assign( NavigatorButton, { - displayName: 'Navigator.Button', - } ), - /** - * The `Navigator.BackButton` component can be used to navigate to a screen and - * should be used in combination with the `Navigator`, the `Navigator.Screen` - * and the `Navigator.Button` components. - * - * @example - * ```jsx - * import { Navigator } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back - * - *
- *
- * ); - * ``` - */ - BackButton: Object.assign( NavigatorBackButton, { - displayName: 'Navigator.BackButton', - } ), -} ); diff --git a/packages/components/src/navigator/legacy.ts b/packages/components/src/navigator/legacy.ts index 1caa5380fc049..54bc9e13829b1 100644 --- a/packages/components/src/navigator/legacy.ts +++ b/packages/components/src/navigator/legacy.ts @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { Navigator as InternalNavigator } from './navigator/component'; +import { Navigator as InternalNavigator } from './navigator-provider/component'; import { NavigatorScreen as InternalNavigatorScreen } from './navigator-screen/component'; import { NavigatorButton as InternalNavigatorButton } from './navigator-button/component'; import { NavigatorBackButton as InternalNavigatorBackButton } from './navigator-back-button/component'; @@ -10,8 +10,9 @@ export { useNavigator } from './use-navigator'; /** * The `NavigatorProvider` component allows rendering nested views/panels/menus - * (via the `NavigatorScreen` component and navigate between them - * (via the `NavigatorButton` and `NavigatorBackButton` components). + * (via the `NavigatorScreen` component and navigate between these different + * view (via the `NavigatorButton` and `NavigatorBackButton` components or the + * `useNavigator` hook). * * ```jsx * import { @@ -47,7 +48,8 @@ export const NavigatorProvider = Object.assign( InternalNavigator, { /** * The `NavigatorScreen` component represents a single view/screen/panel and * should be used in combination with the `NavigatorProvider`, the - * `NavigatorButton` and the `NavigatorBackButton` components. + * `NavigatorButton` and the `NavigatorBackButton` components (or the `useNavigator` + * hook). * * @example * ```jsx @@ -84,7 +86,7 @@ export const NavigatorScreen = Object.assign( InternalNavigatorScreen, { /** * The `NavigatorButton` component can be used to navigate to a screen and should * be used in combination with the `NavigatorProvider`, the `NavigatorScreen` - * and the `NavigatorBackButton` components. + * and the `NavigatorBackButton` components (or the `useNavigator` hook). * * @example * ```jsx @@ -121,7 +123,8 @@ export const NavigatorButton = Object.assign( InternalNavigatorButton, { /** * The `NavigatorBackButton` component can be used to navigate to a screen and * should be used in combination with the `NavigatorProvider`, the - * `NavigatorScreen` and the `NavigatorButton` components. + * `NavigatorScreen` and the `NavigatorButton` components (or the `useNavigator` + * hook). * * @example * ```jsx diff --git a/packages/components/src/navigator/navigator-back-button/README.md b/packages/components/src/navigator/navigator-back-button/README.md new file mode 100644 index 0000000000000..01d4221be536e --- /dev/null +++ b/packages/components/src/navigator/navigator-back-button/README.md @@ -0,0 +1,15 @@ +# `NavigatorBackButton` + +
+This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. +
+ +The `NavigatorBackButton` component can be used to navigate to a screen and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorScreen`](/packages/components/src/navigator/navigator-screen/README.md) and the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md) components (or the `useNavigator` hook). + +## Usage + +Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. + +### Inherited props + +`NavigatorBackButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. diff --git a/packages/components/src/navigator/navigator-back-button/component.tsx b/packages/components/src/navigator/navigator-back-button/component.tsx index b5c4de7df78a8..9e8d98a963d25 100644 --- a/packages/components/src/navigator/navigator-back-button/component.tsx +++ b/packages/components/src/navigator/navigator-back-button/component.tsx @@ -23,5 +23,5 @@ function UnconnectedNavigatorBackButton( export const NavigatorBackButton = contextConnect( UnconnectedNavigatorBackButton, - 'Navigator.BackButton' + 'NavigatorBackButton' ); diff --git a/packages/components/src/navigator/navigator-back-button/hook.ts b/packages/components/src/navigator/navigator-back-button/hook.ts index d6fcd39647bff..9ddc095292190 100644 --- a/packages/components/src/navigator/navigator-back-button/hook.ts +++ b/packages/components/src/navigator/navigator-back-button/hook.ts @@ -20,7 +20,7 @@ export function useNavigatorBackButton( as = Button, ...otherProps - } = useContextSystem( props, 'Navigator.BackButton' ); + } = useContextSystem( props, 'NavigatorBackButton' ); const { goBack } = useNavigator(); const handleClick: React.MouseEventHandler< HTMLButtonElement > = diff --git a/packages/components/src/navigator/navigator-button/README.md b/packages/components/src/navigator/navigator-button/README.md new file mode 100644 index 0000000000000..72154ec317da4 --- /dev/null +++ b/packages/components/src/navigator/navigator-button/README.md @@ -0,0 +1,38 @@ +# `NavigatorButton` + +
+This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. +
+ +The `NavigatorButton` component can be used to navigate to a screen and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorScreen`](/packages/components/src/navigator/navigator-screen/README.md) and the [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) components (or the `useNavigator` hook). + +## Usage + +Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. + +## Props + +The component accepts the following props: + +### `attributeName`: `string` + +The HTML attribute used to identify the `NavigatorButton`, which is used by `Navigator` to restore focus. + +- Required: No +- Default: `id` + +### `onClick`: `React.MouseEventHandler< HTMLElement >` + +The callback called in response to a `click` event. + +- Required: No + +### `path`: `string` + +The path of the screen to navigate to. The value of this prop needs to be [a valid value for an HTML attribute](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2). + +- Required: Yes + +### Inherited props + +`NavigatorButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. diff --git a/packages/components/src/navigator/navigator-button/component.tsx b/packages/components/src/navigator/navigator-button/component.tsx index a6dc796337672..7c14c8108e82c 100644 --- a/packages/components/src/navigator/navigator-button/component.tsx +++ b/packages/components/src/navigator/navigator-button/component.tsx @@ -23,5 +23,5 @@ function UnconnectedNavigatorButton( export const NavigatorButton = contextConnect( UnconnectedNavigatorButton, - 'Navigator.Button' + 'NavigatorButton' ); diff --git a/packages/components/src/navigator/navigator-button/hook.ts b/packages/components/src/navigator/navigator-button/hook.ts index 59d2aaa65662d..3e39b05661e1b 100644 --- a/packages/components/src/navigator/navigator-button/hook.ts +++ b/packages/components/src/navigator/navigator-button/hook.ts @@ -25,7 +25,7 @@ export function useNavigatorButton( as = Button, attributeName = 'id', ...otherProps - } = useContextSystem( props, 'Navigator.Button' ); + } = useContextSystem( props, 'NavigatorButton' ); const escapedPath = escapeAttribute( path ); diff --git a/packages/components/src/navigator/navigator-provider/README.md b/packages/components/src/navigator/navigator-provider/README.md new file mode 100644 index 0000000000000..b9bc8f0c6bcdc --- /dev/null +++ b/packages/components/src/navigator/navigator-provider/README.md @@ -0,0 +1,98 @@ +# `NavigatorProvider` + +
+This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. +
+ +The `NavigatorProvider` component allows rendering nested views/panels/menus (via the [`NavigatorScreen` component](/packages/components/src/navigator/navigator-screen/README.md)) and navigate between these different states (via the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md), [`NavigatorToParentButton`](/packages/components/src/navigator/navigator-to-parent-button/README.md) and [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) components or the `useNavigator` hook). The Global Styles sidebar is an example of this. + +## Usage + +```jsx +import { + __experimentalNavigatorProvider as NavigatorProvider, + __experimentalNavigatorScreen as NavigatorScreen, + __experimentalNavigatorButton as NavigatorButton, + __experimentalNavigatorBackButton as NavigatorBackButton, +} from '@wordpress/components'; + +const MyNavigation = () => ( + + +

This is the home screen.

+ + Navigate to child screen. + +
+ + +

This is the child screen.

+ Go back +
+
+); +``` + +### Hierarchical `path`s + +`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. + +`Navigator` will treat "back" navigations as going to the parent screen — it is therefore responsibility of the consumer of the component to create the correct screen hierarchy. + +For example: + +- `/` is the root of all paths. There should always be a screen with `path="/"`. +- `/parent/child` is a child of `/parent`. +- `/parent/child/grand-child` is a child of `/parent/child`. +- `/parent/:param` is a child of `/parent` as well. +- if the current screen has a `path` with value `/parent/child/grand-child`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. + +### Height and animations + +Due to how `NavigatorScreen` animations work, it is recommended that the `NavigatorProvider` component is assigned a `height` to prevent some potential UI jumps while moving across screens. + +## Props + +The component accepts the following props: + +### `initialPath`: `string` + +The initial active path. + +- Required: No + +## The `navigator` object + +You can retrieve a `navigator` instance by using the `useNavigator` hook. + +The `navigator` instance has a few properties: + +### `goTo`: `( path: string, options: NavigateOptions ) => void` + +The `goTo` function allows navigating to a given path. The second argument can augment the navigation operations with different options. + +The available options are: + +- `focusTargetSelector`: `string`. An optional property used to specify the CSS selector used to restore focus on the matching element when navigating back; +- `isBack`: `boolean`. An optional property used to specify whether the navigation should be considered as backwards (thus enabling focus restoration when possible, and causing the animation to be backwards too); +- `skipFocus`: `boolean`. An optional property used to opt out of `Navigator`'s focus management, useful when the consumer of the component wants to manage focus themselves; + +### `goBack`: `( path: string, options: NavigateOptions ) => void` + +The `goBack` function allows navigating to the parent screen. Parent/child navigation only works if the paths you define are hierarchical (see note above). + +When a match is not found, the function will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) are found. + +The available options are the same as for the `goTo` method, except for the `isBack` property, which is not available for the `goBack` method. + +### `location`: `NavigatorLocation` + +The `location` object represent the current location, and has a few properties: + +- `path`: `string`. The path associated to the location. +- `isBack`: `boolean`. A flag that is `true` when the current location was reached by navigating backwards. +- `isInitial`: `boolean`. A flag that is `true` only for the initial location. + +### `params`: `Record< string, string | string[] >` + +The parsed record of parameters from the current location. For example if the current screen path is `/product/:productId` and the location is `/product/123`, then `params` will be `{ productId: '123' }`. diff --git a/packages/components/src/navigator/navigator/component.tsx b/packages/components/src/navigator/navigator-provider/component.tsx similarity index 84% rename from packages/components/src/navigator/navigator/component.tsx rename to packages/components/src/navigator/navigator-provider/component.tsx index bd49b3682fb14..604034a952557 100644 --- a/packages/components/src/navigator/navigator/component.tsx +++ b/packages/components/src/navigator/navigator-provider/component.tsx @@ -222,7 +222,7 @@ function UnconnectedNavigator( children, className, ...otherProps - } = useContextSystem( props, 'Navigator' ); + } = useContextSystem( props, 'NavigatorProvider' ); const [ routerState, dispatch ] = useReducer( routerReducer, @@ -275,7 +275,7 @@ function UnconnectedNavigator( const cx = useCx(); const classes = useMemo( - () => cx( styles.navigatorWrapper, className ), + () => cx( styles.navigatorProviderWrapper, className ), [ className, cx ] ); @@ -288,4 +288,42 @@ function UnconnectedNavigator( ); } -export const Navigator = contextConnect( UnconnectedNavigator, 'Navigator' ); +/** + * The `NavigatorProvider` component allows rendering nested views/panels/menus + * (via the `NavigatorScreen` component and navigate between these different + * view (via the `NavigatorButton` and `NavigatorBackButton` components or the + * `useNavigator` hook). + * + * ```jsx + * import { + * __experimentalNavigatorProvider as NavigatorProvider, + * __experimentalNavigatorScreen as NavigatorScreen, + * __experimentalNavigatorButton as NavigatorButton, + * __experimentalNavigatorBackButton as NavigatorBackButton, + * } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ +export const Navigator = contextConnect( + UnconnectedNavigator, + 'NavigatorProvider' +); + +export default Navigator; diff --git a/packages/components/src/navigator/navigator-screen/README.md b/packages/components/src/navigator/navigator-screen/README.md new file mode 100644 index 0000000000000..583da461cd3c2 --- /dev/null +++ b/packages/components/src/navigator/navigator-screen/README.md @@ -0,0 +1,33 @@ +# `NavigatorScreen` + +
+This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. +
+ +The `NavigatorScreen` component represents a single view/screen/panel and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md) and the [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) components (or the `useNavigator` hook). + +## Usage + +Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. + +## Props + +The component accepts the following props: + +### `path`: `string` + +The screen"s path, matched against the current path stored in the navigator. + +`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. + +`Navigator` will treat "back" navigations as going to the parent screen — it is therefore responsibility of the consumer of the component to create the correct screen hierarchy. + +For example: + +- `/` is the root of all paths. There should always be a screen with `path="/"`. +- `/parent/child` is a child of `/parent`. +- `/parent/child/grand-child` is a child of `/parent/child`. +- `/parent/:param` is a child of `/parent` as well. +- if the current screen has a `path` with value `/parent/child/grand-child`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. + +- Required: Yes diff --git a/packages/components/src/navigator/navigator-screen/component.tsx b/packages/components/src/navigator/navigator-screen/component.tsx index fe0d81b90a17b..16cc0df24e35d 100644 --- a/packages/components/src/navigator/navigator-screen/component.tsx +++ b/packages/components/src/navigator/navigator-screen/component.tsx @@ -36,7 +36,7 @@ function UnconnectedNavigatorScreen( ) { if ( ! /^\//.test( props.path ) ) { warning( - 'wp.components.Navigator.Screen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' + 'wp.components.NavigatorScreen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' ); } @@ -48,7 +48,7 @@ function UnconnectedNavigatorScreen( path, onAnimationEnd: onAnimationEndProp, ...otherProps - } = useContextSystem( props, 'Navigator.Screen' ); + } = useContextSystem( props, 'NavigatorScreen' ); const { location, match, addScreen, removeScreen } = useContext( NavigatorContext ); @@ -155,5 +155,5 @@ function UnconnectedNavigatorScreen( export const NavigatorScreen = contextConnect( UnconnectedNavigatorScreen, - 'Navigator.Screen' + 'NavigatorScreen' ); diff --git a/packages/components/src/navigator/navigator-to-parent-button/README.md b/packages/components/src/navigator/navigator-to-parent-button/README.md new file mode 100644 index 0000000000000..0100ea9b8d2e1 --- /dev/null +++ b/packages/components/src/navigator/navigator-to-parent-button/README.md @@ -0,0 +1,17 @@ +# `NavigatorToParentButton` + +
+This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. +
+ +This component is deprecated. Please use the [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) component instead. + +The `NavigatorToParentButton` component can be used to navigate to a screen and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorScreen`](/packages/components/src/navigator/navigator-screen/README.md) and the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md) components (or the `useNavigator` hook). + +## Usage + +Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. + +### Inherited props + +`NavigatorToParentButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. diff --git a/packages/components/src/navigator/navigator-to-parent-button/component.tsx b/packages/components/src/navigator/navigator-to-parent-button/component.tsx index f1c2d27e2284a..161370a4323f4 100644 --- a/packages/components/src/navigator/navigator-to-parent-button/component.tsx +++ b/packages/components/src/navigator/navigator-to-parent-button/component.tsx @@ -17,16 +17,13 @@ function UnconnectedNavigatorToParentButton( ) { deprecated( 'wp.components.NavigatorToParentButton', { since: '6.7', - alternative: 'wp.components.Navigator.BackButton', + alternative: 'wp.components.NavigatorBackButton', } ); return ; } -/** - * @deprecated - */ export const NavigatorToParentButton = contextConnect( UnconnectedNavigatorToParentButton, - 'Navigator.ToParentButton' + 'NavigatorToParentButton' ); diff --git a/packages/components/src/navigator/stories/index.story.tsx b/packages/components/src/navigator/stories/index.story.tsx index e9e342bb0d2ee..f703969320ca5 100644 --- a/packages/components/src/navigator/stories/index.story.tsx +++ b/packages/components/src/navigator/stories/index.story.tsx @@ -8,20 +8,20 @@ import type { Meta, StoryObj } from '@storybook/react'; */ import Button from '../../button'; import { VStack } from '../../v-stack'; +import { + NavigatorProvider, + NavigatorScreen, + NavigatorButton, + NavigatorBackButton, + useNavigator, +} from '../legacy'; import { HStack } from '../../h-stack'; -import { Navigator, useNavigator } from '../'; - -const meta: Meta< typeof Navigator > = { - component: Navigator, - subcomponents: { - // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 - Screen: Navigator.Screen, - // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 - Button: Navigator.Button, - // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 - BackButton: Navigator.BackButton, - }, - title: 'Components/Navigator', + +const meta: Meta< typeof NavigatorProvider > = { + component: NavigatorProvider, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + subcomponents: { NavigatorScreen, NavigatorButton, NavigatorBackButton }, + title: 'Components (Experimental)/Navigator', argTypes: { as: { control: { type: null } }, children: { control: { type: null } }, @@ -40,10 +40,10 @@ const meta: Meta< typeof Navigator > = { * detail of the Navigator component. Do not use outside of * its source code. */ - [data-wp-component="Navigator"] { + [data-wp-component="NavigatorProvider"] { height: 250px; } - [data-wp-component="Navigator.Screen"] { + [data-wp-component="NavigatorScreen"]:not([data-sticky]) { padding: 8px; } ` } @@ -55,55 +55,55 @@ const meta: Meta< typeof Navigator > = { }; export default meta; -export const Default: StoryObj< typeof Navigator > = { +export const Default: StoryObj< typeof NavigatorProvider > = { args: { initialPath: '/', children: ( <> - +

This is the home screen.

- + Go to child screen. - + - + Go to dynamic path screen with id 1. - + - + Go to dynamic path screen with id 2. - + -
+ - +

This is the child screen.

- + Go back - +
- Go to grand child screen. - + - + - +

This is the grand child screen.

- + Go back - -
+ + - + - + ), }, @@ -119,14 +119,14 @@ function DynamicScreen() { This screen can parse params dynamically. The current id is:{ ' ' } { params.id }

- + Go back - + ); } -export const WithNestedInitialPath: StoryObj< typeof Navigator > = { +export const WithNestedInitialPath: StoryObj< typeof NavigatorProvider > = { ...Default, args: { ...Default.args, @@ -138,7 +138,7 @@ const NavigatorButtonWithSkipFocus = ( { path, onClick, ...props -}: React.ComponentProps< typeof Navigator.Button > ) => { +}: React.ComponentProps< typeof NavigatorButton > ) => { const { goTo } = useNavigator(); return ( @@ -156,7 +156,7 @@ const NavigatorButtonWithSkipFocus = ( { ); }; -export const SkipFocus: StoryObj< typeof Navigator > = { +export const SkipFocus: StoryObj< typeof NavigatorProvider > = { args: { initialPath: '/', children: ( @@ -170,19 +170,19 @@ export const SkipFocus: StoryObj< typeof Navigator > = { display: 'contents', } } > - +

Home screen

- + Go to child screen. - -
+ + - +

Child screen

- + Go back to home screen - -
+ + diff --git a/packages/components/src/navigator/styles.ts b/packages/components/src/navigator/styles.ts index 167d4ac07de3d..fd355bf4dd48f 100644 --- a/packages/components/src/navigator/styles.ts +++ b/packages/components/src/navigator/styles.ts @@ -3,7 +3,7 @@ */ import { css, keyframes } from '@emotion/react'; -export const navigatorWrapper = css` +export const navigatorProviderWrapper = css` position: relative; /* Prevents horizontal overflow while animating screen transitions */ overflow-x: clip; diff --git a/packages/components/src/navigator/test/index.tsx b/packages/components/src/navigator/test/index.tsx index cab6e9a4cdadf..81ffd8d69208d 100644 --- a/packages/components/src/navigator/test/index.tsx +++ b/packages/components/src/navigator/test/index.tsx @@ -14,8 +14,14 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import Button from '../../button'; -import { Navigator, useNavigator } from '..'; -import { NavigatorToParentButton } from '../legacy'; +import { + NavigatorProvider, + NavigatorScreen, + NavigatorButton, + NavigatorBackButton, + NavigatorToParentButton, + useNavigator, +} from '../legacy'; import type { NavigateOptions } from '../types'; const INVALID_HTML_ATTRIBUTE = { @@ -70,11 +76,11 @@ function CustomNavigatorButton( { path, onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof Navigator.Button >, 'onClick' > & { +}: Omit< ComponentPropsWithoutRef< typeof NavigatorButton >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { return ( - { // Used to spy on the values passed to `navigator.goTo`. onClick?.( { type: 'goTo', path } ); @@ -89,7 +95,7 @@ function CustomNavigatorGoToBackButton( { path, onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof Navigator.Button >, 'onClick' > & { +}: Omit< ComponentPropsWithoutRef< typeof NavigatorButton >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { const { goTo } = useNavigator(); @@ -109,7 +115,7 @@ function CustomNavigatorGoToSkipFocusButton( { path, onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof Navigator.Button >, 'onClick' > & { +}: Omit< ComponentPropsWithoutRef< typeof NavigatorButton >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { const { goTo } = useNavigator(); @@ -128,14 +134,11 @@ function CustomNavigatorGoToSkipFocusButton( { function CustomNavigatorBackButton( { onClick, ...props -}: Omit< - ComponentPropsWithoutRef< typeof Navigator.BackButton >, - 'onClick' -> & { +}: Omit< ComponentPropsWithoutRef< typeof NavigatorBackButton >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { return ( - { // Used to spy on the values passed to `navigator.goBack`. onClick?.( { type: 'goBack' } ); @@ -148,10 +151,7 @@ function CustomNavigatorBackButton( { function CustomNavigatorToParentButton( { onClick, ...props -}: Omit< - ComponentPropsWithoutRef< typeof Navigator.BackButton >, - 'onClick' -> & { +}: Omit< ComponentPropsWithoutRef< typeof NavigatorBackButton >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { return ( @@ -194,13 +194,13 @@ const ProductScreen = ( { const { params } = useNavigator(); return ( - +

{ SCREEN_TEXT.product }

Product ID is { params.productId }

{ BUTTON_TEXT.back } -
+ ); }; @@ -215,8 +215,8 @@ const MyNavigation = ( { const [ outerInputValue, setOuterInputValue ] = useState( '' ); return ( <> - - + +

{ SCREEN_TEXT.home }

{ /* * A button useful to test focus restoration. This button is the first @@ -254,9 +254,9 @@ const MyNavigation = ( { > { BUTTON_TEXT.toInvalidHtmlPathScreen } -
+ - +

{ SCREEN_TEXT.child }

{ /* * A button useful to test focus restoration. This button is the first @@ -286,30 +286,30 @@ const MyNavigation = ( { } } value={ innerInputValue } /> -
+ - +

{ SCREEN_TEXT.nested }

{ BUTTON_TEXT.back } -
+ - +

{ SCREEN_TEXT.invalidHtmlPath }

{ BUTTON_TEXT.back } -
+ - { /* A `Navigator.Screen` with `path={ PATHS.NOT_FOUND }` is purposefully not included. */ } -
+ { /* A `NavigatorScreen` with `path={ PATHS.NOT_FOUND }` is purposefully not included. */ } + { return ( <> - - + +

{ SCREEN_TEXT.home }

{ /* * A button useful to test focus restoration. This button is the first @@ -349,9 +349,9 @@ const MyHierarchicalNavigation = ( { > { BUTTON_TEXT.toChildScreen } -
+ - +

{ SCREEN_TEXT.child }

{ /* * A button useful to test focus restoration. This button is the first @@ -370,9 +370,9 @@ const MyHierarchicalNavigation = ( { > { BUTTON_TEXT.back } -
+ - +

{ SCREEN_TEXT.nested }

{ BUTTON_TEXT.backUsingGoTo } -
+ { BUTTON_TEXT.goToWithSkipFocus } -
+ ); }; @@ -406,8 +406,8 @@ const MyDeprecatedNavigation = ( { } ) => { return ( <> - - + +

{ SCREEN_TEXT.home }

{ /* * A button useful to test focus restoration. This button is the first @@ -421,9 +421,9 @@ const MyDeprecatedNavigation = ( { > { BUTTON_TEXT.toChildScreen } -
+ - +

{ SCREEN_TEXT.child }

{ /* * A button useful to test focus restoration. This button is the first @@ -442,17 +442,17 @@ const MyDeprecatedNavigation = ( { > { BUTTON_TEXT.back } -
+ - +

{ SCREEN_TEXT.nested }

{ BUTTON_TEXT.back } -
-
+ + ); }; @@ -643,10 +643,10 @@ describe( 'Navigator', () => { } ); it( 'should warn if the `path` prop does not follow the required format', () => { - render( Test ); + render( Test ); expect( console ).toHaveWarnedWith( - 'wp.components.Navigator.Screen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' + 'wp.components.NavigatorScreen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' ); } ); @@ -880,7 +880,7 @@ describe( 'Navigator', () => { // Rendering `NavigatorToParentButton` logs a deprecation notice expect( console ).toHaveWarnedWith( - 'wp.components.NavigatorToParentButton is deprecated since version 6.7. Please use wp.components.Navigator.BackButton instead.' + 'wp.components.NavigatorToParentButton is deprecated since version 6.7. Please use wp.components.NavigatorBackButton instead.' ); } ); diff --git a/packages/components/src/navigator/types.ts b/packages/components/src/navigator/types.ts index aeb5fd3b12c7f..ad26d3f1c9f49 100644 --- a/packages/components/src/navigator/types.ts +++ b/packages/components/src/navigator/types.ts @@ -100,24 +100,6 @@ export type NavigatorProps = { export type NavigatorScreenProps = { /** * The screen's path, matched against the current path stored in the navigator. - * - * `Navigator` assumes that screens are organized hierarchically according - * to their `path`, which should follow a URL-like scheme where each path - * segment starts with and is separated by the `/` character. - * - * `Navigator` will treat "back" navigations as going to the parent screen — - * it is, therefore, the responsibility of the consumer of the component to - * create the correct screen hierarchy. - * - * For example: - * - `/` is the root of all paths. There should always be a screen with - * `path="/"`; - * - `/parent/child` is a child of `/parent`; - * - `/parent/child/grand-child` is a child of `/parent/child`; - * - `/parent/:param` is a child of `/parent` as well; - * - if the current screen has a `path="/parent/child/grand-child"`, when - * going "back" `Navigator` will try to recursively navigate the path - * hierarchy until a matching screen (or the root `/`) is found. */ path: string; /** diff --git a/packages/components/src/navigator/use-navigator.ts b/packages/components/src/navigator/use-navigator.ts index 1ea99f3f1c857..7ac35d73150d3 100644 --- a/packages/components/src/navigator/use-navigator.ts +++ b/packages/components/src/navigator/use-navigator.ts @@ -10,10 +10,7 @@ import { NavigatorContext } from './context'; import type { Navigator } from './types'; /** - * Retrieves a `navigator` instance. This hook provides advanced functionality, - * such as imperatively navigating to a new location (with options like - * navigating back or skipping focus restoration) and accessing the current - * location and path parameters. + * Retrieves a `navigator` instance. */ export function useNavigator(): Navigator { const { location, params, goTo, goBack, goToParent } = diff --git a/storybook/manager-head.html b/storybook/manager-head.html index 8525e48fffa58..f3eaef1350921 100644 --- a/storybook/manager-head.html +++ b/storybook/manager-head.html @@ -7,7 +7,6 @@ 'customselectcontrol-v2', 'dimensioncontrol', 'navigation', - 'navigator', 'progressbar', 'theme', ];