Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions datajunction-ui/src/app/pages/NodePage/__tests__/NodePage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,62 @@ vi.mock('recharts', () => ({
Tooltip: () => null,
}));

// Mock reactflow (lineage/dimensions/graph tabs). Tabs are lazy-loaded, so
// reactflow is dynamically imported mid-render; loading the real lib inside
// jsdom during render blocks the event loop. The dedicated tab tests cover
// reactflow rendering — here we only need NodePage to mount the right tab.
vi.mock('reactflow/dist/style.css', () => ({}));
vi.mock('reactflow', async () => {
const React = await vi.importActual('react');
const { useState } = React;
const Passthrough = ({ children }) => <div>{children}</div>;
return {
__esModule: true,
default: ({ children, nodes, nodeTypes }) => (
<div data-testid="reactflow">
{(nodes || []).map(n => {
const NodeComp = nodeTypes?.[n.type];
return NodeComp ? (
<NodeComp key={n.id} data={n.data} />
) : (
<div key={n.id}>{n.id}</div>
);
})}
{children}
</div>
),
ReactFlowProvider: Passthrough,
Panel: Passthrough,
Background: () => null,
Controls: () => null,
MiniMap: () => null,
Handle: () => null,
Position: { Left: 'left', Right: 'right', Top: 'top', Bottom: 'bottom' },
MarkerType: { ArrowClosed: 'arrowclosed', Arrow: 'arrow' },
useNodesState: initial => {
const [n, s] = useState(initial || []);
return [n, s, () => {}];
},
useEdgesState: initial => {
const [e, s] = useState(initial || []);
return [e, s, () => {}];
},
addEdge: (params, eds) => [...(eds || []), params],
};
});

// Mock CodeMirror (materialization config editor). Like reactflow, it's pulled
// in via a lazy tab and dynamically importing it (plus every language grammar
// from codemirror-extensions-langs) mid-render blocks jsdom. The materialization
// tab's own tests cover the editor.
vi.mock('@uiw/react-codemirror', () => ({
__esModule: true,
default: ({ value }) => <div data-testid="codemirror">{value}</div>,
}));
vi.mock('@uiw/codemirror-extensions-langs', () => ({
langs: new Proxy({}, { get: () => () => [] }),
}));

// ResizeObserver is not defined in jsdom
global.ResizeObserver = function (callback) {
return { observe: () => {}, disconnect: () => {}, unobserve: () => {} };
Expand Down
28 changes: 16 additions & 12 deletions datajunction-ui/src/app/pages/NodePage/index.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import * as React from 'react';
import { useParams } from 'react-router-dom';
import { useContext, useEffect, useState } from 'react';
import { useContext, useEffect, useState, lazy, Suspense } from 'react';
import Tab from '../../components/Tab';
import NamespaceHeader from '../../components/NamespaceHeader';
import NodeInfoTab from './NodeInfoTab';
import NodeColumnTab from './NodeColumnTab';
import NodeDataFlowTab from './NodeDataFlowTab';
import NodeDimensionsTab from './NodeDimensionsTab';
import NodeHistory from './NodeHistory';
import NotebookDownload from './NotebookDownload';
import DJClientContext from '../../providers/djclient';
import NodeMaterializationTab from './NodeMaterializationTab';
import NodePreAggregationsTab from './NodePreAggregationsTab';
import ClientCodePopover from './ClientCodePopover';
import WatchButton from './WatchNodeButton';
import NodesWithDimension from './NodesWithDimension';
import NodeColumnLineage from './NodeLineageTab';
import EditIcon from '../../icons/EditIcon';
import ChartIcon from '../../icons/ChartIcon';
import AlertIcon from '../../icons/AlertIcon';
import LoadingIcon from '../../icons/LoadingIcon';
import NodeDependenciesTab from './NodeDependenciesTab';
import { useNavigate } from 'react-router-dom';

// Tabs are lazy-loaded so a node page only pulls the deps of the tab actually
// shown. Otherwise every visit loads reactflow (lineage/dimensions/graph),
// recharts (data flow) and codemirror (materialization) even for the Info tab.
const NodeInfoTab = lazy(() => import('./NodeInfoTab'));
const NodeColumnTab = lazy(() => import('./NodeColumnTab'));
const NodeDataFlowTab = lazy(() => import('./NodeDataFlowTab'));
const NodeDimensionsTab = lazy(() => import('./NodeDimensionsTab'));
const NodeHistory = lazy(() => import('./NodeHistory'));
const NodeMaterializationTab = lazy(() => import('./NodeMaterializationTab'));
const NodePreAggregationsTab = lazy(() => import('./NodePreAggregationsTab'));
const NodesWithDimension = lazy(() => import('./NodesWithDimension'));
const NodeColumnLineage = lazy(() => import('./NodeLineageTab'));
const NodeDependenciesTab = lazy(() => import('./NodeDependenciesTab'));

export function NodePage() {
const djClient = useContext(DJClientContext).DataJunctionAPI;
const navigate = useNavigate();
Expand Down Expand Up @@ -317,7 +321,7 @@ export function NodePage() {
</span>
</div>
<div className="dj-tabs-bar">{tabsList(node).map(buildTabs)}</div>
{tabToDisplay}
<Suspense fallback={<LoadingIcon />}>{tabToDisplay}</Suspense>
</div>
) : node?.message !== undefined ? (
<div className="message alert" style={{ margin: '20px' }}>
Expand Down
Loading