Skip to content

Commit

Permalink
Navigator: stabilize and export APIs (WordPress#64613)
Browse files Browse the repository at this point in the history
* Remove experimental status from Storybook

* NavigatorProvider => Navigator (internally)

* Move legacy exports to separate file

* Export overloaded notation

* Use overloaded notation in Storybook

* Use overloaded notation in unit tests (skipping deprecated component for now)

* Update docs manifest.json

* Navigator: update docs

* Navigator.BackButton: update docs

* NavigatorToParentButton: delete README

* Navigator.Button: update docs

* NavigatorScreen: update docs

* Restore `NavigatorToParentButton` test

* Use dot notation in context namespaces too

* Use named export for `NavigatorToParentButton` too

* Fixup docs manifest

* Export stable version from components package

* CHANGELOG

* Change index.ts extension to tsx to allow Storybook to extract JSDocs better, remove duplicate JSDocs

* Remove unnecessary and mispelled display name for top-level `Navigator`

* Remove @example tag from top-level export

* Clean up unused imports

* Fix storybook styles

* Update new section of the docs

* Update more docs

* Update deprecation warning

* Update and align docs

* Fix tests warning checks

* Typo

* Fix useContextSystem names

* Remove mentions of the `useNavigator` hook

* useNavigator JSDocs

* Move all READMEs under the main README

* Move README to component's root folder

* Update types JSDocs to match README

* Update docs manifest

* Fix JSDocs typos, grammar, and broken links.

* Update storybook selector

---

Co-authored-by: ciampo <[email protected]>
Co-authored-by: mirka <[email protected]>
Co-authored-by: tyxla <[email protected]>
  • Loading branch information
4 people authored and huubl committed Oct 2, 2024
1 parent 4f97ebb commit cedb4d4
Show file tree
Hide file tree
Showing 24 changed files with 445 additions and 385 deletions.
30 changes: 3 additions & 27 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1098,33 +1098,9 @@
"parent": "components"
},
{
"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",
"title": "Navigator",
"slug": "navigator",
"markdown_source": "../packages/components/src/navigator/README.md",
"parent": "components"
},
{
Expand Down
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- `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)

Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ 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';
Expand Down
168 changes: 168 additions & 0 deletions packages/components/src/navigator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# `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 = () => (
<Navigator initialPath="/">
<Navigator.Screen path="/">
<p>This is the home screen.</p>
<Navigator.Button path="/child">
Navigate to child screen.
</Navigator.Button>
</Navigator.Screen>
<Navigator.Screen path="/child">
<p>This is the child screen.</p>
<Navigator.BackButton>Go back</Navigator.BackButton>
</Navigator.Screen>
</Navigator>
);
```

### 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' }`.
131 changes: 131 additions & 0 deletions packages/components/src/navigator/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* 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 = () => (
* <Navigator initialPath="/">
* <Navigator.Screen path="/">
* <p>This is the home screen.</p>
* <Navigator.Button path="/child">
* Navigate to child screen.
* </Navigator.Button>
* </Navigator.Screen>
*
* <Navigator.Screen path="/child">
* <p>This is the child screen.</p>
* <Navigator.BackButton>
* Go back
* </Navigator.BackButton>
* </Navigator.Screen>
* </Navigator>
* );
* ```
*/
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 = () => (
* <Navigator initialPath="/">
* <Navigator.Screen path="/">
* <p>This is the home screen.</p>
* <Navigator.Button path="/child">
* Navigate to child screen.
* </Navigator.Button>
* </Navigator.Screen>
*
* <Navigator.Screen path="/child">
* <p>This is the child screen.</p>
* <Navigator.BackButton>
* Go back
* </Navigator.BackButton>
* </Navigator.Screen>
* </Navigator>
* );
* ```
*/
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 = () => (
* <Navigator initialPath="/">
* <Navigator.Screen path="/">
* <p>This is the home screen.</p>
* <Navigator.Button path="/child">
* Navigate to child screen.
* </Navigator.Button>
* </Navigator.Screen>
*
* <Navigator.Screen path="/child">
* <p>This is the child screen.</p>
* <Navigator.BackButton>
* Go back
* </Navigator.BackButton>
* </Navigator.Screen>
* </Navigator>
* );
* ```
*/
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 = () => (
* <Navigator initialPath="/">
* <Navigator.Screen path="/">
* <p>This is the home screen.</p>
* <Navigator.Button path="/child">
* Navigate to child screen.
* </Navigator.Button>
* </Navigator.Screen>
*
* <Navigator.Screen path="/child">
* <p>This is the child screen.</p>
* <Navigator.BackButton>
* Go back
* </Navigator.BackButton>
* </Navigator.Screen>
* </Navigator>
* );
* ```
*/
BackButton: Object.assign( NavigatorBackButton, {
displayName: 'Navigator.BackButton',
} ),
} );
Loading

0 comments on commit cedb4d4

Please sign in to comment.