diff --git a/plugins/woocommerce-admin/package-lock.json b/plugins/woocommerce-admin/package-lock.json index c86be0b0860..50733e69f1c 100644 --- a/plugins/woocommerce-admin/package-lock.json +++ b/plugins/woocommerce-admin/package-lock.json @@ -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 diff --git a/plugins/woocommerce-admin/package.json b/plugins/woocommerce-admin/package.json index 45d9cfd5360..55c459206a0 100644 --- a/plugins/woocommerce-admin/package.json +++ b/plugins/woocommerce-admin/package.json @@ -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", diff --git a/plugins/woocommerce-admin/packages/components/src/link/stories/index.js b/plugins/woocommerce-admin/packages/components/src/link/stories/index.js index 206f1910240..b5792fa5194 100644 --- a/plugins/woocommerce-admin/packages/components/src/link/stories/index.js +++ b/plugins/woocommerce-admin/packages/components/src/link/stories/index.js @@ -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 ( - + WooCommerce.com ); }; + +export const WCAdmin = () => { + return ( + + Analytics: Orders + + ); +}; + +export const WPAdmin = () => { + return ( + + New Product + + ); +}; diff --git a/plugins/woocommerce-admin/packages/components/src/link/test/index.js b/plugins/woocommerce-admin/packages/components/src/link/test/index.js new file mode 100644 index 00000000000..a8512a9151c --- /dev/null +++ b/plugins/woocommerce-admin/packages/components/src/link/test/index.js @@ -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( + + WooCommerce.com + + ); + + expect( container.firstChild ).toMatchInlineSnapshot( ` + + WooCommerce.com + + ` ); + } ); + + it( 'should render `wp-admin` links', () => { + const { container } = render( + + New Post + + ); + + expect( container.firstChild ).toMatchInlineSnapshot( ` + + New Post + + ` ); + } ); + + it( 'should render `wc-admin` links', () => { + const { container } = render( + + Analytics: Orders + + ); + + expect( container.firstChild ).toMatchInlineSnapshot( ` + + Analytics: Orders + + ` ); + } ); + + it( 'should render links without a type as `wc-admin`', () => { + const { container } = render( + + Analytics: Orders + + ); + + expect( container.firstChild ).toMatchInlineSnapshot( ` + + Analytics: Orders + + ` ); + } ); + + it( 'should allow custom props to be passed through', () => { + const { container } = render( + + WooCommerce.com + + ); + + expect( container.firstChild ).toMatchInlineSnapshot( ` + + WooCommerce.com + + ` ); + } ); + + it( 'should support `onClick`', () => { + const clickHandler = jest.fn(); + + const { container } = render( + + WooCommerce.com + + ); + + fireEvent.click( container.firstChild ); + + expect( clickHandler ).toHaveBeenCalled(); + } ); +} ); diff --git a/plugins/woocommerce-admin/packages/components/src/list/index.js b/plugins/woocommerce-admin/packages/components/src/list/index.js index 5bebe52118a..a17d22a9c7a 100644 --- a/plugins/woocommerce-admin/packages/components/src/list/index.js +++ b/plugins/woocommerce-admin/packages/components/src/list/index.js @@ -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 ( diff --git a/plugins/woocommerce-admin/packages/components/src/list/stories/index.js b/plugins/woocommerce-admin/packages/components/src/list/stories/index.js index 2ae3bcbc311..3ce10fd3f5e 100644 --- a/plugins/woocommerce-admin/packages/components/src/list/stories/index.js +++ b/plugins/woocommerce-admin/packages/components/src/list/stories/index.js @@ -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: , title: 'WooCommerce.com', href: 'https://woocommerce.com', + onClick: logItemClick, }, { before: , after: , title: 'WordPress.org', href: 'https://wordpress.org', + onClick: logItemClick, }, { before: , @@ -63,9 +87,10 @@ export const BeforeAndAfter = () => { before: , 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 ; }; -export const CustomStyle = () => { +export const CustomStyleAndTags = () => { const listItems = [ { before: , after: , title: 'WooCommerce.com', href: 'https://woocommerce.com', + onClick: logItemClick, + listItemTag: 'woocommerce.com-link', }, { before: , after: , title: 'WordPress.org', href: 'https://wordpress.org', + onClick: logItemClick, + listItemTag: 'wordpress.org-link', }, { before: , @@ -95,10 +124,12 @@ export const CustomStyle = () => { before: , 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', }, ]; diff --git a/plugins/woocommerce-admin/packages/components/src/list/test/index.js b/plugins/woocommerce-admin/packages/components/src/list/test/index.js new file mode 100644 index 00000000000..3f57d756631 --- /dev/null +++ b/plugins/woocommerce-admin/packages/components/src/list/test/index.js @@ -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( ); + + 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( ); + + 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( ); + + 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( ); + + 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(); + } ); +} );