Merge pull request #19235 from overleaf/rh-link-sharing-leave-modal

[web] Confirmation modal when leaving project on sharing updates screen

GitOrigin-RevId: a19201f5157c2e92f98480bc59ad1f405601945d
This commit is contained in:
roo hutton 2024-07-18 07:43:47 +01:00 committed by Copybot
parent 168f83277b
commit 2bed549195
5 changed files with 126 additions and 2 deletions

View file

@ -14,6 +14,7 @@
"about_to_delete_the_following_projects": "",
"about_to_delete_user_preamble": "",
"about_to_enable_managed_users": "",
"about_to_leave_project": "",
"about_to_leave_projects": "",
"about_to_trash_projects": "",
"accept": "",
@ -707,6 +708,7 @@
"leave_any_group_subscriptions": "",
"leave_group": "",
"leave_now": "",
"leave_project": "",
"leave_projects": "",
"left": "",
"length_unit": "",

View file

@ -0,0 +1,53 @@
import OLButton from '@/features/ui/components/ol/ol-button'
import OLModal, {
OLModalBody,
OLModalFooter,
OLModalHeader,
} from '@/features/ui/components/ol/ol-modal'
import Notification from '@/shared/components/notification'
import { Modal } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
type LeaveProjectModalProps = {
showModal: boolean
handleCloseModal: () => void
handleLeaveAction: () => void
}
function LeaveProjectModal({
showModal,
handleCloseModal,
handleLeaveAction,
}: LeaveProjectModalProps) {
const { t } = useTranslation()
return (
<OLModal
animation
show={showModal}
onHide={handleCloseModal}
id="action-project-modal"
backdrop="static"
>
<OLModalHeader closeButton>
<Modal.Title>{t('leave_project')}</Modal.Title>
</OLModalHeader>
<OLModalBody>
<p>{t('about_to_leave_project')}</p>
<Notification
content={t('this_action_cannot_be_undone')}
type="warning"
/>
</OLModalBody>
<OLModalFooter>
<OLButton variant="secondary" onClick={handleCloseModal}>
{t('cancel')}
</OLButton>
<OLButton variant="danger" onClick={() => handleLeaveAction()}>
{t('confirm')}
</OLButton>
</OLModalFooter>
</OLModal>
)
}
export default LeaveProjectModal

View file

@ -2,7 +2,7 @@ import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
import { Trans, useTranslation } from 'react-i18next'
import withErrorBoundary from '@/infrastructure/error-boundary'
import { GenericErrorBoundaryFallback } from '@/shared/components/generic-error-boundary-fallback'
import { useCallback } from 'react'
import { useCallback, useState } from 'react'
import getMeta from '@/utils/meta'
import { postJSON } from '@/infrastructure/fetch-json'
import { debugConsole } from '@/utils/debugging'
@ -10,7 +10,10 @@ import useAsync from '@/shared/hooks/use-async'
import Notification from '@/shared/components/notification'
import { sendMB } from '@/infrastructure/event-tracking'
import LeaveProjectModal from './leave-project-modal'
function SharingUpdatesRoot() {
const [showModal, setShowModal] = useState(false)
const { isReady } = useWaitForI18n()
const { t } = useTranslation()
const { isLoading, isSuccess, isError, runAsync } = useAsync()
@ -58,6 +61,11 @@ function SharingUpdatesRoot() {
return (
<div className="container">
<LeaveProjectModal
handleLeaveAction={leaveProject}
showModal={showModal}
handleCloseModal={() => setShowModal(false)}
/>
<div className="row">
<div className="col-md-6 col-md-offset-3">
<div className="card sharing-updates">
@ -132,7 +140,7 @@ function SharingUpdatesRoot() {
// eslint-disable-next-line react/jsx-key
<button
className="btn btn-inline-link"
onClick={() => leaveProject()}
onClick={() => setShowModal(true)}
disabled={isLoading || isSuccess}
/>,
]}

View file

@ -25,6 +25,7 @@
"about_to_delete_the_following_projects": "You are about to delete the following projects",
"about_to_delete_user_preamble": "Youre about to delete __userName__ (__userEmail__). Doing this will mean:",
"about_to_enable_managed_users": "By enabling the Managed Users feature, all existing members of your group subscription will be invited to become managed. This will give you admin rights over their account. You will also have the option to invite new members to join the subscription and become managed.",
"about_to_leave_project": "You are about to leave this project.",
"about_to_leave_projects": "You are about to leave the following projects:",
"about_to_trash_projects": "You are about to trash the following projects:",
"abstract": "Abstract",
@ -1045,6 +1046,7 @@
"leave_group": "Leave group",
"leave_labs": "Leave Overleaf Labs",
"leave_now": "Leave now",
"leave_project": "Leave Project",
"leave_projects": "Leave Projects",
"left": "Left",
"length_unit": "Length unit",

View file

@ -0,0 +1,59 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import LeaveProjectModal from '@/features/token-access/components/leave-project-modal'
describe('<LeaveProjectModal/>', function () {
const closeHandler = sinon.stub()
const leaveHandler = sinon.stub()
it('does not render when no show prop is passed', function () {
render(
<LeaveProjectModal
handleCloseModal={closeHandler}
handleLeaveAction={leaveHandler}
showModal={false}
/>
)
const leaveText = screen.queryByText('Leave Project')
expect(leaveText).to.be.null
})
it('renders when the show prop is passed', function () {
render(
<LeaveProjectModal
handleCloseModal={closeHandler}
handleLeaveAction={leaveHandler}
showModal
/>
)
const leaveText = screen.queryByText('Leave Project')
expect(leaveText).to.exist
})
it('calls the close handler when dismissed', async function () {
const user = userEvent.setup()
render(
<LeaveProjectModal
handleCloseModal={closeHandler}
handleLeaveAction={leaveHandler}
showModal
/>
)
await user.click(screen.getByRole('button', { name: /Cancel/i }))
expect(closeHandler).to.have.been.calledOnce
})
it('calls the leave handler when confirmed', async function () {
const user = userEvent.setup()
render(
<LeaveProjectModal
handleCloseModal={closeHandler}
handleLeaveAction={leaveHandler}
showModal
/>
)
await user.click(screen.getByRole('button', { name: /Confirm/i }))
expect(leaveHandler).to.have.been.calledOnce
})
})