mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
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:
parent
168f83277b
commit
2bed549195
5 changed files with 126 additions and 2 deletions
|
@ -14,6 +14,7 @@
|
||||||
"about_to_delete_the_following_projects": "",
|
"about_to_delete_the_following_projects": "",
|
||||||
"about_to_delete_user_preamble": "",
|
"about_to_delete_user_preamble": "",
|
||||||
"about_to_enable_managed_users": "",
|
"about_to_enable_managed_users": "",
|
||||||
|
"about_to_leave_project": "",
|
||||||
"about_to_leave_projects": "",
|
"about_to_leave_projects": "",
|
||||||
"about_to_trash_projects": "",
|
"about_to_trash_projects": "",
|
||||||
"accept": "",
|
"accept": "",
|
||||||
|
@ -707,6 +708,7 @@
|
||||||
"leave_any_group_subscriptions": "",
|
"leave_any_group_subscriptions": "",
|
||||||
"leave_group": "",
|
"leave_group": "",
|
||||||
"leave_now": "",
|
"leave_now": "",
|
||||||
|
"leave_project": "",
|
||||||
"leave_projects": "",
|
"leave_projects": "",
|
||||||
"left": "",
|
"left": "",
|
||||||
"length_unit": "",
|
"length_unit": "",
|
||||||
|
|
|
@ -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
|
|
@ -2,7 +2,7 @@ import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
import withErrorBoundary from '@/infrastructure/error-boundary'
|
import withErrorBoundary from '@/infrastructure/error-boundary'
|
||||||
import { GenericErrorBoundaryFallback } from '@/shared/components/generic-error-boundary-fallback'
|
import { GenericErrorBoundaryFallback } from '@/shared/components/generic-error-boundary-fallback'
|
||||||
import { useCallback } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import getMeta from '@/utils/meta'
|
import getMeta from '@/utils/meta'
|
||||||
import { postJSON } from '@/infrastructure/fetch-json'
|
import { postJSON } from '@/infrastructure/fetch-json'
|
||||||
import { debugConsole } from '@/utils/debugging'
|
import { debugConsole } from '@/utils/debugging'
|
||||||
|
@ -10,7 +10,10 @@ import useAsync from '@/shared/hooks/use-async'
|
||||||
import Notification from '@/shared/components/notification'
|
import Notification from '@/shared/components/notification'
|
||||||
import { sendMB } from '@/infrastructure/event-tracking'
|
import { sendMB } from '@/infrastructure/event-tracking'
|
||||||
|
|
||||||
|
import LeaveProjectModal from './leave-project-modal'
|
||||||
|
|
||||||
function SharingUpdatesRoot() {
|
function SharingUpdatesRoot() {
|
||||||
|
const [showModal, setShowModal] = useState(false)
|
||||||
const { isReady } = useWaitForI18n()
|
const { isReady } = useWaitForI18n()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { isLoading, isSuccess, isError, runAsync } = useAsync()
|
const { isLoading, isSuccess, isError, runAsync } = useAsync()
|
||||||
|
@ -58,6 +61,11 @@ function SharingUpdatesRoot() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
|
<LeaveProjectModal
|
||||||
|
handleLeaveAction={leaveProject}
|
||||||
|
showModal={showModal}
|
||||||
|
handleCloseModal={() => setShowModal(false)}
|
||||||
|
/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-6 col-md-offset-3">
|
<div className="col-md-6 col-md-offset-3">
|
||||||
<div className="card sharing-updates">
|
<div className="card sharing-updates">
|
||||||
|
@ -132,7 +140,7 @@ function SharingUpdatesRoot() {
|
||||||
// eslint-disable-next-line react/jsx-key
|
// eslint-disable-next-line react/jsx-key
|
||||||
<button
|
<button
|
||||||
className="btn btn-inline-link"
|
className="btn btn-inline-link"
|
||||||
onClick={() => leaveProject()}
|
onClick={() => setShowModal(true)}
|
||||||
disabled={isLoading || isSuccess}
|
disabled={isLoading || isSuccess}
|
||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"about_to_delete_the_following_projects": "You are about to delete the following projects",
|
"about_to_delete_the_following_projects": "You are about to delete the following projects",
|
||||||
"about_to_delete_user_preamble": "You’re about to delete __userName__ (__userEmail__). Doing this will mean:",
|
"about_to_delete_user_preamble": "You’re 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_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_leave_projects": "You are about to leave the following projects:",
|
||||||
"about_to_trash_projects": "You are about to trash the following projects:",
|
"about_to_trash_projects": "You are about to trash the following projects:",
|
||||||
"abstract": "Abstract",
|
"abstract": "Abstract",
|
||||||
|
@ -1045,6 +1046,7 @@
|
||||||
"leave_group": "Leave group",
|
"leave_group": "Leave group",
|
||||||
"leave_labs": "Leave Overleaf Labs",
|
"leave_labs": "Leave Overleaf Labs",
|
||||||
"leave_now": "Leave now",
|
"leave_now": "Leave now",
|
||||||
|
"leave_project": "Leave Project",
|
||||||
"leave_projects": "Leave Projects",
|
"leave_projects": "Leave Projects",
|
||||||
"left": "Left",
|
"left": "Left",
|
||||||
"length_unit": "Length unit",
|
"length_unit": "Length unit",
|
||||||
|
|
|
@ -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
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue