Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix copy modal #604

Merged
merged 3 commits into from
Apr 28, 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
69 changes: 68 additions & 1 deletion cypress/e2e/collection/summary.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@ import { DEFAULT_LANG } from '@graasp/translations';
import { buildCollectionRoute } from '../../../src/config/routes';
import {
CHILDREN_ITEMS_GRID_ID,
CHILD_CARD_COPY_BUTTON_ID,
ITEM_SUMMARY_TITLE_ID,
LIBRARY_ACTION_GROUP_BUTTON_ID,
LIBRARY_ACTION_GROUP_COPY_BUTTON_ID,
LIBRARY_ACTION_GROUP_POP_UP_BUTTONS_ID,
LIKE_COLLECTION_NOT_LOGGED_ID,
SUMMARY_AUTHOR_CONTAINER_ID,
SUMMARY_CREATED_AT_CONTAINER_ID,
SUMMARY_LAST_UPDATE_CONTAINER_ID,
TREE_MODAL_CONFIRM_BUTTON_ID,
buildContributorId,
} from '../../../src/config/selectors';
import { buildPublicAndPrivateEnvironments } from '../../fixtures/environment';
import { PUBLISHED_ITEMS } from '../../fixtures/items';
import { COMPLETE_MEMBERS, MEMBERS } from '../../fixtures/members';
import {
COMPLETE_MEMBERS,
CURRENT_USER,
MEMBERS,
} from '../../fixtures/members';

describe('Collection Summary', () => {
buildPublicAndPrivateEnvironments().forEach((environment) => {
Expand Down Expand Up @@ -114,4 +123,62 @@ describe('Collection Summary', () => {
});
});
});

describe('Signed out', () => {
it('should not show copy button', { defaultCommandTimeout: 10000 }, () => {
cy.setUpApi({ items: PUBLISHED_ITEMS });

const item = PUBLISHED_ITEMS[1];
cy.visit(buildCollectionRoute(item.id));

cy.get(`#${LIBRARY_ACTION_GROUP_BUTTON_ID}`).click();
cy.get(`#${LIBRARY_ACTION_GROUP_POP_UP_BUTTONS_ID}`)
.find('button')
.should('have.length', 2);
});
});

describe('Signed in', () => {
beforeEach(() => {
cy.setUpApi({ currentMember: CURRENT_USER, items: PUBLISHED_ITEMS });
});

it('copy current item and child', { defaultCommandTimeout: 10000 }, () => {
cy.intercept({
method: 'POST',
url: '/items/copy*',
}).as('copy');

const item = PUBLISHED_ITEMS[0];
cy.visit(buildCollectionRoute(item.id));

// copy parent item on home
cy.get(`#${LIBRARY_ACTION_GROUP_BUTTON_ID}`).click();
cy.get(`#${LIBRARY_ACTION_GROUP_COPY_BUTTON_ID}`).click();

cy.get(`#${TREE_MODAL_CONFIRM_BUTTON_ID}`).should('be.disabled');
cy.get(`button`).contains('My Graasp').click();
cy.get(`#${TREE_MODAL_CONFIRM_BUTTON_ID}`).click();

cy.wait('@copy').then(({ request: { url, body } }) => {
expect(url).to.contain(item.id);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
expect(body.to).to.be.undefined;
});

// copy child item on home
const child = PUBLISHED_ITEMS[2];
cy.get(`#${CHILD_CARD_COPY_BUTTON_ID}`).click();

cy.get(`#${TREE_MODAL_CONFIRM_BUTTON_ID}`).should('be.disabled');
cy.get(`button`).contains('My Graasp').click();
cy.get(`#${TREE_MODAL_CONFIRM_BUTTON_ID}`).click();

cy.wait('@copy').then(({ request: { url, body } }) => {
expect(url).to.contain(child.id);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
expect(body.to).to.be.undefined;
});
});
});
});
4 changes: 2 additions & 2 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ITEM_LIKES } from '../fixtures/itemLikes';
import { PUBLISHED_ITEMS } from '../fixtures/items';
import { MEMBERS } from '../fixtures/members';
import {
mockGetAccessibleItems,
mockGetAvatarUrl,
mockGetCategories,
mockGetChildren,
Expand All @@ -15,7 +16,6 @@ import {
mockGetLikedItems,
mockGetMember,
mockGetMembers,
mockGetOwnItems,
mockGetPublishItemInformations,
mockSearch,
mockSignInRedirection,
Expand All @@ -42,7 +42,7 @@ Cypress.Commands.add(
} = {}) => {
const cachedMembers = JSON.parse(JSON.stringify(members));

mockGetOwnItems({ items, currentMember });
mockGetAccessibleItems(items);

mockGetChildren({ items, currentMember });

Expand Down
38 changes: 18 additions & 20 deletions cypress/support/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const {
SIGN_OUT_ROUTE,
buildGetMembersRoute,
buildGetCategoriesRoute,
GET_OWN_ITEMS_ROUTE,
SEARCH_PUBLISHED_ITEMS_ROUTE,
} = API_ROUTES;

Expand Down Expand Up @@ -70,29 +69,28 @@ export const redirectionReply = {
body: null,
};

export const mockGetOwnItems = ({
items,
currentMember,
}: {
items: MockItem[];
currentMember?: MockMember;
}) => {
export const mockGetAccessibleItems = (items: MockItem[]): void => {
cy.intercept(
{
method: DEFAULT_GET.method,
url: `${API_HOST}/${GET_OWN_ITEMS_ROUTE}`,
method: HttpMethod.Get,
pathname: `/${ITEMS_ROUTE}/accessible`,
},
({ reply }) => {
if (!currentMember) {
return reply({ statusCode: StatusCodes.UNAUTHORIZED, body: null });
}
const own = items.filter(
({ creator, path }) =>
creator?.id === currentMember.id && !path.includes('.'),
);
return reply(own);
({ url, reply }) => {
const params = new URL(url).searchParams;

const page = parseInt(params.get('page') ?? '1', 10);
const pageSize = parseInt(params.get('pageSize') ?? '10', 10);

// warning: we don't check memberships
const root = items.filter((i) => !i.path.includes('.'));

// todo: filter
pyphilia marked this conversation as resolved.
Show resolved Hide resolved

const result = root.slice((page - 1) * pageSize, page * pageSize);

reply({ data: result, totalCount: root.length });
},
).as('getOwnItems');
).as('getAccessibleItems');
};

export const mockGetCurrentMember = (
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@graasp/query-client": "3.4.1",
"@graasp/sdk": "4.4.0",
"@graasp/translations": "1.25.3",
"@graasp/ui": "4.15.0",
"@graasp/ui": "4.16.0",
"@mui/icons-material": "5.15.14",
"@mui/lab": "5.0.0-alpha.169",
"@mui/material": "5.15.14",
Expand Down
10 changes: 7 additions & 3 deletions src/components/collection/ChildrenCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import { ItemIcon, Thumbnail } from '@graasp/ui';
import { COLLECTION_CARD_BORDER_RADIUS } from '../../config/cssStyles';
import { useLibraryTranslation } from '../../config/i18n';
import { buildCollectionRoute } from '../../config/routes';
import { CHILD_CARD_COPY_BUTTON_ID } from '../../config/selectors';
import LIBRARY from '../../langs/constants';
import { QueryClientContext } from '../QueryClientContext';
import CopyButton from './CopyButton';
import CopyLinkButton from './CopyLinkButton';
import DownloadButton from './DownloadButton';

Expand Down Expand Up @@ -73,9 +75,9 @@ export const SubItemCard: React.FC<SubItemCardProps> = ({
thumbnail,
subtext,
}) => {
// const { hooks } = useContext(QueryClientContext);
const { hooks } = useContext(QueryClientContext);

// const { data: member } = hooks.useCurrentMember();
const { data: member } = hooks.useCurrentMember();

const { name, id } = item;

Expand All @@ -87,7 +89,9 @@ export const SubItemCard: React.FC<SubItemCardProps> = ({
href={link}
actions={
<>
{/* {member?.id && <CopyButton id={id} />} */}
{member?.id && (
<CopyButton id={CHILD_CARD_COPY_BUTTON_ID} itemId={id} />
)}
<CopyLinkButton itemId={item.id} />
<DownloadButton id={id} />
</>
Expand Down
5 changes: 3 additions & 2 deletions src/components/collection/CollectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { QueryClientContext } from '../QueryClientContext';
import CardMediaComponent from '../common/CardMediaComponent';
import { StyledCard } from '../common/StyledCard';
import ContentDescription from './ContentDescription';
import CopyButton from './CopyButton';
import CopyLinkButton from './CopyLinkButton';
import DownloadButton from './DownloadButton';

Expand Down Expand Up @@ -117,7 +118,7 @@ export const CollectionCard = ({ collection, showIsContentTag }: Props) => {
} = collection;
const { t } = useLibraryTranslation();
const { hooks } = useContext(QueryClientContext);
// const { data: member } = hooks.useCurrentMember();
const { data: member } = hooks.useCurrentMember();
const { data: authorAvatarUrl, isLoading: isLoadingAvatar } =
hooks.useAvatarUrl({
id: creator?.id,
Expand Down Expand Up @@ -204,7 +205,7 @@ export const CollectionCard = ({ collection, showIsContentTag }: Props) => {
</Stack>
<Box>
<DownloadButton id={id} />
{/* {member?.id && <CopyButton id={id} />} */}
{member?.id && <CopyButton itemId={id} />}
<CopyLinkButton itemId={collection.id} />
</Box>
</CardActions>
Expand Down
31 changes: 15 additions & 16 deletions src/components/collection/CopyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';

import { useLibraryTranslation } from '../../config/i18n';
import {
TREE_MODAL_MY_ITEMS_ID,
TREE_MODAL_SHARED_ITEMS_ID,
} from '../../config/selectors';
import LIBRARY from '../../langs/constants';
import { QueryClientContext } from '../QueryClientContext';
import TreeModal from './TreeModal';
import ItemSelectionModal from './copyModal/ItemSelectionModal';

export const useCopyAction = (id?: string) => {
const { t } = useLibraryTranslation();
Expand All @@ -36,29 +32,30 @@ export const useCopyAction = (id?: string) => {
}

// todo: set notifier for copy
const copy = ({ to }: { to: string }) => {
const copy = (to: string | undefined) => {
// remove loading icon on callback
// do not set parent if it is root
const payload: Parameters<typeof copyItems>[0] = {
ids: [id],
};

// if the location to copy the item is MyItems or SharedItems root, then set the payload.to argument to be undefined
payload.to = [TREE_MODAL_MY_ITEMS_ID, TREE_MODAL_SHARED_ITEMS_ID].includes(
to,
)
? undefined
: to;
payload.to = to;

copyItems(payload);
};

const treeModal = user?.id && id && (
<TreeModal
<ItemSelectionModal
title={t(LIBRARY.COPY_BUTTON_MODAL_TITLE)}
open={showTreeModal}
onClose={() => setShowTreeModal(false)}
onConfirm={copy}
itemId={id}
buttonText={() => t(LIBRARY.COPY_MODAL_SUBMIT_BUTTON)}
isDisabled={(items, item) => {
// cannot copy inside itself
return items.some((i) => item.path.includes(i.path));
}}
/>
);

Expand All @@ -70,13 +67,14 @@ export const useCopyAction = (id?: string) => {
};

type Props = {
id: string;
itemId: string;
id?: string;
};

const CopyButton = ({ id }: Props) => {
const CopyButton = ({ id, itemId }: Props) => {
const { t } = useLibraryTranslation();

const { treeModal, isCopying, startCopy } = useCopyAction(id);
const { treeModal, isCopying, startCopy } = useCopyAction(itemId);

const renderButton = () => {
if (isCopying) {
Expand All @@ -92,6 +90,7 @@ const CopyButton = ({ id }: Props) => {
return (
<Tooltip title={t(LIBRARY.COPY_BUTTON_TOOLTIP)}>
<IconButton
id={id}
onClick={startCopy}
aria-label={t(LIBRARY.COPY_BUTTON_TOOLTIP)}
>
Expand Down
Loading
Loading