Scopes: Remove basic selector (#89098)

pull/89173/head
Bogdan Matei 11 months ago committed by GitHub
parent 59d83bc55a
commit 07ec1a303e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 76
      public/app/features/dashboard-scene/scene/Scopes/ScopesFiltersAdvancedSelector.tsx
  2. 110
      public/app/features/dashboard-scene/scene/Scopes/ScopesFiltersBasicSelector.tsx
  3. 116
      public/app/features/dashboard-scene/scene/Scopes/ScopesFiltersScene.tsx
  4. 188
      public/app/features/dashboard-scene/scene/Scopes/ScopesScene.test.tsx
  5. 6
      public/app/features/dashboard-scene/scene/Scopes/ScopesScene.tsx
  6. 5
      public/app/features/dashboard-scene/scene/Scopes/ScopesTreeLevel.tsx
  7. 39
      public/app/features/dashboard-scene/scene/Scopes/testUtils.tsx
  8. 17
      public/locales/en-US/grafana.json
  9. 17
      public/locales/pseudo-LOCALE/grafana.json

@ -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());
await act(async () => dashboardScene.onEnterEditMode());
expect(queryBasicInnerContainer()).not.toBeInTheDocument();
});
it('Closes advanced selector on enter', async () => {
await userEvents.click(getBasicInput());
await userEvents.click(getBasicOpenAdvanced());
it('Closes filters on enter', async () => {
await userEvents.click(getFiltersInput());
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",
"title": "Select scopes"
},
"basicSelector": {
"openAdvanced": "Open advanced scope selector <1></1>",
"input": {
"placeholder": "Select scopes...",
"removeAll": "Remove all scopes"
},
"root": {
"collapse": "Collapse scope filters",
"expand": "Expand scope filters"
"title": "Select scopes"
},
"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čęľ",
"title": "Ŝęľęčŧ şčőpęş"
},
"basicSelector": {
"openAdvanced": "Øpęʼn äđväʼnčęđ şčőpę şęľęčŧőř <1></1>",
"input": {
"placeholder": "Ŝęľęčŧ şčőpęş...",
"removeAll": "Ŗęmővę äľľ şčőpęş"
},
"root": {
"collapse": "Cőľľäpşę şčőpę ƒįľŧęřş",
"expand": "Ēχpäʼnđ şčőpę ƒįľŧęřş"
"title": "Ŝęľęčŧ şčőpęş"
},
"suggestedDashboards": {
"loading": "Ŀőäđįʼnģ đäşĥþőäřđş",
"search": "Fįľŧęř"
"search": "Fįľŧęř",
"toggle": {
"collapse": "Cőľľäpşę şčőpę ƒįľŧęřş",
"expand": "Ēχpäʼnđ şčőpę ƒįľŧęřş"
}
},
"tree": {
"collapse": "Cőľľäpşę",

Loading…
Cancel
Save