Skip to content

Commit

Permalink
Add NavigatorLink and NavigatorBackLink, replace usages across co…
Browse files Browse the repository at this point in the history
…debase
  • Loading branch information
ciampo committed Dec 14, 2021
1 parent 3af4385 commit ba08a42
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 76 deletions.
2 changes: 2 additions & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ export { default as __experimentalNavigationMenu } from './navigation/menu';
export {
NavigatorProvider as __experimentalNavigatorProvider,
NavigatorScreen as __experimentalNavigatorScreen,
NavigatorLink as __experimentalNavigatorLink,
NavigatorBackLink as __experimentalNavigatorBackLink,
useNavigator as __experimentalUseNavigator,
} from './navigator';
export { default as Notice } from './notice';
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/navigator/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { default as NavigatorProvider } from './navigator-provider';
export { default as NavigatorScreen } from './navigator-screen';
export { default as NavigatorLink } from './navigator-link';
export { default as NavigatorBackLink } from './navigator-back-link';
export { default as useNavigator } from './use-navigator';
19 changes: 19 additions & 0 deletions packages/components/src/navigator/navigator-back-link/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `NavigatorBackLink`

<div class="callout callout-alert">
This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
</div>

**TODO**

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.

## Usage

**TODO**

## Props

The component accepts the following props:

**TODO**
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* External dependencies
*/
// eslint-disable-next-line no-restricted-imports
import type { Ref } from 'react';

/**
* WordPress dependencies
*/
import { useCallback } from '@wordpress/element';

/**
* Internal dependencies
*/
import {
contextConnect,
useContextSystem,
WordPressComponentProps,
} from '../../ui/context';
import { View } from '../../view';
import useNavigator from '../use-navigator';
import type { NavigatorBackLinkProps } from '../types';

function NavigatorBackLink(
props: WordPressComponentProps< NavigatorBackLinkProps, 'button' >,
forwardedRef: Ref< any >
) {
const {
children,
onClick,
as = 'button',
...otherProps
} = useContextSystem( props, 'NavigatorBackLink' );

const { pop } = useNavigator();
const handleClick: React.MouseEventHandler< HTMLElement > = useCallback(
( e ) => {
e.preventDefault();
pop();
onClick?.( e );
},
[ pop, onClick ]
);

return (
<View
as={ as }
ref={ forwardedRef }
onClick={ handleClick }
{ ...otherProps }
>
{ children }
</View>
);
}

/**
* TODO: add example
*/
const ConnectedNavigatorBackLink = contextConnect(
NavigatorBackLink,
'NavigatorBackLink'
);

export default ConnectedNavigatorBackLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './component';
19 changes: 19 additions & 0 deletions packages/components/src/navigator/navigator-link/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `NavigatorLink`

<div class="callout callout-alert">
This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
</div>

**TODO**

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.

## Usage

**TODO**

## Props

The component accepts the following props:

**TODO**
72 changes: 72 additions & 0 deletions packages/components/src/navigator/navigator-link/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* External dependencies
*/
// eslint-disable-next-line no-restricted-imports
import type { Ref } from 'react';

/**
* WordPress dependencies
*/
import { useCallback } from '@wordpress/element';

/**
* Internal dependencies
*/
import {
contextConnect,
useContextSystem,
WordPressComponentProps,
} from '../../ui/context';
import { View } from '../../view';
import useNavigator from '../use-navigator';
import type { NavigatorLinkProps } from '../types';

const defaultAttributeName = 'data-navigator-focusable-id';
const defaultSelectorFactory = ( attrName: string, attrValue: string ) =>
`[${ attrName }="${ attrValue }"]`;

function NavigatorLink(
props: WordPressComponentProps< NavigatorLinkProps, 'a' >,
forwardedRef: Ref< any >
) {
const {
path,
children,
onClick,
as = 'button',
attributeName = defaultAttributeName,
selectorFactory = defaultSelectorFactory,
...otherProps
} = useContextSystem( props, 'NavigatorLink' );

const { push } = useNavigator();
const focusTargetSelector = selectorFactory( attributeName, path );
const handleClick: React.MouseEventHandler< HTMLElement > = useCallback(
( e ) => {
e.preventDefault();
push( path, { focusTargetSelector } );
onClick?.( e );
},
[ push, selectorFactory, onClick ]
);

const linkProps = { [ attributeName ]: path, path, ...otherProps };

return (
<View
as={ as }
ref={ forwardedRef }
onClick={ handleClick }
{ ...linkProps }
>
{ children }
</View>
);
}

/**
* TODO: add example
*/
const ConnectedNavigatorLink = contextConnect( NavigatorLink, 'NavigatorLink' );

export default ConnectedNavigatorLink;
1 change: 1 addition & 0 deletions packages/components/src/navigator/navigator-link/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './component';
35 changes: 10 additions & 25 deletions packages/components/src/navigator/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,24 @@ import { Card, CardBody, CardFooter, CardHeader } from '../../card';
import { HStack } from '../../h-stack';
import { Flyout } from '../../flyout';
import { useCx } from '../../utils/hooks/use-cx';
import { NavigatorProvider, NavigatorScreen, useNavigator } from '../';
import {
NavigatorProvider,
NavigatorScreen,
NavigatorLink,
NavigatorBackLink,
} from '../';

export default {
title: 'Components (Experimental)/Navigator',
component: NavigatorProvider,
};

function NavigatorButton( { path, ...props } ) {
const { push } = useNavigator();
const dataAttrName = 'data-navigator-focusable-id';
const dataAttrValue = path;

const dataAttrCssSelector = `[${ dataAttrName }="${ dataAttrValue }"]`;

const buttonProps = {
...props,
[ dataAttrName ]: dataAttrValue,
};

return (
<Button
variant="secondary"
onClick={ () =>
push( path, { focusTargetSelector: dataAttrCssSelector } )
}
{ ...buttonProps }
/>
);
function NavigatorButton( props ) {
return <NavigatorLink as={ Button } variant="secondary" { ...props } />;
}

function NavigatorBackButton( { ...props } ) {
const { pop } = useNavigator();
return <Button variant="secondary" onClick={ pop } { ...props } />;
function NavigatorBackButton( props ) {
return <NavigatorBackLink as={ Button } variant="secondary" { ...props } />;
}

const MyNavigation = () => {
Expand Down
35 changes: 35 additions & 0 deletions packages/components/src/navigator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,38 @@ export type NavigatorScreenProps = {
*/
children: ReactNode;
};

export type NavigatorBackLinkProps = {
/**
* The children elements.
*/
children: ReactNode;
/**
* The callback called in response to a `click` event.
*/
onClick?: React.MouseEventHandler< HTMLElement >;
};

export type NavigatorLinkProps = NavigatorBackLinkProps & {
/**
* The path to navigate to.
*/
path: string;
/**
* The HTML attribute used to identify the `NavigatorLink`, which is used
* by `Navigator` to restore focus.
*
* @default 'data-navigator-focusable-id'
*/
attributeName?: string;
/**
* A function that generates the CSS selector used ny `Navigator` when
* restoring focus.
*
* @default ( attrName, attrValue ) => `[${ attrName }="${ attrValue }"]`;
*/
selectorFactory?: (
attributeName: string,
attributeValue: string
) => string;
};
54 changes: 28 additions & 26 deletions packages/edit-post/src/components/preferences-modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { get } from 'lodash';
import {
__experimentalNavigatorProvider as NavigatorProvider,
__experimentalNavigatorScreen as NavigatorScreen,
__experimentalUseNavigator as useNavigator,
__experimentalNavigatorLink as NavigatorLink,
__experimentalNavigatorBackLink as NavigatorBackLink,
__experimentalItemGroup as ItemGroup,
__experimentalItem as Item,
__experimentalHStack as HStack,
Expand Down Expand Up @@ -55,33 +56,33 @@ import BlockManager from '../block-manager';
const MODAL_NAME = 'edit-post/preferences';
const PREFERENCES_MENU = 'preferences-menu';

function NavigationButton( { as: Tag = Button, path, ...props } ) {
const { push } = useNavigator();
// function NavigationButton( { as: Tag = Button, path, ...props } ) {
// const { push } = useNavigator();

const dataAttrName = 'data-navigator-focusable-id';
const dataAttrValue = path;
// const dataAttrName = 'data-navigator-focusable-id';
// const dataAttrValue = path;

const dataAttrCssSelector = `[${ dataAttrName }="${ dataAttrValue }"]`;
// const dataAttrCssSelector = `[${ dataAttrName }="${ dataAttrValue }"]`;

const tagProps = {
...props,
[ dataAttrName ]: dataAttrValue,
};
// const tagProps = {
// ...props,
// [ dataAttrName ]: dataAttrValue,
// };

return (
<Tag
onClick={ () =>
push( path, { focusTargetSelector: dataAttrCssSelector } )
}
{ ...tagProps }
/>
);
}
// return (
// <Tag
// onClick={ () =>
// push( path, { focusTargetSelector: dataAttrCssSelector } )
// }
// { ...tagProps }
// />
// );
// }

function NavigationBackButton( { as: Tag = Button, ...props } ) {
const { pop } = useNavigator();
return <Tag onClick={ pop } { ...props } />;
}
// function NavigationBackButton( { as: Tag = Button, ...props } ) {
// const { pop } = useNavigator();
// return <Tag onClick={ pop } { ...props } />;
// }

export default function PreferencesModal() {
const isLargeViewport = useViewportMatch( 'medium' );
Expand Down Expand Up @@ -347,7 +348,7 @@ export default function PreferencesModal() {
<ItemGroup>
{ tabs.map( ( tab ) => {
return (
<NavigationButton
<NavigatorLink
key={ tab.name }
path={ tab.name }
as={ Item }
Expand All @@ -369,7 +370,7 @@ export default function PreferencesModal() {
/>
</FlexItem>
</HStack>
</NavigationButton>
</NavigatorLink>
);
} ) }
</ItemGroup>
Expand All @@ -389,7 +390,8 @@ export default function PreferencesModal() {
size="small"
gap="6"
>
<NavigationBackButton
<NavigatorBackLink
as={ Button }
icon={
isRTL() ? chevronRight : chevronLeft
}
Expand Down
Loading

0 comments on commit ba08a42

Please sign in to comment.