Use "untitled" fallback in history entry without title (#1546)

This commit is contained in:
Erik Michelson 2021-10-24 21:08:28 +02:00 committed by GitHub
parent 8096267161
commit 9118c8310b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 53 deletions

View file

@ -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')
})
})
})

View file

@ -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"
]
},
{

View file

@ -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>

View file

@ -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()}

View file

@ -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)}

View file

@ -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>

View file

@ -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>

View 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])
}