mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
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:
parent
9b09f8426d
commit
30fd0bfc9d
5 changed files with 69 additions and 9 deletions
|
@ -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)
|
|
@ -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 <Button onClick={handleUntrashProjects}>{t('untrash')}</Button>
|
||||
}
|
||||
|
|
|
@ -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() {
|
|||
|
||||
<ButtonGroup>
|
||||
{filter === 'trashed' && <UntrashProjectsButton />}
|
||||
{filter === 'archived' && <UnarchiveProjectsButton />}
|
||||
</ButtonGroup>
|
||||
</ButtonToolbar>
|
||||
)
|
||||
|
|
|
@ -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('<ProjectListRoot />', 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<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 () {
|
||||
|
|
|
@ -132,13 +132,13 @@ export const projectsData: Array<Project> = [
|
|||
trashedAndNotOwnedProject,
|
||||
]
|
||||
|
||||
export const currentProjects: Array<Project> = projectsData.filter(
|
||||
export const archivedProjects = projectsData.filter(({ archived }) => archived)
|
||||
|
||||
export const currentProjects = projectsData.filter(
|
||||
({ archived, trashed }) => !archived && !trashed
|
||||
)
|
||||
|
||||
export const trashedProjects: Array<Project> = projectsData.filter(
|
||||
({ trashed }) => trashed
|
||||
)
|
||||
export const trashedProjects = projectsData.filter(({ trashed }) => trashed)
|
||||
|
||||
export const makeLongProjectList = (listLength: number) => {
|
||||
const longList = [...projectsData]
|
||||
|
|
Loading…
Reference in a new issue