Add support for List item tags and link types (https://github.com/woocommerce/woocommerce-admin/pull/4287)
* Add Storybook console addon. * Add Link stories for all link types. * Add unit tests for Link component. * Add unit tests for List component. * Add support for List item `listItemTag`.
This commit is contained in:
parent
015fc89dbd
commit
61a643655a
|
@ -3646,6 +3646,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@storybook/addon-console": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-console/-/addon-console-1.2.1.tgz",
|
||||
"integrity": "sha512-2iDbDTipWonvRpIqLLntfhCGvawFFvoG1xyErpyL7K/HRdQ1zzIvR1Qm83S7TK8Vg+RzZWm4wcDbxx7WOsFCNg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"global": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"@storybook/addon-docs": {
|
||||
"version": "5.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-5.3.18.tgz",
|
||||
|
@ -5313,6 +5322,12 @@
|
|||
"@types/testing-library__react": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"@testing-library/user-event": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-10.1.0.tgz",
|
||||
"integrity": "sha512-qutUm/2lWAD8IiKrss2Cg6Hf8AkcMeylKm09bSMtYC39Ug68aXWkcbc0H/NVD5R1zOHguTjkR/Ppuns6bWksGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/anymatch": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
|
||||
|
@ -22203,7 +22218,7 @@
|
|||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "npm:wp-prettier@1.19.1",
|
||||
"version": "npm:prettier@1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-1.19.1.tgz",
|
||||
"integrity": "sha512-mqAC2r1NDmRjG+z3KCJ/i61tycKlmADIjxnDhQab+KBxSAGbF/W7/zwB2guy/ypIeKrrftNsIYkNZZQKf3vJcg==",
|
||||
"dev": true
|
||||
|
|
|
@ -145,6 +145,7 @@
|
|||
"@octokit/graphql": "4.3.1",
|
||||
"@storybook/addon-a11y": "5.3.18",
|
||||
"@storybook/addon-actions": "5.3.18",
|
||||
"@storybook/addon-console": "1.2.1",
|
||||
"@storybook/addon-docs": "5.3.18",
|
||||
"@storybook/addon-knobs": "5.3.18",
|
||||
"@storybook/addon-links": "5.3.18",
|
||||
|
@ -153,6 +154,7 @@
|
|||
"@storybook/addons": "5.3.18",
|
||||
"@storybook/react": "5.3.18",
|
||||
"@testing-library/react": "^10.0.3",
|
||||
"@testing-library/user-event": "^10.1.0",
|
||||
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
|
||||
"@wordpress/babel-plugin-makepot": "2.1.3",
|
||||
"@wordpress/babel-preset-default": "3.0.2",
|
||||
|
|
|
@ -1,17 +1,62 @@
|
|||
/*
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withConsole } from '@storybook/addon-console';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Link from '../';
|
||||
|
||||
function logLinkClick( event ) {
|
||||
const a = event.currentTarget;
|
||||
const logMessage = `[${ a.textContent }](${ a.href }) ${ a.dataset.linkType } link clicked`;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log( logMessage );
|
||||
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/Link',
|
||||
component: Link,
|
||||
decorators: [ ( storyFn, context ) => withConsole()( storyFn )( context ) ],
|
||||
};
|
||||
|
||||
export const External = () => {
|
||||
return (
|
||||
<Link href="https://woocommerce.com" type="external">
|
||||
<Link
|
||||
href="https://woocommerce.com"
|
||||
type="external"
|
||||
onClick={ logLinkClick }
|
||||
>
|
||||
WooCommerce.com
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export const WCAdmin = () => {
|
||||
return (
|
||||
<Link
|
||||
href="admin.php?page=wc-admin&path=%2Fanalytics%2Forders"
|
||||
type="wc-admin"
|
||||
onClick={ logLinkClick }
|
||||
>
|
||||
Analytics: Orders
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export const WPAdmin = () => {
|
||||
return (
|
||||
<Link
|
||||
href="post-new.php?post_type=product"
|
||||
type="wp-admin"
|
||||
onClick={ logLinkClick }
|
||||
>
|
||||
New Product
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Link from '../index';
|
||||
|
||||
describe( 'Link', () => {
|
||||
it( 'should render `external` links', () => {
|
||||
const { container } = render(
|
||||
<Link href="https://woocommerce.com" type="external">
|
||||
WooCommerce.com
|
||||
</Link>
|
||||
);
|
||||
|
||||
expect( container.firstChild ).toMatchInlineSnapshot( `
|
||||
<a
|
||||
data-link-type="external"
|
||||
href="https://woocommerce.com"
|
||||
>
|
||||
WooCommerce.com
|
||||
</a>
|
||||
` );
|
||||
} );
|
||||
|
||||
it( 'should render `wp-admin` links', () => {
|
||||
const { container } = render(
|
||||
<Link href="post-new.php?post_type=product" type="wp-admin">
|
||||
New Post
|
||||
</Link>
|
||||
);
|
||||
|
||||
expect( container.firstChild ).toMatchInlineSnapshot( `
|
||||
<a
|
||||
data-link-type="wp-admin"
|
||||
href="post-new.php?post_type=product"
|
||||
>
|
||||
New Post
|
||||
</a>
|
||||
` );
|
||||
} );
|
||||
|
||||
it( 'should render `wc-admin` links', () => {
|
||||
const { container } = render(
|
||||
<Link
|
||||
href="admin.php?page=wc-admin&path=%2Fanalytics%2Forders"
|
||||
type="wc-admin"
|
||||
>
|
||||
Analytics: Orders
|
||||
</Link>
|
||||
);
|
||||
|
||||
expect( container.firstChild ).toMatchInlineSnapshot( `
|
||||
<a
|
||||
data-link-type="wc-admin"
|
||||
href="admin.php?page=wc-admin&path=%2Fanalytics%2Forders"
|
||||
>
|
||||
Analytics: Orders
|
||||
</a>
|
||||
` );
|
||||
} );
|
||||
|
||||
it( 'should render links without a type as `wc-admin`', () => {
|
||||
const { container } = render(
|
||||
<Link href="admin.php?page=wc-admin&path=%2Fanalytics%2Forders">
|
||||
Analytics: Orders
|
||||
</Link>
|
||||
);
|
||||
|
||||
expect( container.firstChild ).toMatchInlineSnapshot( `
|
||||
<a
|
||||
data-link-type="wc-admin"
|
||||
href="admin.php?page=wc-admin&path=%2Fanalytics%2Forders"
|
||||
>
|
||||
Analytics: Orders
|
||||
</a>
|
||||
` );
|
||||
} );
|
||||
|
||||
it( 'should allow custom props to be passed through', () => {
|
||||
const { container } = render(
|
||||
<Link
|
||||
href="https://woocommerce.com"
|
||||
type="external"
|
||||
className="foo"
|
||||
target="bar"
|
||||
>
|
||||
WooCommerce.com
|
||||
</Link>
|
||||
);
|
||||
|
||||
expect( container.firstChild ).toMatchInlineSnapshot( `
|
||||
<a
|
||||
class="foo"
|
||||
data-link-type="external"
|
||||
href="https://woocommerce.com"
|
||||
target="bar"
|
||||
>
|
||||
WooCommerce.com
|
||||
</a>
|
||||
` );
|
||||
} );
|
||||
|
||||
it( 'should support `onClick`', () => {
|
||||
const clickHandler = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<Link
|
||||
href="https://woocommerce.com"
|
||||
type="external"
|
||||
onClick={ clickHandler }
|
||||
>
|
||||
WooCommerce.com
|
||||
</Link>
|
||||
);
|
||||
|
||||
fireEvent.click( container.firstChild );
|
||||
|
||||
expect( clickHandler ).toHaveBeenCalled();
|
||||
} );
|
||||
} );
|
|
@ -21,6 +21,16 @@ class List extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
getItemLinkType( item ) {
|
||||
const { href, linkType } = item;
|
||||
|
||||
if ( linkType ) {
|
||||
return linkType;
|
||||
}
|
||||
|
||||
return href ? 'external' : null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, items } = this.props;
|
||||
const listClassName = classnames( 'woocommerce-list', className );
|
||||
|
@ -34,6 +44,7 @@ class List extends Component {
|
|||
className: itemClasses,
|
||||
content,
|
||||
href,
|
||||
listItemTag,
|
||||
onClick,
|
||||
target,
|
||||
title,
|
||||
|
@ -57,8 +68,9 @@ class List extends Component {
|
|||
onKeyDown: ( e ) =>
|
||||
hasAction ? this.handleKeyDown( e, onClick ) : null,
|
||||
target: href ? target : null,
|
||||
type: href ? 'external' : null,
|
||||
type: this.getItemLinkType( item ),
|
||||
href,
|
||||
'data-list-item-tag': listItemTag,
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import Gridicon from 'gridicons';
|
||||
import { withConsole } from '@storybook/addon-console';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -9,9 +10,27 @@ import Gridicon from 'gridicons';
|
|||
import List from '../';
|
||||
import './style.scss';
|
||||
|
||||
function logItemClick( event ) {
|
||||
const a = event.currentTarget;
|
||||
const itemDescription = a.href
|
||||
? `[${ a.textContent }](${ a.href }) ${ a.dataset.linkType }`
|
||||
: `[${ a.textContent }]`;
|
||||
const itemTag = a.dataset.listItemTag
|
||||
? `'${ a.dataset.listItemTag }'`
|
||||
: 'not set';
|
||||
const logMessage = `[${ itemDescription } item clicked (tag: ${ itemTag })`;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log( logMessage );
|
||||
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/List',
|
||||
component: List,
|
||||
decorators: [ ( storyFn, context ) => withConsole()( storyFn )( context ) ],
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
|
@ -19,10 +38,12 @@ export const Default = () => {
|
|||
{
|
||||
title: 'WooCommerce.com',
|
||||
href: 'https://woocommerce.com',
|
||||
onClick: logItemClick,
|
||||
},
|
||||
{
|
||||
title: 'WordPress.org',
|
||||
href: 'https://wordpress.org',
|
||||
onClick: logItemClick,
|
||||
},
|
||||
{
|
||||
title: 'A list item with no action',
|
||||
|
@ -30,9 +51,10 @@ export const Default = () => {
|
|||
{
|
||||
title: 'Click me!',
|
||||
content: 'An alert will be triggered.',
|
||||
onClick: () => {
|
||||
onClick: ( event ) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
window.alert( 'List item clicked' );
|
||||
return logItemClick( event );
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -47,12 +69,14 @@ export const BeforeAndAfter = () => {
|
|||
after: <Gridicon icon="chevron-right" />,
|
||||
title: 'WooCommerce.com',
|
||||
href: 'https://woocommerce.com',
|
||||
onClick: logItemClick,
|
||||
},
|
||||
{
|
||||
before: <Gridicon icon="my-sites" />,
|
||||
after: <Gridicon icon="chevron-right" />,
|
||||
title: 'WordPress.org',
|
||||
href: 'https://wordpress.org',
|
||||
onClick: logItemClick,
|
||||
},
|
||||
{
|
||||
before: <Gridicon icon="link-break" />,
|
||||
|
@ -63,9 +87,10 @@ export const BeforeAndAfter = () => {
|
|||
before: <Gridicon icon="notice" />,
|
||||
title: 'Click me!',
|
||||
content: 'An alert will be triggered.',
|
||||
onClick: () => {
|
||||
onClick: ( event ) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
window.alert( 'List item clicked' );
|
||||
return logItemClick( event );
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -73,19 +98,23 @@ export const BeforeAndAfter = () => {
|
|||
return <List items={ listItems } />;
|
||||
};
|
||||
|
||||
export const CustomStyle = () => {
|
||||
export const CustomStyleAndTags = () => {
|
||||
const listItems = [
|
||||
{
|
||||
before: <Gridicon icon="cart" />,
|
||||
after: <Gridicon icon="chevron-right" />,
|
||||
title: 'WooCommerce.com',
|
||||
href: 'https://woocommerce.com',
|
||||
onClick: logItemClick,
|
||||
listItemTag: 'woocommerce.com-link',
|
||||
},
|
||||
{
|
||||
before: <Gridicon icon="my-sites" />,
|
||||
after: <Gridicon icon="chevron-right" />,
|
||||
title: 'WordPress.org',
|
||||
href: 'https://wordpress.org',
|
||||
onClick: logItemClick,
|
||||
listItemTag: 'wordpress.org-link',
|
||||
},
|
||||
{
|
||||
before: <Gridicon icon="link-break" />,
|
||||
|
@ -95,10 +124,12 @@ export const CustomStyle = () => {
|
|||
before: <Gridicon icon="notice" />,
|
||||
title: 'Click me!',
|
||||
content: 'An alert will be triggered.',
|
||||
onClick: () => {
|
||||
onClick: ( event ) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
window.alert( 'List item clicked' );
|
||||
return logItemClick( event );
|
||||
},
|
||||
listItemTag: 'click-me',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import List from '../index';
|
||||
|
||||
describe( 'List', () => {
|
||||
it( 'should have aria roles for items', () => {
|
||||
const clickHandler = jest.fn();
|
||||
const listItems = [
|
||||
{
|
||||
title: 'WooCommerce.com',
|
||||
href: 'https://woocommerce.com',
|
||||
},
|
||||
{
|
||||
title: 'Click me!',
|
||||
onClick: clickHandler,
|
||||
},
|
||||
];
|
||||
|
||||
render( <List items={ listItems } /> );
|
||||
|
||||
expect( screen.getAllByRole( 'menuitem' ) ).toHaveLength( 2 );
|
||||
} );
|
||||
|
||||
it( 'should support `onClick` for items', () => {
|
||||
const clickHandler = jest.fn();
|
||||
const listItems = [
|
||||
{
|
||||
title: 'WooCommerce.com',
|
||||
href: 'https://woocommerce.com',
|
||||
},
|
||||
{
|
||||
title: 'Click me!',
|
||||
onClick: clickHandler,
|
||||
},
|
||||
];
|
||||
|
||||
render( <List items={ listItems } /> );
|
||||
|
||||
userEvent.click(
|
||||
screen.getByRole( 'menuitem', { name: 'Click me!' } )
|
||||
);
|
||||
|
||||
expect( clickHandler ).toHaveBeenCalled();
|
||||
} );
|
||||
|
||||
it( 'should set `data-link-type` on items', () => {
|
||||
const listItems = [
|
||||
{
|
||||
title: 'Add products',
|
||||
href: '/post-new.php?post_type=product',
|
||||
linkType: 'wp-admin',
|
||||
},
|
||||
{
|
||||
title: 'Market my store',
|
||||
href: '/admin.php?page=wc-admin&path=%2Fmarketing',
|
||||
linkType: 'wc-admin',
|
||||
},
|
||||
{
|
||||
title: 'WooCommerce.com',
|
||||
href: 'https://woocommerce.com',
|
||||
linkType: 'external',
|
||||
},
|
||||
{
|
||||
title: 'WordPress.org',
|
||||
href: 'https://wordpress.org',
|
||||
},
|
||||
];
|
||||
|
||||
render( <List items={ listItems } /> );
|
||||
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'Add products' } ).dataset
|
||||
.linkType
|
||||
).toBe( 'wp-admin' );
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'Market my store' } ).dataset
|
||||
.linkType
|
||||
).toBe( 'wc-admin' );
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'WooCommerce.com' } ).dataset
|
||||
.linkType
|
||||
).toBe( 'external' );
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'WordPress.org' } ).dataset
|
||||
.linkType
|
||||
).toBe( 'external' );
|
||||
} );
|
||||
|
||||
it( 'should set `data-list-item-tag` on items', () => {
|
||||
const listItems = [
|
||||
{
|
||||
title: 'Add products',
|
||||
href: '/post-new.php?post_type=product',
|
||||
linkType: 'wp-admin',
|
||||
listItemTag: 'add-product',
|
||||
},
|
||||
{
|
||||
title: 'Market my store',
|
||||
href: '/admin.php?page=wc-admin&path=%2Fmarketing',
|
||||
linkType: 'wc-admin',
|
||||
listItemTag: 'marketing',
|
||||
},
|
||||
{
|
||||
title: 'WooCommerce.com',
|
||||
href: 'https://woocommerce.com',
|
||||
linkType: 'external',
|
||||
listItemTag: 'woocommerce.com-site',
|
||||
},
|
||||
{
|
||||
title: 'WordPress.org',
|
||||
href: 'https://wordpress.org',
|
||||
},
|
||||
];
|
||||
|
||||
render( <List items={ listItems } /> );
|
||||
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'Add products' } ).dataset
|
||||
.listItemTag
|
||||
).toBe( 'add-product' );
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'Market my store' } ).dataset
|
||||
.listItemTag
|
||||
).toBe( 'marketing' );
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'WooCommerce.com' } ).dataset
|
||||
.listItemTag
|
||||
).toBe( 'woocommerce.com-site' );
|
||||
expect(
|
||||
screen.getByRole( 'menuitem', { name: 'WordPress.org' } ).dataset
|
||||
.listItemTag
|
||||
).toBeUndefined();
|
||||
} );
|
||||
} );
|
Loading…
Reference in New Issue