* 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:
louwie17 2021-05-05 14:43:46 -03:00 committed by GitHub
parent 68324b0e20
commit 99c6044e55
10 changed files with 157 additions and 31 deletions

View File

@ -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 || [] ),

View File

@ -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;
} }

View File

@ -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;
}
} }

View File

@ -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

View File

@ -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>

View File

@ -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;
} }

View File

@ -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(),
} ) ); } ) );

View File

@ -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 );
}
} );
} );

View File

@ -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

View File

@ -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;
}