From 30fd0bfc9d7cb7991e1be04cb4d5edcafbf60a5a Mon Sep 17 00:00:00 2001 From: Jessica Lawshe Date: Mon, 3 Oct 2022 08:20:13 -0500 Subject: [PATCH] Merge pull request #9676 from overleaf/jel-project-tools-unarchive [web] Add unarchive button to project tools GitOrigin-RevId: d1c7c693eb13cb7473b65e311f09ffe7a7f0d88f --- .../buttons/unarchive-projects-button.tsx | 21 ++++++++++ .../buttons/untrash-projects-button.tsx | 8 ++-- .../table/project-tools/project-tools.tsx | 2 + .../components/project-list-root.test.tsx | 39 ++++++++++++++++++- .../project-list/fixtures/projects-data.ts | 8 ++-- 5 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 services/web/frontend/js/features/project-list/components/table/project-tools/buttons/unarchive-projects-button.tsx diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/unarchive-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/unarchive-projects-button.tsx new file mode 100644 index 0000000000..c3958db7ac --- /dev/null +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/unarchive-projects-button.tsx @@ -0,0 +1,21 @@ +import { memo } from 'react' +import { Button } from 'react-bootstrap' +import { useTranslation } from 'react-i18next' +import { useProjectListContext } from '../../../../context/project-list-context' +import { unarchiveProject } from '../../../../util/api' + +function UnarchiveProjectsButton() { + const { selectedProjects, updateProjectViewData } = useProjectListContext() + const { t } = useTranslation() + + const handleUnarchiveProjects = async () => { + for (const project of selectedProjects) { + await unarchiveProject(project.id) + updateProjectViewData({ ...project, archived: false, selected: false }) + } + } + + return +} + +export default memo(UnarchiveProjectsButton) diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx index c6d96144b7..497a786fc5 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/untrash-projects-button.tsx @@ -1,4 +1,4 @@ -import { memo, useCallback } from 'react' +import { memo } from 'react' import { Button } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import { useProjectListContext } from '../../../../context/project-list-context' @@ -8,12 +8,12 @@ function UntrashProjectsButton() { const { selectedProjects, updateProjectViewData } = useProjectListContext() const { t } = useTranslation() - const handleUntrashProjects = useCallback(async () => { - for (const [, project] of Object.entries(selectedProjects)) { + const handleUntrashProjects = async () => { + for (const project of selectedProjects) { await untrashProject(project.id) updateProjectViewData({ ...project, trashed: false, selected: false }) } - }, [selectedProjects, updateProjectViewData]) + } return } diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx index 838722a13b..5dcbaef57a 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/project-tools.tsx @@ -4,6 +4,7 @@ import { useProjectListContext } from '../../../context/project-list-context' import ArchiveProjectsButton from './buttons/archive-projects-button' import DownloadProjectsButton from './buttons/download-projects-button' import TrashProjectsButton from './buttons/trash-projects-button' +import UnarchiveProjectsButton from './buttons/unarchive-projects-button' import UntrashProjectsButton from './buttons/untrash-projects-button' function ProjectTools() { @@ -18,6 +19,7 @@ function ProjectTools() { {filter === 'trashed' && } + {filter === 'archived' && } ) diff --git a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx index 1d66a33e43..208d5a0cef 100644 --- a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx +++ b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx @@ -4,7 +4,11 @@ import fetchMock from 'fetch-mock' import sinon from 'sinon' import ProjectListRoot from '../../../../../frontend/js/features/project-list/components/project-list-root' import { renderWithProjectListContext } from '../helpers/render-with-context' -import { owner, makeLongProjectList } from '../fixtures/projects-data' +import { + owner, + archivedProjects, + makeLongProjectList, +} from '../fixtures/projects-data' const { fullList, currentList, trashedList } = makeLongProjectList(40) const userId = owner.id @@ -191,10 +195,43 @@ describe('', function () { // first one is the select all checkbox fireEvent.click(allCheckboxes[1]) project1Id = allCheckboxes[1].getAttribute('data-project-id') + + actionsToolbar = screen.getAllByRole('toolbar')[0] }) + it('does not show the archive button in toolbar when archive view selected', function () { expect(screen.queryByLabelText('Archive')).to.be.null }) + + it('restores all projects when selected', async function () { + fetchMock.delete(`express:/project/:id/archive`, { + status: 200, + }) + + const unarchiveButton = + within(actionsToolbar).getByText('Restore') + fireEvent.click(unarchiveButton) + + await fetchMock.flush(true) + expect(fetchMock.done()).to.be.true + + screen.getByText('No projects') + }) + + it('only unarchive the selected projects', async function () { + // beforeEach selected all, so uncheck the 1st project + fireEvent.click(allCheckboxes[1]) + + const allCheckboxesChecked = allCheckboxes.filter(c => c.checked) + expect(allCheckboxesChecked.length).to.equal( + archivedProjects.length - 1 + ) + + await fetchMock.flush(true) + expect(fetchMock.done()).to.be.true + + expect(screen.queryByText('No projects')).to.be.null + }) }) describe('trashed projects', function () { diff --git a/services/web/test/frontend/features/project-list/fixtures/projects-data.ts b/services/web/test/frontend/features/project-list/fixtures/projects-data.ts index e04aa1e148..9f603d6c54 100644 --- a/services/web/test/frontend/features/project-list/fixtures/projects-data.ts +++ b/services/web/test/frontend/features/project-list/fixtures/projects-data.ts @@ -132,13 +132,13 @@ export const projectsData: Array = [ trashedAndNotOwnedProject, ] -export const currentProjects: Array = projectsData.filter( +export const archivedProjects = projectsData.filter(({ archived }) => archived) + +export const currentProjects = projectsData.filter( ({ archived, trashed }) => !archived && !trashed ) -export const trashedProjects: Array = projectsData.filter( - ({ trashed }) => trashed -) +export const trashedProjects = projectsData.filter(({ trashed }) => trashed) export const makeLongProjectList = (listLength: number) => { const longList = [...projectsData]