Task list - add a shortcut back to store setup (https://github.com/woocommerce/woocommerce-admin/pull/4853)

Fixes woocommerce/woocommerce-admin#4592

This adds the functionality to support a store setup tab displayed in ActivityPanel. The tab currently just redirects to the home page, rather than implementing the nice-to-haves of displaying the task list in the panel. The reason for this, is that I found it would require significant refactoring of the task list to support this currently.

As part of this I have also refactored rendering of tabs inside into 2 new functional components: <Tabs> and <Tab>. The reason for this is to decompose large untestable components, test them and by virtue of being decomposed they will also be more reusable in future. Ideally would have been refactored to a functional component as well, but it would have been too large a task.
This commit is contained in:
Sam Seay 2020-08-06 10:02:24 +12:00 committed by GitHub
parent 17d79a2d67
commit 0d95facf92
12 changed files with 663 additions and 97 deletions

View File

@ -0,0 +1,28 @@
/**
* Internal dependencies
*/
import { isWCAdmin } from '../utils';
describe( 'isWCAdmin', () => {
it( 'correctly identifies WC admin urls', () => {
[
'https://example.com/wp-admin/admin.php?page=wc-admin',
'https://example.com/wp-admin/admin.php?page=wc-admin&foo=bar',
'/admin.php?page=wc-admin',
'/admin.php?page=wc-admin&foo=bar',
].forEach( ( url ) => {
expect( isWCAdmin( url ) ).toBe( true );
} );
} );
it( 'rejects URLs that are not WC admin urls', () => {
[
'https://example.com/wp-admin/edit.php?page=wc-admin',
'https://example.com/wp-admin/admin.php?page=other',
'/edit.php?page=wc-admin',
'/admin.php?page=other',
].forEach( ( url ) => {
expect( isWCAdmin( url ) ).toBe( false );
} );
} );
} );

View File

@ -182,3 +182,13 @@ export function isOnboardingEnabled() {
return getSetting( 'onboardingEnabled', false );
}
/**
* Determines if a URL is a WC admin url.
*
* @param {*} url - the url to test
* @return {boolean} true if the url is a wc-admin URL
*/
export function isWCAdmin( url ) {
return /admin.php\?page=wc-admin/.test( url );
}

View File

@ -2,23 +2,24 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
import clickOutside from 'react-click-outside';
import { Component, lazy, Suspense } from '@wordpress/element';
import { Button, NavigableMenu } from '@wordpress/components';
import { Button } from '@wordpress/components';
import { compose } from '@wordpress/compose';
import { partial, uniqueId, find } from 'lodash';
import { withDispatch } from '@wordpress/data';
import { uniqueId, find } from 'lodash';
import PagesIcon from 'gridicons/dist/pages';
import CrossIcon from 'gridicons/dist/cross-small';
import classnames from 'classnames';
import { Icon, lifesaver } from '@wordpress/icons';
/**
* WooCommerce dependencies
*/
import { getSetting } from '@woocommerce/wc-admin-settings';
import { getSetting, getAdminLink } from '@woocommerce/wc-admin-settings';
import { H, Section, Spinner } from '@woocommerce/components';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { getHistory } from '@woocommerce/navigation';
import { getHistory, getNewPath } from '@woocommerce/navigation';
/**
* Internal dependencies
@ -31,7 +32,8 @@ import {
getUnapprovedReviews,
getUnreadStock,
} from './unread-indicators';
import { isOnboardingEnabled } from 'dashboard/utils';
import { isOnboardingEnabled, isWCAdmin } from 'dashboard/utils';
import withSelect from 'wc-api/with-select';
const HelpPanel = lazy( () =>
import( /* webpackChunkName: "activity-panels-help" */ './panels/help' )
@ -50,19 +52,14 @@ const ReviewsPanel = lazy( () =>
import( /* webpackChunkName: "activity-panels-inbox" */ './panels/reviews' )
);
import { recordEvent } from 'lib/tracks';
import withSelect from 'wc-api/with-select';
import { Tabs } from './tabs';
import { SetupProgress } from './setup-progress';
const manageStock = getSetting( 'manageStock', 'no' );
const reviewsEnabled = getSetting( 'reviewsEnabled', 'no' );
export class ActivityPanel extends Component {
constructor() {
super( ...arguments );
this.togglePanel = this.togglePanel.bind( this );
this.clearPanel = this.clearPanel.bind( this );
this.toggleMobile = this.toggleMobile.bind( this );
this.renderTab = this.renderTab.bind( this );
constructor( props ) {
super( props );
this.state = {
isPanelOpen: false,
mobileOpen: false,
@ -71,30 +68,34 @@ export class ActivityPanel extends Component {
};
}
togglePanel( tabName ) {
const { isPanelOpen, currentTab } = this.state;
// If a panel is being opened, or if an existing panel is already open and a different one is being opened, record a track.
if ( ! isPanelOpen || tabName !== currentTab ) {
recordEvent( 'activity_panel_open', { tab: tabName } );
}
togglePanel( { name: tabName }, isTabOpen ) {
this.setState( ( state ) => {
if ( tabName === state.currentTab || state.currentTab === '' ) {
return {
isPanelOpen: ! state.isPanelOpen,
currentTab: tabName,
mobileOpen: ! state.isPanelOpen,
};
}
return { currentTab: tabName, isPanelSwitching: true };
const isPanelSwitching =
tabName !== state.currentTab &&
state.currentTab !== '' &&
isTabOpen &&
state.isPanelOpen;
return {
isPanelOpen: isTabOpen,
mobileOpen: isTabOpen,
currentTab: tabName,
isPanelSwitching,
};
} );
}
closePanel() {
this.setState( () => ( {
isPanelOpen: false,
currentTab: '',
} ) );
}
clearPanel() {
this.setState( ( { isPanelOpen } ) =>
isPanelOpen ? { isPanelSwitching: false } : { currentTab: '' }
);
this.setState( () => ( {
isPanelSwitching: false,
} ) );
}
// On smaller screen, the panel buttons are hidden behind a toggle.
@ -108,14 +109,14 @@ export class ActivityPanel extends Component {
}
handleClickOutside( event ) {
const { isPanelOpen, currentTab } = this.state;
const { isPanelOpen } = this.state;
const isClickOnModalOrSnackbar =
event.target.closest(
'.woocommerce-inbox-dismiss-confirmation_modal'
) || event.target.closest( '.components-snackbar__action' );
if ( isPanelOpen && ! isClickOnModalOrSnackbar ) {
this.togglePanel( currentTab );
this.closePanel();
}
}
@ -128,8 +129,10 @@ export class ActivityPanel extends Component {
hasUnreadStock,
isEmbedded,
requestingTaskListOptions,
taskListEnabledResolving,
taskListComplete,
taskListHidden,
taskListEnabled,
query,
} = this.props;
@ -145,6 +148,32 @@ export class ActivityPanel extends Component {
( requestingTaskListOptions === true ||
( taskListHidden === false && taskListComplete === false ) );
// To prevent a flicker between 2 different tab groups, while this option resolves just display no tabs.
if ( taskListEnabledResolving ) {
return [];
}
if ( ! taskListComplete && taskListEnabled && showInbox ) {
return [
{
name: 'inbox',
title: __( 'Inbox', 'woocommerce-admin' ),
icon: <i className="material-icons-outlined">inbox</i>,
unread: hasUnreadNotes,
},
{
name: 'setup',
title: __( 'Store Setup', 'woocommerce-admin' ),
icon: <SetupProgress />,
},
isPerformingSetupTask && {
name: 'help',
title: __( 'Help', 'woocommerce-admin' ),
icon: <i className="material-icons-outlined">support</i>,
},
].filter( Boolean );
}
return [
! isPerformingSetupTask && showInbox
? {
@ -191,24 +220,23 @@ export class ActivityPanel extends Component {
}
getPanelContent( tab ) {
const { hasUnreadOrders, query, hasUnapprovedReviews } = this.props;
const { task } = query;
switch ( tab ) {
case 'inbox':
return <InboxPanel />;
case 'orders':
const { hasUnreadOrders } = this.props;
return <OrdersPanel hasActionableOrders={ hasUnreadOrders } />;
case 'stock':
return <StockPanel />;
case 'reviews':
const { hasUnapprovedReviews } = this.props;
return (
<ReviewsPanel
hasUnapprovedReviews={ hasUnapprovedReviews }
/>
);
case 'help':
const { query } = this.props;
const { task } = query;
return <HelpPanel taskName={ task } />;
default:
return null;
@ -216,15 +244,42 @@ export class ActivityPanel extends Component {
}
renderPanel() {
const { updateOptions, taskListHidden } = this.props;
const { isPanelOpen, currentTab, isPanelSwitching } = this.state;
const tab = find( this.getTabs(), { name: currentTab } );
if ( ! tab ) {
return (
<div className="woocommerce-layout__activity-panel-wrapper" />
);
}
const clearPanel = () => {
this.clearPanel();
};
if ( currentTab === 'setup' ) {
const currentLocation = window.location.href;
const homescreenLocation = getAdminLink(
'admin.php?page=wc-admin'
);
// Don't navigate if we're already on the homescreen, this will cause an infinite loop
if ( currentLocation !== homescreenLocation ) {
// Ensure that if the user is trying to get to the task list they can see it even if
// it was dismissed.
if ( taskListHidden === 'no' ) {
this.redirectToHomeScreen();
} else {
updateOptions( {
woocommerce_task_list_hidden: 'no',
} ).then( this.redirectToHomeScreen );
}
}
return null;
}
const classNames = classnames(
'woocommerce-layout__activity-panel-wrapper',
{
@ -239,8 +294,8 @@ export class ActivityPanel extends Component {
tabIndex={ 0 }
role="tabpanel"
aria-label={ tab.title }
onTransitionEnd={ this.clearPanel }
onAnimationEnd={ this.clearPanel }
onTransitionEnd={ clearPanel }
onAnimationEnd={ clearPanel }
>
<div
className="woocommerce-layout__activity-panel-content"
@ -255,49 +310,17 @@ export class ActivityPanel extends Component {
);
}
renderTab( tab, i ) {
const { currentTab, isPanelOpen } = this.state;
const className = classnames(
'woocommerce-layout__activity-panel-tab',
{
'is-active': isPanelOpen && tab.name === currentTab,
'has-unread': tab.unread,
}
);
const selected = tab.name === currentTab;
let tabIndex = -1;
// Only make this item tabbable if it is the currently selected item, or the panel is closed and the item is the first item.
if ( selected || ( ! isPanelOpen && i === 0 ) ) {
tabIndex = null;
redirectToHomeScreen() {
if ( isWCAdmin( window.location.href ) ) {
getHistory().push( getNewPath( {}, '/', {} ) );
} else {
window.location.href = getAdminLink( 'admin.php?page=wc-admin' );
}
return (
<Button
role="tab"
className={ className }
tabIndex={ tabIndex }
aria-selected={ selected }
aria-controls={ 'activity-panel-' + tab.name }
key={ 'activity-panel-tab-' + tab.name }
id={ 'activity-panel-tab-' + tab.name }
onClick={ partial( this.togglePanel, tab.name ) }
>
{ tab.icon }
{ tab.title }{ ' ' }
{ tab.unread && (
<span className="screen-reader-text">
{ __( 'unread activity', 'woocommerce-admin' ) }
</span>
) }
</Button>
);
}
render() {
const tabs = this.getTabs();
const { mobileOpen } = this.state;
const { mobileOpen, currentTab, isPanelOpen } = this.state;
const headerId = uniqueId( 'activity-panel-header_' );
const panelClasses = classnames( 'woocommerce-layout__activity-panel', {
'is-mobile-open': this.state.mobileOpen,
@ -322,7 +345,9 @@ export class ActivityPanel extends Component {
aria-labelledby={ headerId }
>
<Button
onClick={ this.toggleMobile }
onClick={ () => {
this.toggleMobile();
} }
label={
mobileOpen
? __(
@ -343,13 +368,14 @@ export class ActivityPanel extends Component {
) }
</Button>
<div className={ panelClasses }>
<NavigableMenu
role="tablist"
orientation="horizontal"
className="woocommerce-layout__activity-panel-tabs"
>
{ tabs && tabs.map( this.renderTab ) }
</NavigableMenu>
<Tabs
tabs={ tabs }
tabOpen={ isPanelOpen }
selectedTab={ currentTab }
onTabClick={ ( tab, tabOpen ) => {
this.togglePanel( tab, tabOpen );
} }
/>
{ this.renderPanel() }
</div>
</Section>
@ -370,6 +396,13 @@ export default compose(
const hasUnapprovedReviews = getUnapprovedReviews( select );
const { getOption, isResolving } = select( OPTIONS_STORE_NAME );
const taskListEnabledResolving = isResolving( 'getOption', [
'woocommerce_homescreen_enabled',
] );
// This indicates the task list is in progress, but not if it has been hidden or not
const taskListEnabled = isOnboardingEnabled();
let requestingTaskListOptions, taskListComplete, taskListHidden;
if ( isOnboardingEnabled() ) {
@ -390,9 +423,14 @@ export default compose(
hasUnreadStock,
hasUnapprovedReviews,
requestingTaskListOptions,
taskListEnabledResolving,
taskListComplete,
taskListHidden,
taskListEnabled,
};
} ),
withDispatch( ( dispatch ) => ( {
updateOptions: dispatch( OPTIONS_STORE_NAME ).updateOptions,
} ) ),
clickOutside
)( ActivityPanel );

View File

@ -0,0 +1,22 @@
export const SetupProgress = () => (
<svg
className="setup-progress"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20Z"
stroke="#DCDCDE"
strokeWidth="2"
/>
<path
d="M4 12V12C4 16.4183 7.58172 20 12 20V20C16.4183 20 20 16.4183 20 12V12C20 7.58172 16.4183 4 12 4V4"
// stroke="#1E1E1E"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
);

View File

@ -44,6 +44,15 @@
width: 18px;
height: 18px;
// custom progress icon, requires specific coloring
&.setup-progress {
fill: none;
path:last-child {
stroke: currentColor;
}
}
@include breakpoint( '>960px' ) {
width: 24px;
height: 24px;
@ -77,6 +86,7 @@
width: 100%;
height: $header-height;
color: $gray-700;
white-space: nowrap;
&::before {
background-color: var(--wp-admin-theme-color);

View File

@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import classnames from 'classnames';
export const Tab = ( {
icon,
title,
name,
unread,
selected,
isPanelOpen,
onTabClick,
index,
} ) => {
const className = classnames( 'woocommerce-layout__activity-panel-tab', {
'is-active': isPanelOpen && selected,
'has-unread': unread,
} );
let tabIndex = -1;
// Only make this item tabbable if it is the currently selected item, or the panel is closed and the item is the first item.
if ( selected || ( ! isPanelOpen && index === 0 ) ) {
tabIndex = null;
}
const tabKey = `activity-panel-tab-${ name }`;
return (
<Button
role="tab"
className={ className }
tabIndex={ tabIndex }
aria-selected={ selected }
aria-controls={ `activity-panel-${ name }` }
key={ tabKey }
id={ tabKey }
onClick={ () => {
onTabClick( name );
} }
>
{ icon }
{ title }{ ' ' }
{ unread && (
<span className="screen-reader-text">
{ __( 'unread activity', 'woocommerce-admin' ) }
</span>
) }
</Button>
);
};

View File

@ -0,0 +1,247 @@
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import PagesIcon from 'gridicons/dist/pages';
import { Tab } from '../';
const renderTab = () =>
render(
<Tab
icon={ null }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected
isPanelOpen
index={ 0 }
onTabClick={ () => {} }
/>
);
describe( 'ActivityPanel Tab', () => {
it( 'displays a title and unread status based on props', () => {
const { getByText } = render(
<Tab
icon={ null }
title={ 'Hello World' }
name={ 'overview' }
unread
selected
isPanelOpen
index={ 0 }
onTabClick={ () => {} }
/>
);
expect( getByText( 'Hello World' ) ).not.toBeNull();
expect( getByText( 'unread activity' ) ).not.toBeNull();
} );
it( 'renders the node passed to icon', () => {
const { getByText } = render(
<Tab
icon={ <div>Fake icon</div> }
title={ 'Hello World' }
name={ 'overview' }
unread
selected
isPanelOpen
index={ 0 }
onTabClick={ () => {} }
/>
);
expect( getByText( 'Fake icon' ) ).not.toBeNull();
} );
it( 'does not display unread status if unread is false', () => {
const { queryByText } = render(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected
isPanelOpen
index={ 0 }
onTabClick={ () => {} }
/>
);
expect( queryByText( 'unread activity' ) ).toBeNull();
} );
it( 'is tabbable if its selected or if its closed and the first item', () => {
const { getByRole, rerender } = render(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected
isPanelOpen={ false }
index={ 1 }
onTabClick={ () => {} }
/>
);
let tab = getByRole( 'tab' );
// Tab index is set to null if its the currently selected item, or the panel is closed and the item is the first item.
expect( tab ).not.toHaveAttribute( 'tabindex' );
rerender(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected={ false }
isPanelOpen={ false }
index={ 0 }
onTabClick={ () => {} }
/>
);
tab = getByRole( 'tab' );
expect( tab ).not.toHaveAttribute( 'tabindex' );
} );
it( 'is not tabbable if its not selected, or its open', () => {
const { getByRole, rerender } = render(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected={ false }
isPanelOpen={ false }
index={ 1 }
onTabClick={ () => {} }
/>
);
let tab = getByRole( 'tab' );
// Tab index is not set if its the currently selected item, or the panel is closed and the item is the first item.
expect( tab ).toHaveAttribute( 'tabindex', '-1' );
rerender(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected={ false }
isPanelOpen={ true }
index={ 1 }
onTabClick={ () => {} }
/>
);
tab = getByRole( 'tab' );
expect( tab ).toHaveAttribute( 'tabindex', '-1' );
} );
it( 'calls the onTabClick handler if a tab is clicked', () => {
const onTabClickSpy = jest.fn();
const { getByRole } = render(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected={ false }
isPanelOpen={ true }
index={ 1 }
onTabClick={ onTabClickSpy }
/>
);
fireEvent.click( getByRole( 'tab' ) );
expect( onTabClickSpy ).toHaveBeenCalledTimes( 1 );
} );
it( 'derives aria-controls and id from the name prop', () => {
const nameProp = 'some-name';
const { getByRole } = render(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ nameProp }
unread={ false }
selected={ false }
isPanelOpen={ true }
index={ 1 }
onTabClick={ () => {} }
/>
);
const tab = getByRole( 'tab' );
expect( tab ).toHaveAttribute(
'aria-controls',
`activity-panel-${ nameProp }`
);
expect( tab ).toHaveAttribute(
'id',
`activity-panel-tab-${ nameProp }`
);
} );
it( 'has an is-active class if isPanelOpen is true', () => {
const { getByRole, rerender } = renderTab();
expect( getByRole( 'tab' ) ).toHaveClass( 'is-active' );
rerender(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected={ false }
isPanelOpen={ false }
index={ 1 }
onTabClick={ () => {} }
/>
);
expect( getByRole( 'tab' ) ).not.toHaveClass( 'is-active' );
} );
it( 'has an has-unread class if unread is true', () => {
const { getByRole, rerender } = render(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ true }
selected={ false }
isPanelOpen={ false }
index={ 1 }
onTabClick={ () => {} }
/>
);
expect( getByRole( 'tab' ) ).toHaveClass( 'has-unread' );
rerender(
<Tab
icon={ <PagesIcon /> }
title={ 'Hello World' }
name={ 'overview' }
unread={ false }
selected={ false }
isPanelOpen={ false }
index={ 1 }
onTabClick={ () => {} }
/>
);
expect( getByRole( 'tab' ) ).not.toHaveClass( 'has-unread' );
} );
} );

View File

@ -0,0 +1,63 @@
import { NavigableMenu } from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
import { Tab } from '../tab';
import { recordEvent } from 'lib/tracks';
export const Tabs = ( {
tabs,
onTabClick,
selectedTab: selectedTabName,
tabOpen = false,
} ) => {
const [ { tabOpen: tabIsOpenState, currentTab }, setTabState ] = useState( {
tabOpen,
currentTab: selectedTabName,
} );
// Keep state synced with props
useEffect( () => {
setTabState( {
tabOpen,
currentTab: selectedTabName,
} );
}, [ tabOpen, selectedTabName ] );
return (
<NavigableMenu
role="tablist"
orientation="horizontal"
className="woocommerce-layout__activity-panel-tabs"
>
{ tabs &&
tabs.map( ( tab, i ) => (
<Tab
key={ i }
index={ i }
isPanelOpen={ tabIsOpenState }
selected={ currentTab === tab.name }
{ ...tab }
onTabClick={ () => {
const isTabOpen =
currentTab === tab.name || currentTab === ''
? ! tabIsOpenState
: true;
// If a panel is being opened, or if an existing panel is already open and a different one is being opened, record a track.
if ( ! isTabOpen || currentTab !== tab.name ) {
recordEvent( 'activity_panel_open', {
tab: tab.name,
} );
}
setTabState( {
tabOpen: isTabOpen,
currentTab: tab.name,
} );
onTabClick( tab, isTabOpen );
} }
/>
) ) }
</NavigableMenu>
);
};

View File

@ -0,0 +1,95 @@
jest.mock( 'lib/tracks', () => ( { recordEvent: jest.fn() } ) );
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { Tabs } from '../';
import { recordEvent } from 'lib/tracks';
const generateTabs = () => {
return [ '0', '1', '2', '3' ].map( ( name ) => ( {
name,
title: `Tab ${ name }`,
icon: <span>icon</span>,
unread: false,
} ) );
};
describe( 'Activity Panel Tabs', () => {
it( 'correctly tracks the selected tab', () => {
const { getAllByRole } = render(
<Tabs
selectedTab={ '3' }
tabs={ generateTabs() }
onTabClick={ () => {} }
/>
);
const tabs = getAllByRole( 'tab' );
fireEvent.click( tabs[ 2 ] );
expect( tabs[ 2 ] ).toHaveClass( 'is-active' );
fireEvent.click( tabs[ 3 ] );
expect( tabs[ 2 ] ).not.toHaveClass( 'is-active' );
expect( tabs[ 3 ] ).toHaveClass( 'is-active' );
} );
it( 'closes a tab if its the same one last opened', () => {
const { getAllByRole } = render(
<Tabs
selectedTab={ '3' }
tabs={ generateTabs() }
onTabClick={ () => {} }
/>
);
const tabs = getAllByRole( 'tab' );
fireEvent.click( tabs[ 2 ] );
expect( tabs[ 2 ] ).toHaveClass( 'is-active' );
fireEvent.click( tabs[ 2 ] );
expect( tabs[ 2 ] ).not.toHaveClass( 'is-active' );
} );
it( 'triggers onTabClick with the selected when a tab is clicked', () => {
const tabClickSpy = jest.fn();
const generatedTabs = generateTabs();
const { getAllByRole } = render(
<Tabs
selectedTab={ '3' }
tabs={ generatedTabs }
onTabClick={ tabClickSpy }
/>
);
const tabs = getAllByRole( 'tab' );
fireEvent.click( tabs[ 3 ] );
expect( tabClickSpy ).toHaveBeenCalledWith( generatedTabs[ 3 ], true );
} );
it( 'records an event when panels are being opened and when the open panel changes', () => {
const generatedTabs = generateTabs();
const { getAllByRole } = render(
<Tabs
selectedTab={ '3' }
tabs={ generatedTabs }
onTabClick={ () => {} }
/>
);
const tabs = getAllByRole( 'tab' );
fireEvent.click( tabs[ 3 ] );
expect( recordEvent ).toHaveBeenCalledWith( 'activity_panel_open', {
tab: generatedTabs[ 3 ].name,
} );
} );
} );

View File

@ -22,8 +22,8 @@ import ActivityPanel from './activity-panel';
import { recordEvent } from 'lib/tracks';
class Header extends Component {
constructor() {
super();
constructor( props ) {
super( props );
this.state = {
isScrolled: false,
};

View File

@ -44,6 +44,7 @@ const ProfileWizard = lazy( () =>
import( /* webpackChunkName: "profile-wizard" */ 'profile-wizard' )
);
import getReports from 'analytics/report/get-reports';
import { isWCAdmin } from 'dashboard/utils';
const TIME_EXCLUDED_SCREENS_FILTER = 'woocommerce_admin_time_excluded_screens';
@ -252,9 +253,7 @@ export class Controller extends Component {
* @param {Array} excludedScreens - wc-admin screens to avoid updating.
*/
export function updateLinkHref( item, nextQuery, excludedScreens ) {
const isWCAdmin = /admin.php\?page=wc-admin/.test( item.href );
if ( isWCAdmin ) {
if ( isWCAdmin( item.href ) ) {
const search = last( item.href.split( '?' ) );
const query = parse( search );
const defaultPath = window.wcAdminFeatures.homescreen

View File

@ -19,7 +19,7 @@
"baseUrl": "./client",
"paths": {
"@woocommerce/*": [ "../packages/*/src" ]
},
"exclude": [ "node_modules", "build" ]
}
}
},
"exclude": [ "node_modules", "build" ]
}