Skip to content
This repository has been archived by the owner on Jan 23, 2025. It is now read-only.

Commit

Permalink
feat: add duplicate item action (#932)
Browse files Browse the repository at this point in the history
* feat: add duplicate item action

* build: remove name at duplicate item payload

* fix: renaming parent to parentsIDs

* fix: remove unused copy translation

* feat: add duplicate tests

* test: test parentId to be first parent of item at duplicate item

* test: refactor test for duplicate item

* test: add a test for parent id when duplicating item on home

* test: refactor duplicate tests for different views

---------

Co-authored-by: Kim Lan Phan Hoang <pyphilia@gmail.com>
  • Loading branch information
LinaYahya and pyphilia authored Jan 23, 2024
1 parent 8cf22c2 commit 175f173
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 2 deletions.
48 changes: 48 additions & 0 deletions cypress/e2e/item/duplicate/duplicateItem.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getParentsIdsFromPath } from '@/utils/item';

import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import ITEM_LAYOUT_MODES from '../../../../src/enums/itemLayoutModes';
import { SAMPLE_ITEMS } from '../../../fixtures/items';
import duplicateItem from '../../../support/actionsUtils';

describe('duplicate Item in Home', () => {
Object.values(ITEM_LAYOUT_MODES).forEach((view) => {
it(`duplicate item on Home in ${view} view`, () => {
cy.setUpApi(SAMPLE_ITEMS);
cy.visit(HOME_PATH);
cy.switchMode(view);

// duplicate
const { id: duplicateItemId } = SAMPLE_ITEMS.items[0];
duplicateItem({ id: duplicateItemId });

cy.wait('@copyItems').then(({ request: { url, body } }) => {
expect(url).to.contain(duplicateItemId);
// as we duplicate on home parentId will be undefined
expect(body.parentId).to.equal(undefined);
});
});
});
});
describe('duplicate Item in item', () => {
Object.values(ITEM_LAYOUT_MODES).forEach((view) => {
it(`duplicate item in item in ${view} view`, () => {
cy.setUpApi(SAMPLE_ITEMS);
const { id, path } = SAMPLE_ITEMS.items[0];
const parentsIds = getParentsIdsFromPath(path);

// go to children item
cy.visit(buildItemPath(id));
cy.switchMode(view);

// duplicate
const { id: duplicateItemId } = SAMPLE_ITEMS.items[2];
duplicateItem({ id: duplicateItemId });

cy.wait('@copyItems').then(({ request: { url, body } }) => {
expect(url).to.contain(duplicateItemId);
expect(body.parentId).to.equal(parentsIds[0]);
});
});
});
});
12 changes: 12 additions & 0 deletions cypress/support/actionsUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {
ITEM_MENU_DUPLICATE_BUTTON_CLASS,
buildItemMenu,
buildItemMenuButtonId,
} from '@/config/selectors';

const duplicateItem = ({ id }: { id: string }): void => {
cy.get(`#${buildItemMenuButtonId(id)}`).click();
cy.get(`#${buildItemMenu(id)} .${ITEM_MENU_DUPLICATE_BUTTON_CLASS}`).click();
};

export default duplicateItem;
27 changes: 27 additions & 0 deletions src/components/main/ItemMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useContext, useState } from 'react';

import FileCopyIcon from '@mui/icons-material/FileCopy';
import FlagIcon from '@mui/icons-material/Flag';
import LabelImportantIcon from '@mui/icons-material/LabelImportant';
import MoreVertIcon from '@mui/icons-material/MoreVert';
Expand All @@ -11,9 +12,13 @@ import MenuItem from '@mui/material/MenuItem';
import { DiscriminatedItem } from '@graasp/sdk';
import { ActionButton } from '@graasp/ui';

import { mutations } from '@/config/queryClient';
import { getParentsIdsFromPath } from '@/utils/item';

import { useBuilderTranslation } from '../../config/i18n';
import {
ITEM_MENU_BUTTON_CLASS,
ITEM_MENU_DUPLICATE_BUTTON_CLASS,
ITEM_MENU_FLAG_BUTTON_CLASS,
ITEM_MENU_SHORTCUT_BUTTON_CLASS,
buildItemMenu,
Expand Down Expand Up @@ -49,6 +54,7 @@ const ItemMenu = ({
CreateShortcutModalContext,
);
const { openModal: openFlagModal } = useContext(FlagItemModalContext);
const { mutate: copyItems } = mutations.useCopyItems();

const handleClick: IconButtonProps['onClick'] = (event) => {
setAnchorEl(event.currentTarget);
Expand All @@ -68,6 +74,18 @@ const ItemMenu = ({
handleClose();
};

const handleDuplicate = () => {
const parentsIds = getParentsIdsFromPath(item.path);
// get the close parent if not then undefined
const to =
parentsIds.length > 1 ? parentsIds[parentsIds.length - 2] : undefined;

const newPayload = {
ids: [item.id],
to,
};
copyItems(newPayload);
};
const renderEditorActions = () => {
if (!canEdit) {
return null;
Expand Down Expand Up @@ -116,6 +134,15 @@ const ItemMenu = ({
itemIds={[item.id]}
onClick={handleClose}
/>,
<MenuItem
onClick={handleDuplicate}
className={ITEM_MENU_DUPLICATE_BUTTON_CLASS}
>
<ListItemIcon>
<FileCopyIcon />
</ListItemIcon>
{translateBuilder(BUILDER.ITEM_MENU_DUPLICATE_MENU_ITEM)}
</MenuItem>,
];
};

Expand Down
1 change: 1 addition & 0 deletions src/config/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export const SHARED_ITEMS_ERROR_ALERT_ID = 'sharedItemsErrorAlert';
export const PUBLISHED_ITEMS_ERROR_ALERT_ID = 'publishedItemsErrorAlert';
export const FAVORITE_ITEMS_ERROR_ALERT_ID = 'favoriteItemsErrorAlert';
export const ITEM_MENU_SHORTCUT_BUTTON_CLASS = 'itemMenuShortcutButton';
export const ITEM_MENU_DUPLICATE_BUTTON_CLASS = 'itemMenuDuplicateButton';
export const ITEM_MENU_FAVORITE_BUTTON_CLASS = 'itemMenuFavoriteButton';
export const ITEM_MENU_FLAG_BUTTON_CLASS = 'itemMenuFlagButton';
export const buildFlagListItemId = (type: string): string =>
Expand Down
1 change: 1 addition & 0 deletions src/langs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export const BUILDER = {
ITEM_MEMBERSHIPS_TABLE_PERMISSION_HEADER:
'ITEM_MEMBERSHIPS_TABLE_PERMISSION_HEADER',
ITEM_MENU_CREATE_SHORTCUT_MENU_ITEM: 'ITEM_MENU_CREATE_SHORTCUT_MENU_ITEM',
ITEM_MENU_DUPLICATE_MENU_ITEM: 'ITEM_MENU_DUPLICATE_MENU_ITEM',
ITEM_MENU_FLAG_MENU_ITEM: 'ITEM_MENU_FLAG_MENU_ITEM',
ITEM_METADATA_CREATED_AT_TITLE: 'ITEM_METADATA_CREATED_AT_TITLE',
ITEM_METADATA_LINK_TITLE: 'ITEM_METADATA_LINK_TITLE',
Expand Down
5 changes: 3 additions & 2 deletions src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,11 @@
"SHORT_LINK_MAX_CHARS_ERROR": "The short link must not exceed {{data}} chars.",
"SHORT_LINK_INVALID_CHARS_ERROR": "The short link contains invalid chars ({{data}}).",
"SHORT_LINK_UNKNOWN_ERROR": "An unknown error occurred.",
"You can also find the items of this page in ''My Graasp''. This page will be unavailable soon.": "You can also find the items of this page in ''My Graasp''. This page ''Shared Items'' will be unavailable soon.",
"ITEM_MENU_DUPLICATE_MENU_ITEM": "Duplicate",
"EMPTY_FOLDER_CHILDREN_FOR_THIS_ITEM": "This item does not contain any folder.",
"TO_FOLDER": "to {{name}}",
"ROOT": "Root",
"ITEM_SELECTION_NAVIGATION_RECENT_ITEMS": "Recent Items",
"ITEM_SELECTION_NAVIGATION_PARENT": "Move above",
"You can also find the items of this page in ''My Graasp''. This page will be unavailable soon.": "You can also find the items of this page in ''My Graasp''. This page ''Shared Items'' will be unavailable soon."
"ITEM_SELECTION_NAVIGATION_PARENT": "Move above"
}

0 comments on commit 175f173

Please sign in to comment.