diff --git a/cypress/e2e/item/bookmarks/bookmarks.cy.ts b/cypress/e2e/item/bookmarks/bookmarks.cy.ts index 2cbbdfc37..e86cb871b 100644 --- a/cypress/e2e/item/bookmarks/bookmarks.cy.ts +++ b/cypress/e2e/item/bookmarks/bookmarks.cy.ts @@ -3,6 +3,7 @@ import { PackedItemBookmarkFactory, } from '@graasp/sdk'; +import { SortingOptions } from '@/components/table/types'; import { BUILDER } from '@/langs/constants'; import i18n, { BUILDER_NAMESPACE } from '../../../../src/config/i18n'; @@ -12,6 +13,9 @@ import { BOOKMARKED_ITEMS_ID, CREATE_ITEM_BUTTON_ID, ITEM_SEARCH_INPUT_ID, + SORTING_ORDERING_SELECTOR_ASC, + SORTING_ORDERING_SELECTOR_DESC, + SORTING_SELECT_SELECTOR, buildItemCard, } from '../../../../src/config/selectors'; import { CURRENT_USER } from '../../../fixtures/members'; @@ -56,11 +60,10 @@ describe('Bookmarked Item', () => { bookmarkedItems: BOOKMARKED_ITEMS, }); i18n.changeLanguage(CURRENT_USER.extra.lang as string); - cy.visit(HOME_PATH); + cy.visit(BOOKMARKED_ITEMS_PATH); }); it('Empty search', () => { - cy.visit(BOOKMARKED_ITEMS_PATH); i18n.changeLanguage(CURRENT_USER.extra.lang as string); const searchText = 'mysearch'; cy.get(`#${ITEM_SEARCH_INPUT_ID}`).type(searchText); @@ -72,19 +75,18 @@ describe('Bookmarked Item', () => { }); it("New button doesn't exist", () => { - cy.visit(BOOKMARKED_ITEMS_PATH); cy.get(`#${CREATE_ITEM_BUTTON_ID}`).should('not.exist'); }); it('check bookmarked items view', () => { - cy.visit(BOOKMARKED_ITEMS_PATH); - for (const { item } of BOOKMARKED_ITEMS) { cy.get(`#${buildItemCard(item.id)}`).should('be.visible'); } }); it('add item to bookmarks', () => { + cy.visit(HOME_PATH); + const item = NON_BOOKMARKED_ITEM; addToBookmark(item.id); @@ -103,6 +105,35 @@ describe('Bookmarked Item', () => { expect(request.url).to.contain(itemId); }); }); + + it('Sorting & Ordering', () => { + cy.get(`${SORTING_SELECT_SELECTOR} input`).should( + 'have.value', + SortingOptions.ItemUpdatedAt, + ); + cy.get(SORTING_ORDERING_SELECTOR_DESC).should('be.visible'); + + cy.get(SORTING_SELECT_SELECTOR).click(); + cy.get('li[data-value="item.name"]').click(); + + // check items are ordered by name + cy.get(`#${BOOKMARKED_ITEMS_ID} h5`).then(($e) => { + BOOKMARKED_ITEMS.sort((a, b) => (a.item.name < b.item.name ? 1 : -1)); + for (let idx = 0; idx < BOOKMARKED_ITEMS.length; idx += 1) { + expect($e[idx].innerText).to.eq(BOOKMARKED_ITEMS[idx].item.name); + } + }); + + // change ordering + cy.get(SORTING_ORDERING_SELECTOR_DESC).click(); + cy.get(SORTING_ORDERING_SELECTOR_ASC).should('be.visible'); + cy.get(`#${BOOKMARKED_ITEMS_ID} h5`).then(($e) => { + BOOKMARKED_ITEMS.reverse(); + for (let idx = 0; idx < BOOKMARKED_ITEMS.length; idx += 1) { + expect($e[idx].innerText).to.eq(BOOKMARKED_ITEMS[idx].item.name); + } + }); + }); }); describe('Error Handling', () => { diff --git a/cypress/e2e/item/home/home.cy.ts b/cypress/e2e/item/home/home.cy.ts index 83317dc00..f02cdf1a5 100644 --- a/cypress/e2e/item/home/home.cy.ts +++ b/cypress/e2e/item/home/home.cy.ts @@ -3,6 +3,7 @@ import { PackedLocalFileItemFactory, } from '@graasp/sdk'; +import { SortingOptions } from '@/components/table/types'; import { ITEM_PAGE_SIZE } from '@/config/constants'; import i18n from '../../../../src/config/i18n'; @@ -13,6 +14,9 @@ import { DROPZONE_SELECTOR, HOME_LOAD_MORE_BUTTON_SELECTOR, ITEM_SEARCH_INPUT_ID, + SORTING_ORDERING_SELECTOR_ASC, + SORTING_ORDERING_SELECTOR_DESC, + SORTING_SELECT_SELECTOR, buildItemCard, buildMapViewId, } from '../../../../src/config/selectors'; @@ -84,6 +88,36 @@ describe('Home', () => { }); }); + it.only('Sorting & ordering', () => { + cy.get(`${SORTING_SELECT_SELECTOR} input`).should( + 'have.value', + SortingOptions.ItemUpdatedAt, + ); + cy.get(SORTING_ORDERING_SELECTOR_DESC).should('be.visible'); + + // change sorting + cy.get(SORTING_SELECT_SELECTOR).click(); + cy.get('li[data-value="item.name"]').click(); + cy.wait(['@getAccessibleItems', '@getAccessibleItems']).then( + ([ + _first, + { + request: { url }, + }, + ]) => { + expect(url).to.contain('item.name'); + expect(url).to.contain('desc'); + }, + ); + + // change ordering + cy.get(SORTING_ORDERING_SELECTOR_DESC).click(); + cy.get(SORTING_ORDERING_SELECTOR_ASC).should('be.visible'); + cy.wait('@getAccessibleItems').then(({ request: { url } }) => { + expect(url).to.contain('asc'); + }); + }); + describe('Search', () => { it('Search should trigger refetch', () => { const searchText = 'mysearch'; diff --git a/cypress/e2e/item/publish/viewPublished.cy.ts b/cypress/e2e/item/publish/viewPublished.cy.ts index 3372c6715..e63089695 100644 --- a/cypress/e2e/item/publish/viewPublished.cy.ts +++ b/cypress/e2e/item/publish/viewPublished.cy.ts @@ -1,5 +1,6 @@ import { PackedFolderItemFactory } from '@graasp/sdk'; +import { SortingOptions } from '@/components/table/types'; import { BUILDER } from '@/langs/constants'; import i18n, { BUILDER_NAMESPACE } from '../../../../src/config/i18n'; @@ -9,6 +10,9 @@ import { ITEM_SEARCH_INPUT_ID, PUBLISHED_ITEMS_ERROR_ALERT_ID, PUBLISHED_ITEMS_ID, + SORTING_ORDERING_SELECTOR_ASC, + SORTING_ORDERING_SELECTOR_DESC, + SORTING_SELECT_SELECTOR, buildItemCard, } from '../../../../src/config/selectors'; import { PublishedItemFactory } from '../../../fixtures/items'; @@ -66,6 +70,35 @@ describe('Published Items', () => { cy.get(`#${buildItemCard(id)}`).should('be.visible'); } }); + + it.only('Sorting & Ordering', () => { + cy.get(`${SORTING_SELECT_SELECTOR} input`).should( + 'have.value', + SortingOptions.ItemUpdatedAt, + ); + cy.get(SORTING_ORDERING_SELECTOR_DESC).should('be.visible'); + + cy.get(SORTING_SELECT_SELECTOR).click(); + cy.get('li[data-value="item.name"]').click(); + + // check items are ordered by name + cy.get(`#${PUBLISHED_ITEMS_ID} h5`).then(($e) => { + items.sort((a, b) => (a.name < b.name ? 1 : -1)); + for (let idx = 0; idx < items.length; idx += 1) { + expect($e[idx].innerText).to.eq(items[idx].name); + } + }); + + // change ordering + cy.get(SORTING_ORDERING_SELECTOR_DESC).click(); + cy.get(SORTING_ORDERING_SELECTOR_ASC).should('be.visible'); + cy.get(`#${PUBLISHED_ITEMS_ID} h5`).then(($e) => { + items.reverse(); + for (let idx = 0; idx < items.length; idx += 1) { + expect($e[idx].innerText).to.eq(items[idx].name); + } + }); + }); }); describe('Error Handling', () => { diff --git a/cypress/e2e/item/trash/viewTrash.cy.ts b/cypress/e2e/item/trash/viewTrash.cy.ts index f248df548..2778882ba 100644 --- a/cypress/e2e/item/trash/viewTrash.cy.ts +++ b/cypress/e2e/item/trash/viewTrash.cy.ts @@ -1,14 +1,18 @@ import { PackedRecycledItemDataFactory } from '@graasp/sdk'; +import { SortingOptions } from '@/components/table/types'; import { BUILDER } from '@/langs/constants'; import i18n, { BUILDER_NAMESPACE } from '../../../../src/config/i18n'; -import { HOME_PATH, RECYCLE_BIN_PATH } from '../../../../src/config/paths'; +import { RECYCLE_BIN_PATH } from '../../../../src/config/paths'; import { CREATE_ITEM_BUTTON_ID, ITEM_SEARCH_INPUT_ID, RECYCLED_ITEMS_ERROR_ALERT_ID, RECYCLED_ITEMS_ROOT_CONTAINER, + SORTING_ORDERING_SELECTOR_ASC, + SORTING_ORDERING_SELECTOR_DESC, + SORTING_SELECT_SELECTOR, buildItemCard, } from '../../../../src/config/selectors'; import { CURRENT_USER } from '../../../fixtures/members'; @@ -19,7 +23,7 @@ const recycledItemData = [ PackedRecycledItemDataFactory(), ]; -describe('Bookmarked Item', () => { +describe('View trash', () => { describe('Member has no recycled items', () => { it('Show empty table', () => { cy.setUpApi({ @@ -39,11 +43,10 @@ describe('Bookmarked Item', () => { recycledItemData, }); i18n.changeLanguage(CURRENT_USER.extra.lang as string); - cy.visit(HOME_PATH); + cy.visit(RECYCLE_BIN_PATH); }); it('Empty search', () => { - cy.visit(RECYCLE_BIN_PATH); i18n.changeLanguage(CURRENT_USER.extra.lang as string); const searchText = 'mysearch'; cy.get(`#${ITEM_SEARCH_INPUT_ID}`).type(searchText); @@ -55,17 +58,43 @@ describe('Bookmarked Item', () => { }); it('New button should not exist', () => { - cy.visit(RECYCLE_BIN_PATH); cy.get(`#${CREATE_ITEM_BUTTON_ID}`).should('not.exist'); }); it('check recycled item layout', () => { - cy.visit(RECYCLE_BIN_PATH); - for (const { item } of recycledItemData) { cy.get(`#${buildItemCard(item.id)}`).should('be.visible'); } }); + + it.only('Sorting & Ordering', () => { + cy.get(`${SORTING_SELECT_SELECTOR} input`).should( + 'have.value', + SortingOptions.ItemUpdatedAt, + ); + cy.get(SORTING_ORDERING_SELECTOR_DESC).should('be.visible'); + + cy.get(SORTING_SELECT_SELECTOR).click(); + cy.get('li[data-value="item.name"]').click(); + + // check items are ordered by name + cy.get(`#${RECYCLED_ITEMS_ROOT_CONTAINER} h5`).then(($e) => { + recycledItemData.sort((a, b) => (a.item.name < b.item.name ? 1 : -1)); + for (let idx = 0; idx < recycledItemData.length; idx += 1) { + expect($e[idx].innerText).to.eq(recycledItemData[idx].item.name); + } + }); + + // change ordering + cy.get(SORTING_ORDERING_SELECTOR_DESC).click(); + cy.get(SORTING_ORDERING_SELECTOR_ASC).should('be.visible'); + cy.get(`#${RECYCLED_ITEMS_ROOT_CONTAINER} h5`).then(($e) => { + recycledItemData.reverse(); + for (let idx = 0; idx < recycledItemData.length; idx += 1) { + expect($e[idx].innerText).to.eq(recycledItemData[idx].item.name); + } + }); + }); }); describe('Error Handling', () => { diff --git a/cypress/e2e/item/view/viewFolder.cy.ts b/cypress/e2e/item/view/viewFolder.cy.ts index e3bdcd4c8..2754b45af 100644 --- a/cypress/e2e/item/view/viewFolder.cy.ts +++ b/cypress/e2e/item/view/viewFolder.cy.ts @@ -1,12 +1,17 @@ import { PackedFolderItemFactory } from '@graasp/sdk'; +import { SortingOptionsForFolder } from '../../../../src/components/table/types'; import i18n from '../../../../src/config/i18n'; import { buildItemPath } from '../../../../src/config/paths'; import { CREATE_ITEM_BUTTON_ID, ITEM_SEARCH_INPUT_ID, NAVIGATION_HOME_ID, + SORTING_ORDERING_SELECTOR_ASC, + SORTING_ORDERING_SELECTOR_DESC, + SORTING_SELECT_SELECTOR, buildItemCard, + buildItemsTableId, buildMapViewId, } from '../../../../src/config/selectors'; import { ItemLayoutMode } from '../../../../src/enums'; @@ -18,9 +23,11 @@ const item1 = PackedFolderItemFactory(); const child1 = PackedFolderItemFactory({ parentItem }); const child2 = PackedFolderItemFactory({ parentItem }); -const children = [child1, child2]; +const child3 = PackedFolderItemFactory({ parentItem }); +const child4 = PackedFolderItemFactory({ parentItem }); +const children = [child1, child2, child3, child4]; -const items = [parentItem, item1, child1, child2]; +const items = [parentItem, item1, ...children]; describe('View Folder', () => { it('View folder on map by default', () => { @@ -89,9 +96,41 @@ describe('View Folder', () => { cy.get(`#${ITEM_SEARCH_INPUT_ID}`).type(child1.name); cy.get(`#${buildItemCard(child1.id)}`).should('be.visible'); }); + + it.only('Sorting & Ordering', () => { + const { id } = parentItem; + cy.visit(buildItemPath(id)); + + cy.get(`${SORTING_SELECT_SELECTOR} input`).should( + 'have.value', + SortingOptionsForFolder.Order, + ); + cy.get(SORTING_ORDERING_SELECTOR_ASC).should('be.visible'); + + cy.get(SORTING_SELECT_SELECTOR).click(); + cy.get('li[data-value="item.name"]').click(); + + // check items are ordered by name + cy.get(`#${buildItemsTableId(parentItem.id)} h5`).then(($e) => { + children.sort((a, b) => (a.name > b.name ? 1 : -1)); + for (let idx = 0; idx < children.length; idx += 1) { + expect($e[idx].innerText).to.eq(children[idx].name); + } + }); + + // change ordering + cy.get(SORTING_ORDERING_SELECTOR_ASC).click(); + cy.get(SORTING_ORDERING_SELECTOR_DESC).should('be.visible'); + cy.get(`#${buildItemsTableId(parentItem.id)} h5`).then(($e) => { + children.reverse(); + for (let idx = 0; idx < children.length; idx += 1) { + expect($e[idx].innerText).to.eq(children[idx].name); + } + }); + }); }); -it('Layout mode', () => { +it('Folder Layout mode', () => { cy.setUpApi({ items: [parentItem, child1], }); diff --git a/src/components/item/FolderContent.tsx b/src/components/item/FolderContent.tsx index 0cf75cd21..46bd5cdb4 100644 --- a/src/components/item/FolderContent.tsx +++ b/src/components/item/FolderContent.tsx @@ -25,7 +25,8 @@ import ItemsTable from '../main/ItemsTable'; import NewItemButton from '../main/NewItemButton'; import { DesktopMap } from '../map/DesktopMap'; import SortingSelect from '../table/SortingSelect'; -import { SortingOptionsForFolder, useSorting } from '../table/useSorting'; +import { SortingOptionsForFolder } from '../table/types'; +import { useSorting } from '../table/useSorting'; import FolderDescription from './FolderDescription'; import { useItemSearch } from './ItemSearch'; import ModeButton from './header/ModeButton'; @@ -87,6 +88,7 @@ const FolderContent = ({ item }: { item: PackedItem }): JSX.Element => { {itemSearch.input} diff --git a/src/components/main/NewItemButton.tsx b/src/components/main/NewItemButton.tsx index 6d48e4f85..f08ac5045 100644 --- a/src/components/main/NewItemButton.tsx +++ b/src/components/main/NewItemButton.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { Add as AddIcon } from '@mui/icons-material'; +import { ButtonProps } from '@mui/material'; import { DiscriminatedItem } from '@graasp/sdk'; import { Button } from '@graasp/ui'; @@ -12,9 +13,13 @@ import NewItemModal from './NewItemModal'; type Props = { previousItemId?: DiscriminatedItem['id']; + size?: ButtonProps['size']; }; -const NewItemButton = ({ previousItemId }: Props): JSX.Element => { +const NewItemButton = ({ + previousItemId, + size = 'small', +}: Props): JSX.Element => { const [open, setOpen] = useState(false); const { t: translateBuilder } = useBuilderTranslation(); @@ -34,7 +39,7 @@ const NewItemButton = ({ previousItemId }: Props): JSX.Element => { color="primary" aria-label="add" startIcon={} - size="small" + size={size} > {translateBuilder(BUILDER.NEW_ITEM_BUTTON)} diff --git a/src/components/pages/BookmarkedItemsScreen.tsx b/src/components/pages/BookmarkedItemsScreen.tsx index 06672fe37..78e97406a 100644 --- a/src/components/pages/BookmarkedItemsScreen.tsx +++ b/src/components/pages/BookmarkedItemsScreen.tsx @@ -20,11 +20,8 @@ import { useItemSearch } from '../item/ItemSearch'; import ModeButton from '../item/header/ModeButton'; import ItemsTable from '../main/ItemsTable'; import SortingSelect from '../table/SortingSelect'; -import { - SortingOptions, - useSorting, - useTranslatedSortingOptions, -} from '../table/useSorting'; +import { SortingOptions } from '../table/types'; +import { useSorting, useTranslatedSortingOptions } from '../table/useSorting'; const BookmarkedItems = (): JSX.Element | null => { const { t: translateBuilder } = useBuilderTranslation(); diff --git a/src/components/pages/HomeScreen.tsx b/src/components/pages/HomeScreen.tsx index 575e8e4ec..fa3299530 100644 --- a/src/components/pages/HomeScreen.tsx +++ b/src/components/pages/HomeScreen.tsx @@ -33,7 +33,8 @@ import NewItemButton from '../main/NewItemButton'; import { DesktopMap } from '../map/DesktopMap'; import ShowOnlyMeButton from '../table/ShowOnlyMeButton'; import SortingSelect from '../table/SortingSelect'; -import { SortingOptions, useSorting } from '../table/useSorting'; +import { SortingOptions } from '../table/types'; +import { useSorting } from '../table/useSorting'; const HomeScreen = (): JSX.Element => { const { t: translateBuilder } = useBuilderTranslation(); @@ -232,7 +233,7 @@ const HomeScreen = (): JSX.Element => { spacing={1} > {itemSearch.input} - + diff --git a/src/components/pages/PublishedItemsScreen.tsx b/src/components/pages/PublishedItemsScreen.tsx index 33a9961ba..ff9aba080 100644 --- a/src/components/pages/PublishedItemsScreen.tsx +++ b/src/components/pages/PublishedItemsScreen.tsx @@ -21,11 +21,8 @@ import { useItemSearch } from '../item/ItemSearch'; import ModeButton from '../item/header/ModeButton'; import ItemsTable from '../main/ItemsTable'; import SortingSelect from '../table/SortingSelect'; -import { - SortingOptions, - useSorting, - useTranslatedSortingOptions, -} from '../table/useSorting'; +import { SortingOptions } from '../table/types'; +import { useSorting, useTranslatedSortingOptions } from '../table/useSorting'; const PublishedItemsScreen = (): JSX.Element | null => { const { t: translateBuilder } = useBuilderTranslation(); diff --git a/src/components/pages/RecycledItemsScreen.tsx b/src/components/pages/RecycledItemsScreen.tsx index 962660654..5f082f8f8 100644 --- a/src/components/pages/RecycledItemsScreen.tsx +++ b/src/components/pages/RecycledItemsScreen.tsx @@ -22,11 +22,8 @@ import { useItemSearch } from '../item/ItemSearch'; import ModeButton from '../item/header/ModeButton'; import ItemCard from '../table/ItemCard'; import SortingSelect from '../table/SortingSelect'; -import { - SortingOptions, - useSorting, - useTranslatedSortingOptions, -} from '../table/useSorting'; +import { SortingOptions } from '../table/types'; +import { useSorting, useTranslatedSortingOptions } from '../table/useSorting'; const RecycledItemsScreen = (): JSX.Element | null => { const { t: translateBuilder } = useBuilderTranslation(); diff --git a/src/components/table/SortingSelect.tsx b/src/components/table/SortingSelect.tsx index 8bfe138d7..9fdc78608 100644 --- a/src/components/table/SortingSelect.tsx +++ b/src/components/table/SortingSelect.tsx @@ -14,10 +14,11 @@ import { import { ArrowDownNarrowWide, ArrowUpWideNarrow } from 'lucide-react'; import { useBuilderTranslation } from '@/config/i18n'; +import { SORTING_SELECT_SELECTOR_TEST_ID } from '@/config/selectors'; import { Ordering } from '@/enums'; import { BUILDER } from '@/langs/constants'; -import { AllSortingOptions, SortingOptions } from './useSorting'; +import { AllSortingOptions, SortingOptions } from './types'; const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; @@ -63,6 +64,7 @@ export const SortingSelect = ({ {label}