Adding context property to tasklist tracks event, introducing LayoutContext (#33287)

This commit is contained in:
Joel Thiessen 2022-06-14 11:31:50 -07:00 committed by GitHub
parent 143d86490d
commit 183ac65c82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 242 additions and 137 deletions

View File

@ -6,11 +6,9 @@ import { TaskType } from './types';
/** /**
* Filters tasks to only visible tasks, taking in account snoozed tasks. * Filters tasks to only visible tasks, taking in account snoozed tasks.
*/ */
export function getVisibleTasks( tasks: TaskType[] ) { export const getVisibleTasks = ( tasks: TaskType[] ) =>
const nowTimestamp = Date.now(); tasks.filter(
return tasks.filter(
( task ) => ( task ) =>
! task.isDismissed && ! task.isDismissed &&
( ! task.isSnoozed || task.snoozedUntil < nowTimestamp ) ( ! task.isSnoozed || task.snoozedUntil < Date.now() )
); );
}

View File

@ -2,7 +2,7 @@
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { lazy, useState } from '@wordpress/element'; import { lazy, useState, useContext, useMemo } from '@wordpress/element';
import { useSelect } from '@wordpress/data'; import { useSelect } from '@wordpress/data';
import { uniqueId, find } from 'lodash'; import { uniqueId, find } from 'lodash';
import { Icon, help as helpIcon, external } from '@wordpress/icons'; import { Icon, help as helpIcon, external } from '@wordpress/icons';
@ -12,6 +12,7 @@ import {
OPTIONS_STORE_NAME, OPTIONS_STORE_NAME,
useUser, useUser,
useUserPreferences, useUserPreferences,
getVisibleTasks,
} from '@woocommerce/data'; } from '@woocommerce/data';
import { getHistory } from '@woocommerce/navigation'; import { getHistory } from '@woocommerce/navigation';
import { recordEvent } from '@woocommerce/tracks'; import { recordEvent } from '@woocommerce/tracks';
@ -36,6 +37,8 @@ import {
import { getUnapprovedReviews } from '../homescreen/activity-panel/reviews/utils'; import { getUnapprovedReviews } from '../homescreen/activity-panel/reviews/utils';
import { ABBREVIATED_NOTIFICATION_SLOT_NAME } from './panels/inbox/abbreviated-notifications-panel'; import { ABBREVIATED_NOTIFICATION_SLOT_NAME } from './panels/inbox/abbreviated-notifications-panel';
import { getAdminSetting } from '~/utils/admin-settings'; import { getAdminSetting } from '~/utils/admin-settings';
import { useActiveSetupTasklist } from '~/tasks';
import { LayoutContext } from '~/layout';
const HelpPanel = lazy( () => const HelpPanel = lazy( () =>
import( /* webpackChunkName: "activity-panels-help" */ './panels/help' ) import( /* webpackChunkName: "activity-panels-help" */ './panels/help' )
@ -61,6 +64,12 @@ export const ActivityPanel = ( { isEmbedded, query } ) => {
const { fills } = useSlot( ABBREVIATED_NOTIFICATION_SLOT_NAME ); const { fills } = useSlot( ABBREVIATED_NOTIFICATION_SLOT_NAME );
const hasExtendedNotifications = Boolean( fills?.length ); const hasExtendedNotifications = Boolean( fills?.length );
const { updateUserPreferences, ...userData } = useUserPreferences(); const { updateUserPreferences, ...userData } = useUserPreferences();
const activeSetupList = useActiveSetupTasklist();
const layoutContext = useContext( LayoutContext );
const updatedLayoutContext = useMemo(
() => layoutContext.getExtendedContext( 'activity-panel' ),
[ layoutContext ]
);
const getPreviewSiteBtnTrackData = ( select, getOption ) => { const getPreviewSiteBtnTrackData = ( select, getOption ) => {
let trackData = {}; let trackData = {};
@ -137,6 +146,8 @@ export const ActivityPanel = ( { isEmbedded, query } ) => {
requestingTaskListOptions, requestingTaskListOptions,
setupTaskListComplete, setupTaskListComplete,
setupTaskListHidden, setupTaskListHidden,
setupTasksCompleteCount,
setupTasksCount,
previewSiteBtnTrackData, previewSiteBtnTrackData,
} = useSelect( ( select ) => { } = useSelect( ( select ) => {
const { getOption } = select( OPTIONS_STORE_NAME ); const { getOption } = select( OPTIONS_STORE_NAME );
@ -144,7 +155,10 @@ export const ActivityPanel = ( { isEmbedded, query } ) => {
ONBOARDING_STORE_NAME ONBOARDING_STORE_NAME
); );
const isSetupTaskListHidden = getTaskList( 'setup' )?.isHidden; const setupList = getTaskList( activeSetupList );
const isSetupTaskListHidden = setupList?.isHidden;
const setupVisibleTasks = getVisibleTasks( setupList?.tasks || [] );
const extendedTaskList = getTaskList( 'extended' ); const extendedTaskList = getTaskList( 'extended' );
const thingsToDoCount = getThingsToDoNextCount( extendedTaskList ); const thingsToDoCount = getThingsToDoNextCount( extendedTaskList );
@ -160,8 +174,12 @@ export const ActivityPanel = ( { isEmbedded, query } ) => {
requestingTaskListOptions: ! hasFinishedResolution( requestingTaskListOptions: ! hasFinishedResolution(
'getTaskLists' 'getTaskLists'
), ),
setupTaskListComplete: getTaskList( 'setup' )?.isComplete, setupTaskListComplete: setupList?.isComplete,
setupTaskListHidden: isSetupTaskListHidden, setupTaskListHidden: isSetupTaskListHidden,
setupTasksCount: setupVisibleTasks.length,
setupTasksCompleteCount: setupVisibleTasks.filter(
( task ) => task.isComplete
).length,
isCompletedTask: Boolean( isCompletedTask: Boolean(
query.task && getTask( query.task )?.isComplete query.task && getTask( query.task )?.isComplete
), ),
@ -230,7 +248,14 @@ export const ActivityPanel = ( { isEmbedded, query } ) => {
const setup = { const setup = {
name: 'setup', name: 'setup',
title: __( 'Finish setup', 'woocommerce' ), title: __( 'Finish setup', 'woocommerce' ),
icon: <SetupProgress />, icon: (
<SetupProgress
setupTasksComplete={ setupTasksCompleteCount }
setupCompletePercent={ Math.ceil(
( setupTasksCompleteCount / setupTasksCount ) * 100
) }
/>
),
visible: visible:
currentUserCan( 'manage_woocommerce' ) && currentUserCan( 'manage_woocommerce' ) &&
! requestingTaskListOptions && ! requestingTaskListOptions &&
@ -349,6 +374,7 @@ export const ActivityPanel = ( { isEmbedded, query } ) => {
const showHelpHighlightTooltip = shouldShowHelpTooltip(); const showHelpHighlightTooltip = shouldShowHelpTooltip();
return ( return (
<LayoutContext.Provider value={ updatedLayoutContext }>
<div> <div>
<H id={ headerId } className="screen-reader-text"> <H id={ headerId } className="screen-reader-text">
{ __( 'Store Activity', 'woocommerce' ) } { __( 'Store Activity', 'woocommerce' ) }
@ -398,6 +424,7 @@ export const ActivityPanel = ( { isEmbedded, query } ) => {
/> />
) : null } ) : null }
</div> </div>
</LayoutContext.Provider>
); );
}; };

View File

@ -1,7 +1,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
import Tasks from '~/tasks'; import { Tasks } from '~/tasks';
type QueryTypeProps = { type QueryTypeProps = {
query: { query: {

View File

@ -1,21 +1,38 @@
export const SetupProgress = () => ( export const SetupProgress = ( {
setupTasksComplete,
setupCompletePercent,
} ) => (
<svg <svg
className="woocommerce-layout__activity-panel-tab-icon setup-progress" className="woocommerce-layout__activity-panel-tab-icon setup-progress"
width="18" viewBox="0 0 25 25"
height="18"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
> >
<path <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" className="setup-progress-ring"
stroke="#DCDCDE" d="M 12.476 23.237 C 18.369 23.237 23.146 18.414 23.146 12.464 C 23.146 6.512 18.369 1.687 12.476 1.687 C 6.581 1.687 1.803 6.512 1.803 12.464 C 1.803 18.414 6.581 23.237 12.476 23.237 Z"
strokeWidth="2"
/> />
<path <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" className="setup-progress-slice"
strokeWidth="2" transform="matrix(-0.034188, 0, 0, 0.034134, 38.373184, -8.278505)"
strokeLinecap="round" d="M 522 607 A 237 237 0 0 1 759 370 L 759 607 Z"
fill={ setupTasksComplete > 0 ? 'currentColor' : 'white' }
/>
<path
className="setup-progress-slice"
transform="matrix(-0.034188, 0, 0, -0.034134, 38.368454, 33.13131)"
d="M 522 607 A 237 237 0 0 1 759 370 L 759 607 Z"
fill={ setupCompletePercent >= 50 ? 'currentColor' : 'white' }
/>
<path
className="setup-progress-slice"
transform="matrix(0.034188, 0, 0, -0.034134, -13.500516, 33.133827)"
d="M 522 607 A 237 237 0 0 1 759 370 L 759 607 Z"
fill={ setupCompletePercent >= 75 ? 'currentColor' : 'white' }
/>
<path
className="setup-progress-slice"
transform="matrix(0.034188, 0, 0, 0.034134, -13.495783, -8.281025)"
d="M 522 607 A 237 237 0 0 1 759 370 L 759 607 Z"
fill="white"
/> />
</svg> </svg>
); );

View File

@ -29,11 +29,12 @@
} }
} }
// custom progress icon, requires specific coloring .setup-progress-slice {
&.setup-progress { stroke: none;
path:first-child {
stroke: '#DCDCDE';
} }
.setup-progress-ring {
stroke-width: 2px;
} }
} }
@ -69,8 +70,6 @@
outline: none; outline: none;
cursor: pointer; cursor: pointer;
background-color: $studio-white; background-color: $studio-white;
max-width: min-content;
min-width: 80px;
width: 100%; width: 100%;
height: $header-height; height: $header-height;
color: $gray-700; color: $gray-700;
@ -204,16 +203,15 @@
} }
transform: translateX(100%); transform: translateX(100%);
@include activity-panel-slide(); @include activity-panel-slide();
position: fixed; position: absolute;
right: 0; right: 0;
top: #{$header-height + $adminbar-height-mobile}; top: 100%;
z-index: 1000; z-index: 1000;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
@include breakpoint( '>782px' ) { @include breakpoint( '>782px' ) {
height: calc(100vh - #{$header-height + $adminbar-height}); height: calc(100vh - #{$header-height + $adminbar-height});
top: #{$header-height + $adminbar-height};
} }
.has-woocommerce-navigation & { .has-woocommerce-navigation & {
height: calc(100vh - #{$header-height}); height: calc(100vh - #{$header-height});

View File

@ -44,7 +44,9 @@ import { getAdminSetting } from '~/utils/admin-settings';
import { ProgressTitle } from '../task-lists'; import { ProgressTitle } from '../task-lists';
const Tasks = lazy( () => const Tasks = lazy( () =>
import( /* webpackChunkName: "tasks" */ '../tasks' ) import( /* webpackChunkName: "tasks" */ '../tasks' ).then( ( module ) => ( {
default: module.Tasks,
} ) )
); );
const TwoColumnTasks = lazy( () => const TwoColumnTasks = lazy( () =>

View File

@ -4,7 +4,7 @@
import { SlotFillProvider } from '@wordpress/components'; import { SlotFillProvider } from '@wordpress/components';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { withSelect } from '@wordpress/data'; import { withSelect } from '@wordpress/data';
import { Component, lazy, Suspense } from '@wordpress/element'; import { Component, lazy, Suspense, createContext } from '@wordpress/element';
import { import {
unstable_HistoryRouter as HistoryRouter, unstable_HistoryRouter as HistoryRouter,
Route, Route,
@ -15,7 +15,7 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
import { Children, cloneElement } from 'react'; import { Children, cloneElement } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get, isFunction, identity } from 'lodash'; import { get, isFunction, identity, memoize } from 'lodash';
import { parse } from 'qs'; import { parse } from 'qs';
import { getHistory, getQuery } from '@woocommerce/navigation'; import { getHistory, getQuery } from '@woocommerce/navigation';
import { import {
@ -51,6 +51,20 @@ const WCPayUsageModal = lazy( () =>
) )
); );
const LayoutContextPrototype = {
getExtendedContext( newItem ) {
return { ...this, path: [ ...this.path, newItem ] };
},
toString() {
return this.path.join( '/' );
},
path: [],
};
export const LayoutContext = createContext(
LayoutContextPrototype.getExtendedContext( 'root' )
);
export class PrimaryLayout extends Component { export class PrimaryLayout extends Component {
render() { render() {
const { children } = this.props; const { children } = this.props;
@ -111,6 +125,13 @@ const LayoutSwitchWrapper = ( props ) => {
}; };
class _Layout extends Component { class _Layout extends Component {
memoizedLayoutContext = memoize( ( page ) =>
LayoutContextPrototype.getExtendedContext(
page?.breadcrumbs[ page.breadcrumbs.length - 1 ]?.toLowerCase() ||
'page'
)
);
componentDidMount() { componentDidMount() {
this.recordPageViewTrack(); this.recordPageViewTrack();
} }
@ -195,6 +216,9 @@ class _Layout extends Component {
const query = this.getQuery( location && location.search ); const query = this.getQuery( location && location.search );
return ( return (
<LayoutContext.Provider
value={ this.memoizedLayoutContext( page ) }
>
<SlotFillProvider> <SlotFillProvider>
<div className="woocommerce-layout"> <div className="woocommerce-layout">
<Header <Header
@ -210,7 +234,10 @@ class _Layout extends Component {
{ ! isEmbedded && ( { ! isEmbedded && (
<PrimaryLayout> <PrimaryLayout>
<div className="woocommerce-layout__main"> <div className="woocommerce-layout__main">
<Controller { ...restProps } query={ query } /> <Controller
{ ...restProps }
query={ query }
/>
</div> </div>
</PrimaryLayout> </PrimaryLayout>
) } ) }
@ -227,6 +254,7 @@ class _Layout extends Component {
) } ) }
<PluginArea scope="woocommerce-tasks" /> <PluginArea scope="woocommerce-tasks" />
</SlotFillProvider> </SlotFillProvider>
</LayoutContext.Provider>
); );
} }
} }

View File

@ -1,11 +1,10 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { Tasks } from './tasks';
import './fills'; import './fills';
import './deprecated-tasks'; import './deprecated-tasks';
export * from './tasks';
export * from './placeholder'; export * from './placeholder';
export * from './reminder-bar'; export * from './reminder-bar';
export * from './hooks/useActiveSetupList'; export * from './hooks/useActiveSetupList';
export default Tasks;

View File

@ -6,7 +6,8 @@ import { useSelect, useDispatch } from '@wordpress/data';
import { import {
ONBOARDING_STORE_NAME, ONBOARDING_STORE_NAME,
OPTIONS_STORE_NAME, OPTIONS_STORE_NAME,
TaskListType, TaskType,
getVisibleTasks,
} from '@woocommerce/data'; } from '@woocommerce/data';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import { Link } from '@woocommerce/components'; import { Link } from '@woocommerce/components';
@ -112,15 +113,10 @@ export const TasksReminderBar: React.FC< ReminderBarProps > = ( {
REMINDER_BAR_HIDDEN_OPTION, REMINDER_BAR_HIDDEN_OPTION,
] ); ] );
const visibleTasks = const visibleTasks = getVisibleTasks( taskList?.tasks || [] );
taskList?.tasks.filter(
( task ) =>
! task.isDismissed &&
( ! task.isSnoozed || task.snoozedUntil < Date.now() )
) || [];
const completedTasks = const completedTasks =
visibleTasks?.filter( ( task ) => task.isComplete ) || []; visibleTasks.filter( ( task: TaskType ) => task.isComplete ) || [];
const isResolved = taskListIsResolved && optionIsResolved; const isResolved = taskListIsResolved && optionIsResolved;

View File

@ -10,13 +10,14 @@ import {
} from '@woocommerce/data'; } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks'; import { recordEvent } from '@woocommerce/tracks';
import { TaskItem, useSlot } from '@woocommerce/experimental'; import { TaskItem, useSlot } from '@woocommerce/experimental';
import { useCallback } from '@wordpress/element'; import { useCallback, useContext } from '@wordpress/element';
import { useDispatch } from '@wordpress/data'; import { useDispatch } from '@wordpress/data';
import { WooOnboardingTaskListItem } from '@woocommerce/onboarding'; import { WooOnboardingTaskListItem } from '@woocommerce/onboarding';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { LayoutContext } from '~/layout';
import './task-list.scss'; import './task-list.scss';
export type TaskListItemProps = { export type TaskListItemProps = {
@ -35,6 +36,7 @@ export const TaskListItem: React.FC< TaskListItemProps > = ( {
task, task,
} ) => { } ) => {
const { createNotice } = useDispatch( 'core/notices' ); const { createNotice } = useDispatch( 'core/notices' );
const layoutContext = useContext( LayoutContext );
const { const {
dismissTask, dismissTask,
@ -117,6 +119,7 @@ export const TaskListItem: React.FC< TaskListItemProps > = ( {
const trackClick = () => { const trackClick = () => {
recordEvent( 'tasklist_click', { recordEvent( 'tasklist_click', {
task_name: id, task_name: id,
context: layoutContext.toString(),
} ); } );
if ( ! isComplete ) { if ( ! isComplete ) {

View File

@ -2,7 +2,7 @@
* External dependencies * External dependencies
*/ */
import { __, _n, sprintf } from '@wordpress/i18n'; import { __, _n, sprintf } from '@wordpress/i18n';
import { useEffect, useRef, useState } from '@wordpress/element'; import { useEffect, useRef, useState, useContext } from '@wordpress/element';
import { Card, CardHeader } from '@wordpress/components'; import { Card, CardHeader } from '@wordpress/components';
import { useSelect } from '@wordpress/data'; import { useSelect } from '@wordpress/data';
import { Badge } from '@woocommerce/components'; import { Badge } from '@woocommerce/components';
@ -21,6 +21,7 @@ import { TaskListItem } from './task-list-item';
import { TaskListMenu } from './task-list-menu'; import { TaskListMenu } from './task-list-menu';
import './task-list.scss'; import './task-list.scss';
import { ProgressHeader } from '~/task-lists/progress-header'; import { ProgressHeader } from '~/task-lists/progress-header';
import { LayoutContext } from '~/layout';
export type TaskListProps = TaskListType & { export type TaskListProps = TaskListType & {
query: { query: {
@ -51,6 +52,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
} ); } );
const prevQueryRef = useRef( query ); const prevQueryRef = useRef( query );
const visibleTasks = getVisibleTasks( tasks ); const visibleTasks = getVisibleTasks( tasks );
const layoutContext = useContext( LayoutContext );
const incompleteTasks = tasks.filter( const incompleteTasks = tasks.filter(
( task ) => ! task.isComplete && ! task.isDismissed ( task ) => ! task.isComplete && ! task.isDismissed
@ -64,6 +66,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
recordEvent( eventPrefix + 'view', { recordEvent( eventPrefix + 'view', {
number_tasks: visibleTasks.length, number_tasks: visibleTasks.length,
store_connected: profileItems.wccom_connected, store_connected: profileItems.wccom_connected,
context: layoutContext.toString(),
} ); } );
}; };

View File

@ -4,7 +4,7 @@
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { MenuGroup, MenuItem } from '@wordpress/components'; import { MenuGroup, MenuItem } from '@wordpress/components';
import { check } from '@wordpress/icons'; import { check } from '@wordpress/icons';
import { Fragment, useEffect } from '@wordpress/element'; import { Fragment, useEffect, useState } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data'; import { useDispatch, useSelect } from '@wordpress/data';
import { import {
ONBOARDING_STORE_NAME, ONBOARDING_STORE_NAME,
@ -33,6 +33,7 @@ import { SectionedTaskListPlaceholder } from '~/two-column-tasks/sectioned-task-
export type TasksProps = { export type TasksProps = {
query: { task?: string }; query: { task?: string };
context?: string;
}; };
function getTaskListComponent( taskListId: string ) { function getTaskListComponent( taskListId: string ) {
@ -155,12 +156,9 @@ export const Tasks: React.FC< TasksProps > = ( { query } ) => {
? id.endsWith( 'two_column' ) ? id.endsWith( 'two_column' )
: ! id.endsWith( 'two_column' ) : ! id.endsWith( 'two_column' )
) )
.filter( ( { isVisible }: TaskListType ) => isVisible )
.map( ( taskList: TaskListType ) => { .map( ( taskList: TaskListType ) => {
const { id, isHidden, isVisible, isToggleable } = taskList; const { id, isHidden, isToggleable } = taskList;
if ( ! isVisible ) {
return null;
}
const TaskListComponent = getTaskListComponent( id ); const TaskListComponent = getTaskListComponent( id );
return ( return (

View File

@ -144,6 +144,7 @@ describe( 'TaskList', () => {
); );
expect( recordEvent ).toHaveBeenCalledTimes( 1 ); expect( recordEvent ).toHaveBeenCalledTimes( 1 );
expect( recordEvent ).toHaveBeenCalledWith( 'tasklist_view', { expect( recordEvent ).toHaveBeenCalledWith( 'tasklist_view', {
context: 'root',
number_tasks: 0, number_tasks: 0,
store_connected: null, store_connected: null,
} ); } );
@ -166,6 +167,7 @@ describe( 'TaskList', () => {
); );
expect( recordEvent ).toHaveBeenCalledTimes( 1 ); expect( recordEvent ).toHaveBeenCalledTimes( 1 );
expect( recordEvent ).toHaveBeenCalledWith( 'extended_tasklist_view', { expect( recordEvent ).toHaveBeenCalledWith( 'extended_tasklist_view', {
context: 'root',
number_tasks: 0, number_tasks: 0,
store_connected: null, store_connected: null,
} ); } );

View File

@ -1,7 +1,7 @@
/** /**
* External dependencies * External dependencies
*/ */
import { useEffect, useRef, useState } from '@wordpress/element'; import { useEffect, useRef, useState, useContext } from '@wordpress/element';
import { Panel, PanelBody, PanelRow } from '@wordpress/components'; import { Panel, PanelBody, PanelRow } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data'; import { useSelect, useDispatch } from '@wordpress/data';
import { ONBOARDING_STORE_NAME, getVisibleTasks } from '@woocommerce/data'; import { ONBOARDING_STORE_NAME, getVisibleTasks } from '@woocommerce/data';
@ -20,6 +20,7 @@ import { ProgressHeader } from '~/task-lists/progress-header';
import { SectionPanelTitle } from './section-panel-title'; import { SectionPanelTitle } from './section-panel-title';
import { TaskListItem } from './task-list-item'; import { TaskListItem } from './task-list-item';
import { TaskListCompletedHeader } from './completed-header'; import { TaskListCompletedHeader } from './completed-header';
import { LayoutContext } from '~/layout';
type PanelBodyProps = Omit< PanelBody.Props, 'title' | 'onToggle' > & { type PanelBodyProps = Omit< PanelBody.Props, 'title' | 'onToggle' > & {
title: string | React.ReactNode | undefined; title: string | React.ReactNode | undefined;
@ -51,6 +52,7 @@ export const SectionedTaskList: React.FC< TaskListProps > = ( {
const [ openPanel, setOpenPanel ] = useState< string | null >( const [ openPanel, setOpenPanel ] = useState< string | null >(
sections?.find( ( section ) => ! section.isComplete )?.id || null sections?.find( ( section ) => ! section.isComplete )?.id || null
); );
const layoutContext = useContext( LayoutContext );
const prevQueryRef = useRef( query ); const prevQueryRef = useRef( query );
@ -64,6 +66,7 @@ export const SectionedTaskList: React.FC< TaskListProps > = ( {
recordEvent( `${ eventPrefix }view`, { recordEvent( `${ eventPrefix }view`, {
number_tasks: visibleTasks.length, number_tasks: visibleTasks.length,
store_connected: profileItems.wccom_connected, store_connected: profileItems.wccom_connected,
context: layoutContext.toString(),
} ); } );
}; };

View File

@ -5,17 +5,21 @@ import { __ } from '@wordpress/i18n';
import { getNewPath, navigateTo } from '@woocommerce/navigation'; import { getNewPath, navigateTo } from '@woocommerce/navigation';
import { import {
ONBOARDING_STORE_NAME, ONBOARDING_STORE_NAME,
OPTIONS_STORE_NAME,
TaskType, TaskType,
useUserPreferences, useUserPreferences,
} from '@woocommerce/data'; } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks'; import { recordEvent } from '@woocommerce/tracks';
import { TaskItem, useSlot } from '@woocommerce/experimental'; import { TaskItem, useSlot } from '@woocommerce/experimental';
import { useCallback } from '@wordpress/element'; import { useCallback, useContext } from '@wordpress/element';
import { useDispatch } from '@wordpress/data'; import { useDispatch } from '@wordpress/data';
import { WooOnboardingTaskListItem } from '@woocommerce/onboarding'; import { WooOnboardingTaskListItem } from '@woocommerce/onboarding';
import classnames from 'classnames'; import classnames from 'classnames';
/**
* Internal dependencies
*/
import { LayoutContext } from '~/layout';
export type TaskListItemProps = { export type TaskListItemProps = {
task: TaskType; task: TaskType;
eventPrefix?: string; eventPrefix?: string;
@ -35,6 +39,8 @@ export const TaskListItem: React.FC< TaskListItemProps > = ( {
undoSnoozeTask, undoSnoozeTask,
} = useDispatch( ONBOARDING_STORE_NAME ); } = useDispatch( ONBOARDING_STORE_NAME );
const layoutContext = useContext( LayoutContext );
const slot = useSlot( const slot = useSlot(
`woocommerce_onboarding_task_list_item_${ task.id }` `woocommerce_onboarding_task_list_item_${ task.id }`
); );
@ -68,6 +74,7 @@ export const TaskListItem: React.FC< TaskListItemProps > = ( {
const trackClick = () => { const trackClick = () => {
recordEvent( `${ eventPrefix }click`, { recordEvent( `${ eventPrefix }click`, {
task_name: task.id, task_name: task.id,
context: layoutContext.toString(),
} ); } );
if ( ! task.isComplete ) { if ( ! task.isComplete ) {

View File

@ -2,7 +2,13 @@
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useEffect, useRef, useState, createElement } from '@wordpress/element'; import {
useEffect,
useRef,
useState,
createElement,
useContext,
} from '@wordpress/element';
import { Button, Card } from '@wordpress/components'; import { Button, Card } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data'; import { useSelect, useDispatch } from '@wordpress/data';
import { EllipsisMenu } from '@woocommerce/components'; import { EllipsisMenu } from '@woocommerce/components';
@ -29,6 +35,7 @@ import TaskListCompleted from './completed';
import { ProgressHeader } from '~/task-lists/progress-header'; import { ProgressHeader } from '~/task-lists/progress-header';
import { TaskListItemTwoColumn } from './task-list-item-two-column'; import { TaskListItemTwoColumn } from './task-list-item-two-column';
import { TaskListCompletedHeader } from './completed-header'; import { TaskListCompletedHeader } from './completed-header';
import { LayoutContext } from '~/layout';
export type TaskListProps = TaskListType & { export type TaskListProps = TaskListType & {
eventName?: string; eventName?: string;
@ -71,6 +78,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
} >( {} ); } >( {} );
const [ activeTaskId, setActiveTaskId ] = useState( '' ); const [ activeTaskId, setActiveTaskId ] = useState( '' );
const [ showDismissModal, setShowDismissModal ] = useState( false ); const [ showDismissModal, setShowDismissModal ] = useState( false );
const layoutContext = useContext( LayoutContext );
const prevQueryRef = useRef( query ); const prevQueryRef = useRef( query );
@ -83,6 +91,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
recordEvent( `${ listEventPrefix }view`, { recordEvent( `${ listEventPrefix }view`, {
number_tasks: visibleTasks.length, number_tasks: visibleTasks.length,
store_connected: profileItems.wccom_connected, store_connected: profileItems.wccom_connected,
context: layoutContext.toString(),
} ); } );
}; };
@ -179,6 +188,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
const trackClick = ( task: TaskType ) => { const trackClick = ( task: TaskType ) => {
recordEvent( `${ listEventPrefix }click`, { recordEvent( `${ listEventPrefix }click`, {
task_name: task.id, task_name: task.id,
context: layoutContext.toString(),
} ); } );
}; };

View File

@ -148,6 +148,7 @@ describe( 'TaskList', () => {
); );
expect( recordEvent ).toHaveBeenCalledTimes( 1 ); expect( recordEvent ).toHaveBeenCalledTimes( 1 );
expect( recordEvent ).toHaveBeenCalledWith( 'tasklist_view', { expect( recordEvent ).toHaveBeenCalledWith( 'tasklist_view', {
context: 'root',
number_tasks: 0, number_tasks: 0,
store_connected: null, store_connected: null,
} ); } );
@ -170,6 +171,7 @@ describe( 'TaskList', () => {
); );
expect( recordEvent ).toHaveBeenCalledTimes( 1 ); expect( recordEvent ).toHaveBeenCalledTimes( 1 );
expect( recordEvent ).toHaveBeenCalledWith( 'tasklist_view', { expect( recordEvent ).toHaveBeenCalledWith( 'tasklist_view', {
context: 'root',
number_tasks: 0, number_tasks: 0,
store_connected: null, store_connected: null,
} ); } );
@ -193,6 +195,7 @@ describe( 'TaskList', () => {
); );
expect( recordEvent ).toHaveBeenCalledTimes( 1 ); expect( recordEvent ).toHaveBeenCalledTimes( 1 );
expect( recordEvent ).toHaveBeenCalledWith( 'extended_tasklist_view', { expect( recordEvent ).toHaveBeenCalledWith( 'extended_tasklist_view', {
context: 'root',
number_tasks: 0, number_tasks: 0,
store_connected: null, store_connected: null,
} ); } );

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Adding property to tasks tracks events to indicate context.

View File

@ -171,7 +171,7 @@ class TaskList {
* @return bool * @return bool
*/ */
public function is_visible() { public function is_visible() {
if ( ! $this->visible ) { if ( ! $this->visible || ! count( $this->get_viewable_tasks() ) > 0 ) {
return false; return false;
} }
return ! $this->is_hidden(); return ! $this->is_hidden();

View File

@ -104,7 +104,6 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test getting a customers default token, when there no token is expictly set. * Test getting a customers default token, when there no token is expictly set.
* This should be the "first created". * This should be the "first created".
* @see WC_Payment_Token::create() * @see WC_Payment_Token::create()
* @group failing
* @since 2.6.0 * @since 2.6.0
*/ */
public function test_wc_get_customer_default_token_returns_first_created_when_no_default_token_set() { public function test_wc_get_customer_default_token_returns_first_created_when_no_default_token_set() {

View File

@ -8,6 +8,7 @@
require_once __DIR__ . '/test-task.php'; require_once __DIR__ . '/test-task.php';
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskList; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskList;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
/** /**
* class WC_Admin_Tests_OnboardingTasks_TaskList * class WC_Admin_Tests_OnboardingTasks_TaskList
@ -89,6 +90,13 @@ class WC_Admin_Tests_OnboardingTasks_TaskList extends WC_Unit_Test_Case {
* Tests that lists can be hidden. * Tests that lists can be hidden.
*/ */
public function test_visible() { public function test_visible() {
$this->assertFalse( $this->list->is_visible() );
$this->list->add_task(
new TestTask(
new TaskList(),
array( 'id' => 'my-task' )
)
);
$this->assertTrue( $this->list->is_visible() ); $this->assertTrue( $this->list->is_visible() );
} }