mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-29 03:54:26 -05:00
fix: use actual noteTitle in DeleteNoteSidebarEntry (#1729)
fixes #1728 Signed-off-by: Philip Molares <philip.molares@udo.edu> Co-Authored-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
6f320d82a5
commit
4089f0c6ed
6 changed files with 151 additions and 42 deletions
50
cypress/integration/deleteNote.spec.ts
Normal file
50
cypress/integration/deleteNote.spec.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('Delete note', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visitTestEditor()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('correctly deletes a note', () => {
|
||||||
|
cy.intercept('DELETE', '/mock-backend/api/private/notes/mock_note_id', {
|
||||||
|
statusCode: 200
|
||||||
|
})
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.button').click()
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal').should('be.visible')
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal.noteTitle').should('be.visible').text().should('eq', '')
|
||||||
|
cy.getByCypressId('deletionModal.confirmButton').should('be.visible').click()
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal').should('not.be.exist')
|
||||||
|
cy.getByCypressId('notification-toast').should('not.exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays an error notification if something goes wrong', () => {
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.button').click()
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal').should('be.visible')
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal.noteTitle').should('be.visible').text().should('eq', '')
|
||||||
|
cy.getByCypressId('deletionModal.confirmButton').should('be.visible').click()
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal').should('not.exist')
|
||||||
|
cy.getByCypressId('notification-toast').should('be.visible')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('displays the note title coming from', () => {
|
||||||
|
const title = 'mock_title'
|
||||||
|
it('yaml metadata', () => {
|
||||||
|
cy.setCodemirrorContent(`---\ntitle: ${title}\n---`)
|
||||||
|
})
|
||||||
|
it('opengraph', () => {
|
||||||
|
cy.setCodemirrorContent(`---\nopengraph:\n title: ${title}\n---`)
|
||||||
|
})
|
||||||
|
it('just first heading', () => {
|
||||||
|
cy.setCodemirrorContent(`# ${title}`)
|
||||||
|
})
|
||||||
|
afterEach(() => {
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.button').click()
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal').should('be.visible')
|
||||||
|
cy.getByCypressId('sidebar.deleteNote.modal.noteTitle').should('be.visible').text().should('eq', title)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -9,6 +9,7 @@ import { Button, Modal } from 'react-bootstrap'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
import type { CommonModalProps } from './common-modal'
|
import type { CommonModalProps } from './common-modal'
|
||||||
import { CommonModal } from './common-modal'
|
import { CommonModal } from './common-modal'
|
||||||
|
import { cypressId } from '../../../utils/cypress-attribute'
|
||||||
|
|
||||||
export interface DeletionModalProps extends CommonModalProps {
|
export interface DeletionModalProps extends CommonModalProps {
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
|
@ -31,7 +32,7 @@ export const DeletionModal: React.FC<DeletionModalProps> = ({
|
||||||
<CommonModal show={show} onHide={onHide} title={title} titleIcon={titleIcon} showCloseButton={true} {...props}>
|
<CommonModal show={show} onHide={onHide} title={title} titleIcon={titleIcon} showCloseButton={true} {...props}>
|
||||||
<Modal.Body className='text-dark'>{children}</Modal.Body>
|
<Modal.Body className='text-dark'>{children}</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<Button variant='danger' onClick={onConfirm}>
|
<Button {...cypressId('deletionModal.confirmButton')} variant='danger' onClick={onConfirm}>
|
||||||
<Trans i18nKey={deletionButtonI18nKey} />
|
<Trans i18nKey={deletionButtonI18nKey} />
|
||||||
</Button>
|
</Button>
|
||||||
</Modal.Footer>
|
</Modal.Footer>
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||||
|
import { Trans } from 'react-i18next'
|
||||||
|
import { DeletionModal } from '../../../common/modals/deletion-modal'
|
||||||
|
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||||
|
import type { ModalVisibilityProps } from '../../../common/modals/common-modal'
|
||||||
|
|
||||||
|
export interface DeleteNoteModalProps extends ModalVisibilityProps {
|
||||||
|
onConfirm: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A modal that asks the user if they really want to delete the current note.
|
||||||
|
*
|
||||||
|
* @param show Defines if the modal should be shown
|
||||||
|
* @param onHide A callback that fires if the modal should be hidden without confirmation
|
||||||
|
* @param onConfirm A callback that fires if the user confirmed the request
|
||||||
|
*/
|
||||||
|
export const DeleteNoteModal: React.FC<DeleteNoteModalProps> = ({ show, onHide, onConfirm }) => {
|
||||||
|
const noteTitle = useApplicationState((state) => state.noteDetails.noteTitle)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DeletionModal
|
||||||
|
{...cypressId('sidebar.deleteNote.modal')}
|
||||||
|
onConfirm={onConfirm}
|
||||||
|
deletionButtonI18nKey={'editor.modal.deleteNote.button'}
|
||||||
|
show={show}
|
||||||
|
onHide={onHide}
|
||||||
|
title={'editor.modal.deleteNote.title'}>
|
||||||
|
<h5>
|
||||||
|
<Trans i18nKey={'editor.modal.deleteNote.question'} />
|
||||||
|
</h5>
|
||||||
|
<ul>
|
||||||
|
<li {...cypressId('sidebar.deleteNote.modal.noteTitle')}> {noteTitle}</li>
|
||||||
|
</ul>
|
||||||
|
<h6>
|
||||||
|
<Trans i18nKey={'editor.modal.deleteNote.warning'} />
|
||||||
|
</h6>
|
||||||
|
</DeletionModal>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { Fragment, useCallback, useState } from 'react'
|
||||||
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||||
|
import type { SpecificSidebarEntryProps } from '../types'
|
||||||
|
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||||
|
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||||
|
import { showErrorNotification } from '../../../../redux/ui-notifications/methods'
|
||||||
|
import { deleteNote } from '../../../../api/notes'
|
||||||
|
import { DeleteNoteModal } from './delete-note-modal'
|
||||||
|
import { ShowIf } from '../../../common/show-if/show-if'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sidebar entry that can be used to delete the current note.
|
||||||
|
*
|
||||||
|
* @param hide {@code true} if the entry shouldn't be visible
|
||||||
|
* @param className Additional css class names for the sidebar entry
|
||||||
|
*/
|
||||||
|
export const DeleteNoteSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ hide, className }) => {
|
||||||
|
useTranslation()
|
||||||
|
const [showDialog, setShowDialog] = useState(false)
|
||||||
|
const noteId = useApplicationState((state) => state.noteDetails.id)
|
||||||
|
const openDialog = useCallback(() => setShowDialog(true), [])
|
||||||
|
const closeDialog = useCallback(() => setShowDialog(false), [])
|
||||||
|
const deleteNoteAndCloseDialog = useCallback(() => {
|
||||||
|
deleteNote(noteId)
|
||||||
|
.catch(showErrorNotification('landing.history.error.deleteNote.text'))
|
||||||
|
.finally(() => setShowDialog(false))
|
||||||
|
}, [noteId])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<SidebarButton
|
||||||
|
{...cypressId('sidebar.deleteNote.button')}
|
||||||
|
icon={'trash'}
|
||||||
|
className={className}
|
||||||
|
hide={hide}
|
||||||
|
onClick={openDialog}>
|
||||||
|
<Trans i18nKey={'landing.history.menu.deleteNote'} />
|
||||||
|
</SidebarButton>
|
||||||
|
<ShowIf condition={showDialog}>
|
||||||
|
<DeleteNoteModal onHide={closeDialog} onConfirm={deleteNoteAndCloseDialog} show={showDialog} />
|
||||||
|
</ShowIf>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useCallback, useRef, useState } from 'react'
|
import React, { useCallback, useRef, useState } from 'react'
|
||||||
import { useClickAway } from 'react-use'
|
import { useClickAway } from 'react-use'
|
||||||
import { DeleteNoteSidebarEntry } from './specific-sidebar-entries/delete-note-sidebar-entry'
|
import { DeleteNoteSidebarEntry } from './delete-note-sidebar-entry/delete-note-sidebar-entry'
|
||||||
import { DocumentInfoSidebarEntry } from './specific-sidebar-entries/document-info-sidebar-entry'
|
import { DocumentInfoSidebarEntry } from './specific-sidebar-entries/document-info-sidebar-entry'
|
||||||
import { ExportMenuSidebarMenu } from './specific-sidebar-entries/export-menu-sidebar-menu'
|
import { ExportMenuSidebarMenu } from './specific-sidebar-entries/export-menu-sidebar-menu'
|
||||||
import { ImportMenuSidebarMenu } from './specific-sidebar-entries/import-menu-sidebar-menu'
|
import { ImportMenuSidebarMenu } from './specific-sidebar-entries/import-menu-sidebar-menu'
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { Fragment, useState } from 'react'
|
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
|
||||||
import { DeletionModal } from '../../../common/modals/deletion-modal'
|
|
||||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
|
||||||
import type { SpecificSidebarEntryProps } from '../types'
|
|
||||||
|
|
||||||
export const DeleteNoteSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ hide, className }) => {
|
|
||||||
useTranslation()
|
|
||||||
const [showDialog, setShowDialog] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<SidebarButton icon={'trash'} className={className} hide={hide} onClick={() => setShowDialog(true)}>
|
|
||||||
<Trans i18nKey={'landing.history.menu.deleteNote'} />
|
|
||||||
</SidebarButton>
|
|
||||||
<DeletionModal
|
|
||||||
onConfirm={() => setShowDialog(false)}
|
|
||||||
deletionButtonI18nKey={'editor.modal.deleteNote.button'}
|
|
||||||
show={showDialog}
|
|
||||||
onHide={() => setShowDialog(false)}
|
|
||||||
title={'editor.modal.deleteNote.title'}>
|
|
||||||
<h5>
|
|
||||||
<Trans i18nKey={'editor.modal.deleteNote.question'} />
|
|
||||||
</h5>
|
|
||||||
<ul>
|
|
||||||
<li> noteTitle</li>
|
|
||||||
</ul>
|
|
||||||
<h6>
|
|
||||||
<Trans i18nKey={'editor.modal.deleteNote.warning'} />
|
|
||||||
</h6>
|
|
||||||
</DeletionModal>
|
|
||||||
</Fragment>
|
|
||||||
)
|
|
||||||
}
|
|
Loading…
Reference in a new issue