Skip to content

Commit

Permalink
Merge pull request #6034 from gooddata/ovel/apply-filters-phase-4
Browse files Browse the repository at this point in the history
feat: Apply filters follow up
  • Loading branch information
kandl authored Mar 11, 2025
2 parents d6ba8af + f53489b commit d552e94
Show file tree
Hide file tree
Showing 44 changed files with 755 additions and 169 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@gooddata/sdk-ui-all",
"comment": "Support of new filter mode Apply all at once in Dashboard",
"type": "none"
}
],
"packageName": "@gooddata/sdk-ui-all"
}
19 changes: 17 additions & 2 deletions libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
addAttributeFilter,
changeAttributeFilterSelection,
removeAttributeFilters,
resetFilterContextWorkingSelection,
} from "../../commands/filters.js";
import { CrossFiltering } from "../../commands/drill.js";
import { crossFilteringRequested, crossFilteringResolved } from "../../events/drill.js";
Expand All @@ -24,6 +25,7 @@ import { removeAttributeFiltersHandler } from "../filterContext/attributeFilter/
import isEmpty from "lodash/isEmpty.js";
import {
selectEnableCrossFilteringAliasTitles,
selectEnableDashboardFiltersApplyModes,
selectEnableDuplicatedLabelValuesInAttributeFilter,
} from "../../store/config/configSelectors.js";
import { selectAttributeFilterConfigsDisplayAsLabelMap } from "../../store/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
Expand Down Expand Up @@ -125,6 +127,12 @@ export function* crossFilteringHandler(ctx: DashboardContext, cmd: CrossFilterin

const correlation = `crossFiltering_${uuid()}`;

const enableDashboardFiltersApplyModes: ReturnType<typeof selectEnableDashboardFiltersApplyModes> =
yield select(selectEnableDashboardFiltersApplyModes);
if (enableDashboardFiltersApplyModes) {
yield put(resetFilterContextWorkingSelection());
}

// Cleanup of previous cross-filtering state
if (!shouldUpdateExistingCrossFiltering) {
yield call(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,13 @@ export function* changeAttributeDisplayFormHandler(
]),
);

if (!isWorkingSelectionChange || !enableDashboardFiltersApplyModes) {
const changedFilter: ReturnType<ReturnType<typeof selectFilterContextAttributeFilterByLocalId>> =
yield select(selectFilterContextAttributeFilterByLocalId(filterLocalId));
const changedFilter: ReturnType<ReturnType<typeof selectFilterContextAttributeFilterByLocalId>> =
yield select(selectFilterContextAttributeFilterByLocalId(filterLocalId));

invariant(
changedFilter,
"Inconsistent state in changeAttributeDisplayFormHandler, cannot update attribute filter for given local identifier.",
);
yield dispatchDashboardEvent(attributeDisplayFormChanged(ctx, changedFilter, cmd.correlationId));
yield call(dispatchFilterContextChanged, ctx, cmd);
}
invariant(
changedFilter,
"Inconsistent state in changeAttributeDisplayFormHandler, cannot update attribute filter for given local identifier.",
);
yield dispatchDashboardEvent(attributeDisplayFormChanged(ctx, changedFilter, cmd.correlationId));
yield call(dispatchFilterContextChanged, ctx, cmd);
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ export function* changeAttributeFilterSelectionHandler(
yield call(resetCrossFiltering, cmd);
}

if (!isWorkingSelectionChange || !enableDashboardFiltersApplyModes) {
yield dispatchDashboardEvent(attributeFilterSelectionChanged(ctx, changedFilter, cmd.correlationId));
yield call(dispatchFilterContextChanged, ctx, cmd);
}
yield dispatchDashboardEvent(attributeFilterSelectionChanged(ctx, changedFilter, cmd.correlationId));
yield call(dispatchFilterContextChanged, ctx, cmd);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import { selectEffectiveDateFilterOptions } from "../../store/dateFilterConfig/d
import { findDateFilterOptionByValue } from "../../../_staging/dateFilterConfig/dateFilterOptionMapping.js";
import { selectAttributeFilterConfigsOverrides } from "../../store/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
import { removeAttributeFilters } from "../../commands/filters.js";
import { selectCrossFilteringFiltersLocalIdentifiers } from "../../store/drill/drillSelectors.js";
import {
selectCrossFilteringFiltersLocalIdentifiers,
selectIsCrossFiltering,
} from "../../store/drill/drillSelectors.js";
import { drillActions } from "../../store/drill/index.js";
import { filterContextActions } from "../../store/filterContext/index.js";
import { selectEnableImmediateAttributeFilterDisplayAsLabelMigration } from "../../store/config/configSelectors.js";
Expand Down Expand Up @@ -74,5 +77,14 @@ export function* applyWorkingSelectionHandler(ctx: DashboardContext, cmd: IDashb
yield put(
filterContextActions.applyWorkingSelection({ enableImmediateAttributeFilterDisplayAsLabelMigration }),
);
const isCrossFiltering: ReturnType<typeof selectIsCrossFiltering> = yield select(selectIsCrossFiltering);

if (isCrossFiltering) {
yield call(resetCrossFiltering, cmd);
}
yield call(dispatchFilterContextChanged, ctx, cmd);
}

export function* resetWorkingSelectionHandler() {
yield put(filterContextActions.resetWorkingSelection());
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,21 @@ export function* changeDateFilterSelectionHandler(
);

if (!isWorkingSelectionChange) {
const affectedFilter: ReturnType<typeof selectFilterContextDateFilter> = cmd.payload.dataSet
? yield select(selectFilterContextDateFilterByDataSet(cmd.payload.dataSet))
: yield select(selectFilterContextDateFilter);

const isCrossFiltering = yield select(selectIsCrossFiltering);
if (isCrossFiltering) {
yield call(resetCrossFiltering, cmd);
}
}

yield dispatchDashboardEvent(
dateFilterChanged(ctx, affectedFilter, cmd.payload.dateFilterOptionLocalId, cmd.correlationId),
);
const affectedFilter: ReturnType<typeof selectFilterContextDateFilter> = cmd.payload.dataSet
? yield select(selectFilterContextDateFilterByDataSet(cmd.payload.dataSet))
: yield select(selectFilterContextDateFilter);

yield call(dispatchFilterContextChanged, ctx, cmd);
}
yield dispatchDashboardEvent(
dateFilterChanged(ctx, affectedFilter, cmd.payload.dateFilterOptionLocalId, cmd.correlationId),
);

yield call(dispatchFilterContextChanged, ctx, cmd);
}

function dateFilterSelectionToDateFilter(dateFilterSelection: DateFilterSelection): IDashboardDateFilter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import {
reloadFilterViews,
changeFilterContextSelectionByParams,
} from "../../commands/index.js";
import { selectFilterContextDefinition } from "../../store/filterContext/filterContextSelectors.js";
import {
selectFilterContextDefinition,
selectWorkingFilterContextDefinition,
} from "../../store/filterContext/filterContextSelectors.js";
import { selectCrossFilteringFiltersLocalIdentifiers } from "../../store/drill/drillSelectors.js";
import { selectFilterViews, filterViewsActions } from "../../store/filterViews/index.js";
import {
Expand All @@ -39,6 +42,11 @@ import { loadFilterViews } from "../dashboard/initializeDashboardHandler/loadFil
import { PromiseFnReturnType } from "../../types/sagas.js";
import { resetCrossFiltering } from "./common.js";
import { selectAttributeFilterConfigsOverrides } from "../../store/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
import { filterContextActions } from "../../store/filterContext/index.js";
import {
selectDashboardFiltersApplyMode,
selectEnableDashboardFiltersApplyModes,
} from "../../store/config/configSelectors.js";

function createFilterView(
ctx: DashboardContext,
Expand All @@ -53,9 +61,21 @@ export function* saveFilterViewHandler(ctx: DashboardContext, cmd: SaveFilterVie
throw Error("Dashboard ref must be provided.");
}

const filterContext: ReturnType<typeof selectFilterContextDefinition> = yield select(
const appliedFilterContext: ReturnType<typeof selectFilterContextDefinition> = yield select(
selectFilterContextDefinition,
);
const workingFilterContext: ReturnType<typeof selectWorkingFilterContextDefinition> = yield select(
selectWorkingFilterContextDefinition,
);
const filtersApplyMode: ReturnType<typeof selectDashboardFiltersApplyMode> = yield select(
selectDashboardFiltersApplyMode,
);
const enableDashboardFiltersApplyModes: ReturnType<typeof selectEnableDashboardFiltersApplyModes> =
yield select(selectEnableDashboardFiltersApplyModes);
const filterContext =
filtersApplyMode.mode === "ALL_AT_ONCE" && enableDashboardFiltersApplyModes
? workingFilterContext
: appliedFilterContext;

const virtualFilters: ReturnType<typeof selectCrossFilteringFiltersLocalIdentifiers> = yield select(
selectCrossFilteringFiltersLocalIdentifiers,
Expand Down Expand Up @@ -133,6 +153,11 @@ export function* applyFilterViewHandler(ctx: DashboardContext, cmd: ApplyFilterV

if (filterView) {
yield call(resetCrossFiltering, cmd);
const enableDashboardFiltersApplyModes: ReturnType<typeof selectEnableDashboardFiltersApplyModes> =
yield select(selectEnableDashboardFiltersApplyModes);
if (enableDashboardFiltersApplyModes) {
yield put(filterContextActions.resetWorkingSelection());
}
yield put(
changeFilterContextSelectionByParams({
filters: filterView.filterContext.filters,
Expand Down
3 changes: 2 additions & 1 deletion libs/sdk-ui-dashboard/src/model/commandHandlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ import { attributeHierarchyModifiedHandler } from "./widgets/attributeHierarchyM
import { addDateFilterHandler } from "./filterContext/dateFilter/addDateFilterHandler.js";
import { removeDateFiltersHandler } from "./filterContext/dateFilter/removeDateFiltersHandler.js";
import { moveDateFilterHandler } from "./filterContext/dateFilter/moveDateFilterHandler.js";
import { applyWorkingSelectionHandler } from "./filterContext/common.js";
import { applyWorkingSelectionHandler, resetWorkingSelectionHandler } from "./filterContext/common.js";
import { changeDateFilterWithDimensionModeHandler } from "./dashboard/changeDateFilterWithDimensionModeHandler.js";
import { changeDateFilterTitleHandler } from "./dashboard/changeDateFilterTitleHandler.js";
import { changeAttributeFilterLimitingItemsHandler } from "./dashboard/changeAttributeFilterLimitingItemsHandler.js";
Expand Down Expand Up @@ -146,6 +146,7 @@ export const DefaultCommandHandlers: {
"GDC.DASH/CMD.FILTER_CONTEXT.DATE_FILTER.REMOVE": removeDateFiltersHandler,
"GDC.DASH/CMD.FILTER_CONTEXT.DATE_FILTER.MOVE": moveDateFilterHandler,
"GDC.DASH/CMD.FILTER_CONTEXT.APPLY_WORKING_SELECTION": applyWorkingSelectionHandler,
"GDC.DASH/CMD.FILTER_CONTEXT.RESET_WORKING_SELECTION": resetWorkingSelectionHandler,
"GDC.DASH/CMD.ATTRIBUTE_FILTER_CONFIG.SET_MODE": changeAttributeFilterModeHandler,
"GDC.DASH/CMD.ATTRIBUTE_FILTER_CONFIG.SET_DISPLAY_AS_LABEL": changeAttributeFilterDisplayAsLabelHandler,
"GDC.DASH/CMD.ATTRIBUTE_FILTER_CONFIG.SET_LIMITING_ITEMS": changeAttributeFilterLimitingItemsHandler,
Expand Down
1 change: 1 addition & 0 deletions libs/sdk-ui-dashboard/src/model/commands/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export type DashboardCommandType =
| "GDC.DASH/CMD.FILTER_CONTEXT.FILTER_VIEW.CHANGE_DEFAULT_STATUS"
| "GDC.DASH/CMD.FILTER_CONTEXT.FILTER_VIEW.RELOAD"
| "GDC.DASH/CMD.FILTER_CONTEXT.APPLY_WORKING_SELECTION"
| "GDC.DASH/CMD.FILTER_CONTEXT.RESET_WORKING_SELECTION"
// @internal
| "GDC.DASH/CMD.USERS.LOAD_ALL"
| "GDC.DASH/CMD.INSIGHT_WIDGET.EXPORT_RAW"
Expand Down
35 changes: 35 additions & 0 deletions libs/sdk-ui-dashboard/src/model/commands/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1413,3 +1413,38 @@ export function applyFilterContextWorkingSelection(
correlationId,
};
}

/**
* Command for reseting all working filters.
* It resets the working filters in to same state as applied filters.
*
* @remarks
* This command ot usually used with setting dashboardApplyFiltersMode: ALL_AT_ONCE
*
* If you want to reset applied filters too, you may use {@link changeFilterContextSelection} and pass all available filters to it.
*
* @remarks
* See {@link applyFilterContextWorkingSelection} for a factory function that will help you create this command.
*
* @alpha
*/
export interface ResetFilterContextWorkingSelection extends IDashboardCommand {
readonly type: "GDC.DASH/CMD.FILTER_CONTEXT.RESET_WORKING_SELECTION";
}

/**
* Creates the {@link ResetFilterContextWorkingSelection} command.
*
* @param correlationId - specify correlation id. It will be included in all events that will be emitted during the command processing.
* @returns apply reset working filters command
*
* @alpha
*/
export function resetFilterContextWorkingSelection(
correlationId?: string,
): ResetFilterContextWorkingSelection {
return {
type: "GDC.DASH/CMD.FILTER_CONTEXT.RESET_WORKING_SELECTION",
correlationId,
};
}
4 changes: 4 additions & 0 deletions libs/sdk-ui-dashboard/src/model/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
SetFilterViewAsDefault,
ReloadFilterViews,
ApplyFilterContextWorkingSelection,
ResetFilterContextWorkingSelection,
} from "./filters.js";
import {
ChangeInsightWidgetFilterSettings,
Expand Down Expand Up @@ -215,6 +216,7 @@ export type {
SetFilterViewAsDefaultPayload,
ReloadFilterViews,
ApplyFilterContextWorkingSelection,
ResetFilterContextWorkingSelection,
} from "./filters.js";
export {
changeDateFilterSelection,
Expand Down Expand Up @@ -244,6 +246,7 @@ export {
setFilterViewAsDefault,
reloadFilterViews,
applyFilterContextWorkingSelection,
resetFilterContextWorkingSelection,
} from "./filters.js";

export type {
Expand Down Expand Up @@ -619,6 +622,7 @@ export type DashboardCommands =
| ReloadFilterViews
| ToggleLayoutSectionHeaders
| ApplyFilterContextWorkingSelection
| ResetFilterContextWorkingSelection
//internal
| SetScreenSize
| LoadAllWorkspaceUsers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,15 @@ const applyWorkingSelection: FilterContextReducer<PayloadAction<IApplyWorkingSel
//
//

const resetWorkingSelection: FilterContextReducer<PayloadAction> = (state) => {
invariant(state.workingFilterContextDefinition, "Attempt to edit uninitialized working filter context");
state.workingFilterContextDefinition = { filters: [] };
};

//
//
//

export const filterContextReducers = {
setFilterContext,
updateFilterContextIdentity,
Expand All @@ -772,4 +781,5 @@ export const filterContextReducers = {
changeLimitingItems,
setPreloadedAttributesWithReferences,
applyWorkingSelection,
resetWorkingSelection,
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import {
isDashboardDateFilterWithDimension,
isObjRef,
isDashboardCommonDateFilter,
isDashboardDateFilter,
objRefToString,
getAttributeElementsItems,
} from "@gooddata/sdk-model";
import { ObjRefMap, newDisplayFormMap } from "../../../_staging/metadata/objRefMap.js";
Expand All @@ -34,7 +32,7 @@ import keyBy from "lodash/keyBy.js";
import keys from "lodash/keys.js";
import sortBy from "lodash/sortBy.js";
import { selectEnableImmediateAttributeFilterDisplayAsLabelMigration } from "../config/configSelectors.js";
import { applyFilterContext } from "./filterContextUtils.js";
import { applyFilterContext, getFilterIdentifier } from "./filterContextUtils.js";

const selectSelf = createSelector(
(state: DashboardState) => state,
Expand Down Expand Up @@ -140,33 +138,13 @@ export const selectIsWorkingFilterContextChanged: DashboardSelector<boolean | un
selectEnableImmediateAttributeFilterDisplayAsLabelMigration,
(filterContext, workingFilterContext, enableImmediateAttributeFilterDisplayAsLabelMigration) => {
if (filterContext.filters.length !== workingFilterContext.filters.length) {
return false;
}

function getFilterIdentifier(filter: FilterContextItem): string {
if (isDashboardAttributeFilter(filter)) {
const localIdentifier = filter.attributeFilter.localIdentifier;
if (!localIdentifier) {
console.warn(
"Attribute filter without localIdentifier found. Using displayForm as fallback which may not be reliable.",
);
return objRefToString(filter.attributeFilter.displayForm);
}
return localIdentifier;
}
if (isDashboardDateFilter(filter)) {
return (
(filter.dateFilter.dataSet && objRefToString(filter.dateFilter.dataSet)) ??
"default_date_filter"
);
}
throw new Error("Unknown filter type");
return true;
}

const appliedFilters = keyBy(filterContext.filters, getFilterIdentifier);
const workingFilters = keyBy(workingFilterContext.filters, getFilterIdentifier);

return keys(appliedFilters)
return !keys(appliedFilters)
.map((key): boolean => {
const appliedFilter = appliedFilters[key];
const workingFilter = workingFilters[key];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
IDashboardAttributeFilter,
IDashboardDateFilter,
IFilterContextDefinition,
objRefToString,
} from "@gooddata/sdk-model";
import type { IWorkingFilterContextDefinition } from "./filterContextState.js";

Expand Down Expand Up @@ -97,3 +98,25 @@ export function applyFilterContext(
filters: [workingCommonDateFilter, ...filters],
};
}

/**
* @internal
*/
export function getFilterIdentifier(filter: FilterContextItem): string {
if (isDashboardAttributeFilter(filter)) {
const localIdentifier = filter.attributeFilter.localIdentifier;
if (!localIdentifier) {
console.warn(
"Attribute filter without localIdentifier found. Using displayForm as fallback which may not be reliable.",
);
return objRefToString(filter.attributeFilter.displayForm);
}
return localIdentifier;
}
if (isDashboardDateFilter(filter)) {
return (
(filter.dateFilter.dataSet && objRefToString(filter.dateFilter.dataSet)) ?? "default_date_filter"
);
}
throw new Error("Unknown filter type");
}
Loading

0 comments on commit d552e94

Please sign in to comment.