Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(QuickFilter): Add results count #193

Merged
merged 5 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion e2e/fixtures/pages/ExploreProfilesPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,21 @@ export class ExploreProfilesPage extends PyroscopePage {
return this.getByLabel('Quick filter');
}

async assertQuickFilter(explectedPlaceholder: string, expectedValue: string) {
async assertQuickFilter(explectedPlaceholder: string, expectedValue: string, expectedResultsCount: number) {
await expect(await this.getQuickFilterInput().getAttribute('placeholder')).toBe(explectedPlaceholder);
await expect(this.getQuickFilterInput()).toHaveValue(expectedValue);
await this.assertQuickFilterResultsCount(expectedResultsCount);
}

async enterQuickFilterText(searchText: string) {
await this.getQuickFilterInput().fill(searchText);
await this.waitForTimeout(250); // see SceneQuickFilter.DEBOUNCE_DELAY
}

async assertQuickFilterResultsCount(expectedCount: number) {
await expect(this.getByTestId('quick-filter-results-count')).toHaveText(String(expectedCount));
}

/* Layout switcher */

getLayoutSwitcher() {
Expand Down
6 changes: 5 additions & 1 deletion e2e/tests/all-services-view/all-services.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test.describe('All services view', () => {

// body scene controls
await exploreProfilesPage.assertSelectedProfileType('process_cpu/cpu');
await exploreProfilesPage.assertQuickFilter('Search services (comma-separated regexes are supported)', '');
await exploreProfilesPage.assertQuickFilter('Search services (comma-separated regexes are supported)', '', 3);
await exploreProfilesPage.assertSelectedLayout('Grid');

// body
Expand Down Expand Up @@ -53,8 +53,12 @@ test.describe('All services view', () => {
});

test('Quick filter', async ({ exploreProfilesPage }) => {
await exploreProfilesPage.assertQuickFilterResultsCount(3);

await exploreProfilesPage.enterQuickFilterText('sharing,load');

await exploreProfilesPage.assertQuickFilterResultsCount(2);

await expect(exploreProfilesPage.getPanels()).toHaveCount(2);
await expect(exploreProfilesPage.getPanelByTitle('load-generator')).toBeVisible();
await expect(exploreProfilesPage.getPanelByTitle('ride-sharing-app')).toBeVisible();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion e2e/tests/favorites-view/favorites-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test.describe('Favorites view', () => {
await exploreProfilesPage.assertSelectedTimeRange('2024-03-13 19:00:00 to 2024-03-13 19:50:00');

// body scene controls
await exploreProfilesPage.assertQuickFilter('Search favorites (comma-separated regexes are supported)', '');
await exploreProfilesPage.assertQuickFilter('Search favorites (comma-separated regexes are supported)', '', 4);
await exploreProfilesPage.assertSelectedLayout('Grid');
await exploreProfilesPage.assertHideNoDataSwitcher(false);

Expand All @@ -38,8 +38,12 @@ test.describe('Favorites view', () => {
});

test('Quick filter', async ({ exploreProfilesPage }) => {
await exploreProfilesPage.assertQuickFilterResultsCount(4);

await exploreProfilesPage.enterQuickFilterText('load,ride');

await exploreProfilesPage.assertQuickFilterResultsCount(3);

await expect(exploreProfilesPage.getPanels()).toHaveCount(3);
await expect(exploreProfilesPage.getPanelByTitle('load-generator · cpu (process_cpu)')).toBeVisible();
await expect(exploreProfilesPage.getPanelByTitle('ride-sharing-app · inuse_space (memory)')).toBeVisible();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion e2e/tests/labels-view/labels.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test.describe('Labels view', () => {
await exploreProfilesPage.assertSelectedService('ride-sharing-app');
await exploreProfilesPage.assertSelectedProfileType('process_cpu/cpu');
await exploreProfilesPage.assertFilters([]);
await exploreProfilesPage.assertQuickFilter('Search labels (comma-separated regexes are supported)', '');
await exploreProfilesPage.assertQuickFilter('Search labels (comma-separated regexes are supported)', '', 7);
await exploreProfilesPage.assertSelectedPanelType('Time series');
await exploreProfilesPage.assertSelectedLayout('Grid');
await exploreProfilesPage.assertHideNoDataSwitcher(false);
Expand Down Expand Up @@ -153,8 +153,12 @@ test.describe('Labels view', () => {
});

test('Quick filter', async ({ exploreProfilesPage }) => {
await exploreProfilesPage.assertQuickFilterResultsCount(3);

await exploreProfilesPage.enterQuickFilterText('us-east');

await exploreProfilesPage.assertQuickFilterResultsCount(1);

await expect(exploreProfilesPage.getGroupByPanels()).toHaveCount(1);
await expect(exploreProfilesPage.getPanelByTitle('us-east')).toBeVisible();
});
Expand Down Expand Up @@ -229,8 +233,12 @@ test.describe('Labels view', () => {
});

test('Quick filter', async ({ exploreProfilesPage }) => {
await exploreProfilesPage.assertQuickFilterResultsCount(7);

await exploreProfilesPage.enterQuickFilterText('region,vehicle');

await exploreProfilesPage.assertQuickFilterResultsCount(2);

await expect(exploreProfilesPage.getGroupByPanels()).toHaveCount(2);
await expect(exploreProfilesPage.getPanelByTitle('region (3)')).toBeVisible();
await expect(exploreProfilesPage.getPanelByTitle('vehicle (4)')).toBeVisible();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion e2e/tests/profile-types-view/profile-types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async function assertMainUiElements(exploreProfilesPage: ExploreProfilesPage) {

// body scene controls
await exploreProfilesPage.assertSelectedService('ride-sharing-app');
await exploreProfilesPage.assertQuickFilter('Search profile types (comma-separated regexes are supported)', '');
await exploreProfilesPage.assertQuickFilter('Search profile types (comma-separated regexes are supported)', '', 6);
await exploreProfilesPage.assertSelectedLayout('Grid');
}

Expand All @@ -37,8 +37,12 @@ test.describe('Profile types view', () => {
});

test('Quick filter', async ({ exploreProfilesPage }) => {
await exploreProfilesPage.assertQuickFilterResultsCount(6);

await exploreProfilesPage.enterQuickFilterText('samples,alloc_space');

await exploreProfilesPage.assertQuickFilterResultsCount(2);

await expect(exploreProfilesPage.getPanels()).toHaveCount(2);
await expect(exploreProfilesPage.getPanelByTitle('samples (process_cpu)')).toBeVisible();
await expect(exploreProfilesPage.getPanelByTitle('alloc_space (memory)')).toBeVisible();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ export class SceneByVariableRepeaterGrid extends SceneObjectBase<SceneByVariable
subscribeToQuickFilterChange() {
const quickFilter = sceneGraph.findByKeyAndType(this, 'quick-filter', SceneQuickFilter);

this.subscribeToState((newState, prevState) => {
if (newState.items.length !== prevState.items.length) {
quickFilter.setResultsCount(newState.items.length);
}
});

const onChangeState = (newState: SceneQuickFilterState, prevState?: SceneQuickFilterState) => {
if (newState.searchText !== prevState?.searchText) {
this.renderGridItems();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import {
SceneComponentProps,
SceneObjectBase,
SceneObjectState,
SceneObjectUrlSyncConfig,
SceneObjectUrlValues,
} from '@grafana/scenes';
import { Icon, IconButton, Input, useStyles2 } from '@grafana/ui';
import { Icon, IconButton, Input, Tag, useStyles2 } from '@grafana/ui';
import { reportInteraction } from '@shared/domain/reportInteraction';
import React from 'react';

export interface SceneQuickFilterState extends SceneObjectState {
placeholder: string;
searchText: string;
onChange?: (searchText: string) => void;
resultsCount: string;
}

export class SceneQuickFilter extends SceneObjectBase<SceneQuickFilterState> {
Expand All @@ -28,13 +30,18 @@ export class SceneQuickFilter extends SceneObjectBase<SceneQuickFilterState> {
key: 'quick-filter',
placeholder,
searchText: SceneQuickFilter.DEFAULT_SEARCH_TEXT,
resultsCount: '',
});
}

setPlaceholder(placeholder: string) {
this.setState({ placeholder });
}

setResultsCount(resultsCount: number) {
this.setState({ resultsCount: String(resultsCount) });
}

getUrlState() {
return {
searchText: this.state.searchText,
Expand All @@ -55,7 +62,11 @@ export class SceneQuickFilter extends SceneObjectBase<SceneQuickFilterState> {
this.setState({ searchText: e.target.value });
};

clear = () => {
reset() {
this.setState({ placeholder: '', searchText: '', resultsCount: '' });
}

clearSearchText = () => {
this.setState({ searchText: '' });
};

Expand All @@ -65,7 +76,7 @@ export class SceneQuickFilter extends SceneObjectBase<SceneQuickFilterState> {

static Component = ({ model }: SceneComponentProps<SceneQuickFilter>) => {
const styles = useStyles2(getStyles);
const { placeholder, searchText } = model.useState();
const { placeholder, searchText, resultsCount } = model.useState();

return (
<div className={styles.filter}>
Expand All @@ -75,11 +86,23 @@ export class SceneQuickFilter extends SceneObjectBase<SceneQuickFilterState> {
placeholder={placeholder}
value={searchText}
prefix={<Icon name="search" />}
suffix={<IconButton name="times" aria-label="Clear search" onClick={model.clear} />}
suffix={
<>
{resultsCount !== '' && (
<Tag
className={styles.resultsCount}
name={resultsCount}
colorIndex={9}
data-testid="quick-filter-results-count"
/>
)}
<IconButton name="times" aria-label="Clear search" onClick={model.clearSearchText} />
</>
}
onChange={model.onChange}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Escape') {
model.clear();
model.clearSearchText();
}
}}
onFocus={model.onFocus}
Expand All @@ -89,9 +112,16 @@ export class SceneQuickFilter extends SceneObjectBase<SceneQuickFilterState> {
};
}

const getStyles = () => ({
const getStyles = (theme: GrafanaTheme2) => ({
filter: css`
flex: 1;
min-width: 112px;
`,
resultsCount: css`
margin-right: ${theme.spacing(1)};
border-radius: 11px;
padding: 2px 8px;
color: ${theme.colors.text.primary};
background-color: ${theme.colors.background.secondary};
`,
});
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ export class SceneExploreAllServices extends SceneObjectBase<SceneExploreAllServ
}

onActivate() {
const quickFilter = sceneGraph.findByKeyAndType(this, 'quick-filter', SceneQuickFilter);
quickFilter.setPlaceholder('Search services (comma-separated regexes are supported)');
sceneGraph
.findByKeyAndType(this, 'quick-filter', SceneQuickFilter)
.setPlaceholder('Search services (comma-separated regexes are supported)');
}

// see SceneProfilesExplorer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ export class SceneExploreFavorites extends SceneObjectBase<SceneExploreFavorites
}

onActivate() {
const quickFilter = sceneGraph.findByKeyAndType(this, 'quick-filter', SceneQuickFilter);
quickFilter.setPlaceholder('Search favorites (comma-separated regexes are supported)');
sceneGraph
.findByKeyAndType(this, 'quick-filter', SceneQuickFilter)
.setPlaceholder('Search favorites (comma-separated regexes are supported)');

const expandPanelSub = this.subscribeToEvent(EventExpandPanel, async (event) => {
this.openExpandedPanelDrawer(event.payload.item);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class SceneGroupByLabels extends SceneObjectBase<SceneGroupByLabelsState>

return groupByVariable.subscribeToState((newState, prevState) => {
if (newState.value !== prevState?.value) {
quickFilter.clear();
quickFilter.clearSearchText();

this.renderBody(newState);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ export class SceneLabelValuesGrid extends SceneObjectBase<SceneLabelValuesGridSt
subscribeToQuickFilterChange() {
const quickFilter = sceneGraph.findByKeyAndType(this, 'quick-filter', SceneQuickFilter);

this.subscribeToState((newState, prevState) => {
if (newState.items.length !== prevState.items.length) {
quickFilter.setResultsCount(newState.items.length);
}
});

const onChangeState = (newState: SceneQuickFilterState, prevState?: SceneQuickFilterState) => {
if (newState.searchText !== prevState?.searchText) {
this.renderGridItems();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ export class SceneExploreServiceProfileTypes extends SceneObjectBase<SceneExplor
}

onActivate(item?: GridItemData) {
const quickFilter = sceneGraph.findByKeyAndType(this, 'quick-filter', SceneQuickFilter);
quickFilter.setPlaceholder('Search profile types (comma-separated regexes are supported)');
sceneGraph
.findByKeyAndType(this, 'quick-filter', SceneQuickFilter)
.setPlaceholder('Search profile types (comma-separated regexes are supported)');

if (item) {
this.initVariables(item);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ export class SceneProfilesExplorer extends SceneObjectBase<SceneProfilesExplorer
}

resetVariables(nextExplorationType: string) {
sceneGraph.findByKeyAndType(this, 'quick-filter', SceneQuickFilter).clear();
sceneGraph.findByKeyAndType(this, 'quick-filter', SceneQuickFilter).reset();
sceneGraph.findByKeyAndType(this, 'groupBy', GroupByVariable).changeValueTo(GroupByVariable.DEFAULT_VALUE);
sceneGraph.findByKeyAndType(this, 'panel-type-switcher', ScenePanelTypeSwitcher).reset();

Expand Down
Loading