Skip to content

Commit

Permalink
fix: show empty accessible (#701)
Browse files Browse the repository at this point in the history
* fix: show empty accessible

* refactor: apply PR requested changes
  • Loading branch information
pyphilia authored Oct 9, 2024
1 parent 6ee09e8 commit 4f64c21
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 36 deletions.
13 changes: 11 additions & 2 deletions cypress/e2e/collection/summary.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PermissionLevel, formatDate, isChildOf } from '@graasp/sdk';
import { DEFAULT_LANG } from '@graasp/translations';

import { i18nConfig } from '../../../src/config/i18n';
import { buildCollectionRoute } from '../../../src/config/routes';
import {
CHILDREN_ITEMS_GRID_ID,
Expand All @@ -16,10 +17,13 @@ import {
TREE_MODAL_CONFIRM_BUTTON_ID,
buildContributorId,
} from '../../../src/config/selectors';
import LIBRARY from '../../../src/langs/constants';
import { buildPublicAndPrivateEnvironments } from '../../fixtures/environment';
import { PUBLISHED_ITEMS } from '../../fixtures/items';
import { CURRENT_USER, MEMBERS } from '../../fixtures/members';

const i18n = i18nConfig();

describe('Collection Summary', () => {
buildPublicAndPrivateEnvironments().forEach((environment) => {
it('Layout', { defaultCommandTimeout: 10000 }, () => {
Expand Down Expand Up @@ -137,6 +141,7 @@ describe('Collection Summary', () => {
describe('Signed in', () => {
beforeEach(() => {
cy.setUpApi({ currentMember: CURRENT_USER, items: PUBLISHED_ITEMS });
i18n.changeLanguage(CURRENT_USER.extra.lang);
});

it('copy current item and child', { defaultCommandTimeout: 10000 }, () => {
Expand All @@ -153,7 +158,9 @@ describe('Collection Summary', () => {
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(`button`)
.contains(i18n.t(LIBRARY.COPY_MODAL_MY_GRAASP_BREADCRUMB))
.click();
cy.get(`#${TREE_MODAL_CONFIRM_BUTTON_ID}`).click();

cy.wait('@copy').then(({ request: { url, body } }) => {
Expand All @@ -167,7 +174,9 @@ describe('Collection Summary', () => {
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(`button`)
.contains(i18n.t(LIBRARY.COPY_MODAL_MY_GRAASP_BREADCRUMB))
.click();
cy.get(`#${TREE_MODAL_CONFIRM_BUTTON_ID}`).click();

cy.wait('@copy').then(({ request: { url, body } }) => {
Expand Down
49 changes: 49 additions & 0 deletions cypress/e2e/home/copy.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { HOME_ROUTE } from '../../../src/config/routes';
import { buildCollectionCardCopyButtonId } from '../../../src/config/selectors';
import { PUBLISHED_ITEMS } from '../../fixtures/items';
import { CURRENT_USER } from '../../fixtures/members';

describe('Signed out', () => {
it(`Cannot copy`, () => {
cy.setUpApi({ items: PUBLISHED_ITEMS });
cy.visit(HOME_ROUTE);

cy.get(`#${buildCollectionCardCopyButtonId(PUBLISHED_ITEMS[0].id)}`).should(
'not.exist',
);
});
});

describe('Signed in', () => {
it(`Show copy button and show my graasp root and recent`, () => {
cy.setUpApi({
currentMember: CURRENT_USER,
items: PUBLISHED_ITEMS,
accessibleItems: PUBLISHED_ITEMS,
recentCollections: PUBLISHED_ITEMS,
});
cy.visit(HOME_ROUTE);
cy.get(
`#${buildCollectionCardCopyButtonId(PUBLISHED_ITEMS[0].id)}`,
).click();

// show root item menu in copy dialog
cy.get(`[role="dialog"] #root`).should('be.visible');
});

it(`Show copy button and my graasp for empty accessible`, () => {
cy.setUpApi({
currentMember: CURRENT_USER,
items: PUBLISHED_ITEMS,
accessibleItems: [],
recentCollections: PUBLISHED_ITEMS,
});
cy.visit(HOME_ROUTE);
cy.get(
`#${buildCollectionCardCopyButtonId(PUBLISHED_ITEMS[0].id)}`,
).click();

// show root item menu in copy dialog
cy.get(`[role="dialog"] #root`).should('be.visible');
});
});
10 changes: 9 additions & 1 deletion 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 {
mockCopyItems,
mockGetAccessibleItems,
mockGetAvatarUrl,
mockGetCategories,
Expand All @@ -16,6 +17,7 @@ import {
mockGetLikedItems,
mockGetMember,
mockGetPublishItemInformations,
mockGetRecentCollections,
mockSearch,
mockSignInRedirection,
mockSignOut,
Expand All @@ -28,6 +30,8 @@ Cypress.Commands.add(
members = Object.values(MEMBERS),
currentMember,
categories = SAMPLE_CATEGORIES,
recentCollections = [],
accessibleItems = [],
getCurrentMemberError = false,
getCategoriesError = false,
getItemCategoriesError = false,
Expand All @@ -41,7 +45,9 @@ Cypress.Commands.add(
} = {}) => {
const cachedMembers = JSON.parse(JSON.stringify(members));

mockGetAccessibleItems(items);
mockGetAccessibleItems(accessibleItems);

mockGetRecentCollections(recentCollections);

mockGetChildren({ items, currentMember });

Expand Down Expand Up @@ -72,5 +78,7 @@ Cypress.Commands.add(
mockSearch({ searchResultItems }, searchError);

mockGetLikedItems({ itemLikes }, getLikedItemsError);

mockCopyItems();
},
);
2 changes: 2 additions & 0 deletions cypress/support/cypress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ declare global {
interface Chainable {
setUpApi(arg: {
items: MockItem[];
recentCollections?: MockItem[];
accessibleItems?: MockItem[];
members?: MockMember[];
currentMember?: MockMember;
categories?: Category[];
Expand Down
29 changes: 29 additions & 0 deletions cypress/support/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@ export const mockGetAccessibleItems = (items: MockItem[]): void => {
).as('getAccessibleItems');
};

export const mockGetRecentCollections = (
recentCollections: MockItem[],
): void => {
cy.intercept(
{
method: HttpMethod.Get,
pathname: `/${ITEMS_ROUTE}/collections/recent`,
},
({ reply }) => {
reply(recentCollections);
},
).as('getRecentCollections');
};

export const mockGetCurrentMember = (
currentMember?: MockMember,
shouldThrowError = false,
Expand Down Expand Up @@ -535,3 +549,18 @@ export const mockGetPublishItemInformations = (items: MockItem[]): void => {
},
).as('getPublishItemInformations');
};

export const mockCopyItems = (): void => {
cy.intercept(
{
method: HttpMethod.Post,
url: new RegExp(`${API_HOST}/items/copy\\?id\\=`),
},
({ reply }) => {
// todo: do for all children
return reply({
statusCode: StatusCodes.ACCEPTED,
});
},
).as('copyItems');
};
5 changes: 4 additions & 1 deletion src/components/collection/CollectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Avatar } from '@graasp/ui';

import { useLibraryTranslation } from '../../config/i18n';
import { buildCollectionRoute } from '../../config/routes';
import { buildCollectionCardCopyButtonId } from '../../config/selectors';
import LIBRARY from '../../langs/constants';
import { ItemOrSearchedItem } from '../../utils/types';
import { QueryClientContext } from '../QueryClientContext';
Expand Down Expand Up @@ -205,7 +206,9 @@ export const CollectionCard = ({ collection, showIsContentTag }: Props) => {
</Stack>
<Box>
<DownloadButton id={id} />
{member?.id && <CopyButton itemId={id} />}
{member?.id && (
<CopyButton id={buildCollectionCardCopyButtonId(id)} itemId={id} />
)}
<CopyLinkButton itemId={collection.id} />
</Box>
</CardActions>
Expand Down
4 changes: 2 additions & 2 deletions src/components/collection/CollectionsGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const CollectionsGrid = ({
justifyContent="flex-start"
id={id}
>
{Array.from({ length: 4 }).map(() => (
<Grid xs={6} sm={4} md={3} lg={3} xl={2}>
{Array.from({ length: 4 }, (_, idx) => idx).map((idx) => (
<Grid key={idx} xs={6} sm={4} md={3} lg={3} xl={2}>
<Skeleton height={height} sx={{ transform: 'unset' }} />
</Grid>
))}
Expand Down
54 changes: 39 additions & 15 deletions src/components/collection/copyModal/AccessibleNavigationTree.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { useContext, useState } from 'react';

import { Alert, Skeleton } from '@mui/material';
import { Alert, Pagination, Skeleton, Stack } from '@mui/material';

import { ItemType, PermissionLevel } from '@graasp/sdk';
import { RowMenuProps, RowMenus } from '@graasp/ui';
import { type RowMenuProps, RowMenus } from '@graasp/ui';

import { useLibraryTranslation } from '../../../config/i18n';
import LIBRARY from '../../../langs/constants';
import { QueryClientContext } from '../../QueryClientContext';

interface AccessibleNavigationTreeProps {
isDisabled?: RowMenuProps['isDisabled'];
onClick: RowMenuProps['onClick'];
onNavigate: RowMenuProps['onNavigate'];
selectedId?: string;
Expand All @@ -18,13 +19,14 @@ interface AccessibleNavigationTreeProps {
const PAGE_SIZE = 10;

const AccessibleNavigationTree = ({
isDisabled,
onClick,
onNavigate,
selectedId,
}: AccessibleNavigationTreeProps): JSX.Element => {
const { t } = useLibraryTranslation();
const [page, setPage] = useState(1);
const { hooks } = useContext(QueryClientContext);
const [page, setPage] = useState(1);
const { t: translateLibrary } = useLibraryTranslation();
const { data: accessibleItems, isLoading } = hooks.useAccessibleItems(
{
permissions: [PermissionLevel.Write, PermissionLevel.Admin],
Expand All @@ -37,17 +39,35 @@ const AccessibleNavigationTree = ({
? Math.ceil(accessibleItems.totalCount / PAGE_SIZE)
: 0;

if (accessibleItems) {
if (accessibleItems?.data) {
return (
<RowMenus
onClick={onClick}
onNavigate={onNavigate}
selectedId={selectedId}
elements={accessibleItems?.data}
nbPages={nbPages}
page={page}
setPage={setPage}
/>
<Stack
height="100%"
flex={1}
direction="column"
justifyContent="space-between"
>
<Stack>
<RowMenus
elements={accessibleItems.data}
onNavigate={onNavigate}
selectedId={selectedId}
onClick={onClick}
isDisabled={isDisabled}
/>
</Stack>
<Stack direction="row" justifyContent="end">
{nbPages > 1 && (
<Pagination
sx={{ justifyContent: 'end' }}
size="small"
count={nbPages}
page={page}
onChange={(_, p) => setPage(p)}
/>
)}
</Stack>
</Stack>
);
}

Expand All @@ -61,7 +81,11 @@ const AccessibleNavigationTree = ({
);
}

return <Alert severity="error">{t(LIBRARY.UNEXPECTED_ERROR_MESSAGE)}</Alert>;
return (
<Alert severity="error">
{translateLibrary(LIBRARY.UNEXPECTED_ERROR_MESSAGE)}
</Alert>
);
};

export default AccessibleNavigationTree;
34 changes: 22 additions & 12 deletions src/components/collection/copyModal/RootNavigationTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { useContext } from 'react';
import { Alert, Skeleton, Typography } from '@mui/material';

import { ItemType, PermissionLevel } from '@graasp/sdk';
import { NavigationElement, RowMenuProps, RowMenus } from '@graasp/ui';
import {
type NavigationElement,
type RowMenuProps,
RowMenus,
} from '@graasp/ui';

import { useLibraryTranslation } from '../../../config/i18n';
import LIBRARY from '../../../langs/constants';
Expand All @@ -23,12 +27,15 @@ const RootNavigationTree = ({
onNavigate,
rootMenuItems,
selectedId,
}: RootNavigationTreeProps): JSX.Element => {
const { t } = useLibraryTranslation();

}: RootNavigationTreeProps): JSX.Element | null => {
const { hooks } = useContext(QueryClientContext);
// todo: to change with real recent items (most used)
const { data: recentItems, isLoading } = hooks.useAccessibleItems(
const { t: translateLibrary } = useLibraryTranslation();

const {
data: recentItems,
isLoading,
isSuccess,
} = hooks.useAccessibleItems(
// you can move into an item you have at least write permission
{
permissions: [PermissionLevel.Admin, PermissionLevel.Write],
Expand All @@ -37,23 +44,22 @@ const RootNavigationTree = ({
{ pageSize: 5 },
);

if (recentItems?.data?.length) {
if (isSuccess) {
return (
<>
<Typography color="darkgrey" variant="subtitle2">
{t(LIBRARY.COPY_MODAL_HOME_TITLE)}
{translateLibrary(LIBRARY.COPY_MODAL_HOME_TITLE)}
</Typography>
<RowMenus
elements={rootMenuItems}
onNavigate={onNavigate}
selectedId={selectedId}
onClick={onClick}
// root items cannot be disabled - but they are disabled by the button
/>
{recentItems && (
{Boolean(recentItems.data.length) && (
<>
<Typography color="darkgrey" variant="subtitle2">
{t(LIBRARY.COPY_MODAL_RECENT_TITLE)}
{translateLibrary(LIBRARY.COPY_MODAL_RECENT_TITLE)}
</Typography>
<RowMenus
elements={recentItems.data}
Expand All @@ -78,7 +84,11 @@ const RootNavigationTree = ({
);
}

return <Alert severity="error">{t(LIBRARY.UNEXPECTED_ERROR_MESSAGE)}</Alert>;
return (
<Alert severity="error">
{translateLibrary(LIBRARY.UNEXPECTED_ERROR_MESSAGE)}
</Alert>
);
};

export default RootNavigationTree;
Loading

0 comments on commit 4f64c21

Please sign in to comment.