From 430192e9d171ebc340f0f92310ea4a5fe8f7769b Mon Sep 17 00:00:00 2001 From: Quentin Valmori Date: Thu, 6 Feb 2020 12:14:22 +0100 Subject: [PATCH] feat: Use Markdown viewer for a cozy-note (#1369) --- package.json | 2 +- react/Viewer/TextViewer.jsx | 15 ++-- react/Viewer/TextViewer.spec.jsx | 69 ++++++++++++++++++ .../__snapshots__/TextViewer.spec.jsx.snap | 72 +++++++++++++++++++ react/Viewer/index.spec.jsx | 1 + yarn.lock | 42 ++++++++--- 6 files changed, 184 insertions(+), 17 deletions(-) create mode 100644 react/Viewer/TextViewer.spec.jsx create mode 100644 react/Viewer/__snapshots__/TextViewer.spec.jsx.snap diff --git a/package.json b/package.json index de489deaea..4cd3e1e87a 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "commitlint": "7.6.1", "commitlint-config-cozy": "0.3.27", "copyfiles": "2.1.1", - "cozy-client": "9.4.0", + "cozy-client": "^10.5.0", "cozy-device-helper": "1.7.5", "cozy-doctypes": "^1.69.0", "css-loader": "0.28.11", diff --git a/react/Viewer/TextViewer.jsx b/react/Viewer/TextViewer.jsx index c424a2a765..79c030d19e 100644 --- a/react/Viewer/TextViewer.jsx +++ b/react/Viewer/TextViewer.jsx @@ -2,7 +2,7 @@ import React from 'react' import ReactMarkdown from 'react-markdown' import PropTypes from 'prop-types' import cx from 'classnames' -import { withClient } from 'cozy-client' +import { withClient, models } from 'cozy-client' import Spinner from '../Spinner' import withFileUrl from './withFileUrl' import styles from './styles.styl' @@ -30,7 +30,11 @@ const Loader = () => ( ) -class TextViewer extends React.Component { +export const isMarkdown = file => + file.mime === 'text/markdown' || + /.md$/.test(file.name) || + models.file.isNote(file) +export class TextViewer extends React.Component { state = { text: '', isMarkdown: false, @@ -52,14 +56,14 @@ class TextViewer extends React.Component { const { url, file } = this.props try { const parsedURL = new URL(url) - const client = this.props.client.getClient() + const client = this.props.client.getStackClient() const response = await client.fetch('GET', parsedURL.pathname) const text = await response.text() - const isMarkdown = file.mime === 'text/markdown' || /.md$/.test(file.name) + if (this._mounted) { this.setState({ text, - isMarkdown, + isMarkdown: isMarkdown(file), loading: false }) } @@ -78,7 +82,6 @@ class TextViewer extends React.Component { render() { const { loading, error, text, isMarkdown } = this.state const { file, renderFallbackExtraContent } = this.props - if (loading) return else if (error) return ( diff --git a/react/Viewer/TextViewer.spec.jsx b/react/Viewer/TextViewer.spec.jsx new file mode 100644 index 0000000000..18c032a7bf --- /dev/null +++ b/react/Viewer/TextViewer.spec.jsx @@ -0,0 +1,69 @@ +import React from 'react' +import { shallow } from 'enzyme' +import { TextViewer, isMarkdown } from './TextViewer' +import { createMockClient } from 'cozy-client' +import renderer from 'react-test-renderer' +const props = { + client: createMockClient({}), + url: 'https://foo.mycozy.cloud', + file: { + _id: '1', + _type: 'io.cozy.files', + name: 'My File' + } +} + +describe('isMarkdown function', () => { + it('test markdown function', () => { + const note = { + name: 'My Note.cozy-note', + type: 'file', + metadata: { + content: 'my prosemirror content', + schema: '1', + title: 'prosemirror title', + version: '3' + } + } + expect(isMarkdown({ mime: 'text/markdown' })).toBe(true) + expect(isMarkdown({ name: 'text.md' })).toBe(true) + expect(isMarkdown(note)).toBe(true) + expect(isMarkdown({ name: 'text.txt' })).toBe(false) + }) +}) +describe('TextViewer Component', () => { + it('should display the loader ', () => { + const comp = shallow() + expect(comp).toMatchSnapshot() + }) + it('should display the error component and render with renderFallback', () => { + const comp = renderer.create( + {file.name}} + /> + ) + comp.getInstance().setState({ error: true, loading: false }) + expect(comp.toJSON()).toMatchSnapshot() + }) + it('should display the text viewer', () => { + const comp = renderer.create() + comp.getInstance().setState({ + loading: false, + isMarkdown: false, + text: 'The content of my file' + }) + expect(comp.toJSON()).toMatchSnapshot() + }) + + it('should display the markdown viewer', () => { + const comp = renderer.create() + comp.getInstance().setState({ + loading: false, + isMarkdown: true, + text: + "It's very easy to make some words **bold** and other words *italic* with Markdown" + }) + expect(comp.toJSON()).toMatchSnapshot() + }) +}) diff --git a/react/Viewer/__snapshots__/TextViewer.spec.jsx.snap b/react/Viewer/__snapshots__/TextViewer.spec.jsx.snap new file mode 100644 index 0000000000..89dc719cd9 --- /dev/null +++ b/react/Viewer/__snapshots__/TextViewer.spec.jsx.snap @@ -0,0 +1,72 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TextViewer Component should display the error component and render with renderFallback 1`] = ` +
+ + + +

+ My File +

+ + My File + +
+`; + +exports[`TextViewer Component should display the loader 1`] = ``; + +exports[`TextViewer Component should display the markdown viewer 1`] = ` +
+

+ My File +

+
+

+ It's very easy to make some words + + bold + + and other words + + italic + + with Markdown +

+
+
+`; + +exports[`TextViewer Component should display the text viewer 1`] = ` +
+

+ My File +

+
+    The content of my file
+  
+
+`; diff --git a/react/Viewer/index.spec.jsx b/react/Viewer/index.spec.jsx index 72c590bc24..8cf3dcbcb2 100644 --- a/react/Viewer/index.spec.jsx +++ b/react/Viewer/index.spec.jsx @@ -131,6 +131,7 @@ describe('Plain text file detection', () => { expect(isPlainText('text/markdown')).toBe(true) expect(isPlainText('application/text')).toBe(false) expect(isPlainText('something/text/else')).toBe(false) + expect(isPlainText('text/vnd.cozy.note+markdown')).toBe(true) }) it('should not match complex text formats', () => { diff --git a/yarn.lock b/yarn.lock index 09bc4c2323..d4fbdf5134 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3054,6 +3054,11 @@ btoa-lite@^1.0.0: resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + buble@0.19.7: version "0.19.7" resolved "https://registry.yarnpkg.com/buble/-/buble-0.19.7.tgz#1dfd080ab688101aad5388d3304bc82601a244fd" @@ -4291,19 +4296,24 @@ cosmiconfig@^5.0.1: js-yaml "^3.13.1" parse-json "^4.0.0" -cozy-client@9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/cozy-client/-/cozy-client-9.4.0.tgz#c57a05716a6d4184743fe2031e38a691fb85f93e" - integrity sha512-5YK7OhKH/hsHRYMWVfP4S8wzJk9D7VsQZb1gNUWbMtZnxKZ0lP6mIuyBL4iYg4olqmr4lj77k9utBzW1oZ8bag== +cozy-client@^10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/cozy-client/-/cozy-client-10.5.0.tgz#0dbf05786d9549c266d2f2c20a7448af6d828bb7" + integrity sha512-j2z1lj1yxLOK6mNPKnS+DjLibXrABVMsd9+YM9Beh29KW6f5gefNXecrfBcfWGoUOYF2z3j2xtxeTnncEWQM+A== dependencies: + btoa "^1.2.1" cozy-device-helper "^1.7.3" - cozy-stack-client "^9.2.0" + cozy-logger "^1.6.0" + cozy-stack-client "^10.4.0" + isomorphic-fetch "^2.2.1" lodash "^4.17.13" microee "^0.0.6" + opn "^6.0.0" prop-types "^15.6.2" react-redux "^5.0.7" redux "^3.7.2" redux-thunk "^2.3.0" + server-destroy "^1.0.1" sift "^6.0.0" url-search-params-polyfill "^7.0.0" @@ -4341,10 +4351,10 @@ cozy-logger@^1.6.0: chalk "^2.4.2" json-stringify-safe "5.0.1" -cozy-stack-client@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cozy-stack-client/-/cozy-stack-client-9.2.0.tgz#3d6a9af9e42f6f2d8fad7fe57e83713a3bcafc77" - integrity sha512-ivMjA+EtV7lu6lKzuOIiS00XcTVL6WRbycSmfO7SJrAU12kWp54rlKDS9rMFA2NjCEShjTVdu0intkxYbuimsw== +cozy-stack-client@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/cozy-stack-client/-/cozy-stack-client-10.4.0.tgz#5fc104eb881982c42feae126c2cb6de58e416f9f" + integrity sha512-VoiztT2NXaNQYOtp2w4QVQh9Y05ikJ4ityIOATY9mNjG6Ah+cs42P0SxBw9rcG6J6/hRI6gZqeo95qFwixDdMg== dependencies: detect-node "^2.0.4" mime "^2.4.0" @@ -8054,7 +8064,7 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -isomorphic-fetch@^2.1.1: +isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= @@ -11190,6 +11200,13 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" +opn@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-6.0.0.tgz#3c5b0db676d5f97da1233d1ed42d182bc5a27d2d" + integrity sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ== + dependencies: + is-wsl "^1.1.0" + optimist@0.6.x, optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -14362,6 +14379,11 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" +server-destroy@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" + integrity sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0= + set-blocking@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-1.0.0.tgz#cd5e5d938048df1ac92dfe92e1f16add656f5ec5"