1
0
Fork 0
mirror of https://github.com/hedgedoc/hedgedoc.git synced 2025-03-25 14:12:28 +00:00

Added markdown-file import ()

* Added markdown-file import

* Reset file input after read, don't add unnecessary blank lines

* Add cypress-file-upload dependency

* Add cypress tests for md file importing

* Added CHANGELOG entry
This commit is contained in:
Erik Michelson 2020-10-09 21:26:04 +02:00 committed by GitHub
parent c1d4ac1014
commit 729ad652b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 113 additions and 7 deletions
CHANGELOG.md
cypress
fixtures
integration
support
package.json
public/locales
src/components/editor
yarn.lock

View file

@ -44,6 +44,7 @@
- All images can be clicked to show them in full screen.
- Code blocks have a 'Copy code to clipboard' button.
- Code blocks with 'vega-lite' as language are rendered as [vega-lite diagrams](https://vega.github.io/vega-lite/examples/).
- Markdown files can be imported into an existing note directly from the editor.
### Changed

View file

@ -0,0 +1,2 @@
# Some short import test file
:)

View file

@ -0,0 +1,42 @@
describe('Import markdown file', () => {
beforeEach(() => {
cy.visit('/n/test')
cy.get('.btn.active.btn-outline-secondary > i.fa-columns')
.should('exist')
cy.get('.CodeMirror textarea')
.type('{ctrl}a', { force: true })
.type('{backspace}')
})
it('import on blank note', () => {
cy.get('button#editor-menu-import')
.click()
cy.get('.import-md-file')
.click()
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
.attachFile('import.md')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '# Some short import test file')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
.should('have.text', ':)')
})
it('import on note with content', () => {
cy.get('.CodeMirror textarea')
.type('test\nabc', { force: true })
cy.get('button#editor-menu-import')
.click()
cy.get('.import-md-file')
.click()
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
.attachFile('import.md')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', 'test')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
.should('have.text', 'abc')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span')
.should('have.text', '# Some short import test file')
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span > span')
.should('have.text', ':)')
})
})

View file

@ -14,6 +14,7 @@
// ***********************************************************
import 'cypress-commands'
import 'cypress-file-upload'
import './checkLinks'
import './config'
import './login'

View file

@ -159,6 +159,7 @@
"cross-env": "7.0.2",
"cypress": "5.3.0",
"cypress-commands": "1.1.0",
"cypress-file-upload": "^4.1.1",
"eslint-plugin-chai-friendly": "0.6.0",
"eslint-plugin-cypress": "2.11.2",
"http-server": "0.12.3",

View file

@ -301,7 +301,8 @@
"pdf": "PDF export is unavailable."
},
"import": {
"clipboard": "Clipboard"
"clipboard": "Clipboard",
"file": "Markdown file"
},
"modal": {
"snippetImport": {

View file

@ -6,16 +6,15 @@ import { ConnectionIndicator } from './connection-indicator/connection-indicator
import { DocumentInfoButton } from './document-info/document-info-button'
import { EditorMenu } from './menus/editor-menu'
import { ExportMenu } from './menus/export-menu'
import { ImportMenu } from './menus/import-menu'
import { ImportMenu, ImportProps } from './menus/import-menu'
import { PermissionButton } from './permissions/permission-button'
import { RevisionButton } from './revisions/revision-button'
export interface DocumentBarProps {
title: string
noteContent: string
}
export const DocumentBar: React.FC<DocumentBarProps> = ({ title, noteContent }) => {
export const DocumentBar: React.FC<DocumentBarProps & ImportProps> = ({ title, noteContent, updateNoteContent }) => {
useTranslation()
return (
@ -28,7 +27,7 @@ export const DocumentBar: React.FC<DocumentBarProps> = ({ title, noteContent })
<PermissionButton/>
</div>
<div className="ml-auto navbar-nav">
<ImportMenu/>
<ImportMenu updateNoteContent={updateNoteContent} noteContent={noteContent}/>
<ExportMenu title={title} noteContent={noteContent}/>
<EditorMenu noteTitle={title}/>
<ConnectionIndicator/>

View file

@ -0,0 +1,45 @@
import React, { Fragment, useCallback, useRef } from 'react'
import { Dropdown } from 'react-bootstrap'
import { Trans } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { ImportProps } from '../menus/import-menu'
export const ImportFile: React.FC<ImportProps> = ({ noteContent, updateNoteContent }) => {
const fileInputReference = useRef<HTMLInputElement>(null)
const doImport = useCallback(() => {
const fileInput = fileInputReference.current
if (!fileInput) {
return
}
fileInput.addEventListener('change', () => {
if (!fileInput.files || fileInput.files.length < 1) {
return
}
const file = fileInput.files[0]
const fileReader = new FileReader()
fileReader.addEventListener('load', () => {
const newContent = fileReader.result as string
if (noteContent.length === 0) {
updateNoteContent(newContent)
} else {
updateNoteContent(noteContent + '\n' + newContent)
}
})
fileReader.addEventListener('loadend', () => {
fileInput.value = ''
})
fileReader.readAsText(file)
})
fileInput.click()
}, [fileInputReference, noteContent, updateNoteContent])
return (
<Fragment>
<input type='file' ref={fileInputReference} className='d-none' accept='.md, text/markdown, text/plain'/>
<Dropdown.Item className='small import-md-file' onClick={doImport}>
<ForkAwesomeIcon icon='file-text-o' className={'mx-2'}/>
<Trans i18nKey='editor.import.file'/>
</Dropdown.Item>
</Fragment>
)
}

View file

@ -2,8 +2,14 @@ import React from 'react'
import { Dropdown } from 'react-bootstrap'
import { Trans } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { ImportFile } from '../import/import-file'
export const ImportMenu: React.FC = () => {
export interface ImportProps {
noteContent: string
updateNoteContent: (content: string) => void
}
export const ImportMenu: React.FC<ImportProps> = ({ updateNoteContent, noteContent }) => {
return (
<Dropdown className='small mx-1' alignRight={true}>
<Dropdown.Toggle variant='light' size='sm' id='editor-menu-import' className=''>
@ -27,6 +33,7 @@ export const ImportMenu: React.FC = () => {
<ForkAwesomeIcon icon='clipboard' className={'mx-2'}/>
<Trans i18nKey='editor.import.clipboard'/>
</Dropdown.Item>
<ImportFile updateNoteContent={updateNoteContent} noteContent={noteContent}/>
</Dropdown.Menu>
</Dropdown>
)

View file

@ -115,7 +115,7 @@ export const Editor: React.FC = () => {
<DocumentTitle title={documentTitle}/>
<div className={'d-flex flex-column vh-100'}>
<AppBar/>
<DocumentBar title={documentTitle} noteContent={markdownContent}/>
<DocumentBar title={documentTitle} noteContent={markdownContent} updateNoteContent={(newContent) => setMarkdownContent(newContent)}/>
<Splitter
showLeft={editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH}
left={

View file

@ -4802,6 +4802,13 @@ cypress-commands@1.1.0:
resolved "https://registry.yarnpkg.com/cypress-commands/-/cypress-commands-1.1.0.tgz#9248190168783deb8ab27ae7c722e3e01d172c97"
integrity sha512-Q8Jr25pHJQFXwln6Hp8O+Hgs8Z506Y2wA9F1Te2cTajjc5L9gtt9WPOcw1Ogh+OgyqaMHF+uq31vdfImRTio5Q==
cypress-file-upload@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-4.1.1.tgz#952713c8104ab7008de99c65bd63f74b244fe4df"
integrity sha512-tX6UhuJ63rNgjdzxglpX+ZYf/bM6PDhFMtt1qCBljLtAgdearqyfD1AHqyh59rOHCjfM+bf6FA3o9b/mdaX6pw==
dependencies:
mime "^2.4.4"
cypress@5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-5.3.0.tgz#91122219ae66ab910058970dbf36619ab0fbde6c"