Skip to content

Commit

Permalink
feat: autodetect links and use universal link - refs #274052
Browse files Browse the repository at this point in the history
  • Loading branch information
dobri1408 authored Sep 13, 2024
1 parent 0a84bf2 commit 9c13101
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 74 deletions.
40 changes: 34 additions & 6 deletions src/Blocks/Footnote/FootnotesBlockView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import {
openAccordionOrTabIfContainsFootnoteReference,
getAllBlocksAndSlateFields,
makeFootnoteListOfUniqueItems,
makeFootnote,
} from '@eeacms/volto-slate-footnote/editor/utils';
import './less/public.less';

import { UniversalLink } from '@plone/volto/components';

const alphabet = 'abcdefghijklmnopqrstuvwxyz';
const urlRegex = /https?:\/\/[^\s]+/g;

/**
* @summary The React component that displays the list of footnotes inserted
Expand All @@ -19,6 +19,7 @@ const alphabet = 'abcdefghijklmnopqrstuvwxyz';
* @param {Object} props Contains the properties `data` and `properties` as
* received from the Volto form.
*/

const FootnotesBlockView = (props) => {
const { data, properties, tabData, content } = props;
const { title, global, placeholder = 'Footnotes' } = data;
Expand Down Expand Up @@ -75,6 +76,30 @@ const FootnotesBlockView = (props) => {
startList = citationIndice;
}

const renderTextWithLinks = (text) => {
if (!text) return null;
const parts = text.split(urlRegex);
const links = text.match(urlRegex);
let result = [];

parts.forEach((part, index) => {
result.push(<span key={`text-${index}`}>{part}</span>);

if (links && links[index]) {
result.push(
<UniversalLink
key={`link-${index}`}
href={links[index]}
openLinkInNewTab={false}
>
{links[index]}
</UniversalLink>,
);
}
});

return result;
};
return (
<div className="footnotes-listing-block">
<h3 title={placeholder}>{title}</h3>
Expand All @@ -85,16 +110,19 @@ const FootnotesBlockView = (props) => {
const { uid, footnote, zoteroId, parentUid } = note;
const { refs } = note;
const refsList = refs ? Object.keys(refs) : null;

// const history = createBrowserHistory();
// const api = new Api();
// const store = configureStore(window.__data, history, api);
const footnoteText = !footnote
? ''
: footnote.replace('<?xml version="1.0"?>', '');
return (
<li
key={`footnote-${zoteroId || uid}`}
id={`footnote-${zoteroId || uid}`}
>
<div
dangerouslySetInnerHTML={{
__html: makeFootnote(footnote),
}}
/>
<div>{renderTextWithLinks(footnoteText)}</div>
{refsList ? (
<>
{/** some footnotes are never parent so we need the parent to reference */}
Expand Down
102 changes: 57 additions & 45 deletions src/editor/render.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ import { UniversalLink } from '@plone/volto/components';
* @param {string} footnote
* @returns {string} formatted footnote
*/
const makeFootnote = (footnote) => {
return footnote ? footnote.replace('<?xml version="1.0"?>', '') : '';
};

const urlRegex = /https?:\/\/[^\s]+/g;

export const FootnoteElement = (props) => {
const { attributes, children, element, mode, extras } = props;
Expand All @@ -37,7 +36,7 @@ export const FootnoteElement = (props) => {
const notesObjResult = isEmpty(metadata)
? makeFootnoteListOfUniqueItems(storeBlocks)
: makeFootnoteListOfUniqueItems(blocks);
// will cosider zotero citations and footnote
// will consider zotero citations and footnote
// notesObjResult contains all zotero/footnote as unique, and contain refs for other zotero/footnote
const indiceIfZoteroId = data.extra
? [
Expand All @@ -52,6 +51,29 @@ export const FootnoteElement = (props) => {
: // no extra citations (no multiples)
`[${Object.keys(notesObjResult).indexOf(zoteroId) + 1}]`;

const renderTextWithLinks = (text) => {
if (!text) return null;
const parts = text.split(urlRegex);
const links = text.match(urlRegex);
let result = [];

parts.forEach((part, index) => {
result.push(<span key={`text-${index}`}>{part}</span>);
if (links && links[index]) {
result.push(
<UniversalLink
key={`link-${index}`}
href={links[index]}
openLinkInNewTab={false}
>
{links[index]}
</UniversalLink>,
);
}
});

return result;
};
const citationIndice = zoteroId // ZOTERO
? indiceIfZoteroId
: // FOOTNOTES
Expand All @@ -76,13 +98,17 @@ export const FootnoteElement = (props) => {
Object.keys(notesObjResult).find(
(noteKey) => notesObjResult[noteKey].uid === uid,
) ||
// if not found in parent, search in refs, it might be a footnote references multiple times
// if not found in parent, search in refs, it might be a footnote referenced multiple times
Object.keys(notesObjResult).find(
(noteKey) =>
notesObjResult[noteKey].uid === uid ||
(notesObjResult[noteKey].refs && notesObjResult[noteKey].refs[uid]),
);

const footnoteText = !data.footnote
? ''
: data.footnote.replace('<?xml version="1.0"?>', '');

return (
<>
{mode === 'view' ? (
Expand All @@ -109,7 +135,6 @@ export const FootnoteElement = (props) => {
<Popup.Content>
<List divided relaxed selection>
<List.Item
as={UniversalLink}
href={`#footnote-${citationRefId}`}
onClick={() =>
openAccordionOrTabIfContainsFootnoteReference(
Expand All @@ -120,37 +145,34 @@ export const FootnoteElement = (props) => {
>
<List.Content>
<List.Description>
<div
dangerouslySetInnerHTML={{
__html: makeFootnote(data.footnote),
}}
/>{' '}
{renderTextWithLinks(footnoteText)}
</List.Description>
</List.Content>
</List.Item>
{data.extra &&
data.extra.map((item) => (
<List.Item
as={UniversalLink}
href={`#footnote-${item.zoteroId || item.uid}`}
onClick={() =>
openAccordionOrTabIfContainsFootnoteReference(
`#footnote-${item.zoteroId || item.uid}`,
)
}
key={`#footnote-${item.zoteroId || item.uid}`}
>
<List.Content>
<List.Description>
<div
dangerouslySetInnerHTML={{
__html: makeFootnote(item.footnote),
}}
/>{' '}
</List.Description>
</List.Content>
</List.Item>
))}
data.extra.map((item) => {
const footnoteText = !item.footnote
? ''
: item.footnote.replace('<?xml version="1.0"?>', '');

return (
<List.Item
href={`#footnote-${item.zoteroId || item.uid}`}
onClick={() =>
openAccordionOrTabIfContainsFootnoteReference(
`#footnote-${item.zoteroId || item.uid}`,
)
}
key={`#footnote-${item.zoteroId || item.uid}`}
>
<List.Content>
<List.Description>
{renderTextWithLinks(footnoteText)}
</List.Description>
</List.Content>
</List.Item>
);
})}
</List>
</Popup.Content>
</Popup>
Expand All @@ -173,7 +195,6 @@ export const FootnoteElement = (props) => {
<Popup.Content>
<List divided relaxed selection>
<List.Item
as={UniversalLink}
href={`#footnote-${citationRefId}`}
onClick={() =>
openAccordionOrTabIfContainsFootnoteReference(
Expand All @@ -184,18 +205,13 @@ export const FootnoteElement = (props) => {
>
<List.Content>
<List.Description>
<div
dangerouslySetInnerHTML={{
__html: makeFootnote(data.footnote),
}}
/>{' '}
{renderTextWithLinks(footnoteText)}
</List.Description>
</List.Content>
</List.Item>
{data.extra &&
data.extra.map((item) => (
<List.Item
as={UniversalLink}
href={`#footnote-${item.zoteroId || item.uid}`}
onClick={() =>
openAccordionOrTabIfContainsFootnoteReference(
Expand All @@ -206,11 +222,7 @@ export const FootnoteElement = (props) => {
>
<List.Content>
<List.Description>
<div
dangerouslySetInnerHTML={{
__html: makeFootnote(item.footnote),
}}
/>{' '}
{renderTextWithLinks(item.footnote)}
</List.Description>
</List.Content>
</List.Item>
Expand Down
9 changes: 0 additions & 9 deletions src/editor/utils.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import config from '@plone/volto/registry';
import { Node } from 'slate';
import { getAllBlocks } from '@plone/volto-slate/utils';

/**
* remove <?xml version="1.0"?> from the string
* @param {*} footnote - xml format
* @returns string
*/
export const makeFootnote = (footnote) => {
return footnote ? footnote.replace('<?xml version="1.0"?>', '') : '';
};
/**
* retrive all slate children of nested objects
* @param {object} path - the keys that we want to extract the slate children from
Expand Down
14 changes: 0 additions & 14 deletions src/editor/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
makeFootnote,
openAccordionOrTabIfContainsFootnoteReference,
getAllBlocksAndSlateFields,
} from './utils';
Expand All @@ -9,19 +8,6 @@ jest.mock('@plone/volto-slate/utils', () => ({
getAllBlocks: jest.fn(),
}));

describe('makeFootnote', () => {
it('should remove xml version string from footnote', () => {
const xmlString = '<?xml version="1.0"?>Test text';
const expectedResult = 'Test text';
expect(makeFootnote(xmlString)).toEqual(expectedResult);
});

it('should return empty string when footnote is null or undefined', () => {
expect(makeFootnote(null)).toEqual('');
expect(makeFootnote(undefined)).toEqual('');
});
});

describe('openAccordionOrTabIfContainsFootnoteReference', () => {
it('should open accordion if it contains footnote reference', () => {
document.body.innerHTML = `
Expand Down

0 comments on commit 9c13101

Please sign in to comment.