@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 +39 -1
- package/Jenkinsfile +1 -1
- package/cypress/integration/01-slate-footnote-block.js +80 -0
- package/cypress/support/commands.js +171 -1
- package/cypress/support/index.js +79 -5
- package/cypress.json +6 -1
- package/package.json +1 -1
- package/src/Blocks/Footnote/FootnotesBlockView.jsx +34 -10
- package/src/editor/FootnoteEditor.jsx +48 -16
- package/src/editor/MultiSelectSearchWidget.jsx +150 -0
- package/src/editor/extensions.js +1 -2
- package/src/editor/render.jsx +219 -40
- package/src/editor/styles.less +3 -27
- package/src/editor/utils.js +126 -28
- package/src/index.js +1 -1
- package/cypress/integration/01-block-basics.js +0 -36
- package/cypress/integration/02-block-slate.js +0 -63
package/src/editor/utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import config from '@plone/volto/registry';
|
|
2
2
|
import { Node } from 'slate';
|
|
3
|
+
import { getAllBlocks } from 'volto-slate/utils';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* remove <?xml version="1.0"?> from the string
|
|
@@ -12,6 +13,46 @@ export const makeFootnote = (footnote) => {
|
|
|
12
13
|
return free;
|
|
13
14
|
};
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Extends volto-slate getAllBlocks functionality also to SlateJSONFields
|
|
18
|
+
* inserted within blocks via Metadata / Metadata section block
|
|
19
|
+
* @param {Object} properties metadata properties received by the View component
|
|
20
|
+
* @returns {Array} Returns a flat array of blocks and slate fields
|
|
21
|
+
*/
|
|
22
|
+
export const getAllBlocksAndSlateFields = (properties) => {
|
|
23
|
+
const blocks = getAllBlocks(properties, []);
|
|
24
|
+
const flat_blocks = [];
|
|
25
|
+
for (const b_idx in blocks) {
|
|
26
|
+
const block = blocks[b_idx];
|
|
27
|
+
if (block['@type'] === 'metadataSection') {
|
|
28
|
+
const fields = block.fields;
|
|
29
|
+
for (const f_idx in fields) {
|
|
30
|
+
const field = fields[f_idx];
|
|
31
|
+
if (field?.field?.widget === 'slate') {
|
|
32
|
+
const field_id = field.field.id;
|
|
33
|
+
flat_blocks.push({
|
|
34
|
+
'@type': 'slate',
|
|
35
|
+
id: field_id,
|
|
36
|
+
value: properties[field_id]?.length ? properties[field_id] : null,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else if (block['@type'] === 'metadata') {
|
|
41
|
+
if (block?.data?.widget === 'slate') {
|
|
42
|
+
const f_id = block.data.id;
|
|
43
|
+
flat_blocks.push({
|
|
44
|
+
'@type': 'slate',
|
|
45
|
+
id: f_id,
|
|
46
|
+
value: properties[f_id]?.length ? properties[f_id] : null,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
flat_blocks.push(block);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return flat_blocks;
|
|
54
|
+
};
|
|
55
|
+
|
|
15
56
|
/**
|
|
16
57
|
* Will make an object with keys for every zoteroId and some uid that are unique
|
|
17
58
|
* or referenced multiple times
|
|
@@ -34,37 +75,28 @@ export const makeFootnoteListOfUniqueItems = (blocks) => {
|
|
|
34
75
|
if (footnotes.includes(node.type) && node.data) {
|
|
35
76
|
// for citations (Zotero items) create refs for same zoteroId
|
|
36
77
|
if (node.data.zoteroId) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
notesObjResult[node.data.zoteroId].refs = {
|
|
46
|
-
[notesObjResult[node.data.zoteroId].uid]: true,
|
|
47
|
-
[node.data.uid]: true,
|
|
48
|
-
};
|
|
78
|
+
iterateZoteroObj(notesObjResult, node.data);
|
|
79
|
+
// itereate the extra obj for multiple citations
|
|
80
|
+
if (node.data.extra) {
|
|
81
|
+
node.data.extra.forEach((zoteroObjItem) =>
|
|
82
|
+
// send the uid of the parent
|
|
83
|
+
// of the word the will have the reference indice
|
|
84
|
+
iterateZoteroObj(notesObjResult, zoteroObjItem, node.data.uid),
|
|
85
|
+
);
|
|
49
86
|
}
|
|
50
87
|
// for footnotes - create refs, on identical text
|
|
51
|
-
} else {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
88
|
+
} else if (node.data.extra) {
|
|
89
|
+
iterateFootnoteObj(notesObjResult, node.data);
|
|
90
|
+
node.data.extra.forEach((footnoteObjItem) =>
|
|
91
|
+
// since is called in case of extra, the parent is needed
|
|
92
|
+
iterateFootnoteObj(
|
|
93
|
+
notesObjResult,
|
|
94
|
+
footnoteObjItem,
|
|
95
|
+
node.data.uid,
|
|
96
|
+
),
|
|
55
97
|
);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
notesObjResult[node.data.uid] = { ...node.data };
|
|
59
|
-
} else if (notesObjResult[found].refs) {
|
|
60
|
-
notesObjResult[found].refs[node.data.uid] = true;
|
|
61
|
-
} else {
|
|
62
|
-
// add its own uid in refs for easier parsing in html
|
|
63
|
-
notesObjResult[found].refs = {
|
|
64
|
-
[notesObjResult[found].uid]: true,
|
|
65
|
-
[node.data.uid]: true,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
98
|
+
} else {
|
|
99
|
+
iterateFootnoteObj(notesObjResult, node.data);
|
|
68
100
|
}
|
|
69
101
|
}
|
|
70
102
|
});
|
|
@@ -72,3 +104,69 @@ export const makeFootnoteListOfUniqueItems = (blocks) => {
|
|
|
72
104
|
|
|
73
105
|
return notesObjResult;
|
|
74
106
|
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Will change the notesObjResultTemp to add new property if the zoteroId is new or add to the existing ones refs
|
|
110
|
+
* @param {Object} notesObjResultTemp - the object that will configure the zotero items
|
|
111
|
+
* @param {Object} zoteroObj - the footnote object
|
|
112
|
+
* @param {string} zoteroObj.zoteroId - id of the zotero citation
|
|
113
|
+
* @param {string} zoteroObj.uid - id of the slate item
|
|
114
|
+
* @param {string} zoteroObj.footnote - xml citation from zotero
|
|
115
|
+
* @param {string} parentUid - will be needed because html element (the word) that references multiple citations
|
|
116
|
+
* will have the id as the main uid, the ids from the extra will not matter in this case
|
|
117
|
+
*/
|
|
118
|
+
const iterateZoteroObj = (notesObjResultTemp, zoteroObj, parentUid) => {
|
|
119
|
+
const uid = parentUid || zoteroObj.uid;
|
|
120
|
+
// add new zoteroId
|
|
121
|
+
if (!notesObjResultTemp[zoteroObj.zoteroId]) {
|
|
122
|
+
notesObjResultTemp[zoteroObj.zoteroId] = {
|
|
123
|
+
...zoteroObj,
|
|
124
|
+
uid,
|
|
125
|
+
};
|
|
126
|
+
// if zoteroId and refs exist then add the uid to the refs
|
|
127
|
+
} else if (notesObjResultTemp[zoteroObj.zoteroId].refs) {
|
|
128
|
+
notesObjResultTemp[zoteroObj.zoteroId].refs[uid] = true;
|
|
129
|
+
} else {
|
|
130
|
+
// if zoteroId exists but not refs, add its own uid also in refs for easier parsing in html
|
|
131
|
+
notesObjResultTemp[zoteroObj.zoteroId].refs = {
|
|
132
|
+
[notesObjResultTemp[zoteroObj.zoteroId].uid]: true,
|
|
133
|
+
[uid]: true,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Will change the notesObjResultTemp to add new property if the footnote uid is new or add to the refs of the existing ones
|
|
140
|
+
* Some footnotes will always be in extra, so we need parentId to know where to find it in render
|
|
141
|
+
* @param {Object} notesObjResultTemp - the object that will configure the zotero items
|
|
142
|
+
* @param {Object} node - the footnote object
|
|
143
|
+
* @param {string} node.zoteroId - id of the zotero citation
|
|
144
|
+
* @param {string} node.parentUid - id of the parent footnote
|
|
145
|
+
* @param {string} node.uid - id of the slate item
|
|
146
|
+
* @param {string} node.footnote - xml citation from zotero
|
|
147
|
+
* @param {string} parentUid - will be needed because html element (the word) that references multiple citations
|
|
148
|
+
* will have the id as the main uid, the ids from the extra will not matter in this case
|
|
149
|
+
*/
|
|
150
|
+
const iterateFootnoteObj = (notesObjResultTemp, node, parentUid) => {
|
|
151
|
+
const uid = parentUid || node.uid;
|
|
152
|
+
const found = Object.keys(notesObjResultTemp).find((noteId) => {
|
|
153
|
+
return notesObjResultTemp[noteId].footnote === node.footnote;
|
|
154
|
+
});
|
|
155
|
+
// has not yet been added
|
|
156
|
+
if (!found) {
|
|
157
|
+
// will use the parentUid instead of own uid for render to be able to reference to the correct element
|
|
158
|
+
//(word containing the footnotes)
|
|
159
|
+
notesObjResultTemp[node.uid] = parentUid
|
|
160
|
+
? { ...node, parentUid }
|
|
161
|
+
: { ...node };
|
|
162
|
+
// the element is found, just add it's own uid to the list of refs, the parent is already known
|
|
163
|
+
} else if (notesObjResultTemp[found].refs) {
|
|
164
|
+
notesObjResultTemp[found].refs[node.uid] = true;
|
|
165
|
+
} else {
|
|
166
|
+
// element found but doesn't have refs yet, this means that it is a parent, so add it's existing uid and the current one
|
|
167
|
+
notesObjResultTemp[found].refs = {
|
|
168
|
+
[notesObjResultTemp[found].uid]: true,
|
|
169
|
+
[uid]: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
};
|
package/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import FootnotesBlockEdit from './Blocks/Footnote/FootnotesBlockEdit';
|
|
|
5
5
|
import FootnotesBlockSchema from './Blocks/Footnote/FootnotesBlockSchema';
|
|
6
6
|
import { FOOTNOTE } from './constants';
|
|
7
7
|
import installFootnoteEditor from './editor';
|
|
8
|
-
import SearchWidget from '@eeacms/volto-slate-footnote/editor/
|
|
8
|
+
import SearchWidget from '@eeacms/volto-slate-footnote/editor/MultiSelectSearchWidget';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @summary Called from Volto to configure new or existing Volto block types.
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { setupBeforeEach, tearDownAfterEach } from '../support';
|
|
2
|
-
|
|
3
|
-
describe('Blocks Tests', () => {
|
|
4
|
-
beforeEach(setupBeforeEach);
|
|
5
|
-
afterEach(tearDownAfterEach);
|
|
6
|
-
|
|
7
|
-
it('Add Block: Empty', () => {
|
|
8
|
-
// Change page title
|
|
9
|
-
cy.get('.documentFirstHeading > .public-DraftStyleDefault-block')
|
|
10
|
-
.clear()
|
|
11
|
-
.type('My Add-on Page')
|
|
12
|
-
.get('.documentFirstHeading span[data-text]')
|
|
13
|
-
.contains('My Add-on Page');
|
|
14
|
-
|
|
15
|
-
cy.get('.documentFirstHeading > .public-DraftStyleDefault-block').type(
|
|
16
|
-
'{enter}',
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
// Add block
|
|
20
|
-
cy.get('.ui.basic.icon.button.block-add-button').first().click();
|
|
21
|
-
cy.get('.blocks-chooser .title').contains('Text').click();
|
|
22
|
-
cy.get('.content.active.text .button.slateFootnotes').contains('Footnotes').click();
|
|
23
|
-
|
|
24
|
-
// Configure block
|
|
25
|
-
cy.get('[id=sidebar-properties] [name=title]').click().type('Footnotes');
|
|
26
|
-
cy.get('[id=sidebar-properties] label[for=field-global]').click();
|
|
27
|
-
|
|
28
|
-
// Save
|
|
29
|
-
cy.get('#toolbar-save').click();
|
|
30
|
-
cy.url().should('eq', Cypress.config().baseUrl + '/cypress/my-page');
|
|
31
|
-
|
|
32
|
-
// then the page view should contain our changes
|
|
33
|
-
cy.contains('My Add-on Page');
|
|
34
|
-
cy.contains('Footnotes');
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { setupBeforeEach, tearDownAfterEach } from '../support';
|
|
2
|
-
|
|
3
|
-
describe('Blocks Tests', () => {
|
|
4
|
-
beforeEach(setupBeforeEach);
|
|
5
|
-
afterEach(tearDownAfterEach);
|
|
6
|
-
|
|
7
|
-
it('Add Block and create footnote', () => {
|
|
8
|
-
// Change page title
|
|
9
|
-
cy.get('.documentFirstHeading > .public-DraftStyleDefault-block')
|
|
10
|
-
.clear()
|
|
11
|
-
.type('My Add-on Page')
|
|
12
|
-
.get('.documentFirstHeading span[data-text]')
|
|
13
|
-
.contains('My Add-on Page');
|
|
14
|
-
|
|
15
|
-
cy.get('.documentFirstHeading > .public-DraftStyleDefault-block').type(
|
|
16
|
-
'{enter}',
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
// Add some text with footnotes
|
|
20
|
-
cy.get('.slate-editor [contenteditable=true]')
|
|
21
|
-
.focus()
|
|
22
|
-
.click()
|
|
23
|
-
.wait(1000)
|
|
24
|
-
.type('Colorless green ideas sleep furiously.');
|
|
25
|
-
|
|
26
|
-
cy.get('.slate-editor.selected [contenteditable=true]').setSelection(
|
|
27
|
-
'furiously',
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
cy.wait(1000);
|
|
31
|
-
|
|
32
|
-
cy.get('.slate-inline-toolbar .button-wrapper a[title="Footnote"]').click();
|
|
33
|
-
cy.get('.sidebar-container [id=field-footnote]').click().type('Citation');
|
|
34
|
-
cy.get('.sidebar-container .form .header button:first-of-type').click();
|
|
35
|
-
|
|
36
|
-
// Add block
|
|
37
|
-
cy.get('.slate-editor [contenteditable=true]')
|
|
38
|
-
.focus()
|
|
39
|
-
.click()
|
|
40
|
-
.wait(1000)
|
|
41
|
-
.type('{enter}');
|
|
42
|
-
|
|
43
|
-
cy.get('.ui.basic.icon.button.block-add-button').first().click();
|
|
44
|
-
cy.get('.blocks-chooser .title').contains('Text').click();
|
|
45
|
-
cy.get('.content.active.text .button.slateFootnotes')
|
|
46
|
-
.contains('Footnotes')
|
|
47
|
-
.click();
|
|
48
|
-
|
|
49
|
-
// Configure block
|
|
50
|
-
cy.get('[id=sidebar-properties] [name=title]').click().type('Footnotes');
|
|
51
|
-
cy.get('[id=sidebar-properties] label[for=field-global]').click();
|
|
52
|
-
|
|
53
|
-
// Save
|
|
54
|
-
cy.get('#toolbar-save').click();
|
|
55
|
-
cy.url().should('eq', Cypress.config().baseUrl + '/cypress/my-page');
|
|
56
|
-
|
|
57
|
-
// then the page view should contain our changes
|
|
58
|
-
cy.contains('My Add-on Page');
|
|
59
|
-
cy.get('span.citation-item').contains('furiously');
|
|
60
|
-
cy.contains('Footnotes');
|
|
61
|
-
cy.contains('Citation');
|
|
62
|
-
});
|
|
63
|
-
});
|