From 67d7acbf244c5342062e15dd3db1a6a8bee6d9af Mon Sep 17 00:00:00 2001 From: Bilal MEDDAH Date: Thu, 20 Jul 2023 10:58:02 +0200 Subject: [PATCH 1/6] update: remove onLinkFound fn --- .../components/ResourceEditor/CodeEditor.tsx | 18 ++------- .../components/ResourceEditor/index.tsx | 39 +------------------ 2 files changed, 4 insertions(+), 53 deletions(-) diff --git a/src/shared/components/ResourceEditor/CodeEditor.tsx b/src/shared/components/ResourceEditor/CodeEditor.tsx index 7886a79a4..2829daa60 100644 --- a/src/shared/components/ResourceEditor/CodeEditor.tsx +++ b/src/shared/components/ResourceEditor/CodeEditor.tsx @@ -1,7 +1,7 @@ import React, { forwardRef } from 'react'; import codemiror, { EditorConfiguration } from 'codemirror'; import { UnControlled as CodeMirror } from 'react-codemirror2'; -import { INDENT_UNIT } from './editorUtils'; +import { INDENT_UNIT, highlightUrlOverlay } from './editorUtils'; import { clsx } from 'clsx'; import { Spin } from 'antd'; @@ -12,25 +12,13 @@ type TCodeEditor = { fullscreen: boolean; keyFoldCode(cm: any): void; handleChange(editor: any, data: any, value: any): void; - onLinksFound(): void; }; type TEditorConfiguration = EditorConfiguration & { foldCode: boolean; }; const CodeEditor = forwardRef( - ( - { - busy, - value, - editable, - fullscreen, - keyFoldCode, - handleChange, - onLinksFound, - }, - ref - ) => { + ({ busy, value, editable, fullscreen, keyFoldCode, handleChange }, ref) => { return ( ( )} onChange={handleChange} editorDidMount={editor => { + highlightUrlOverlay(editor); (ref as React.MutableRefObject).current = editor; }} - onUpdate={onLinksFound} /> ); diff --git a/src/shared/components/ResourceEditor/index.tsx b/src/shared/components/ResourceEditor/index.tsx index e9f0facec..34ed4fe12 100644 --- a/src/shared/components/ResourceEditor/index.tsx +++ b/src/shared/components/ResourceEditor/index.tsx @@ -15,23 +15,12 @@ import 'codemirror/addon/fold/foldcode'; import 'codemirror/addon/fold/foldgutter'; import 'codemirror/addon/fold/brace-fold'; -import isValidUrl, { - isAllowedProtocal, - isStorageLink, - isUrlCurieFormat, -} from '../../../utils/validUrl'; import CodeEditor from './CodeEditor'; import { RootState } from '../../store/reducers'; -import { - useEditorPopover, - useEditorTooltip, - CODEMIRROR_LINK_CLASS, -} from './useEditorTooltip'; +import { useEditorPopover, useEditorTooltip } from './useEditorTooltip'; import { DATA_EXPLORER_GRAPH_FLOW_PATH } from '../../store/reducers/data-explorer'; import ResourceResolutionCache from './ResourcesLRUCache'; import './ResourceEditor.less'; - -const AnchorLinkIcon = require('../../images/AnchorLink.svg'); export interface ResourceEditorProps { rawData: { [key: string]: any }; onSubmit: (rawData: { [key: string]: any }) => void; @@ -53,15 +42,6 @@ export interface ResourceEditorProps { const switchMarginRight = { marginRight: 5 }; -const isClickableLine = (url: string) => { - return ( - isValidUrl(url) && - isAllowedProtocal(url) && - !isUrlCurieFormat(url) && - !isStorageLink(url) - ); -}; - const ResourceEditor: React.FunctionComponent = props => { const { rawData, @@ -96,7 +76,6 @@ const ResourceEditor: React.FunctionComponent = props => { oidc: state.oidc, config: state.config, })); - const userAuthenticated = oidc.user && oidc.user.access_token; const keyFoldCode = (cm: any) => { cm.foldCode(cm.getCursor()); }; @@ -131,16 +110,6 @@ const ResourceEditor: React.FunctionComponent = props => { } onMetadataChange?.(checked); }; - const onLinksFound = () => { - const elements = document.getElementsByClassName('cm-string'); - Array.from(elements).forEach((item, index) => { - const itemSpan = item as HTMLSpanElement; - const url = itemSpan.innerText.replace(/^"|"$/g, ''); - if (isClickableLine(url)) { - itemSpan.classList.add(CODEMIRROR_LINK_CLASS); - } - }); - }; React.useEffect(() => { setEditing(false); @@ -204,11 +173,6 @@ const ResourceEditor: React.FunctionComponent = props => {
{showControlPanel && (
@@ -298,7 +262,6 @@ const ResourceEditor: React.FunctionComponent = props => { editable={editable} handleChange={handleChange} keyFoldCode={keyFoldCode} - onLinksFound={onLinksFound} fullscreen={fullscreen} />
From a5139ec3a9257ce63e697636f9b082aba50a7361 Mon Sep 17 00:00:00 2001 From: Bilal MEDDAH Date: Thu, 20 Jul 2023 10:58:46 +0200 Subject: [PATCH 2/6] update: add highlightUrlOverlay fn to replace onLinkFound --- .../components/ResourceEditor/editorUtils.ts | 41 ++++++++++++++++++- .../ResourceEditor/useEditorTooltip.tsx | 8 +--- src/utils/validUrl.ts | 4 +- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/shared/components/ResourceEditor/editorUtils.ts b/src/shared/components/ResourceEditor/editorUtils.ts index 4a4d519bb..83964f230 100644 --- a/src/shared/components/ResourceEditor/editorUtils.ts +++ b/src/shared/components/ResourceEditor/editorUtils.ts @@ -1,6 +1,7 @@ import { NexusClient, Resource } from '@bbp/nexus-sdk'; import { has } from 'lodash'; import isValidUrl, { + isAllowedProtocol, isExternalLink, isStorageLink, isUrlCurieFormat, @@ -46,6 +47,8 @@ type TReturnedResolvedData = Omit< export const LINE_HEIGHT = 15; export const INDENT_UNIT = 4; +export const CODEMIRROR_HOVER_CLASS = 'CodeMirror-hover-tooltip'; +export const CODEMIRROR_LINK_CLASS = 'fusion-resource-link'; const NEAR_BY = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0]; const isDownloadableLink = (resource: Resource) => { return Boolean( @@ -74,6 +77,15 @@ export const getDataExplorerResourceItemArray = ( data._rev, ]) as TDEResource; }; +export const isClickableLine = (url: string) => { + return ( + isValidUrl(url) && + isAllowedProtocol(url) && + !isUrlCurieFormat(url) && + !isStorageLink(url) + ); +}; + export function getTokenAndPosAt(e: MouseEvent, current: CodeMirror.Editor) { const node = e.target || e.srcElement; const text = @@ -86,9 +98,12 @@ export function getTokenAndPosAt(e: MouseEvent, current: CodeMirror.Editor) { }; const pos = current.coordsChar(coords); const token = current.getTokenAt(pos); - if (token && token.string === text) { + const url = token + ? token.string.replace(/\\/g, '').replace(/\"/g, '') + : null; + if (token && url === text) { return { - token, + url, coords: { left: editorRect.left, top: coords.top + LINE_HEIGHT, @@ -101,6 +116,28 @@ export function getTokenAndPosAt(e: MouseEvent, current: CodeMirror.Editor) { coords: { left: editorRect.left, top: e.pageY }, }; } +export const highlightUrlOverlay = (editor: CodeMirror.Editor) => { + editor.addOverlay({ + token: function(stream: any) { + const rx_word = '" '; // Define what separates a word + let ch = stream.peek(); + let word = ''; + + if (rx_word.includes(ch) || ch === '\uE000' || ch === '\uE001') { + stream.next(); + return null; + } + + while ((ch = stream.peek()) && !rx_word.includes(ch)) { + word += ch; + stream.next(); + } + + if (isClickableLine(word)) return CODEMIRROR_LINK_CLASS; // CSS class: cm-url + return; + }, + }); +}; export async function editorLinkResolutionHandler({ nexus, apiEndpoint, diff --git a/src/shared/components/ResourceEditor/useEditorTooltip.tsx b/src/shared/components/ResourceEditor/useEditorTooltip.tsx index 9e06ade0d..a86f4db0e 100644 --- a/src/shared/components/ResourceEditor/useEditorTooltip.tsx +++ b/src/shared/components/ResourceEditor/useEditorTooltip.tsx @@ -227,9 +227,7 @@ function useEditorTooltip({ async function onMouseOver(ev: MouseEvent) { const node = ev.target as HTMLElement; if (node) { - const { token } = getTokenAndPosAt(ev, currentEditor); - const content = token?.string || ''; - const url = content.replace(/\\/g, '').replace(/\"/g, ''); + const { url } = getTokenAndPosAt(ev, currentEditor); if (url && mayBeResolvableLink(url)) { node.classList.add('wait-for-tooltip'); removeTooltipsFromDOM(); @@ -341,9 +339,7 @@ function useEditorPopover({ removeTooltipsFromDOM(); const node = ev.target as HTMLElement; if (node) { - const { token } = getTokenAndPosAt(ev, currentEditor); - const content = token?.string || ''; - const url = content.replace(/\\/g, '').replace(/\"/g, ''); + const { url } = getTokenAndPosAt(ev, currentEditor); if (url && mayBeResolvableLink(url)) { editorLinkResolutionHandler({ nexus, diff --git a/src/utils/validUrl.ts b/src/utils/validUrl.ts index cc641c87e..ec59b6913 100644 --- a/src/utils/validUrl.ts +++ b/src/utils/validUrl.ts @@ -34,7 +34,7 @@ function isExternalLink(url: string): boolean { function isStorageLink(url: string): boolean { return url.startsWith('file:///gpfs'); } -function isAllowedProtocal(url: string): boolean { +function isAllowedProtocol(url: string): boolean { return url.startsWith('https://') || url.startsWith('http://'); } @@ -43,6 +43,6 @@ export { isUrlCurieFormat, isExternalLink, isStorageLink, - isAllowedProtocal, + isAllowedProtocol, }; export default isValidUrl; From 7b80d02380206ff05a659753e7f46589a130203c Mon Sep 17 00:00:00 2001 From: Bilal MEDDAH Date: Thu, 20 Jul 2023 10:58:55 +0200 Subject: [PATCH 3/6] update: style and test --- src/shared/components/ResourceEditor/ResourceEditor.less | 2 +- src/shared/components/ResourceEditor/ResourceEditor.spec.tsx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/shared/components/ResourceEditor/ResourceEditor.less b/src/shared/components/ResourceEditor/ResourceEditor.less index d78da0b66..293486693 100644 --- a/src/shared/components/ResourceEditor/ResourceEditor.less +++ b/src/shared/components/ResourceEditor/ResourceEditor.less @@ -62,7 +62,7 @@ } .code-mirror-editor { - .fusion-resource-link { + .cm-fusion-resource-link { color: #0974ca !important; cursor: pointer !important; background-color: rgba(#0974ca, 0.12); diff --git a/src/shared/components/ResourceEditor/ResourceEditor.spec.tsx b/src/shared/components/ResourceEditor/ResourceEditor.spec.tsx index f33982c32..4a17a1a27 100644 --- a/src/shared/components/ResourceEditor/ResourceEditor.spec.tsx +++ b/src/shared/components/ResourceEditor/ResourceEditor.spec.tsx @@ -24,13 +24,11 @@ document.createRange = () => { describe('ResourceEditor', () => { it('check if code editor will be rendered in the screen', async () => { const editor = React.createRef(); - const onLinksFound = jest.fn(); const { queryByText, container } = render( {}} handleChange={() => {}} From 17e84626252da809b0200f1bf3677a2bacd4779f Mon Sep 17 00:00:00 2001 From: Bilal MEDDAH Date: Thu, 20 Jul 2023 11:00:42 +0200 Subject: [PATCH 4/6] fix: linting --- src/shared/components/ResourceEditor/editorUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shared/components/ResourceEditor/editorUtils.ts b/src/shared/components/ResourceEditor/editorUtils.ts index 83964f230..a7b916bce 100644 --- a/src/shared/components/ResourceEditor/editorUtils.ts +++ b/src/shared/components/ResourceEditor/editorUtils.ts @@ -118,17 +118,17 @@ export function getTokenAndPosAt(e: MouseEvent, current: CodeMirror.Editor) { } export const highlightUrlOverlay = (editor: CodeMirror.Editor) => { editor.addOverlay({ - token: function(stream: any) { - const rx_word = '" '; // Define what separates a word + token: (stream: any) => { + const rxWord = '" '; // Define what separates a word let ch = stream.peek(); let word = ''; - if (rx_word.includes(ch) || ch === '\uE000' || ch === '\uE001') { + if (rxWord.includes(ch) || ch === '\uE000' || ch === '\uE001') { stream.next(); return null; } - while ((ch = stream.peek()) && !rx_word.includes(ch)) { + while ((ch = stream.peek()) && !rxWord.includes(ch)) { word += ch; stream.next(); } From a66d0e91a76b31196fb4b5f2fee00286c120296f Mon Sep 17 00:00:00 2001 From: Bilal MEDDAH Date: Thu, 20 Jul 2023 11:19:56 +0200 Subject: [PATCH 5/6] fix: remove the highlight/action for the property path value --- src/shared/components/ResourceEditor/ResourceEditor.less | 2 +- src/shared/components/ResourceEditor/editorUtils.ts | 5 +++-- src/shared/components/ResourceEditor/useEditorTooltip.tsx | 7 +++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/shared/components/ResourceEditor/ResourceEditor.less b/src/shared/components/ResourceEditor/ResourceEditor.less index 293486693..ebfa1d05c 100644 --- a/src/shared/components/ResourceEditor/ResourceEditor.less +++ b/src/shared/components/ResourceEditor/ResourceEditor.less @@ -62,7 +62,7 @@ } .code-mirror-editor { - .cm-fusion-resource-link { + .cm-fusion-resource-link:not(.cm-property) { color: #0974ca !important; cursor: pointer !important; background-color: rgba(#0974ca, 0.12); diff --git a/src/shared/components/ResourceEditor/editorUtils.ts b/src/shared/components/ResourceEditor/editorUtils.ts index a7b916bce..45849d234 100644 --- a/src/shared/components/ResourceEditor/editorUtils.ts +++ b/src/shared/components/ResourceEditor/editorUtils.ts @@ -118,11 +118,12 @@ export function getTokenAndPosAt(e: MouseEvent, current: CodeMirror.Editor) { } export const highlightUrlOverlay = (editor: CodeMirror.Editor) => { editor.addOverlay({ - token: (stream: any) => { + token: (stream: any, tall: any, call: any) => { const rxWord = '" '; // Define what separates a word let ch = stream.peek(); let word = ''; - + // \uE001: end of line + // \uE000: start of line if (rxWord.includes(ch) || ch === '\uE000' || ch === '\uE001') { stream.next(); return null; diff --git a/src/shared/components/ResourceEditor/useEditorTooltip.tsx b/src/shared/components/ResourceEditor/useEditorTooltip.tsx index a86f4db0e..693c019cd 100644 --- a/src/shared/components/ResourceEditor/useEditorTooltip.tsx +++ b/src/shared/components/ResourceEditor/useEditorTooltip.tsx @@ -4,6 +4,7 @@ import clsx from 'clsx'; import { useNexusContext } from '@bbp/react-nexus'; import { useSelector } from 'react-redux'; import { + CODEMIRROR_HOVER_CLASS, TEditorPopoverResolvedData, editorLinkResolutionHandler, getTokenAndPosAt, @@ -15,8 +16,6 @@ import useResolutionActions from './useResolutionActions'; const downloadImg = require('../../images/DownloadingLoop.svg'); -export const CODEMIRROR_HOVER_CLASS = 'CodeMirror-hover-tooltip'; -export const CODEMIRROR_LINK_CLASS = 'fusion-resource-link'; type TTooltipCreator = Pick< TEditorPopoverResolvedData, 'error' | 'resolvedAs' | 'results' @@ -226,7 +225,7 @@ function useEditorTooltip({ async function onMouseOver(ev: MouseEvent) { const node = ev.target as HTMLElement; - if (node) { + if (node && !node.classList.contains('cm-property')) { const { url } = getTokenAndPosAt(ev, currentEditor); if (url && mayBeResolvableLink(url)) { node.classList.add('wait-for-tooltip'); @@ -338,7 +337,7 @@ function useEditorPopover({ async function onMouseDown(_: CodeMirror.Editor, ev: MouseEvent) { removeTooltipsFromDOM(); const node = ev.target as HTMLElement; - if (node) { + if (node && !node.classList.contains('cm-property')) { const { url } = getTokenAndPosAt(ev, currentEditor); if (url && mayBeResolvableLink(url)) { editorLinkResolutionHandler({ From 7a70357ab5eb5bf98469ae7639193712776e1404 Mon Sep 17 00:00:00 2001 From: Bilal MEDDAH Date: Thu, 20 Jul 2023 11:52:31 +0200 Subject: [PATCH 6/6] fix: clean code --- src/shared/components/ResourceEditor/editorUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/ResourceEditor/editorUtils.ts b/src/shared/components/ResourceEditor/editorUtils.ts index 45849d234..28448bd43 100644 --- a/src/shared/components/ResourceEditor/editorUtils.ts +++ b/src/shared/components/ResourceEditor/editorUtils.ts @@ -134,7 +134,7 @@ export const highlightUrlOverlay = (editor: CodeMirror.Editor) => { stream.next(); } - if (isClickableLine(word)) return CODEMIRROR_LINK_CLASS; // CSS class: cm-url + if (isClickableLine(word)) return CODEMIRROR_LINK_CLASS; return; }, });