mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 15:14:56 -04:00
Use "untitled" fallback in history entry without title (#1546)
This commit is contained in:
parent
8096267161
commit
9118c8310b
8 changed files with 129 additions and 53 deletions
|
@ -5,25 +5,84 @@
|
|||
*/
|
||||
|
||||
describe('History', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/history')
|
||||
})
|
||||
|
||||
describe('History Mode', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/history')
|
||||
})
|
||||
|
||||
it('Cards', () => {
|
||||
cy.get('div.card')
|
||||
.should('be.visible')
|
||||
cy.get('div.card').should('be.visible')
|
||||
})
|
||||
|
||||
it('Table', () => {
|
||||
cy.get('i.fa-table')
|
||||
.click()
|
||||
cy.get('table.history-table')
|
||||
.should('be.visible')
|
||||
cy.get('[data-cypress-id="history-mode-table"]').click()
|
||||
cy.get('[data-cypress-id="history-table"]').should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
describe('entry title', () => {
|
||||
describe('is as given when not empty', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage('history')
|
||||
cy.intercept('GET', '/mock-backend/api/private/me/history', {
|
||||
body: [
|
||||
{
|
||||
identifier: 'cypress',
|
||||
title: 'Features',
|
||||
lastVisited: '2020-05-16T22:26:56.547Z',
|
||||
pinStatus: false,
|
||||
tags: []
|
||||
}
|
||||
]
|
||||
})
|
||||
cy.visit('/history')
|
||||
})
|
||||
|
||||
it('in table view', () => {
|
||||
cy.get('[data-cypress-id="history-mode-table"]').click()
|
||||
cy.get('[data-cypress-id="history-table"]').should('be.visible')
|
||||
cy.get('[data-cypress-id="history-entry-title"]').contains('Features')
|
||||
})
|
||||
|
||||
it('in cards view', () => {
|
||||
cy.get('[data-cypress-id="history-entry-title"]').contains('Features')
|
||||
})
|
||||
})
|
||||
describe('is untitled when not empty', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage('history')
|
||||
cy.intercept('GET', '/mock-backend/api/private/me/history', {
|
||||
body: [
|
||||
{
|
||||
identifier: 'cypress-no-title',
|
||||
title: '',
|
||||
lastVisited: '2020-05-16T22:26:56.547Z',
|
||||
pinStatus: false,
|
||||
tags: []
|
||||
}
|
||||
]
|
||||
})
|
||||
cy.visit('/history')
|
||||
})
|
||||
|
||||
it('in table view', () => {
|
||||
cy.get('[data-cypress-id="history-mode-table"]').click()
|
||||
cy.get('[data-cypress-id="history-table"]').should('be.visible')
|
||||
cy.get('[data-cypress-id="history-entry-title"]').contains('Untitled')
|
||||
})
|
||||
|
||||
it('in cards view', () => {
|
||||
cy.get('[data-cypress-id="history-entry-title"]').contains('Untitled')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Pinning', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/history')
|
||||
})
|
||||
|
||||
describe('working', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('PUT', '/mock-backend/api/private/me/history/features', (req) => {
|
||||
|
@ -32,29 +91,17 @@ describe('History', () => {
|
|||
})
|
||||
|
||||
it('Cards', () => {
|
||||
cy.get('div.card')
|
||||
.should('be.visible')
|
||||
cy.get('.history-pin.btn')
|
||||
.first()
|
||||
.as('pin-button')
|
||||
cy.get('@pin-button')
|
||||
.should('have.class', 'pinned')
|
||||
.click()
|
||||
cy.get('@pin-button')
|
||||
.should('not.have.class', 'pinned')
|
||||
cy.get('div.card').should('be.visible')
|
||||
cy.get('.history-pin.btn').first().as('pin-button')
|
||||
cy.get('@pin-button').should('have.class', 'pinned').click()
|
||||
cy.get('@pin-button').should('not.have.class', 'pinned')
|
||||
})
|
||||
|
||||
it('Table', () => {
|
||||
cy.get('i.fa-table')
|
||||
.click()
|
||||
cy.get('.history-pin.btn')
|
||||
.first()
|
||||
.as('pin-button')
|
||||
cy.get('@pin-button')
|
||||
.should('have.class', 'pinned')
|
||||
.click()
|
||||
cy.get('@pin-button')
|
||||
.should('not.have.class', 'pinned')
|
||||
cy.get('i.fa-table').click()
|
||||
cy.get('.history-pin.btn').first().as('pin-button')
|
||||
cy.get('@pin-button').should('have.class', 'pinned').click()
|
||||
cy.get('@pin-button').should('not.have.class', 'pinned')
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -66,23 +113,15 @@ describe('History', () => {
|
|||
})
|
||||
|
||||
it('Cards', () => {
|
||||
cy.get('div.card')
|
||||
.should('be.visible')
|
||||
cy.get('.fa-thumb-tack')
|
||||
.first()
|
||||
.click()
|
||||
cy.get('.notifications-area .toast')
|
||||
.should('be.visible')
|
||||
cy.get('div.card').should('be.visible')
|
||||
cy.get('.fa-thumb-tack').first().click()
|
||||
cy.get('.notifications-area .toast').should('be.visible')
|
||||
})
|
||||
|
||||
it('Table', () => {
|
||||
cy.get('i.fa-table')
|
||||
.click()
|
||||
cy.get('.fa-thumb-tack')
|
||||
.first()
|
||||
.click()
|
||||
cy.get('.notifications-area .toast')
|
||||
.should('be.visible')
|
||||
cy.get('i.fa-table').click()
|
||||
cy.get('.fa-thumb-tack').first().click()
|
||||
cy.get('.notifications-area .toast').should('be.visible')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
[
|
||||
{
|
||||
"identifier": "29QLD0AmT-adevdOPECtqg",
|
||||
"title": "HedgeDoc community call 2020-04-26",
|
||||
"title": "",
|
||||
"lastVisited": "2020-05-16T22:26:56.547Z",
|
||||
"pinStatus": false,
|
||||
"tags": [
|
||||
"HedgeDoc",
|
||||
"Community Call"
|
||||
"empty title",
|
||||
"should be untitled"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@ import type { OnWordCountCalculatedMessage } from '../../../render-page/window-p
|
|||
import { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message'
|
||||
import { useEditorReceiveHandler } from '../../../render-page/window-post-message-communicator/hooks/use-editor-receive-handler'
|
||||
import { useEffectOnRendererReady } from '../../../render-page/window-post-message-communicator/hooks/use-effect-on-renderer-ready'
|
||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||
|
||||
/**
|
||||
* Creates a new info line for the document information dialog that holds the
|
||||
|
@ -42,7 +43,7 @@ export const DocumentInfoLineWordCount: React.FC = () => {
|
|||
</ShowIf>
|
||||
<ShowIf condition={wordCount !== null}>
|
||||
<Trans i18nKey={'editor.modal.documentInfo.words'}>
|
||||
<UnitalicBoldText text={wordCount ?? ''} data-cypress-id={'document-info-word-count'} />
|
||||
<UnitalicBoldText text={wordCount ?? ''} {...cypressId('document-info-word-count')} />
|
||||
</Trans>
|
||||
</ShowIf>
|
||||
</DocumentInfoLine>
|
||||
|
|
|
@ -14,6 +14,8 @@ import type { HistoryEntryProps, HistoryEventHandlers } from '../history-content
|
|||
import { PinButton } from '../pin-button/pin-button'
|
||||
import { formatHistoryDate } from '../utils'
|
||||
import './history-card.scss'
|
||||
import { useHistoryEntryTitle } from '../use-history-entry-title'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
|
||||
export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = ({
|
||||
entry,
|
||||
|
@ -29,6 +31,8 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
|
|||
onDeleteClick(entry.identifier)
|
||||
}, [onDeleteClick, entry.identifier])
|
||||
|
||||
const entryTitle = useHistoryEntryTitle(entry)
|
||||
|
||||
return (
|
||||
<div className='p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4'>
|
||||
<Card className='card-min-height' text={'dark'} bg={'light'}>
|
||||
|
@ -38,7 +42,9 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
|
|||
</div>
|
||||
<Link to={`/n/${entry.identifier}`} className='text-decoration-none flex-fill text-dark'>
|
||||
<div className={'d-flex flex-column justify-content-between'}>
|
||||
<Card.Title className='m-0 mt-1dot5'>{entry.title}</Card.Title>
|
||||
<Card.Title className='m-0 mt-1dot5' {...cypressId('history-entry-title')}>
|
||||
{entryTitle}
|
||||
</Card.Title>
|
||||
<div>
|
||||
<div className='text-black-50 mt-2'>
|
||||
<ForkAwesomeIcon icon='clock-o' /> {DateTime.fromISO(entry.lastVisited).toRelative()}
|
||||
|
|
|
@ -11,6 +11,8 @@ import { EntryMenu } from '../entry-menu/entry-menu'
|
|||
import type { HistoryEntryProps, HistoryEventHandlers } from '../history-content/history-content'
|
||||
import { PinButton } from '../pin-button/pin-button'
|
||||
import { formatHistoryDate } from '../utils'
|
||||
import { useHistoryEntryTitle } from '../use-history-entry-title'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
|
||||
export const HistoryTableRow: React.FC<HistoryEntryProps & HistoryEventHandlers> = ({
|
||||
entry,
|
||||
|
@ -18,11 +20,12 @@ export const HistoryTableRow: React.FC<HistoryEntryProps & HistoryEventHandlers>
|
|||
onRemoveClick,
|
||||
onDeleteClick
|
||||
}) => {
|
||||
const entryTitle = useHistoryEntryTitle(entry)
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<Link to={`/n/${entry.identifier}`} className='text-light'>
|
||||
{entry.title}
|
||||
<Link to={`/n/${entry.identifier}`} className='text-light' {...cypressId('history-entry-title')}>
|
||||
{entryTitle}
|
||||
</Link>
|
||||
</td>
|
||||
<td>{formatHistoryDate(entry.lastVisited)}</td>
|
||||
|
@ -42,7 +45,7 @@ export const HistoryTableRow: React.FC<HistoryEntryProps & HistoryEventHandlers>
|
|||
/>
|
||||
<EntryMenu
|
||||
id={entry.identifier}
|
||||
title={entry.title}
|
||||
title={entryTitle}
|
||||
origin={entry.origin}
|
||||
isDark={true}
|
||||
onRemove={() => onRemoveClick(entry.identifier)}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { Pager } from '../../common/pagination/pager'
|
|||
import type { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content'
|
||||
import { HistoryTableRow } from './history-table-row'
|
||||
import './history-table.scss'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
|
||||
export const HistoryTable: React.FC<HistoryEntriesProps & HistoryEventHandlers> = ({
|
||||
entries,
|
||||
|
@ -22,7 +23,7 @@ export const HistoryTable: React.FC<HistoryEntriesProps & HistoryEventHandlers>
|
|||
}) => {
|
||||
useTranslation()
|
||||
return (
|
||||
<Table striped bordered hover size='sm' variant='dark' className={'history-table'}>
|
||||
<Table striped bordered hover size='sm' variant='dark' className={'history-table'} {...cypressId('history-table')}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
|
|
|
@ -22,6 +22,7 @@ import { HistoryEntryOrigin } from '../../../redux/history/types'
|
|||
import { importHistoryEntries, refreshHistoryState, setHistoryEntries } from '../../../redux/history/methods'
|
||||
import { showErrorNotification } from '../../../redux/ui-notifications/methods'
|
||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
|
||||
export type HistoryToolbarChange = (newState: HistoryToolbarState) => void
|
||||
|
||||
|
@ -223,7 +224,11 @@ export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange
|
|||
<ToggleButton className={'btn-light'} value={ViewStateEnum.CARD} title={t('landing.history.toolbar.cards')}>
|
||||
<ForkAwesomeIcon icon={'sticky-note'} className={'fa-fix-line-height'} />
|
||||
</ToggleButton>
|
||||
<ToggleButton className={'btn-light'} value={ViewStateEnum.TABLE} title={t('landing.history.toolbar.table')}>
|
||||
<ToggleButton
|
||||
{...cypressId('history-mode-table')}
|
||||
className={'btn-light'}
|
||||
value={ViewStateEnum.TABLE}
|
||||
title={t('landing.history.toolbar.table')}>
|
||||
<ForkAwesomeIcon icon={'table'} className={'fa-fix-line-height'} />
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
|
|
21
src/components/history-page/use-history-entry-title.ts
Normal file
21
src/components/history-page/use-history-entry-title.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { HistoryEntry } from '../../redux/history/types'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
/**
|
||||
* Hook that returns the title of a note in the history if present or the translation for "untitled" otherwise.
|
||||
* @param entry The history entry containing a title property, that might be an empty string.
|
||||
* @return A memoized string containing either the title of the entry or the translated version of "untitled".
|
||||
*/
|
||||
export const useHistoryEntryTitle = (entry: HistoryEntry): string => {
|
||||
const { t } = useTranslation()
|
||||
return useMemo(() => {
|
||||
return entry.title !== '' ? entry.title : t('editor.untitledNote')
|
||||
}, [t, entry])
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue