From b9081ec4be1e035161a0b05718eea0e03bf8a25c Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 24 Apr 2024 17:36:24 +0200 Subject: [PATCH] fix: fix copy modal --- cypress/e2e/collection/summary.cy.ts | 69 +++++- cypress/support/commands.ts | 4 +- cypress/support/server.ts | 40 ++-- package.json | 2 +- src/components/collection/ChildrenCard.tsx | 10 +- src/components/collection/CollectionCard.tsx | 5 +- src/components/collection/CopyButton.tsx | 30 +-- src/components/collection/TreeModal.tsx | 88 -------- .../collection/TreeModalContent.tsx | 96 -------- .../copyModal/AccessibleNavigationTree.tsx | 65 ++++++ .../copyModal/ChildrenNavigationTree.tsx | 63 ++++++ .../copyModal/ItemSelectionModal.tsx | 210 ++++++++++++++++++ .../copyModal/RootNavigationTree.tsx | 87 ++++++++ .../summary/SummaryActionButtons.tsx | 33 ++- src/components/layout/MainWrapper.tsx | 1 + src/components/pages/Home.tsx | 14 +- src/config/selectors.ts | 6 + src/langs/ar.json | 2 - src/langs/constants.ts | 7 +- src/langs/de.json | 2 - src/langs/en.json | 10 +- src/langs/es.json | 2 - src/langs/fr.json | 8 +- src/langs/it.json | 2 - yarn.lock | 125 ++++++----- 25 files changed, 651 insertions(+), 330 deletions(-) delete mode 100644 src/components/collection/TreeModal.tsx delete mode 100644 src/components/collection/TreeModalContent.tsx create mode 100644 src/components/collection/copyModal/AccessibleNavigationTree.tsx create mode 100644 src/components/collection/copyModal/ChildrenNavigationTree.tsx create mode 100644 src/components/collection/copyModal/ItemSelectionModal.tsx create mode 100644 src/components/collection/copyModal/RootNavigationTree.tsx diff --git a/cypress/e2e/collection/summary.cy.ts b/cypress/e2e/collection/summary.cy.ts index d6a2c435..971c9e19 100644 --- a/cypress/e2e/collection/summary.cy.ts +++ b/cypress/e2e/collection/summary.cy.ts @@ -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) => { @@ -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; + }); + }); + }); }); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 92f5474c..4aa85cc1 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -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, @@ -15,7 +16,6 @@ import { mockGetLikedItems, mockGetMember, mockGetMembers, - mockGetOwnItems, mockGetPublishItemInformations, mockSearch, mockSignInRedirection, @@ -42,7 +42,7 @@ Cypress.Commands.add( } = {}) => { const cachedMembers = JSON.parse(JSON.stringify(members)); - mockGetOwnItems({ items, currentMember }); + mockGetAccessibleItems(items); mockGetChildren({ items, currentMember }); diff --git a/cypress/support/server.ts b/cypress/support/server.ts index 96647971..dd9dc528 100644 --- a/cypress/support/server.ts +++ b/cypress/support/server.ts @@ -41,7 +41,6 @@ const { SIGN_OUT_ROUTE, buildGetMembersRoute, buildGetCategoriesRoute, - GET_OWN_ITEMS_ROUTE, SEARCH_PUBLISHED_ITEMS_ROUTE, } = API_ROUTES; @@ -70,29 +69,30 @@ 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); + + // as { page: number; pageSize: number }; + + // warning: we don't check memberships + const root = items.filter((i) => !i.path.includes('.')); + + // todo: filter + + const result = root.slice((page - 1) * pageSize, page * pageSize); + + reply({ data: result, totalCount: root.length }); }, - ).as('getOwnItems'); + ).as('getAccessibleItems'); }; export const mockGetCurrentMember = ( diff --git a/package.json b/package.json index 81ea780b..c1c2041d 100644 --- a/package.json +++ b/package.json @@ -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": "github:graasp/graasp-ui#row-menu", "@mui/icons-material": "5.15.14", "@mui/lab": "5.0.0-alpha.169", "@mui/material": "5.15.14", diff --git a/src/components/collection/ChildrenCard.tsx b/src/components/collection/ChildrenCard.tsx index 3c8b5fc6..be188682 100644 --- a/src/components/collection/ChildrenCard.tsx +++ b/src/components/collection/ChildrenCard.tsx @@ -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'; @@ -73,9 +75,9 @@ export const SubItemCard: React.FC = ({ thumbnail, subtext, }) => { - // const { hooks } = useContext(QueryClientContext); + const { hooks } = useContext(QueryClientContext); - // const { data: member } = hooks.useCurrentMember(); + const { data: member } = hooks.useCurrentMember(); const { name, id } = item; @@ -87,7 +89,9 @@ export const SubItemCard: React.FC = ({ href={link} actions={ <> - {/* {member?.id && } */} + {member?.id && ( + + )} diff --git a/src/components/collection/CollectionCard.tsx b/src/components/collection/CollectionCard.tsx index d31fb61a..bc2d15f3 100644 --- a/src/components/collection/CollectionCard.tsx +++ b/src/components/collection/CollectionCard.tsx @@ -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'; @@ -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, @@ -204,7 +205,7 @@ export const CollectionCard = ({ collection, showIsContentTag }: Props) => { - {/* {member?.id && } */} + {member?.id && } diff --git a/src/components/collection/CopyButton.tsx b/src/components/collection/CopyButton.tsx index 77f5ee0c..20084648 100644 --- a/src/components/collection/CopyButton.tsx +++ b/src/components/collection/CopyButton.tsx @@ -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(); @@ -36,7 +32,7 @@ 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[0] = { @@ -44,21 +40,23 @@ export const useCopyAction = (id?: string) => { }; // 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 && ( - 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)); + }} /> ); @@ -70,13 +68,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) { @@ -92,6 +91,7 @@ const CopyButton = ({ id }: Props) => { return ( diff --git a/src/components/collection/TreeModal.tsx b/src/components/collection/TreeModal.tsx deleted file mode 100644 index f5d15405..00000000 --- a/src/components/collection/TreeModal.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { useState } from 'react'; - -import Button from '@mui/material/Button'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; - -import { useLibraryTranslation } from '../../config/i18n'; -import { - COPY_MODAL_TITLE_ID, - TREE_MODAL_CONFIRM_BUTTON_ID, -} from '../../config/selectors'; -import LIBRARY from '../../langs/constants'; -import TreeModalContent from './TreeModalContent'; - -type TreeModalProps = { - onConfirm: (args: { to: string }) => void; - onClose: (args: { id: string | null; open: boolean }) => void; - title: string; - open?: boolean; -}; - -/* - TODO: factor out the behavior of the component? It is shared with - https://github.com/graasp/graasp-builder/blob/8af8a794b687abdd3d0e3ec83a6d16eb72e1e5f6/src/components/main/TreeModal.js - */ -const TreeModal = ({ - open = false, - title, - onClose, - onConfirm, -}: TreeModalProps) => { - const { t } = useLibraryTranslation(); - - const [selectedId, setSelectedId] = useState(); - - const handleClose = () => { - onClose({ id: null, open: false }); - }; - - const onClickConfirm = () => { - if (selectedId) { - onConfirm({ to: selectedId }); - handleClose(); - } - }; - - const onTreeItemSelect = (nodeId: string) => { - if (selectedId === nodeId) { - setSelectedId(undefined); - } else { - setSelectedId(nodeId); - } - }; - - return ( - - {title} - - - - - - - - - ); -}; - -export default TreeModal; diff --git a/src/components/collection/TreeModalContent.tsx b/src/components/collection/TreeModalContent.tsx deleted file mode 100644 index d1530182..00000000 --- a/src/components/collection/TreeModalContent.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { useContext } from 'react'; - -import { CircularProgress, Typography } from '@mui/material'; - -import { DiscriminatedItem, ItemType } from '@graasp/sdk'; -import { DynamicTreeView } from '@graasp/ui'; - -import { TREE_VIEW_MAX_WIDTH } from '../../config/constants'; -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'; - -type TreeModalContentProps = { - open: boolean; - selectedId?: string; - onTreeItemSelect: (nodeId: string) => void; -}; - -const TreeModalContent = ({ - open, - selectedId, - onTreeItemSelect, -}: TreeModalContentProps) => { - const { t } = useLibraryTranslation(); - - const { hooks } = useContext(QueryClientContext); - const { useItem, useOwnItems, useChildren, useSharedItems } = hooks; - const { - data: ownItems, - isLoading: isOwnItemsLoading, - isSuccess: isOwnItemsSuccess, - } = useOwnItems(); - // todo: get only shared items with write/admin rights - // otherwise choosing an item without the write rights will result in an error - const { - data: sharedItems, - isLoading: isSharedItemsLoading, - isSuccess: isSharedItemsSuccess, - } = useSharedItems(); - - const isFolder = (i: DiscriminatedItem) => i.type === ItemType.FOLDER; - - // compute tree only when the modal is open - if (open) { - if (isOwnItemsSuccess && isSharedItemsSuccess) { - return ( - <> - useChildren(id, undefined, options)} - useItem={useItem} - showCheckbox - rootLabel={t(LIBRARY.OWN_ITEMS_LABEL)} - rootId={TREE_MODAL_MY_ITEMS_ID} - showItemFilter={isFolder} - shouldFetchChildrenForItem={isFolder} - /> - useChildren(id, undefined, options)} - useItem={useItem} - showCheckbox - rootLabel={t(LIBRARY.SHARED_ITEMS_LABEL)} - rootId={TREE_MODAL_SHARED_ITEMS_ID} - showItemFilter={isFolder} - shouldFetchChildrenForItem={isFolder} - /> - - ); - } - if (isOwnItemsLoading || isSharedItemsLoading) { - return ; - } - return {t(LIBRARY.UNEXPECTED_ERROR_MESSAGE)}; - } - return null; -}; -export default TreeModalContent; diff --git a/src/components/collection/copyModal/AccessibleNavigationTree.tsx b/src/components/collection/copyModal/AccessibleNavigationTree.tsx new file mode 100644 index 00000000..c589938e --- /dev/null +++ b/src/components/collection/copyModal/AccessibleNavigationTree.tsx @@ -0,0 +1,65 @@ +import { useContext, useState } from 'react'; + +import { Alert, Skeleton } from '@mui/material'; + +import { ItemType, PermissionLevel } from '@graasp/sdk'; +import { RowMenuProps, RowMenus } from '@graasp/ui'; + +import { QueryClientContext } from '../../QueryClientContext'; + +interface AccessibleNavigationTreeProps { + onClick: RowMenuProps['onClick']; + onNavigate: RowMenuProps['onNavigate']; + selectedId?: string; +} + +const PAGE_SIZE = 10; + +const AccessibleNavigationTree = ({ + onClick, + onNavigate, + selectedId, +}: AccessibleNavigationTreeProps): JSX.Element => { + const [page, setPage] = useState(1); + // todo: show only items with admin rights + const { hooks } = useContext(QueryClientContext); + const { data: accessibleItems, isLoading } = hooks.useAccessibleItems( + { + permissions: [PermissionLevel.Write, PermissionLevel.Admin], + types: [ItemType.FOLDER], + }, + { page }, + ); + + const nbPages = accessibleItems + ? Math.ceil(accessibleItems.totalCount / PAGE_SIZE) + : 0; + + if (accessibleItems) { + return ( + + ); + } + + if (isLoading) { + return ( + <> + + + + + ); + } + + return An unexpected error happened; +}; + +export default AccessibleNavigationTree; diff --git a/src/components/collection/copyModal/ChildrenNavigationTree.tsx b/src/components/collection/copyModal/ChildrenNavigationTree.tsx new file mode 100644 index 00000000..ac1e983f --- /dev/null +++ b/src/components/collection/copyModal/ChildrenNavigationTree.tsx @@ -0,0 +1,63 @@ +import { useContext } from 'react'; + +import { Alert, Box, Skeleton } from '@mui/material'; + +import { ItemType } from '@graasp/sdk'; +import { NavigationElement, RowMenuProps, RowMenus } from '@graasp/ui'; + +import { useLibraryTranslation } from '../../../config/i18n'; +import LIBRARY from '../../../langs/constants'; +import { QueryClientContext } from '../../QueryClientContext'; + +interface ChildrenNavigationTreeProps { + isDisabled?: RowMenuProps['isDisabled']; + selectedId?: string; + selectedNavigationItem: NavigationElement; + onClick: RowMenuProps['onClick']; + onNavigate: RowMenuProps['onNavigate']; +} + +const ChildrenNavigationTree = ({ + onClick, + selectedId, + selectedNavigationItem, + onNavigate, + isDisabled, +}: ChildrenNavigationTreeProps): JSX.Element => { + const { t } = useLibraryTranslation(); + const { hooks } = useContext(QueryClientContext); + const { data: children, isLoading } = hooks.useChildren( + selectedNavigationItem.id, + ); + // TODO: use hook's filter when available + const folders = children?.filter((f) => f.type === ItemType.FOLDER); + + if (children) { + return ( + + {t(LIBRARY.COPY_MODAL_EMPTY_FOLDER)} + + } + /> + ); + } + if (isLoading) { + return ( + <> + + + + + ); + } + return An unexpected error happened; +}; + +export default ChildrenNavigationTree; diff --git a/src/components/collection/copyModal/ItemSelectionModal.tsx b/src/components/collection/copyModal/ItemSelectionModal.tsx new file mode 100644 index 00000000..8cb6b6d4 --- /dev/null +++ b/src/components/collection/copyModal/ItemSelectionModal.tsx @@ -0,0 +1,210 @@ +import { useContext, useState } from 'react'; + +import { Home as HomeIcon } from '@mui/icons-material'; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Skeleton, + Stack, +} from '@mui/material'; + +import { DiscriminatedItem } from '@graasp/sdk'; +import { COMMON } from '@graasp/translations'; +import { Breadcrumbs, NavigationElement } from '@graasp/ui'; + +import { + useCommonTranslation, + useLibraryTranslation, +} from '../../../config/i18n'; +import { + COPY_MODAL_TITLE_ID, + TREE_MODAL_CONFIRM_BUTTON_ID, +} from '../../../config/selectors'; +import LIBRARY from '../../../langs/constants'; +import { QueryClientContext } from '../../QueryClientContext'; +import AccessibleNavigationTree from './AccessibleNavigationTree'; +import ChildrenNavigationTree from './ChildrenNavigationTree'; +import RootNavigationTree from './RootNavigationTree'; + +const dialogId = 'items-tree-modal'; + +export type ItemSelectionModalProps = { + buttonText?: (itemName?: string) => string; + /** + * disabled rows + * */ + isDisabled?: ( + items: DiscriminatedItem[], + item: NavigationElement, + homeId: string, + ) => boolean; + itemId: string; + onClose?: (args: { id: string | null; open: boolean }) => void; + onConfirm?: (destination: string | undefined) => void; + open?: boolean; + title: string; +}; + +const ItemSelectionModal = ({ + buttonText = () => 'Submit', + isDisabled = () => false, + itemId, + onClose, + onConfirm, + open = false, + title, +}: ItemSelectionModalProps): JSX.Element => { + const { t: translateLibrary } = useLibraryTranslation(); + const { t: translateCommon } = useCommonTranslation(); + const { hooks } = useContext(QueryClientContext); + const { data: itemToCopy, isLoading } = hooks.useItem(itemId); + + // special elements for breadcrumbs + // root displays specific paths + const ROOT_BREADCRUMB: NavigationElement = { + icon: , + name: '', + path: 'selectionModalRoot', + id: 'selectionModalRoot', + }; + // my graasp displays accessible items + const MY_GRAASP_BREADCRUMB: NavigationElement = { + name: translateLibrary(LIBRARY.COPY_MODAL_MY_GRAASP_BREADCRUMB), + id: 'root', + path: 'root', + }; + + const SPECIAL_BREADCRUMB_IDS = [ROOT_BREADCRUMB.id, MY_GRAASP_BREADCRUMB.id]; + + const [selectedItem, setSelectedItem] = useState(); + + // keep track of the navigation item that can be different from the selected item + const [selectedNavigationItem, setSelectedNavigationItem] = + useState(ROOT_BREADCRUMB); + + const { data: navigationParents } = hooks.useParents({ + id: selectedNavigationItem.id, + enabled: !SPECIAL_BREADCRUMB_IDS.includes(selectedNavigationItem.id), + }); + + const handleClose = () => { + onClose?.({ id: null, open: false }); + }; + + const onClickConfirm = () => { + onConfirm?.( + selectedItem?.id === MY_GRAASP_BREADCRUMB.id + ? undefined + : selectedItem?.id, + ); + handleClose(); + }; + + // row menu navigation + const onNavigate = (item: NavigationElement) => { + setSelectedNavigationItem(item); + setSelectedItem(item); + }; + + const isDisabledLocal = (item: NavigationElement) => + !itemToCopy || isDisabled([itemToCopy], item, MY_GRAASP_BREADCRUMB.id); + + return ( + + {title} + + + + + {itemToCopy && selectedNavigationItem.id === ROOT_BREADCRUMB.id && ( + + isDisabled([itemToCopy], item, MY_GRAASP_BREADCRUMB.id) + } + onClick={setSelectedItem} + selectedId={selectedItem?.id} + onNavigate={onNavigate} + rootMenuItems={[MY_GRAASP_BREADCRUMB]} + /> + )} + {isLoading && ( + <> + + + + + )} + {selectedNavigationItem.id === MY_GRAASP_BREADCRUMB.id && ( + + )} + {!SPECIAL_BREADCRUMB_IDS.includes(selectedNavigationItem.id) && ( + + )} + + + + + + + + ); +}; + +export default ItemSelectionModal; diff --git a/src/components/collection/copyModal/RootNavigationTree.tsx b/src/components/collection/copyModal/RootNavigationTree.tsx new file mode 100644 index 00000000..4f84e619 --- /dev/null +++ b/src/components/collection/copyModal/RootNavigationTree.tsx @@ -0,0 +1,87 @@ +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 { useLibraryTranslation } from '../../../config/i18n'; +import LIBRARY from '../../../langs/constants'; +import { QueryClientContext } from '../../QueryClientContext'; + +interface RootNavigationTreeProps { + isDisabled?: RowMenuProps['isDisabled']; + onClick: RowMenuProps['onClick']; + onNavigate: RowMenuProps['onNavigate']; + rootMenuItems: NavigationElement[]; + selectedId?: string; +} + +const RootNavigationTree = ({ + isDisabled, + onClick, + onNavigate, + rootMenuItems, + selectedId, +}: RootNavigationTreeProps): JSX.Element => { + const { t } = useLibraryTranslation(); + + const { hooks } = useContext(QueryClientContext); + // todo: to change with real recent items (most used) + const { data: recentItems, isLoading } = hooks.useAccessibleItems( + // you can move into an item you have at least write permission + { + permissions: [PermissionLevel.Admin, PermissionLevel.Write], + types: [ItemType.FOLDER], + }, + { pageSize: 5 }, + ); + const recentFolders = recentItems?.data?.filter( + ({ type }) => type === ItemType.FOLDER, + ); + + if (recentItems) { + return ( + <> + + {t(LIBRARY.COPY_MODAL_HOME_TITLE)} + + + {recentFolders && ( + <> + + {t(LIBRARY.COPY_MODAL_RECENT_TITLE)} + + + + )} + + ); + } + + if (isLoading) { + return ( + <> + + + + + ); + } + + return An unexpected error happened; +}; + +export default RootNavigationTree; diff --git a/src/components/collection/summary/SummaryActionButtons.tsx b/src/components/collection/summary/SummaryActionButtons.tsx index fd26cb1c..5c1de416 100644 --- a/src/components/collection/summary/SummaryActionButtons.tsx +++ b/src/components/collection/summary/SummaryActionButtons.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { Code, Download, MoreVert } from '@mui/icons-material'; +import { Code, CopyAll, Download, MoreVert } from '@mui/icons-material'; import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'; import { Button, @@ -15,6 +15,11 @@ import { DiscriminatedItem } from '@graasp/sdk'; import { useLibraryTranslation } from '../../../config/i18n'; import { buildPlayerViewItemRoute } from '../../../config/paths'; +import { + LIBRARY_ACTION_GROUP_BUTTON_ID, + LIBRARY_ACTION_GROUP_COPY_BUTTON_ID, + LIBRARY_ACTION_GROUP_POP_UP_BUTTONS_ID, +} from '../../../config/selectors'; import LIBRARY from '../../../langs/constants'; import { openInNewTab } from '../../../utils/helpers'; import { useCopyAction } from '../CopyButton'; @@ -31,12 +36,13 @@ type SummaryActionButtonsProps = { isLogged: boolean; }; -const SummaryActionButtons: React.FC = ({ +const SummaryActionButtons = ({ item, -}) => { + isLogged, +}: SummaryActionButtonsProps): JSX.Element => { const { t } = useLibraryTranslation(); - const { treeModal } = useCopyAction(item?.id); + const { treeModal, startCopy } = useCopyAction(item?.id); const { startDownload } = useDownloadAction(item?.id); @@ -87,6 +93,7 @@ const SummaryActionButtons: React.FC = ({ aria-label="select merge strategy" aria-haspopup="menu" onClick={handleToggle} + id={LIBRARY_ACTION_GROUP_BUTTON_ID} > @@ -117,6 +124,7 @@ const SummaryActionButtons: React.FC = ({ variant="contained" orientation="vertical" fullWidth + id={LIBRARY_ACTION_GROUP_POP_UP_BUTTONS_ID} > = ({ > {t(LIBRARY.SUMMARY_ACTIONS_DOWNLOAD)} - {/* {isLogged && ( + {isLogged && ( - ) : ( - - ) - } + startIcon={} + id={LIBRARY_ACTION_GROUP_COPY_BUTTON_ID} > {t(LIBRARY.SUMMARY_ACTIONS_COPY)} - )} */} + )} { }); return ( - - + + { title={t(LIBRARY.HOME_RECENT_COLLECTIONS_TITLE)} /> - + { {t(LIBRARY.HOME_VIEW_MORE_IN_LIBRARY_BUTTON)} - - + + ); }; export default Home; diff --git a/src/config/selectors.ts b/src/config/selectors.ts index c5df05be..064fd231 100644 --- a/src/config/selectors.ts +++ b/src/config/selectors.ts @@ -88,3 +88,9 @@ export const RECENT_COLLECTIONS_DRAWER_ITEM_ID = 'recentCollectionsDrawerItemId'; export const MY_PUBLICATIONS_DRAWER_ITEM_ID = 'myPublicationsDrawerItemId'; export const LIKED_COLLECTIONS_DRAWER_ITEM_ID = 'likedCollectionsDrawerItemId'; +export const LIBRARY_ACTION_GROUP_BUTTON_ID = 'libraryActionGroupButton'; +export const LIBRARY_ACTION_GROUP_COPY_BUTTON_ID = + 'libraryActionGroupCopyButton'; +export const LIBRARY_ACTION_GROUP_POP_UP_BUTTONS_ID = + 'libraryActionGroupPopUpButtons'; +export const CHILD_CARD_COPY_BUTTON_ID = 'childCardCopyButton'; diff --git a/src/langs/ar.json b/src/langs/ar.json index e2b304e5..f83da7c2 100644 --- a/src/langs/ar.json +++ b/src/langs/ar.json @@ -86,8 +86,6 @@ "COLLECTION_INFO_BUTTON": "معلومات", "SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL": "إغلاق مشروط تسجيل الدخول", "VIEW_BUTTON_TOOLTIP": "عرض العنصر", - "SHARED_ITEMS_LABEL": "العناصر المنشورة", - "OWN_ITEMS_LABEL": "العناصر المملوكة", "SUMMARY_CONTENT_TITLE": "محتوى", "SUMMARY_DETAILS_TITLE": "تفاصيل", "SUMMARY_DETAILS_CREATED_AT_TITLE": "مخلوق", diff --git a/src/langs/constants.ts b/src/langs/constants.ts index 47dc7a9d..231ce741 100644 --- a/src/langs/constants.ts +++ b/src/langs/constants.ts @@ -89,8 +89,6 @@ export const LIBRARY = { SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL: 'SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL', VIEW_BUTTON_TOOLTIP: 'VIEW_BUTTON_TOOLTIP', - OWN_ITEMS_LABEL: 'OWN_ITEMS_LABEL', - SHARED_ITEMS_LABEL: 'SHARED_ITEMS_LABEL', SUMMARY_DETAILS_TITLE: 'SUMMARY_DETAILS_TITLE', SUMMARY_DETAILS_CREATED_AT_TITLE: 'SUMMARY_DETAILS_CREATED_AT_TITLE', SUMMARY_DETAILS_UPDATED_AT_TITLE: 'SUMMARY_DETAILS_UPDATED_AT_TITLE', @@ -136,5 +134,10 @@ export const LIBRARY = { PREVIEW_ALERT_ADMIN_ACTION: 'PREVIEW_ALERT_ADMIN_ACTION', PUBLISH_ITEM_BUTTON: 'PUBLISH_ITEM_BUTTON', LOADING_TEXT: 'LOADING_TEXT', + COPY_MODAL_EMPTY_FOLDER: 'COPY_MODAL_EMPTY_FOLDER', + COPY_MODAL_HOME_TITLE: 'COPY_MODAL_HOME_TITLE', + COPY_MODAL_RECENT_TITLE: 'COPY_MODAL_RECENT_TITLE', + COPY_MODAL_SUBMIT_BUTTON: 'COPY_MODAL_SUBMIT_BUTTON', + COPY_MODAL_MY_GRAASP_BREADCRUMB: 'COPY_MODAL_MY_GRAASP_BREADCRUMB', }; export default LIBRARY; diff --git a/src/langs/de.json b/src/langs/de.json index f30dbd76..bfb9dc0a 100644 --- a/src/langs/de.json +++ b/src/langs/de.json @@ -86,8 +86,6 @@ "COLLECTION_INFO_BUTTON": "Information", "SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL": "Anmelden Modal schliessen", "VIEW_BUTTON_TOOLTIP": "Element ansehen", - "SHARED_ITEMS_LABEL": "Geteilte Elemente", - "OWN_ITEMS_LABEL": "Meine Gegenstände", "SUMMARY_CONTENT_TITLE": "Inhalt", "SUMMARY_DETAILS_TITLE": "Details", "SUMMARY_DETAILS_CREATED_AT_TITLE": "Erstellt", diff --git a/src/langs/en.json b/src/langs/en.json index 4420fdb3..5f6e5ec8 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -80,14 +80,11 @@ "COPY_ITEM_TOASTR_LINK": "Click here to open the item on Graasp.", "COPY_LINK_SUCCESS_MESSAGE": "The item resource link was successfully copied.", "COPY_LINK_FAILURE_MESSAGE": "An error occured while copying the resource link.", - "COPY_BUTTON_MODAL_MY_ITEMS_ROOT": "My Items", "AVATAR_ALT": "{{name}}'s avatar", "COLLECTION_EMPTY_DESCRIPTION_TEXT": "This item has no description.", "COLLECTION_INFO_BUTTON": "Information", "SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL": "Close Sign In Modal", "VIEW_BUTTON_TOOLTIP": "View item", - "SHARED_ITEMS_LABEL": "Shared Items", - "OWN_ITEMS_LABEL": "Owned Items", "SUMMARY_CONTENT_TITLE": "Content", "SUMMARY_DETAILS_TITLE": "Details", "SUMMARY_DETAILS_CREATED_AT_TITLE": "Created", @@ -132,5 +129,10 @@ "PREVIEW_ALERT_DESCRIPTION": "This item is not published. What you are seeing here is only a preview. You can see this because you have access to the item. It cannot be viewed publicly.", "PREVIEW_ALERT_ADMIN_ACTION": "If you would like to share this collection with everyone, you can publish this item in the Builder using the link below.", "PUBLISH_ITEM_BUTTON": "Publish Now", - "LOADING_TEXT": "Loading" + "LOADING_TEXT": "Loading", + "COPY_MODAL_EMPTY_FOLDER": "The folder is empty.", + "COPY_MODAL_HOME_TITLE": "Home", + "COPY_MODAL_RECENT_TITLE": "Recent items", + "COPY_MODAL_SUBMIT_BUTTON": "Submit", + "COPY_MODAL_MY_GRAASP_BREADCRUMB": "My Graasp" } diff --git a/src/langs/es.json b/src/langs/es.json index 66752128..9c9ada71 100644 --- a/src/langs/es.json +++ b/src/langs/es.json @@ -86,8 +86,6 @@ "COLLECTION_INFO_BUTTON": "Información", "SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL": "Cerrar modal de iniciar de sesión ", "VIEW_BUTTON_TOOLTIP": "Ver ítem", - "SHARED_ITEMS_LABEL": "Artículos compartidos", - "OWN_ITEMS_LABEL": "Artículos propios", "SUMMARY_CONTENT_TITLE": "Contenido", "SUMMARY_DETAILS_TITLE": "Detalles", "SUMMARY_DETAILS_CREATED_AT_TITLE": "Creado", diff --git a/src/langs/fr.json b/src/langs/fr.json index 39c717e0..cc8793c8 100644 --- a/src/langs/fr.json +++ b/src/langs/fr.json @@ -86,8 +86,6 @@ "COLLECTION_INFO_BUTTON": "Information", "SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL": "Fermer le dialogue de connection", "VIEW_BUTTON_TOOLTIP": "Explorer l'élément", - "SHARED_ITEMS_LABEL": "Éléments partagés avec moi", - "OWN_ITEMS_LABEL": "Mes Éléments", "SUMMARY_CONTENT_TITLE": "Contenu", "SUMMARY_DETAILS_TITLE": "Détails", "SUMMARY_DETAILS_CREATED_AT_TITLE": "Créé", @@ -124,5 +122,9 @@ "PUBLIC_PROFILE": "Mes collections", "PROFILE_SETTINGS": "Profil", "EDIT": "Modifier", - "SOCIAL_PROFILES": "Profils sociaux" + "SOCIAL_PROFILES": "Profils sociaux", + "COPY_MODAL_EMPTY_FOLDER": "Le dossier est vide.", + "COPY_MODAL_HOME_TITLE": "Accueil", + "COPY_MODAL_RECENT_TITLE": "Éléments récents", + "COPY_MODAL_SUBMIT_BUTTON": "Valider" } diff --git a/src/langs/it.json b/src/langs/it.json index 862e7baa..633def02 100644 --- a/src/langs/it.json +++ b/src/langs/it.json @@ -86,8 +86,6 @@ "COLLECTION_INFO_BUTTON": "Informazione", "SIGN_IN_MODAL_CLOSE_BUTTON_ARIA_LABEL": "Chiudi Accedi in modalità modale", "VIEW_BUTTON_TOOLTIP": "Visualizza elemento", - "SHARED_ITEMS_LABEL": "Elementi condivisi", - "OWN_ITEMS_LABEL": "Oggetti di proprietà", "SUMMARY_CONTENT_TITLE": "Contenuto", "SUMMARY_DETAILS_TITLE": "Dettagli", "SUMMARY_DETAILS_CREATED_AT_TITLE": "Creato", diff --git a/yarn.lock b/yarn.lock index c709f7fc..6e4c0ed9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,19 +19,22 @@ __metadata: languageName: node linkType: hard -"@ag-grid-community/client-side-row-model@npm:31.1.1": - version: 31.1.1 - resolution: "@ag-grid-community/client-side-row-model@npm:31.1.1" +"@ag-grid-community/client-side-row-model@npm:31.2.1": + version: 31.2.1 + resolution: "@ag-grid-community/client-side-row-model@npm:31.2.1" dependencies: - "@ag-grid-community/core": "npm:31.1.1" - checksum: 10/871116d80ee740f417d533abb59bf57831aaba81625fef4fa75b6f538b8a9a29c909a50e8e45231b3f476735e4deaff69e6a1a18efa18f2141f6904497b37f2e + "@ag-grid-community/core": "npm:31.2.1" + tslib: "npm:^2.3.0" + checksum: 10/e1efa79831b644d4aff5ccae32a2b8ff0c9383060f64befeff6aaf0c21c6a081c44f970ee1ea32737c7bbd977f69afdf4b3d2b269c1fdfc24306cf75e6b618ce languageName: node linkType: hard -"@ag-grid-community/core@npm:31.1.1": - version: 31.1.1 - resolution: "@ag-grid-community/core@npm:31.1.1" - checksum: 10/6c830b9b3a29b2ec05e84137d9f739ef9727bbf9c505c6fe5bec39146ad2ad5e606866932cf606e91b7c5a236adc093cf3779533122f33c5102ad3ec1b368c09 +"@ag-grid-community/core@npm:31.2.1": + version: 31.2.1 + resolution: "@ag-grid-community/core@npm:31.2.1" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10/2bf06604fd620b543a3422fb2039f32ee28a405a7eea1c374e13e317c3a7d2fa146d4af73edbf131732bab5ab0d75ee9ba199ed0c9bad5f60afbfc8759e98aad languageName: node linkType: hard @@ -1438,11 +1441,11 @@ __metadata: languageName: node linkType: hard -"@graasp/ui@npm:4.15.0": - version: 4.15.0 - resolution: "@graasp/ui@npm:4.15.0" +"@graasp/ui@github:graasp/graasp-ui#row-menu": + version: 4.15.1 + resolution: "@graasp/ui@https://github.com/graasp/graasp-ui.git#commit=5099feafbd0d35f557bc9444b2c7ca28ee49c4b8" dependencies: - "@ag-grid-community/client-side-row-model": "npm:31.1.1" + "@ag-grid-community/client-side-row-model": "npm:31.2.1" "@ag-grid-community/react": "npm:^31.1.1" "@ag-grid-community/styles": "npm:^31.1.1" "@storybook/react-vite": "npm:7.6.17" @@ -1455,7 +1458,7 @@ __metadata: react-rnd: "npm:10.4.1" react-text-mask: "npm:5.5.0" uuid: "npm:9.0.1" - vitest: "npm:1.4.0" + vitest: "npm:1.5.0" peerDependencies: "@emotion/cache": ~11.10.7 || ~11.11.0 "@emotion/react": ~11.10.6 || ~11.11.0 @@ -1472,7 +1475,7 @@ __metadata: react-router-dom: ^6.11.0 stylis: ^4.1.3 stylis-plugin-rtl: ^2.1.1 - checksum: 10/990fb627fc03d4e831a55589f53c7e78380bd771ea34bbc5cf111e1849c18616fea440ff03a5b8a5e3379cb5fcda0a72759a90b846b70b430e37eb6d21f6f08e + checksum: 10/6129ddc6815e8635258be8733f674b2bacb76545f73957f05becdab079055c7f11924742cfb6e95ed078670c528ea1e9c1bcafda62c3b4bf882aa1e49b03d80a languageName: node linkType: hard @@ -3537,57 +3540,57 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:1.4.0": - version: 1.4.0 - resolution: "@vitest/expect@npm:1.4.0" +"@vitest/expect@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/expect@npm:1.5.0" dependencies: - "@vitest/spy": "npm:1.4.0" - "@vitest/utils": "npm:1.4.0" + "@vitest/spy": "npm:1.5.0" + "@vitest/utils": "npm:1.5.0" chai: "npm:^4.3.10" - checksum: 10/00d794a807b7e496d8450133430c8528d4b6cfaba9520bf49640c941b14acaa7b28f151c249b44d935740cae887f0648980db63f38e37bdeb6c2906387e15188 + checksum: 10/9ee8014a4ee25fbebd702e171abd60a461d51144d19cc3c8fb5309c6836305f2cbfced66af12ab52a114810ddca651329c1e4e1de065df05a3a488ba8b98bb44 languageName: node linkType: hard -"@vitest/runner@npm:1.4.0": - version: 1.4.0 - resolution: "@vitest/runner@npm:1.4.0" +"@vitest/runner@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/runner@npm:1.5.0" dependencies: - "@vitest/utils": "npm:1.4.0" + "@vitest/utils": "npm:1.5.0" p-limit: "npm:^5.0.0" pathe: "npm:^1.1.1" - checksum: 10/7b8a692de5cef72ef698e83eb5bbb89076924e7a557ed087e80c5080e000a575f34c481f3b880aa2588da5a095504dc55216c319f6924eddfcfc3412f10a27b2 + checksum: 10/f13875fd220e6c79bae4e462706d81dfda93d3c0aa351e457b363897035a7306609da28d7120c30d39bd71e70d9c70aea884ab2edbb1e27e24550ff86576b1ee languageName: node linkType: hard -"@vitest/snapshot@npm:1.4.0": - version: 1.4.0 - resolution: "@vitest/snapshot@npm:1.4.0" +"@vitest/snapshot@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/snapshot@npm:1.5.0" dependencies: magic-string: "npm:^0.30.5" pathe: "npm:^1.1.1" pretty-format: "npm:^29.7.0" - checksum: 10/43e22f8aeef4b87bcce79b37775415d4b558e32d906992d4a0acbe81c8e84cbfe3e488dd32c504c4f4d8f2c3f96842acb524b4b210036fda6796e64d0140d5f6 + checksum: 10/cd55c5c2aa1b44784253f47eaa8a3ee0e48b528d98524b5fd496b1b8f7d38fd16d1ff98d64bc43e90200553e858ac2811dbafd065c9718a43a2cea23d9b03a89 languageName: node linkType: hard -"@vitest/spy@npm:1.4.0": - version: 1.4.0 - resolution: "@vitest/spy@npm:1.4.0" +"@vitest/spy@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/spy@npm:1.5.0" dependencies: tinyspy: "npm:^2.2.0" - checksum: 10/0e48f9a64f62801c2abf10df1013ec5e5b75c47bdca6a5d4c8246b3dd7bdf01ade3df6c99fd0751a870a16bd63c127b3e58e0f5cbc320c48d0727ab5da89d028 + checksum: 10/4e692d4a7c043728d046670f611da1d418a733e2d2212b674e55e7137b67b66097fe4e723c09a4bb56c3943f4b62eb1b9cd69238e26af0f876612357099dbad3 languageName: node linkType: hard -"@vitest/utils@npm:1.4.0": - version: 1.4.0 - resolution: "@vitest/utils@npm:1.4.0" +"@vitest/utils@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/utils@npm:1.5.0" dependencies: diff-sequences: "npm:^29.6.3" estree-walker: "npm:^3.0.3" loupe: "npm:^2.3.7" pretty-format: "npm:^29.7.0" - checksum: 10/2261705e2edc10376f2524a4bf6616688680094d94fff683681a1ef8d3d59271dee2d80893efad8e6437bbdb00390e2edd754d94cf42100db86f2cfd9c44826f + checksum: 10/80c2d0d31328df212c1eb1416c308e6763a23d12aaafd9b350d3c853d0bd929706c56d30d1f53e50cfff2359770bd65c082c918c6d15db5f39cd72e64f79cda3 languageName: node linkType: hard @@ -7783,7 +7786,7 @@ __metadata: "@graasp/query-client": "npm:3.4.1" "@graasp/sdk": "npm:4.4.0" "@graasp/translations": "npm:1.25.3" - "@graasp/ui": "npm:4.15.0" + "@graasp/ui": "github:graasp/graasp-ui#row-menu" "@mui/icons-material": "npm:5.15.14" "@mui/lab": "npm:5.0.0-alpha.169" "@mui/material": "npm:5.15.14" @@ -12719,10 +12722,10 @@ __metadata: languageName: node linkType: hard -"tinypool@npm:^0.8.2": - version: 0.8.2 - resolution: "tinypool@npm:0.8.2" - checksum: 10/5e2cdddc1caf437e3b8d8c56c1c66dffcb46008be4b2e37d457b0921699c6b79930dd8d652e4890c5e1e24688489259da83fd853bc0ce348d8a0375dedefc2ba +"tinypool@npm:^0.8.3": + version: 0.8.4 + resolution: "tinypool@npm:0.8.4" + checksum: 10/7365944c2532f240111443e7012be31a634faf1a02db08a91db3aa07361c26a374d0be00a0f2ea052c4bee39c107ba67f1f814c108d9d51dfc725c559c1a9c03 languageName: node linkType: hard @@ -12863,7 +12866,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.6.2": +"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 10/bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca @@ -13346,9 +13349,9 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:1.4.0": - version: 1.4.0 - resolution: "vite-node@npm:1.4.0" +"vite-node@npm:1.5.0": + version: 1.5.0 + resolution: "vite-node@npm:1.5.0" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.3.4" @@ -13357,7 +13360,7 @@ __metadata: vite: "npm:^5.0.0" bin: vite-node: vite-node.mjs - checksum: 10/691e828c2abe6b62d44183c4e04bdfd119fed405439126fbdc5bfb791644baee3961c1ce429a67b360cc3d8b7c472160c7e82c59491f044a232b4ff480d8a2a2 + checksum: 10/ebcb8ac18bbef161d7eea5e89a587bdcbe2973bbd384535a2f912bce30a8aba445a0f444db367f0f218072d77c8405f82cec96035e41efef19af7870972b99e4 languageName: node linkType: hard @@ -13401,15 +13404,15 @@ __metadata: languageName: node linkType: hard -"vitest@npm:1.4.0": - version: 1.4.0 - resolution: "vitest@npm:1.4.0" +"vitest@npm:1.5.0": + version: 1.5.0 + resolution: "vitest@npm:1.5.0" dependencies: - "@vitest/expect": "npm:1.4.0" - "@vitest/runner": "npm:1.4.0" - "@vitest/snapshot": "npm:1.4.0" - "@vitest/spy": "npm:1.4.0" - "@vitest/utils": "npm:1.4.0" + "@vitest/expect": "npm:1.5.0" + "@vitest/runner": "npm:1.5.0" + "@vitest/snapshot": "npm:1.5.0" + "@vitest/spy": "npm:1.5.0" + "@vitest/utils": "npm:1.5.0" acorn-walk: "npm:^8.3.2" chai: "npm:^4.3.10" debug: "npm:^4.3.4" @@ -13421,15 +13424,15 @@ __metadata: std-env: "npm:^3.5.0" strip-literal: "npm:^2.0.0" tinybench: "npm:^2.5.1" - tinypool: "npm:^0.8.2" + tinypool: "npm:^0.8.3" vite: "npm:^5.0.0" - vite-node: "npm:1.4.0" + vite-node: "npm:1.5.0" why-is-node-running: "npm:^2.2.2" peerDependencies: "@edge-runtime/vm": "*" "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": 1.4.0 - "@vitest/ui": 1.4.0 + "@vitest/browser": 1.5.0 + "@vitest/ui": 1.5.0 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -13447,7 +13450,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10/cf4675657f4a9ea755d0af70d62827fca9daee64e81d0392067c70a0d1f5f8fd4a47523e28ecf42d667e4d4d7c68b09d5e08389d4b58dc36065364f6c76cda7d + checksum: 10/ad487ab8578fd7d1fe39d2ef3d8355dacd41ae2774a23b96160b3d6a6347507679963158f190ef21cd864f72c389808605d0be662d69289ceaaa0beb55f5091c languageName: node linkType: hard