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

feat: refactor move modal, use accessible with pagination #951

Merged
merged 8 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions cypress/e2e/item/home/home.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ describe('Home', () => {
);

// visit child
const { id: childChildId } = SAMPLE_ITEMS.items[2];
const { id: childChildId } = SAMPLE_ITEMS.items[3];
cy.goToItemInGrid(childChildId);

// expect no children
Expand Down Expand Up @@ -225,7 +225,7 @@ describe('Home', () => {
});

// visit child
const { id: childChildId } = SAMPLE_ITEMS.items[2];
const { id: childChildId } = SAMPLE_ITEMS.items[3];
cy.goToItemInList(childChildId);

// expect no children
Expand Down
6 changes: 2 additions & 4 deletions cypress/e2e/item/move/gridMoveItem.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
HOME_MODAL_ITEM_ID,
ITEM_MENU_MOVE_BUTTON_CLASS,
buildItemMenu,
buildItemMenuButtonId,
Expand Down Expand Up @@ -51,7 +50,7 @@ describe('Move Item in Grid', () => {

// move
const { id: movedItem } = SAMPLE_ITEMS.items[2];
const { id: toItem, path: toItemPath } = SAMPLE_ITEMS.items[3];
const { id: toItem, path: toItemPath } = SAMPLE_ITEMS.items[1];
moveItem({ id: movedItem, toItemPath });

cy.wait('@moveItems').then(({ request: { body, url } }) => {
Expand All @@ -70,8 +69,7 @@ describe('Move Item in Grid', () => {

// move
const { id: movedItem } = SAMPLE_ITEMS.items[2];
const toItem = HOME_MODAL_ITEM_ID;
moveItem({ id: movedItem, toItemPath: toItem });
moveItem({ id: movedItem, toItemPath: 'selectionModalMyGraasp' });

cy.wait('@moveItems').then(({ request: { body, url } }) => {
expect(body.parentId).to.equal(undefined);
Expand Down
57 changes: 47 additions & 10 deletions cypress/e2e/item/move/listMoveItem.cy.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
HOME_MODAL_ITEM_ID,
ITEM_MENU_MOVE_BUTTON_CLASS,
buildItemMenu,
buildItemMenuButtonId,
buildItemRowArrowId,
buildNavigationModalItemId,
} from '../../../../src/config/selectors';
import { ITEM_LAYOUT_MODES } from '../../../../src/enums';
import { SAMPLE_ITEMS } from '../../../fixtures/items';
import { TABLE_ITEM_RENDER_TIME } from '../../../support/constants';

const openMoveModal = ({ id: movedItemId }: { id: string }) => {
const menuSelector = `#${buildItemMenuButtonId(movedItemId)}`;
cy.wait(TABLE_ITEM_RENDER_TIME);
cy.get(menuSelector).click();
cy.get(
`#${buildItemMenu(movedItemId)} .${ITEM_MENU_MOVE_BUTTON_CLASS}`,
).click();
};

const moveItem = ({
id: movedItemId,
toItemPath,
Expand All @@ -18,12 +28,7 @@ const moveItem = ({
toItemPath: string;
rootId?: string;
}) => {
const menuSelector = `#${buildItemMenuButtonId(movedItemId)}`;
cy.wait(TABLE_ITEM_RENDER_TIME);
cy.get(menuSelector).click();
cy.get(
`#${buildItemMenu(movedItemId)} .${ITEM_MENU_MOVE_BUTTON_CLASS}`,
).click();
openMoveModal({ id: movedItemId });

cy.handleTreeMenu(toItemPath, rootId);
};
Expand Down Expand Up @@ -57,7 +62,7 @@ describe('Move Item in List', () => {

// move
const { id: movedItem } = SAMPLE_ITEMS.items[2];
const { id: toItem, path: toItemPath } = SAMPLE_ITEMS.items[3];
const { id: toItem, path: toItemPath } = SAMPLE_ITEMS.items[1];
moveItem({ id: movedItem, toItemPath });

cy.wait('@moveItems').then(({ request: { body, url } }) => {
Expand All @@ -66,6 +71,39 @@ describe('Move Item in List', () => {
});
});

it('cannot move inside self children', () => {
cy.setUpApi(SAMPLE_ITEMS);
const { id } = SAMPLE_ITEMS.items[0];

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

cy.switchMode(ITEM_LAYOUT_MODES.LIST);

const { id: movedItemId } = SAMPLE_ITEMS.items[2];
const parentId = SAMPLE_ITEMS.items[0].id;
const childId = SAMPLE_ITEMS.items[6].id;
openMoveModal({ id: movedItemId });
// parent is disabled
cy.get(`#${buildNavigationModalItemId(parentId)} button`).should(
'be.disabled',
);
cy.get(`#${buildNavigationModalItemId(parentId)}`).trigger('mouseover');
cy.get(`#${buildItemRowArrowId(parentId)}`).click();

// self is disabled
cy.get(`#${buildNavigationModalItemId(movedItemId)} button`).should(
'be.disabled',
);
cy.get(`#${buildNavigationModalItemId(movedItemId)}`).trigger('mouseover');
cy.get(`#${buildItemRowArrowId(movedItemId)}`).click();

// inner child is disabled
cy.get(`#${buildNavigationModalItemId(childId)} button`).should(
'be.disabled',
);
});

it('move item to Home', () => {
cy.setUpApi(SAMPLE_ITEMS);
const { id } = SAMPLE_ITEMS.items[0];
Expand All @@ -77,8 +115,7 @@ describe('Move Item in List', () => {

// move
const { id: movedItem } = SAMPLE_ITEMS.items[2];
const toItem = HOME_MODAL_ITEM_ID;
moveItem({ id: movedItem, toItemPath: toItem });
moveItem({ id: movedItem, toItemPath: 'selectionModalMyGraasp' });

cy.wait('@moveItems').then(({ request: { body, url } }) => {
expect(body.parentId).to.equal(undefined);
Expand Down
6 changes: 2 additions & 4 deletions cypress/e2e/item/move/listMoveMultiple.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
HOME_MODAL_ITEM_ID,
ITEMS_TABLE_MOVE_SELECTED_ITEMS_ID,
buildItemsTableRowIdAttribute,
} from '../../../../src/config/selectors';
Expand Down Expand Up @@ -55,7 +54,7 @@ describe('Move Items in List', () => {

// move
const itemIds = [SAMPLE_ITEMS.items[2].id, SAMPLE_ITEMS.items[4].id];
const { id: toItem, path: toItemPath } = SAMPLE_ITEMS.items[3];
const { id: toItem, path: toItemPath } = SAMPLE_ITEMS.items[1];
moveItems({ itemIds, toItemPath });

cy.wait('@moveItems').then(({ request: { body, url } }) => {
Expand All @@ -75,8 +74,7 @@ describe('Move Items in List', () => {

// move
const itemIds = [SAMPLE_ITEMS.items[2].id, SAMPLE_ITEMS.items[4].id];
const toItem = HOME_MODAL_ITEM_ID;
moveItems({ itemIds, toItemPath: toItem });
moveItems({ itemIds, toItemPath: 'selectionModalMyGraasp' });

cy.wait('@moveItems').then(({ request: { body, url } }) => {
expect(body.parentId).to.equal(undefined);
Expand Down
23 changes: 23 additions & 0 deletions cypress/fixtures/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ const sampleItems: DiscriminatedItem[] = [
hasThumbnail: false,
},
},
{
...DEFAULT_FOLDER_ITEM,
id: 'eef09f5a-5688-11eb-ae93-0242ac130003',
name: 'own_item_name7',
path: 'ecafbd2a_5688_11eb_ae93_0242ac130002.fdf09f5a_5688_11eb_ae93_0242ac130003.eef09f5a_5688_11eb_ae93_0242ac130003',
settings: {
hasThumbnail: false,
},
},
];
export const SAMPLE_ITEMS: ApiConfig = {
items: [
Expand Down Expand Up @@ -200,6 +209,20 @@ export const SAMPLE_ITEMS: ApiConfig = {
},
],
},
{
...sampleItems[6],
memberships: [
{
item: sampleItems[6],
permission: PermissionLevel.Admin,
member: MEMBERS.ANNA,
creator: MEMBERS.ANNA,
createdAt: '2021-08-11T12:56:36.834Z',
updatedAt: '2021-08-11T12:56:36.834Z',
id: '2dd4caf9-538a-317a-86d3-99432b223c12',
},
],
},
],
// memberships: [],
};
Expand Down
6 changes: 3 additions & 3 deletions cypress/support/commands/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
SHARE_ITEM_EMAIL_INPUT_ID,
SHARE_ITEM_SHARE_BUTTON_ID,
TREE_MODAL_CONFIRM_BUTTON_ID,
buildHomeModalItemID,
buildItemFormAppOptionId,
buildItemRowArrowId,
buildNavigationModalItemId,
buildPermissionOptionId,
buildTreeItemId,
} from '../../../src/config/selectors';
Expand Down Expand Up @@ -58,7 +58,7 @@ Cypress.Commands.add(
// click on the element
if (idx === array.length - 1) {
cy.wrap($tree)
.get(`#${buildHomeModalItemID(value)}`)
.get(`#${buildNavigationModalItemId(value)}`)
.first()
.click();
}
Expand All @@ -68,7 +68,7 @@ Cypress.Commands.add(
!$tree.find(`#${buildTreeItemId(array[idx + 1], treeRootId)}`).length
) {
cy.wrap($tree)
.get(`#${buildHomeModalItemID(value)}`)
.get(`#${buildNavigationModalItemId(value)}`)
.trigger('mouseover')
.get(`#${buildItemRowArrowId(value)}`)
.first()
Expand Down
2 changes: 1 addition & 1 deletion cypress/support/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const REDIRECTION_TIME = 500;
export const CAPTION_EDIT_PAUSE = 2000;

export const ROW_HEIGHT = 48;
export const TABLE_ITEM_RENDER_TIME = 8000;
export const TABLE_ITEM_RENDER_TIME = 7000;
export const TABLE_MEMBERSHIP_RENDER_TIME = 3000;
export const FIXTURES_THUMBNAILS_FOLDER = './thumbnails';
export const CHATBOX_LOADING_TIME = 5000;
Expand Down
75 changes: 60 additions & 15 deletions src/components/common/MoveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ import {
MoveButton as GraaspMoveButton,
} from '@graasp/ui';

import { validate } from 'uuid';

import { mutations } from '@/config/queryClient';
import { hooks, mutations } from '@/config/queryClient';
import { applyEllipsisOnLength, getDirectParentId } from '@/utils/item';

import { useBuilderTranslation } from '../../config/i18n';
import {
HOME_MODAL_ITEM_ID,
ITEM_MENU_MOVE_BUTTON_CLASS,
ITEM_MOVE_BUTTON_CLASS,
} from '../../config/selectors';
import { BUILDER } from '../../langs/constants';
import TreeModal, { TreeModalProps } from '../main/MoveTreeModal';
import { NavigationElement } from '../main/itemSelectionModal/Breadcrumbs';
import ItemSelectionModal, {
ItemSelectionModalProps,
} from '../main/itemSelectionModal/ItemSelectionModal';

const TITLE_MAX_NAME_LENGTH = 15;

type MoveButtonProps = {
itemIds: string[];
Expand All @@ -42,6 +45,8 @@ const MoveButton = ({
const [open, setOpen] = useState(false);
const [itemIds, setItemIds] = useState<string[]>(defaultItemsIds || []);

const { data: items } = hooks.useItems(itemIds);

const openMoveModal = (newItemIds: string[]) => {
setOpen(true);
setItemIds(newItemIds);
Expand All @@ -51,14 +56,11 @@ const MoveButton = ({
setOpen(false);
};

const onConfirm: TreeModalProps['onConfirm'] = (payload) => {
const onConfirm: ItemSelectionModalProps['onConfirm'] = (payload) => {
// change item's root id to null
const newPayload = {
...payload,
to:
payload.to && payload.to !== HOME_MODAL_ITEM_ID && validate(payload.to)
? payload.to
: undefined,
ids: itemIds,
to: payload,
};
moveItems(newPayload);
onClose();
Expand All @@ -73,6 +75,47 @@ const MoveButton = ({
onClick?.();
};

const isDisabled = (item: NavigationElement, homeId: string) => {
if (items?.data) {
// cannot move inside self and below
const moveInSelf = Object.values(items.data).some((i) =>
item.path.includes(i.path),
);

// cannot move in same direct parent
// todo: not opti because we only have the ids from the table
const directParentIds = Object.values(items.data).map((i) =>
getDirectParentId(i.path),
);
const moveInDirectParent = directParentIds.includes(item.id);

// cannot move to home if was already on home
let moveToHome = false;

if (items?.data) {
moveToHome =
item.id === homeId &&
!getDirectParentId(Object.values(items.data)[0].path);
}
return moveInSelf || moveInDirectParent || moveToHome;
}
return false;
};

const title = items
? translateBuilder(BUILDER.MOVE_ITEM_MODAL_TITLE, {
name: applyEllipsisOnLength(
Object.values(items.data)[0].name,
TITLE_MAX_NAME_LENGTH,
),
// -1 because we show one name
count: itemIds.length - 1,
})
: translateBuilder(BUILDER.MOVE_ITEM_MODAL_TITLE);

const buttonText = (name?: string) =>
translateBuilder(BUILDER.MOVE_BUTTON, { name, count: name ? 1 : 0 });

return (
<>
<GraaspMoveButton
Expand All @@ -85,13 +128,15 @@ const MoveButton = ({
iconClassName={ITEM_MOVE_BUTTON_CLASS}
/>

{itemIds.length > 0 && open && (
<TreeModal
{items?.data && open && (
<ItemSelectionModal
title={title}
isDisabled={isDisabled}
buttonText={buttonText}
onClose={onClose}
open={open}
itemIds={itemIds}
onConfirm={onConfirm}
title={BUILDER.MOVE_ITEM_MODAL_TITLE}
items={Object.values(items.data)}
/>
)}
</>
Expand Down
Loading