Scopes: Implement clear search for nodes (#89678)

This commit is contained in:
Bogdan Matei 2024-06-26 13:18:50 +03:00 committed by GitHub
parent 54cfae8fef
commit 1040dc1baf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 19 additions and 45 deletions

View File

@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
import { GrafanaTheme2, Scope, urlUtil } from '@grafana/data'; import { GrafanaTheme2, Scope, urlUtil } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
import { CustomScrollbar, Icon, IconButton, Input, LoadingPlaceholder, useStyles2 } from '@grafana/ui'; import { CustomScrollbar, FilterInput, LoadingPlaceholder, useStyles2 } from '@grafana/ui';
import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { useQueryParams } from 'app/core/hooks/useQueryParams';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
@ -70,23 +70,12 @@ export function ScopesDashboardsSceneRenderer({ model }: SceneComponentProps<Sco
return ( return (
<> <>
<div className={styles.searchInputContainer}> <div className={styles.searchInputContainer}>
<Input <FilterInput
prefix={<Icon name="search" />}
placeholder={t('scopes.suggestedDashboards.search', 'Search')}
disabled={isLoading} disabled={isLoading}
data-testid="scopes-dashboards-search" placeholder={t('scopes.suggestedDashboards.search', 'Search')}
value={searchQuery} value={searchQuery}
suffix={ data-testid="scopes-dashboards-search"
searchQuery && !isLoading ? ( onChange={(value) => model.changeSearchQuery(value)}
<IconButton
aria-label={t('scopes.suggestedDashboards.clear', 'Clear search')}
name="times"
data-testid="scopes-dashboards-clear"
onClick={() => model.changeSearchQuery('')}
/>
) : undefined
}
onChange={(evt) => model.changeSearchQuery(evt.currentTarget.value)}
/> />
</div> </div>

View File

@ -45,7 +45,6 @@ import {
queryDashboardsContainer, queryDashboardsContainer,
queryDashboardsExpand, queryDashboardsExpand,
renderDashboard, renderDashboard,
getDashboardsClear,
} from './testUtils'; } from './testUtils';
jest.mock('@grafana/runtime', () => ({ jest.mock('@grafana/runtime', () => ({
@ -286,22 +285,6 @@ describe('ScopesScene', () => {
expect(queryDashboard('2')).not.toBeInTheDocument(); expect(queryDashboard('2')).not.toBeInTheDocument();
}); });
it('Clears the filter', async () => {
await userEvents.click(getDashboardsExpand());
await userEvents.click(getFiltersInput());
await userEvents.click(getApplicationsExpand());
await userEvents.click(getApplicationsSlothPictureFactorySelect());
await userEvents.click(getFiltersApply());
expect(getDashboard('1')).toBeInTheDocument();
expect(getDashboard('2')).toBeInTheDocument();
await userEvents.type(getDashboardsSearch(), '1');
expect(queryDashboard('2')).not.toBeInTheDocument();
await userEvents.click(getDashboardsClear());
expect(getDashboardsSearch().value).toBe('');
expect(getDashboard('1')).toBeInTheDocument();
expect(getDashboard('2')).toBeInTheDocument();
});
it('Deduplicates the dashboards list', async () => { it('Deduplicates the dashboards list', async () => {
await userEvents.click(getDashboardsExpand()); await userEvents.click(getDashboardsExpand());
await userEvents.click(getFiltersInput()); await userEvents.click(getFiltersInput());

View File

@ -1,10 +1,10 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { useMemo } from 'react'; import { useEffect, useMemo, useState } from 'react';
import Skeleton from 'react-loading-skeleton'; import Skeleton from 'react-loading-skeleton';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { Checkbox, Icon, IconButton, Input, RadioButtonDot, useStyles2 } from '@grafana/ui'; import { Checkbox, FilterInput, IconButton, RadioButtonDot, useStyles2 } from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization'; import { t, Trans } from 'app/core/internationalization';
import { NodesMap, TreeScope } from './types'; import { NodesMap, TreeScope } from './types';
@ -37,18 +37,24 @@ export function ScopesTreeLevel({
const scopeNames = scopes.map(({ scopeName }) => scopeName); const scopeNames = scopes.map(({ scopeName }) => scopeName);
const anyChildExpanded = childNodesArr.some(({ isExpanded }) => isExpanded); const anyChildExpanded = childNodesArr.some(({ isExpanded }) => isExpanded);
const [queryValue, setQueryValue] = useState(node.query);
useEffect(() => {
setQueryValue(node.query);
}, [node.query]);
const onQueryUpdate = useMemo(() => debounce(onNodeUpdate, 500), [onNodeUpdate]); const onQueryUpdate = useMemo(() => debounce(onNodeUpdate, 500), [onNodeUpdate]);
return ( return (
<> <>
{!anyChildExpanded && ( {!anyChildExpanded && (
<Input <FilterInput
prefix={<Icon name="filter" />}
className={styles.searchInput}
placeholder={t('scopes.tree.search', 'Search')} placeholder={t('scopes.tree.search', 'Search')}
defaultValue={node.query} value={queryValue}
className={styles.searchInput}
data-testid={`scopes-tree-${nodeId}-search`} data-testid={`scopes-tree-${nodeId}-search`}
onInput={(evt) => onQueryUpdate(nodePath, true, evt.currentTarget.value)} onChange={(value) => {
setQueryValue(value);
onQueryUpdate(nodePath, true, value);
}}
/> />
)} )}

View File

@ -310,7 +310,6 @@ const selectors = {
expand: 'scopes-dashboards-expand', expand: 'scopes-dashboards-expand',
container: 'scopes-dashboards-container', container: 'scopes-dashboards-container',
search: 'scopes-dashboards-search', search: 'scopes-dashboards-search',
clear: 'scopes-dashboards-clear',
loading: 'scopes-dashboards-loading', loading: 'scopes-dashboards-loading',
dashboard: (uid: string) => `scopes-dashboards-${uid}`, dashboard: (uid: string) => `scopes-dashboards-${uid}`,
}, },
@ -326,13 +325,12 @@ export const getDashboardsExpand = () => screen.getByTestId(selectors.dashboards
export const queryDashboardsContainer = () => screen.queryByTestId(selectors.dashboards.container); export const queryDashboardsContainer = () => screen.queryByTestId(selectors.dashboards.container);
export const getDashboardsContainer = () => screen.getByTestId(selectors.dashboards.container); export const getDashboardsContainer = () => screen.getByTestId(selectors.dashboards.container);
export const getDashboardsSearch = () => screen.getByTestId<HTMLInputElement>(selectors.dashboards.search); export const getDashboardsSearch = () => screen.getByTestId<HTMLInputElement>(selectors.dashboards.search);
export const getDashboardsClear = () => screen.getByTestId(selectors.dashboards.clear);
export const queryAllDashboard = (uid: string) => screen.queryAllByTestId(selectors.dashboards.dashboard(uid)); export const queryAllDashboard = (uid: string) => screen.queryAllByTestId(selectors.dashboards.dashboard(uid));
export const queryDashboard = (uid: string) => screen.queryByTestId(selectors.dashboards.dashboard(uid)); export const queryDashboard = (uid: string) => screen.queryByTestId(selectors.dashboards.dashboard(uid));
export const getDashboard = (uid: string) => screen.getByTestId(selectors.dashboards.dashboard(uid)); export const getDashboard = (uid: string) => screen.getByTestId(selectors.dashboards.dashboard(uid));
export const getApplicationsExpand = () => screen.getByTestId(selectors.tree.expand('applications')); export const getApplicationsExpand = () => screen.getByTestId(selectors.tree.expand('applications'));
export const getApplicationsSearch = () => screen.getByTestId(selectors.tree.search('applications')); export const getApplicationsSearch = () => screen.getByTestId<HTMLInputElement>(selectors.tree.search('applications'));
export const queryApplicationsSlothPictureFactoryTitle = () => export const queryApplicationsSlothPictureFactoryTitle = () =>
screen.queryByTestId(selectors.tree.title('applications-slothPictureFactory')); screen.queryByTestId(selectors.tree.title('applications-slothPictureFactory'));
export const getApplicationsSlothPictureFactoryTitle = () => export const getApplicationsSlothPictureFactoryTitle = () =>

View File

@ -1656,7 +1656,6 @@
"title": "Select scopes" "title": "Select scopes"
}, },
"suggestedDashboards": { "suggestedDashboards": {
"clear": "Clear search",
"loading": "Loading dashboards", "loading": "Loading dashboards",
"search": "Search", "search": "Search",
"toggle": { "toggle": {

View File

@ -1656,7 +1656,6 @@
"title": "Ŝęľęčŧ şčőpęş" "title": "Ŝęľęčŧ şčőpęş"
}, },
"suggestedDashboards": { "suggestedDashboards": {
"clear": "Cľęäř şęäřčĥ",
"loading": "Ŀőäđįʼnģ đäşĥþőäřđş", "loading": "Ŀőäđįʼnģ đäşĥþőäřđş",
"search": "Ŝęäřčĥ", "search": "Ŝęäřčĥ",
"toggle": { "toggle": {