mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Variables: Make renamed or missing variable section expandable
* Chore: feedback after PR comments
(cherry picked from commit 44d7d6546f
)
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
This commit is contained in:
parent
272f850fb2
commit
fcd0c382b6
@ -5,20 +5,26 @@ import { Icon } from '..';
|
|||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
label: string;
|
label: ReactNode;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
/** Callback for the toggle functionality */
|
||||||
|
onToggle?: (isOpen: boolean) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CollapsableSection: FC<Props> = ({ label, isOpen, children }) => {
|
export const CollapsableSection: FC<Props> = ({ label, isOpen, onToggle, children }) => {
|
||||||
const [open, toggleOpen] = useState<boolean>(isOpen);
|
const [open, toggleOpen] = useState<boolean>(isOpen);
|
||||||
const styles = useStyles2(collapsableSectionStyles);
|
const styles = useStyles2(collapsableSectionStyles);
|
||||||
const headerStyle = open ? styles.header : styles.headerCollapsed;
|
const headerStyle = open ? styles.header : styles.headerCollapsed;
|
||||||
const tooltip = `Click to ${open ? 'collapse' : 'expand'}`;
|
const tooltip = `Click to ${open ? 'collapse' : 'expand'}`;
|
||||||
|
const onClick = () => {
|
||||||
|
onToggle?.(!open);
|
||||||
|
toggleOpen(!open);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div onClick={() => toggleOpen(!open)} className={headerStyle} title={tooltip}>
|
<div onClick={onClick} className={headerStyle} title={tooltip}>
|
||||||
{label}
|
{label}
|
||||||
<Icon name={open ? 'angle-down' : 'angle-right'} size="xl" className={styles.icon} />
|
<Icon name={open ? 'angle-down' : 'angle-right'} size="xl" className={styles.icon} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,10 +17,7 @@ const mapStateToProps = (state: StoreState) => ({
|
|||||||
variables: getEditorVariables(state),
|
variables: getEditorVariables(state),
|
||||||
idInEditor: state.templating.editor.id,
|
idInEditor: state.templating.editor.id,
|
||||||
dashboard: state.dashboard.getModel(),
|
dashboard: state.dashboard.getModel(),
|
||||||
unknownsNetwork: state.templating.inspect.unknownsNetwork,
|
|
||||||
unknownExists: state.templating.inspect.unknownExits,
|
|
||||||
usagesNetwork: state.templating.inspect.usagesNetwork,
|
usagesNetwork: state.templating.inspect.usagesNetwork,
|
||||||
unknown: state.templating.inspect.unknown,
|
|
||||||
usages: state.templating.inspect.usages,
|
usages: state.templating.inspect.usages,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -119,7 +116,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
|
|||||||
usages={this.props.usages}
|
usages={this.props.usages}
|
||||||
usagesNetwork={this.props.usagesNetwork}
|
usagesNetwork={this.props.usagesNetwork}
|
||||||
/>
|
/>
|
||||||
{this.props.unknownExists ? <VariablesUnknownTable usages={this.props.unknownsNetwork} /> : null}
|
<VariablesUnknownTable variables={this.props.variables} dashboard={this.props.dashboard} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{variableToEdit && <VariableEditorEditor identifier={toVariableIdentifier(variableToEdit)} />}
|
{variableToEdit && <VariableEditorEditor identifier={toVariableIdentifier(variableToEdit)} />}
|
||||||
|
@ -109,12 +109,10 @@ export const switchToListMode = (): ThunkResult<void> => (dispatch, getState) =>
|
|||||||
const state = getState();
|
const state = getState();
|
||||||
const variables = getEditorVariables(state);
|
const variables = getEditorVariables(state);
|
||||||
const dashboard = state.dashboard.getModel();
|
const dashboard = state.dashboard.getModel();
|
||||||
const { unknown, usages } = createUsagesNetwork(variables, dashboard);
|
const { usages } = createUsagesNetwork(variables, dashboard);
|
||||||
const unknownsNetwork = transformUsagesToNetwork(unknown);
|
|
||||||
const unknownExits = Object.keys(unknown).length > 0;
|
|
||||||
const usagesNetwork = transformUsagesToNetwork(usages);
|
const usagesNetwork = transformUsagesToNetwork(usages);
|
||||||
|
|
||||||
dispatch(initInspect({ unknown, usages, usagesNetwork, unknownsNetwork, unknownExits }));
|
dispatch(initInspect({ usages, usagesNetwork }));
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getNextAvailableId(type: VariableType, variables: VariableModel[]): string {
|
export function getNextAvailableId(type: VariableType, variables: VariableModel[]): string {
|
||||||
|
@ -25,7 +25,14 @@ export const VariablesUnknownButton: FC<Props> = ({ id, usages }) => {
|
|||||||
return (
|
return (
|
||||||
<NetworkGraphModal show={false} title={`Showing usages for: $${id}`} nodes={nodes} edges={network.edges}>
|
<NetworkGraphModal show={false} title={`Showing usages for: $${id}`} nodes={nodes} edges={network.edges}>
|
||||||
{({ showModal }) => {
|
{({ showModal }) => {
|
||||||
return <IconButton onClick={() => showModal()} name="code-branch" title="Show usages" />;
|
return (
|
||||||
|
<IconButton
|
||||||
|
onClick={() => showModal()}
|
||||||
|
name="code-branch"
|
||||||
|
title="Show usages"
|
||||||
|
data-testid="VariablesUnknownButton"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
</NetworkGraphModal>
|
</NetworkGraphModal>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as runtime from '@grafana/runtime';
|
||||||
|
import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
import { VariablesUnknownTable, VariablesUnknownTableProps } from './VariablesUnknownTable';
|
||||||
|
import { customBuilder } from '../shared/testing/builders';
|
||||||
|
import * as utils from './utils';
|
||||||
|
import { UsagesToNetwork } from './utils';
|
||||||
|
|
||||||
|
async function getTestContext(
|
||||||
|
overrides: Partial<VariablesUnknownTableProps> | undefined = {},
|
||||||
|
usages: UsagesToNetwork[] = []
|
||||||
|
) {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
const reportInteractionSpy = jest.spyOn(runtime, 'reportInteraction').mockImplementation();
|
||||||
|
const getUnknownsNetworkSpy = jest.spyOn(utils, 'getUnknownsNetwork').mockResolvedValue(usages);
|
||||||
|
const defaults: VariablesUnknownTableProps = {
|
||||||
|
variables: [],
|
||||||
|
dashboard: null,
|
||||||
|
};
|
||||||
|
const props = { ...defaults, ...overrides };
|
||||||
|
const { rerender } = render(<VariablesUnknownTable {...props} />);
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByRole('heading', { name: /renamed or missing variables/i })).toBeInTheDocument()
|
||||||
|
);
|
||||||
|
|
||||||
|
return { reportInteractionSpy, getUnknownsNetworkSpy, rerender };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('VariablesUnknownTable', () => {
|
||||||
|
describe('when rendered', () => {
|
||||||
|
it('then it should render the section header', async () => {
|
||||||
|
await getTestContext();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when expanding the section', () => {
|
||||||
|
it('then it should show loading spinner', async () => {
|
||||||
|
await getTestContext();
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitFor(() => expect(screen.getByText('Loading...')).toBeInTheDocument());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('then it should call getUnknownsNetwork', async () => {
|
||||||
|
const { getUnknownsNetworkSpy } = await getTestContext();
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitFor(() => expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('then it should report the interaction', async () => {
|
||||||
|
const { reportInteractionSpy } = await getTestContext();
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitFor(() => expect(screen.getByText('Loading...')).toBeInTheDocument());
|
||||||
|
|
||||||
|
expect(reportInteractionSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(reportInteractionSpy).toHaveBeenCalledWith('Unknown variables section expanded');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('but when expanding it again without changes to variables or dashboard', () => {
|
||||||
|
it('then it should not call getUnknownsNetwork', async () => {
|
||||||
|
const { getUnknownsNetworkSpy } = await getTestContext();
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitFor(() => expect(screen.getByTitle('Click to collapse')).toBeInTheDocument());
|
||||||
|
expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitFor(() => expect(screen.getByTitle('Click to expand')).toBeInTheDocument());
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitFor(() => expect(screen.getByTitle('Click to collapse')).toBeInTheDocument());
|
||||||
|
|
||||||
|
expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and there are no renamed or missing variables', () => {
|
||||||
|
it('then it should render the correct message', async () => {
|
||||||
|
await getTestContext();
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitForElementToBeRemoved(() => screen.getByText('Loading...'));
|
||||||
|
|
||||||
|
expect(screen.getByText('No renamed or missing variables found.')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and there are renamed or missing variables', () => {
|
||||||
|
it('then it should render the table', async () => {
|
||||||
|
const variable = customBuilder().withId('Renamed Variable').withName('Renamed Variable').build();
|
||||||
|
const usages = [{ variable, nodes: [], edges: [], showGraph: false }];
|
||||||
|
const { reportInteractionSpy } = await getTestContext({}, usages);
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitForElementToBeRemoved(() => screen.getByText('Loading...'));
|
||||||
|
|
||||||
|
expect(screen.queryByText('No renamed or missing variables found.')).not.toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Renamed Variable')).toBeInTheDocument();
|
||||||
|
expect(screen.getAllByTestId('VariablesUnknownButton')).toHaveLength(1);
|
||||||
|
|
||||||
|
// make sure we don't report the interaction for slow expansion
|
||||||
|
expect(reportInteractionSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(reportInteractionSpy).toHaveBeenCalledWith('Unknown variables section expanded');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('but when the unknown processing takes a while', () => {
|
||||||
|
const origDateNow = Date.now;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
Date.now = origDateNow;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('then it should report slow expansion', async () => {
|
||||||
|
const variable = customBuilder().withId('Renamed Variable').withName('Renamed Variable').build();
|
||||||
|
const usages = [{ variable, nodes: [], edges: [], showGraph: false }];
|
||||||
|
const { reportInteractionSpy } = await getTestContext({}, usages);
|
||||||
|
const dateNowStart = 1000;
|
||||||
|
const dateNowStop = 2000;
|
||||||
|
Date.now = jest.fn().mockReturnValueOnce(dateNowStart).mockReturnValue(dateNowStop);
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
|
||||||
|
await waitForElementToBeRemoved(() => screen.getByText('Loading...'));
|
||||||
|
|
||||||
|
// make sure we report the interaction for slow expansion
|
||||||
|
expect(reportInteractionSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(reportInteractionSpy.mock.calls[0][0]).toEqual('Unknown variables section expanded');
|
||||||
|
expect(reportInteractionSpy.mock.calls[1][0]).toEqual('Slow unknown variables expansion');
|
||||||
|
expect(reportInteractionSpy.mock.calls[1][1]).toEqual({ elapsed: 1000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,26 +1,95 @@
|
|||||||
import React, { FC } from 'react';
|
import React, { ReactElement, useEffect, useState } from 'react';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { Icon, Tooltip, useStyles } from '@grafana/ui';
|
import { useAsync } from 'react-use';
|
||||||
|
import { CollapsableSection, HorizontalGroup, Icon, Spinner, Tooltip, useStyles, VerticalGroup } from '@grafana/ui';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { UsagesToNetwork } from './utils';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { VariablesUnknownButton } from './VariablesUnknownButton';
|
|
||||||
|
|
||||||
interface Props {
|
import { VariableModel } from '../types';
|
||||||
usages: UsagesToNetwork[];
|
import { DashboardModel } from '../../dashboard/state';
|
||||||
|
import { VariablesUnknownButton } from './VariablesUnknownButton';
|
||||||
|
import { getUnknownsNetwork, UsagesToNetwork } from './utils';
|
||||||
|
|
||||||
|
export const SLOW_VARIABLES_EXPANSION_THRESHOLD = 1000;
|
||||||
|
|
||||||
|
export interface VariablesUnknownTableProps {
|
||||||
|
variables: VariableModel[];
|
||||||
|
dashboard: DashboardModel | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VariablesUnknownTable: FC<Props> = ({ usages }) => {
|
export function VariablesUnknownTable({ variables, dashboard }: VariablesUnknownTableProps): ReactElement {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [changed, setChanged] = useState(0);
|
||||||
|
const [usages, setUsages] = useState<UsagesToNetwork[]>([]);
|
||||||
const style = useStyles(getStyles);
|
const style = useStyles(getStyles);
|
||||||
|
useEffect(() => setChanged((prevState) => prevState + 1), [variables, dashboard]);
|
||||||
|
const { loading } = useAsync(async () => {
|
||||||
|
if (open && changed > 0) {
|
||||||
|
// make sure we only fetch when opened and variables or dashboard have changed
|
||||||
|
const start = Date.now();
|
||||||
|
const unknownsNetwork = await getUnknownsNetwork(variables, dashboard);
|
||||||
|
const stop = Date.now();
|
||||||
|
const elapsed = stop - start;
|
||||||
|
if (elapsed >= SLOW_VARIABLES_EXPANSION_THRESHOLD) {
|
||||||
|
reportInteraction('Slow unknown variables expansion', { elapsed });
|
||||||
|
}
|
||||||
|
setChanged(0);
|
||||||
|
setUsages(unknownsNetwork);
|
||||||
|
return unknownsNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}, [variables, dashboard, open, changed]);
|
||||||
|
|
||||||
|
const onToggle = (isOpen: boolean) => {
|
||||||
|
if (isOpen) {
|
||||||
|
reportInteraction('Unknown variables section expanded');
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpen(isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.container}>
|
<div className={style.container}>
|
||||||
|
<CollapsableSection label={<CollapseLabel />} isOpen={open} onToggle={onToggle}>
|
||||||
|
{loading && (
|
||||||
|
<VerticalGroup justify="center">
|
||||||
|
<HorizontalGroup justify="center">
|
||||||
|
<span>Loading...</span>
|
||||||
|
<Spinner size={16} />
|
||||||
|
</HorizontalGroup>
|
||||||
|
</VerticalGroup>
|
||||||
|
)}
|
||||||
|
{!loading && usages && (
|
||||||
|
<>
|
||||||
|
{usages.length === 0 && <NoUnknowns />}
|
||||||
|
{usages.length > 0 && <UnknownTable usages={usages} />}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CollapsableSection>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CollapseLabel(): ReactElement {
|
||||||
|
const style = useStyles(getStyles);
|
||||||
|
return (
|
||||||
<h5>
|
<h5>
|
||||||
Unknown Variables
|
Renamed or missing variables
|
||||||
<Tooltip content="This table lists all variable references that no longer exist in this dashboard.">
|
<Tooltip content="Click to expand a list with all variable references that have been renamed or are missing from the dashboard.">
|
||||||
<Icon name="info-circle" className={style.infoIcon} />
|
<Icon name="info-circle" className={style.infoIcon} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</h5>
|
</h5>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
<div>
|
function NoUnknowns(): ReactElement {
|
||||||
|
return <span>No renamed or missing variables found.</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UnknownTable({ usages }: { usages: UsagesToNetwork[] }): ReactElement {
|
||||||
|
const style = useStyles(getStyles);
|
||||||
|
return (
|
||||||
<table className="filter-table filter-table--hover">
|
<table className="filter-table filter-table--hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -48,16 +117,13 @@ export const VariablesUnknownTable: FC<Props> = ({ usages }) => {
|
|||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => ({
|
const getStyles = (theme: GrafanaTheme) => ({
|
||||||
container: css`
|
container: css`
|
||||||
margin-top: ${theme.spacing.xl};
|
margin-top: ${theme.spacing.xl};
|
||||||
padding-top: ${theme.spacing.xl};
|
padding-top: ${theme.spacing.xl};
|
||||||
border-top: 1px solid ${theme.colors.panelBorder};
|
|
||||||
`,
|
`,
|
||||||
infoIcon: css`
|
infoIcon: css`
|
||||||
margin-left: ${theme.spacing.sm};
|
margin-left: ${theme.spacing.sm};
|
||||||
|
@ -10,19 +10,13 @@ describe('variableInspectReducer', () => {
|
|||||||
.givenReducer(variableInspectReducer, { ...initialVariableInspectState })
|
.givenReducer(variableInspectReducer, { ...initialVariableInspectState })
|
||||||
.whenActionIsDispatched(
|
.whenActionIsDispatched(
|
||||||
initInspect({
|
initInspect({
|
||||||
unknownExits: true,
|
|
||||||
unknownsNetwork: [{ edges: [], nodes: [], showGraph: true, variable }],
|
|
||||||
usagesNetwork: [{ edges: [], nodes: [], showGraph: true, variable }],
|
usagesNetwork: [{ edges: [], nodes: [], showGraph: true, variable }],
|
||||||
usages: [{ variable, tree: {} }],
|
usages: [{ variable, tree: {} }],
|
||||||
unknown: [{ variable, tree: {} }],
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.thenStateShouldEqual({
|
.thenStateShouldEqual({
|
||||||
unknownExits: true,
|
|
||||||
unknownsNetwork: [{ edges: [], nodes: [], showGraph: true, variable }],
|
|
||||||
usagesNetwork: [{ edges: [], nodes: [], showGraph: true, variable }],
|
usagesNetwork: [{ edges: [], nodes: [], showGraph: true, variable }],
|
||||||
usages: [{ variable, tree: {} }],
|
usages: [{ variable, tree: {} }],
|
||||||
unknown: [{ variable, tree: {} }],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,40 +2,22 @@ import { UsagesToNetwork, VariableUsageTree } from './utils';
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
export interface VariableInspectState {
|
export interface VariableInspectState {
|
||||||
unknown: VariableUsageTree[];
|
|
||||||
usages: VariableUsageTree[];
|
usages: VariableUsageTree[];
|
||||||
unknownsNetwork: UsagesToNetwork[];
|
|
||||||
usagesNetwork: UsagesToNetwork[];
|
usagesNetwork: UsagesToNetwork[];
|
||||||
unknownExits: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialVariableInspectState: VariableInspectState = {
|
export const initialVariableInspectState: VariableInspectState = {
|
||||||
unknown: [],
|
|
||||||
usages: [],
|
usages: [],
|
||||||
unknownsNetwork: [],
|
|
||||||
usagesNetwork: [],
|
usagesNetwork: [],
|
||||||
unknownExits: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const variableInspectReducerSlice = createSlice({
|
const variableInspectReducerSlice = createSlice({
|
||||||
name: 'templating/inspect',
|
name: 'templating/inspect',
|
||||||
initialState: initialVariableInspectState,
|
initialState: initialVariableInspectState,
|
||||||
reducers: {
|
reducers: {
|
||||||
initInspect: (
|
initInspect: (state, action: PayloadAction<{ usages: VariableUsageTree[]; usagesNetwork: UsagesToNetwork[] }>) => {
|
||||||
state,
|
const { usages, usagesNetwork } = action.payload;
|
||||||
action: PayloadAction<{
|
|
||||||
unknown: VariableUsageTree[];
|
|
||||||
usages: VariableUsageTree[];
|
|
||||||
unknownsNetwork: UsagesToNetwork[];
|
|
||||||
usagesNetwork: UsagesToNetwork[];
|
|
||||||
unknownExits: boolean;
|
|
||||||
}>
|
|
||||||
) => {
|
|
||||||
const { unknown, usages, unknownExits, unknownsNetwork, usagesNetwork } = action.payload;
|
|
||||||
state.usages = usages;
|
state.usages = usages;
|
||||||
state.unknown = unknown;
|
|
||||||
state.unknownsNetwork = unknownsNetwork;
|
|
||||||
state.unknownExits = unknownExits;
|
|
||||||
state.usagesNetwork = usagesNetwork;
|
state.usagesNetwork = usagesNetwork;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -179,29 +179,18 @@ export interface VariableUsageTree {
|
|||||||
|
|
||||||
export interface VariableUsages {
|
export interface VariableUsages {
|
||||||
unUsed: VariableModel[];
|
unUsed: VariableModel[];
|
||||||
unknown: VariableUsageTree[];
|
|
||||||
usages: VariableUsageTree[];
|
usages: VariableUsageTree[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createUsagesNetwork = (variables: VariableModel[], dashboard: DashboardModel | null): VariableUsages => {
|
export const createUsagesNetwork = (variables: VariableModel[], dashboard: DashboardModel | null): VariableUsages => {
|
||||||
if (!dashboard) {
|
if (!dashboard) {
|
||||||
return { unUsed: [], unknown: [], usages: [] };
|
return { unUsed: [], usages: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const unUsed: VariableModel[] = [];
|
const unUsed: VariableModel[] = [];
|
||||||
let usages: VariableUsageTree[] = [];
|
let usages: VariableUsageTree[] = [];
|
||||||
let unknown: VariableUsageTree[] = [];
|
|
||||||
const model = dashboard.getSaveModelClone();
|
const model = dashboard.getSaveModelClone();
|
||||||
|
|
||||||
const unknownVariables = getUnknownVariableStrings(variables, model);
|
|
||||||
for (const unknownVariable of unknownVariables) {
|
|
||||||
const props = getPropsWithVariable(unknownVariable, { key: 'model', value: model }, {});
|
|
||||||
if (Object.keys(props).length) {
|
|
||||||
const variable = ({ id: unknownVariable, name: unknownVariable } as unknown) as VariableModel;
|
|
||||||
unknown.push({ variable, tree: props });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const variable of variables) {
|
for (const variable of variables) {
|
||||||
const variableId = variable.id;
|
const variableId = variable.id;
|
||||||
const props = getPropsWithVariable(variableId, { key: 'model', value: model }, {});
|
const props = getPropsWithVariable(variableId, { key: 'model', value: model }, {});
|
||||||
@ -214,9 +203,46 @@ export const createUsagesNetwork = (variables: VariableModel[], dashboard: Dashb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { unUsed, unknown, usages };
|
return { unUsed, usages };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function getUnknownsNetwork(
|
||||||
|
variables: VariableModel[],
|
||||||
|
dashboard: DashboardModel | null
|
||||||
|
): Promise<UsagesToNetwork[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// can be an expensive call so we avoid blocking the main thread
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
const unknowns = createUnknownsNetwork(variables, dashboard);
|
||||||
|
resolve(transformUsagesToNetwork(unknowns));
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUnknownsNetwork(variables: VariableModel[], dashboard: DashboardModel | null): VariableUsageTree[] {
|
||||||
|
if (!dashboard) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let unknown: VariableUsageTree[] = [];
|
||||||
|
const model = dashboard.getSaveModelClone();
|
||||||
|
|
||||||
|
const unknownVariables = getUnknownVariableStrings(variables, model);
|
||||||
|
for (const unknownVariable of unknownVariables) {
|
||||||
|
const props = getPropsWithVariable(unknownVariable, { key: 'model', value: model }, {});
|
||||||
|
if (Object.keys(props).length) {
|
||||||
|
const variable = ({ id: unknownVariable, name: unknownVariable } as unknown) as VariableModel;
|
||||||
|
unknown.push({ variable, tree: props });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
getAllAffectedPanelIdsForVariableChange is a function that extracts all the panel ids that are affected by a single variable
|
getAllAffectedPanelIdsForVariableChange is a function that extracts all the panel ids that are affected by a single variable
|
||||||
change. It will traverse all chained variables to identify all cascading changes too.
|
change. It will traverse all chained variables to identify all cascading changes too.
|
||||||
|
Loading…
Reference in New Issue
Block a user