mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Scopes: Remove basic selector (#89098)
This commit is contained in:
parent
59d83bc55a
commit
07ec1a303e
@ -1,76 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { SceneComponentProps } from '@grafana/scenes';
|
||||
import { Button, Drawer, Spinner, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { ScopesFiltersScene } from './ScopesFiltersScene';
|
||||
import { ScopesTreeLevel } from './ScopesTreeLevel';
|
||||
|
||||
export function ScopesFiltersAdvancedSelector({ model }: SceneComponentProps<ScopesFiltersScene>) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const { nodes, loadingNodeName, dirtyScopeNames, isLoadingScopes, isAdvancedOpened } = model.useState();
|
||||
|
||||
if (!isAdvancedOpened) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={t('scopes.advancedSelector.title', 'Select scopes')}
|
||||
size="sm"
|
||||
onClose={() => {
|
||||
model.closeAdvancedSelector();
|
||||
model.resetDirtyScopeNames();
|
||||
}}
|
||||
>
|
||||
{isLoadingScopes ? (
|
||||
<Spinner data-testid="scopes-advanced-loading" />
|
||||
) : (
|
||||
<ScopesTreeLevel
|
||||
showQuery={true}
|
||||
nodes={nodes}
|
||||
nodePath={['']}
|
||||
loadingNodeName={loadingNodeName}
|
||||
scopeNames={dirtyScopeNames}
|
||||
onNodeUpdate={(path, isExpanded, query) => model.updateNode(path, isExpanded, query)}
|
||||
onNodeSelectToggle={(path) => model.toggleNodeSelect(path)}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.buttonGroup}>
|
||||
<Button
|
||||
variant="primary"
|
||||
data-testid="scopes-advanced-apply"
|
||||
onClick={() => {
|
||||
model.closeAdvancedSelector();
|
||||
model.updateScopes();
|
||||
}}
|
||||
>
|
||||
<Trans i18nKey="scopes.advancedSelector.apply">Apply</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
data-testid="scopes-advanced-cancel"
|
||||
onClick={() => {
|
||||
model.closeAdvancedSelector();
|
||||
model.resetDirtyScopeNames();
|
||||
}}
|
||||
>
|
||||
<Trans i18nKey="scopes.advancedSelector.cancel">Cancel</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
buttonGroup: css({
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
marginTop: theme.spacing(8),
|
||||
}),
|
||||
};
|
||||
};
|
@ -1,110 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { SceneComponentProps } from '@grafana/scenes';
|
||||
import { Icon, IconButton, Input, Spinner, Toggletip, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { ScopesFiltersScene } from './ScopesFiltersScene';
|
||||
import { ScopesTreeLevel } from './ScopesTreeLevel';
|
||||
|
||||
export function ScopesFiltersBasicSelector({ model }: SceneComponentProps<ScopesFiltersScene>) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const { nodes, loadingNodeName, scopes, dirtyScopeNames, isLoadingScopes, isBasicOpened } = model.useState();
|
||||
const { isViewing } = model.scopesParent.useState();
|
||||
|
||||
const scopesTitles = scopes.map(({ spec: { title } }) => title).join(', ');
|
||||
|
||||
return (
|
||||
<div className={styles.container} data-testid="scopes-basic-container">
|
||||
<Toggletip
|
||||
show={isBasicOpened}
|
||||
closeButton={false}
|
||||
content={
|
||||
<div className={styles.innerContainer} data-testid="scopes-basic-inner-container">
|
||||
{isLoadingScopes ? (
|
||||
<Spinner data-testid="scopes-basic-loading" />
|
||||
) : (
|
||||
<ScopesTreeLevel
|
||||
showQuery={false}
|
||||
nodes={nodes}
|
||||
nodePath={['']}
|
||||
loadingNodeName={loadingNodeName}
|
||||
scopeNames={dirtyScopeNames}
|
||||
onNodeUpdate={(path, isExpanded, query) => model.updateNode(path, isExpanded, query)}
|
||||
onNodeSelectToggle={(path) => model.toggleNodeSelect(path)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
footer={
|
||||
<button
|
||||
className={styles.openAdvancedButton}
|
||||
data-testid="scopes-basic-open-advanced"
|
||||
onClick={() => model.openAdvancedSelector()}
|
||||
>
|
||||
<Trans i18nKey="scopes.basicSelector.openAdvanced">
|
||||
Open advanced scope selector <Icon name="arrow-right" />
|
||||
</Trans>
|
||||
</button>
|
||||
}
|
||||
onOpen={() => model.openBasicSelector()}
|
||||
onClose={() => {
|
||||
model.closeBasicSelector();
|
||||
model.updateScopes();
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
readOnly
|
||||
placeholder={t('scopes.basicSelector.placeholder', 'Select scopes...')}
|
||||
loading={isLoadingScopes}
|
||||
value={scopesTitles}
|
||||
aria-label={t('scopes.basicSelector.placeholder', 'Select scopes...')}
|
||||
data-testid="scopes-basic-input"
|
||||
suffix={
|
||||
scopes.length > 0 && !isViewing ? (
|
||||
<IconButton
|
||||
aria-label={t('scopes.basicSelector.removeAll', 'Remove all scopes')}
|
||||
name="times"
|
||||
onClick={() => model.removeAllScopes()}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</Toggletip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
container: css({
|
||||
width: '100%',
|
||||
|
||||
'& > div': css({
|
||||
padding: 0,
|
||||
|
||||
'& > div': css({
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
innerContainer: css({
|
||||
minWidth: 400,
|
||||
padding: theme.spacing(0, 1),
|
||||
}),
|
||||
openAdvancedButton: css({
|
||||
backgroundColor: theme.colors.secondary.main,
|
||||
border: 'none',
|
||||
borderTop: `1px solid ${theme.colors.secondary.border}`,
|
||||
display: 'block',
|
||||
fontSize: theme.typography.pxToRem(12),
|
||||
margin: 0,
|
||||
padding: theme.spacing(1.5),
|
||||
textAlign: 'right',
|
||||
width: '100%',
|
||||
}),
|
||||
};
|
||||
};
|
@ -1,8 +1,9 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { isEqual } from 'lodash';
|
||||
import React from 'react';
|
||||
import { finalize, from, Subscription } from 'rxjs';
|
||||
|
||||
import { Scope } from '@grafana/data';
|
||||
import { GrafanaTheme2, Scope } from '@grafana/data';
|
||||
import {
|
||||
SceneComponentProps,
|
||||
sceneGraph,
|
||||
@ -12,10 +13,11 @@ import {
|
||||
SceneObjectUrlValues,
|
||||
SceneObjectWithUrlSync,
|
||||
} from '@grafana/scenes';
|
||||
import { Button, Drawer, IconButton, Input, Spinner, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { ScopesFiltersAdvancedSelector } from './ScopesFiltersAdvancedSelector';
|
||||
import { ScopesFiltersBasicSelector } from './ScopesFiltersBasicSelector';
|
||||
import { ScopesScene } from './ScopesScene';
|
||||
import { ScopesTreeLevel } from './ScopesTreeLevel';
|
||||
import { fetchNodes, fetchScope, fetchScopes } from './api';
|
||||
import { NodesMap } from './types';
|
||||
|
||||
@ -25,8 +27,7 @@ export interface ScopesFiltersSceneState extends SceneObjectState {
|
||||
scopes: Scope[];
|
||||
dirtyScopeNames: string[];
|
||||
isLoadingScopes: boolean;
|
||||
isBasicOpened: boolean;
|
||||
isAdvancedOpened: boolean;
|
||||
isOpened: boolean;
|
||||
}
|
||||
|
||||
export class ScopesFiltersScene extends SceneObjectBase<ScopesFiltersSceneState> implements SceneObjectWithUrlSync {
|
||||
@ -58,8 +59,7 @@ export class ScopesFiltersScene extends SceneObjectBase<ScopesFiltersSceneState>
|
||||
scopes: [],
|
||||
dirtyScopeNames: [],
|
||||
isLoadingScopes: false,
|
||||
isBasicOpened: false,
|
||||
isAdvancedOpened: false,
|
||||
isOpened: false,
|
||||
});
|
||||
|
||||
this.addActivationHandler(() => {
|
||||
@ -153,24 +153,14 @@ export class ScopesFiltersScene extends SceneObjectBase<ScopesFiltersSceneState>
|
||||
}
|
||||
}
|
||||
|
||||
public openBasicSelector() {
|
||||
public open() {
|
||||
if (!this.scopesParent.state.isViewing) {
|
||||
this.setState({ isBasicOpened: true, isAdvancedOpened: false });
|
||||
this.setState({ isOpened: true });
|
||||
}
|
||||
}
|
||||
|
||||
public closeBasicSelector() {
|
||||
this.setState({ isBasicOpened: false });
|
||||
}
|
||||
|
||||
public openAdvancedSelector() {
|
||||
if (!this.scopesParent.state.isViewing) {
|
||||
this.setState({ isBasicOpened: false, isAdvancedOpened: true });
|
||||
}
|
||||
}
|
||||
|
||||
public closeAdvancedSelector() {
|
||||
this.setState({ isAdvancedOpened: false });
|
||||
public close() {
|
||||
this.setState({ isOpened: false });
|
||||
}
|
||||
|
||||
public getSelectedScopes(): Scope[] {
|
||||
@ -196,7 +186,7 @@ export class ScopesFiltersScene extends SceneObjectBase<ScopesFiltersSceneState>
|
||||
}
|
||||
|
||||
public enterViewMode() {
|
||||
this.setState({ isBasicOpened: false, isAdvancedOpened: false });
|
||||
this.setState({ isOpened: false });
|
||||
}
|
||||
|
||||
private getScopeNames(): string[] {
|
||||
@ -205,10 +195,88 @@ export class ScopesFiltersScene extends SceneObjectBase<ScopesFiltersSceneState>
|
||||
}
|
||||
|
||||
export function ScopesFiltersSceneRenderer({ model }: SceneComponentProps<ScopesFiltersScene>) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const { nodes, loadingNodeName, dirtyScopeNames, isLoadingScopes, isOpened, scopes } = model.useState();
|
||||
const { isViewing } = model.scopesParent.useState();
|
||||
|
||||
const scopesTitles = scopes.map(({ spec: { title } }) => title).join(', ');
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScopesFiltersBasicSelector model={model} />
|
||||
<ScopesFiltersAdvancedSelector model={model} />
|
||||
<Input
|
||||
readOnly
|
||||
placeholder={t('scopes.filters.input.placeholder', 'Select scopes...')}
|
||||
loading={isLoadingScopes}
|
||||
value={scopesTitles}
|
||||
aria-label={t('scopes.filters.input.placeholder', 'Select scopes...')}
|
||||
data-testid="scopes-filters-input"
|
||||
suffix={
|
||||
scopes.length > 0 && !isViewing ? (
|
||||
<IconButton
|
||||
aria-label={t('scopes.filters.input.removeAll', 'Remove all scopes')}
|
||||
name="times"
|
||||
onClick={() => model.removeAllScopes()}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
onClick={() => model.open()}
|
||||
/>
|
||||
|
||||
{isOpened && (
|
||||
<Drawer
|
||||
title={t('scopes.filters.title', 'Select scopes')}
|
||||
size="sm"
|
||||
onClose={() => {
|
||||
model.close();
|
||||
model.resetDirtyScopeNames();
|
||||
}}
|
||||
>
|
||||
{isLoadingScopes ? (
|
||||
<Spinner data-testid="scopes-filters-loading" />
|
||||
) : (
|
||||
<ScopesTreeLevel
|
||||
nodes={nodes}
|
||||
nodePath={['']}
|
||||
loadingNodeName={loadingNodeName}
|
||||
scopeNames={dirtyScopeNames}
|
||||
onNodeUpdate={(path, isExpanded, query) => model.updateNode(path, isExpanded, query)}
|
||||
onNodeSelectToggle={(path) => model.toggleNodeSelect(path)}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.buttonGroup}>
|
||||
<Button
|
||||
variant="primary"
|
||||
data-testid="scopes-filters-apply"
|
||||
onClick={() => {
|
||||
model.close();
|
||||
model.updateScopes();
|
||||
}}
|
||||
>
|
||||
<Trans i18nKey="scopes.filters.apply">Apply</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
data-testid="scopes-filters-cancel"
|
||||
onClick={() => {
|
||||
model.close();
|
||||
model.resetDirtyScopeNames();
|
||||
}}
|
||||
>
|
||||
<Trans i18nKey="scopes.filters.cancel">Cancel</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</Drawer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
buttonGroup: css({
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
marginTop: theme.spacing(8),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
@ -13,8 +13,6 @@ import {
|
||||
fetchNodesSpy,
|
||||
fetchScopeSpy,
|
||||
fetchScopesSpy,
|
||||
getAdvancedApply,
|
||||
getAdvancedCancel,
|
||||
getApplicationsClustersExpand,
|
||||
getApplicationsClustersSelect,
|
||||
getApplicationsExpand,
|
||||
@ -22,29 +20,28 @@ import {
|
||||
getApplicationsSlothPictureFactorySelect,
|
||||
getApplicationsSlothPictureFactoryTitle,
|
||||
getApplicationsSlothVoteTrackerSelect,
|
||||
getBasicInnerContainer,
|
||||
getBasicInput,
|
||||
getBasicOpenAdvanced,
|
||||
getFiltersApply,
|
||||
getFiltersCancel,
|
||||
getFiltersInput,
|
||||
getClustersExpand,
|
||||
getClustersSelect,
|
||||
getClustersSlothClusterNorthSelect,
|
||||
getClustersSlothClusterSouthSelect,
|
||||
getDashboard,
|
||||
getDashboardsContainer,
|
||||
getDashboardsExpand,
|
||||
getDashboardsSearch,
|
||||
getRootExpand,
|
||||
mocksNodes,
|
||||
mocksScopeDashboardBindings,
|
||||
mocksScopes,
|
||||
queryAdvancedApply,
|
||||
queryFiltersApply,
|
||||
queryApplicationsClustersSlothClusterNorthTitle,
|
||||
queryApplicationsClustersTitle,
|
||||
queryApplicationsSlothPictureFactoryTitle,
|
||||
queryApplicationsSlothVoteTrackerTitle,
|
||||
queryBasicInnerContainer,
|
||||
queryDashboard,
|
||||
queryDashboardsContainer,
|
||||
queryRootExpand,
|
||||
queryDashboardsExpand,
|
||||
renderDashboard,
|
||||
} from './testUtils';
|
||||
|
||||
@ -123,14 +120,14 @@ describe('ScopesScene', () => {
|
||||
|
||||
describe('Tree', () => {
|
||||
it('Navigates through scopes nodes', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsClustersExpand());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
});
|
||||
|
||||
it('Fetches scope details on select', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothVoteTrackerSelect());
|
||||
await waitFor(() => expect(fetchScopeSpy).toHaveBeenCalledTimes(1));
|
||||
@ -138,24 +135,24 @@ describe('ScopesScene', () => {
|
||||
|
||||
it('Selects the proper scopes', async () => {
|
||||
await act(async () => filtersScene.updateScopes(['slothPictureFactory', 'slothVoteTracker']));
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
expect(getApplicationsSlothVoteTrackerSelect()).toBeChecked();
|
||||
expect(getApplicationsSlothPictureFactorySelect()).toBeChecked();
|
||||
});
|
||||
|
||||
it('Can select scopes from same level', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothVoteTrackerSelect());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getApplicationsClustersSelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
expect(getBasicInput().value).toBe('slothVoteTracker, slothPictureFactory, Cluster Index Helper');
|
||||
await userEvents.click(getFiltersApply());
|
||||
expect(getFiltersInput().value).toBe('slothVoteTracker, slothPictureFactory, Cluster Index Helper');
|
||||
});
|
||||
|
||||
it("Can't navigate deeper than the level where scopes are selected", async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothVoteTrackerSelect());
|
||||
await userEvents.click(getApplicationsClustersExpand());
|
||||
@ -163,25 +160,24 @@ describe('ScopesScene', () => {
|
||||
});
|
||||
|
||||
it('Can select a node from an upper level', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothVoteTrackerSelect());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getClustersSelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
expect(getBasicInput().value).toBe('Cluster Index Helper');
|
||||
await userEvents.click(getFiltersApply());
|
||||
expect(getFiltersInput().value).toBe('Cluster Index Helper');
|
||||
});
|
||||
|
||||
it('Respects only one select per container', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getClustersExpand());
|
||||
await userEvents.click(getClustersSlothClusterNorthSelect());
|
||||
expect(getClustersSlothClusterSouthSelect()).toBeDisabled();
|
||||
});
|
||||
|
||||
it('Search works', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getBasicOpenAdvanced());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.type(getApplicationsSearch(), 'Clusters');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
@ -197,43 +193,16 @@ describe('ScopesScene', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic selector', () => {
|
||||
describe('Filters', () => {
|
||||
it('Opens', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
expect(getBasicInnerContainer()).toBeInTheDocument();
|
||||
await userEvents.click(getFiltersInput());
|
||||
expect(getFiltersApply()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Fetches scope details on save', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getClustersSelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await waitFor(() => expect(fetchScopesSpy).toHaveBeenCalled());
|
||||
expect(filtersScene.getSelectedScopes()).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'indexHelperCluster')
|
||||
);
|
||||
});
|
||||
|
||||
it('Shows selected scopes', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getClustersSelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
expect(getBasicInput().value).toEqual('Cluster Index Helper');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Advanced selector', () => {
|
||||
it('Opens', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getBasicOpenAdvanced());
|
||||
expect(queryBasicInnerContainer()).not.toBeInTheDocument();
|
||||
expect(getAdvancedApply()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Fetches scope details on save', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getBasicOpenAdvanced());
|
||||
await userEvents.click(getClustersSelect());
|
||||
await userEvents.click(getAdvancedApply());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => expect(fetchScopesSpy).toHaveBeenCalled());
|
||||
expect(filtersScene.getSelectedScopes()).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'indexHelperCluster')
|
||||
@ -241,85 +210,73 @@ describe('ScopesScene', () => {
|
||||
});
|
||||
|
||||
it("Doesn't save the scopes on close", async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getBasicOpenAdvanced());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getClustersSelect());
|
||||
await userEvents.click(getAdvancedCancel());
|
||||
await userEvents.click(getFiltersCancel());
|
||||
await waitFor(() => expect(fetchScopesSpy).not.toHaveBeenCalled());
|
||||
expect(filtersScene.getSelectedScopes()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Selectors interoperability', () => {
|
||||
it('Replicates the same structure from basic to advanced selector', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getBasicOpenAdvanced());
|
||||
expect(getApplicationsSlothPictureFactoryTitle()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Replicates the same structure from advanced to basic selector', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getBasicOpenAdvanced());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getAdvancedApply());
|
||||
await userEvents.click(getBasicInput());
|
||||
expect(getApplicationsSlothPictureFactoryTitle()).toBeInTheDocument();
|
||||
it('Shows selected scopes', async () => {
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getClustersSelect());
|
||||
await userEvents.click(getFiltersApply());
|
||||
expect(getFiltersInput().value).toEqual('Cluster Index Helper');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dashboards list', () => {
|
||||
it('Toggles expanded state', async () => {
|
||||
await userEvents.click(getRootExpand());
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
expect(getDashboardsContainer()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not fetch dashboards list when the list is not expanded', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => expect(fetchDashboardsSpy).not.toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('Fetches dashboards list when the list is expanded', async () => {
|
||||
await userEvents.click(getRootExpand());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => expect(fetchDashboardsSpy).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('Fetches dashboards list when the list is expanded after scope selection', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getRootExpand());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await waitFor(() => expect(fetchDashboardsSpy).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('Shows dashboards for multiple scopes', async () => {
|
||||
await userEvents.click(getRootExpand());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
expect(getDashboard('1')).toBeInTheDocument();
|
||||
expect(getDashboard('2')).toBeInTheDocument();
|
||||
expect(queryDashboard('3')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('4')).not.toBeInTheDocument();
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsSlothVoteTrackerSelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
expect(getDashboard('1')).toBeInTheDocument();
|
||||
expect(getDashboard('2')).toBeInTheDocument();
|
||||
expect(getDashboard('3')).toBeInTheDocument();
|
||||
expect(getDashboard('4')).toBeInTheDocument();
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
expect(queryDashboard('1')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('2')).not.toBeInTheDocument();
|
||||
expect(getDashboard('3')).toBeInTheDocument();
|
||||
@ -327,11 +284,11 @@ describe('ScopesScene', () => {
|
||||
});
|
||||
|
||||
it('Filters the dashboards list', async () => {
|
||||
await userEvents.click(getRootExpand());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
expect(getDashboard('1')).toBeInTheDocument();
|
||||
expect(getDashboard('2')).toBeInTheDocument();
|
||||
await userEvents.type(getDashboardsSearch(), '1');
|
||||
@ -346,43 +303,36 @@ describe('ScopesScene', () => {
|
||||
expect(scopesScene.state.isExpanded).toEqual(false);
|
||||
});
|
||||
|
||||
it('Closes basic selector on enter', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
it('Closes filters on enter', async () => {
|
||||
await userEvents.click(getFiltersInput());
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(queryBasicInnerContainer()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Closes advanced selector on enter', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getBasicOpenAdvanced());
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(queryAdvancedApply()).not.toBeInTheDocument();
|
||||
expect(queryFiltersApply()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Closes dashboards list on enter', async () => {
|
||||
await userEvents.click(getRootExpand());
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(queryDashboardsContainer()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not open basic selector when view mode is active', async () => {
|
||||
it('Does not open filters when view mode is active', async () => {
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
await userEvents.click(getBasicInput());
|
||||
expect(queryBasicInnerContainer()).not.toBeInTheDocument();
|
||||
await userEvents.click(getFiltersInput());
|
||||
expect(queryFiltersApply()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Hides the expand button when view mode is active', async () => {
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(queryRootExpand()).not.toBeInTheDocument();
|
||||
expect(queryDashboardsExpand()).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Enrichers', () => {
|
||||
it('Data requests', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => {
|
||||
const queryRunner = sceneGraph.findObject(dashboardScene, (o) => o.state.key === 'data-query-runner')!;
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(
|
||||
@ -390,9 +340,9 @@ describe('ScopesScene', () => {
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsSlothVoteTrackerSelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => {
|
||||
const queryRunner = sceneGraph.findObject(dashboardScene, (o) => o.state.key === 'data-query-runner')!;
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(
|
||||
@ -402,9 +352,9 @@ describe('ScopesScene', () => {
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => {
|
||||
const queryRunner = sceneGraph.findObject(dashboardScene, (o) => o.state.key === 'data-query-runner')!;
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(
|
||||
@ -414,19 +364,19 @@ describe('ScopesScene', () => {
|
||||
});
|
||||
|
||||
it('Filters requests', async () => {
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsExpand());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => {
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'slothPictureFactory')
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsSlothVoteTrackerSelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => {
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(
|
||||
mocksScopes.filter(
|
||||
@ -435,9 +385,9 @@ describe('ScopesScene', () => {
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersInput());
|
||||
await userEvents.click(getApplicationsSlothPictureFactorySelect());
|
||||
await userEvents.click(getBasicInput());
|
||||
await userEvents.click(getFiltersApply());
|
||||
await waitFor(() => {
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'slothVoteTracker')
|
||||
|
@ -94,10 +94,10 @@ export function ScopesSceneRenderer({ model }: SceneComponentProps<ScopesScene>)
|
||||
className={cx(!isExpanded && styles.iconNotExpanded)}
|
||||
aria-label={
|
||||
isExpanded
|
||||
? t('scopes.root.collapse', 'Collapse scope filters')
|
||||
: t('scopes.root.expand', 'Expand scope filters')
|
||||
? t('scopes.suggestedDashboards.toggle.collapse', 'Collapse scope filters')
|
||||
: t('scopes.suggestedDashboards.toggle..expand', 'Expand scope filters')
|
||||
}
|
||||
data-testid="scopes-root-expand"
|
||||
data-testid="scopes-dashboards-expand"
|
||||
onClick={() => model.toggleIsExpanded()}
|
||||
/>
|
||||
)}
|
||||
|
@ -10,7 +10,6 @@ import { t } from 'app/core/internationalization';
|
||||
import { NodesMap } from './types';
|
||||
|
||||
export interface ScopesTreeLevelProps {
|
||||
showQuery: boolean;
|
||||
nodes: NodesMap;
|
||||
nodePath: string[];
|
||||
loadingNodeName: string | undefined;
|
||||
@ -20,7 +19,6 @@ export interface ScopesTreeLevelProps {
|
||||
}
|
||||
|
||||
export function ScopesTreeLevel({
|
||||
showQuery,
|
||||
nodes,
|
||||
nodePath,
|
||||
loadingNodeName,
|
||||
@ -43,7 +41,7 @@ export function ScopesTreeLevel({
|
||||
|
||||
return (
|
||||
<>
|
||||
{showQuery && !anyChildExpanded && (
|
||||
{!anyChildExpanded && (
|
||||
<Input
|
||||
prefix={<Icon name="filter" />}
|
||||
className={styles.searchInput}
|
||||
@ -101,7 +99,6 @@ export function ScopesTreeLevel({
|
||||
<div className={styles.itemChildren}>
|
||||
{childNode.isExpanded && (
|
||||
<ScopesTreeLevel
|
||||
showQuery={showQuery}
|
||||
nodes={node.nodes}
|
||||
nodePath={childNodePath}
|
||||
loadingNodeName={loadingNodeName}
|
||||
|
@ -229,29 +229,21 @@ export const fetchScopesSpy = jest.spyOn(api, 'fetchScopes');
|
||||
export const fetchDashboardsSpy = jest.spyOn(api, 'fetchDashboards');
|
||||
|
||||
const selectors = {
|
||||
root: {
|
||||
expand: 'scopes-root-expand',
|
||||
},
|
||||
tree: {
|
||||
search: (nodeId: string) => `scopes-tree-${nodeId}-search`,
|
||||
select: (nodeId: string) => `scopes-tree-${nodeId}-checkbox`,
|
||||
expand: (nodeId: string) => `scopes-tree-${nodeId}-expand`,
|
||||
title: (nodeId: string) => `scopes-tree-${nodeId}-title`,
|
||||
},
|
||||
basicSelector: {
|
||||
container: 'scopes-basic-container',
|
||||
innerContainer: 'scopes-basic-inner-container',
|
||||
loading: 'scopes-basic-loading',
|
||||
openAdvanced: 'scopes-basic-open-advanced',
|
||||
input: 'scopes-basic-input',
|
||||
},
|
||||
advancedSelector: {
|
||||
container: 'scopes-advanced-container',
|
||||
loading: 'scopes-advanced-loading',
|
||||
apply: 'scopes-advanced-apply',
|
||||
cancel: 'scopes-advanced-cancel',
|
||||
filters: {
|
||||
input: 'scopes-filters-input',
|
||||
container: 'scopes-filters-container',
|
||||
loading: 'scopes-filters-loading',
|
||||
apply: 'scopes-filters-apply',
|
||||
cancel: 'scopes-filters-cancel',
|
||||
},
|
||||
dashboards: {
|
||||
expand: 'scopes-dashboards-expand',
|
||||
container: 'scopes-dashboards-container',
|
||||
search: 'scopes-dashboards-search',
|
||||
loading: 'scopes-dashboards-loading',
|
||||
@ -259,18 +251,13 @@ const selectors = {
|
||||
},
|
||||
};
|
||||
|
||||
export const queryRootExpand = () => screen.queryByTestId(selectors.root.expand);
|
||||
export const getRootExpand = () => screen.getByTestId(selectors.root.expand);
|
||||
|
||||
export const queryBasicInnerContainer = () => screen.queryByTestId(selectors.basicSelector.innerContainer);
|
||||
export const getBasicInnerContainer = () => screen.getByTestId(selectors.basicSelector.innerContainer);
|
||||
export const getBasicInput = () => screen.getByTestId<HTMLInputElement>(selectors.basicSelector.input);
|
||||
export const getBasicOpenAdvanced = () => screen.getByTestId(selectors.basicSelector.openAdvanced);
|
||||
|
||||
export const queryAdvancedApply = () => screen.queryByTestId(selectors.advancedSelector.apply);
|
||||
export const getAdvancedApply = () => screen.getByTestId(selectors.advancedSelector.apply);
|
||||
export const getAdvancedCancel = () => screen.getByTestId(selectors.advancedSelector.cancel);
|
||||
export const getFiltersInput = () => screen.getByTestId<HTMLInputElement>(selectors.filters.input);
|
||||
export const queryFiltersApply = () => screen.queryByTestId(selectors.filters.apply);
|
||||
export const getFiltersApply = () => screen.getByTestId(selectors.filters.apply);
|
||||
export const getFiltersCancel = () => screen.getByTestId(selectors.filters.cancel);
|
||||
|
||||
export const queryDashboardsExpand = () => screen.queryByTestId(selectors.dashboards.expand);
|
||||
export const getDashboardsExpand = () => screen.getByTestId(selectors.dashboards.expand);
|
||||
export const queryDashboardsContainer = () => screen.queryByTestId(selectors.dashboards.container);
|
||||
export const getDashboardsContainer = () => screen.getByTestId(selectors.dashboards.container);
|
||||
export const getDashboardsSearch = () => screen.getByTestId(selectors.dashboards.search);
|
||||
|
@ -1594,23 +1594,22 @@
|
||||
"dismissable-button": "Close"
|
||||
},
|
||||
"scopes": {
|
||||
"advancedSelector": {
|
||||
"filters": {
|
||||
"apply": "Apply",
|
||||
"cancel": "Cancel",
|
||||
"input": {
|
||||
"placeholder": "Select scopes...",
|
||||
"removeAll": "Remove all scopes"
|
||||
},
|
||||
"title": "Select scopes"
|
||||
},
|
||||
"basicSelector": {
|
||||
"openAdvanced": "Open advanced scope selector <1></1>",
|
||||
"placeholder": "Select scopes...",
|
||||
"removeAll": "Remove all scopes"
|
||||
},
|
||||
"root": {
|
||||
"collapse": "Collapse scope filters",
|
||||
"expand": "Expand scope filters"
|
||||
},
|
||||
"suggestedDashboards": {
|
||||
"loading": "Loading dashboards",
|
||||
"search": "Filter"
|
||||
"search": "Filter",
|
||||
"toggle": {
|
||||
"collapse": "Collapse scope filters",
|
||||
"expand": "Expand scope filters"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"collapse": "Collapse",
|
||||
|
@ -1594,23 +1594,22 @@
|
||||
"dismissable-button": "Cľőşę"
|
||||
},
|
||||
"scopes": {
|
||||
"advancedSelector": {
|
||||
"filters": {
|
||||
"apply": "Åppľy",
|
||||
"cancel": "Cäʼnčęľ",
|
||||
"input": {
|
||||
"placeholder": "Ŝęľęčŧ şčőpęş...",
|
||||
"removeAll": "Ŗęmővę äľľ şčőpęş"
|
||||
},
|
||||
"title": "Ŝęľęčŧ şčőpęş"
|
||||
},
|
||||
"basicSelector": {
|
||||
"openAdvanced": "Øpęʼn äđväʼnčęđ şčőpę şęľęčŧőř <1></1>",
|
||||
"placeholder": "Ŝęľęčŧ şčőpęş...",
|
||||
"removeAll": "Ŗęmővę äľľ şčőpęş"
|
||||
},
|
||||
"root": {
|
||||
"collapse": "Cőľľäpşę şčőpę ƒįľŧęřş",
|
||||
"expand": "Ēχpäʼnđ şčőpę ƒįľŧęřş"
|
||||
},
|
||||
"suggestedDashboards": {
|
||||
"loading": "Ŀőäđįʼnģ đäşĥþőäřđş",
|
||||
"search": "Fįľŧęř"
|
||||
"search": "Fįľŧęř",
|
||||
"toggle": {
|
||||
"collapse": "Cőľľäpşę şčőpę ƒįľŧęřş",
|
||||
"expand": "Ēχpäʼnđ şčőpę ƒįľŧęřş"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"collapse": "Cőľľäpşę",
|
||||
|
Loading…
Reference in New Issue
Block a user