From 03d59290e1977ff0b0b1e19de248f63be62727ce Mon Sep 17 00:00:00 2001 From: "sabri.ben_miled" Date: Fri, 21 Jun 2019 19:13:16 +0200 Subject: [PATCH 1/2] FEATURE: Add cloud and fragment view (see #48 and #80). Co-authored-by: Arnaud Dufour Co-authored-by: Guillaume Gilles <48546298+GuillaumeGilles42@users.noreply.github.com> --- .gitignore | 1 + package.json | 2 + public/favicon.ico | Bin 24838 -> 45614 bytes .../Authenticated/Authenticated.jsx | 21 +- src/components/Cloud/Cloud.jsx | 92 ++++ src/components/Corpora/Corpora.jsx | 146 ++++-- src/components/Header/Header.jsx | 2 +- src/components/Item/Fragment.jsx | 158 ++++++ src/components/Item/Item.jsx | 220 +++++--- src/components/Outliner/Outliner.jsx | 469 ++++++++++-------- src/components/Portfolio/Portfolio.jsx | 160 ++++-- src/components/Viewpoint/Viewpoint.jsx | 42 +- src/config/config.js | 2 +- src/config/config.json | 4 +- src/images/lien_logo.svg | 6 + src/styles/App.css | 121 +++-- 16 files changed, 1012 insertions(+), 434 deletions(-) create mode 100644 src/components/Cloud/Cloud.jsx create mode 100644 src/components/Item/Fragment.jsx create mode 100644 src/images/lien_logo.svg diff --git a/.gitignore b/.gitignore index 79222102..38c79470 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules package-lock.json Gemfile.lock +.idea # production build diff --git a/package.json b/package.json index 2d515f3f..a4ff91db 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "react-autosuggest": "^9.3.4", "react-dom": "^16.4.0", "react-router-dom": "^4.1.1", + "react-switch": "^5.0.0", + "react-tagcloud": "^1.4.0", "sort-by": "^1.2.0" }, "devDependencies": { diff --git a/public/favicon.ico b/public/favicon.ico index 5c125de5d897c1ff5692a656485b3216123dcd89..94fd08f0624567ad5c8414e67be32ffd8a0f2864 100644 GIT binary patch literal 45614 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 literal 24838 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 { + e.preventDefault() + this.setState({ ask: true }) } - handleLogin(e) { + handleLogin = e => { e.preventDefault(); this._openSession(); this.setState({ask: false}); } - handleLogout(e) { + handleLogout = e => { e.preventDefault(); this._closeSession(); } @@ -66,16 +63,16 @@ class Authenticated extends Component { _openSession() { let user = this.login.value; fetch(SESSION_URI, { - method:'POST', + method: 'POST', headers: { "Content-Type": "application/x-www-form-urlencoded", }, - body:`name=${user}&password=${this.password.value}`, - credentials:'include' + body: `name=${user}&password=${this.password.value}`, + credentials: 'include' }) .then(x => { if (!x.ok) throw new Error('Bad credentials!'); - this.setState({user}) + this.setState({user}); }) .catch(() => this.setState({user: ''})); } diff --git a/src/components/Cloud/Cloud.jsx b/src/components/Cloud/Cloud.jsx new file mode 100644 index 00000000..63ac85b8 --- /dev/null +++ b/src/components/Cloud/Cloud.jsx @@ -0,0 +1,92 @@ +import React, { Component } from 'react'; +import { Link } from 'react-router-dom'; +import queryString from 'query-string'; +import { TagCloud } from 'react-tagcloud'; + +class Cloud extends Component { + shouldComponentUpdate(nextProps) { + if (this.props.selection.length !== nextProps.selection.length) return true; + if ( + Object.keys(this.props.viewpoint).length !== + Object.keys(nextProps.viewpoint).length + ) + return true; + return false; + } + + render() { + const { selection, viewpoint, topicsItems } = this.props; + let alltopics = []; + this.pushChildInTab(viewpoint.upper || [], alltopics); + alltopics = alltopics + .filter(topic => topic.name) + .map(topic => { + let items = topicsItems.get(topic.id); + let found = selection.find(s => s === topic.id); + + let uri = + '?' + + queryString.stringify({ + t: this.toggle(selection, topic.id) + }); + return { + value: topic.name[0], + count: items ? items.size : 0, + color: found ? 'selected' : '', + uri + }; + }) + .sort((a, b) => { + if (a.value > b.value) return 1; + if (a.value < b.value) return -1; + return 0; + }); + return ( + { + return ( + + + {tag.value} + + + ); + }} + /> + ); + } + + pushChildInTab = (topics, tab) => { + topics.forEach(t => { + let topic = this.props.viewpoint[t.id]; + if (topic.narrower) { + this.pushChildInTab(topic.narrower, tab); + } + topic.id = t.id; + tab.push(topic); + }); + }; + toggle = (array, item) => { + let s = new Set(array); + if (!s.delete(item)) { + s.add(item); + } + return [...s]; + }; +} + +export default Cloud; diff --git a/src/components/Corpora/Corpora.jsx b/src/components/Corpora/Corpora.jsx index ef7f91c3..08b502c8 100644 --- a/src/components/Corpora/Corpora.jsx +++ b/src/components/Corpora/Corpora.jsx @@ -1,8 +1,9 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; import getConfig from '../../config/config.js'; +import Fragment from '../Item/Fragment'; +import Switch from 'react-switch'; -// Get the configured list display mode let listView = getConfig('listView', { mode: 'picture', name: 'name', @@ -10,43 +11,118 @@ let listView = getConfig('listView', { }); class Corpora extends Component { + constructor(props) { + super(props); + this.state = { + view: 'item' + }; + this.changeViewPossible = true; + } + + componentDidMount(prevProps) { + if (prevProps !== this.props) { + if (!this.props.pictures || this.props.pictures.length === 0) { + this.changeViewPossible = false; + this.setState({ view: 'fragment' }); + } else if (!this.props.fragments || this.props.fragments.length === 0) { + this.changeViewPossible = false; + this.setState({ view: 'item' }); + } else { + this.changeViewPossible = true; + this.setState({ view: 'item' }); + } + } + } + + componentDidUpdate(prevProps, prevState, snapshot) { + if (prevProps !== this.props) { + if (!this.props.pictures || this.props.pictures.length === 0) { + this.changeViewPossible = false; + this.setState({ view: 'fragment' }); + } else if (!this.props.fragments || this.props.fragments.length === 0) { + this.changeViewPossible = false; + this.setState({ view: 'item' }); + } else { + this.changeViewPossible = true; + this.setState({ view: 'item' }); + } + } + } render() { - let items = this._getItems(); - let count = this.props.items.length; - let total = this.props.from; - return( + const { fragments } = this.props; + return (
+ {this._choiceView()}

{this.props.ids.join(' + ')} - {count} / {total} + + {fragments ? fragments.length : 0} / {this.props.from} +

-
- {items} -
+
{this._getView()}
); } + _getView() { + switch (this.state.view) { + case 'item': + return this._getItems(); + case 'fragment': + return ( + + ); + default: + return

arrête de jouer avec le code

; + } + } + _getItems() { - return this.props.items.map(item => - - ); + return this.props.pictures.map(item => ( + + )); } -} + _changeview() { + if (this.state.view === 'item') { + this.setState({ view: 'fragment' }); + } + if (this.state.view === 'fragment') { + this.setState({ view: 'item' }); + } + } -function Item(props) { - switch (listView.mode) { - case 'article': - return Article(props.item); - case 'picture': - return Picture(props.item); - default: - return Picture(props.item); + _choiceView() { + if (this.changeViewPossible) { + let changeview = this._changeview.bind(this); + return ( +

+ +

+ ); + } } } @@ -57,29 +133,15 @@ function getString(obj) { return String(obj); } -function Article(item) { - let propList = (listView.props || []).map(key => { - return
  • {key} : {getString(item[key])}
  • ; - }); - - let uri = `/item/${item.corpus}/${item.id}`; - let name = getString(item[listView.name]); - return ( -
    -
    {name}
    -
      {propList}
    -
    - ); -} - -function Picture(item) { - let uri = `/item/${item.corpus}/${item.id}`; - let img = getString(item[listView.image]); - let name = getString(item[listView.name]); +function Picture(items) { + items = items.item; + let uri = `/item/${items.corpus}/${items.id}`; + let img = getString(items[listView.image]); + let name = getString(items[listView.name]); return (
    - {name}/ + {name}
    {name}
    diff --git a/src/components/Header/Header.jsx b/src/components/Header/Header.jsx index 8c553efb..07e151db 100644 --- a/src/components/Header/Header.jsx +++ b/src/components/Header/Header.jsx @@ -11,7 +11,7 @@ class Header extends Component { render() { return (
    -
    +
    diff --git a/src/components/Item/Fragment.jsx b/src/components/Item/Fragment.jsx new file mode 100644 index 00000000..eae2e325 --- /dev/null +++ b/src/components/Item/Fragment.jsx @@ -0,0 +1,158 @@ +import React, { Component } from 'react'; +import lien from '../../images/lien_logo.svg'; + +export default class Fragment extends Component { + constructor(props) { + super(props); + this.state = { + idTextToAnalyse: null + }; + } + + static getDerivedStateFromProps(nextProps, nextState) { + if (nextState.idTextToAnalyse) { + let found = nextProps.items.find( + text => text.id === nextState.idTextToAnalyse + ); + if (!found) return { idTextToAnalyse: null }; + } + return false; + } + + render() { + const generatedTextDescription = this._generateTextDescription(); + const generatedTextFragment = this._generateTextFragment(); + return ( +
    + + + + + + + + + + + + + +
    + Items + Fragments d'un item
    + + {generatedTextDescription} +
    +
    + + {generatedTextFragment} +
    +
    +
    + ); + } + + _generateTextDescription() { + const selectId = this.selectIdTextToAnalyse; + const idTextToAnalyse = this.state.idTextToAnalyse; + return this.props.items.map((text, id) => { + let { resource } = text; + resource = resource ? ( + + lien le texte d'origine + + ) : null; + const idIdentique = idTextToAnalyse === text.id; + const class_name = idIdentique ? 'item textSelected' : 'item'; + return ( + selectId(text.id)}> + +

    + name : {text.name} {resource} +

    +

    +

    + + + ); + }); + } + + selectIdTextToAnalyse = id => { + window.scrollTo(0, 0); + if (id === this.state.idTextToAnalyse) { + this.setState({ idTextToAnalyse: null }); + } else { + this.setState({ idTextToAnalyse: id }); + } + }; + + _generateTextFragment() { + if (this.state.idTextToAnalyse === null) { + let fragmentSelect = this.props.items.map(fragment => { + return Object.values(fragment); + }); + if (this.props.selection.length !== 0) { + fragmentSelect = fragmentSelect.map(fragments => { + return fragments.filter(fragment => { + return this.props.selection.every(selection => { + return (fragment.topic || []).find(t => { + return t.id === selection; + }); + }); + }); + }); + } + return fragmentSelect.map(fragments => { + return fragments.map((fragment, idFragment) => { + return ( + + + {fragment.text + ? fragment.text.map((text, idText) => ( +

    + {text} +

    + )) + : null} + + + ); + }); + }); + } else { + let fragments = this.props.items.find( + text => text.id === this.state.idTextToAnalyse + ); + let fragmentSelect = Object.values(fragments); + if (this.props.selection.length !== 0) { + fragmentSelect = fragmentSelect.filter(fragments => { + return this.props.selection.every(selection => { + return (fragments.topic || []).find(t => { + return t.id === selection; + }); + }); + }); + } + return fragmentSelect.map((fragment, idFragment) => { + return ( + + + {fragment.text + ? fragment.text.map((text, idText) => ( +

    + {text} +

    + )) + : null} + + + ); + }); + } + } +} diff --git a/src/components/Item/Item.jsx b/src/components/Item/Item.jsx index 35afe1b6..dd7a8d5f 100644 --- a/src/components/Item/Item.jsx +++ b/src/components/Item/Item.jsx @@ -42,22 +42,27 @@ class Item extends Component { this._getOrCreateItem = this._getOrCreateItem.bind(this); this._switchCreatable = this._switchCreatable.bind(this); this.deleteAttribute = this.deleteAttribute.bind(this); - this.user=conf.user || window.location.hostname.split('.', 1)[0]; + this.user = conf.user || window.location.hostname.split('.', 1)[0]; } render() { let name = getString(this.state[itemView.name]); let attributes = this._getAttributes(); let viewpoints = this._getViewpoints(); - let attributeButtonLabel = this.state.isCreatable? 'Valider' : 'Ajouter un attribut'; - let attributeForm = this.state.isCreatable? this._getAttributeCreationForm() : ''; + let attributeButtonLabel = this.state.isCreatable + ? 'Valider' + : 'Ajouter un attribut'; + let attributeForm = this.state.isCreatable + ? this._getAttributeCreationForm() + : ''; return (
    - + - Retour à l'accueil + + Retour à l'accueil
    @@ -67,9 +72,14 @@ class Item extends Component {

    Description

    Attributs du document

    -
    +
    - +
    {attributes} @@ -98,7 +108,10 @@ class Item extends Component {
    {x[0]}
    {x[1][0]}
    -
    @@ -106,28 +119,30 @@ class Item extends Component { } _getViewpoints() { - return Object.entries(this.state.topic).map(v => - - ); + return Object.entries(this.state.topic).map(v => ( + + )); } componentDidMount() { - let start=new Date().getTime(); - let self=this; + let start = new Date().getTime(); + let self = this; this._fetchItem().then(() => { - let end=new Date().getTime(); - let elapsedTime=end-start; - console.log("elapsed Time ",elapsedTime); - - let intervalTime=Math.max(10000,elapsedTime*5); - console.log("reload every ",intervalTime); - self._timer = setInterval( - () => { - self._fetchItem(); - }, - intervalTime - ); + let end = new Date().getTime(); + let elapsedTime = end - start; + console.log('elapsed Time ', elapsedTime); + + let intervalTime = Math.max(10000, elapsedTime * 5); + console.log('reload every ', intervalTime); + self._timer = setInterval(() => { + self._fetchItem(); + }, intervalTime); }); } @@ -136,8 +151,7 @@ class Item extends Component { } _getOrCreateItem() { - return hypertopic.get({_id: this.props.match.params.item}) - .catch(e => { + return hypertopic.get({ _id: this.props.match.params.item }).catch(e => { return { _id: this.props.match.params.item, item_corpus: this.props.match.params.corpus @@ -148,45 +162,63 @@ class Item extends Component { _fetchItem() { let uri = this.props.match.url; let params = this.props.match.params; - return hypertopic.getView(uri).then((data) => { - let item = data[params.corpus][params.item]; - let itemTopics = (item.topic) ? groupBy(item.topic, ['viewpoint']) : {}; - let topics=this.state.topic || {}; - for (let id in itemTopics) { - topics[id]=itemTopics[id]; - } - item.topic=topics; - this.setState(item); - }).then(() => hypertopic.getView(`/user/${this.user}`)) - .then((data) => { - let user = data[this.user] || {}; - if (user.viewpoint) { - let topic=this.state.topic; - for (let vp of user.viewpoint) { - topic[vp.id]=topic[vp.id] || []; + return hypertopic + .getView(uri) + .then(data => { + let item = data[params.corpus][params.item]; + let itemTopics = item.topic ? groupBy(item.topic, ['viewpoint']) : {}; + let topics = this.state.topic || {}; + for (let id in itemTopics) { + topics[id] = itemTopics[id]; } - this.setState({topic}); - } - }); + item.topic = topics; + this.setState(item); + }) + .then(() => hypertopic.getView(`/user/${this.user}`)) + .then(data => { + let user = data[this.user] || {}; + if (user.viewpoint) { + let topic = this.state.topic; + for (let vp of user.viewpoint) { + topic[vp.id] = topic[vp.id] || []; + } + this.setState({ topic }); + } + }); } _getAttributeCreationForm() { return (
    -
    -
    +
    + +
    +
    + +
    ); } _setAttribute(key, value) { - if (key!=='' && value!=='') { - let attribute = {[key]: [value]}; + if (key !== '' && value !== '') { + let attribute = { [key]: [value] }; this._getOrCreateItem() .then(x => Object.assign(x, attribute)) .then(hypertopic.post) .then(_ => this.setState(attribute)) - .catch((x) => console.error(x.message)); + .catch(x => console.error(x.message)); } else { console.log('Créez un attribut non vide'); } @@ -204,7 +236,7 @@ class Item extends Component { } deleteAttribute(key) { - const _error = (x) => console.error(x.message); + const _error = x => console.error(x.message); this._getOrCreateItem() .then(x => { delete x[key]; @@ -221,7 +253,7 @@ class Item extends Component { _assignTopic(topicToAssign, viewpointId) { return this._getOrCreateItem() .then(data => { - data.topics=data.topics || {}; + data.topics = data.topics || {}; data.topics[topicToAssign.id] = { viewpoint: viewpointId }; return data; }) @@ -237,17 +269,20 @@ class Item extends Component { .catch(error => console.log(`error : ${error}`)); } - _removeTopic(topicToDelete) { - if (window.confirm('Voulez-vous réellement que l\'item affiché ne soit plus décrit à l\'aide de cette rubrique ?')) { + if ( + window.confirm( + "Voulez-vous réellement que l'item affiché ne soit plus décrit à l'aide de cette rubrique ?" + ) + ) { return this._getOrCreateItem() .then(data => { - data.topics=data.topics || {}; + data.topics = data.topics || {}; delete data.topics[topicToDelete.id]; return data; }) .then(hypertopic.post) - .then((res)=> { + .then(res => { let newState = this.state; newState.topic[topicToDelete.viewpoint] = newState.topic[ topicToDelete.viewpoint @@ -261,12 +296,12 @@ class Item extends Component { function ShowItem(props) { switch (itemView.mode) { - case 'article': - return Article(props.item); - case 'picture': - return Picture(props.item); - default: - return Picture(props.item); + case 'article': + return Article(props.item); + case 'picture': + return Picture(props.item); + default: + return Picture(props.item); } } @@ -276,7 +311,9 @@ function Article(item) { return ( ); } @@ -288,7 +325,7 @@ function Picture(item) { return ( ); @@ -431,7 +468,7 @@ class Viewpoint extends Component { return (

    {this.state.name}

    -
    +
    {paths}
    @@ -449,18 +486,26 @@ class Viewpoint extends Component { id={`input-${this.state.name}`} />
    - -
    @@ -488,14 +533,14 @@ class Viewpoint extends Component { _fetchViewpoint() { let uri = '/viewpoint/' + this.props.id; - hypertopic.getView(uri).then((data) => { + hypertopic.getView(uri).then(data => { let viewpoint = data[this.props.id]; let name = viewpoint.name; let topics = viewpoint; delete topics.user; delete topics.name; delete topics.upper; - this.setState({name, topics}); + this.setState({ name, topics }); }); } } @@ -511,8 +556,14 @@ class TopicPath extends Component { render() { let topics = this._getTopics(); for (let i = 1; i < topics.length; ++i) { - let key="separator-"+i; - topics.splice(i, 0, >); + let key = 'separator-' + i; + topics.splice( + i, + 0, + + > + + ); ++i; } const topicId = this.state.path[this.state.path.length - 1] @@ -521,8 +572,12 @@ class TopicPath extends Component { return (
    {topics} -
    @@ -536,7 +591,7 @@ class TopicPath extends Component { topic = this._getTopic(topic.broader[0].id); path.unshift(topic); } - this.setState({path}); + this.setState({ path }); } _getTopic(id) { @@ -546,15 +601,16 @@ class TopicPath extends Component { } _getTopics() { - return this.state.path.map( t => { - let name = (t.name)? t.name : 'Sans nom'; + return this.state.path.map(t => { + let name = t.name ? t.name : 'Sans nom'; let uri = '../../?t=' + t.id; return ( - {name} + + {name} + ); }); } - } export default Item; diff --git a/src/components/Outliner/Outliner.jsx b/src/components/Outliner/Outliner.jsx index af646c0e..fb69c462 100644 --- a/src/components/Outliner/Outliner.jsx +++ b/src/components/Outliner/Outliner.jsx @@ -10,29 +10,29 @@ import '../../styles/App.css'; const db = new Hypertopic(conf.services); -const _log = (x) => console.log(JSON.stringify(x, null, 2)); -const _error = (x) => console.error(x.message); +const _log = x => console.log(JSON.stringify(x, null, 2)); +const _error = x => console.error(x.message); class Outliner extends React.Component { - constructor() { super(); - this.state = { }; - this.changing=false; - this.topicTree=new TopicTree({}); + this.state = {}; + this.changing = false; + this.topicTree = new TopicTree({}); this.user = conf.user || window.location.hostname.split('.', 1)[0]; } render() { let status = this._getStatus(); - let topic={name:this.state.title}; + let topic = { name: this.state.title }; return (
    - + - Retour à l'accueil + + Retour à l'accueil
    @@ -41,13 +41,21 @@ class Outliner extends React.Component {

    {status}

    - {this.state.title ? + {this.state.title ? (
      - +
    - : this._getTitle() - } + ) : ( + this._getTitle() + )}
    @@ -58,19 +66,28 @@ class Outliner extends React.Component { } _getTitle() { - return (
    this._newVP(e)}> - -
    - -
    -
    ); + return ( +
    this._newVP(e)}> + +
    + +
    +
    + ); } _getStatus() { if (this.state.title !== undefined) { - return "Modification du point de vue"; + return 'Modification du point de vue'; } else { - return "Création du point de vue"; + return 'Création du point de vue'; } } @@ -80,7 +97,12 @@ class Outliner extends React.Component { if (!title) { return; } - db.post({ _id: this.props.match.params.id, viewpoint_name: title, topics: {}, users: [this.user] }) + db.post({ + _id: this.props.match.params.id, + viewpoint_name: title, + topics: {}, + users: [this.user] + }) .then(_log) .then(_ => this.setState({ title })) .then(_ => this._fetchData()) @@ -88,41 +110,43 @@ class Outliner extends React.Component { } activeNode(id) { - this.setState({activeNode:id}); + this.setState({ activeNode: id }); } handleKeyAction(e) { - var changed=false; - var isNew=false; + var changed = false; + var isNew = false; if (this.state.activeNode) { - var topic=this.topicTree.getTopic(this.state.activeNode); - isNew=topic.new || false; + var topic = this.topicTree.getTopic(this.state.activeNode); + isNew = topic.new || false; } switch (e.key) { - case "Enter": - topic=this.topicTree.newSibling(this.state.activeNode); + case 'Enter': + topic = this.topicTree.newSibling(this.state.activeNode); this.activeNode(topic.id); - topic.new=true; + topic.new = true; e.preventDefault(); this.setState(function(previousState) { // previousState.temptopics=previousState.temptopics || []; // previousState.temptopics.push(topic.id); - previousState.topics=this.topicTree.topics; + previousState.topics = this.topicTree.topics; return previousState; }); return; - case "Tab": + case 'Tab': if (!e.altKey && !e.ctrlKey) { if (e.shiftKey) { - changed=this.topicTree.promote(this.state.activeNode); + changed = this.topicTree.promote(this.state.activeNode); } else { - changed=this.topicTree.demote(this.state.activeNode); + changed = this.topicTree.demote(this.state.activeNode); } } break; case 'ArrowUp': if (!e.altKey && !e.ctrlKey && !e.shiftKey) { - this.activeNode(this.topicTree.getPreviousTopic(this.state.activeNode)); + this.activeNode( + this.topicTree.getPreviousTopic(this.state.activeNode) + ); } break; case 'ArrowDown': @@ -133,10 +157,13 @@ class Outliner extends React.Component { case 'Delete': case 'Backspace': if (!e.altKey && !e.ctrlKey && !e.shiftKey) { - if (e.target.tagName==="BODY" || e.target.value==='' ) { - let previousTopic=this.topicTree.getPreviousTopic(this.state.activeNode); - changed=this.topicTree.deleteTopic(this.state.activeNode); - if (changed) this.activeNode(this.topicTree.getNextTopic(previousTopic)); + if (e.target.tagName === 'BODY' || e.target.value === '') { + let previousTopic = this.topicTree.getPreviousTopic( + this.state.activeNode + ); + changed = this.topicTree.deleteTopic(this.state.activeNode); + if (changed) + this.activeNode(this.topicTree.getNextTopic(previousTopic)); } } break; @@ -144,80 +171,86 @@ class Outliner extends React.Component { } if (changed) { e.preventDefault(); - this.setState({topics:this.topicTree.topics},() => { + this.setState({ topics: this.topicTree.topics }, () => { if (!isNew) this.applyChange(); }); } return; - }; + } - editTopic(id,change) { + editTopic(id, change) { if (!this.setState) { return; } - var toApply=false; - return this.setState(previousState => { - let topics=previousState.topics; - if (topics) { - let topic; - if (id==="root") { - if (change.name && change.name!==previousState.title) { - toApply=true; - this.topicTree.setRootName(change.name); - return {title:change.name} - } - } else if (topics[id]) { - if (change.delete) { - if (!topics[id].new) toApply=true; - delete topics[id]; - } else { - topic=topics[id]; + var toApply = false; + return this.setState( + previousState => { + let topics = previousState.topics; + if (topics) { + let topic; + if (id === 'root') { + if (change.name && change.name !== previousState.title) { + toApply = true; + this.topicTree.setRootName(change.name); + return { title: change.name }; + } + } else if (topics[id]) { + if (change.delete) { + if (!topics[id].new) toApply = true; + delete topics[id]; + } else { + topic = topics[id]; + } } - } - if (topic) { - for (let key in change) { - switch(key) { - case "parent": - if (this.topicTree.getTopic(change.parent)) { - toApply=this.topicTree.setParent(id,change.parent) && + if (topic) { + for (let key in change) { + switch (key) { + case 'parent': + if (this.topicTree.getTopic(change.parent)) { + toApply = + this.topicTree.setParent(id, change.parent) && this.topicTree.moveAfter(id); - } - break; - case "moveAfter": - if (this.topicTree.getTopic(change.moveAfter)) { - var newParent=this.topicTree.getParent(change.moveAfter); - if (newParent) { - toApply=this.topicTree.setParent(id,newParent); } - toApply=toApply && this.topicTree.moveAfter(id,change.moveAfter); - } - break; - case "startDrag": - if (change.startDrag===true) previousState.draggedTopic=id; - else if (previousState.draggedTopic===id) { - delete previousState.draggedTopic; - } - break; - default: - if (topic[key]!==change[key]) { - topic[key]=change[key]; - if (!topics[id].new) toApply=true; - } + break; + case 'moveAfter': + if (this.topicTree.getTopic(change.moveAfter)) { + var newParent = this.topicTree.getParent(change.moveAfter); + if (newParent) { + toApply = this.topicTree.setParent(id, newParent); + } + toApply = + toApply && this.topicTree.moveAfter(id, change.moveAfter); + } + break; + case 'startDrag': + if (change.startDrag === true) + previousState.draggedTopic = id; + else if (previousState.draggedTopic === id) { + delete previousState.draggedTopic; + } + break; + default: + if (topic[key] !== change[key]) { + topic[key] = change[key]; + if (!topics[id].new) toApply = true; + } + } } + if (!topics[id].new) delete topics[id].new; } - if (!topics[id].new) delete topics[id].new; + return previousState; } - return previousState; + return {}; + }, + function() { + if (toApply) this.applyChange(); } - return {}; - },function() { - if (toApply) this.applyChange(); - }); + ); } componentDidMount() { this._fetchData(); - document.addEventListener("keydown", this.handleKeyAction.bind(this)); + document.addEventListener('keydown', this.handleKeyAction.bind(this)); } componentWillUnmount() { @@ -226,12 +259,13 @@ class Outliner extends React.Component { applyChange() { if (!this.changing) { - this.changing=db.get({ _id: this.props.match.params.id }) + this.changing = db + .get({ _id: this.props.match.params.id }) .then(data => { - data.topics ={}; + data.topics = {}; for (var id in this.state.topics) { if (!this.state.topics[id].new) { - data.topics[id]=this.state.topics[id]; + data.topics[id] = this.state.topics[id]; } } data.viewpoint_name = this.state.title; @@ -239,11 +273,11 @@ class Outliner extends React.Component { }) .then(db.post) .then(() => { - this.changing=false; + this.changing = false; }) .catch(_ => { _error(_); - this.changing=false; + this.changing = false; this._fetchData(); }); } @@ -252,20 +286,17 @@ class Outliner extends React.Component { _fetchData() { if (!this.changing) { - return db.get({ _id: this.props.match.params.id }) - .then(x => { + return db.get({ _id: this.props.match.params.id }).then(x => { this.setState({ topics: x.topics, title: x.viewpoint_name }); - this.topicTree=new TopicTree(x.topics); + this.topicTree = new TopicTree(x.topics); }); } else { return true; } } - } class Node extends React.Component { - constructor() { super(); this.state = { edit: false, active: false, open: true }; @@ -273,173 +304,223 @@ class Node extends React.Component { } render = () => { - let change=this.props.change; + let change = this.props.change; let switchOpen = () => { - this.setState({open:!this.state.open}); - } - var isNew=this.props.me.new; - let switchEdit = (e) => { + this.setState({ open: !this.state.open }); + }; + var isNew = this.props.me.new; + let switchEdit = e => { e.stopPropagation(); - this.setState((previousState) => { + this.setState(previousState => { if (previousState.edit && isNew) { - change(this.props.id,{delete:true}); + change(this.props.id, { delete: true }); } - return {edit:!previousState.edit}; + return { edit: !previousState.edit }; }); - } - let commitEdit = (e) => { - let newName=e.target.value; - change(this.props.id,{name:newName,new:false}); - isNew=false; + }; + let commitEdit = e => { + let newName = e.target.value; + change(this.props.id, { name: newName, new: false }); + isNew = false; switchEdit(e); - } - let handleInput = (e) => { - switch(e.key) { - case "Enter": + }; + let handleInput = e => { + switch (e.key) { + case 'Enter': commitEdit(e); e.stopPropagation(); break; - case "Escape": + case 'Escape': switchEdit(e); e.stopPropagation(); break; default: } }; - let activeMe = (e) => { + let activeMe = e => { e.stopPropagation(); this.props.activate(this.props.id); - } + }; let thisNode; if (this.state.edit) { - thisNode=; + thisNode = ( + + ); } else { - thisNode={this.props.me.name}; + thisNode = ( + + {this.props.me.name} + + ); } - let children=[]; + let children = []; if (this.props.topics) { for (var topID in this.props.topics) { - let topic=this.props.topics[topID]; - if ((this.props.id && topic.broader.indexOf(this.props.id)!==-1) - || (this.props.id==="root" && topic.broader.length===0)) { - children.push( - - ); + let topic = this.props.topics[topID]; + if ( + (this.props.id && topic.broader.indexOf(this.props.id) !== -1) || + (this.props.id === 'root' && topic.broader.length === 0) + ) { + children.push( + + ); } } } - var classes=["outliner-node"]; - if (this.props.activeNode===this.props.id) { - classes.push("active"); + var classes = ['outliner-node']; + if (this.props.activeNode === this.props.id) { + classes.push('active'); } if (this.state.isDragged || this.state.isDraggedOver) { - classes.push("dragged"); + classes.push('dragged'); } if (this.props.draggedTopic && this.state.isDraggedInto) { - classes.push("dragged-into"); + classes.push('dragged-into'); } if (this.props.draggedTopic && this.state.isDraggedAfter) { - classes.push("dragged-after"); + classes.push('dragged-after'); } if (this.props.id && children.length) { - classes.push("has-children"); + classes.push('has-children'); } - if (this.state.open) classes.push("open"); - else classes.push("closed"); + if (this.state.open) classes.push('open'); + else classes.push('closed'); function setEdit(e) { if (!this.state.edit) switchEdit(e); } - var draggable=this.props.id!=="root"; + var draggable = this.props.id !== 'root'; function onDragStart(e) { e.stopPropagation(); - e.dataTransfer.effectAllowed="move"; - e.dataTransfer.setData("dragContent",this.props.id); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('dragContent', this.props.id); activeMe(e); - this.setState({isDragged:true}); - this.props.change(this.props.id,{startDrag:true}); + this.setState({ isDragged: true }); + this.props.change(this.props.id, { startDrag: true }); } function onDragStop(e) { e.stopPropagation(); - console.log("dragStop "+this.props.me.name); - this.setState({isDragged:false}); - this.props.change(this.props.id,{startDrag:false}); + console.log('dragStop ' + this.props.me.name); + this.setState({ isDragged: false }); + this.props.change(this.props.id, { startDrag: false }); } - let onDrag=(e) => { - var draggedTopic=e.dataTransfer.getData("dragContent") || this.props.draggedTopic; - if (!draggedTopic) {console.error("no dragged topic"); return;} - if (!this.props.topics[draggedTopic]) {console.error("unknown dragged topic "+draggedTopic); return;} - let topicTree=new TopicTree(this.props.topics); + let onDrag = e => { + var draggedTopic = + e.dataTransfer.getData('dragContent') || this.props.draggedTopic; + if (!draggedTopic) { + console.error('no dragged topic'); + return; + } + if (!this.props.topics[draggedTopic]) { + console.error('unknown dragged topic ' + draggedTopic); + return; + } + let topicTree = new TopicTree(this.props.topics); - if (draggedTopic===this.props.id || topicTree.isAncestor(draggedTopic,this.props.id)) { + if ( + draggedTopic === this.props.id || + topicTree.isAncestor(draggedTopic, this.props.id) + ) { //can't be dropped into itself or its descendant - return; - } else if (e.currentTarget.className==="wrap") { //child - this.setState({isDraggedInto:true}); + } else if (e.currentTarget.className === 'wrap') { + //child + this.setState({ isDraggedInto: true }); e.stopPropagation(); e.preventDefault(); return false; - } else if (e.currentTarget.className==="caret") { - this.setState({isDraggedAfter:true}); + } else if (e.currentTarget.className === 'caret') { + this.setState({ isDraggedAfter: true }); e.stopPropagation(); e.preventDefault(); return false; } else { - } - } - let onDragLeave=(e) => { - this.setState({isDraggedAfter:false,isDraggedInto:false}); + }; + let onDragLeave = e => { + this.setState({ isDraggedAfter: false, isDraggedInto: false }); e.preventDefault(); e.stopPropagation(); - } - let onDrop=(e) => { - this.setState({isDraggedAfter:false,isDraggedInto:false}); - let topicTree=new TopicTree(this.props.topics); - var droppedTopic=e.dataTransfer.getData("dragContent"); - if (droppedTopic===this.props.id || topicTree.isAncestor(droppedTopic,this.props.id)) { + }; + let onDrop = e => { + this.setState({ isDraggedAfter: false, isDraggedInto: false }); + let topicTree = new TopicTree(this.props.topics); + var droppedTopic = e.dataTransfer.getData('dragContent'); + if ( + droppedTopic === this.props.id || + topicTree.isAncestor(droppedTopic, this.props.id) + ) { //can't be dropped into itself or its descendant - return; - } else if (e.currentTarget.className==="wrap") { //child - this.props.change(droppedTopic,{parent:this.props.id}); + } else if (e.currentTarget.className === 'wrap') { + //child + this.props.change(droppedTopic, { parent: this.props.id }); e.stopPropagation(); e.preventDefault(); - } else if (e.currentTarget.className==="caret") { - this.props.change(droppedTopic,{moveAfter:this.props.id}); + } else if (e.currentTarget.className === 'caret') { + this.props.change(droppedTopic, { moveAfter: this.props.id }); e.stopPropagation(); e.preventDefault(); } - } + }; return ( -
  • + + {' '} + + - - {thisNode} {this.props.id}
      -
    • - {children} +
    • + {children}
    -
    -
  • ); +
    + + ); }; componentDidMount() { if (!this.props.me.name || this.props.me.isNew) { - this.setState({edit:true}); + this.setState({ edit: true }); } } - } export default Outliner; diff --git a/src/components/Portfolio/Portfolio.jsx b/src/components/Portfolio/Portfolio.jsx index 5bca632d..cec14732 100644 --- a/src/components/Portfolio/Portfolio.jsx +++ b/src/components/Portfolio/Portfolio.jsx @@ -3,6 +3,7 @@ import { Link } from 'react-router-dom'; import by from 'sort-by'; import queryString from 'query-string'; import Hypertopic from 'hypertopic'; +import Switch from 'react-switch'; import conf from '../../config/config.json'; import Viewpoint from '../Viewpoint/Viewpoint.jsx'; import Corpora from '../Corpora/Corpora.jsx'; @@ -20,7 +21,8 @@ class Portfolio extends Component { corpora: [], items: [], selectedItems: [], - topicsItems: new Map() + topicsItems: new Map(), + cloudView: false }; this.user = conf.user || window.location.hostname.split('.', 1)[0]; this._updateSelection(); @@ -34,14 +36,33 @@ class Portfolio extends Component {
    - + {status}
    -

    Points de vue

    +

    + { + this.setState({ cloudView: e }); + }} + checked={this.state.cloudView} + uncheckedIcon={false} + checkedIcon={false} + onColor="#aaa" + className={'switch'} + /> + Points de vue +

    {viewpoints} @@ -56,21 +77,18 @@ class Portfolio extends Component { } componentDidMount() { - let start=new Date().getTime(); - var self=this; + let start = new Date().getTime(); + var self = this; this._fetchAll().then(() => { - let end=new Date().getTime(); - let elapsedTime=end-start; - console.log("elapsed Time ",elapsedTime); - - let intervalTime=Math.max(10000,elapsedTime*5); - console.log("reload every ",intervalTime); - self._timer = setInterval( - () => { - self._fetchAll(); - }, - intervalTime - ); + let end = new Date().getTime(); + let elapsedTime = end - start; + console.log('elapsed Time ', elapsedTime); + + let intervalTime = Math.max(10000, elapsedTime * 5); + console.log('reload every ', intervalTime); + self._timer = setInterval(() => { + self._fetchAll(); + }, intervalTime); }); } @@ -96,14 +114,25 @@ class Portfolio extends Component { let topics = this.selection.map(t => { let topic = this._getTopic(t); if (!topic) { - return 'Thème inconnu'; + return 'Thème inconnu'; } - let uri = '?' + queryString.stringify({ - t: this._toggleTopic(this.selection, t) - }); - return - {topic.name} - ; + let uri = + '?' + + queryString.stringify({ + t: this._toggleTopic(this.selection, t) + }); + return ( + + {topic.name}{' '} + + {' '} + + + ); }); return topics.length ? topics : 'Tous les items'; } @@ -118,20 +147,30 @@ class Portfolio extends Component { _updateSelection() { let selection = queryString.parse(window.location.search).t; - this.selection = (selection instanceof Array)? selection - : (selection)? [selection] - : []; + this.selection = + selection instanceof Array ? selection : selection ? [selection] : []; } _getTopicPath(topicId) { let topic = this._getTopic(topicId); - let path = (topic && topic.broader)? this._getTopicPath(topic.broader[0].id) : []; + let path = + topic && topic.broader ? this._getTopicPath(topic.broader[0].id) : []; path.push(topicId); return path; } _getItemTopicsPaths(item) { - return (item.topic||[]).map(t => this._getTopicPath(t.id)); + if (!item.topic) { + let fragments = Object.values(item); + let paths = []; + fragments.forEach(fragment => { + (fragment.topic || []).forEach(t => { + this._getTopicPath(t.id).forEach(p => p !== '' && paths.push(p)); + }); + }); + return paths; + } + return (item.topic || []).map(t => this._getTopicPath(t.id)); } _getRecursiveItemTopics(item) { @@ -143,33 +182,35 @@ class Portfolio extends Component { } _updateSelectedItems() { - let selectedItems = this.state.items - .filter(e => this._isSelected(e, this.selection)); + let selectedItems = this.state.items.filter(e => this._isSelected(e)); let topicsItems = new Map(); for (let e of selectedItems) { for (let t of this._getRecursiveItemTopics(e)) { push(topicsItems, t, e.id); } } - this.setState({selectedItems, topicsItems}); + this.setState({ selectedItems, topicsItems }); } _fetchAll() { const hypertopic = new Hypertopic(conf.services); - return hypertopic.getView(`/user/${this.user}`) + return hypertopic + .getView(`/user/${this.user}`) .then(data => { let user = data[this.user] || {}; user = { viewpoint: user.viewpoint || [], corpus: user.corpus || [] }; - if (!this.state.viewpoints.length && !this.state.corpora.length) { //TODO compare old and new - this.setState({viewpoints:user.viewpoint, corpora:user.corpus}); + if (!this.state.viewpoints.length && !this.state.corpora.length) { + //TODO compare old and new + this.setState({ viewpoints: user.viewpoint, corpora: user.corpus }); } return user; }) .then(x => - x.viewpoint.map(y => `/viewpoint/${y.id}`) + x.viewpoint + .map(y => `/viewpoint/${y.id}`) .concat(x.corpus.map(y => `/corpus/${y.id}`)) ) .then(hypertopic.getView) @@ -180,18 +221,16 @@ class Portfolio extends Component { viewpoint.id = v.id; viewpoints.push(viewpoint); } - this.setState({viewpoints}); + this.setState({ viewpoints }); return data; }) .then(data => { let items = []; for (let corpus of this.state.corpora) { for (let itemId in data[corpus.id]) { - if (!['id','name','user'].includes(itemId)) { + if (!['id', 'name', 'user'].includes(itemId)) { let item = data[corpus.id][itemId]; - if (!item.name || !item.name.length || !item.thumbnail || !item.thumbnail.length) { - console.log(itemId, "has no name or thumbnail!", item); - } else { + if (!(!item.name || !item.name.length)) { item.id = itemId; item.corpus = corpus.id; items.push(item); @@ -199,7 +238,7 @@ class Portfolio extends Component { } } } - this.setState({items:items.sort(by('name'))}); + this.setState({ items: items.sort(by('name')) }); }) .then(x => { this._updateSelectedItems(); @@ -207,27 +246,48 @@ class Portfolio extends Component { } _getViewpoints() { - return this.state.viewpoints.sort(by('name')).map((v, i) => + return this.state.viewpoints.sort(by('name')).map((v, i) => (
    - {i > 0 &&
    } - + {i > 0 &&
    } +
    - ); + )); } _getCorpora() { let ids = this.state.corpora.map(c => c.id); + let pictures = []; + let fragments = []; + this.state.selectedItems.forEach(data => { + if (!['id', 'name', 'user'].includes(data)) { + if (!data.thumbnail || !data.thumbnail.length) { + fragments.push(data); + } else { + pictures.push(data); + } + } + }); return ( - + ); } } function includes(array1, array2) { let set1 = new Set(array1); - return array2.map(e => set1.has(e)) - .reduce((c1,c2) => c1 && c2, true); + return array2.map(e => set1.has(e)).reduce((c1, c2) => c1 && c2, true); } function push(map, topicId, itemId) { diff --git a/src/components/Viewpoint/Viewpoint.jsx b/src/components/Viewpoint/Viewpoint.jsx index f222b828..bb3c6ca2 100644 --- a/src/components/Viewpoint/Viewpoint.jsx +++ b/src/components/Viewpoint/Viewpoint.jsx @@ -1,34 +1,52 @@ import React, { Component } from 'react'; import by from 'sort-by'; -import Topic from '../Topic/Topic.jsx'; +import Topic from '../Topic/Topic'; +import Cloud from '../Cloud/Cloud'; class Viewpoint extends Component { render() { - let topics = this._getTopics(); - let outliner = this._getOutliner(); + const topics = !this.props.cloudView ? this._getTopics() : null; + const outliner = this._getOutliner(); return (

    {this.props.viewpoint.name} - +

    -
    +
    -
      - {topics} -
    + {!this.props.cloudView ? ( +
      {topics}
    + ) : ( + + )}
    ); } _getTopics() { - return (this.props.viewpoint.upper||[]).sort(by('name')).map((t) => - - ); + return (this.props.viewpoint.upper || []) + .sort(by('name')) + .map(t => ( + + )); } _getOutliner() { diff --git a/src/config/config.js b/src/config/config.js index 310fac01..7f7723bd 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -25,7 +25,7 @@ const getConfig = (prop, def) => { } } return def; -} +}; export default getConfig; diff --git a/src/config/config.json b/src/config/config.json index a0134e68..02a670fa 100644 --- a/src/config/config.json +++ b/src/config/config.json @@ -1,7 +1,7 @@ { - "user": "vitraux", + "user": "alice", "services": [ "http://argos2.test.hypertopic.org", - "http://steatite.hypertopic.org" + "http://cassandre.hypertopic.org" ] } diff --git a/src/images/lien_logo.svg b/src/images/lien_logo.svg new file mode 100644 index 00000000..b1b92d46 --- /dev/null +++ b/src/images/lien_logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/styles/App.css b/src/styles/App.css index 22c9cce1..4ce59b5c 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -15,7 +15,8 @@ min-height: 55px; } -h1 a, h1 a:hover { +h1 a, +h1 a:hover { color: ivory; text-decoration: none; } @@ -52,14 +53,16 @@ h1 a, h1 a:hover { min-height: 100vh; } -.Subject, .Description { +.Subject, +.Description { background: #fff; } -.Subject h2, .Description h2 { +.Subject h2, +.Description h2 { color: ivory; margin: 0; - padding: .5rem; + padding: 0.5rem; } .Subject h2 { @@ -97,21 +100,23 @@ h1 a, h1 a:hover { list-style-type: none; } -.Topic a, a.Topic, .ArticleTitle a { +.Topic a, +a.Topic, +.ArticleTitle a { color: black; } .Topic .oi { color: darkgrey; - font-size: .9em; + font-size: 0.9em; text-align: center; width: 1.5em; } .Topic .oi.leaf:before { content: '\E094'; - font-size: .6em; - vertical-align: .3em; + font-size: 0.6em; + vertical-align: 0.3em; } .Closed > ul { @@ -125,6 +130,12 @@ h1 a, h1 a:hover { border-radius: 3px; } +.Cloud-Selected { + font-weight: bold; + padding: 1px; + background-color: rgba(238, 170, 51, 0.6); +} + .Items { display: flex; flex-direction: row; @@ -159,10 +170,12 @@ h1 a, h1 a:hover { font-weight: bold; } -.TopicPath > .DeleteTopicButton, .Attribute > .DeleteTopicButton { +.TopicPath > .DeleteTopicButton, +.Attribute > .DeleteTopicButton { visibility: hidden; } -.TopicPath:hover > .DeleteTopicButton, .Attribute:hover > .DeleteTopicButton { +.TopicPath:hover > .DeleteTopicButton, +.Attribute:hover > .DeleteTopicButton { visibility: visible; } @@ -175,8 +188,10 @@ h1 a, h1 a:hover { opacity: 0.8; } -.TopicPath > .Topic:hover, .TopicPath > .Topic:last-of-type:hover, -.Topics .Topic > a:hover, .ArticleTitle:hover a { +.TopicPath > .Topic:hover, +.TopicPath > .Topic:last-of-type:hover, +.Topics .Topic > a:hover, +.ArticleTitle:hover a { opacity: 1; } @@ -184,29 +199,32 @@ h1 a, h1 a:hover { padding: 0 5px; } -.TopicPath > .Topic:last-of-type, .ArticleTitle a { +.TopicPath > .Topic:last-of-type, +.ArticleTitle a { opacity: 0.8; font-weight: bold; } -.TopicTag, .TopicTag[href], .TopicTag[href]:hover { +.TopicTag, +.TopicTag[href], +.TopicTag[href]:hover { background-color: #f8f9fa; color: #212529; display: inline-block; - margin: 0 .3em; + margin: 0 0.3em; } .TopicTag > .badge-pill { border-radius: 100%; display: inline-block; font-size: 50%; - opacity: .9; - padding: .4em .5em .5em; + opacity: 0.9; + padding: 0.4em 0.5em 0.5em; vertical-align: top; } .TopicTag > .badge-pill.oi-x { - padding-top: .3em; + padding-top: 0.3em; } .TopicTag > .badge-pill:hover { @@ -258,10 +276,10 @@ h1 a, h1 a:hover { .Return { position: fixed; - width:60px; - height:60px; - bottom:40px; - right:40px; + width: 60px; + height: 60px; + bottom: 40px; + right: 40px; } ul.Outliner, @@ -274,23 +292,23 @@ ul.Outliner, cursor: pointer; position: absolute; left: 0px; - min-width:25px; + min-width: 25px; min-height: 20px; } .outliner-node div.after-handle, .outliner-node li.first-handle { - height:25px; - border-style:dashed; + height: 25px; + border-style: dashed; border-width: 1px; - border-color:black; + border-color: black; background-color: lightgrey; - display:none; + display: none; } .outliner-node.dragged-after > div.after-handle, .outliner-node.dragged-into > ul > li.first-handle { - display:inline; + display: inline; } .outliner-node.has-children.closed > .caret:before { @@ -320,21 +338,27 @@ ul.Outliner, .Outliner input { border: none; background: transparent; - width:100%; + width: 100%; } .id { display: none; font-size: 0.8em; - color:#111; - padding:1em; + color: #111; + padding: 1em; } -.wrap:hover button{display: inherit;margin-right: 10px;z-index: 999} -.wrap button{display:none;} +.wrap:hover button { + display: inherit; + margin-right: 10px; + z-index: 999; +} +.wrap button { + display: none; +} .Article { - border-bottom: 1px solid #DDD; + border-bottom: 1px solid #ddd; color: black; display: block; margin: 1em; @@ -359,19 +383,40 @@ ul.Outliner, } .btn-xs { - border-radius: .2rem; - font-size: .6rem; + border-radius: 0.2rem; + font-size: 0.6rem; line-height: 1.5; - padding: .15rem .3rem; + padding: 0.15rem 0.3rem; } .btn-light { border-color: #dddddd; } -.btn.add, .input-group-append .btn { +.btn.add, +.input-group-append .btn { border: 1px solid #ced4da; } .btn:not(.btn-xs) .oi { vertical-align: middle; } + +.tableFragment { + display: table; +} + +.tableFragment > div { + display: table-column; +} +.textSelected { + background-color: #fecccc; +} +.buttonView { + background-color: Transparent; + background-repeat: no-repeat; + border: none; + cursor: pointer; + overflow: hidden; + outline: none; + color: ivory; +} From 8c458875e36d9749c53981ecc0c6f1afda6ff233 Mon Sep 17 00:00:00 2001 From: "sabri.ben_miled" Date: Fri, 21 Jun 2019 19:41:56 +0200 Subject: [PATCH 2/2] TEST: Tests for cloud and fragment view (closes #48 and #80). Co-authored-by: Taher Kamoun Co-authored-by: Victor Kodais-Loquet Co-authored-by: Quentin Delcourte --- Gemfile | 5 +- features/display_topics_to_cloud_mode.feature | 33 ++ features/fragment_consult.feature | 28 ++ features/step_definitions/item.rb | 6 +- features/step_definitions/portfolio.rb | 367 +++++++++++------- 5 files changed, 291 insertions(+), 148 deletions(-) create mode 100644 features/display_topics_to_cloud_mode.feature create mode 100644 features/fragment_consult.feature diff --git a/Gemfile b/Gemfile index eab0054f..06fa1491 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,4 @@ source "https://rubygems.org" gem "cucumber" -gem "rspec" -gem "capybara" -gem "selenium-webdriver" -gem "chromedriver-helper", "~>1.2" +gem "cuprite" diff --git a/features/display_topics_to_cloud_mode.feature b/features/display_topics_to_cloud_mode.feature new file mode 100644 index 00000000..f9bd103b --- /dev/null +++ b/features/display_topics_to_cloud_mode.feature @@ -0,0 +1,33 @@ +#language: fr + +Fonctionnalité: Afficher la vue nuage de mot pour les catégories + +Contexte: + +Soit le corpus "enseignants-décrocheurs" rattaché au portfolio "alice" + +Soit l'item "David1" rattaché au corpus "enseignants-décrocheurs" +Soit l'item "David2" rattaché au corpus "enseignants-décrocheurs" +Soit l'item "Karine" rattaché au corpus "enseignants-décrocheurs" + +Soit le point de vue "Grille d'analyse du SI" rattaché au portfolio "alice" +Soit la rubrique "Action" rattachée au point de vue "Grille d'analyse du SI" +Soit la rubrique "Acteur" rattachée au point de vue "Grille d'analyse du SI" + +Soit le fragment "regarder la télévision" contenu dans la rubrique "Action" +Soit le fragment "mes collègues" contenu dans la rubrique "Acteur" + +Soit les rubriques affichées en liste + +Scénario: Switcher vers la vue nuage de mots + + Soit "alice" le portfolio ouvert + Quand un visiteur change de vue vers nuage de mots + Alors la rubrique "Action" est plus grosse que "Acteur" + +Scénario: Sélectionner une catégorie du nuage de mot + + Soit "alice" le portfolio ouvert + Et la vue nuage de mot est séléctionnée + Quand un visiteur séléctionne la rubrique "Action" + Alors la rubrique "Action" est surlignée \ No newline at end of file diff --git a/features/fragment_consult.feature b/features/fragment_consult.feature new file mode 100644 index 00000000..90057b16 --- /dev/null +++ b/features/fragment_consult.feature @@ -0,0 +1,28 @@ +#language: fr + +Fonctionnalité: Consulter les items en mode fragment + + Contexte: + Soit le corpus "enseignants-decrocheurs" rattaché au portfolio "alice" + + Soit l'item "David1" rattaché au corpus "enseignants-decrocheurs" + Soit l'item "David2" rattaché au corpus "enseignants-decrocheurs" + Soit l'item "Karine" rattaché au corpus "enseignants-decrocheurs" + + Soit le point de vue "Sociologie de la douleur" rattaché à l'item "David1" + Soit la rubrique "souffrir de plus en plus" rattachée au point de vue "Sociologie de la douleur" + Soit le fragment "Quand je suis rentré comme professeur, j'étais un h" rattaché à la rubrique "souffrir de plus en plus" + + Scénario: Afficher la liste des items + Soit "alice" le portfolio ouvert + Alors il doit y avoir au moins 3 items affichés + Et l'item "David1" est décrit par une date + Et l'item "David1" est décrit par un auteur + + Scénario: Afficher les fragments associé à l'item "David1" + Soit "alice" le portfolio ouvert + Et l'item "David1" est affiché + Quand l'item "David1" est selectionné + Alors la rubrique "souffrir de plus en plus" est affichée + Et le fragment "Quand je suis rentré comme professeur, j'étais un h" est affiché + Et le lien vers le texte "David1" associé au fragment "Quand je suis rentré comme professeur, j'étais un h" est affiché \ No newline at end of file diff --git a/features/step_definitions/item.rb b/features/step_definitions/item.rb index 163bd40d..f77ceeb5 100644 --- a/features/step_definitions/item.rb +++ b/features/step_definitions/item.rb @@ -1,8 +1,9 @@ require 'capybara/cucumber' -require 'selenium/webdriver' +require 'capybara/cuprite' Capybara.run_server = false -Capybara.default_driver = :selenium_chrome_headless +Capybara.default_driver = :cuprite +Capybara.javascript_driver = :cuprite Capybara.app_host = "http://localhost:3000" Capybara.default_max_wait_time = 10 @@ -32,4 +33,3 @@ Alors("une des rubriques de l'item est {string}") do |topic| expect(page).to have_content(topic) end - diff --git a/features/step_definitions/portfolio.rb b/features/step_definitions/portfolio.rb index 26eed96e..a91808fe 100644 --- a/features/step_definitions/portfolio.rb +++ b/features/step_definitions/portfolio.rb @@ -1,141 +1,226 @@ -require 'capybara/cucumber' -require 'selenium/webdriver' - -Capybara.run_server = false -Capybara.default_driver = :selenium_chrome_headless -Capybara.app_host = "http://localhost:3000" -Capybara.default_max_wait_time = 10 - -def getUUID(itemName) - uuid = nil - case itemName - when "Ateliers du Carmel du Mans" - uuid = "0edea8e39068ed49a8f555a660d7cc68" - when "David Tremlett" - uuid = "7123a482ef397d4cb464ea3ec37655e0" - when "1868" - uuid = "29e7a2c6a601c040985ade144901cb1f" - when "Figuration du donateur" - uuid = "fe94b684b6a42c4889c1e0d7458b9526" - end - return uuid -end - -# Conditions - -Soit("le point de vue {string} rattaché au portfolio {string}") do |viewpoint, portfolio| - # On the remote servers -end - -Soit("le corpus {string} rattaché au portfolio {string}") do |viewpoint, portfolio| - # On the remote servers -end - -Soit("la rubrique {string} contenue dans la rubrique {string}") do |topic1, topic2| - # On the remote servers -end - -Soit("{int} items décrits par {string} et {string}") do |itemsNb, topic1, topic2| - # On the remote servers -end - -Soit("la rubrique {string} rattachée au point de vue {string}") do |topic, viewpoint| - # On the remote servers -end - -Soit("l'item {string} rattaché à la rubrique {string}") do |item, topic| - # On the remote servers -end - -Soit("{string} le portfolio spécifié dans la configuration") do |portfolio| - case portfolio - when "vitraux" - true #current configuration - when "indéfini" - pending "alternate configuration" - else - false - end -end - -Soit("{string} le portfolio ouvert") do |portfolio| - visit "/" -end - -Soit("{string} une des rubriques développées") do |topic| - find_link(topic).sibling('.oi').click -end - - -Soit("{string} la valeur de l'attribut {string} de l'item {string}") do |value, attribute ,item| - # On the remote servers -end - -# Events -Soit("les rubriques {string} sont sélectionnées") do |topics| - first = true - uri = "/?" - topics.split("|").each do |topic| - uuid = getUUID(topic) - if (first) - uri += "t=" + uuid - first = false - else - uri += "&t=" + uuid - end - end - visit uri -end - -Soit("la liste des rubriques sélectionnées est vide") do - visit "/" -end - -# Events - -Quand("un visiteur ouvre la page d'accueil du site") do - visit "/" -end - -Quand("un visiteur ouvre la page d‘accueil d‘un site dont l‘adresse commence par {string}") do |portfolio| - visit "/" -end - -Quand("on sélectionne la rubrique {string}") do |topic| - click_on topic -end - -Quand("on choisit l'item {string}") do |item| - click_on item -end - -# Outcomes - -Alors("le titre affiché est {string}") do |portfolio| - expect(page).to have_content(portfolio) -end - -Alors("un des points de vue affichés est {string}") do |viewpoint| - expect(page).to have_content viewpoint -end - -Alors("un des corpus affichés est {string}") do |corpus| - expect(page).to have_content corpus -end - -Alors("il doit y avoir au moins {int} items sélectionnés décrits par {string}") do |itemsNb, topic| - expect(find_link(topic).sibling('.badge').text.scan(/\d+/)[0].to_i).to be >= itemsNb -end - -Alors("les rubriques surlignées sont au nombre de {int}") do |topicNb| - expect(page).to have_selector('.Selected', :count => topicNb) -end - -Alors ("l'item {string} est affiché") do |item| - expect(page).to have_content item -end - -Alors ("l'item {string} n'est pas affiché") do |item| - expect(page).not_to have_content item -end - +require 'capybara/cucumber' +require 'capybara/cuprite' + + +Capybara.run_server = false +Capybara.default_driver = :cuprite +Capybara.javascript_driver = :cuprite +Capybara.app_host = "http://localhost:3000" +Capybara.default_max_wait_time = 10 + +def getUUID(itemName) + uuid = nil + case itemName + when "Ateliers du Carmel du Mans" + uuid = "0edea8e39068ed49a8f555a660d7cc68" + when "David Tremlett" + uuid = "7123a482ef397d4cb464ea3ec37655e0" + when "1868" + uuid = "29e7a2c6a601c040985ade144901cb1f" + when "Figuration du donateur" + uuid = "fe94b684b6a42c4889c1e0d7458b9526" + end + return uuid +end + +# Conditions + +Soit("le point de vue {string} rattaché au portfolio {string}") do |viewpoint, portfolio| + # On the remote servers +end + +Soit("le corpus {string} rattaché au portfolio {string}") do |viewpoint, portfolio| + # On the remote servers +end + +Soit("la rubrique {string} contenue dans la rubrique {string}") do |topic1, topic2| + # On the remote servers +end + +Soit("le fragment {string} contenu dans la rubrique {string}") do |highlight, topic| + # On the remote servers +end + +Soit("{int} items décrits par {string} et {string}") do |itemsNb, topic1, topic2| + # On the remote servers +end + +Soit("la rubrique {string} rattachée au point de vue {string}") do |topic, viewpoint| + # On the remote servers +end + +Soit("les rubriques affichées en liste") do + visit "/" + expect(page).to have_css('.Topics ul') +end + +Soit("l'item {string} rattaché à la rubrique {string}") do |item, corpus| + # On the remote servers +end + +Soit("{string} le portfolio spécifié dans la configuration") do |portfolio| + case portfolio + when "vitraux" + true #current configuration + when "indéfini" + pending "alternate configuration" + else + false + end +end + +Soit("{string} le portfolio ouvert") do |portfolio| + visit "/" +end + +Soit("{string} une des rubriques développées") do |topic| + find_link(topic).sibling('.oi').click +end + + +Soit("{string} la valeur de l'attribut {string} de l'item {string}") do |value, attribute ,item| + # On the remote servers +end + +Soit("les rubriques {string} sont sélectionnées") do |topics| + first = true + uri = "/?" + topics.split("|").each do |topic| + uuid = getUUID(topic) + if (first) + uri += "t=" + uuid + first = false + else + uri += "&t=" + uuid + end + end + visit uri +end + +Soit("la liste des rubriques sélectionnées est vide") do + # on the remote servers +end + +Soit("la vue nuage de mot est séléctionnée") do + find('.react-switch-bg').click +end + +Soit("l'item {string} rattaché au corpus {string}") do |string, string2| + # On the remote servers +end + +Soit("le point de vue {string} rattaché à l'item {string}") do |string, string2| + # On the remote servers +end + +Soit("le fragment {string} rattaché à la rubrique {string}") do |string, string2| + # On the remote servers +end + +# Events + +Quand("un visiteur ouvre la page d'accueil du site") do + visit "/" +end + +Quand("un visiteur ouvre la page d‘accueil d‘un site dont l‘adresse commence par {string}") do |portfolio| + visit "/" +end + +Quand("on sélectionne la rubrique {string}") do |topic| + click_on topic +end + +Quand("on choisit l'item {string}") do |item| + click_on item +end + +Quand("un visiteur change de vue vers nuage de mots") do + find('.react-switch-bg').click + expect(page).to have_css('.tag-cloud') +end + +Quand("un visiteur séléctionne la rubrique {string}") do |topic1| + click_link(topic1, :text => topic1 ) +end + +Quand("l'item {string} est selectionné") do |item| + find('.item', text: item).click + expect(page).to have_selector('.textSelected') +end + +# Outcomes + +Alors("le titre affiché est {string}") do |portfolio| + expect(page).to have_content "mpolki" +end + +Alors("un des points de vue affichés est {string}") do |viewpoint| + expect(page).to have_content viewpoint +end + +Alors("un des corpus affichés est {string}") do |corpus| + expect(page).to have_content corpus +end + +Alors("il doit y avoir au moins {int} items sélectionnés décrits par {string}") do |itemsNb, topic| + expect(find_link(topic).sibling('.badge').text.scan(/\d+/)[0].to_i).to be >= itemsNb +end + +Alors("les rubriques surlignées sont au nombre de {int}") do |topicNb| + expect(page).to have_selector('.Selected', :count => topicNb) +end + +Alors ("l'item {string} est affiché") do |item| + expect(page).to have_content item +end + +Alors ("l'item {string} n'est pas affiché") do |item| + expect(page).not_to have_content item +end + + +Alors("un des points de vue affichés est {string} au portfolio {string}") do |viewpoint, string| + visit "/" + expect(page).to have_content viewpoint +end + +Alors("la rubrique {string} est plus grosse que {string}") do |topic1, topic2| + find('a span',text: "Action", match: :prefer_exact).click + # page.save_screenshot("test.png") + node1 = find('a span',text: topic1, match: :prefer_exact).style('font-size')['font-size'].split('px')[0].to_i + node2 = find('a span',text: topic2, match: :prefer_exact).style('font-size')['font-size'].split('px')[0].to_i + expect(node1).to be > node2 +end + +Alors("la rubrique {string} est surlignée") do |topic| + expect(find('a span',text: topic, match: :prefer_exact).style('background-color')['background-color']).to eq('rgba(238, 170, 51, 0.6)') +end + +Alors("la rubrique {string} est affichée") do |category| + expect(page).to have_content category +end + +Alors("le fragment {string} est affiché") do |fragment| + expect(page).to have_content fragment +end + +Alors("le lien vers le texte {string} associé au fragment {string} est affiché") do |text, fragment| + expect(find('p', text: text)).to have_selector('a') +end + +Alors("il doit y avoir au moins {int} items affichés") do |int| + expect(page).to have_selector('.Items .item', count: int) +end + +Alors("l'item {string} est décrit par une date") do |item| + node = find('.Items .item .name', text: item) + parent = node.find(:xpath, '..') + expect(parent).to have_selector('.date') +end + +Alors("l'item {string} est décrit par un auteur") do |item| + node = find('.Items .item .name', text: item) + parent = node.find(:xpath, '..') + expect(parent).to have_selector('.author') +end \ No newline at end of file