From 8f2754e59e4b8a7df01503051bfaafc5da8d9d7b Mon Sep 17 00:00:00 2001 From: Ugo Palatucci Date: Tue, 7 Mar 2023 13:06:54 +0100 Subject: [PATCH] feat: highlight parts --- .../EnvironmentEditor/EnvironmentForm.tsx | 7 ++- .../SidebarEditor/SidebarEditor.tsx | 5 ++ .../SidebarEditor/useEditorHighlighter.ts | 39 ++++++++++++ src/utils/components/SidebarEditor/utils.ts | 60 +++++++++++++++++++ src/utils/resources/vm/utils/constants.ts | 20 +++++++ .../wizard/tabs/disks/WizardDisksTab.tsx | 7 ++- .../tabs/metadata/WizardMetadataTab.tsx | 7 ++- .../wizard/tabs/network/WizardNetworkTab.tsx | 7 ++- .../tabs/overview/WizardOverviewTab.tsx | 7 ++- .../tabs/scheduling/WizardSchedulingTab.tsx | 7 ++- .../wizard/tabs/scripts/WizardScriptsTab.tsx | 7 ++- .../VirtualMachineConfigurationTab.tsx | 2 +- .../tabs/configuration/disk/DiskListPage.tsx | 7 ++- .../network/NetworkInterfaceListPage.tsx | 7 ++- .../VirtualMachineSchedulingPage.tsx | 7 ++- .../tabs/configuration/scripts/ScriptsTab.tsx | 7 ++- .../details/VirtualMachineDetailsPage.tsx | 7 ++- 17 files changed, 197 insertions(+), 13 deletions(-) create mode 100644 src/utils/components/SidebarEditor/useEditorHighlighter.ts diff --git a/src/utils/components/EnvironmentEditor/EnvironmentForm.tsx b/src/utils/components/EnvironmentEditor/EnvironmentForm.tsx index 45dbbbfd99..85d54601c0 100644 --- a/src/utils/components/EnvironmentEditor/EnvironmentForm.tsx +++ b/src/utils/components/EnvironmentEditor/EnvironmentForm.tsx @@ -3,6 +3,7 @@ import { useImmer } from 'use-immer'; import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { Button, Form } from '@patternfly/react-core'; import { PlusCircleIcon } from '@patternfly/react-icons'; @@ -60,7 +61,11 @@ const EnvironmentForm: FC = ({ vm, onEditChange, updateVM if (!loaded) return ; return ( - resource={temporaryVM} onChange={setTemporaryVM}> + + resource={temporaryVM} + onChange={setTemporaryVM} + pathsToHighlight={PATHS_TO_HIGHLIGH.ENV_TAB} + >
{environments.length !== 0 && ( diff --git a/src/utils/components/SidebarEditor/SidebarEditor.tsx b/src/utils/components/SidebarEditor/SidebarEditor.tsx index c6182cafd1..50bb8cb1fe 100644 --- a/src/utils/components/SidebarEditor/SidebarEditor.tsx +++ b/src/utils/components/SidebarEditor/SidebarEditor.tsx @@ -20,6 +20,7 @@ import { import Loading from '../Loading/Loading'; import { SidebarEditorContext } from './SidebarEditorContext'; +import { useEditorHighlighter } from './useEditorHighlighter'; import { safeLoad } from './utils'; import './sidebar-editor.scss'; @@ -29,6 +30,7 @@ type SidebarEditorProps = { onResourceUpdate?: (newResource: Resource) => Promise; children: ReactNode | ReactNode[] | ((resource: Resource) => ReactNode); onChange?: (resource: Resource) => void; + pathsToHighlight?: string[]; }; const SidebarEditor = ({ @@ -36,6 +38,7 @@ const SidebarEditor = ({ resource, onResourceUpdate, onChange, + pathsToHighlight = ['spec.template.spec.domain.devices.disks', 'spec.template.spec.volumes'], }: SidebarEditorProps): JSX.Element => { const [editableYAML, setEditableYAML] = useState(''); const [loading, setLoading] = useState(false); @@ -50,6 +53,7 @@ const SidebarEditor = ({ const { showEditor, isEditable } = useContext(SidebarEditorContext); const editedResource = safeLoad(editableYAML); + const editorRef = useEditorHighlighter(editableYAML, pathsToHighlight, showEditor); const changeResource = (newValue: string) => { setEditableYAML(newValue); @@ -95,6 +99,7 @@ const SidebarEditor = ({ onChange={changeResource} onSave={() => onUpdate(editedResource)} options={{ readOnly: !isEditable }} + ref={editorRef} /> diff --git a/src/utils/components/SidebarEditor/useEditorHighlighter.ts b/src/utils/components/SidebarEditor/useEditorHighlighter.ts new file mode 100644 index 0000000000..aa8f43adc2 --- /dev/null +++ b/src/utils/components/SidebarEditor/useEditorHighlighter.ts @@ -0,0 +1,39 @@ +import { useEffect, useRef, useState } from 'react'; + +import { YAMLEditorRef } from '@openshift-console/dynamic-plugin-sdk'; + +import { createSelection, getLinesToHighlight } from './utils'; + +export const useEditorHighlighter = ( + editableYAML: string, + pathsToHighlight: string[], + showEditor: boolean, +) => { + const [editor, setEditor] = useState(); + const isHighlighed = useRef(false); + + useEffect(() => { + isHighlighed.current = false; + }, [pathsToHighlight, showEditor]); + + useEffect(() => { + const highlightPaths = async () => { + if (editor && editableYAML && pathsToHighlight && !isHighlighed.current) { + isHighlighed.current = true; + const ranges = getLinesToHighlight(editableYAML, pathsToHighlight); + + await editor.getAction('editor.foldAll').run(); + + const selections = ranges.map((range) => createSelection(range)); + + editor.setSelections(selections); + await editor.getAction('editor.unfoldRecursively').run(); + setTimeout(() => editor.revealLineInCenter(ranges.at(-1).start), 500); + } + }; + + highlightPaths(); + }, [editableYAML, editor, pathsToHighlight]); + + return (ref: YAMLEditorRef) => setEditor(ref?.editor); +}; diff --git a/src/utils/components/SidebarEditor/utils.ts b/src/utils/components/SidebarEditor/utils.ts index d23a89ca2e..6f8db57b59 100644 --- a/src/utils/components/SidebarEditor/utils.ts +++ b/src/utils/components/SidebarEditor/utils.ts @@ -8,3 +8,63 @@ export const safeLoad = (value: string): Resource | undefined => { return; } }; + +export type LineRange = { start: number; end: number }; + +const getLineFromPath = (resourceYAML: string, path): LineRange => { + const yamlLines = resourceYAML.split('\n'); + let startRange = 0; + let endRange = yamlLines.length - 1; + + const properties = path.split('.'); + + for (const propertyDepth in properties) { + const property = properties[propertyDepth]; + + // at every iteration, go one level deeper, remove initial indentation for that range. + const replaceIndentationRegex = new RegExp(`^[ ]{${2 * parseInt(propertyDepth)}}`); + + const rangeLines = yamlLines + .slice(startRange + 1, endRange) + .map((line) => line.replace(replaceIndentationRegex, '')); + + // find the property + const startPropertyRange = rangeLines.findIndex((line) => line.startsWith(`${property}:`)); + + // find next property at same depth level + let rangeLength = rangeLines + .slice(startPropertyRange + 1) + .findIndex((line) => line.match(/^[A-z]+:/g)); + + if (rangeLength === -1) rangeLength = rangeLines.length - startPropertyRange; + + // property not found + if (startPropertyRange === -1) return undefined; + + startRange += startPropertyRange + 1; + + endRange = startRange + rangeLength; + } + + // editor lines starts from 1, array starts from 0 + return { start: startRange + 1, end: endRange + 1 }; +}; + +export const getLinesToHighlight = ( + resourceYAML: string, + pathsToHighlight: string[], +): LineRange[] => + pathsToHighlight + .map((path) => getLineFromPath(resourceYAML, path)) + .filter((highlightLine) => !!highlightLine); + +export const createSelection = (range: LineRange) => ({ + startLineNumber: range.start, + startColumn: 0, + endLineNumber: range.end + 1, + endColumn: 0, + selectionStartLineNumber: range.start, + selectionStartColumn: 0, + positionLineNumber: range.end + 1, + positionColumn: 0, +}); diff --git a/src/utils/resources/vm/utils/constants.ts b/src/utils/resources/vm/utils/constants.ts index 3b369a8599..5e65897540 100644 --- a/src/utils/resources/vm/utils/constants.ts +++ b/src/utils/resources/vm/utils/constants.ts @@ -1,3 +1,23 @@ export const NO_DATA_DASH = '-'; export const MILLISECONDS_TO_SECONDS_MULTIPLIER = 1000; + +export const PATHS_TO_HIGHLIGH = { + DETAILS_TAB: [ + 'spec.template.metadata.annotations', + 'spec.template.metadata.labels', + 'spec.template.spec.domain.cpu', + 'spec.template.spec.domain.resources.requests', + 'metadata.labels', + ], + ENV_TAB: ['spec.template.spec.domain.devices.disks', 'spec.template.spec.volumes'], + DISKS_TAB: ['spec.template.spec.domain.devices.disks', 'spec.template.spec.volumes'], + NETWORK_TAB: ['spec.template.spec.networks', 'spec.template.spec.domain.devices.interfaces'], + SCHEDULING_TAB: [ + 'spec.template.spec.affinity', + 'spec.template.spec.tolerations', + 'spec.template.spec.nodeSelector', + 'spec.template.metadata.annotations', + ], + SCRIPTS_TAB: ['spec.template.spec.volumes', 'spec.template.spec.accessCredentials'], +}; diff --git a/src/views/catalog/wizard/tabs/disks/WizardDisksTab.tsx b/src/views/catalog/wizard/tabs/disks/WizardDisksTab.tsx index 98fe0d0527..8b5717922d 100644 --- a/src/views/catalog/wizard/tabs/disks/WizardDisksTab.tsx +++ b/src/views/catalog/wizard/tabs/disks/WizardDisksTab.tsx @@ -7,6 +7,7 @@ import SidebarEditor from '@kubevirt-utils/components/SidebarEditor/SidebarEdito import SidebarEditorSwitch from '@kubevirt-utils/components/SidebarEditor/SidebarEditorSwitch'; import WindowsDrivers from '@kubevirt-utils/components/WindowsDrivers/WindowsDrivers'; import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { ensurePath } from '@kubevirt-utils/utils/utils'; import { ListPageBody, @@ -36,7 +37,11 @@ const WizardDisksTab: WizardTab = ({ vm, loaded, updateVM, tabsData, updateTabsD return (
- updateVM(newVM)}> + updateVM(newVM)} + pathsToHighlight={PATHS_TO_HIGHLIGH.DISKS_TAB} + > { return ( - updateVM(newVM)}> + updateVM(newVM)} + pathsToHighlight={PATHS_TO_HIGHLIGH.DETAILS_TAB} + > {(resource) => ( <> diff --git a/src/views/catalog/wizard/tabs/network/WizardNetworkTab.tsx b/src/views/catalog/wizard/tabs/network/WizardNetworkTab.tsx index 198467aba4..cb09136105 100644 --- a/src/views/catalog/wizard/tabs/network/WizardNetworkTab.tsx +++ b/src/views/catalog/wizard/tabs/network/WizardNetworkTab.tsx @@ -5,6 +5,7 @@ import { useModal } from '@kubevirt-utils/components/ModalProvider/ModalProvider import SidebarEditor from '@kubevirt-utils/components/SidebarEditor/SidebarEditor'; import SidebarEditorSwitch from '@kubevirt-utils/components/SidebarEditor/SidebarEditorSwitch'; import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { ListPageBody, ListPageCreateButton } from '@openshift-console/dynamic-plugin-sdk'; import { Flex, FlexItem } from '@patternfly/react-core'; @@ -22,7 +23,11 @@ const WizardNetworkTab: WizardTab = ({ vm, updateVM }) => { return (
- updateVM(newVM)}> + updateVM(newVM)} + pathsToHighlight={PATHS_TO_HIGHLIGH.NETWORK_TAB} + > ( - updateVM(newVM)}> + updateVM(newVM)} + pathsToHighlight={PATHS_TO_HIGHLIGH.DETAILS_TAB} + > {(resource) => ( <> diff --git a/src/views/catalog/wizard/tabs/scheduling/WizardSchedulingTab.tsx b/src/views/catalog/wizard/tabs/scheduling/WizardSchedulingTab.tsx index 5e0e07c929..7f9c09b081 100644 --- a/src/views/catalog/wizard/tabs/scheduling/WizardSchedulingTab.tsx +++ b/src/views/catalog/wizard/tabs/scheduling/WizardSchedulingTab.tsx @@ -4,6 +4,7 @@ import { WizardTab } from '@catalog/wizard/tabs'; import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; import SidebarEditor from '@kubevirt-utils/components/SidebarEditor/SidebarEditor'; import SidebarEditorSwitch from '@kubevirt-utils/components/SidebarEditor/SidebarEditorSwitch'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { PageSection } from '@patternfly/react-core'; import WizardSchedulingGrid from './components/WizardSchedulingGrid'; @@ -13,7 +14,11 @@ import './wizard-scheduling-tab.scss'; const WizardSchedulingTab: WizardTab = ({ vm, updateVM }) => { return ( - resource={vm} onResourceUpdate={updateVM}> + + resource={vm} + onResourceUpdate={updateVM} + pathsToHighlight={PATHS_TO_HIGHLIGH.SCHEDULING_TAB} + > {(resource) => ( <> diff --git a/src/views/catalog/wizard/tabs/scripts/WizardScriptsTab.tsx b/src/views/catalog/wizard/tabs/scripts/WizardScriptsTab.tsx index 2550355eea..f686e66eaf 100644 --- a/src/views/catalog/wizard/tabs/scripts/WizardScriptsTab.tsx +++ b/src/views/catalog/wizard/tabs/scripts/WizardScriptsTab.tsx @@ -8,6 +8,7 @@ import { useModal } from '@kubevirt-utils/components/ModalProvider/ModalProvider import SidebarEditor from '@kubevirt-utils/components/SidebarEditor/SidebarEditor'; import SidebarEditorSwitch from '@kubevirt-utils/components/SidebarEditor/SidebarEditorSwitch'; import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { DescriptionList, Divider, PageSection } from '@patternfly/react-core'; import SSHKey from './components/SSHKey'; @@ -21,7 +22,11 @@ const WizardScriptsTab: WizardTab = ({ vm, updateVM }) => { return ( - updateVM(newVM)}> + updateVM(newVM)} + pathsToHighlight={PATHS_TO_HIGHLIGH.SCRIPTS_TAB} + > = onClick={() => redirectTab(name)} title={{title}} > - + {activeTabKey === name && } ))} diff --git a/src/views/virtualmachines/details/tabs/configuration/disk/DiskListPage.tsx b/src/views/virtualmachines/details/tabs/configuration/disk/DiskListPage.tsx index 766f2301ac..2804ea64ff 100644 --- a/src/views/virtualmachines/details/tabs/configuration/disk/DiskListPage.tsx +++ b/src/views/virtualmachines/details/tabs/configuration/disk/DiskListPage.tsx @@ -4,6 +4,7 @@ import { RouteComponentProps } from 'react-router'; import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel'; import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; import SidebarEditor from '@kubevirt-utils/components/SidebarEditor/SidebarEditor'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { k8sUpdate } from '@openshift-console/dynamic-plugin-sdk'; import DiskList from './tables/disk/DiskList'; @@ -32,7 +33,11 @@ const DiskListPage: React.FC = ({ obj }) => { return (
- + diff --git a/src/views/virtualmachines/details/tabs/configuration/network/NetworkInterfaceListPage.tsx b/src/views/virtualmachines/details/tabs/configuration/network/NetworkInterfaceListPage.tsx index a67ae92523..abc3a17dfe 100644 --- a/src/views/virtualmachines/details/tabs/configuration/network/NetworkInterfaceListPage.tsx +++ b/src/views/virtualmachines/details/tabs/configuration/network/NetworkInterfaceListPage.tsx @@ -8,6 +8,7 @@ import SidebarEditor from '@kubevirt-utils/components/SidebarEditor/SidebarEdito import SidebarEditorSwitch from '@kubevirt-utils/components/SidebarEditor/SidebarEditorSwitch'; import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation'; import { useVMIAndPodsForVM } from '@kubevirt-utils/resources/vm'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { k8sUpdate, ListPageBody, @@ -48,7 +49,11 @@ const NetworkInterfaceListPage: React.FC = ({ obj return (
- + return ( - + {(resource) => } diff --git a/src/views/virtualmachines/details/tabs/configuration/scripts/ScriptsTab.tsx b/src/views/virtualmachines/details/tabs/configuration/scripts/ScriptsTab.tsx index f18a481e0a..e1525bb34e 100644 --- a/src/views/virtualmachines/details/tabs/configuration/scripts/ScriptsTab.tsx +++ b/src/views/virtualmachines/details/tabs/configuration/scripts/ScriptsTab.tsx @@ -14,6 +14,7 @@ import SidebarEditorSwitch from '@kubevirt-utils/components/SidebarEditor/Sideba import { VMAuthorizedSSHKeyModal } from '@kubevirt-utils/components/VMAuthorizedSSHKeyModal/VMAuthorizedSSHKeyModal'; import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation'; import { asAccessReview } from '@kubevirt-utils/resources/shared'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { k8sUpdate, K8sVerb, @@ -70,7 +71,11 @@ const ScriptsTab: React.FC = ({ obj: vm }) => { return ( - + {(resource) => ( diff --git a/src/views/virtualmachines/details/tabs/details/VirtualMachineDetailsPage.tsx b/src/views/virtualmachines/details/tabs/details/VirtualMachineDetailsPage.tsx index 0dccefa602..d862f00416 100644 --- a/src/views/virtualmachines/details/tabs/details/VirtualMachineDetailsPage.tsx +++ b/src/views/virtualmachines/details/tabs/details/VirtualMachineDetailsPage.tsx @@ -4,6 +4,7 @@ import { RouteComponentProps } from 'react-router'; import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel'; import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; import SidebarEditor from '@kubevirt-utils/components/SidebarEditor/SidebarEditor'; +import { PATHS_TO_HIGHLIGH } from '@kubevirt-utils/resources/vm/utils/constants'; import { k8sUpdate } from '@openshift-console/dynamic-plugin-sdk'; import { Divider, PageSection } from '@patternfly/react-core'; @@ -33,7 +34,11 @@ const VirtualMachineDetailsPage: React.FC<VirtualMachineDetailsPageProps> = ({ o return ( <div> <PageSection> - <SidebarEditor resource={vm} onResourceUpdate={onChangeResource}> + <SidebarEditor + resource={vm} + onResourceUpdate={onChangeResource} + pathsToHighlight={PATHS_TO_HIGHLIGH.DETAILS_TAB} + > {(resource) => <DetailsSection vm={resource} pathname={location?.pathname} />} </SidebarEditor> </PageSection>