Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
AD_BIDS_PUBLISHER_DIMENSION,
} from "@rilldata/web-common/features/dashboards/stores/test-data/data";
import { compressUrlParams } from "@rilldata/web-common/features/dashboards/url-state/compression.ts";
import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus.ts";
import { m } from "@rilldata/web-common/lib/i18n/gen/messages";
import { ExploreStateURLParams } from "@rilldata/web-common/features/dashboards/url-state/url-params.ts";
import { MetricsViewsProvider } from "@rilldata/web-common/features/metrics-views/providers/MetricsViewsProvider.svelte.ts";
import {
Expand Down Expand Up @@ -870,6 +872,104 @@ describe("dimensionFilterAction", () => {
filterManager.sortedFilterManagers.dimensions[0].selectedValues,
).toEqual(["Google"]);
});

// Click to filter from a chart, table, leaderboard or search while the dimension has a
// Contains filter. The click selects a concrete value, so the filter converts to Select
// the way it did before the filter managers existed, and the user is told about it.
describe("with a Contains filter applied", () => {
function createWithContainsFilter() {
const filterManager = createFilterManager();
filterManager.setUrlParams(
perMetricsViewParams({
[AD_BIDS_METRICS_NAME]: `${AD_BIDS_PUBLISHER_DIMENSION} LIKE '%oo%'`,
}),
);
const manager = filterManager.sortedFilterManagers.dimensions[0];
expect(manager.mode).toBe(DimensionFilterMode.Contains);
expect(manager.inputText).toBe("oo");

const emit = vi.spyOn(eventBus, "emit");
cleanups.push(() => emit.mockRestore());
return { filterManager, manager, emit };
}

it("toggleValue converts the filter to Select with the clicked value", () => {
const { filterManager, manager, emit } = createWithContainsFilter();

filterManager.dimensionFilterAction(
AD_BIDS_PUBLISHER_DIMENSION,
(manager) => manager.toggleValue("Google", false),
);

expect(manager.mode).toBe(DimensionFilterMode.Select);
expect(manager.inputText).toBe("");
expect(manager.selectedValues).toEqual(["Google"]);
expect(manager.expr).toEqual(
createInExpression(AD_BIDS_PUBLISHER_DIMENSION, ["Google"]),
);
expect(filterManager.topLevelJoiner.param[AD_BIDS_METRICS_NAME]).toBe(
`${AD_BIDS_PUBLISHER_DIMENSION} IN ('Google')`,
);
expect(emit).toHaveBeenCalledWith(
"notification",
expect.objectContaining({ message: m.filter_converted_to_select() }),
);
});

it("toggleValue keeps exclude when converting", () => {
const filterManager = createFilterManager();
filterManager.setUrlParams(
perMetricsViewParams({
[AD_BIDS_METRICS_NAME]: `${AD_BIDS_PUBLISHER_DIMENSION} NLIKE '%oo%'`,
}),
);

filterManager.dimensionFilterAction(
AD_BIDS_PUBLISHER_DIMENSION,
(manager) => manager.toggleValue("Google", false),
);

expect(filterManager.topLevelJoiner.param[AD_BIDS_METRICS_NAME]).toBe(
`${AD_BIDS_PUBLISHER_DIMENSION} NIN ('Google')`,
);
});

it("appendSelectedValues converts the filter to Select with the added values", () => {
const { filterManager, manager, emit } = createWithContainsFilter();

// The callback return type is `any`, hence the cast.
const added = filterManager.dimensionFilterAction(
AD_BIDS_PUBLISHER_DIMENSION,
(manager) => manager.appendSelectedValues(["Google", "Facebook"]),
) as string[];

expect(added).toEqual(["Google", "Facebook"]);
expect(manager.mode).toBe(DimensionFilterMode.Select);
expect(manager.selectedValues).toEqual(["Google", "Facebook"]);
expect(filterManager.topLevelJoiner.param[AD_BIDS_METRICS_NAME]).toBe(
`${AD_BIDS_PUBLISHER_DIMENSION} IN ('Google','Facebook')`,
);
expect(emit).toHaveBeenCalledWith(
"notification",
expect.objectContaining({ message: m.filter_converted_to_select() }),
);
});

it("removeSelectedValues leaves the Contains filter alone", () => {
const { filterManager, manager, emit } = createWithContainsFilter();

filterManager.dimensionFilterAction(
AD_BIDS_PUBLISHER_DIMENSION,
(manager) => manager.removeSelectedValues(["Google"]),
);

expect(manager.mode).toBe(DimensionFilterMode.Contains);
expect(filterManager.topLevelJoiner.param[AD_BIDS_METRICS_NAME]).toBe(
`${AD_BIDS_PUBLISHER_DIMENSION} LIKE '%oo%'`,
);
expect(emit).not.toHaveBeenCalledWith("notification", expect.anything());
});
});
});

describe("clear", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export class DimensionFilterManager {
}

public toggleValue(dimensionValue: string, isExclusiveFilter: boolean) {
this.leaveContainsMode();
const inIdx = this.selectedValues.findIndex((v) => v === dimensionValue);

if (inIdx === -1) {
Expand All @@ -156,6 +157,7 @@ export class DimensionFilterManager {
}

public appendSelectedValues(dimensionValues: string[]) {
this.leaveContainsMode();
const newValues = dimensionValues.filter(
(v) => !this.selectedValues.includes(v),
);
Expand All @@ -171,6 +173,18 @@ export class DimensionFilterManager {
this.commit();
}

/**
* Click to filter (charts, tables, leaderboards, search) selects concrete values,
* which a Contains filter cannot hold. So the filter converts to Select,
* and `commit` reports the conversion.
* Contains mode keeps no selected values, so the clicked value becomes the whole selection.
*/
private leaveContainsMode() {
if (this.mode !== DimensionFilterMode.Contains) return;
this.mode = DimensionFilterMode.Select;
this.inputText = "";
}

public setInList(values: string[], exclude: boolean) {
this.mode = DimensionFilterMode.InList;
this.selectedValues = values;
Expand Down
Loading