@eeacms/volto-slate-footnote 7.0.0 → 7.1.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,7 +4,22 @@ 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
- ### [7.0.0](https://github.com/eea/volto-slate-footnote/compare/6.3.0...7.0.0) - 22 April 2024
7
+ ### [7.1.0](https://github.com/eea/volto-slate-footnote/compare/7.0.1...7.1.0) - 13 September 2024
8
+
9
+ #### :rocket: New Features
10
+
11
+ - feat: autodetect links and use universal link - refs #274052 [dobri1408 - [`9c13101`](https://github.com/eea/volto-slate-footnote/commit/9c13101b62b1df150e1b3dbb93807df60a074ad5)]
12
+
13
+ #### :hammer_and_wrench: Others
14
+
15
+ - Release 7.1.0 [alin - [`c15afb0`](https://github.com/eea/volto-slate-footnote/commit/c15afb0be79ba9c4e3a46e3c190a41db468e88cd)]
16
+ ### [7.0.1](https://github.com/eea/volto-slate-footnote/compare/7.0.0...7.0.1) - 23 August 2024
17
+
18
+ #### :hammer_and_wrench: Others
19
+
20
+ - test: Fix tests on Volto 16 [Alin Voinea - [`1de9cc0`](https://github.com/eea/volto-slate-footnote/commit/1de9cc0dc69f1e6316654c149e7e9c0912f27749)]
21
+ - Squashed commit of the following: [Alin Voinea - [`207a2d3`](https://github.com/eea/volto-slate-footnote/commit/207a2d3be243d8ff60e64dfe627c7b32443ea222)]
22
+ ## [7.0.0](https://github.com/eea/volto-slate-footnote/compare/6.3.0...7.0.0) - 22 April 2024
8
23
 
9
24
  #### :rocket: New Features
10
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-slate-footnote",
3
- "version": "7.0.0",
3
+ "version": "7.1.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",
@@ -23,6 +23,7 @@
23
23
  "@cypress/code-coverage": "^3.10.0",
24
24
  "@plone/scripts": "*",
25
25
  "babel-plugin-transform-class-properties": "^6.24.1",
26
+ "cypress": "13.1.0",
26
27
  "dotenv": "^16.3.2",
27
28
  "husky": "^8.0.3",
28
29
  "lint-staged": "^14.0.1",
@@ -3,13 +3,13 @@ import {
3
3
  openAccordionOrTabIfContainsFootnoteReference,
4
4
  getAllBlocksAndSlateFields,
5
5
  makeFootnoteListOfUniqueItems,
6
- makeFootnote,
7
6
  } from '@eeacms/volto-slate-footnote/editor/utils';
8
7
  import './less/public.less';
9
8
 
10
9
  import { UniversalLink } from '@plone/volto/components';
11
10
 
12
11
  const alphabet = 'abcdefghijklmnopqrstuvwxyz';
12
+ const urlRegex = /https?:\/\/[^\s]+/g;
13
13
 
14
14
  /**
15
15
  * @summary The React component that displays the list of footnotes inserted
@@ -19,6 +19,7 @@ const alphabet = 'abcdefghijklmnopqrstuvwxyz';
19
19
  * @param {Object} props Contains the properties `data` and `properties` as
20
20
  * received from the Volto form.
21
21
  */
22
+
22
23
  const FootnotesBlockView = (props) => {
23
24
  const { data, properties, tabData, content } = props;
24
25
  const { title, global, placeholder = 'Footnotes' } = data;
@@ -75,6 +76,30 @@ const FootnotesBlockView = (props) => {
75
76
  startList = citationIndice;
76
77
  }
77
78
 
79
+ const renderTextWithLinks = (text) => {
80
+ if (!text) return null;
81
+ const parts = text.split(urlRegex);
82
+ const links = text.match(urlRegex);
83
+ let result = [];
84
+
85
+ parts.forEach((part, index) => {
86
+ result.push(<span key={`text-${index}`}>{part}</span>);
87
+
88
+ if (links && links[index]) {
89
+ result.push(
90
+ <UniversalLink
91
+ key={`link-${index}`}
92
+ href={links[index]}
93
+ openLinkInNewTab={false}
94
+ >
95
+ {links[index]}
96
+ </UniversalLink>,
97
+ );
98
+ }
99
+ });
100
+
101
+ return result;
102
+ };
78
103
  return (
79
104
  <div className="footnotes-listing-block">
80
105
  <h3 title={placeholder}>{title}</h3>
@@ -85,16 +110,19 @@ const FootnotesBlockView = (props) => {
85
110
  const { uid, footnote, zoteroId, parentUid } = note;
86
111
  const { refs } = note;
87
112
  const refsList = refs ? Object.keys(refs) : null;
113
+
114
+ // const history = createBrowserHistory();
115
+ // const api = new Api();
116
+ // const store = configureStore(window.__data, history, api);
117
+ const footnoteText = !footnote
118
+ ? ''
119
+ : footnote.replace('<?xml version="1.0"?>', '');
88
120
  return (
89
121
  <li
90
122
  key={`footnote-${zoteroId || uid}`}
91
123
  id={`footnote-${zoteroId || uid}`}
92
124
  >
93
- <div
94
- dangerouslySetInnerHTML={{
95
- __html: makeFootnote(footnote),
96
- }}
97
- />
125
+ <div>{renderTextWithLinks(footnoteText)}</div>
98
126
  {refsList ? (
99
127
  <>
100
128
  {/** some footnotes are never parent so we need the parent to reference */}
@@ -15,9 +15,8 @@ import { UniversalLink } from '@plone/volto/components';
15
15
  * @param {string} footnote
16
16
  * @returns {string} formatted footnote
17
17
  */
18
- const makeFootnote = (footnote) => {
19
- return footnote ? footnote.replace('<?xml version="1.0"?>', '') : '';
20
- };
18
+
19
+ const urlRegex = /https?:\/\/[^\s]+/g;
21
20
 
22
21
  export const FootnoteElement = (props) => {
23
22
  const { attributes, children, element, mode, extras } = props;
@@ -37,7 +36,7 @@ export const FootnoteElement = (props) => {
37
36
  const notesObjResult = isEmpty(metadata)
38
37
  ? makeFootnoteListOfUniqueItems(storeBlocks)
39
38
  : makeFootnoteListOfUniqueItems(blocks);
40
- // will cosider zotero citations and footnote
39
+ // will consider zotero citations and footnote
41
40
  // notesObjResult contains all zotero/footnote as unique, and contain refs for other zotero/footnote
42
41
  const indiceIfZoteroId = data.extra
43
42
  ? [
@@ -52,6 +51,29 @@ export const FootnoteElement = (props) => {
52
51
  : // no extra citations (no multiples)
53
52
  `[${Object.keys(notesObjResult).indexOf(zoteroId) + 1}]`;
54
53
 
54
+ const renderTextWithLinks = (text) => {
55
+ if (!text) return null;
56
+ const parts = text.split(urlRegex);
57
+ const links = text.match(urlRegex);
58
+ let result = [];
59
+
60
+ parts.forEach((part, index) => {
61
+ result.push(<span key={`text-${index}`}>{part}</span>);
62
+ if (links && links[index]) {
63
+ result.push(
64
+ <UniversalLink
65
+ key={`link-${index}`}
66
+ href={links[index]}
67
+ openLinkInNewTab={false}
68
+ >
69
+ {links[index]}
70
+ </UniversalLink>,
71
+ );
72
+ }
73
+ });
74
+
75
+ return result;
76
+ };
55
77
  const citationIndice = zoteroId // ZOTERO
56
78
  ? indiceIfZoteroId
57
79
  : // FOOTNOTES
@@ -76,13 +98,17 @@ export const FootnoteElement = (props) => {
76
98
  Object.keys(notesObjResult).find(
77
99
  (noteKey) => notesObjResult[noteKey].uid === uid,
78
100
  ) ||
79
- // if not found in parent, search in refs, it might be a footnote references multiple times
101
+ // if not found in parent, search in refs, it might be a footnote referenced multiple times
80
102
  Object.keys(notesObjResult).find(
81
103
  (noteKey) =>
82
104
  notesObjResult[noteKey].uid === uid ||
83
105
  (notesObjResult[noteKey].refs && notesObjResult[noteKey].refs[uid]),
84
106
  );
85
107
 
108
+ const footnoteText = !data.footnote
109
+ ? ''
110
+ : data.footnote.replace('<?xml version="1.0"?>', '');
111
+
86
112
  return (
87
113
  <>
88
114
  {mode === 'view' ? (
@@ -109,7 +135,6 @@ export const FootnoteElement = (props) => {
109
135
  <Popup.Content>
110
136
  <List divided relaxed selection>
111
137
  <List.Item
112
- as={UniversalLink}
113
138
  href={`#footnote-${citationRefId}`}
114
139
  onClick={() =>
115
140
  openAccordionOrTabIfContainsFootnoteReference(
@@ -120,37 +145,34 @@ export const FootnoteElement = (props) => {
120
145
  >
121
146
  <List.Content>
122
147
  <List.Description>
123
- <div
124
- dangerouslySetInnerHTML={{
125
- __html: makeFootnote(data.footnote),
126
- }}
127
- />{' '}
148
+ {renderTextWithLinks(footnoteText)}
128
149
  </List.Description>
129
150
  </List.Content>
130
151
  </List.Item>
131
152
  {data.extra &&
132
- data.extra.map((item) => (
133
- <List.Item
134
- as={UniversalLink}
135
- href={`#footnote-${item.zoteroId || item.uid}`}
136
- onClick={() =>
137
- openAccordionOrTabIfContainsFootnoteReference(
138
- `#footnote-${item.zoteroId || item.uid}`,
139
- )
140
- }
141
- key={`#footnote-${item.zoteroId || item.uid}`}
142
- >
143
- <List.Content>
144
- <List.Description>
145
- <div
146
- dangerouslySetInnerHTML={{
147
- __html: makeFootnote(item.footnote),
148
- }}
149
- />{' '}
150
- </List.Description>
151
- </List.Content>
152
- </List.Item>
153
- ))}
153
+ data.extra.map((item) => {
154
+ const footnoteText = !item.footnote
155
+ ? ''
156
+ : item.footnote.replace('<?xml version="1.0"?>', '');
157
+
158
+ return (
159
+ <List.Item
160
+ href={`#footnote-${item.zoteroId || item.uid}`}
161
+ onClick={() =>
162
+ openAccordionOrTabIfContainsFootnoteReference(
163
+ `#footnote-${item.zoteroId || item.uid}`,
164
+ )
165
+ }
166
+ key={`#footnote-${item.zoteroId || item.uid}`}
167
+ >
168
+ <List.Content>
169
+ <List.Description>
170
+ {renderTextWithLinks(footnoteText)}
171
+ </List.Description>
172
+ </List.Content>
173
+ </List.Item>
174
+ );
175
+ })}
154
176
  </List>
155
177
  </Popup.Content>
156
178
  </Popup>
@@ -173,7 +195,6 @@ export const FootnoteElement = (props) => {
173
195
  <Popup.Content>
174
196
  <List divided relaxed selection>
175
197
  <List.Item
176
- as={UniversalLink}
177
198
  href={`#footnote-${citationRefId}`}
178
199
  onClick={() =>
179
200
  openAccordionOrTabIfContainsFootnoteReference(
@@ -184,18 +205,13 @@ export const FootnoteElement = (props) => {
184
205
  >
185
206
  <List.Content>
186
207
  <List.Description>
187
- <div
188
- dangerouslySetInnerHTML={{
189
- __html: makeFootnote(data.footnote),
190
- }}
191
- />{' '}
208
+ {renderTextWithLinks(footnoteText)}
192
209
  </List.Description>
193
210
  </List.Content>
194
211
  </List.Item>
195
212
  {data.extra &&
196
213
  data.extra.map((item) => (
197
214
  <List.Item
198
- as={UniversalLink}
199
215
  href={`#footnote-${item.zoteroId || item.uid}`}
200
216
  onClick={() =>
201
217
  openAccordionOrTabIfContainsFootnoteReference(
@@ -206,11 +222,7 @@ export const FootnoteElement = (props) => {
206
222
  >
207
223
  <List.Content>
208
224
  <List.Description>
209
- <div
210
- dangerouslySetInnerHTML={{
211
- __html: makeFootnote(item.footnote),
212
- }}
213
- />{' '}
225
+ {renderTextWithLinks(item.footnote)}
214
226
  </List.Description>
215
227
  </List.Content>
216
228
  </List.Item>
@@ -1,15 +1,6 @@
1
1
  import config from '@plone/volto/registry';
2
2
  import { Node } from 'slate';
3
3
  import { getAllBlocks } from '@plone/volto-slate/utils';
4
-
5
- /**
6
- * remove <?xml version="1.0"?> from the string
7
- * @param {*} footnote - xml format
8
- * @returns string
9
- */
10
- export const makeFootnote = (footnote) => {
11
- return footnote ? footnote.replace('<?xml version="1.0"?>', '') : '';
12
- };
13
4
  /**
14
5
  * retrive all slate children of nested objects
15
6
  * @param {object} path - the keys that we want to extract the slate children from
@@ -79,6 +70,14 @@ export const openAccordionOrTabIfContainsFootnoteReference = (footnoteId) => {
79
70
  return true;
80
71
  };
81
72
 
73
+ /**
74
+ * Will open accordion if contains footnote reference
75
+ * @param {string} footnoteId
76
+ * @deprecated This function got renamed to {@link openAccordionOrTabIfContainsFootnoteReference}
77
+ */
78
+ export const openAccordionIfContainsFootnoteReference =
79
+ openAccordionOrTabIfContainsFootnoteReference;
80
+
82
81
  const blockTypesOperations = {
83
82
  metadataSection: (block, properties) => {
84
83
  const fields = block?.fields || [];
@@ -1,5 +1,4 @@
1
1
  import {
2
- makeFootnote,
3
2
  openAccordionOrTabIfContainsFootnoteReference,
4
3
  getAllBlocksAndSlateFields,
5
4
  } from './utils';
@@ -9,19 +8,6 @@ jest.mock('@plone/volto-slate/utils', () => ({
9
8
  getAllBlocks: jest.fn(),
10
9
  }));
11
10
 
12
- describe('makeFootnote', () => {
13
- it('should remove xml version string from footnote', () => {
14
- const xmlString = '<?xml version="1.0"?>Test text';
15
- const expectedResult = 'Test text';
16
- expect(makeFootnote(xmlString)).toEqual(expectedResult);
17
- });
18
-
19
- it('should return empty string when footnote is null or undefined', () => {
20
- expect(makeFootnote(null)).toEqual('');
21
- expect(makeFootnote(undefined)).toEqual('');
22
- });
23
- });
24
-
25
11
  describe('openAccordionOrTabIfContainsFootnoteReference', () => {
26
12
  it('should open accordion if it contains footnote reference', () => {
27
13
  document.body.innerHTML = `