diff --git a/packages/datagateway-common/src/api/index.test.tsx b/packages/datagateway-common/src/api/index.test.tsx index 01d349039..dedceba84 100644 --- a/packages/datagateway-common/src/api/index.test.tsx +++ b/packages/datagateway-common/src/api/index.test.tsx @@ -347,6 +347,7 @@ describe('generic api functions', () => { afterEach(() => { jest.restoreAllMocks(); jest.resetModules(); + window.history.pushState({}, 'Test', '/'); }); describe('useSort', () => { @@ -398,6 +399,47 @@ describe('generic api functions', () => { search: '?', }); }); + + it('returns callback that, when called without shift modifier, replaces sort with the new one', () => { + window.history.pushState( + {}, + 'Test', + '?sort=%7B%22name%22%3A%22asc%22%7D' + ); + const { result } = renderHook(() => useSort(), { + wrapper, + }); + + act(() => { + result.current('title', 'asc', 'push', false); + }); + + expect(pushSpy).toHaveBeenCalledWith({ + search: `?sort=${encodeURIComponent('{"title":"asc"}')}`, + }); + }); + + it('returns callback that, when called with shift modifier, appends new sort to the existing one', () => { + window.history.pushState( + {}, + 'Test', + '?sort=%7B%22name%22%3A%22asc%22%7D' + ); + + const { result } = renderHook(() => useSort(), { + wrapper, + }); + + act(() => { + result.current('title', 'asc', 'push', true); + }); + + console.log(history.location.search); + + expect(pushSpy).toHaveBeenCalledWith({ + search: `?sort=${encodeURIComponent('{"name":"asc","title":"asc"}')}`, + }); + }); }); describe('usePushFilter', () => { diff --git a/packages/datagateway-common/src/api/index.tsx b/packages/datagateway-common/src/api/index.tsx index 8c61ee671..d0b2638e2 100644 --- a/packages/datagateway-common/src/api/index.tsx +++ b/packages/datagateway-common/src/api/index.tsx @@ -260,7 +260,8 @@ export const getApiParams = ( export const useSort = (): (( sortKey: string, order: Order | null, - updateMethod: UpdateMethod + updateMethod: UpdateMethod, + shiftDown?: boolean ) => void) => { const { push, replace } = useHistory(); @@ -268,17 +269,25 @@ export const useSort = (): (( ( sortKey: string, order: Order | null, - updateMethod: UpdateMethod + updateMethod: UpdateMethod, + shiftDown?: boolean ): void => { let query = parseSearchToQuery(window.location.search); if (order !== null) { - query = { - ...query, - sort: { - ...query.sort, - [sortKey]: order, - }, - }; + query = shiftDown + ? { + ...query, + sort: { + ...query.sort, + [sortKey]: order, + }, + } + : { + ...query, + sort: { + [sortKey]: order, + }, + }; } else { // if order is null, user no longer wants to sort by that column so remove column from sort state const { [sortKey]: order, ...rest } = query.sort; diff --git a/packages/datagateway-common/src/card/cardView.component.test.tsx b/packages/datagateway-common/src/card/cardView.component.test.tsx index 1a5674810..023aefe7b 100644 --- a/packages/datagateway-common/src/card/cardView.component.test.tsx +++ b/packages/datagateway-common/src/card/cardView.component.test.tsx @@ -227,7 +227,7 @@ describe('Card View', () => { await user.click( await screen.findByRole('button', { name: 'Sort by TITLE' }) ); - expect(onSort).toHaveBeenNthCalledWith(1, 'title', 'asc', 'push'); + expect(onSort).toHaveBeenNthCalledWith(1, 'title', 'asc', 'push', false); updatedProps = { ...updatedProps, @@ -240,7 +240,7 @@ describe('Card View', () => { name: 'Sort by TITLE, current direction ascending', }) ); - expect(onSort).toHaveBeenNthCalledWith(2, 'title', 'desc', 'push'); + expect(onSort).toHaveBeenNthCalledWith(2, 'title', 'desc', 'push', false); updatedProps = { ...updatedProps, @@ -253,7 +253,7 @@ describe('Card View', () => { name: 'Sort by TITLE, current direction descending', }) ); - expect(onSort).toHaveBeenNthCalledWith(3, 'title', null, 'push'); + expect(onSort).toHaveBeenNthCalledWith(3, 'title', null, 'push', false); }); it('default sort applied correctly', () => { @@ -272,9 +272,9 @@ describe('Card View', () => { }; render(); - expect(onSort).toHaveBeenCalledWith('title', 'asc', 'replace'); - expect(onSort).toHaveBeenCalledWith('name', 'desc', 'replace'); - expect(onSort).toHaveBeenCalledWith('test', 'asc', 'replace'); + expect(onSort).toHaveBeenCalledWith('title', 'asc', 'replace', false); + expect(onSort).toHaveBeenCalledWith('name', 'desc', 'replace', false); + expect(onSort).toHaveBeenCalledWith('test', 'asc', 'replace', false); }); it('can sort by description with label', async () => { @@ -290,7 +290,7 @@ describe('Card View', () => { await user.click( await screen.findByRole('button', { name: 'Sort by NAME' }) ); - expect(onSort).toHaveBeenCalledWith('name', 'asc', 'push'); + expect(onSort).toHaveBeenCalledWith('name', 'asc', 'push', false); }); it('can sort by description without label', async () => { @@ -306,7 +306,7 @@ describe('Card View', () => { await user.click( await screen.findByRole('button', { name: 'Sort by NAME' }) ); - expect(onSort).toHaveBeenCalledWith('name', 'asc', 'push'); + expect(onSort).toHaveBeenCalledWith('name', 'asc', 'push', false); }); it('page changed when sort applied', async () => { @@ -318,7 +318,7 @@ describe('Card View', () => { await screen.findByRole('button', { name: 'Sort by TITLE' }) ); - expect(onSort).toHaveBeenCalledWith('title', 'asc', 'push'); + expect(onSort).toHaveBeenCalledWith('title', 'asc', 'push', false); expect(onPageChange).toHaveBeenCalledWith(1); }); @@ -377,7 +377,53 @@ describe('Card View', () => { await user.click( await screen.findByRole('button', { name: 'Sort by VISITID' }) ); - expect(onSort).toHaveBeenCalledWith('visitId', 'asc', 'push'); + expect(onSort).toHaveBeenCalledWith('visitId', 'asc', 'push', false); + }); + + it('calls onSort with correct parameters when shift key is pressed', async () => { + const updatedProps = { + ...props, + title: { dataKey: 'title', disableSort: true }, + description: { dataKey: 'name', disableSort: true }, + information: [ + { dataKey: 'visitId' }, + { + dataKey: 'name', + label: 'Name', + content: (entity: Entity) => entity.name, + }, + ], + page: 1, + }; + render(); + + // Click with shift to sort ascending + await user.keyboard('{Shift>}'); + await user.click( + await screen.findByRole('button', { name: 'Sort by VISITID' }) + ); + await user.keyboard('{/Shift}'); + + expect(onSort).toHaveBeenCalledWith('visitId', 'asc', 'push', true); + + await user.click( + await screen.findByRole('button', { name: 'Sort by NAME' }) + ); + + expect(onSort).toHaveBeenCalledWith('name', 'asc', 'push', false); + }); + + it('displays correct icon when no sort applied', async () => { + render(); + + const cards = await screen.findAllByRole('button', { name: /Sort by/ }); + expect( + within(cards[0]).getByTestId(`ArrowDownwardIcon`) + ).toBeInTheDocument(); + + await user.keyboard('{Shift>}'); + expect(within(cards[0]).getByTestId(`AddIcon`)).toBeInTheDocument(); + await user.keyboard('{/Shift}'); }); it('information displays with content that has no tooltip', async () => { diff --git a/packages/datagateway-common/src/card/cardView.component.tsx b/packages/datagateway-common/src/card/cardView.component.tsx index fcbb48d44..e7dab298d 100644 --- a/packages/datagateway-common/src/card/cardView.component.tsx +++ b/packages/datagateway-common/src/card/cardView.component.tsx @@ -33,6 +33,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import AdvancedFilter from './advancedFilter.component'; import EntityCard, { EntityImageDetails } from './entityCard.component'; +import AddIcon from '@mui/icons-material/Add'; const SelectedChips = styled('ul')(({ theme }) => ({ display: 'inline-flex', @@ -85,7 +86,8 @@ export interface CardViewProps { onSort: ( sort: string, order: Order | null, - updateMethod: UpdateMethod + updateMethod: UpdateMethod, + shiftDown?: boolean ) => void; // Props to get title, description of the card @@ -189,6 +191,30 @@ const CardView = (props: CardViewProps): React.ReactElement => { sort, } = props; + const [shiftDown, setShiftDown] = React.useState(false); + // add event listener to listen for shift key being pressed + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent): void => { + if (event.key === 'Shift') { + setShiftDown(true); + } + }; + + const handleKeyUp = (event: KeyboardEvent): void => { + if (event.key === 'Shift') { + setShiftDown(false); + } + }; + + document.addEventListener('keydown', handleKeyDown); + document.addEventListener('keyup', handleKeyUp); + + return (): void => { + document.removeEventListener('keydown', handleKeyDown); + document.removeEventListener('keyup', handleKeyUp); + }; + }, []); + // Results options (by default it is 10, 20 and 30). const resOptions = React.useMemo( () => @@ -233,20 +259,20 @@ const CardView = (props: CardViewProps): React.ReactElement => { //defaultSort has been provided React.useEffect(() => { if (title.defaultSort !== undefined && sort[title.dataKey] === undefined) - onSort(title.dataKey, title.defaultSort, 'replace'); + onSort(title.dataKey, title.defaultSort, 'replace', false); if ( description && description.defaultSort !== undefined && sort[description.dataKey] === undefined ) - onSort(description.dataKey, description.defaultSort, 'replace'); + onSort(description.dataKey, description.defaultSort, 'replace', false); if (information) { information.forEach((element: CardViewDetails) => { if ( element.defaultSort !== undefined && sort[element.dataKey] === undefined ) - onSort(element.dataKey, element.defaultSort, 'replace'); + onSort(element.dataKey, element.defaultSort, 'replace', false); }); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -593,11 +619,12 @@ const CardView = (props: CardViewProps): React.ReactElement => { { + onClick={(event) => { onSort( s.dataKey, nextSortDirection(s.dataKey), - 'push' + 'push', + event.shiftKey ); if (page !== 1) { onPageChange(1); @@ -615,14 +642,21 @@ const CardView = (props: CardViewProps): React.ReactElement => { > - - {s.dataKey in sort && sort[s.dataKey]} - + { + + {s.dataKey in sort && sort[s.dataKey]} + + } ))} diff --git a/packages/datagateway-common/src/table/headerRenderers/__snapshots__/dataHeader.component.test.tsx.snap b/packages/datagateway-common/src/table/headerRenderers/__snapshots__/dataHeader.component.test.tsx.snap index f463d63d1..fd5596d65 100644 --- a/packages/datagateway-common/src/table/headerRenderers/__snapshots__/dataHeader.component.test.tsx.snap +++ b/packages/datagateway-common/src/table/headerRenderers/__snapshots__/dataHeader.component.test.tsx.snap @@ -131,7 +131,9 @@ exports[`Data column header component renders correctly with sort and filter 1`] class="MuiBox-root css-0" > @@ -142,13 +144,13 @@ exports[`Data column header component renders correctly with sort and filter 1`]

@@ -257,7 +259,9 @@ exports[`Data column header component renders correctly with sort but no filter class="MuiBox-root css-0" > @@ -268,13 +272,13 @@ exports[`Data column header component renders correctly with sort but no filter

diff --git a/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.test.tsx b/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.test.tsx index 96dfe5a5f..b357cc3eb 100644 --- a/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.test.tsx +++ b/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.test.tsx @@ -71,7 +71,7 @@ describe('Data column header component', () => { it('sets asc order', async () => { render(); await user.click(await screen.findByRole('button', { name: 'Test' })); - expect(onSort).toHaveBeenCalledWith('test', 'asc', 'push'); + expect(onSort).toHaveBeenCalledWith('test', 'asc', 'push', false); }); it('sets desc order', async () => { @@ -84,7 +84,7 @@ describe('Data column header component', () => { /> ); await user.click(await screen.findByRole('button', { name: 'Test' })); - expect(onSort).toHaveBeenCalledWith('test', 'desc', 'push'); + expect(onSort).toHaveBeenCalledWith('test', 'desc', 'push', false); }); it('sets null order', async () => { @@ -97,7 +97,7 @@ describe('Data column header component', () => { /> ); await user.click(await screen.findByRole('button', { name: 'Test' })); - expect(onSort).toHaveBeenCalledWith('test', null, 'push'); + expect(onSort).toHaveBeenCalledWith('test', null, 'push', false); }); }); @@ -105,11 +105,28 @@ describe('Data column header component', () => { it('sets asc order', () => { render(); - expect(onSort).toHaveBeenCalledWith('test', 'asc', 'replace'); + expect(onSort).toHaveBeenCalledWith('test', 'asc', 'replace', false); }); it('sets desc order', () => { render(); - expect(onSort).toHaveBeenCalledWith('test', 'desc', 'replace'); + expect(onSort).toHaveBeenCalledWith('test', 'desc', 'replace', false); + }); + }); + + describe('changes icons in the label', () => { + it('changes icon to Add when shift is pressed', async () => { + render(); + expect(screen.getByTestId('AddIcon')).toBeInTheDocument(); + }); + + it('changes icon to ArrowUpward when hovered', async () => { + render(); + expect(screen.getByTestId('SortIcon')).toBeInTheDocument(); + await user.hover(await screen.findByRole('button', { name: 'Test' })); + expect(screen.getByTestId('ArrowUpwardIcon')).toBeInTheDocument(); + + await user.unhover(await screen.findByRole('button', { name: 'Test' })); + expect(screen.getByTestId('SortIcon')).toBeInTheDocument(); }); }); diff --git a/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.tsx b/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.tsx index 0fb8aa963..ccc62f224 100644 --- a/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.tsx +++ b/packages/datagateway-common/src/table/headerRenderers/dataHeader.component.tsx @@ -9,7 +9,11 @@ import { Divider, SxProps, } from '@mui/material'; +import { StyledTooltip } from '../../arrowtooltip.component'; import Draggable from 'react-draggable'; +import SortIcon from '@mui/icons-material/Sort'; +import AddIcon from '@mui/icons-material/Add'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; const DataHeader = ( props: TableHeaderProps & { @@ -18,13 +22,15 @@ const DataHeader = ( onSort: ( column: string, order: Order | null, - defaultSort: UpdateMethod + defaultSort: UpdateMethod, + shiftDown?: boolean ) => void; resizeColumn: (dataKey: string, deltaX: number) => void; labelString: string; icon?: React.ComponentType; filterComponent?: (label: string, dataKey: string) => React.ReactElement; defaultSort?: Order; + shiftDown?: boolean; } ): React.ReactElement => { const { @@ -39,15 +45,18 @@ const DataHeader = ( resizeColumn, icon: Icon, filterComponent, + shiftDown, } = props; const currSortDirection = sort[dataKey]; + const [hover, setHover] = React.useState(false); + //Apply default sort on page load (but only if not already defined in URL params) //This will apply them in the order of the column definitions given to a table React.useEffect(() => { if (defaultSort !== undefined && currSortDirection === undefined) - onSort(dataKey, defaultSort, 'replace'); + onSort(dataKey, defaultSort, 'replace', false); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -64,16 +73,39 @@ const DataHeader = ( } const inner = !disableSort ? ( - onSort(dataKey, nextSortDirection, 'push')} + - - {label} - - + { + onSort(dataKey, nextSortDirection, 'push', event.shiftKey); + if (!(dataKey in sort)) setHover(false); + }} + {...(!(dataKey in sort) + ? { + onMouseEnter: () => setHover(true), + onMouseLeave: () => setHover(false), + IconComponent: hover + ? ArrowUpwardIcon + : shiftDown + ? AddIcon + : SortIcon, + sx: { + transition: 'opacity 0.5s', + opacity: hover ? 0.7 : 1, + }, + } + : {})} + > + + {label} + + + ) : ( {label} diff --git a/packages/datagateway-common/src/table/table.component.test.tsx b/packages/datagateway-common/src/table/table.component.test.tsx index 527561ffe..1e4030d8d 100644 --- a/packages/datagateway-common/src/table/table.component.test.tsx +++ b/packages/datagateway-common/src/table/table.component.test.tsx @@ -76,7 +76,7 @@ describe('Table component', () => { it('calls onSort function when sort label clicked', async () => { render(); await user.click(await screen.findByText('Test 1')); - expect(onSort).toHaveBeenCalledWith('TEST1', 'asc', 'push'); + expect(onSort).toHaveBeenCalledWith('TEST1', 'asc', 'push', false); }); it('calls onSort function when defaultSort has been specified', () => { @@ -89,8 +89,23 @@ describe('Table component', () => { }; render(
); - expect(onSort).toHaveBeenCalledWith('TEST1', 'asc', 'replace'); - expect(onSort).toHaveBeenCalledWith('TEST2', 'desc', 'replace'); + expect(onSort).toHaveBeenCalledWith('TEST1', 'asc', 'replace', false); + expect(onSort).toHaveBeenCalledWith('TEST2', 'desc', 'replace', false); + }); + + it('calls onSort with correct parameters when shift key is pressed', async () => { + render(
); + + // Click with shift to sort ascending + await user.keyboard('{Shift>}'); + await user.click(await screen.findByRole('button', { name: 'Test 1' })); + await user.keyboard('{/Shift}'); + + expect(onSort).toHaveBeenCalledWith('TEST1', 'asc', 'push', true); + + await user.click(await screen.findByRole('button', { name: 'Test 2' })); + + expect(onSort).toHaveBeenCalledWith('TEST2', 'asc', 'push', false); }); it('renders select column correctly', async () => { diff --git a/packages/datagateway-common/src/table/table.component.tsx b/packages/datagateway-common/src/table/table.component.tsx index 5c5d3d9da..aa07dd089 100644 --- a/packages/datagateway-common/src/table/table.component.tsx +++ b/packages/datagateway-common/src/table/table.component.tsx @@ -163,6 +163,30 @@ const VirtualizedTable = React.memo( disableSelectAll, } = props; + const [shiftDown, setShiftDown] = React.useState(false); + // add event listener to listen for shift key being pressed + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent): void => { + if (event.key === 'Shift') { + setShiftDown(true); + } + }; + + const handleKeyUp = (event: KeyboardEvent): void => { + if (event.key === 'Shift') { + setShiftDown(false); + } + }; + + document.addEventListener('keydown', handleKeyDown); + document.addEventListener('keyup', handleKeyUp); + + return (): void => { + document.removeEventListener('keydown', handleKeyDown); + document.removeEventListener('keyup', handleKeyUp); + }; + }, []); + if ( (props.loadMoreRows && typeof totalRowCount === 'undefined') || (totalRowCount && typeof props.loadMoreRows === 'undefined') @@ -452,6 +476,7 @@ const VirtualizedTable = React.memo( filterComponent={filterComponent} resizeColumn={resizeColumn} defaultSort={defaultSort} + shiftDown={shiftDown} /> )} className={className} diff --git a/packages/datagateway-dataview/cypress/e2e/card/datasets.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/datasets.cy.ts index 3296fe33b..44c985b40 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/datasets.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/datasets.cy.ts @@ -50,13 +50,13 @@ describe('Datasets Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('DATASET 1'); - // multiple fields + // multiple fields (shift click) cy.contains('[role="button"]', 'Create Time').click(); cy.wait('@getDatasetsOrder', { timeout: 10000, }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@getDatasetsOrder', { timeout: 10000, }); @@ -65,6 +65,17 @@ describe('Datasets Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('DATASET 1'); + + // should replace current sort if clicked without shift + cy.get('@nameSortButton').click(); + + cy.contains('[aria-label="Sort by NAME"]', 'desc').should('exist'); + cy.contains('[role="button"]', 'desc').should('exist'); + cy.contains('[aria-label="Sort by CREATE TIME"]', 'asc').should( + 'not.exist' + ); + + cy.get('[data-testid="card"]').first().contains('DATASET 61'); }); it('should be able to filter by multiple fields', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/card/dls/datasets.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/dls/datasets.cy.ts index fa7e2cdd9..b9455fa83 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/dls/datasets.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/dls/datasets.cy.ts @@ -81,13 +81,13 @@ describe('DLS - Datasets Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('DATASET 1'); - // multiple fields + // multiple fields (shift click) cy.get('@timeSortButton').click(); cy.wait('@getDatasetsOrder', { timeout: 10000, }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@getDatasetsOrder', { timeout: 10000, }); @@ -96,6 +96,17 @@ describe('DLS - Datasets Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('DATASET 1'); + + // should replace current sort if clicked without shift + cy.get('@nameSortButton').click(); + + cy.contains('[aria-label="Sort by NAME"]', 'desc').should('exist'); + cy.contains('[role="button"]', 'desc').should('exist'); + cy.contains('[aria-label="Sort by CREATE TIME"]', 'asc').should( + 'not.exist' + ); + + cy.get('[data-testid="card"]').first().contains('DATASET 61'); }); it('should be able to filter by multiple fields', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/card/investigations.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/investigations.cy.ts index 59aa31f72..2ba276b02 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/investigations.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/investigations.cy.ts @@ -63,9 +63,9 @@ describe('Investigations Cards', () => { .first() .contains('Analysis reflect work or hour color maybe.'); - // multiple fields + // multiple fields (shift click) cy.contains('[role="button"]', 'Start Date').click(); - cy.get('@titleSortButton').click(); + cy.get('@titleSortButton').click({ shiftKey: true }); cy.wait('@getInvestigationsOrder', { timeout: 10000 }); cy.contains('[aria-label="Sort by TITLE"]', 'asc').should('exist'); @@ -74,6 +74,14 @@ describe('Investigations Cards', () => { cy.get('[data-testid="card"]') .first() .contains('Analysis reflect work or hour color maybe.'); + + // should replace current sort if clicked without shift + cy.contains('[role="button"]', 'Start Date').click(); + cy.wait('@getInvestigationsOrder', { timeout: 10000 }); + + cy.contains('[aria-label="Sort by START DATE"]', 'desc').should('exist'); + cy.contains('[aria-label="Sort by TITLE"]', 'asc').should('not.exist'); + cy.get('[data-testid="card"]').first().contains('Put modern else answer.'); }); it('should be able to filter by multiple fields', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/card/isis/dataPublications.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/isis/dataPublications.cy.ts index e38c1b435..918aa94dc 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/isis/dataPublications.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/isis/dataPublications.cy.ts @@ -72,9 +72,9 @@ describe('ISIS - Data Publication Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('Article'); - // multiple fields + // multiple fields (shift click) cy.contains('[role="button"]', 'Title').click(); - cy.get('@dateSortButton').click(); + cy.get('@dateSortButton').click({ shiftKey: true }); cy.wait('@getDataPublicationsOrder', { timeout: 10000 }); cy.contains('[aria-label="Sort by TITLE"]', 'asc').should('exist'); @@ -83,6 +83,17 @@ describe('ISIS - Data Publication Cards', () => { ); cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('Article'); + + // should replace current sort if clicked without shift + cy.get('@dateSortButton').click(); + cy.wait('@getDataPublicationsOrder', { timeout: 10000 }); + + cy.contains('[aria-label="Sort by PUBLICATION DATE"]', 'desc').should( + 'exist' + ); + cy.contains('[aria-label="Sort by TITLE"]', 'asc').should('not.exist'); + + cy.get('[data-testid="card"]').first().contains('Church'); }); it('should be able to filter by multiple fields', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/card/isis/datasets.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/isis/datasets.cy.ts index 0ae174db3..62407373b 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/isis/datasets.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/isis/datasets.cy.ts @@ -84,13 +84,13 @@ describe('ISIS - Datasets Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('DATASET 19'); - // multiple fields + // multiple fields (shift click) cy.get('@timeSortButton').click(); cy.wait('@getDatasetsOrder', { timeout: 10000, }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@getDatasetsOrder', { timeout: 10000, }); @@ -99,6 +99,16 @@ describe('ISIS - Datasets Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('DATASET 19'); + + // should replace current sort if clicked without shift + cy.get('@nameSortButton').click(); + + cy.contains('[aria-label="Sort by NAME"]', 'desc').should('exist'); + cy.contains('[aria-label="Sort by CREATE TIME"]', 'asc').should( + 'not.exist' + ); + + cy.get('[data-testid="card"]').first().contains('DATASET 79'); }); it('should be able to filter by multiple fields', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/card/isis/facilityCycles.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/isis/facilityCycles.cy.ts index 88695d603..68533d878 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/isis/facilityCycles.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/isis/facilityCycles.cy.ts @@ -53,15 +53,23 @@ describe('ISIS - FacilityCycles Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('2001 cycle 2'); - // multiple fields + // multiple fields (shift click) cy.get('@dateSortButton').click(); - cy.contains('[role="button"]', 'End Date').click(); + cy.contains('[role="button"]', 'End Date').click({ shiftKey: true }); cy.wait('@getFacilityCyclesOrder', { timeout: 10000 }); cy.contains('[aria-label="Sort by START DATE"]', 'asc').should('exist'); cy.contains('[aria-label="Sort by END DATE"]', 'asc').should('exist'); cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('2001 cycle 2'); + + // should replace current sort if clicked without shift + cy.get('@dateSortButton').click(); + + cy.contains('[aria-label="Sort by START DATE"]', 'desc').should('exist'); + cy.contains('[aria-label="Sort by END DATE"]', 'asc').should('not.exist'); + cy.contains('[role="button"]', 'asc').should('not.exist'); + cy.get('[data-testid="card"]').first().contains('2004 cycle 3'); }); it('should be able to filter by multiple fields', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/card/isis/instruments.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/isis/instruments.cy.ts index 2937c76f0..753505c16 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/isis/instruments.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/isis/instruments.cy.ts @@ -47,11 +47,9 @@ describe('ISIS - Instruments Cards', () => { //Revert the default sort cy.contains('[role="button"]', 'Name').as('nameSortButton').click(); cy.get('@nameSortButton').click(); - cy.wait('@getInstrumentsOrder', { timeout: 10000 }); // ascending cy.get('@nameSortButton').click(); - cy.wait('@getInstrumentsOrder', { timeout: 10000 }); cy.contains('[role="button"]', 'asc').should('exist'); cy.contains('[role="button"]', 'desc').should('not.exist'); @@ -61,7 +59,6 @@ describe('ISIS - Instruments Cards', () => { // descending cy.get('@nameSortButton').click(); - cy.wait('@getInstrumentsOrder', { timeout: 10000 }); cy.contains('[role="button"]', 'asc').should('not.exist'); cy.contains('[role="button"]', 'desc').should('exist'); @@ -77,25 +74,35 @@ describe('ISIS - Instruments Cards', () => { .first() .contains('Stop prove field onto think suffer measure.'); + // multiple fields (shift click) cy.contains('[role="button"]', 'Description').click(); - cy.wait('@getInstrumentsOrder', { timeout: 10000 }); cy.get('[data-testid="card"]') .first() .contains('Sound low certain challenge yet sport happy.'); - cy.contains('[role="button"]', 'Type').click(); - cy.wait('@getInstrumentsOrder', { timeout: 10000 }); + cy.contains('[role="button"]', 'Type').click({ shiftKey: true }); cy.contains('[aria-label="Sort by DESCRIPTION"]', 'asc').should('exist'); cy.contains('[aria-label="Sort by TYPE"]', 'asc').should('exist'); cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('10'); + + // should replace current sort if clicked without shift + cy.contains('[role="button"]', 'Name').click(); + cy.contains('[aria-label="Sort by NAME"]', 'asc').should('exist'); + cy.contains('[aria-label="Sort by DESCRIPTION"]', 'asc').should( + 'not.exist' + ); + cy.contains('[aria-label="Sort by TYPE"]', 'asc').should('not.exist'); + cy.contains('[role="button"]', 'desc').should('not.exist'); + cy.get('[data-testid="card"]') + .first() + .contains('Eight imagine picture tough.'); }); it('should be able to filter by multiple fields', () => { //Revert the default sort cy.contains('[role="button"]', 'Name').as('nameSortButton').click(); cy.get('@nameSortButton').click(); - cy.wait('@getInstrumentsOrder', { timeout: 10000 }); cy.get('[data-testid="advanced-filters-link"]').click(); cy.get('[aria-label="Filter by Name"]').first().type('oil'); diff --git a/packages/datagateway-dataview/cypress/e2e/card/isis/investigations.cy.ts b/packages/datagateway-dataview/cypress/e2e/card/isis/investigations.cy.ts index 408b68aa1..0c6cd802c 100644 --- a/packages/datagateway-dataview/cypress/e2e/card/isis/investigations.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/card/isis/investigations.cy.ts @@ -146,15 +146,24 @@ describe('ISIS - Investigations Cards', () => { cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('Stop system investment'); - // multiple fields + // multiple fields (shift click) cy.get('@dateSortButton').click(); - cy.get('@titleSortButton').click(); + cy.get('@titleSortButton').click({ shiftKey: true }); cy.wait('@getInvestigationsOrder', { timeout: 10000 }); cy.contains('[aria-label="Sort by TITLE"]', 'asc').should('exist'); cy.contains('[aria-label="Sort by START DATE"]', 'asc').should('exist'); cy.contains('[role="button"]', 'desc').should('not.exist'); cy.get('[data-testid="card"]').first().contains('Stop system investment'); + + // should replace current sort if clicked without shift + cy.get('@titleSortButton').click(); + cy.wait('@getInvestigationsOrder', { timeout: 10000 }); + + cy.contains('[aria-label="Sort by TITLE"]', 'desc').should('exist'); + cy.contains('[aria-label="Sort by START DATE"]', 'asc').should('not.exist'); + cy.contains('[role="button"]', 'asc').should('not.exist'); + cy.get('[data-testid="card"]').first().contains('Stop system investment'); }); it('should be able to filter by multiple fields', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/datafiles.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/datafiles.cy.ts index 8c37e62fc..813e5d290 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/datafiles.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/datafiles.cy.ts @@ -70,23 +70,65 @@ describe('Datafiles Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 4); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="4"]').contains( '/five/with/question.bmp' ); - // multiple columns + // multiple columns (shift click) cy.contains('[role="button"]', 'Name').click(); cy.wait('@datafilesOrder', { timeout: 10000 }); - cy.contains('[role="button"]', 'Modified Time').click(); + cy.contains('[role="button"]', 'Modified Time').click({ shiftKey: true }); cy.wait('@datafilesOrder', { timeout: 10000 }); cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('Datafile 1071'); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Location').click(); + cy.get('[aria-sort="ascending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="4"]').contains( + '/able/leg/policy.gif' + ); + }); + + it('should change icons when sorting on a column', () => { + cy.wait( + [ + '@investigations', + '@datafilesCount', + '@datasets', + '@datafilesOrder', + '@datafilesOrder', + ], + { + timeout: 10000, + } + ); + + cy.get('[data-testid="SortIcon"]').should('have.length', 4); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Location').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Location').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Name').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 2); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/datasets.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/datasets.cy.ts index fa73864af..05af2e223 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/datasets.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/datasets.cy.ts @@ -55,20 +55,49 @@ describe('Datasets Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 1'); + // multiple columns (shift + click) cy.contains('[role="button"]', 'Create Time').as('timeSortButton').click(); - cy.get('@timeSortButton').click(); - cy.get('@nameSortButton').click(); - cy.get('@nameSortButton').click(); + cy.get('@timeSortButton').click({ shiftKey: true }); + cy.get('@nameSortButton').click({ shiftKey: true }); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 61'); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Modified Time').click(); + cy.contains('[role="button"]', 'Modified Time').click(); + cy.get('[aria-sort="descending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 61'); + }); + + it('should change icons when sorting on a column', () => { + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Create Time').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Create Time').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Modified Time').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 1); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/dls/datafiles.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/dls/datafiles.cy.ts index f8e316209..10cd0da45 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/dls/datafiles.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/dls/datafiles.cy.ts @@ -71,25 +71,56 @@ describe('DLS - Datafiles Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 4); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="4"]').contains( '/time/run/drug.jpeg' ); - // multiple columns + // multiple columns (shift click) cy.get('@timeSortButton').click(); cy.wait('@datafilesOrder', { timeout: 10000 }); - cy.contains('[role="button"]', 'Name').as('nameSortButton').click(); + cy.contains('[role="button"]', 'Name') + .as('nameSortButton') + .click({ shiftKey: true }); cy.wait('@datafilesOrder', { timeout: 10000 }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@datafilesOrder', { timeout: 10000 }); cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('Datafile 60'); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Location').click(); + cy.get('[aria-sort="ascending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="4"]').contains( + '/analysis/unit/bank.tiff' + ); + }); + + it('should change icons when sorting on a column', () => { + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Location').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Location').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Name').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 2); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/dls/datasets.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/dls/datasets.cy.ts index a5f9ce928..1614f8f56 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/dls/datasets.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/dls/datasets.cy.ts @@ -1,7 +1,6 @@ describe('DLS - Datasets Table', () => { beforeEach(() => { cy.intercept('**/investigations/1').as('investigations'); - cy.intercept('**/investigations/findone?*').as('investigationsFindOne'); cy.intercept('**/datasets/count?*').as('datasetsCount'); cy.intercept('**/datasets?*').as('datasets'); cy.login(); @@ -9,7 +8,6 @@ describe('DLS - Datasets Table', () => { [ '@investigations', '@investigations', - '@investigationsFindOne', '@datasetsCount', '@datasets', '@datasets', @@ -78,24 +76,55 @@ describe('DLS - Datasets Table', () => { cy.get('@nameSortButton').click(); cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 1'); - // multiple columns + // multiple columns (shift click) cy.get('@timeSortButton').click(); cy.wait('@datasets', { timeout: 10000 }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@datasets', { timeout: 10000 }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@datasets', { timeout: 10000 }); cy.get('[aria-rowcount="2"]').should('exist'); cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 1'); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Modified Time').click(); + cy.contains('[role="button"]', 'Modified Time').click(); + cy.get('[aria-sort="descending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 61'); + }); + + it('should change icons when sorting on a column', () => { + // clear the default sort + cy.contains('[role="button"]', 'Create Time').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Create Time').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Create Time').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Modified Time').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 1); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/dls/myData.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/dls/myData.cy.ts index 803ddc0fa..bbf38f5c9 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/dls/myData.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/dls/myData.cy.ts @@ -68,23 +68,53 @@ describe('DLS - MyData Table', () => { cy.get('@titleSortButton').click(); cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 5); + + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); cy.get('[aria-rowindex="1"] [aria-colindex="2"]').contains( 'Star enter wide nearly off.' ); - // multiple columns + // multiple columns (shift + click) cy.get('@titleSortButton').click(); - cy.contains('[role="button"]', 'Instrument').click(); + cy.contains('[role="button"]', 'Instrument').click({ shiftKey: true }); cy.get('[aria-rowindex="1"] [aria-colindex="2"]').contains( 'Across prepare why go.' ); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Visit').click(); + cy.get('[aria-sort="ascending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('72'); + }); + + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Start Date').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 5); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Instrument').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Instrument').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Visit').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 3); }); it('should be able to filter with role, text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/investigations.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/investigations.cy.ts index 4a046e913..1153dea63 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/investigations.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/investigations.cy.ts @@ -65,22 +65,49 @@ describe('Investigations Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 6); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains( 'Analysis reflect work or hour color maybe. Much team discussion message weight.' ); - // multiple columns + // multiple columns (shift + click) cy.contains('[role="button"]', 'Start Date').click(); - cy.get('@titleSortButton').click(); + cy.get('@titleSortButton').click({ shiftKey: true }); cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains( 'Analysis reflect work or hour color maybe. Much team discussion message weight.' ); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Visit').click(); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="4"]').contains('1'); + }); + + it('should change icons when sorting on a column', () => { + cy.get('[data-testid="SortIcon"]').should('have.length', 6); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Visit').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Visit').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Title').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 4); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/isis/dataPublications.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/isis/dataPublications.cy.ts index e997bea4f..0b0fee3ec 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/isis/dataPublications.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/isis/dataPublications.cy.ts @@ -80,24 +80,54 @@ describe('ISIS - Data Publication Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.be.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="1"]').contains( 'Article subject amount' ); - // multiple columns - cy.get('@dateSortButton').click(); + // multiple columns (shift click) cy.get('@dateSortButton').click(); - cy.get('@titleSortButton').click(); + cy.get('@dateSortButton').click({ shiftKey: true }); + cy.get('@titleSortButton').click({ shiftKey: true }); cy.get('[aria-rowindex="1"] [aria-colindex="1"]').contains( 'Church child time Congress' ); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Publication Date').click(); + cy.get('[aria-sort="ascending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('2010-02-24'); + }); + + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Publication Date').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'DOI').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'DOI').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Title').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 1); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/isis/datafiles.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/isis/datafiles.cy.ts index 9a36b164d..51ae140a2 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/isis/datafiles.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/isis/datafiles.cy.ts @@ -87,25 +87,60 @@ describe('ISIS - Datafiles Table', () => { cy.get('@locationSortButton').click(); cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 4); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="4"]').contains( '/debate/form/growth.gif' ); - // multiple columns + // multiple columns (shift click) cy.get('@timeSortButton').click(); cy.wait('@datafilesOrder', { timeout: 10000 }); - cy.contains('[role="button"]', 'Name').as('nameSortButton').click(); + cy.contains('[role="button"]', 'Name') + .as('nameSortButton') + .click({ shiftKey: true }); cy.wait('@datafilesOrder', { timeout: 10000 }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@datafilesOrder', { timeout: 10000 }); cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('Datafile 78'); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Location').click(); + cy.wait('@datafilesOrder', { timeout: 10000 }); + cy.get('[aria-sort="ascending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="4"]').contains( + '/add/go/interview.png' + ); + }); + + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Modified Time').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 4); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Location').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Location').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Name').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 2); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/isis/datasets.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/isis/datasets.cy.ts index 702e7b5c1..985098ac6 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/isis/datasets.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/isis/datasets.cy.ts @@ -78,21 +78,52 @@ describe('ISIS - Datasets Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 19'); - // multiple columns + // multiple columns (shift click) cy.get('@timeSortButton').click(); cy.wait('@datasetsOrder', { timeout: 10000 }); - cy.get('@nameSortButton').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); cy.wait('@datasetsOrder', { timeout: 10000 }); cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 19'); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Modified Time').click(); + cy.contains('[role="button"]', 'Modified Time').click(); + cy.get('[aria-sort="descending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('DATASET 79'); + }); + + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Create Time').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Create Time').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Create Time').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Modified Time').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 1); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/isis/facilityCycles.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/isis/facilityCycles.cy.ts index 9ec657116..da858de13 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/isis/facilityCycles.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/isis/facilityCycles.cy.ts @@ -59,15 +59,51 @@ describe('ISIS - FacilityCycles Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="2"]').contains( '2001-04-02 00:00:00' ); + + // multiple columns (shift click) + cy.contains('[role="button"]', 'Start Date').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); + cy.get('[aria-rowindex="1"] [aria-colindex="1"]').contains('2001 cycle 2'); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'End Date').click(); + cy.contains('[role="button"]', 'End Date').click(); + cy.get('[aria-sort="descending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="1"]').contains('2004 cycle 3'); + }); + + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Start Date').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 3); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Start Date').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Start Date').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'End Date').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 1); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-dataview/cypress/e2e/table/isis/instruments.cy.ts b/packages/datagateway-dataview/cypress/e2e/table/isis/instruments.cy.ts index 7ea36932d..fea99b6b8 100644 --- a/packages/datagateway-dataview/cypress/e2e/table/isis/instruments.cy.ts +++ b/packages/datagateway-dataview/cypress/e2e/table/isis/instruments.cy.ts @@ -62,15 +62,52 @@ describe('ISIS - Instruments Table', () => { cy.get('[aria-sort="ascending"]').should('not.exist'); cy.get('[aria-sort="descending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 2); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="2"]').contains( 'Stop prove field onto think suffer measure.' ); + + // multiple columns (shift click) + cy.contains('[role="button"]', 'Type').click(); + cy.get('@nameSortButton').click({ shiftKey: true }); + cy.get('[aria-sort="ascending"]').should('have.length', 2); + + // should replace the previous sort when clicked without shift + cy.get('@nameSortButton').click(); + cy.get('[aria-sort="descending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('5'); + }); + + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Name').click(); + cy.contains('[role="button"]', 'Name').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 2); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Name').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Name').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Type').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.contains('[role="button"]', 'Type').trigger('mouseout'); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 1); }); it('should be able to filter with text filter on multiple columns', () => { diff --git a/packages/datagateway-download/cypress/e2e/adminDownloadStatus.cy.ts b/packages/datagateway-download/cypress/e2e/adminDownloadStatus.cy.ts index 270dcad09..97a6b68f1 100644 --- a/packages/datagateway-download/cypress/e2e/adminDownloadStatus.cy.ts +++ b/packages/datagateway-download/cypress/e2e/adminDownloadStatus.cy.ts @@ -77,20 +77,21 @@ describe('Admin Download Status', () => { cy.get('@accessMethodSortButton').click(); cy.get('[aria-sort="ascending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('[aria-sort="descending"]').should('not.exist'); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 8); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="5"]').should( 'have.text', 'https' ); - // multiple columns + // multiple columns (shift + click) cy.contains('[role="button"]', 'Deleted').click(); - cy.contains('[role="button"]', 'Availability').click(); + cy.contains('[role="button"]', 'Availability').click({ shiftKey: true }); + cy.get('[aria-sort="ascending"]').should('have.length', 2); cy.get('[aria-rowindex="2"] [aria-colindex="6"]').should( 'have.text', @@ -100,6 +101,36 @@ describe('Admin Download Status', () => { 'have.text', 'Expired' ); + + // should replace previous sort when clicked without shift + cy.contains('[role="button"]', 'Prepared ID').click(); + cy.get('[aria-sort="ascending"]').should('have.length', 1); + }); + + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Requested Date').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 8); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'ID').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'ID').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Full Name').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 6); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-download/cypress/e2e/downloadCart.cy.ts b/packages/datagateway-download/cypress/e2e/downloadCart.cy.ts index 2ca7ad9e9..2b4918234 100644 --- a/packages/datagateway-download/cypress/e2e/downloadCart.cy.ts +++ b/packages/datagateway-download/cypress/e2e/downloadCart.cy.ts @@ -68,11 +68,19 @@ describe('Download Cart', () => { 'investigation' ); - cy.contains('[role="button"]', 'Name').click(); + // multisort with shift key + cy.contains('[role="button"]', 'Name').click({ shiftKey: true }); cy.get('[aria-rowindex=1] [aria-colindex=1]').should( 'have.text', 'INVESTIGATION 10' ); + + // replace previous sory by clicking without shift key + cy.contains('[role="button"]', 'Name').click(); + cy.get('[aria-rowindex=1] [aria-colindex=1]').should( + 'have.text', + 'INVESTIGATION 8' + ); }); it('should be able to filter cart items by name and type', () => { diff --git a/packages/datagateway-download/cypress/e2e/downloadStatus.cy.ts b/packages/datagateway-download/cypress/e2e/downloadStatus.cy.ts index 117ab83d7..606c9331f 100644 --- a/packages/datagateway-download/cypress/e2e/downloadStatus.cy.ts +++ b/packages/datagateway-download/cypress/e2e/downloadStatus.cy.ts @@ -109,12 +109,12 @@ describe('Download Status', () => { cy.get('@nameSortButton').click(); cy.get('[aria-sort="ascending"]').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionDesc').should('not.exist'); - cy.get('.MuiTableSortLabel-iconDirectionAsc').should( - 'have.css', - 'opacity', - '0' - ); + cy.get('[aria-sort="descending"]').should('not.exist'); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + cy.get('[data-testid="SortIcon"]').should('have.length', 4); + cy.get('[data-testid="ArrowUpwardIcon"]').should('not.exist'); + cy.get('[aria-rowindex="1"] [aria-colindex="1"]').should( 'have.text', 'test-file-1' @@ -125,14 +125,43 @@ describe('Download Status', () => { 'Available' ); - // multiple columns + // multiple columns (shift + click) + cy.contains('[role="button"]', 'Download Name').click(); + cy.contains('[role="button"]', 'Availability').click({ shiftKey: true }); + cy.get('[aria-sort="ascending"]').should('have.length', 2); + + cy.get('[aria-rowindex="1"] [aria-colindex="3"]').contains('Available'); + + // should replace previous sort when clicked without shift cy.contains('[role="button"]', 'Access Method').click(); - cy.contains('[role="button"]', 'Availability').click(); + cy.get('[aria-sort="ascending"]').should('have.length', 1); + cy.get('[aria-rowindex="1"] [aria-colindex="1"]').contains('test-file-2'); + }); - cy.get('[aria-rowindex="1"] [aria-colindex="1"]').should( - 'have.text', - 'test-file-4' - ); + it('should change icons when sorting on a column', () => { + // clear default sort + cy.contains('[role="button"]', 'Requested Date').click(); + + cy.get('[data-testid="SortIcon"]').should('have.length', 4); + + // check icon when clicking on a column + cy.contains('[role="button"]', 'Download Name').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('exist'); + + // check icon when clicking on a column again + cy.contains('[role="button"]', 'Download Name').click(); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + cy.get('.MuiTableSortLabel-iconDirectionAsc').should('not.exist'); + + // check icon when hovering over a column + cy.contains('[role="button"]', 'Access Method').trigger('mouseover'); + cy.get('[data-testid="ArrowUpwardIcon"]').should('have.length', 1); + cy.get('[data-testid="ArrowDownwardIcon"]').should('have.length', 1); + + // check icons when shift is held + cy.get('.App').trigger('keydown', { key: 'Shift' }); + cy.get('[data-testid="AddIcon"]').should('have.length', 2); }); it('should be able to filter with both text & date filters on multiple columns', () => { diff --git a/packages/datagateway-download/src/downloadCart/downloadCartTable.component.test.tsx b/packages/datagateway-download/src/downloadCart/downloadCartTable.component.test.tsx index fa22a6f42..84a46db79 100644 --- a/packages/datagateway-download/src/downloadCart/downloadCartTable.component.test.tsx +++ b/packages/datagateway-download/src/downloadCart/downloadCartTable.component.test.tsx @@ -242,6 +242,8 @@ describe('Download cart table component', () => { }); it('should sort data when headers are clicked', async () => { + // use skipHover to avoid triggering sort tooltips which slow the test down + user = userEvent.setup({ skipHover: true }); renderComponent(); const typeSortLabel = await screen.findByRole('button', { @@ -272,7 +274,9 @@ describe('Download cart table component', () => { name: 'downloadCart.name', }); + await user.keyboard('{Shift>}'); await user.click(nameSortLabel); + await user.keyboard('{/Shift}'); rows = await screen.findAllByText(/(DATAFILE|DATASET|INVESTIGATION) \d/); // row should be sorted by type desc & name asc. @@ -281,7 +285,9 @@ describe('Download cart table component', () => { expect(rows[2]).toHaveTextContent('DATASET 1'); expect(rows[3]).toHaveTextContent('DATAFILE 1'); + await user.keyboard('{Shift>}'); await user.click(nameSortLabel); + await user.keyboard('{/Shift}'); rows = await screen.findAllByText(/(DATAFILE|DATASET|INVESTIGATION) \d/); // row should be sorted by type desc & name desc. @@ -298,6 +304,15 @@ describe('Download cart table component', () => { expect(rows[1]).toHaveTextContent('INVESTIGATION 2'); expect(rows[2]).toHaveTextContent('DATASET 1'); expect(rows[3]).toHaveTextContent('DATAFILE 1'); + + await user.click(nameSortLabel); + + rows = await screen.findAllByText(/(DATAFILE|DATASET|INVESTIGATION) \d/); + // row should be sorted by name asc. + expect(rows[0]).toHaveTextContent('DATAFILE 1'); + expect(rows[1]).toHaveTextContent('DATASET 1'); + expect(rows[2]).toHaveTextContent('INVESTIGATION 1'); + expect(rows[3]).toHaveTextContent('INVESTIGATION 2'); }); it('should filter data when text fields are typed into', async () => { diff --git a/packages/datagateway-download/src/downloadCart/downloadCartTable.component.tsx b/packages/datagateway-download/src/downloadCart/downloadCartTable.component.tsx index 4c389d298..17963293c 100644 --- a/packages/datagateway-download/src/downloadCart/downloadCartTable.component.tsx +++ b/packages/datagateway-download/src/downloadCart/downloadCartTable.component.tsx @@ -194,9 +194,11 @@ const DownloadCartTable: React.FC = ( [t, textFilter] ); const onSort = React.useCallback( - (column: string, order: 'desc' | 'asc' | null) => { + (column: string, order: 'desc' | 'asc' | null, _, shiftDown?: boolean) => { if (order) { - setSort({ ...sort, [column]: order }); + shiftDown + ? setSort({ ...sort, [column]: order }) + : setSort({ [column]: order }); } else { const { [column]: order, ...restOfSort } = sort; setSort(restOfSort); diff --git a/packages/datagateway-download/src/downloadStatus/__snapshots__/adminDownloadStatusTable.component.test.tsx.snap b/packages/datagateway-download/src/downloadStatus/__snapshots__/adminDownloadStatusTable.component.test.tsx.snap index 55c0f2651..f0915329e 100644 --- a/packages/datagateway-download/src/downloadStatus/__snapshots__/adminDownloadStatusTable.component.test.tsx.snap +++ b/packages/datagateway-download/src/downloadStatus/__snapshots__/adminDownloadStatusTable.component.test.tsx.snap @@ -101,7 +101,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -112,13 +114,13 @@ exports[`Admin Download Status Table should render correctly 1`] = `

@@ -224,7 +226,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -235,13 +239,13 @@ exports[`Admin Download Status Table should render correctly 1`] = `

@@ -347,7 +351,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -358,13 +364,13 @@ exports[`Admin Download Status Table should render correctly 1`] = `

@@ -470,7 +476,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -481,13 +489,13 @@ exports[`Admin Download Status Table should render correctly 1`] = `

@@ -593,7 +601,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -604,13 +614,13 @@ exports[`Admin Download Status Table should render correctly 1`] = `

@@ -716,7 +726,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -727,13 +739,13 @@ exports[`Admin Download Status Table should render correctly 1`] = `

@@ -878,7 +890,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -1023,7 +1037,9 @@ exports[`Admin Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -1034,13 +1050,13 @@ exports[`Admin Download Status Table should render correctly 1`] = `

diff --git a/packages/datagateway-download/src/downloadStatus/__snapshots__/downloadStatusTable.component.test.tsx.snap b/packages/datagateway-download/src/downloadStatus/__snapshots__/downloadStatusTable.component.test.tsx.snap index 4fbc6d645..6801228c7 100644 --- a/packages/datagateway-download/src/downloadStatus/__snapshots__/downloadStatusTable.component.test.tsx.snap +++ b/packages/datagateway-download/src/downloadStatus/__snapshots__/downloadStatusTable.component.test.tsx.snap @@ -61,7 +61,9 @@ exports[`Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -72,13 +74,13 @@ exports[`Download Status Table should render correctly 1`] = `

@@ -184,7 +186,9 @@ exports[`Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -195,13 +199,13 @@ exports[`Download Status Table should render correctly 1`] = `

@@ -307,7 +311,9 @@ exports[`Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -318,13 +324,13 @@ exports[`Download Status Table should render correctly 1`] = `

@@ -431,7 +437,9 @@ exports[`Download Status Table should render correctly 1`] = ` class="MuiBox-root css-0" > diff --git a/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.test.tsx b/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.test.tsx index 20d4870a4..68c297fdf 100644 --- a/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.test.tsx +++ b/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.test.tsx @@ -156,6 +156,8 @@ describe('Admin Download Status Table', () => { }); it('should send sort request on sort', async () => { + // use skipHover to avoid triggering sort tooltips which slow the test down + user = userEvent.setup({ delay: null, skipHover: true }); renderComponent(); // Table is sorted by createdAt desc by default @@ -182,22 +184,39 @@ describe('Admin Download Status Table', () => { 'downloadStatus.transport' ); + // should replace the sort by username with sort by access method await user.click(accessMethodSortLabel); expect(fetchAdminDownloads).toHaveBeenCalledWith( { downloadApiUrl: mockedSettings.downloadApiUrl, facilityName: mockedSettings.facilityName, }, - `WHERE download.facilityName = '${mockedSettings.facilityName}' ORDER BY download.userName asc, download.transport asc, download.id ASC LIMIT 0, 50` + `WHERE download.facilityName = '${mockedSettings.facilityName}' ORDER BY download.transport asc, download.id ASC LIMIT 0, 50` ); + // should append sort if shift key is pressed + await user.keyboard('{Shift>}'); + await user.click(usernameSortLabel); + await user.keyboard('{/Shift}'); + + expect(fetchAdminDownloads).toHaveBeenCalledWith( + { + downloadApiUrl: mockedSettings.downloadApiUrl, + facilityName: mockedSettings.facilityName, + }, + `WHERE download.facilityName = '${mockedSettings.facilityName}' ORDER BY download.transport asc, download.userName asc, download.id ASC LIMIT 0, 50` + ); + + await user.keyboard('{Shift>}'); await user.click(accessMethodSortLabel); + await user.keyboard('{/Shift}'); + expect(fetchAdminDownloads).toHaveBeenCalledWith( { downloadApiUrl: mockedSettings.downloadApiUrl, facilityName: mockedSettings.facilityName, }, - `WHERE download.facilityName = '${mockedSettings.facilityName}' ORDER BY download.userName asc, download.transport desc, download.id ASC LIMIT 0, 50` + `WHERE download.facilityName = '${mockedSettings.facilityName}' ORDER BY download.transport desc, download.userName asc, download.id ASC LIMIT 0, 50` ); await user.click(accessMethodSortLabel); @@ -212,6 +231,9 @@ describe('Admin Download Status Table', () => { describe('text filters', () => { it('should filter username properly', async () => { + // use skipHover to avoid triggering sort tooltips which slow the test down + user = userEvent.setup({ delay: null, skipHover: true }); + renderComponent(); await flushPromises(); @@ -248,6 +270,9 @@ describe('Admin Download Status Table', () => { }); it('should filter download availability properly', async () => { + // use skipHover to avoid triggering sort tooltips which slow the test down + user = userEvent.setup({ delay: null, skipHover: true }); + renderComponent(); await flushPromises(); @@ -305,12 +330,16 @@ describe('Admin Download Status Table', () => { it('sends filter request on date filter', async () => { applyDatePickerWorkaround(); + // use skipHover to avoid triggering sort tooltips which slow the test down + user = userEvent.setup({ delay: null, skipHover: true }); + renderComponent(); await flushPromises(); // Table is sorted by createdAt desc by default // To keep working test, we will remove all sorts on the table beforehand await user.click(await screen.findByText('downloadStatus.createdAt')); + await flushPromises(); // Get the Requested Data From filter input const dateFromFilterInput = screen.getByRole('textbox', { @@ -337,18 +366,15 @@ describe('Admin Download Status Table', () => { await user.type(dateToFilterInput, '2020-01-02_23:59:00'); await flushPromises(); - // have to wrap the expect in a waitFor because for some reason - // await user.type doesn't wait until the full thing is typed in before resolving - // causing fetchAdminDownloads to be called with partial values - await waitFor(() => { - expect(fetchAdminDownloads).toHaveBeenCalledWith( - { - downloadApiUrl: mockedSettings.downloadApiUrl, - facilityName: mockedSettings.facilityName, - }, - `WHERE download.facilityName = '${mockedSettings.facilityName}' AND download.createdAt BETWEEN {ts '2020-01-01 00:00:00'} AND {ts '2020-01-02 23:59:00'} ORDER BY download.id ASC LIMIT 0, 50` - ); - }); + expect(fetchAdminDownloads).toHaveBeenCalledWith( + { + downloadApiUrl: mockedSettings.downloadApiUrl, + facilityName: mockedSettings.facilityName, + }, + `WHERE download.facilityName = '${mockedSettings.facilityName}' AND download.createdAt BETWEEN {ts '2020-01-01 00:00:00'} AND {ts '2020-01-02 23:59:00'} ORDER BY download.id ASC LIMIT 0, 50` + ); + + (fetchAdminDownloads as jest.Mock).mockClear(); await user.clear(dateFromFilterInput); await user.clear(dateToFilterInput); diff --git a/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.tsx b/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.tsx index f9125908e..6752b35b6 100644 --- a/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.tsx +++ b/packages/datagateway-download/src/downloadStatus/adminDownloadStatusTable.component.tsx @@ -370,9 +370,16 @@ const AdminDownloadStatusTable: React.FC = () => { }, ]} sort={sort} - onSort={(column: string, order: 'desc' | 'asc' | null) => { + onSort={( + column: string, + order: 'desc' | 'asc' | null, + _, + shiftDown?: boolean + ) => { if (order) { - setSort({ ...sort, [column]: order }); + shiftDown + ? setSort({ ...sort, [column]: order }) + : setSort({ [column]: order }); } else { const { [column]: order, ...restOfSort } = sort; setSort(restOfSort); diff --git a/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.test.tsx b/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.test.tsx index 19627c92e..775a4af2c 100644 --- a/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.test.tsx +++ b/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.test.tsx @@ -47,7 +47,7 @@ describe('Download Status Table', () => { let user: ReturnType; beforeEach(() => { - user = userEvent.setup(); + user = userEvent.setup({ delay: null }); (downloadDeleted as jest.Mock).mockImplementation(() => Promise.resolve()); (fetchDownloads as jest.Mock).mockImplementation(() => @@ -188,6 +188,8 @@ describe('Download Status Table', () => { }); it('should sort data when headers are clicked', async () => { + // use skipHover to avoid triggering sort tooltips which slow the test down + user = userEvent.setup({ delay: null, skipHover: true }); renderComponent(); // Table is sorted by createdAt desc by default @@ -220,7 +222,9 @@ describe('Download Status Table', () => { // Get the download name sort header. const nameSortLabel = screen.getByText('downloadStatus.filename'); + await user.keyboard('{Shift>}'); await user.click(nameSortLabel); + await user.keyboard('{/Shift}'); // name should be in asc order rows = await screen.findAllByText(/^test-file-\d$/); @@ -230,7 +234,9 @@ describe('Download Status Table', () => { expect(rows[3]).toHaveTextContent('test-file-4'); expect(rows[4]).toHaveTextContent('test-file-5'); + await user.keyboard('{Shift>}'); await user.click(nameSortLabel); + await user.keyboard('{/Shift}'); // name should be in desc order rows = await screen.findAllByText(/^test-file-\d$/); @@ -239,6 +245,26 @@ describe('Download Status Table', () => { expect(rows[2]).toHaveTextContent('test-file-5'); expect(rows[3]).toHaveTextContent('test-file-4'); expect(rows[4]).toHaveTextContent('test-file-2'); + + await user.click(accessMethodSortLabel); + + // name should be in desc order + rows = await screen.findAllByText(/^test-file-\d$/); + expect(rows[0]).toHaveTextContent('test-file-5'); + expect(rows[1]).toHaveTextContent('test-file-4'); + expect(rows[2]).toHaveTextContent('test-file-3'); + expect(rows[3]).toHaveTextContent('test-file-2'); + expect(rows[4]).toHaveTextContent('test-file-1'); + + await user.click(accessMethodSortLabel); + + // access methods should be in asc order, globus < https + rows = await screen.findAllByText(/^test-file-\d$/); + expect(rows[0]).toHaveTextContent('test-file-2'); + expect(rows[1]).toHaveTextContent('test-file-4'); + expect(rows[2]).toHaveTextContent('test-file-5'); + expect(rows[3]).toHaveTextContent('test-file-1'); + expect(rows[4]).toHaveTextContent('test-file-3'); }); it('should filter data when text fields are typed into', async () => { diff --git a/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.tsx b/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.tsx index 68cafe642..0c8f3c082 100644 --- a/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.tsx +++ b/packages/datagateway-download/src/downloadStatus/downloadStatusTable.component.tsx @@ -326,9 +326,16 @@ const DownloadStatusTable: React.FC = ( }, ]} sort={sort} - onSort={(column: string, order: 'desc' | 'asc' | null) => { + onSort={( + column: string, + order: 'desc' | 'asc' | null, + _, + shiftDown?: boolean + ) => { if (order) { - setSort({ ...sort, [column]: order }); + shiftDown + ? setSort({ ...sort, [column]: order }) + : setSort({ [column]: order }); } else { const { [column]: order, ...restOfSort } = sort; setSort(restOfSort); diff --git a/packages/datagateway-download/src/downloadTab/__snapshots__/downloadTab.component.test.tsx.snap b/packages/datagateway-download/src/downloadTab/__snapshots__/downloadTab.component.test.tsx.snap index 049b6b42f..fd2d892c1 100644 --- a/packages/datagateway-download/src/downloadTab/__snapshots__/downloadTab.component.test.tsx.snap +++ b/packages/datagateway-download/src/downloadTab/__snapshots__/downloadTab.component.test.tsx.snap @@ -122,7 +122,9 @@ exports[`DownloadTab should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -133,13 +135,13 @@ exports[`DownloadTab should render correctly 1`] = `

@@ -245,7 +247,9 @@ exports[`DownloadTab should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -256,13 +260,13 @@ exports[`DownloadTab should render correctly 1`] = `

@@ -368,7 +372,9 @@ exports[`DownloadTab should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -379,13 +385,13 @@ exports[`DownloadTab should render correctly 1`] = `

@@ -423,7 +429,9 @@ exports[`DownloadTab should render correctly 1`] = ` class="MuiBox-root css-0" > @@ -434,13 +442,13 @@ exports[`DownloadTab should render correctly 1`] = `