diff --git a/src/actions/pipelines.js b/src/actions/pipelines.js index 8882b425d8..aabdf139a8 100644 --- a/src/actions/pipelines.js +++ b/src/actions/pipelines.js @@ -98,8 +98,7 @@ export function loadInitialPipelineData() { const url = getUrl('main'); // obtain the status of expandAllPipelines to decide whether it needs to overwrite the // list of visible nodes - const expandAllPipelines = - state.display.expandAllPipelines || state.expandAllPipelines; + const expandAllPipelines = state.expandAllPipelines; let newState = await loadJsonData(url).then((data) => preparePipelineState(data, true, expandAllPipelines) ); @@ -122,7 +121,7 @@ export function loadInitialPipelineData() { */ export function loadPipelineData(pipelineID) { return async function (dispatch, getState) { - const { dataSource, pipeline, display, expandAllPipelines } = getState(); + const { dataSource, pipeline, expandAllPipelines } = getState(); if (pipelineID && pipelineID === pipeline.active) { return; @@ -136,10 +135,8 @@ export function loadPipelineData(pipelineID) { active: pipelineID, }); - const shouldExpandAllPipelines = - display.expandAllPipelines || expandAllPipelines; const newState = await loadJsonData(url).then((data) => - preparePipelineState(data, false, shouldExpandAllPipelines) + preparePipelineState(data, false, expandAllPipelines) ); // Set active pipeline here rather than dispatching two separate actions, diff --git a/src/components/app/app.js b/src/components/app/app.js index ba70cfb8af..eb20c97bef 100644 --- a/src/components/app/app.js +++ b/src/components/app/app.js @@ -91,24 +91,18 @@ App.propTypes = { */ theme: PropTypes.oneOf(['dark', 'light']), /** - * Override visibility of various features, e.g. icon buttons - */ - visible: PropTypes.shape({ - labelBtn: PropTypes.bool, - layerBtn: PropTypes.bool, - exportBtn: PropTypes.bool, - pipelineBtn: PropTypes.bool, - sidebar: PropTypes.bool, - }), - /** - * Determines if certain elements are displayed, e.g global tool bar, sidebar + * Determines if certain elements are displayed, e.g globalNavigation, sidebar */ display: PropTypes.shape({ - globalToolbar: PropTypes.bool, + globalNavigation: PropTypes.bool, sidebar: PropTypes.bool, miniMap: PropTypes.bool, - expandAllPipelines: PropTypes.bool, + expandPipelinesBtn: PropTypes.bool, + exportBtn: PropTypes.bool, + labelBtn: PropTypes.bool, + layerBtn: PropTypes.bool, }), + expandAllPipelines: PropTypes.bool, }; export default App; diff --git a/src/components/export-modal/export-modal.js b/src/components/export-modal/export-modal.js index 1b58856e41..a4665205ff 100644 --- a/src/components/export-modal/export-modal.js +++ b/src/components/export-modal/export-modal.js @@ -10,9 +10,6 @@ import './export-modal.scss'; * Modal to allow users to choose between SVG/PNG export formats */ const ExportModal = ({ graphSize, theme, onToggle, visible }) => { - if (!visible.exportBtn) { - return null; - } return ( onToggle(false)} diff --git a/src/components/export-modal/export-modal.test.js b/src/components/export-modal/export-modal.test.js index be2ab2dfd7..29dd0aa727 100644 --- a/src/components/export-modal/export-modal.test.js +++ b/src/components/export-modal/export-modal.test.js @@ -29,7 +29,6 @@ describe('ExportModal', () => { }), theme: expect.stringMatching(/light|dark/), visible: expect.objectContaining({ - exportBtn: expect.any(Boolean), exportModal: expect.any(Boolean), settingsModal: expect.any(Boolean), }), diff --git a/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.js b/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.js index d40e6ecfeb..c6210f87f7 100644 --- a/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.js +++ b/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.js @@ -32,6 +32,7 @@ export const FlowchartPrimaryToolbar = ({ onToggleTextLabels, textLabels, visible, + display, visibleLayers, expandedPipelines, onToggleExpandAllPipelines, @@ -59,7 +60,7 @@ export const FlowchartPrimaryToolbar = ({ icon={LabelIcon} labelText={`${textLabels ? 'Hide' : 'Show'} text labels`} onClick={() => onToggleTextLabels(!textLabels)} - visible={visible.labelBtn} + visible={display.labelBtn} /> onToggleLayers(!visibleLayers)} - visible={visible.layerBtn} + visible={display.layerBtn} /> onToggleExportModal(true)} - visible={visible.exportBtn} + visible={display.exportBtn} /> @@ -109,6 +110,7 @@ export const mapStateToProps = (state) => ({ displaySidebar: state.display.sidebar, textLabels: state.textLabels, visible: state.visible, + display: state.display, visibleLayers: Boolean(getVisibleLayerIDs(state).length), expandedPipelines: state.expandAllPipelines, }); diff --git a/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.test.js b/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.test.js index a6f328c8a5..75c80e0c6d 100644 --- a/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.test.js +++ b/src/components/flowchart-primary-toolbar/flowchart-primary-toolbar.test.js @@ -18,25 +18,25 @@ describe('PrimaryToolbar', () => { expect(wrapper.find('.pipeline-icon-toolbar__button').length).toBe(5); }); - it('hides all buttons (except menu button) when visible prop is false for each of them', () => { - const visible = { + it('hides all buttons (except menu button) when display prop is false for each of them', () => { + const display = { labelBtn: false, layerBtn: false, exportBtn: false, - pipelineBtn: false, + expandPipelinesBtn: false, }; const wrapper = setup.mount(, { - visible, + display, }); expect(wrapper.find('.pipeline-icon-toolbar__button').length).toBe(1); }); - it('hides one button when visible prop is false for one of them', () => { - const visible = { + it('hides one button when display prop is false for one of them', () => { + const display = { labelBtn: false, }; const wrapper = setup.mount(, { - visible, + display, }); expect(wrapper.find('.pipeline-icon-toolbar__button').length).toBe(4); }); @@ -57,6 +57,7 @@ describe('PrimaryToolbar', () => { displaySidebar: true, textLabels: mockState.spaceflights.textLabels, visible: mockState.spaceflights.visible, + display: mockState.spaceflights.display, [callback]: mockFn, }; const wrapper = setup.mount(); @@ -73,14 +74,16 @@ describe('PrimaryToolbar', () => { expandedPipelines: expect.any(Boolean), displaySidebar: true, visible: expect.objectContaining({ - exportBtn: expect.any(Boolean), exportModal: expect.any(Boolean), metadataModal: expect.any(Boolean), settingsModal: expect.any(Boolean), + sidebar: expect.any(Boolean), + }), + display: expect.objectContaining({ + exportBtn: expect.any(Boolean), labelBtn: expect.any(Boolean), layerBtn: expect.any(Boolean), - pipelineBtn: expect.any(Boolean), - sidebar: expect.any(Boolean), + expandPipelinesBtn: expect.any(Boolean), }), visibleLayers: expect.any(Boolean), }; diff --git a/src/components/flowchart-wrapper/flowchart-wrapper.js b/src/components/flowchart-wrapper/flowchart-wrapper.js index bbc04812f1..9353b0de2a 100644 --- a/src/components/flowchart-wrapper/flowchart-wrapper.js +++ b/src/components/flowchart-wrapper/flowchart-wrapper.js @@ -58,6 +58,7 @@ export const FlowChartWrapper = ({ pipelines, sidebarVisible, activePipeline, + displayExportBtn, }) => { const history = useHistory(); const { pathname, search } = useLocation(); @@ -349,7 +350,7 @@ export const FlowChartWrapper = ({ {isRunningLocally() ? null : } - + {displayExportBtn && } ); @@ -366,6 +367,7 @@ export const mapStateToProps = (state) => ({ pipelines: state.pipeline.ids, activePipeline: state.pipeline.active, sidebarVisible: state.visible.sidebar, + displayExportBtn: state.display.exportBtn, }); export const mapDispatchToProps = (dispatch) => ({ diff --git a/src/components/flowchart/flowchart.js b/src/components/flowchart/flowchart.js index 1590a7823e..cdc87ce2e6 100644 --- a/src/components/flowchart/flowchart.js +++ b/src/components/flowchart/flowchart.js @@ -596,7 +596,7 @@ export class FlowChart extends Component { * Render React elements */ render() { - const { chartSize, layers, visibleGraph, displayGlobalToolbar } = + const { chartSize, layers, visibleGraph, displayGlobalNavigation } = this.props; const { outerWidth = 0, outerHeight = 0 } = chartSize; @@ -656,7 +656,7 @@ export class FlowChart extends Component { className={classnames('pipeline-flowchart__layer-names', { 'pipeline-flowchart__layer-names--visible': layers.length, 'pipeline-flowchart__layer-names--no-global-toolbar': - !displayGlobalToolbar, + !displayGlobalNavigation, })} ref={this.layerNamesRef} /> @@ -689,7 +689,7 @@ export const mapStateToProps = (state, ownProps) => ({ clickedNode: state.node.clicked, chartSize: getChartSize(state), chartZoom: getChartZoom(state), - displayGlobalToolbar: state.display.globalToolbar, + displayGlobalNavigation: state.display.globalNavigation, edges: state.graph.edges || emptyEdges, focusMode: state.visible.modularPipelineFocusMode, graphSize: state.graph.size || emptyGraphSize, diff --git a/src/components/flowchart/flowchart.test.js b/src/components/flowchart/flowchart.test.js index d350c80dd5..602292805c 100644 --- a/src/components/flowchart/flowchart.test.js +++ b/src/components/flowchart/flowchart.test.js @@ -40,14 +40,14 @@ const mockChartSize = ( describe('FlowChart', () => { it('renders without crashing', () => { const svg = setup - .mount() + .mount() .find('svg'); expect(svg.length).toEqual(1); expect(svg.hasClass('pipeline-flowchart__graph')).toBe(true); }); it('renders nodes with D3', () => { - const wrapper = setup.mount(); + const wrapper = setup.mount(); const nodes = wrapper.render().find('.pipeline-node'); const nodeNames = nodes.map((i, el) => select(el).text()).get(); const mockNodes = getVisibleNodeIDs(mockState.spaceflights); @@ -59,7 +59,7 @@ describe('FlowChart', () => { }); it('a transform to fit the graph in container was applied', () => { - const wrapper = setup.mount(); + const wrapper = setup.mount(); const instance = wrapper.find('FlowChart').instance(); const viewTransform = getViewTransform(instance.view); @@ -87,7 +87,7 @@ describe('FlowChart', () => { }); const wrapper = setup.mount( - + ); const instance = wrapper.find('FlowChart').instance(); const viewExtents = getViewExtents(instance.view); @@ -130,7 +130,7 @@ describe('FlowChart', () => { }); const wrapper = setup.mount( - + ); const instance = wrapper.find('FlowChart').instance(); const viewExtents = getViewExtents(instance.view); @@ -181,7 +181,7 @@ describe('FlowChart', () => { window.addEventListener = jest.fn((event, callback) => { map[event] = callback; }); - const wrapper = setup.mount(); + const wrapper = setup.mount(); const spy = jest.spyOn( wrapper.find('FlowChart').instance(), 'updateChartSize' @@ -242,7 +242,7 @@ describe('FlowChart', () => { it('applies active class to nodes when nodeActive prop set', () => { const wrapper = setup.mount( { it('applies collapsed-hint class to nodes with input parameters are hovered during collapsed state', () => { const wrapper = setup.mount( { it('applies parameter-indicator--visible class to nodes with input parameters when nodeDisabled prop set', () => { const wrapper = setup.mount( { it('applies pipeline-node--dataset-input class to input dataset nodes under focus mode', () => { const wrapper = setup.mount( { it('applies pipeline-edge--dataset--input class to input dataset edges under focus mode', () => { const wrapper = setup.mount( { it('applies pipeline-node-input--active class to input/outout nodes when hovering over them under focus mode', () => { const wrapper = setup.mount( { it('applies pipeline-node-input--selected class to input/outout nodes when selecting one of them under focus mode', () => { const wrapper = setup.mount( { it('applies pipeline-node--parameter-input class to input parameter nodes under focus mode', () => { const wrapper = setup.mount( { }); it('getHoveredParameterLabel returns parameter count when there are more than 1 hidden parameters ', () => { - const wrapper = setup.mount(); + const wrapper = setup.mount(); const parameterNames = ['params1', 'params2']; const instance = wrapper.find('FlowChart').instance(); const label = instance.getHoveredParameterLabel(parameterNames); @@ -408,7 +408,7 @@ describe('FlowChart', () => { }); it('getHoveredParameterLabel returns parameter name when there is 1 hidden parameter ', () => { - const wrapper = setup.mount(); + const wrapper = setup.mount(); const parameterNames = ['params1']; const instance = wrapper.find('FlowChart').instance(); const label = instance.getHoveredParameterLabel(parameterNames); @@ -416,13 +416,13 @@ describe('FlowChart', () => { }); it('shows layers when layers are visible', () => { - const wrapper = setup.mount(); + const wrapper = setup.mount(); expect(wrapper.render().find('.pipeline-layer').length).toBe(2); }); it('hides layers when layers.length is 0', () => { const wrapper = setup.mount( - + ); expect(wrapper.render().find('.pipeline-layer').length).toBe(0); }); @@ -430,7 +430,7 @@ describe('FlowChart', () => { it('shows tooltip when tooltip prop set as visible', () => { const wrapper = setup.mount( { it('hides tooltip when tooltip prop not set as visible', () => { const wrapper = setup.mount( { inputOutputDataNodes: expect.any(Object), inputOutputDataEdges: expect.any(Object), focusMode: expect.any(Object), - displayGlobalToolbar: expect.any(Boolean), + displayGlobalNavigation: expect.any(Boolean), }; expect(mapStateToProps(mockState.spaceflights)).toEqual(expectedResult); }); @@ -532,7 +532,7 @@ describe('map dispatch props to async actions', () => { it('applies faded class to all nodes that are not included in the hovered focus mode icon pipeline', () => { const wrapper = setup.mount( { theme: expect.stringMatching(/light|dark/), visible: { code: false, - exportBtn: true, exportModal: false, graph: true, - labelBtn: true, - layerBtn: true, miniMap: true, - miniMapBtn: true, modularPipelineFocusMode: null, metadataModal: false, - pipelineBtn: true, settingsModal: false, shareableUrlModal: false, sidebar: true, diff --git a/src/components/minimap-toolbar/minimap-toolbar.js b/src/components/minimap-toolbar/minimap-toolbar.js index dc706b09f3..6ac462ee1f 100644 --- a/src/components/minimap-toolbar/minimap-toolbar.js +++ b/src/components/minimap-toolbar/minimap-toolbar.js @@ -34,7 +34,7 @@ export const MiniMapToolbar = ({ id="minimap-toggle-icon" labelText={`${visible.miniMap ? 'Hide' : 'Show'} minimap`} onClick={() => onToggleMiniMap(!visible.miniMap)} - visible={visible.miniMapBtn} + visible={displayMiniMap} /> )} { chartZoom: expect.any(Object), visible: expect.objectContaining({ miniMap: expect.any(Boolean), - miniMapBtn: expect.any(Boolean), }), }; expect(mapStateToProps(mockState.spaceflights)).toEqual(expectedResult); diff --git a/src/components/minimap/minimap.js b/src/components/minimap/minimap.js index 52f180f786..07d5223219 100644 --- a/src/components/minimap/minimap.js +++ b/src/components/minimap/minimap.js @@ -95,14 +95,14 @@ export class MiniMap extends Component { * Updates drawing and zoom if props have changed */ update(prevProps = {}) { - const { visible, chartZoom } = this.props; + const { miniMapVisible, chartZoom } = this.props; - if (visible) { + if (miniMapVisible) { const changed = (...names) => this.changed(names, prevProps, this.props); if ( changed( - 'visible', + 'miniMapVisible', 'nodes', 'clickedNodes', 'linkedNodes', @@ -113,11 +113,11 @@ export class MiniMap extends Component { drawNodes.call(this); } - if (changed('visible', 'chartZoom') && chartZoom.applied) { + if (changed('miniMapVisible', 'chartZoom') && chartZoom.applied) { drawViewport.call(this); } - if (changed('visible', 'nodes', 'textLabels', 'chartSize')) { + if (changed('miniMapVisible', 'nodes', 'textLabels', 'chartSize')) { this.resetView(); } } @@ -329,7 +329,11 @@ export class MiniMap extends Component { return (
({ - visible: state.visible.miniMap, + miniMapVisible: state.visible.miniMap, + displayMiniMap: state.display.miniMap, mapSize: getMapSize(state), clickedNode: state.node.clicked, chartSize: getChartSize(state), diff --git a/src/components/minimap/minimap.test.js b/src/components/minimap/minimap.test.js index cfd10229c3..ce735bb343 100644 --- a/src/components/minimap/minimap.test.js +++ b/src/components/minimap/minimap.test.js @@ -39,7 +39,7 @@ describe('MiniMap', () => { }); it('does not update nodes when not visible', () => { - const wrapper = setup.mount(); + const wrapper = setup.mount(); const nodes = wrapper.render().find('.pipeline-minimap-node'); expect(nodes.length).toEqual(0); }); @@ -144,7 +144,8 @@ describe('MiniMap', () => { it('maps state to props', () => { const expectedResult = { - visible: expect.any(Boolean), + miniMapVisible: expect.any(Boolean), + displayMiniMap: expect.any(Boolean), mapSize: expect.any(Object), clickedNode: null, chartSize: expect.any(Object), diff --git a/src/components/settings-modal/settings-modal.test.js b/src/components/settings-modal/settings-modal.test.js index 43e723644a..749e2889e5 100644 --- a/src/components/settings-modal/settings-modal.test.js +++ b/src/components/settings-modal/settings-modal.test.js @@ -42,7 +42,6 @@ describe('SettingsModal', () => { it('maps state to props', () => { const expectedResult = { visible: expect.objectContaining({ - exportBtn: expect.any(Boolean), exportModal: expect.any(Boolean), settingsModal: expect.any(Boolean), }), diff --git a/src/components/sidebar/sidebar.js b/src/components/sidebar/sidebar.js index 037d19369e..653a104ba7 100644 --- a/src/components/sidebar/sidebar.js +++ b/src/components/sidebar/sidebar.js @@ -17,7 +17,7 @@ import './sidebar.scss'; */ export const Sidebar = ({ disableRunSelection, - displayGlobalToolbar, + displayGlobalNavigation, displaySidebar, enableComparisonView, enableShowChanges, @@ -83,7 +83,7 @@ export const Sidebar = ({
@@ -102,7 +102,7 @@ export const Sidebar = ({ }; const mapStateToProps = (state) => ({ - displayGlobalToolbar: state.display.globalToolbar, + displayGlobalNavigation: state.display.globalNavigation, displaySidebar: state.display.sidebar, visible: state.visible.sidebar, }); diff --git a/src/components/wrapper/wrapper.js b/src/components/wrapper/wrapper.js index a90e83778a..37f289fe0f 100644 --- a/src/components/wrapper/wrapper.js +++ b/src/components/wrapper/wrapper.js @@ -21,10 +21,10 @@ import './wrapper.scss'; /** * Main app container. Handles showing/hiding the sidebar nav, and theme classes. */ -export const Wrapper = ({ displayGlobalToolbar, theme }) => { +export const Wrapper = ({ displayGlobalNavigation, theme }) => { const { data: versionData } = useApolloQuery(GET_VERSIONS, { client, - skip: !displayGlobalToolbar || !isRunningLocally(), + skip: !displayGlobalNavigation || !isRunningLocally(), }); const [isOutdated, setIsOutdated] = useState(false); const [latestVersion, setLatestVersion] = useState(null); @@ -45,7 +45,7 @@ export const Wrapper = ({ displayGlobalToolbar, theme }) => { >

Kedro-Viz

- {displayGlobalToolbar ? ( + {displayGlobalNavigation ? ( { }; export const mapStateToProps = (state) => ({ - displayGlobalToolbar: state.display.globalToolbar, + displayGlobalNavigation: state.display.globalNavigation, theme: state.theme, }); diff --git a/src/components/wrapper/wrapper.test.js b/src/components/wrapper/wrapper.test.js index e751b64c8f..af8411aa5f 100644 --- a/src/components/wrapper/wrapper.test.js +++ b/src/components/wrapper/wrapper.test.js @@ -3,12 +3,12 @@ import { mockState, setup } from '../../utils/state.mock'; const { theme } = mockState.spaceflights; const mockProps = { - displayGlobalToolbar: true, + displayGlobalNavigation: true, theme, }; const mockPropsNoGlobalToolbar = { - displayGlobalToolbar: false, + displayGlobalNavigation: false, theme, }; @@ -26,14 +26,14 @@ describe('Wrapper', () => { expect(container.hasClass(`kui-theme--dark`)).toBe(theme === 'dark'); }); - it('only displays the h1 and the FlowChartWrapper when displayGlobalToolbar is false', () => { + it('only displays the h1 and the FlowChartWrapper when displayGlobalNavigation is false', () => { const wrapper = setup.shallow(Wrapper, mockPropsNoGlobalToolbar); expect(wrapper.children()).toHaveLength(2); }); it('maps state to props', () => { expect(mapStateToProps(mockState.spaceflights)).toEqual({ - displayGlobalToolbar: true, + displayGlobalNavigation: true, theme, }); }); diff --git a/src/store/initial-state.js b/src/store/initial-state.js index 9e65f9497f..8ca9197d94 100644 --- a/src/store/initial-state.js +++ b/src/store/initial-state.js @@ -32,25 +32,23 @@ export const createInitialState = () => ({ }, visible: { code: false, - exportBtn: true, exportModal: false, graph: true, - labelBtn: true, - layerBtn: true, metadataModal: false, miniMap: true, - miniMapBtn: true, modularPipelineFocusMode: null, - pipelineBtn: true, settingsModal: false, shareableUrlModal: false, sidebar: window.innerWidth > sidebarWidth.breakpoint, }, display: { - globalToolbar: true, + globalNavigation: true, sidebar: true, miniMap: true, - expandAllPipelines: false, + expandPipelinesBtn: true, + exportBtn: true, + labelBtn: true, + layerBtn: true, }, zoom: {}, runsMetadata: {}, @@ -251,9 +249,7 @@ const getInitialState = (props = {}) => { const urlParams = parseUrlParameters(); const nonPipelineState = prepareNonPipelineState(props, urlParams); - const expandAllPipelines = - nonPipelineState.display.expandAllPipelines || - nonPipelineState.expandAllPipelines; + const expandAllPipelines = nonPipelineState.expandAllPipelines; const pipelineState = preparePipelineState( props.data, diff --git a/src/store/initial-state.test.js b/src/store/initial-state.test.js index 2ac6a3fb5f..c35259ec69 100644 --- a/src/store/initial-state.test.js +++ b/src/store/initial-state.test.js @@ -132,11 +132,11 @@ describe('getInitialState', () => { textLabels: true, theme: 'dark', expandAllPipelines: false, - visible: { + display: { exportBtn: true, labelBtn: true, layerBtn: true, - pipelineBtn: true, + expandPipelinesBtn: true, }, }); }); @@ -149,7 +149,7 @@ describe('getInitialState', () => { }) ).toMatchObject({ theme: 'light', - visible: { labelBtn: true }, + display: { labelBtn: true }, }); }); diff --git a/tools/test-lib/react-app/app.js b/tools/test-lib/react-app/app.js index ad6565f6c4..422dad81c4 100644 --- a/tools/test-lib/react-app/app.js +++ b/tools/test-lib/react-app/app.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import KedroViz from '@quantumblack/kedro-viz'; -import VizComponent from './VizComponent'; +import VizComponent from './viz-component'; import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; import spaceflights from '@quantumblack/kedro-viz/lib/utils/data/spaceflights.mock.json'; import demo from '@quantumblack/kedro-viz/lib/utils/data/demo.mock.json'; diff --git a/tools/test-lib/react-app/VizComponent.js b/tools/test-lib/react-app/viz-component.js similarity index 84% rename from tools/test-lib/react-app/VizComponent.js rename to tools/test-lib/react-app/viz-component.js index 8a10c5330a..62b0356759 100644 --- a/tools/test-lib/react-app/VizComponent.js +++ b/tools/test-lib/react-app/viz-component.js @@ -6,9 +6,9 @@ function VizComponent({ data }) {