Merge pull request #9676 from overleaf/jel-project-tools-unarchive

[web] Add unarchive button to project tools

GitOrigin-RevId: d1c7c693eb13cb7473b65e311f09ffe7a7f0d88f
This commit is contained in:
Jessica Lawshe 2022-10-03 08:20:13 -05:00 committed by Copybot
parent 9b09f8426d
commit 30fd0bfc9d
5 changed files with 69 additions and 9 deletions

View file

@ -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 <Button onClick={handleUnarchiveProjects}>{t('unarchive')}</Button>
}
export default memo(UnarchiveProjectsButton)

View file

@ -1,4 +1,4 @@
import { memo, useCallback } from 'react' import { memo } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useProjectListContext } from '../../../../context/project-list-context' import { useProjectListContext } from '../../../../context/project-list-context'
@ -8,12 +8,12 @@ function UntrashProjectsButton() {
const { selectedProjects, updateProjectViewData } = useProjectListContext() const { selectedProjects, updateProjectViewData } = useProjectListContext()
const { t } = useTranslation() const { t } = useTranslation()
const handleUntrashProjects = useCallback(async () => { const handleUntrashProjects = async () => {
for (const [, project] of Object.entries(selectedProjects)) { for (const project of selectedProjects) {
await untrashProject(project.id) await untrashProject(project.id)
updateProjectViewData({ ...project, trashed: false, selected: false }) updateProjectViewData({ ...project, trashed: false, selected: false })
} }
}, [selectedProjects, updateProjectViewData]) }
return <Button onClick={handleUntrashProjects}>{t('untrash')}</Button> return <Button onClick={handleUntrashProjects}>{t('untrash')}</Button>
} }

View file

@ -4,6 +4,7 @@ import { useProjectListContext } from '../../../context/project-list-context'
import ArchiveProjectsButton from './buttons/archive-projects-button' import ArchiveProjectsButton from './buttons/archive-projects-button'
import DownloadProjectsButton from './buttons/download-projects-button' import DownloadProjectsButton from './buttons/download-projects-button'
import TrashProjectsButton from './buttons/trash-projects-button' import TrashProjectsButton from './buttons/trash-projects-button'
import UnarchiveProjectsButton from './buttons/unarchive-projects-button'
import UntrashProjectsButton from './buttons/untrash-projects-button' import UntrashProjectsButton from './buttons/untrash-projects-button'
function ProjectTools() { function ProjectTools() {
@ -18,6 +19,7 @@ function ProjectTools() {
<ButtonGroup> <ButtonGroup>
{filter === 'trashed' && <UntrashProjectsButton />} {filter === 'trashed' && <UntrashProjectsButton />}
{filter === 'archived' && <UnarchiveProjectsButton />}
</ButtonGroup> </ButtonGroup>
</ButtonToolbar> </ButtonToolbar>
) )

View file

@ -4,7 +4,11 @@ import fetchMock from 'fetch-mock'
import sinon from 'sinon' import sinon from 'sinon'
import ProjectListRoot from '../../../../../frontend/js/features/project-list/components/project-list-root' import ProjectListRoot from '../../../../../frontend/js/features/project-list/components/project-list-root'
import { renderWithProjectListContext } from '../helpers/render-with-context' 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 { fullList, currentList, trashedList } = makeLongProjectList(40)
const userId = owner.id const userId = owner.id
@ -191,10 +195,43 @@ describe('<ProjectListRoot />', function () {
// first one is the select all checkbox // first one is the select all checkbox
fireEvent.click(allCheckboxes[1]) fireEvent.click(allCheckboxes[1])
project1Id = allCheckboxes[1].getAttribute('data-project-id') 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 () { it('does not show the archive button in toolbar when archive view selected', function () {
expect(screen.queryByLabelText('Archive')).to.be.null 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<HTMLInputElement>('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 () { describe('trashed projects', function () {

View file

@ -132,13 +132,13 @@ export const projectsData: Array<Project> = [
trashedAndNotOwnedProject, trashedAndNotOwnedProject,
] ]
export const currentProjects: Array<Project> = projectsData.filter( export const archivedProjects = projectsData.filter(({ archived }) => archived)
export const currentProjects = projectsData.filter(
({ archived, trashed }) => !archived && !trashed ({ archived, trashed }) => !archived && !trashed
) )
export const trashedProjects: Array<Project> = projectsData.filter( export const trashedProjects = projectsData.filter(({ trashed }) => trashed)
({ trashed }) => trashed
)
export const makeLongProjectList = (listLength: number) => { export const makeLongProjectList = (listLength: number) => {
const longList = [...projectsData] const longList = [...projectsData]