# HG changeset patch # User ymh # Date 1539016547 -7200 # Node ID ea92f4fe783d543dad1adf2931270e68162a92e0 # Parent 1f340f3597a886a5ef479fd74f4957899b800f61 - move SlateEditor and dependencies to its own folder - remove Immutable - remove redux-persist-immutable - remobe redux-immutable - update libraries - added tests on store manipulations (accessor and reducers) diff -r 1f340f3597a8 -r ea92f4fe783d client/package.json --- a/client/package.json Tue Oct 09 19:07:47 2018 +0200 +++ b/client/package.json Mon Oct 08 18:35:47 2018 +0200 @@ -2,35 +2,36 @@ "name": "client", "version": "0.1.0", "private": true, - "homepage": ".", + "homepage": "http://www.iri.centrepompidou.fr", "dependencies": { "@types/react-modal": "^3.2.1", "@types/slate-html-serializer": "^0.6.2", - "@types/slate-plain-serializer": "^0.5.1", + "@types/slate-plain-serializer": "^0.6.0", "@types/slate-react": "^0.18.0", "bootstrap": "^4.1.3", + "connected-react-router": "^4.5.0", "i18next": "^11.6.0", - "immutable": "^3.8.1", + "immutable": "^3.8.2", + "jquery": "^3.3.1", "jwt-decode": "^2.2.0", "localforage": "^1.5.0", "lodash": "^4.17.4", "moment": "^2.18.1", - "npm": "^5.10.0", + "npm": "^6.4.1", + "popper.js": "^1.14.4", "qs": "^6.5.0", - "react": "^15.5.4", - "react-dom": "^15.5.4", + "ramda": "^0.25.0", + "react": "^16.5.2", + "react-dom": "^16.5.2", "react-modal": "^3.5.1", - "react-overlays": "^0.7.0", - "react-portal": "^3.1.0", + "react-overlays": "^0.8.3", + "react-portal": "^4.1.5", "react-redux": "^5.0.5", - "react-router-redux": "next", - "redux": "^3.6.0", - "redux-immutable": "^4.0.0", - "redux-persist": "^4.8.2", - "redux-persist-immutable": "^4.3.0", - "redux-persist-transform-immutable": "^4.3.0", - "redux-saga": "^0.15.6", - "slate": "^0.40.2", + "react-router": "^4.3.1", + "redux": "^4.0.0", + "redux-persist": "^5.10.0", + "redux-saga": "^0.16.0", + "slate": "^0.41.1", "slate-html-serializer": "^0.7.2", "slate-plain-serializer": "^0.6.2", "slate-react": "^0.18.5", @@ -38,12 +39,13 @@ "yarn": "^1.6.0" }, "devDependencies": { - "enzyme": "^2.8.2", + "enzyme": "^3.6.0", + "enzyme-adapter-react-16": "^1.5.0", "material-design-icons": "^3.0.1", - "node-sass-chokidar": "^0.0.1", + "node-sass-chokidar": "^1.3.3", "npm-run-all": "^4.1.2", - "react-scripts": "1.0.2", - "react-test-renderer": "^15.5.4" + "react-scripts": "1.1.5", + "react-test-renderer": "^16.5.2" }, "scripts": { "build-css": "node-sass-chokidar --include-path ./src --include-path ./src/scss --include-path ./node_modules src/ -o src/", diff -r 1f340f3597a8 -r ea92f4fe783d client/src/AnnotationPlugin.js --- a/client/src/AnnotationPlugin.js Tue Oct 09 19:07:47 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -function AnnotationPlugin(options) { - - const { onChange } = options - - return { - onSelect(event, change) { - event.preventDefault() - - const { value } = change - const { selection } = value - const { start, end} = selection - - if (selection.isCollapsed) { - return; - } - - let nodes = []; - let hasStarted = false; - let hasEnded = false; - - // Keep only the relevant nodes, - // i.e. nodes which are contained within selection - value.document.nodes.forEach((node) => { - if (start.isInNode(node)) { - hasStarted = true; - } - if (hasStarted && !hasEnded) { - nodes.push(node); - } - if (end.isAtEndOfNode(node)) { - hasEnded = true; - } - }); - - let text = ''; - - // Concatenate the nodes text - if (nodes.length === 1) { - text = nodes[0].text.substring(start.offset, end.offset); - } else { - text = nodes.map((node) => { - if (start.isInNode(node)) return node.text.substring(start.offset); - if (end.isAtEndOfNode(node)) return node.text.substring(0, end.offset); - return node.text; - }).join('\n'); - } - - if (onChange) { - onChange(text, start.offset, end.offset); - } - } - - }; -} - -export default AnnotationPlugin; diff -r 1f340f3597a8 -r ea92f4fe783d client/src/HtmlSerializer.js --- a/client/src/HtmlSerializer.js Tue Oct 09 19:07:47 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -import React from 'react' -import Html from 'slate-html-serializer' - -const BLOCK_TAGS = { - p: 'paragraph', - ul: 'bulleted-list', - ol: 'numbered-list', - li: 'list-item', -} - -// Add a dictionary of mark tags. -const MARK_TAGS = { - em: 'italic', - strong: 'bold', - u: 'underlined', - category: 'span' -} - -const rules = [ - // Block rules - { - deserialize(el, next) { - const type = BLOCK_TAGS[el.tagName] - if (!type) return - return { - object: 'block', - type: type, - nodes: next(el.childNodes) - } - }, - serialize(obj, children) { - if (obj.object !== 'block') return - switch (obj.type) { - case 'numbered-list': - return
    {children}
; - case 'bulleted-list': - return ; - case 'list-item': - return
  • {children}
  • ; - case 'paragraph': - case 'line': - return

    {children}

    - default: return; - } - } - }, - // Mark rules - { - deserialize(el, next) { - const type = MARK_TAGS[el.tagName] - if (!type) return - return { - object: 'mark', - type: type, - nodes: next(el.childNodes) - } - }, - serialize(obj, children) { - if (obj.object !== 'mark') return - switch (obj.type) { - case 'bold': - return {children} - case 'italic': - return {children} - case 'underlined': - return {children} - case 'category': - return {children} - default: return; - } - } - } -] - -const serializer = new Html({ rules }) - -export default serializer diff -r 1f340f3597a8 -r ea92f4fe783d client/src/__tests__/App.test.js --- a/client/src/__tests__/App.test.js Tue Oct 09 19:07:47 2018 +0200 +++ b/client/src/__tests__/App.test.js Mon Oct 08 18:35:47 2018 +0200 @@ -1,6 +1,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import {Provider} from 'react-redux'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router'; +import { createMemoryHistory } from 'history' import App from '../App'; import configureStore from '../store/configureStore'; @@ -13,13 +15,16 @@ }; }); -const store = configureStore(); +const history = createMemoryHistory(); +const { store } = configureStore(history); it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render( - + + + , div); }); diff -r 1f340f3597a8 -r ea92f4fe783d client/src/actions/__tests__/notesActions.test.js --- a/client/src/actions/__tests__/notesActions.test.js Tue Oct 09 19:07:47 2018 +0200 +++ b/client/src/actions/__tests__/notesActions.test.js Mon Oct 08 18:35:47 2018 +0200 @@ -6,18 +6,46 @@ describe('actions', () => { it('should create an action to add a note', () => { - const text = 'test note' - const session = { id: 'abcd123' } + const text = 'hello world'; + const dateNote = Date.now(); + const data = { + raw: { + document: { + nodes: [ + { + object: 'block', + type: 'paragraph', + nodes: [ + { + object: 'text', + leaves: [ + { + text: text, + }, + ], + }, + ], + }, + ], + }, + }, + plain: text, + html: text, + startedAt: dateNote, + finishedAt: dateNote + 10000, + categories: [], + marginComment: 'empty', + } + const session = { _id: 'abcd123' } const expectedAction = { type: types.ADD_NOTE, note: { - id: expect.stringMatching(/[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}/), - session: session.id, - text: text + _id: expect.stringMatching(/[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}/), + session: session._id } } - const receivedAction = actions.addNote(session, text); + const receivedAction = actions.addNote(session, data); expect(receivedAction).toMatchObject(expectedAction); }) diff -r 1f340f3597a8 -r ea92f4fe783d client/src/actions/sessionsActions.js --- a/client/src/actions/sessionsActions.js Tue Oct 09 19:07:47 2018 +0200 +++ b/client/src/actions/sessionsActions.js Mon Oct 08 18:35:47 2018 +0200 @@ -22,18 +22,18 @@ }; } -export const updateSession = (session, values) => { +export const updateSession = (sessionId, values) => { return { type: types.UPDATE_SESSION, - session: session, + sessionId: sessionId, values: values, }; } -export const deleteSession = (session) => { +export const deleteSession = (sessionId) => { return { type: types.DELETE_SESSION, - session: session, + sessionId: sessionId, }; } @@ -56,5 +56,5 @@ } export const resetActionSession = (session) => { - return { type: types.RESET_ACTION_SESSION, session }; + return { type: types.RESET_ACTION_SESSION, sessionId: session._id }; } diff -r 1f340f3597a8 -r ea92f4fe783d client/src/api/WebAnnotationSerializer.js --- a/client/src/api/WebAnnotationSerializer.js Tue Oct 09 19:07:47 2018 +0200 +++ b/client/src/api/WebAnnotationSerializer.js Mon Oct 08 18:35:47 2018 +0200 @@ -1,15 +1,17 @@ +import * as R from 'ramda'; + class WebAnnotationSerializer { static serialize = (note) => { - const categories = note.get('categories'); + const categories = R.prop('categories', note); const baseAnnotation = { '@context': "http://www.w3.org/ns/anno.jsonld", "type": "Annotation", } - const source = "/session/" + note.get('session') + "/notes/" + note.get('_id'); + const source = "/session/" + R.prop('session', note) + "/notes/" + R.prop('_id', note); return categories.map((category, index) => { diff -r 1f340f3597a8 -r ea92f4fe783d client/src/components/CategoriesTooltip.js --- a/client/src/components/CategoriesTooltip.js Tue Oct 09 19:07:47 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -import React, { Component } from 'react'; - -class CategoriesTooltip extends Component { - - state = { - activeCategory: null, - showCommentControl: false - } - - componentDidUpdate = () => { - if (this.state.showCommentControl) { - this.commentControl.focus(); - } - } - - isCategoryActive = (category) => { - return this.state.activeCategory && this.state.activeCategory.key === category.key - } - - onButtonClick = (category) => { - if (category.hasOwnProperty('hasComment') && category.hasComment === true) { - this.setState({ - activeCategory: this.state.activeCategory ? null : category, - showCommentControl: !this.state.showCommentControl - }) - } else { - if (typeof this.props.onCategoryClick === 'function') { - this.props.onCategoryClick(category) - } - } - } - - onSubmit = (e) => { - e.preventDefault(); - if (this.state.showCommentControl) { - const comment = this.commentControl.value; - const { activeCategory } = this.state; - Object.assign(activeCategory, { comment: comment }); - if (typeof this.props.onCategoryClick === 'function') { - this.props.onCategoryClick(activeCategory) - } - } - } - - render() { - return ( -
    -
    -
    - {this.props.categories.map((category) => - - )} -
    - {this.state.showCommentControl && -
    - { this.commentControl = ref; }} - ref={(commentControl) => { this.commentControl = commentControl; }}/> -
    } -
    -
    - ); - } -} - -export default CategoriesTooltip diff -r 1f340f3597a8 -r ea92f4fe783d client/src/components/CreateGroup.js --- a/client/src/components/CreateGroup.js Tue Oct 09 19:07:47 2018 +0200 +++ b/client/src/components/CreateGroup.js Mon Oct 08 18:35:47 2018 +0200 @@ -80,7 +80,7 @@
    Nouveau groupe
    -
    +
    diff -r 1f340f3597a8 -r ea92f4fe783d client/src/components/CreateSession.js --- a/client/src/components/CreateSession.js Tue Oct 09 19:07:47 2018 +0200 +++ b/client/src/components/CreateSession.js Mon Oct 08 18:35:47 2018 +0200 @@ -9,7 +9,7 @@ static propTypes = { history: PropTypes.object.isRequired, - group: PropTypes.object.isRequired, + group: PropTypes.object, createSession: PropTypes.func.isRequired, }; @@ -31,8 +31,8 @@ createSession = () => { const sessionId = uuidV1(); - const groupName = this.props.group ? this.props.group.get('name') : null; - const protocol = this.props.group ? this.props.group.get('protocol') : null; + const groupName = this.props.group ? this.props.group.name : null; + const protocol = this.props.group ? this.props.group.protocol : null; const {title, description} = this.state; this.props.createSession(sessionId, groupName, protocol, title, description); diff -r 1f340f3597a8 -r ea92f4fe783d client/src/components/Login.js --- a/client/src/components/Login.js Tue Oct 09 19:07:47 2018 +0200 +++ b/client/src/components/Login.js Mon Oct 08 18:35:47 2018 +0200 @@ -50,7 +50,7 @@ renderNonFieldErrors(errorMessages) { if (errorMessages && errorMessages.has('non_field_errors')) { - const errors = errorMessages.get('non_field_errors'); + const errors = errorMessages.non_field_errors; return (