Home Screen - Small refactor to tasks extensibility (https://github.com/woocommerce/woocommerce-admin/pull/5833)
* Added unregister extended task This commit adds a method to unregister extended task when the plugin is deactivated * Refactored task lists handling This commit refactors the task lists handling * Added default task type This commit adds a default task type * Fixed method comment * Moved method to group objects to lib This commit moves method to group objects to `lib` * Refactored getUngroupedTasks method Co-authored-by: Fernando Marichal <contacto@fernandomarichal.com>
This commit is contained in:
parent
484e945460
commit
dc02c47499
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Returns an object with items grouped by the sent key.
|
||||
*
|
||||
* @param {Array} array array of objects.
|
||||
* @param {string} key the object prop that will be used to group elements.
|
||||
* @param {string} defaultKey if the key is not found in the object, it will use this value.
|
||||
* @return {Object} Object that contains the grouped elements.
|
||||
*/
|
||||
export const groupListOfObjectsBy = (
|
||||
array,
|
||||
key,
|
||||
defaultKey = 'undefined'
|
||||
) => {
|
||||
if ( array && Array.isArray( array ) && array.length ) {
|
||||
if ( ! key ) {
|
||||
return array;
|
||||
}
|
||||
return array.reduce( ( result, currentValue ) => {
|
||||
if ( ! currentValue[ key ] ) {
|
||||
currentValue[ key ] = defaultKey;
|
||||
}
|
||||
( result[ currentValue[ key ] ] =
|
||||
result[ currentValue[ key ] ] || [] ).push( currentValue );
|
||||
return result;
|
||||
}, {} );
|
||||
}
|
||||
return {};
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { groupListOfObjectsBy } from '../index.js';
|
||||
|
||||
describe( 'groupListOfObjectsBy', () => {
|
||||
const objectList = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Object name 1',
|
||||
type: 'type1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Object name 2',
|
||||
type: 'type1',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Object name 3',
|
||||
type: 'type1',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Object name 4',
|
||||
type: 'type2',
|
||||
},
|
||||
];
|
||||
|
||||
it( 'handles different params', () => {
|
||||
// Using these params should return an empty object.
|
||||
const emptyObject = groupListOfObjectsBy();
|
||||
const otherEmptyObject = groupListOfObjectsBy( [] );
|
||||
const anotherEmptyObject = groupListOfObjectsBy( 'not an array' );
|
||||
expect( emptyObject ).toMatchObject( {} );
|
||||
expect( otherEmptyObject ).toMatchObject( {} );
|
||||
expect( anotherEmptyObject ).toMatchObject( {} );
|
||||
|
||||
// Not sending a key to use for grouping the elements will return the sent list
|
||||
const ungroupedList = groupListOfObjectsBy( objectList );
|
||||
expect( ungroupedList.length ).toBe( 4 );
|
||||
} );
|
||||
|
||||
it( 'groups objects by type', () => {
|
||||
const { type1, type2 } = groupListOfObjectsBy( objectList, 'type' );
|
||||
expect( type1.length ).toBe( 3 );
|
||||
expect( type2.length ).toBe( 1 );
|
||||
} );
|
||||
|
||||
it( 'groups objects without type', () => {
|
||||
const objectWithoutType = {
|
||||
id: '5',
|
||||
name: 'Object name 5',
|
||||
};
|
||||
objectList.push( objectWithoutType );
|
||||
const { type1, type2 } = groupListOfObjectsBy(
|
||||
objectList,
|
||||
'type',
|
||||
'type2'
|
||||
);
|
||||
expect( type1.length ).toBe( 3 );
|
||||
expect( type2.length ).toBe( 2 );
|
||||
} );
|
||||
|
||||
it( 'groups objects with a new type', () => {
|
||||
const objectWithNewType = {
|
||||
id: '6',
|
||||
name: 'Object name 4',
|
||||
type: 'type3',
|
||||
};
|
||||
objectList.push( objectWithNewType );
|
||||
const { type1, type2, type3 } = groupListOfObjectsBy(
|
||||
objectList,
|
||||
'type',
|
||||
'type2'
|
||||
);
|
||||
expect( type1.length ).toBe( 3 );
|
||||
expect( type2.length ).toBe( 2 );
|
||||
expect( type3.length ).toBe( 1 );
|
||||
} );
|
||||
} );
|
|
@ -60,14 +60,6 @@ export class TaskDashboard extends Component {
|
|||
} );
|
||||
}
|
||||
|
||||
groupBy( array, key ) {
|
||||
return array.reduce( ( result, currentValue ) => {
|
||||
( result[ currentValue[ key ] ] =
|
||||
result[ currentValue[ key ] ] || [] ).push( currentValue );
|
||||
return result;
|
||||
}, {} );
|
||||
}
|
||||
|
||||
toggleCartModal() {
|
||||
const { isCartModalOpen } = this.state;
|
||||
|
||||
|
@ -90,34 +82,29 @@ export class TaskDashboard extends Component {
|
|||
} = this.props;
|
||||
const { isCartModalOpen } = this.state;
|
||||
const allTasks = this.getAllTasks();
|
||||
const { extension: extensionTasks, setup: setupTasks } = this.groupBy(
|
||||
allTasks,
|
||||
'type'
|
||||
);
|
||||
const { extension: extensionTasks, setup: setupTasks } = allTasks;
|
||||
|
||||
return (
|
||||
<>
|
||||
{ setupTasks && ! isSetupTaskListHidden && (
|
||||
<TaskList
|
||||
allTasks={ allTasks }
|
||||
dismissedTasks={ dismissedTasks }
|
||||
isTaskListComplete={ isTaskListComplete }
|
||||
isExtended={ false }
|
||||
query={ query }
|
||||
specificTasks={ setupTasks }
|
||||
tasks={ allTasks }
|
||||
trackedCompletedTasks={ trackedCompletedTasks }
|
||||
/>
|
||||
) }
|
||||
{ extensionTasks && ! isExtendedTaskListHidden && (
|
||||
<TaskList
|
||||
allTasks={ allTasks }
|
||||
dismissedTasks={ dismissedTasks }
|
||||
isExtendedTaskListComplete={
|
||||
isExtendedTaskListComplete
|
||||
}
|
||||
isExtended={ true }
|
||||
query={ query }
|
||||
specificTasks={ extensionTasks }
|
||||
tasks={ allTasks }
|
||||
trackedCompletedTasks={ trackedCompletedTasks }
|
||||
/>
|
||||
) }
|
||||
|
|
|
@ -54,6 +54,20 @@ export class TaskList extends Component {
|
|||
this.possiblyTrackCompletedTasks();
|
||||
}
|
||||
|
||||
getUngroupedTasks() {
|
||||
const { tasks: groupedTasks } = this.props;
|
||||
return Object.values( groupedTasks ).flat();
|
||||
}
|
||||
|
||||
getSpecificTasks() {
|
||||
const { isExtended, tasks: groupedTasks } = this.props;
|
||||
const { extension, setup } = groupedTasks;
|
||||
if ( isExtended ) {
|
||||
return extension;
|
||||
}
|
||||
return setup;
|
||||
}
|
||||
|
||||
possiblyCompleteTaskList() {
|
||||
const {
|
||||
isExtended,
|
||||
|
@ -61,8 +75,8 @@ export class TaskList extends Component {
|
|||
isExtendedTaskListComplete,
|
||||
updateOptions,
|
||||
} = this.props;
|
||||
const isSetupTaskListInComplete = ! isExtended && ! isTaskListComplete;
|
||||
const isExtendedTaskListInComplete =
|
||||
const isSetupTaskListIncomplete = ! isExtended && ! isTaskListComplete;
|
||||
const isExtendedTaskListIncomplete =
|
||||
isExtended && ! isExtendedTaskListComplete;
|
||||
const taskListToComplete = isExtended
|
||||
? { woocommerce_extended_task_list_complete: 'yes' }
|
||||
|
@ -73,7 +87,7 @@ export class TaskList extends Component {
|
|||
|
||||
if (
|
||||
! this.getIncompleteTasks().length &&
|
||||
( isSetupTaskListInComplete || isExtendedTaskListInComplete )
|
||||
( isSetupTaskListIncomplete || isExtendedTaskListIncomplete )
|
||||
) {
|
||||
updateOptions( {
|
||||
...taskListToComplete,
|
||||
|
@ -88,8 +102,8 @@ export class TaskList extends Component {
|
|||
}
|
||||
|
||||
getIncompleteTasks() {
|
||||
const { dismissedTasks, specificTasks } = this.props;
|
||||
return specificTasks.filter(
|
||||
const { dismissedTasks } = this.props;
|
||||
return this.getSpecificTasks().filter(
|
||||
( task ) =>
|
||||
task.visible &&
|
||||
! task.completed &&
|
||||
|
@ -169,8 +183,9 @@ export class TaskList extends Component {
|
|||
}
|
||||
|
||||
getVisibleTasks( type ) {
|
||||
const { allTasks, specificTasks, dismissedTasks } = this.props;
|
||||
const tasks = type === 'all' ? allTasks : specificTasks;
|
||||
const { dismissedTasks } = this.props;
|
||||
const tasks =
|
||||
type === 'all' ? this.getUngroupedTasks() : this.getSpecificTasks();
|
||||
|
||||
return tasks.filter(
|
||||
( task ) => task.visible && ! dismissedTasks.includes( task.key )
|
||||
|
@ -235,9 +250,11 @@ export class TaskList extends Component {
|
|||
}
|
||||
|
||||
getCurrentTask() {
|
||||
const { specificTasks, query } = this.props;
|
||||
const { query } = this.props;
|
||||
const { task } = query;
|
||||
const currentTask = specificTasks.find( ( s ) => s.key === task );
|
||||
const currentTask = this.getSpecificTasks().find(
|
||||
( s ) => s.key === task
|
||||
);
|
||||
|
||||
if ( ! currentTask ) {
|
||||
return null;
|
||||
|
|
|
@ -22,6 +22,7 @@ import Shipping from './tasks/shipping';
|
|||
import Tax from './tasks/tax';
|
||||
import Payments from './tasks/payments';
|
||||
import { installActivateAndConnectWcpay } from './tasks/payments/methods';
|
||||
import { groupListOfObjectsBy } from '../lib/lists';
|
||||
|
||||
export function recordTaskViewEvent(
|
||||
taskName,
|
||||
|
@ -247,10 +248,9 @@ export function getAllTasks( {
|
|||
type: 'setup',
|
||||
},
|
||||
];
|
||||
|
||||
return applyFilters(
|
||||
'woocommerce_admin_onboarding_task_list',
|
||||
tasks,
|
||||
query
|
||||
return groupListOfObjectsBy(
|
||||
applyFilters( 'woocommerce_admin_onboarding_task_list', tasks, query ),
|
||||
'type',
|
||||
'extension'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,49 +17,52 @@ jest.mock( '../tasks' );
|
|||
|
||||
describe( 'TaskDashboard and TaskList', () => {
|
||||
afterEach( () => jest.clearAllMocks() );
|
||||
|
||||
const tasks = [
|
||||
{
|
||||
key: 'optional',
|
||||
title: 'This task is optional',
|
||||
container: null,
|
||||
completed: false,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: true,
|
||||
type: 'setup',
|
||||
},
|
||||
{
|
||||
key: 'required',
|
||||
title: 'This task is required',
|
||||
container: null,
|
||||
completed: false,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: false,
|
||||
type: 'setup',
|
||||
},
|
||||
{
|
||||
key: 'completed',
|
||||
title: 'This task is completed',
|
||||
container: null,
|
||||
completed: true,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: true,
|
||||
type: 'setup',
|
||||
},
|
||||
{
|
||||
key: 'extension',
|
||||
title: 'This task is an extension',
|
||||
container: null,
|
||||
completed: false,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: true,
|
||||
type: 'extension',
|
||||
},
|
||||
];
|
||||
const tasks = {
|
||||
setup: [
|
||||
{
|
||||
key: 'optional',
|
||||
title: 'This task is optional',
|
||||
container: null,
|
||||
completed: false,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: true,
|
||||
type: 'setup',
|
||||
},
|
||||
{
|
||||
key: 'required',
|
||||
title: 'This task is required',
|
||||
container: null,
|
||||
completed: false,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: false,
|
||||
type: 'setup',
|
||||
},
|
||||
{
|
||||
key: 'completed',
|
||||
title: 'This task is completed',
|
||||
container: null,
|
||||
completed: true,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: true,
|
||||
type: 'setup',
|
||||
},
|
||||
],
|
||||
extension: [
|
||||
{
|
||||
key: 'extension',
|
||||
title: 'This task is an extension',
|
||||
container: null,
|
||||
completed: false,
|
||||
visible: true,
|
||||
time: '1 minute',
|
||||
isDismissable: true,
|
||||
type: 'extension',
|
||||
},
|
||||
],
|
||||
};
|
||||
const shorterTasksList = [
|
||||
{
|
||||
key: 'completed-1',
|
||||
|
@ -141,8 +144,7 @@ describe( 'TaskDashboard and TaskList', () => {
|
|||
query={ {} }
|
||||
trackedCompletedTasks={ shorterTasksList }
|
||||
updateOptions={ updateOptions }
|
||||
allTasks={ shorterTasksList }
|
||||
specificTasks={ shorterTasksList }
|
||||
tasks={ { setup: shorterTasksList } }
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -167,8 +169,7 @@ describe( 'TaskDashboard and TaskList', () => {
|
|||
query={ {} }
|
||||
trackedCompletedTasks={ shorterTasksList }
|
||||
updateOptions={ updateOptions }
|
||||
allTasks={ shorterTasksList }
|
||||
specificTasks={ shorterTasksList }
|
||||
tasks={ { setup: shorterTasksList } }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
@ -191,8 +192,7 @@ describe( 'TaskDashboard and TaskList', () => {
|
|||
query={ {} }
|
||||
trackedCompletedTasks={ shorterTasksList }
|
||||
updateOptions={ updateOptions }
|
||||
allTasks={ shorterTasksList }
|
||||
specificTasks={ shorterTasksList }
|
||||
tasks={ { extension: shorterTasksList } }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
@ -208,14 +208,13 @@ describe( 'TaskDashboard and TaskList', () => {
|
|||
act( () => {
|
||||
render(
|
||||
<TaskList
|
||||
allTasks={ tasks }
|
||||
tasks={ tasks }
|
||||
dismissedTasks={ [] }
|
||||
isTaskListComplete={ true }
|
||||
profileItems={ {} }
|
||||
query={ {} }
|
||||
trackedCompletedTasks={ shorterTasksList }
|
||||
updateOptions={ updateOptions }
|
||||
specificTasks={ shorterTasksList }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
|
|
@ -87,7 +87,6 @@ addFilter(
|
|||
time: __( '2 minutes', 'woocommerce-admin' ),
|
||||
isDismissable: true,
|
||||
onDismiss: () => console.log( "The task was dismissed" ),
|
||||
type: 'extension'
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
use Automattic\WooCommerce\Admin\Features\Onboarding;
|
||||
|
||||
/**
|
||||
* Register the JS.
|
||||
* Register the task list item and the JS.
|
||||
*/
|
||||
function add_task_register_script() {
|
||||
|
||||
|
@ -41,3 +41,12 @@ function add_task_register_script() {
|
|||
do_action( 'add_woocommerce_extended_task_list_item', 'woocommerce_admin_add_task_example_name' );
|
||||
}
|
||||
add_action( 'admin_enqueue_scripts', 'add_task_register_script' );
|
||||
|
||||
/**
|
||||
* Unregister task list item.
|
||||
*/
|
||||
function pluginprefix_deactivate() {
|
||||
do_action( 'remove_woocommerce_extended_task_list_item', 'woocommerce_admin_add_task_example_name' );
|
||||
}
|
||||
|
||||
register_deactivation_hook( __FILE__, 'pluginprefix_deactivate' );
|
||||
|
|
|
@ -47,6 +47,7 @@ class OnboardingTasks {
|
|||
add_action( 'add_option_woocommerce_task_list_tracked_completed_tasks', array( $this, 'track_task_completion' ), 10, 2 );
|
||||
add_action( 'update_option_woocommerce_task_list_tracked_completed_tasks', array( $this, 'track_task_completion' ), 10, 2 );
|
||||
add_action( 'add_woocommerce_extended_task_list_item', array( $this, 'add_extended_task_list_item' ), 10, 2 );
|
||||
add_action( 'remove_woocommerce_extended_task_list_item', array( $this, 'remove_extended_task_list_item' ), 10, 2 );
|
||||
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
|
@ -394,6 +395,22 @@ class OnboardingTasks {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the extended task list.
|
||||
*
|
||||
* @param mixed $task_name Task name to remove.
|
||||
*/
|
||||
public static function remove_extended_task_list_item( $task_name ) {
|
||||
if ( $task_name ) {
|
||||
$extended_tasks_list_items = get_option( 'woocommerce_extended_task_list_items', array() );
|
||||
if ( in_array( $task_name, $extended_tasks_list_items, true ) ) {
|
||||
array_push( $extended_tasks_list_items, $task_name );
|
||||
$tasks_list_items = array_diff( $extended_tasks_list_items, array( $task_name ) );
|
||||
update_option( 'woocommerce_extended_task_list_items', $tasks_list_items );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records an event for individual task completion.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue