Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Update TaskList design #4434

Merged
merged 22 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d9a59b9
Update task list to closer match new designs.
jeffstieler May 12, 2020
ea20f8a
Update TaskList placeholder to match the new design.
jeffstieler May 15, 2020
f29053e
Hook up store details task click.
jeffstieler May 15, 2020
ae59bd8
Don't show "skip" prompt for task list on new home screen.
jeffstieler May 15, 2020
c566e6f
Update time estimates for tasks.
jeffstieler May 20, 2020
2dc3a7c
Add progress indicator to TaskList.
jeffstieler May 20, 2020
b1621ef
Use null container for store details task.
jeffstieler May 20, 2020
c01ac84
Fix progress bar styling.
jeffstieler May 21, 2020
e5d8ecf
Just use card menu for TaskList dismissal.
jeffstieler May 21, 2020
40474a6
Don't show TaskList on analytics overview if homepage feature is enab…
jeffstieler May 21, 2020
7059c6d
Initial refactor of TaskList to use WP card.
jeffstieler May 21, 2020
3a4e7ff
Style TaskList items and card header.
jeffstieler May 22, 2020
78deea4
Style "hide" button for TaskList.
jeffstieler May 22, 2020
f062ae9
Don't show TaskList after it's completion.
jeffstieler May 22, 2020
ad9a439
Don't show TaskList placeholder when options are still loading.
jeffstieler May 22, 2020
73a9ace
Fix border radius on progress bar.
jeffstieler May 22, 2020
bf2c6f8
Fix TaskList header style with homepage feature disabled.
jeffstieler May 22, 2020
98b3d2b
Remove defunct function.
jeffstieler May 22, 2020
db9fb2a
Remove unnecessary import.
jeffstieler May 22, 2020
d584d1a
Some minor color tweaks (#4436)
jameskoster May 25, 2020
98b8aa9
Preload task list completed option, show placeholder when loading.
jeffstieler May 25, 2020
4f30b28
Fix alignment of "hide this" button.
jeffstieler May 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions client/dashboard/customizable.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,10 @@ class CustomizableDashboard extends Component {
render() {
const { query, taskListHidden, taskListComplete } = this.props;

const isTaskListEnabled = isOnboardingEnabled() && ! taskListHidden;
const isTaskListEnabled =
isOnboardingEnabled() &&
! taskListHidden &&
! window.wcAdminFeatures.homepage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeffstieler if #4418 is merged first this should be updated to ! getFeatureFlag( 'homepage' );


const isDashboardShown =
! isTaskListEnabled || ( ! query.task && taskListComplete );
Expand All @@ -311,7 +314,7 @@ class CustomizableDashboard extends Component {
<Fragment>
{ isTaskListEnabled && (
<Suspense fallback={ <Spinner /> }>
<TaskList query={ query } inline={ isDashboardShown } />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated nit: Maybe rename isDashboardShown to isTaskListEnabled so it matches the use Home page

<TaskList query={ query } />
</Suspense>
) }
{ isDashboardShown && this.renderDashboardReports() }
Expand Down
14 changes: 14 additions & 0 deletions client/dashboard/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,17 @@
padding-bottom: 10px;
}
}

.woocommerce-task-dashboard__body {
.woocommerce-task-dashboard__container {
.woocommerce-task-card {
.components-card__header.is-size-large {
padding-bottom: 12px;

.woocommerce-card__menu {
margin-top: 8px;
}
}
}
}
}
20 changes: 12 additions & 8 deletions client/homepage/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export const Layout = ( props ) => {
};
}, [] );

const { query, requestingTaskList, taskListHidden } = props;
const isTaskListEnabled = taskListHidden === false;
const { query, requestingTaskList, taskListComplete, taskListHidden } = props;
const isTaskListEnabled = taskListHidden === false && ! taskListComplete;
const isDashboardShown = ! isTaskListEnabled || ! query.task;

const renderColumns = () => {
Expand Down Expand Up @@ -101,15 +101,12 @@ export const Layout = ( props ) => {

const renderTaskList = () => {
if ( requestingTaskList ) {
return <TaskListPlaceholder />;
return;
}

return (
<Suspense fallback={ <Spinner /> }>
<TaskList
query={ query }
inline
/>
<Suspense fallback={ <TaskListPlaceholder /> }>
<TaskList query={ query } />
</Suspense>
);
};
Expand All @@ -133,6 +130,10 @@ Layout.propTypes = {
* If the task list option is being fetched.
*/
requestingTaskList: PropTypes.bool.isRequired,
/**
* If the task list has been completed.
*/
taskListComplete: PropTypes.bool,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theres a proptype violation here, "Invalid prop taskListComplete of type string supplied to Layout, expected boolean." I found it when the task list wasn't visible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unable to reproduce this - can you provide steps?

/**
* If the task list is hidden.
*/
Expand All @@ -152,13 +153,16 @@ export default compose(

if ( isOnboardingEnabled() ) {
const options = getOptions( [
'woocommerce_task_list_complete',
'woocommerce_task_list_hidden',
] );

return {
requestingTaskList: isGetOptionsRequesting( [
'woocommerce_task_list_complete',
'woocommerce_task_list_hidden',
] ),
taskListComplete: Boolean( get( options, [ 'woocommerce_task_list_complete' ] ) ),
taskListHidden: get( options, [ 'woocommerce_task_list_hidden' ] ) === 'yes',
};
}
Expand Down
34 changes: 31 additions & 3 deletions client/homepage/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jest.mock( 'homepage/stats-overview', () => jest.fn().mockReturnValue( null ) );
jest.mock( 'task-list', () => jest.fn().mockReturnValue( '[TaskList]' ) );

describe( 'Homepage Layout', () => {
it( 'should show TaskList placeholder when loading', () => {
it( 'should not show TaskList placeholder when loading', () => {
const { container } = render(
<Layout
requestingTaskList
Expand All @@ -20,8 +20,8 @@ describe( 'Homepage Layout', () => {
/>
);

const placeholder = container.querySelector( '.woocommerce-task-card.is-loading' );
expect( placeholder ).not.toBeNull();
const placeholder = container.querySelector( '.woocommerce-task-card' );
expect( placeholder ).toBeNull();
} );

it( 'should show TaskList inline', async () => {
Expand Down Expand Up @@ -61,4 +61,32 @@ describe( 'Homepage Layout', () => {
const taskList = await screen.findByText( '[TaskList]' )
expect( taskList ).toBeDefined();
} );

it( 'should not show TaskList when user has hidden', () => {
render(
<Layout
requestingTaskList={ false }
taskListComplete={ false }
taskListHidden
query={ {} }
/>
);

const taskList = screen.queryByText( '[TaskList]' )
expect( taskList ).toBeNull();
} );

it( 'should not show TaskList when it is complete', () => {
render(
<Layout
requestingTaskList={ false }
taskListComplete
taskListHidden={ false }
query={ {} }
/>
);

const taskList = screen.queryByText( '[TaskList]' )
expect( taskList ).toBeNull();
} );
} );
162 changes: 53 additions & 109 deletions client/task-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,24 @@ import { Component, cloneElement, Fragment } from '@wordpress/element';
import { get, isEqual } from 'lodash';
import { compose } from '@wordpress/compose';
import classNames from 'classnames';
import { Snackbar, Icon, Button, Modal } from '@wordpress/components';
import {
Button,
Card,
CardBody,
CardHeader,
Modal,
} from '@wordpress/components';
import { withDispatch } from '@wordpress/data';
import {
Icon,
check,
chevronRight,
} from '@wordpress/icons';

/**
* WooCommerce dependencies
*/
import { Card, List, MenuItem, EllipsisMenu } from '@woocommerce/components';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

import { H, List, EllipsisMenu } from '@woocommerce/components';
import { updateQueryString } from '@woocommerce/navigation';
import { PLUGINS_STORE_NAME } from '@woocommerce/data';

Expand Down Expand Up @@ -187,56 +198,20 @@ class TaskDashboard extends Component {
return currentTask;
}

renderPrompt() {
if ( this.props.promptShown ) {
return null;
}

return (
<Snackbar className="woocommerce-task-card__prompt">
<div className="woocommerce-task-card__prompt-pointer" />
<div className="woocommerce-task-card__prompt-content">
<span>
{ __( 'Is this card useful?', 'woocommerce-admin' ) }
</span>
<div className="woocommerce-task-card__prompt-actions">
<Button
isLink
onClick={ () => this.hideTaskCard( 'hide_card' ) }
>
{ __( 'No, hide it', 'woocommerce-admin' ) }
</Button>

<Button isLink onClick={ () => this.keepTaskCard() }>
{ __( 'Yes, keep it', 'woocommerce-admin' ) }
</Button>
</div>
</div>
</Snackbar>
);
}

renderMenu() {
return (
<EllipsisMenu
label={ __( 'Task List Options', 'woocommerce-admin' ) }
renderContent={ () => (
<div className="woocommerce-task-card__section-controls">
<MenuItem
isClickable
onInvoke={ () =>
this.hideTaskCard( 'remove_card' )
}
>
<Icon
icon={ 'trash' }
label={ __( 'Remove block' ) }
/>
{ __( 'Remove this card', 'woocommerce-admin' ) }
</MenuItem>
</div>
) }
/>
<div className="woocommerce-card__menu woocommerce-card__header-item">
<EllipsisMenu
label={ __( 'Task List Options', 'woocommerce-admin' ) }
renderContent={ () => (
<div className="woocommerce-task-card__section-controls">
<Button onClick={ () => this.hideTaskCard( 'remove_card' ) }>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another unrelated comment: It always bugged me that clicking this didn't cause the task list to disappear immediately or at least show a spinner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK to handle this in a follow up?

{ __( 'Hide this', 'woocommerce-admin' ) }
</Button>
</div>
) }
/>
</div>
);
}

Expand Down Expand Up @@ -313,52 +288,23 @@ class TaskDashboard extends Component {
);
}

onSkipStoreSetup = () => {
const completedTaskKeys = this.getTasks()
.filter( ( x ) => x.completed )
.map( ( x ) => x.key );

recordEvent( 'tasklist_skip', {
completed_tasks_count: completedTaskKeys.length,
completed_tasks: completedTaskKeys,
reason: 'skip',
} );

this.props.updateOptions( {
woocommerce_task_list_hidden: 'yes',
} );
};

renderSkipActions() {
return (
<div className="skip-actions">
<Button
isLink
className="is-secondary"
onClick={ this.onSkipStoreSetup }
>
{ __( 'Skip store setup', 'woocommerce-admin' ) }
</Button>
</div>
);
}

render() {
const { inline, query } = this.props;
const { query } = this.props;
const { isCartModalOpen, isWelcomeModalOpen } = this.state;
const currentTask = this.getCurrentTask();
const listTasks = this.getTasks().map( ( task ) => {
task.className = classNames(
task.completed ? 'is-complete' : null,
task.className
);
task.before = task.completed ? (
<i className="material-icons-outlined">check_circle</i>
) : (
<i className="material-icons-outlined">{ task.icon }</i>
task.before = (
<div className="woocommerce-task__icon">
{ task.completed && <Icon icon={ check } /> }
</div>
);
task.after = (
<i className="material-icons-outlined">chevron_right</i>
task.after = ( task.time
? <span className="woocommerce-task-estimated-time">{ task.time }</span>
: <Icon icon={ chevronRight } />
);

if ( ! task.onClick ) {
Expand All @@ -367,6 +313,13 @@ class TaskDashboard extends Component {

return task;
} );
const numCompleteTasks = listTasks.filter( task => task.completed ).length;
const progressBarClass = classNames(
'woocommerce-task-card__progress-bar',
{
'completed': listTasks.length === numCompleteTasks,
}
);

return (
<Fragment>
Expand All @@ -377,23 +330,21 @@ class TaskDashboard extends Component {
} )
) : (
<Fragment>
<Card
className="woocommerce-task-card"
title={ __(
'Set up your store and start selling',
'woocommerce-admin'
) }
description={ __(
'Below you’ll find a list of the most important steps to get your store up and running.',
'woocommerce-admin'
) }
menu={ inline && this.renderMenu() }
>
<List items={ listTasks } />
<Card size="large" className="woocommerce-task-card">
<progress
className={ progressBarClass }
max={ listTasks.length }
value={ numCompleteTasks }
/>
<CardHeader>
<H>{ __( 'Store setup', 'woocommerce-admin' ) }</H>
{ this.renderMenu() }
</CardHeader>
<CardBody>
<List items={ listTasks } />
</CardBody>
</Card>
{ inline && this.renderPrompt() }
{ isWelcomeModalOpen && this.renderWelcomeModal() }
{ this.renderSkipActions() }
</Fragment>
) }
</div>
Expand All @@ -419,16 +370,10 @@ export default compose(
const profileItems = getProfileItems();

const options = getOptions( [
'woocommerce_task_list_prompt_shown',
'woocommerce_task_list_welcome_modal_dismissed',
'woocommerce_task_list_hidden',
'woocommerce_task_list_tracked_completed_tasks',
] );
const promptShown = get(
options,
[ 'woocommerce_task_list_prompt_shown' ],
false
);
const modalDismissed = get(
options,
[ 'woocommerce_task_list_welcome_modal_dismissed' ],
Expand Down Expand Up @@ -459,7 +404,6 @@ export default compose(
return {
modalDismissed,
profileItems,
promptShown,
taskListPayments,
isJetpackConnected: isJetpackConnected(),
incompleteTasks,
Expand Down
Loading