@eeacms/volto-slate-footnote 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,8 +4,46 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- #### [3.0.0](https://github.com/eea/volto-slate-footnote/compare/2.4.0...3.0.0)
7
+ #### [4.0.0](https://github.com/eea/volto-slate-footnote/compare/3.2.0...4.0.0)
8
8
 
9
+ - Use new slate namespaced pluginids [`#23`](https://github.com/eea/volto-slate-footnote/pull/23)
10
+ - Add Sonarqube tag using climate-energy-frontend addons list [`80a159b`](https://github.com/eea/volto-slate-footnote/commit/80a159bb67698cddedadc494e7ae2da32a96faea)
11
+
12
+ #### [3.2.0](https://github.com/eea/volto-slate-footnote/compare/3.1.1...3.2.0)
13
+
14
+ > 13 September 2021
15
+
16
+ - Release Multiple citations (#20) [`#21`](https://github.com/eea/volto-slate-footnote/pull/21)
17
+ - Multiple citations [`#20`](https://github.com/eea/volto-slate-footnote/pull/20)
18
+ - Release 3.2.0 [`6795fcd`](https://github.com/eea/volto-slate-footnote/commit/6795fcd1a4fc429bb70334fbdfa2cb0eedd748fa)
19
+
20
+ #### [3.1.1](https://github.com/eea/volto-slate-footnote/compare/3.1.0...3.1.1)
21
+
22
+ > 10 September 2021
23
+
24
+ - Citations in metadata [`#19`](https://github.com/eea/volto-slate-footnote/pull/19)
25
+ - Citations in metadata [`#18`](https://github.com/eea/volto-slate-footnote/pull/18)
26
+ - Remove console.log [`b5d83e7`](https://github.com/eea/volto-slate-footnote/commit/b5d83e7c13240151f602d122ecf09ba9b8e335f3)
27
+ - Fix slate json field default value in DX layout [`f2805bb`](https://github.com/eea/volto-slate-footnote/commit/f2805bbc15c51ebd914ee5f4eb9dd34ef32e095b)
28
+ - Fix cypress api_url [`6a4966a`](https://github.com/eea/volto-slate-footnote/commit/6a4966a2f92e932e1d0df35351989cda535f15c8)
29
+ - Add Sonarqube tag using ims-frontend addons list [`aed3984`](https://github.com/eea/volto-slate-footnote/commit/aed39840ecd467689eaf0adb352bd00ce95bdbbe)
30
+
31
+ #### [3.1.0](https://github.com/eea/volto-slate-footnote/compare/3.0.0...3.1.0)
32
+
33
+ > 9 September 2021
34
+
35
+ - Release [`#17`](https://github.com/eea/volto-slate-footnote/pull/17)
36
+ - render elements with multiple citations [`#15`](https://github.com/eea/volto-slate-footnote/pull/15)
37
+ - make accordion reference toggle when clicking citations refs #137423 [`#16`](https://github.com/eea/volto-slate-footnote/pull/16)
38
+ - Release 3.1.0 [`d87a1b0`](https://github.com/eea/volto-slate-footnote/commit/d87a1b0b893bb8fc090dcde193d3ac628471c352)
39
+ - Fix cypress tests [`cb08192`](https://github.com/eea/volto-slate-footnote/commit/cb08192508964c274eaaed3dab2d2c5392c43d2d)
40
+
41
+ ### [3.0.0](https://github.com/eea/volto-slate-footnote/compare/2.4.0...3.0.0)
42
+
43
+ > 3 September 2021
44
+
45
+ - Release 3.0.0 [`#14`](https://github.com/eea/volto-slate-footnote/pull/14)
46
+ - Cypress increase wait time [`06fe6e9`](https://github.com/eea/volto-slate-footnote/commit/06fe6e9cea79dc3a5d2a3e88db8d3cc35b967da3)
9
47
  - Update package metadata [`8d45704`](https://github.com/eea/volto-slate-footnote/commit/8d457042b24f4795eb453baebcb0bf0f6740d153)
10
48
  - Make it work with DX metadata [`a5139d2`](https://github.com/eea/volto-slate-footnote/commit/a5139d2af0e7947ba16d7c28912565683726554f)
11
49
 
package/Jenkinsfile CHANGED
@@ -4,7 +4,7 @@ pipeline {
4
4
  environment {
5
5
  GIT_NAME = "volto-slate-footnote"
6
6
  NAMESPACE = "@eeacms"
7
- SONARQUBE_TAGS = "volto.eea.europa.eu,biodiversity.europa.eu"
7
+ SONARQUBE_TAGS = "volto.eea.europa.eu,biodiversity.europa.eu,www.eea.europa.eu-ims,climate-energy.eea.europa.eu"
8
8
  DEPENDENCIES = ""
9
9
  }
10
10
 
@@ -0,0 +1,80 @@
1
+ import { slateBeforeEach, slateAfterEach } from '../support';
2
+
3
+ describe('Slate citations', () => {
4
+ beforeEach(slateBeforeEach);
5
+ afterEach(slateAfterEach);
6
+
7
+ it('Add Footnotes block and create citation', () => {
8
+ // Complete chained commands
9
+ cy.getSlateEditorAndType('Colorless green ideas sleep furiously.');
10
+
11
+ // Footnote
12
+ cy.setSlateSelection('Colorless', 'green');
13
+ cy.clickSlateButton('Footnote');
14
+
15
+ cy.get('.sidebar-container .field-wrapper-footnote .react-select-container')
16
+ .click()
17
+ .type('Citation{enter}');
18
+ cy.get('.sidebar-container .form .header button:first-of-type').click();
19
+
20
+ // Add block
21
+ cy.getSlateEditorAndType('{enter}');
22
+
23
+ cy.get('.ui.basic.icon.button.block-add-button').first().click();
24
+ cy.get('.blocks-chooser .title').contains('Text').click();
25
+ cy.get('.content.active.text .button.slateFootnotes')
26
+ .contains('Footnotes')
27
+ .click();
28
+
29
+ // Configure block
30
+ cy.get('[id=sidebar-properties] [name=title]').click().type('Footnotes');
31
+ cy.get('[id=sidebar-properties] label[for=field-global]').click();
32
+
33
+ // Save
34
+ cy.get('#toolbar-save').click();
35
+ cy.url().should('eq', Cypress.config().baseUrl + '/cypress/my-page');
36
+
37
+ // then the page view should contain our changes
38
+ cy.get('span.citation-item').contains('Colorless green');
39
+ cy.contains('Footnotes');
40
+ cy.contains('Citation');
41
+ });
42
+
43
+ it('Add Footnotes block and create multiple citations', () => {
44
+ // Complete chained commands
45
+ cy.getSlateEditorAndType('Colorless green ideas sleep furiously.');
46
+
47
+ // Footnote
48
+ cy.setSlateSelection('Colorless', 'green');
49
+ cy.clickSlateButton('Footnote');
50
+
51
+ cy.get('.sidebar-container .field-wrapper-footnote .react-select-container')
52
+ .click()
53
+ .type('Citation{enter}')
54
+ .type('Yet another citation{enter}');
55
+ cy.get('.sidebar-container .form .header button:first-of-type').click();
56
+
57
+ // Add block
58
+ cy.getSlateEditorAndType('{enter}');
59
+
60
+ cy.get('.ui.basic.icon.button.block-add-button').first().click();
61
+ cy.get('.blocks-chooser .title').contains('Text').click();
62
+ cy.get('.content.active.text .button.slateFootnotes')
63
+ .contains('Footnotes')
64
+ .click();
65
+
66
+ // Configure block
67
+ cy.get('[id=sidebar-properties] [name=title]').click().type('Footnotes');
68
+ cy.get('[id=sidebar-properties] label[for=field-global]').click();
69
+
70
+ // Save
71
+ cy.get('#toolbar-save').click();
72
+ cy.url().should('eq', Cypress.config().baseUrl + '/cypress/my-page');
73
+
74
+ // then the page view should contain our changes
75
+ cy.get('span.citation-item').contains('Colorless green');
76
+ cy.contains('Footnotes');
77
+ cy.contains('Citation');
78
+ cy.contains('Yet another citation');
79
+ });
80
+ });
@@ -92,7 +92,7 @@ Cypress.Commands.add(
92
92
  title: contentTitle,
93
93
  blocks: {
94
94
  'd3f1c443-583f-4e8e-a682-3bf25752a300': { '@type': 'title' },
95
- '7624cf59-05d0-4055-8f55-5fd6597d84b0': { '@type': 'text' },
95
+ '7624cf59-05d0-4055-8f55-5fd6597d84b0': { '@type': 'slate' },
96
96
  },
97
97
  blocks_layout: {
98
98
  items: [
@@ -125,6 +125,100 @@ Cypress.Commands.add(
125
125
  },
126
126
  );
127
127
 
128
+ // --- Add DX Content-Type ----------------------------------------------------------
129
+ Cypress.Commands.add('addContentType', (name) => {
130
+ let api_url, auth;
131
+ api_url = Cypress.env('API_PATH') || 'http://localhost:8080/Plone';
132
+ auth = {
133
+ user: 'admin',
134
+ pass: 'admin',
135
+ };
136
+ return cy
137
+ .request({
138
+ method: 'POST',
139
+ url: `${api_url}/@controlpanels/dexterity-types/${name}`,
140
+ headers: {
141
+ Accept: 'application/json',
142
+ },
143
+ auth: auth,
144
+ body: {
145
+ title: name,
146
+ },
147
+ })
148
+ .then(() => console.log(`${name} content-type added.`));
149
+ });
150
+
151
+ // --- Remove DX behavior ----------------------------------------------------------
152
+ Cypress.Commands.add('removeContentType', (name) => {
153
+ let api_url, auth;
154
+ api_url = Cypress.env('API_PATH') || 'http://localhost:8080/Plone';
155
+ auth = {
156
+ user: 'admin',
157
+ pass: 'admin',
158
+ };
159
+ return cy
160
+ .request({
161
+ method: 'DELETE',
162
+ url: `${api_url}/@controlpanels/dexterity-types/${name}`,
163
+ headers: {
164
+ Accept: 'application/json',
165
+ },
166
+ auth: auth,
167
+ body: {},
168
+ })
169
+ .then(() => console.log(`${name} content-type removed.`));
170
+ });
171
+
172
+ // --- Add DX field ----------------------------------------------------------
173
+ Cypress.Commands.add('addSlateJSONField', (type, name) => {
174
+ let api_url, auth;
175
+ api_url = Cypress.env('API_PATH') || 'http://localhost:8080/Plone';
176
+ auth = {
177
+ user: 'admin',
178
+ pass: 'admin',
179
+ };
180
+ return cy
181
+ .request({
182
+ method: 'POST',
183
+ url: `${api_url}/@types/${type}`,
184
+ headers: {
185
+ Accept: 'application/json',
186
+ },
187
+ auth: auth,
188
+ body: {
189
+ id: name,
190
+ title: name,
191
+ description: 'Slate JSON Field',
192
+ factory: 'SlateJSONField',
193
+ required: false,
194
+ },
195
+ })
196
+ .then(() => console.log(`${name} SlateJSONField field added to ${type}`));
197
+ });
198
+
199
+ // --- Remove DX field ----------------------------------------------------------
200
+ Cypress.Commands.add('removeSlateJSONField', (type, name) => {
201
+ let api_url, auth;
202
+ api_url = Cypress.env('API_PATH') || 'http://localhost:8080/Plone';
203
+ auth = {
204
+ user: 'admin',
205
+ pass: 'admin',
206
+ };
207
+ return cy
208
+ .request({
209
+ method: 'DELETE',
210
+ url: `${api_url}/@types/${type}/${name}`,
211
+ headers: {
212
+ Accept: 'application/json',
213
+ },
214
+ auth: auth,
215
+ body: {},
216
+ })
217
+ .then(() =>
218
+ console.log(`${name} SlateJSONField field removed from ${type}`),
219
+ );
220
+ });
221
+
128
222
  // --- REMOVE CONTENT --------------------------------------------------------
129
223
  Cypress.Commands.add('removeContent', (path) => {
130
224
  let api_url, auth;
@@ -146,6 +240,41 @@ Cypress.Commands.add('removeContent', (path) => {
146
240
  .then(() => console.log(`${path} removed`));
147
241
  });
148
242
 
243
+ Cypress.Commands.add('typeInSlate', { prevSubject: true }, (subject, text) => {
244
+ return (
245
+ cy
246
+ .wrap(subject)
247
+ .then((subject) => {
248
+ subject[0].dispatchEvent(
249
+ new InputEvent('beforeinput', {
250
+ inputType: 'insertText',
251
+ data: text,
252
+ }),
253
+ );
254
+ return subject;
255
+ })
256
+ // TODO: do this only for Electron-based browser which does not understand instantaneously
257
+ // that the user inserted some text in the block
258
+ .wait(1000)
259
+ );
260
+ });
261
+
262
+ Cypress.Commands.add('lineBreakInSlate', { prevSubject: true }, (subject) => {
263
+ return (
264
+ cy
265
+ .wrap(subject)
266
+ .then((subject) => {
267
+ subject[0].dispatchEvent(
268
+ new InputEvent('beforeinput', { inputType: 'insertLineBreak' }),
269
+ );
270
+ return subject;
271
+ })
272
+ // TODO: do this only for Electron-based browser which does not understand instantaneously
273
+ // that the block was split
274
+ .wait(1000)
275
+ );
276
+ });
277
+
149
278
  // --- SET WORKFLOW ----------------------------------------------------------
150
279
  Cypress.Commands.add(
151
280
  'setWorkflow',
@@ -247,6 +376,47 @@ Cypress.Commands.add(
247
376
  },
248
377
  );
249
378
 
379
+ Cypress.Commands.add('getSlateEditorAndType', (type) => {
380
+ cy.get('.content-area .slate-editor [contenteditable=true]')
381
+ .focus()
382
+ .click()
383
+ .wait(1000)
384
+ .type(type);
385
+ });
386
+
387
+ Cypress.Commands.add('setSlateSelection', (subject, query, endQuery) => {
388
+ cy.get('.slate-editor.selected [contenteditable=true]')
389
+ .focus()
390
+ .click()
391
+ .setSelection(subject, query, endQuery)
392
+ .wait(1000);
393
+ });
394
+
395
+ Cypress.Commands.add('setSlateCursor', (subject, query, endQuery) => {
396
+ cy.get('.slate-editor.selected [contenteditable=true]')
397
+ .focus()
398
+ .click()
399
+ .setCursor(subject, query, endQuery)
400
+ .wait(1000);
401
+ });
402
+
403
+ Cypress.Commands.add('clickSlateButton', (button) => {
404
+ cy.get(`.slate-inline-toolbar .button-wrapper a[title="${button}"]`).click();
405
+ });
406
+
407
+ Cypress.Commands.add('toolbarSave', () => {
408
+ cy.wait(1000);
409
+
410
+ // Save
411
+ cy.get('#toolbar-save').click();
412
+ cy.waitForResourceToLoad('@navigation');
413
+ cy.waitForResourceToLoad('@breadcrumbs');
414
+ cy.waitForResourceToLoad('@actions');
415
+ cy.waitForResourceToLoad('@types');
416
+ cy.waitForResourceToLoad('my-page');
417
+ cy.url().should('eq', Cypress.config().baseUrl + '/cypress/my-page');
418
+ });
419
+
250
420
  // Low level command reused by `setCursorBefore` and `setCursorAfter`, equal to `setCursorAfter`
251
421
  Cypress.Commands.add(
252
422
  'setCursor',
@@ -15,7 +15,6 @@
15
15
 
16
16
  // Import commands.js using ES2015 syntax:
17
17
  import './commands';
18
-
19
18
  // Alternatively you can use CommonJS syntax:
20
19
  // require('./commands')
21
20
 
@@ -24,7 +23,7 @@ import './commands';
24
23
  import '@cypress/code-coverage/support';
25
24
  coverage-end */
26
25
 
27
- export const setupBeforeEach = () => {
26
+ export const slateBeforeEach = (contentType = 'Document') => {
28
27
  cy.autologin();
29
28
  cy.createContent({
30
29
  contentType: 'Folder',
@@ -32,7 +31,7 @@ export const setupBeforeEach = () => {
32
31
  contentTitle: 'Cypress',
33
32
  });
34
33
  cy.createContent({
35
- contentType: 'Document',
34
+ contentType: contentType,
36
35
  contentId: 'my-page',
37
36
  contentTitle: 'My Page',
38
37
  path: 'cypress',
@@ -44,10 +43,85 @@ export const setupBeforeEach = () => {
44
43
  cy.waitForResourceToLoad('@types');
45
44
  cy.waitForResourceToLoad('my-page');
46
45
  cy.navigate('/cypress/my-page/edit');
47
- cy.get(`.block.title [data-contents]`);
48
46
  };
49
47
 
50
- export const tearDownAfterEach = () => {
48
+ export const slateAfterEach = () => {
51
49
  cy.autologin();
52
50
  cy.removeContent('cypress');
53
51
  };
52
+
53
+ export const slateJsonBeforeEach = (contentType = 'slate') => {
54
+ cy.autologin();
55
+ cy.addContentType(contentType);
56
+ cy.addSlateJSONField(contentType, 'slate');
57
+ slateBeforeEach(contentType);
58
+ };
59
+
60
+ export const slateJsonAfterEach = (contentType = 'slate') => {
61
+ cy.autologin();
62
+ cy.removeContentType(contentType);
63
+ slateAfterEach();
64
+ };
65
+
66
+ export const getSelectedSlateEditor = () => {
67
+ return cy.get('.slate-editor.selected [contenteditable=true]').click();
68
+ };
69
+
70
+ export const createSlateBlock = () => {
71
+ cy.get('.ui.basic.icon.button.block-add-button').first().click();
72
+ cy.get('.blocks-chooser .title').contains('Text').click();
73
+ cy.get('.ui.basic.icon.button.slate').contains('Text').click();
74
+ return getSelectedSlateEditor();
75
+ };
76
+
77
+ export const getSlateBlockValue = (sb) => {
78
+ return sb.invoke('attr', 'data-slate-value').then((str) => {
79
+ return typeof str === 'undefined' ? [] : JSON.parse(str);
80
+ });
81
+ };
82
+
83
+ export const createSlateBlockWithList = ({
84
+ numbered,
85
+ firstItemText,
86
+ secondItemText,
87
+ }) => {
88
+ let s1 = createSlateBlock();
89
+
90
+ s1.typeInSlate(firstItemText + secondItemText);
91
+
92
+ // select all contents of slate block
93
+ // - this opens hovering toolbar
94
+ cy.contains(firstItemText + secondItemText).then((el) => {
95
+ selectSlateNodeOfWord(el);
96
+ });
97
+
98
+ // TODO: do not hardcode these selectors:
99
+ if (numbered) {
100
+ // this is the numbered list option in the hovering toolbar
101
+ cy.get('.slate-inline-toolbar > :nth-child(9)').click();
102
+ } else {
103
+ // this is the bulleted list option in the hovering toolbar
104
+ cy.get('.slate-inline-toolbar > :nth-child(10)').click();
105
+ }
106
+
107
+ // move the text cursor
108
+ const sse = getSelectedSlateEditor();
109
+ sse.type('{leftarrow}');
110
+ for (let i = 0; i < firstItemText.length; ++i) {
111
+ sse.type('{rightarrow}');
112
+ }
113
+
114
+ // simulate pressing Enter
115
+ getSelectedSlateEditor().lineBreakInSlate();
116
+
117
+ return s1;
118
+ };
119
+
120
+ export const selectSlateNodeOfWord = (el) => {
121
+ return cy.window().then((win) => {
122
+ var event = new CustomEvent('Test_SelectWord', {
123
+ detail: el[0],
124
+ });
125
+ win.document.dispatchEvent(event);
126
+ });
127
+ };
package/cypress.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "baseUrl": "http://localhost:3000",
3
3
  "viewportWidth": 1280,
4
- "defaultCommandTimeout": 15000,
4
+ "defaultCommandTimeout": 8888,
5
+ "chromeWebSecurity": false,
5
6
  "reporter": "junit",
6
7
  "video": true,
8
+ "retries": {
9
+ "runMode": 8,
10
+ "openMode": 0
11
+ },
7
12
  "reporterOptions": {
8
13
  "mochaFile": "cypress/reports/cypress-[hash].xml",
9
14
  "jenkinsMode": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-slate-footnote",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "volto-slate-footnote: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react';
2
- import { getAllBlocks } from 'volto-slate/utils';
2
+ import { getAllBlocksAndSlateFields } from '@eeacms/volto-slate-footnote/editor/utils';
3
+
3
4
  import './less/public.less';
4
5
  import {
5
6
  makeFootnoteListOfUniqueItems,
@@ -25,7 +26,7 @@ const FootnotesBlockView = (props) => {
25
26
  useEffect(() => {
26
27
  if (properties) {
27
28
  const globalMetadata = global ? metadata : properties;
28
- const blocks = getAllBlocks(globalMetadata, []);
29
+ const blocks = getAllBlocksAndSlateFields(globalMetadata);
29
30
  const notesObjResult = makeFootnoteListOfUniqueItems(blocks);
30
31
 
31
32
  setNodesObjs(notesObjResult);
@@ -39,28 +40,51 @@ const FootnotesBlockView = (props) => {
39
40
  <ol>
40
41
  {Object.keys(notesObj).map((noteId) => {
41
42
  const note = notesObj[noteId];
42
- const { uid, footnote, zoteroId } = note;
43
+ const { uid, footnote, zoteroId, parentUid } = note;
43
44
  const { refs } = note;
44
45
  const refsList = refs ? Object.keys(refs) : null;
45
46
 
46
47
  return (
47
- <li key={uid} id={`footnote-${zoteroId || uid}`}>
48
+ <li
49
+ key={`footnote-${zoteroId || uid}`}
50
+ id={`footnote-${zoteroId || uid}`}
51
+ >
48
52
  <div
49
53
  dangerouslySetInnerHTML={{
50
54
  __html: makeFootnote(footnote),
51
55
  }}
52
56
  />
53
57
  {refsList ? (
54
- refsList.map((ref, index) => (
55
- <sup id={`cite_ref-${ref}`} key={`indice-${ref}`}>
56
- <a href={`#ref-${ref}`} aria-label="Back to content">
57
- {alphabet[index]}
58
+ <>
59
+ {/** some footnotes are never parent so we need the parent to reference */}
60
+ {/** int this case the first from refs has reference to the parent*/}
61
+ <sup
62
+ id={`cite_ref-${refsList[0]}`}
63
+ key={`indice-${refsList[0]}`}
64
+ >
65
+ <a
66
+ href={`#ref-${parentUid || uid}`}
67
+ aria-label="Back to content"
68
+ >
69
+ {alphabet[0]}
58
70
  </a>{' '}
59
71
  </sup>
60
- ))
72
+ {/** following refs will have the uid of the one that references it*/}
73
+ {refsList.slice(1).map((ref, index) => (
74
+ <sup id={`cite_ref-${ref}`} key={`indice-${ref}`}>
75
+ <a href={`#ref-${ref}`} aria-label="Back to content">
76
+ {alphabet[index + 1]}
77
+ </a>{' '}
78
+ </sup>
79
+ ))}
80
+ </>
61
81
  ) : (
62
82
  <sup id={`cite_ref-${uid}`}>
63
- <a href={`#ref-${uid}`} aria-label="Back to content">
83
+ {/** some footnotes are never parent so we need the parent to reference */}
84
+ <a
85
+ href={`#ref-${parentUid || uid}`}
86
+ aria-label="Back to content"
87
+ >
64
88
 
65
89
  </a>{' '}
66
90
  </sup>