From 7375bee6d0f06da5a739ec0153af6da2785a304c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 29 Sep 2021 12:55:38 +0200 Subject: [PATCH 1/6] Navigator: refactor to typescript --- packages/components/src/navigator/context.js | 6 ---- packages/components/src/navigator/context.ts | 13 +++++++ .../src/navigator/{index.js => index.ts} | 0 .../{component.js => component.tsx} | 12 +++++-- .../navigator-provider/{index.js => index.ts} | 0 .../{component.js => component.tsx} | 5 ++- .../navigator-screen/{index.js => index.ts} | 0 packages/components/src/navigator/types.ts | 34 +++++++++++++++++++ .../{use-navigator.js => use-navigator.ts} | 3 +- packages/components/tsconfig.json | 1 + 10 files changed, 64 insertions(+), 10 deletions(-) delete mode 100644 packages/components/src/navigator/context.js create mode 100644 packages/components/src/navigator/context.ts rename packages/components/src/navigator/{index.js => index.ts} (100%) rename packages/components/src/navigator/navigator-provider/{component.js => component.tsx} (54%) rename packages/components/src/navigator/navigator-provider/{index.js => index.ts} (100%) rename packages/components/src/navigator/navigator-screen/{component.js => component.tsx} (92%) rename packages/components/src/navigator/navigator-screen/{index.js => index.ts} (100%) create mode 100644 packages/components/src/navigator/types.ts rename packages/components/src/navigator/{use-navigator.js => use-navigator.ts} (80%) diff --git a/packages/components/src/navigator/context.js b/packages/components/src/navigator/context.js deleted file mode 100644 index 2113ae49082865..00000000000000 --- a/packages/components/src/navigator/context.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * WordPress dependencies - */ -import { createContext } from '@wordpress/element'; - -export const NavigatorContext = createContext(); diff --git a/packages/components/src/navigator/context.ts b/packages/components/src/navigator/context.ts new file mode 100644 index 00000000000000..096e91f3869638 --- /dev/null +++ b/packages/components/src/navigator/context.ts @@ -0,0 +1,13 @@ +/** + * WordPress dependencies + */ +import { createContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import type { NavigatorContext as NavigatorContextType } from './types'; + +// Should it have more opinionated defaults? +const initialContextValue: NavigatorContextType = [ {}, () => {} ]; +export const NavigatorContext = createContext( initialContextValue ); diff --git a/packages/components/src/navigator/index.js b/packages/components/src/navigator/index.ts similarity index 100% rename from packages/components/src/navigator/index.js rename to packages/components/src/navigator/index.ts diff --git a/packages/components/src/navigator/navigator-provider/component.js b/packages/components/src/navigator/navigator-provider/component.tsx similarity index 54% rename from packages/components/src/navigator/navigator-provider/component.js rename to packages/components/src/navigator/navigator-provider/component.tsx index a49553ac5377c4..74221285bbf292 100644 --- a/packages/components/src/navigator/navigator-provider/component.js +++ b/packages/components/src/navigator/navigator-provider/component.tsx @@ -7,9 +7,15 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import { NavigatorContext } from '../context'; +import type { NavigatorProviderProps, NavigatorPath } from '../types'; -function NavigatorProvider( { initialPath, children } ) { - const [ path, setPath ] = useState( { path: initialPath } ); +function NavigatorProvider( { + initialPath, + children, +}: NavigatorProviderProps ) { + const [ path, setPath ] = useState< NavigatorPath >( { + path: initialPath, + } ); return ( @@ -18,4 +24,6 @@ function NavigatorProvider( { initialPath, children } ) { ); } +// TODO: context connect + export default NavigatorProvider; diff --git a/packages/components/src/navigator/navigator-provider/index.js b/packages/components/src/navigator/navigator-provider/index.ts similarity index 100% rename from packages/components/src/navigator/navigator-provider/index.js rename to packages/components/src/navigator/navigator-provider/index.ts diff --git a/packages/components/src/navigator/navigator-screen/component.js b/packages/components/src/navigator/navigator-screen/component.tsx similarity index 92% rename from packages/components/src/navigator/navigator-screen/component.js rename to packages/components/src/navigator/navigator-screen/component.tsx index 3a5e33605a0b3e..8d36706a24308b 100644 --- a/packages/components/src/navigator/navigator-screen/component.js +++ b/packages/components/src/navigator/navigator-screen/component.tsx @@ -15,13 +15,14 @@ import { isRTL } from '@wordpress/i18n'; * Internal dependencies */ import { NavigatorContext } from '../context'; +import type { NavigatorScreenProps } from '../types'; const animationEnterDelay = 0; const animationEnterDuration = 0.14; const animationExitDuration = 0.14; const animationExitDelay = 0; -function NavigatorScreen( { children, path } ) { +function NavigatorScreen( { children, path }: NavigatorScreenProps ) { const prefersReducedMotion = useReducedMotion(); const [ currentPath ] = useContext( NavigatorContext ); const isMatch = currentPath.path === path; @@ -89,4 +90,6 @@ function NavigatorScreen( { children, path } ) { ); } +// TODO: context connect + export default NavigatorScreen; diff --git a/packages/components/src/navigator/navigator-screen/index.js b/packages/components/src/navigator/navigator-screen/index.ts similarity index 100% rename from packages/components/src/navigator/navigator-screen/index.js rename to packages/components/src/navigator/navigator-screen/index.ts diff --git a/packages/components/src/navigator/types.ts b/packages/components/src/navigator/types.ts new file mode 100644 index 00000000000000..30e79276baf79d --- /dev/null +++ b/packages/components/src/navigator/types.ts @@ -0,0 +1,34 @@ +/** + * External dependencies + */ +// eslint-disable-next-line no-restricted-imports +import type { ReactNode } from 'react'; + +type NavigatorPathOptions = { + isBack?: boolean; +}; + +export type NavigatorPath = NavigatorPathOptions & { + path?: string; +}; + +export type NavigatorContext = [ + NavigatorPath, + ( path: NavigatorPath ) => void +]; + +// Returned by the `useNavigator` hook +export type Navigator = { + push: ( path: string, options: NavigatorPathOptions ) => void; +}; + +export type NavigatorProviderProps = { + // TODO: JSDoc comments + initialPath: string; + children: ReactNode; +}; + +export type NavigatorScreenProps = { + path: string; + children: ReactNode; +}; diff --git a/packages/components/src/navigator/use-navigator.js b/packages/components/src/navigator/use-navigator.ts similarity index 80% rename from packages/components/src/navigator/use-navigator.js rename to packages/components/src/navigator/use-navigator.ts index 232fdaafd7d064..56bdfe733062a9 100644 --- a/packages/components/src/navigator/use-navigator.js +++ b/packages/components/src/navigator/use-navigator.ts @@ -7,8 +7,9 @@ import { useContext } from '@wordpress/element'; * Internal dependencies */ import { NavigatorContext } from './context'; +import type { Navigator } from './types'; -function useNavigator() { +function useNavigator(): Navigator { const [ , setPath ] = useContext( NavigatorContext ); return { diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index cd26b356e617e0..3af6745f4be320 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -46,6 +46,7 @@ "src/menu-item/**/*", "src/menu-group/**/*", "src/navigable-container/**/*", + "src/navigator/**/*", "src/number-control/**/*", "src/popover/**/*", "src/range-control/**/*", From b0cb7fafbb64dd76ddf6664c0602755b489bec0f Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 29 Sep 2021 16:55:50 +0200 Subject: [PATCH 2/6] `NavigatorProvider`: connect to the Context System, wrap in `View` --- .../navigator-provider/component.tsx | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/components/src/navigator/navigator-provider/component.tsx b/packages/components/src/navigator/navigator-provider/component.tsx index 74221285bbf292..4c9f4975e7654e 100644 --- a/packages/components/src/navigator/navigator-provider/component.tsx +++ b/packages/components/src/navigator/navigator-provider/component.tsx @@ -1,3 +1,9 @@ +/** + * External dependencies + */ +// eslint-disable-next-line no-restricted-imports +import type { Ref } from 'react'; + /** * WordPress dependencies */ @@ -6,24 +12,40 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ +import { + contextConnect, + useContextSystem, + WordPressComponentProps, +} from '../../ui/context'; +import { View } from '../../view'; import { NavigatorContext } from '../context'; import type { NavigatorProviderProps, NavigatorPath } from '../types'; -function NavigatorProvider( { - initialPath, - children, -}: NavigatorProviderProps ) { +function NavigatorProvider( + props: WordPressComponentProps< NavigatorProviderProps, 'div' >, + forwardedRef: Ref< any > +) { + const { initialPath, children, ...otherProps } = useContextSystem( + props, + 'NavigatorProvider' + ); + const [ path, setPath ] = useState< NavigatorPath >( { path: initialPath, } ); return ( - - { children } - + + + { children } + + ); } -// TODO: context connect +const ConnectedNavigatorProvider = contextConnect( + NavigatorProvider, + 'NavigatorProvider' +); -export default NavigatorProvider; +export default ConnectedNavigatorProvider; From cb30273ed157475410eb1c6588491980855d335a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 29 Sep 2021 16:56:51 +0200 Subject: [PATCH 3/6] `NavigatorScreen`: connect to the Context System, swap simple `div` with `View` --- .../navigator/navigator-screen/component.tsx | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/components/src/navigator/navigator-screen/component.tsx b/packages/components/src/navigator/navigator-screen/component.tsx index 8d36706a24308b..0bb34ed30e8a7c 100644 --- a/packages/components/src/navigator/navigator-screen/component.tsx +++ b/packages/components/src/navigator/navigator-screen/component.tsx @@ -2,7 +2,9 @@ * External dependencies */ // eslint-disable-next-line no-restricted-imports -import { motion } from 'framer-motion'; +import type { Ref } from 'react'; +// eslint-disable-next-line no-restricted-imports +import { motion, MotionProps } from 'framer-motion'; /** * WordPress dependencies @@ -14,6 +16,12 @@ import { isRTL } from '@wordpress/i18n'; /** * Internal dependencies */ +import { + contextConnect, + useContextSystem, + WordPressComponentProps, +} from '../../ui/context'; +import { View } from '../../view'; import { NavigatorContext } from '../context'; import type { NavigatorScreenProps } from '../types'; @@ -22,7 +30,19 @@ const animationEnterDuration = 0.14; const animationExitDuration = 0.14; const animationExitDelay = 0; -function NavigatorScreen( { children, path }: NavigatorScreenProps ) { +// Props specific to `framer-motion` can't be currently passed to `NavigatorScreen`, +// as some of them would overlap with HTML props (e.g. `onAnimationStart`, ...) +type Props = Omit< + WordPressComponentProps< NavigatorScreenProps, 'div', false >, + keyof MotionProps +>; + +function NavigatorScreen( props: Props, forwardedRef: Ref< any > ) { + const { children, path, ...otherProps } = useContextSystem( + props, + 'NavigatorScreen' + ); + const prefersReducedMotion = useReducedMotion(); const [ currentPath ] = useContext( NavigatorContext ); const isMatch = currentPath.path === path; @@ -40,7 +60,11 @@ function NavigatorScreen( { children, path }: NavigatorScreenProps ) { } if ( prefersReducedMotion ) { - return
{ children }
; + return ( + + { children } + + ); } const animate = { @@ -83,6 +107,7 @@ function NavigatorScreen( { children, path }: NavigatorScreenProps ) { return ( { children } @@ -90,6 +115,10 @@ function NavigatorScreen( { children, path }: NavigatorScreenProps ) { ); } -// TODO: context connect +// TODO: docs +const ConnectedNavigatorScreen = contextConnect( + NavigatorScreen, + 'NavigatorScreen' +); -export default NavigatorScreen; +export default ConnectedNavigatorScreen; From c02defa64bb9046625644a4ca3d035cc4b56504a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 29 Sep 2021 17:40:25 +0200 Subject: [PATCH 4/6] Add JSDocs to types, update READMEs --- .../src/navigator/navigator-provider/README.md | 3 +-- .../src/navigator/navigator-screen/README.md | 7 +++---- packages/components/src/navigator/types.ts | 13 ++++++++++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/components/src/navigator/navigator-provider/README.md b/packages/components/src/navigator/navigator-provider/README.md index dead39a0bb7f6d..4ce48a75790b84 100644 --- a/packages/components/src/navigator/navigator-provider/README.md +++ b/packages/components/src/navigator/navigator-provider/README.md @@ -60,8 +60,7 @@ The initial active path. - Required: No - -## The navigator object +## The `navigator` object You can retrieve a `navigator` instance by using the `useNavigator` hook. diff --git a/packages/components/src/navigator/navigator-screen/README.md b/packages/components/src/navigator/navigator-screen/README.md index 65957155b04881..bd48b91444fb12 100644 --- a/packages/components/src/navigator/navigator-screen/README.md +++ b/packages/components/src/navigator/navigator-screen/README.md @@ -14,9 +14,8 @@ Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/ The component accepts the following props: -### `path` +### `path`: `string` -- Type: `string` -- Required: Yes +The screen's path, matched against the current path stored in the navigator. -The path of the current screen. +- Required: Yes diff --git a/packages/components/src/navigator/types.ts b/packages/components/src/navigator/types.ts index 30e79276baf79d..acc5aecb840dff 100644 --- a/packages/components/src/navigator/types.ts +++ b/packages/components/src/navigator/types.ts @@ -23,12 +23,23 @@ export type Navigator = { }; export type NavigatorProviderProps = { - // TODO: JSDoc comments + /** + * The initial active path. + */ initialPath: string; + /** + * The children elements. + */ children: ReactNode; }; export type NavigatorScreenProps = { + /** + * The screen's path, matched against the current path stored in the navigator. + */ path: string; + /** + * The children elements. + */ children: ReactNode; }; From 60a510c5f5c211536547cca02e247463c6b925fd Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 29 Sep 2021 17:40:51 +0200 Subject: [PATCH 5/6] Add JSDocs to exported components/hooks --- .../navigator-provider/component.tsx | 45 +++++++++++++++++++ .../navigator/navigator-screen/component.tsx | 45 ++++++++++++++++++- .../components/src/navigator/use-navigator.ts | 3 ++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/packages/components/src/navigator/navigator-provider/component.tsx b/packages/components/src/navigator/navigator-provider/component.tsx index 4c9f4975e7654e..5f8e82e6137213 100644 --- a/packages/components/src/navigator/navigator-provider/component.tsx +++ b/packages/components/src/navigator/navigator-provider/component.tsx @@ -43,6 +43,51 @@ function NavigatorProvider( ); } +/** + * The `NavigatorProvider` component allows rendering nested panels or menus (via the `NavigatorScreen` component) and navigate between these different states (via the `useNavigator` hook). + * The Global Styles sidebar is an example of this. The `Navigator*` family of components is _not_ opinionated in terms of UI, and can be composed with any UI components to navigate between the nested screens. + * + * @example + * ```jsx + * import { + * __experimentalNavigatorProvider as NavigatorProvider, + * __experimentalNavigatorScreen as NavigatorScreen, + * __experimentalUseNavigator as useNavigator, + * } from '@wordpress/components'; + * + * function NavigatorButton( { + * path, + * isBack = false, + * ...props + * } ) { + * const navigator = useNavigator(); + * return ( + *