diff --git a/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json b/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json
index 8c10ea63e9..d376546018 100644
--- a/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json
+++ b/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json
@@ -350,8 +350,11 @@
"validation.mustBeChecked": "Doit être vérifié.",
"validation.validatePassword": "Un mot de passe doit contenir au moins 3 des éléments suivants: minuscules, majuscules, chiffres, caractères spéciaux.",
"wishlist.emptyListText": "Il n'y a actuellement aucun élément dans cette liste",
+ "wishlistEditFavoritesListDialog.title": "Modifier la liste des favoris",
"wishlistItem.addToCart": "Ajouter au panier",
"wishlistItem.addToCartError": "Un problème est survenu. Veuillez actualiser et réessayer.",
+ "wishlistListActionsDialog.title_initial": "Actions de liste",
+ "wishlistListActionsDialog.edit": "Liste d'édition",
"wishlistPage.disabledMessage": "Désolé, cette fonctionnalité a été désactivée.",
"wishlistPage.fetchErrorMessage": "Un problème est survenu. Veuillez actualiser et réessayer.",
"wishlistPage.headingText": "Listes de favoris",
diff --git a/packages/peregrine/lib/talons/WishlistPage/__tests__/__snapshots__/useActionMenu.spec.js.snap b/packages/peregrine/lib/talons/WishlistPage/__tests__/__snapshots__/useActionMenu.spec.js.snap
new file mode 100644
index 0000000000..ad684825d8
--- /dev/null
+++ b/packages/peregrine/lib/talons/WishlistPage/__tests__/__snapshots__/useActionMenu.spec.js.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`it handles editing a wishlist handleEditWishlist() runs the mutation with a thrown error 1`] = `
+Array [
+ [Error: Wish list "Favorites" already exists.],
+]
+`;
+
+exports[`it handles editing a wishlist handleEditWishlist() runs the mutation with the expected values 1`] = `
+Object {
+ "awaitRefetchQueries": true,
+ "refetchQueries": Array [
+ Object {
+ "query": [MockFunction getCustomerWishlistQuery],
+ },
+ ],
+ "variables": Object {
+ "name": "Birthday Party",
+ "visibility": "PRIVATE",
+ "wishlistId": "5",
+ },
+}
+`;
+
+exports[`returns correct shape 1`] = `
+Object {
+ "editFavoritesListIsOpen": false,
+ "formErrors": Array [
+ false,
+ ],
+ "handleActionMenuClick": [Function],
+ "handleEditWishlist": [Function],
+ "handleHideDialogs": [Function],
+ "handleShowEditFavorites": [Function],
+ "isEditInProgress": false,
+ "listActionsIsOpen": false,
+}
+`;
diff --git a/packages/peregrine/lib/talons/WishlistPage/__tests__/__snapshots__/useWishlist.spec.js.snap b/packages/peregrine/lib/talons/WishlistPage/__tests__/__snapshots__/useWishlist.spec.js.snap
index 8f108be4eb..e09e16d634 100644
--- a/packages/peregrine/lib/talons/WishlistPage/__tests__/__snapshots__/useWishlist.spec.js.snap
+++ b/packages/peregrine/lib/talons/WishlistPage/__tests__/__snapshots__/useWishlist.spec.js.snap
@@ -2,7 +2,6 @@
exports[`returns correct shape 1`] = `
Object {
- "handleActionMenuClick": [Function],
"handleContentToggle": [Function],
"isOpen": true,
}
diff --git a/packages/peregrine/lib/talons/WishlistPage/__tests__/useActionMenu.spec.js b/packages/peregrine/lib/talons/WishlistPage/__tests__/useActionMenu.spec.js
new file mode 100644
index 0000000000..7c933e91b0
--- /dev/null
+++ b/packages/peregrine/lib/talons/WishlistPage/__tests__/useActionMenu.spec.js
@@ -0,0 +1,147 @@
+import React from 'react';
+import { act } from 'react-test-renderer';
+
+import createTestInstance from '../../../util/createTestInstance';
+import { useActionMenu } from '../useActionMenu';
+import { useMutation } from '@apollo/client';
+
+jest.mock('@apollo/client', () => {
+ return {
+ ...jest.requireActual('@apollo/client'),
+ useMutation: jest.fn(() => [
+ jest.fn(),
+ {
+ error: false,
+ loading: false
+ }
+ ])
+ };
+});
+
+jest.mock('../wishlist.gql', () => ({
+ getCustomerWishlistQuery: jest.fn().mockName('getCustomerWishlistQuery'),
+ updateWishlistMutation: jest.fn().mockName('updateWishlistMutation')
+}));
+
+const Component = props => {
+ const talonProps = useActionMenu({ ...props });
+
+ return ;
+};
+
+const baseProps = {
+ id: '5',
+ mutations: { updateWishlistMutation: 'updateWishlistMutation' },
+ queries: {
+ getCustomerWishlistQuery: 'getCustomerWishlistQuery'
+ }
+};
+
+test('returns correct shape', () => {
+ const { root } = createTestInstance();
+ const { talonProps } = root.findByType('i').props;
+
+ expect(talonProps).toMatchSnapshot();
+});
+
+test('it handles opening list actions dialog', () => {
+ const talonPropsResult = [];
+
+ const { root } = createTestInstance();
+ talonPropsResult.push(root.findByType('i').props.talonProps);
+
+ act(() => {
+ talonPropsResult[0].handleActionMenuClick();
+ });
+
+ talonPropsResult.push(root.findByType('i').props.talonProps);
+
+ expect(talonPropsResult[0].editFavoritesListIsOpen).toBe(false);
+ expect(talonPropsResult[0].listActionsIsOpen).toBe(false);
+ expect(talonPropsResult[1].editFavoritesListIsOpen).toBe(false);
+ expect(talonPropsResult[1].listActionsIsOpen).toBe(true);
+});
+
+test('it handles hiding dialog', () => {
+ const talonPropsResult = [];
+
+ const { root } = createTestInstance();
+ talonPropsResult.push(root.findByType('i').props.talonProps);
+
+ act(() => {
+ talonPropsResult[0].handleHideDialogs();
+ });
+
+ talonPropsResult.push(root.findByType('i').props.talonProps);
+
+ expect(talonPropsResult[0].editFavoritesListIsOpen).toBe(false);
+ expect(talonPropsResult[0].listActionsIsOpen).toBe(false);
+ expect(talonPropsResult[1].editFavoritesListIsOpen).toBe(false);
+ expect(talonPropsResult[1].listActionsIsOpen).toBe(false);
+});
+
+test('it handles showing edit favorites dialog', () => {
+ const talonPropsResult = [];
+
+ const { root } = createTestInstance();
+ talonPropsResult.push(root.findByType('i').props.talonProps);
+
+ act(() => {
+ talonPropsResult[0].handleShowEditFavorites();
+ });
+
+ talonPropsResult.push(root.findByType('i').props.talonProps);
+
+ expect(talonPropsResult[0].editFavoritesListIsOpen).toBe(false);
+ expect(talonPropsResult[0].listActionsIsOpen).toBe(false);
+ expect(talonPropsResult[1].editFavoritesListIsOpen).toBe(true);
+ expect(talonPropsResult[1].listActionsIsOpen).toBe(false);
+});
+
+describe('it handles editing a wishlist', () => {
+ test('handleEditWishlist() runs the mutation with the expected values', () => {
+ const editWishlist = jest.fn();
+ useMutation.mockReturnValueOnce([editWishlist, {}]);
+
+ const tree = createTestInstance();
+
+ const { root } = tree;
+ const { talonProps } = root.findByType('i').props;
+
+ const { handleEditWishlist } = talonProps;
+
+ const data = {
+ name: 'Birthday Party',
+ visibility: 'PRIVATE',
+ wishlistId: 5
+ };
+
+ act(() => {
+ handleEditWishlist(data);
+ });
+
+ expect(editWishlist.mock.calls[0][0]).toMatchSnapshot();
+ });
+
+ test('handleEditWishlist() runs the mutation with a thrown error', () => {
+ const error = new Error('Wish list "Favorites" already exists.');
+ const editWishlist = jest.fn(() => {
+ throw error;
+ });
+ useMutation.mockReturnValueOnce([
+ editWishlist,
+ {
+ called: true,
+ error: error,
+ loading: false
+ }
+ ]);
+
+ const tree = createTestInstance();
+
+ const { root } = tree;
+ const { talonProps } = root.findByType('i').props;
+
+ expect(talonProps.formErrors).toMatchSnapshot();
+ });
+});
diff --git a/packages/peregrine/lib/talons/WishlistPage/useActionMenu.js b/packages/peregrine/lib/talons/WishlistPage/useActionMenu.js
new file mode 100644
index 0000000000..2a07047940
--- /dev/null
+++ b/packages/peregrine/lib/talons/WishlistPage/useActionMenu.js
@@ -0,0 +1,93 @@
+import { useCallback, useState } from 'react';
+import { useMutation } from '@apollo/client';
+import DEFAULT_OPERATIONS from './wishlist.gql';
+import mergeOperations from '../../util/shallowMerge';
+
+const dialogs = {
+ NONE: 1,
+ LIST_ACTIONS: 2,
+ EDIT_WISHLIST: 3
+};
+
+/**
+ * @function
+ *
+ * @param {{id}} props
+ * @param {ID} props.id - The unique identifier of the wish list
+ * @param {Object} props.operations - GraphQL operations to be run by the talon.
+ *
+ * @returns {ActionMenuProps}
+ */
+export const useActionMenu = (props = {}) => {
+ const { id } = props;
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
+ const { getCustomerWishlistQuery, updateWishlistMutation } = operations;
+ const [currentDialog, setCurrentDialog] = useState(dialogs.NONE);
+
+ const handleActionMenuClick = useCallback(() => {
+ setCurrentDialog(dialogs.LIST_ACTIONS);
+ }, []);
+
+ const handleHideDialogs = useCallback(() => {
+ setCurrentDialog(dialogs.NONE);
+ }, []);
+
+ const listActionsIsOpen = currentDialog === dialogs.LIST_ACTIONS;
+ const editFavoritesListIsOpen = currentDialog === dialogs.EDIT_WISHLIST;
+
+ const handleShowEditFavorites = useCallback(() => {
+ setCurrentDialog(dialogs.EDIT_WISHLIST);
+ }, []);
+
+ const [
+ updateWishlist,
+ { error: updateWishlistErrors, loading: isEditInProgress }
+ ] = useMutation(updateWishlistMutation);
+
+ const handleEditWishlist = useCallback(
+ async data => {
+ try {
+ await updateWishlist({
+ variables: {
+ name: data.name,
+ visibility: data.visibility,
+ wishlistId: id
+ },
+ refetchQueries: [{ query: getCustomerWishlistQuery }],
+ awaitRefetchQueries: true
+ });
+ setCurrentDialog(dialogs.NONE);
+ } catch (error) {
+ if (process.env.NODE_ENV !== 'production') {
+ console.error(error);
+ }
+ }
+ },
+ [getCustomerWishlistQuery, id, updateWishlist]
+ );
+
+ return {
+ editFavoritesListIsOpen,
+ formErrors: [updateWishlistErrors],
+ handleActionMenuClick,
+ handleEditWishlist,
+ handleHideDialogs,
+ handleShowEditFavorites,
+ isEditInProgress,
+ listActionsIsOpen
+ };
+};
+
+/**
+ * Props data to use when rendering the Wishlist Action Menu component.
+ *
+ * @typedef {Object} ActionMenuProps
+ *
+ * @property {Boolean} editFavoritesListIsOpen Whether the Edit Favorites List dialog is open
+ * @property {Function} handleActionMenuClick Callback to handle action menu clicks
+ * @property {Function} handleEditWishlist Callback to handle edit wishlist
+ * @property {Function} handleHideDialogs Callback to handle hiding all dialogs
+ * @property {Function} handleShowEditFavorites Callback to handle showing the Edit Favorites List Dialog
+ * @property {Boolean} isEditInProgress Whether the update wishlist operation is in progress
+ * @property {Boolean} listActionsIsOpen Whether the list actions dialog is open
+ */
diff --git a/packages/peregrine/lib/talons/WishlistPage/useWishlist.js b/packages/peregrine/lib/talons/WishlistPage/useWishlist.js
index 7906a6e5a8..41a89cda30 100644
--- a/packages/peregrine/lib/talons/WishlistPage/useWishlist.js
+++ b/packages/peregrine/lib/talons/WishlistPage/useWishlist.js
@@ -1,4 +1,4 @@
-import { useCallback, useState } from 'react';
+import { useState } from 'react';
/**
* @function
@@ -12,12 +12,7 @@ export const useWishlist = () => {
setIsOpen(currentValue => !currentValue);
};
- const handleActionMenuClick = useCallback(() => {
- console.log('To be handled by PWA-632');
- }, []);
-
return {
- handleActionMenuClick,
handleContentToggle,
isOpen
};
@@ -32,7 +27,6 @@ export const useWishlist = () => {
*
* @typedef {Object} WishListProps
*
- * @property {Function} handleActionMenuClick Callback to handle action menu clicks
* @property {Function} handleContentToggle Callback to handle list expand toggle
* @property {Boolean} isOpen Boolean which represents if the content is expanded or not
*/
diff --git a/packages/peregrine/lib/talons/WishlistPage/wishlist.gql.js b/packages/peregrine/lib/talons/WishlistPage/wishlist.gql.js
new file mode 100644
index 0000000000..934b996e04
--- /dev/null
+++ b/packages/peregrine/lib/talons/WishlistPage/wishlist.gql.js
@@ -0,0 +1,39 @@
+import { gql } from '@apollo/client';
+
+import { WishlistFragment } from './wishlistFragment.gql';
+
+export const GET_CUSTOMER_WISHLIST = gql`
+ query GetCustomerWishlist {
+ customer {
+ id
+ wishlists {
+ id
+ ...WishlistFragment
+ }
+ }
+ }
+ ${WishlistFragment}
+`;
+
+export const UPDATE_WISHLIST = gql`
+ mutation UpdateWishlist(
+ $name: String!
+ $visibility: WishlistVisibilityEnum!
+ $wishlistId: ID!
+ ) {
+ updateWishlist(
+ name: $name
+ visibility: $visibility
+ wishlistId: $wishlistId
+ ) {
+ name
+ uid
+ visibility
+ }
+ }
+`;
+
+export default {
+ getCustomerWishlistQuery: GET_CUSTOMER_WISHLIST,
+ updateWishlistMutation: UPDATE_WISHLIST
+};
diff --git a/packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js b/packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js
index 9ce3e685c3..c696bdfb9f 100644
--- a/packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js
+++ b/packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js
@@ -206,6 +206,7 @@ test('exposes all hooks and targets', async () => {
talons.Wishlist.WishlistButton.useWishlistButton.ee.wrapWith() wraps export "useWishlistButton.ee" from "Wishlist/WishlistButton/useWishlistButton.ee.js"
talons.Wishlist.WishlistDialog.CreateWishlistForm.useCreateWishlistForm.wrapWith() wraps export "useCreateWishlistForm" from "Wishlist/WishlistDialog/CreateWishlistForm/useCreateWishlistForm.js"
talons.Wishlist.WishlistDialog.useWishlistDialog.wrapWith() wraps export "useWishlistDialog" from "Wishlist/WishlistDialog/useWishlistDialog.js"
+ talons.WishlistPage.useActionMenu.wrapWith() wraps export "useActionMenu" from "WishlistPage/useActionMenu.js"
talons.WishlistPage.useCreateWishlist.wrapWith() wraps export "useCreateWishlist" from "WishlistPage/useCreateWishlist.js"
talons.WishlistPage.useWishlist.wrapWith() wraps export "useWishlist" from "WishlistPage/useWishlist.js"
talons.WishlistPage.useWishlistItem.wrapWith() wraps export "useWishlistItem" from "WishlistPage/useWishlistItem.js"
diff --git a/packages/venia-ui/i18n/en_US.json b/packages/venia-ui/i18n/en_US.json
index 6975492dfb..a6923ee25c 100644
--- a/packages/venia-ui/i18n/en_US.json
+++ b/packages/venia-ui/i18n/en_US.json
@@ -386,8 +386,11 @@
"wishlistConfirmRemoveProductDialog.title": "Remove Product from Wishlist",
"wishlistDialog.createButton": "+ Create a new list",
"wishlistDialog.title": "Add to Favorites",
+ "wishlistEditFavoritesListDialog.title": "Edit Favorites List",
"wishlistItem.addToCart": "Add to Cart",
"wishlistItem.addToCartError": "Something went wrong. Please refresh and try again.",
+ "wishlistListActionsDialog.title_initial": "List Actions",
+ "wishlistListActionsDialog.edit": "Edit List",
"wishlistMoreActionsDialog.copy": "Copy to",
"wishlistMoreActionsDialog.delete": "Remove",
"wishlistMoreActionsDialog.move": "Move to",
diff --git a/packages/venia-ui/lib/components/WishlistPage/__tests__/__snapshots__/actionMenu.ee.spec.js.snap b/packages/venia-ui/lib/components/WishlistPage/__tests__/__snapshots__/actionMenu.ee.spec.js.snap
new file mode 100644
index 0000000000..ddd523989b
--- /dev/null
+++ b/packages/venia-ui/lib/components/WishlistPage/__tests__/__snapshots__/actionMenu.ee.spec.js.snap
@@ -0,0 +1,60 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+
+
+
+
+
+`;
diff --git a/packages/venia-ui/lib/components/WishlistPage/__tests__/__snapshots__/wishlist.spec.js.snap b/packages/venia-ui/lib/components/WishlistPage/__tests__/__snapshots__/wishlist.spec.js.snap
index 3371c364d0..7e352ab3e9 100644
--- a/packages/venia-ui/lib/components/WishlistPage/__tests__/__snapshots__/wishlist.spec.js.snap
+++ b/packages/venia-ui/lib/components/WishlistPage/__tests__/__snapshots__/wishlist.spec.js.snap
@@ -18,49 +18,17 @@ exports[`render closed with items 1`] = `
- Private
+ Public
@@ -121,49 +90,17 @@ exports[`render open with no items 1`] = `
- Private
+ Public
-
+