Add task hierarchy support to task item (https://github.com/woocommerce/woocommerce-admin/pull/6916)
* Add task hierarchy support to task item * Add optional tooltip * Add changelog * Update the logic to keep it more generic * Fix up css * Change level to numbers * Switched the levels around where 1 is the highest priority * Updated classnames and made sure tooltip does not show on completed item
This commit is contained in:
parent
68324b0e20
commit
99c6044e55
|
@ -19,7 +19,7 @@ import { recordEvent } from '@woocommerce/tracks';
|
||||||
*/
|
*/
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
import CartModal from '../dashboard/components/cart-modal';
|
import CartModal from '../dashboard/components/cart-modal';
|
||||||
import { getAllTasks } from './tasks';
|
import { getAllTasks, taskSort } from './tasks';
|
||||||
import { getCountryCode } from '../dashboard/utils';
|
import { getCountryCode } from '../dashboard/utils';
|
||||||
import TaskList from './task-list';
|
import TaskList from './task-list';
|
||||||
import { DisplayOption } from '../header/activity-panel/display-options';
|
import { DisplayOption } from '../header/activity-panel/display-options';
|
||||||
|
@ -179,14 +179,7 @@ const TaskDashboard = ( { userPreferences, query } ) => {
|
||||||
const { task } = query;
|
const { task } = query;
|
||||||
|
|
||||||
const extensionTasks =
|
const extensionTasks =
|
||||||
Array.isArray( extension ) &&
|
Array.isArray( extension ) && extension.sort( taskSort );
|
||||||
extension.sort( ( a, b ) => {
|
|
||||||
if ( Boolean( a.completed ) === Boolean( b.completed ) ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.completed ? 1 : -1;
|
|
||||||
} );
|
|
||||||
|
|
||||||
const currentTask = getCurrentTask( [
|
const currentTask = getCurrentTask( [
|
||||||
...( extensionTasks || [] ),
|
...( extensionTasks || [] ),
|
||||||
|
|
|
@ -19,14 +19,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-list__item:not(.is-complete) {
|
.woocommerce-list__item:not(.complete) {
|
||||||
.woocommerce-task__icon {
|
.woocommerce-task__icon {
|
||||||
border: 1px solid $gray-100;
|
border: 1px solid $gray-100;
|
||||||
background: $white;
|
background: $white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-list__item.is-complete {
|
.woocommerce-list__item.complete {
|
||||||
.woocommerce-list__item-title {
|
.woocommerce-list__item-title {
|
||||||
color: $gray-700;
|
color: $gray-700;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,60 @@
|
||||||
$foreground-color: var(--wp-admin-theme-color);
|
$foreground-color: var(--wp-admin-theme-color);
|
||||||
|
$task-alert-yellow: #f0b849;
|
||||||
|
|
||||||
.woocommerce-task-list__item {
|
.woocommerce-task-list__item {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 4px;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.level-1 {
|
||||||
|
&::before {
|
||||||
|
background-color: $alert-red;
|
||||||
|
}
|
||||||
|
.gridicons-notice-outline {
|
||||||
|
fill: $alert-red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.level-2 {
|
||||||
|
&::before {
|
||||||
|
background-color: $task-alert-yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.woocommerce-task-list__item-title {
|
.woocommerce-task-list__item-title {
|
||||||
color: $foreground-color;
|
color: $foreground-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-task-list__item-before {
|
.woocommerce-task-list__item-before {
|
||||||
margin-right: 20px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: $gap 20px $gap $gap-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-task-list__item-text {
|
||||||
|
padding: $gap 0;
|
||||||
|
|
||||||
|
.woocommerce-pill {
|
||||||
|
padding: 1px $gap-smaller;
|
||||||
|
margin-left: $gap-smaller;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-task-list__item-after {
|
.woocommerce-task-list__item-after {
|
||||||
margin-left: $gap;
|
margin-left: $gap;
|
||||||
|
margin-right: $gap-large;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
padding: $gap 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-task-list__item-before .woocommerce-task__icon {
|
.woocommerce-task-list__item-before .woocommerce-task__icon {
|
||||||
|
@ -31,14 +70,7 @@ $foreground-color: var(--wp-admin-theme-color);
|
||||||
left: 5px;
|
left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-task-list__item-text {
|
&.complete {
|
||||||
.woocommerce-pill {
|
|
||||||
padding: 1px $gap-smaller;
|
|
||||||
margin-left: $gap-smaller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-complete {
|
|
||||||
.woocommerce-task__icon {
|
.woocommerce-task__icon {
|
||||||
background-color: var(--wp-admin-theme-color);
|
background-color: var(--wp-admin-theme-color);
|
||||||
}
|
}
|
||||||
|
@ -51,4 +83,9 @@ $foreground-color: var(--wp-admin-theme-color);
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.components-tooltip .components-popover__content {
|
||||||
|
width: 160px;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon, check } from '@wordpress/icons';
|
import { Icon, check } from '@wordpress/icons';
|
||||||
import { Button } from '@wordpress/components';
|
import { Button, Tooltip } from '@wordpress/components';
|
||||||
import { Text } from '@woocommerce/experimental';
|
import { Text } from '@woocommerce/experimental';
|
||||||
import { __experimentalListItem as ListItem } from '@woocommerce/components';
|
import { __experimentalListItem as ListItem } from '@woocommerce/components';
|
||||||
|
import NoticeOutline from 'gridicons/dist/notice-outline';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +15,8 @@ import classnames from 'classnames';
|
||||||
import './task-item.scss';
|
import './task-item.scss';
|
||||||
import sanitizeHTML from '../lib/sanitize-html';
|
import sanitizeHTML from '../lib/sanitize-html';
|
||||||
|
|
||||||
|
type TaskLevel = 1 | 2 | 3;
|
||||||
|
|
||||||
type TaskItemProps = {
|
type TaskItemProps = {
|
||||||
title: string;
|
title: string;
|
||||||
completed: boolean;
|
completed: boolean;
|
||||||
|
@ -24,6 +27,30 @@ type TaskItemProps = {
|
||||||
time?: string;
|
time?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
|
level?: TaskLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OptionalTaskTooltip: React.FC< {
|
||||||
|
level: TaskLevel;
|
||||||
|
completed: boolean;
|
||||||
|
children: JSX.Element;
|
||||||
|
} > = ( { level, completed, children } ) => {
|
||||||
|
let tooltip = '';
|
||||||
|
if ( level === 1 && ! completed ) {
|
||||||
|
tooltip = __(
|
||||||
|
'This task is required to keep your store running',
|
||||||
|
'woocommerce-admin'
|
||||||
|
);
|
||||||
|
} else if ( level === 2 && ! completed ) {
|
||||||
|
tooltip = __(
|
||||||
|
'This task is required to set up your extension',
|
||||||
|
'woocommerce-admin'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( tooltip === '' ) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
return <Tooltip text={ tooltip }>{ children }</Tooltip>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TaskItem: React.FC< TaskItemProps > = ( {
|
export const TaskItem: React.FC< TaskItemProps > = ( {
|
||||||
|
@ -36,22 +63,32 @@ export const TaskItem: React.FC< TaskItemProps > = ( {
|
||||||
time,
|
time,
|
||||||
content,
|
content,
|
||||||
expanded = false,
|
expanded = false,
|
||||||
|
level = 3,
|
||||||
} ) => {
|
} ) => {
|
||||||
const className = classnames( 'woocommerce-task-list__item', {
|
const className = classnames( 'woocommerce-task-list__item', {
|
||||||
'is-complete': completed,
|
complete: completed,
|
||||||
|
'level-2': level === 2 && ! completed,
|
||||||
|
'level-1': level === 1 && ! completed,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
disableGutters={ false }
|
disableGutters
|
||||||
className={ className }
|
className={ className }
|
||||||
onClick={ onClick }
|
onClick={ onClick }
|
||||||
animation="slide-right"
|
animation="slide-right"
|
||||||
>
|
>
|
||||||
|
<OptionalTaskTooltip level={ level } completed={ completed }>
|
||||||
<div className="woocommerce-task-list__item-before">
|
<div className="woocommerce-task-list__item-before">
|
||||||
|
{ level === 1 && ! completed ? (
|
||||||
|
<NoticeOutline size={ 36 } />
|
||||||
|
) : (
|
||||||
<div className="woocommerce-task__icon">
|
<div className="woocommerce-task__icon">
|
||||||
{ completed && <Icon icon={ check } /> }
|
{ completed && <Icon icon={ check } /> }
|
||||||
</div>
|
</div>
|
||||||
|
) }
|
||||||
</div>
|
</div>
|
||||||
|
</OptionalTaskTooltip>
|
||||||
<div className="woocommerce-task-list__item-text">
|
<div className="woocommerce-task-list__item-text">
|
||||||
<span className="woocommerce-task-list__item-title">
|
<span className="woocommerce-task-list__item-title">
|
||||||
<Text
|
<Text
|
||||||
|
|
|
@ -298,6 +298,7 @@ export const TaskList = ( {
|
||||||
isDismissable={ task.isDismissable }
|
isDismissable={ task.isDismissable }
|
||||||
onDismiss={ () => dismissTask( task ) }
|
onDismiss={ () => dismissTask( task ) }
|
||||||
time={ task.time }
|
time={ task.time }
|
||||||
|
level={ task.level }
|
||||||
/>
|
/>
|
||||||
) ) }
|
) ) }
|
||||||
</ListComp>
|
</ListComp>
|
||||||
|
|
|
@ -258,9 +258,26 @@ export function getAllTasks( {
|
||||||
type: 'setup',
|
type: 'setup',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return groupListOfObjectsBy(
|
const filteredTasks = applyFilters(
|
||||||
applyFilters( 'woocommerce_admin_onboarding_task_list', tasks, query ),
|
'woocommerce_admin_onboarding_task_list',
|
||||||
'type',
|
tasks,
|
||||||
'extension'
|
query
|
||||||
);
|
);
|
||||||
|
for ( const task of filteredTasks ) {
|
||||||
|
task.level = task.level ? parseInt( task.level, 10 ) : 3;
|
||||||
|
}
|
||||||
|
return groupListOfObjectsBy( filteredTasks, 'type', 'extension' );
|
||||||
|
}
|
||||||
|
|
||||||
|
export function taskSort( a, b ) {
|
||||||
|
if ( a.completed || b.completed ) {
|
||||||
|
return a.completed ? 1 : -1;
|
||||||
|
}
|
||||||
|
// Three is the lowest level.
|
||||||
|
const aLevel = a.level || 3;
|
||||||
|
const bLevel = b.level || 3;
|
||||||
|
if ( aLevel === bLevel ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return aLevel > bLevel ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { DisplayOption } from '../../header/activity-panel/display-options';
|
||||||
|
|
||||||
jest.mock( '@wordpress/api-fetch' );
|
jest.mock( '@wordpress/api-fetch' );
|
||||||
jest.mock( '../tasks', () => ( {
|
jest.mock( '../tasks', () => ( {
|
||||||
|
...jest.requireActual( '../tasks' ),
|
||||||
recordTaskViewEvent: jest.fn(),
|
recordTaskViewEvent: jest.fn(),
|
||||||
getAllTasks: jest.fn(),
|
getAllTasks: jest.fn(),
|
||||||
} ) );
|
} ) );
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { taskSort } from '../tasks';
|
||||||
|
|
||||||
|
describe( 'task sort', () => {
|
||||||
|
const list = [
|
||||||
|
{ key: 'comp', completed: true },
|
||||||
|
{ key: 'comp-l1', completed: true, level: 1 },
|
||||||
|
{ key: 'comp-l2', completed: true, level: 2 },
|
||||||
|
{ key: 'uncomp-l3', completed: false, level: 3 },
|
||||||
|
{ key: 'uncomp', completed: false },
|
||||||
|
{ key: 'uncomp-l2', completed: false, level: 2 },
|
||||||
|
{ key: 'uncomp-l1', completed: false, level: 1 },
|
||||||
|
];
|
||||||
|
it( 'should put all l1 levels at the top if not completed', () => {
|
||||||
|
const sorted = [ ...list ].sort( taskSort );
|
||||||
|
expect( sorted[ 0 ].key ).toEqual( 'uncomp-l1' );
|
||||||
|
expect( sorted[ 1 ].key ).toEqual( 'uncomp-l2' );
|
||||||
|
expect( sorted[ 2 ].key ).toEqual( 'uncomp-l3' );
|
||||||
|
expect( sorted[ 3 ].key ).toEqual( 'uncomp' );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'should put all completed items at the bottom', () => {
|
||||||
|
const sorted = [ ...list, { key: 'test', completed: true } ].sort(
|
||||||
|
taskSort
|
||||||
|
);
|
||||||
|
for ( let i = 1; i < 5; i++ ) {
|
||||||
|
expect( sorted[ sorted.length - i ].completed ).toEqual( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
|
@ -92,6 +92,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
|
||||||
- Dev: Delete all products when running product import tests, unskip previously skipped test. #6905
|
- Dev: Delete all products when running product import tests, unskip previously skipped test. #6905
|
||||||
- Enhancement: Add recommended payment methods in payment settings. #6760
|
- Enhancement: Add recommended payment methods in payment settings. #6760
|
||||||
- Enhancement: Add expand/collapse to extendable task list. #6910
|
- Enhancement: Add expand/collapse to extendable task list. #6910
|
||||||
|
- Enhancement: Add task hierarchy support to extended task list. #6916
|
||||||
- Fix: Disable the continue btn on OBW when requested are being made #6838
|
- Fix: Disable the continue btn on OBW when requested are being made #6838
|
||||||
- Fix: Event tracking for merchant email notes #6616
|
- Fix: Event tracking for merchant email notes #6616
|
||||||
- Fix: Use the store timezone to make time data requests #6632
|
- Fix: Use the store timezone to make time data requests #6632
|
||||||
|
|
|
@ -2,3 +2,10 @@ declare module '@woocommerce/e2e-utils';
|
||||||
declare module '@woocommerce/e2e-environment';
|
declare module '@woocommerce/e2e-environment';
|
||||||
declare module '@wordpress/data';
|
declare module '@wordpress/data';
|
||||||
declare module '@wordpress/compose';
|
declare module '@wordpress/compose';
|
||||||
|
declare module 'gridicons/dist/*' {
|
||||||
|
const value: React.ElementType< {
|
||||||
|
size?: 12 | 18 | 24 | 36 | 48 | 54 | 72;
|
||||||
|
onClick?: ( event: MouseEvent | KeyboardEvent ) => void;
|
||||||
|
} >;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue