Skip to content

Commit 8455983

Browse files
committed
refactor: update frontend to use FilterGroup and FilterCondition types
1 parent 0ba9005 commit 8455983

5 files changed

Lines changed: 140 additions & 63 deletions

File tree

frontend/src/components/data-table/filters.ts

Lines changed: 87 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
/* Copyright 2026 Marimo. All rights reserved. */
22
"use no memo";
33

4-
import type { RowData } from "@tanstack/react-table";
4+
import type { ColumnFiltersState, RowData } from "@tanstack/react-table";
55
import type { DataType } from "@/core/kernel/messages";
6-
import type { ConditionType } from "@/plugins/impl/data-frames/schema";
6+
import type {
7+
FilterConditionType,
8+
FilterGroupType,
9+
} from "@/plugins/impl/data-frames/schema";
710
import type { ColumnId } from "@/plugins/impl/data-frames/types";
811
import type { OperatorType } from "@/plugins/impl/data-frames/utils/operators";
912
import { assertNever } from "@/utils/assertNever";
@@ -84,113 +87,145 @@ export type ColumnFilterForType<T extends FilterType> = T extends FilterType
8487
export function filterToFilterCondition(
8588
columnIdString: string,
8689
filter: ColumnFilterValue | undefined,
87-
): ConditionType[] | ConditionType {
90+
): FilterConditionType[] {
8891
if (!filter) {
8992
return [];
9093
}
9194
const columnId = columnIdString as ColumnId;
9295

9396
if (filter.operator === "is_null" || filter.operator === "is_not_null") {
94-
return {
95-
column_id: columnId,
96-
operator: filter.operator,
97-
value: undefined,
98-
};
97+
return [
98+
{
99+
column_id: columnId,
100+
operator: filter.operator,
101+
value: undefined,
102+
type: "condition",
103+
negate: false,
104+
},
105+
];
99106
}
100107

101108
switch (filter.type) {
102109
case "number": {
103-
const conditions: ConditionType[] = [];
110+
const conditions: FilterConditionType[] = [];
104111
if (filter.min !== undefined) {
105112
conditions.push({
106113
column_id: columnId,
107114
operator: ">=",
108115
value: filter.min,
116+
type: "condition",
117+
negate: false,
109118
});
110119
}
111120
if (filter.max !== undefined) {
112121
conditions.push({
113122
column_id: columnId,
114123
operator: "<=",
115124
value: filter.max,
125+
type: "condition",
126+
negate: false,
116127
});
117128
}
118129
return conditions;
119130
}
120131
case "text":
121-
return {
122-
column_id: columnId,
123-
operator: filter.operator,
124-
value: filter.text,
125-
};
132+
return [
133+
{
134+
column_id: columnId,
135+
operator: filter.operator,
136+
value: filter.text,
137+
type: "condition",
138+
negate: false,
139+
},
140+
];
126141
case "datetime": {
127-
const conditions: ConditionType[] = [];
142+
const conditions: FilterConditionType[] = [];
128143
if (filter.min !== undefined) {
129144
conditions.push({
130145
column_id: columnId,
131146
operator: ">=",
132147
value: filter.min.toISOString(),
148+
type: "condition",
149+
negate: false,
133150
});
134151
}
135152
if (filter.max !== undefined) {
136153
conditions.push({
137154
column_id: columnId,
138155
operator: "<=",
139156
value: filter.max.toISOString(),
157+
type: "condition",
158+
negate: false,
140159
});
141160
}
142161
return conditions;
143162
}
144163
case "date": {
145-
const conditions: ConditionType[] = [];
164+
const conditions: FilterConditionType[] = [];
146165
if (filter.min !== undefined) {
147166
conditions.push({
148167
column_id: columnId,
149168
operator: ">=",
150169
value: filter.min.toISOString(),
170+
type: "condition",
171+
negate: false,
151172
});
152173
}
153174
if (filter.max !== undefined) {
154175
conditions.push({
155176
column_id: columnId,
156177
operator: "<=",
157178
value: filter.max.toISOString(),
179+
type: "condition",
180+
negate: false,
158181
});
159182
}
160183
return conditions;
161184
}
162185
case "time": {
163-
const conditions: ConditionType[] = [];
186+
const conditions: FilterConditionType[] = [];
164187
if (filter.min !== undefined) {
165188
conditions.push({
166189
column_id: columnId,
167190
operator: ">=",
168191
value: filter.min.toISOString(),
192+
type: "condition",
193+
negate: false,
169194
});
170195
}
171196
if (filter.max !== undefined) {
172197
conditions.push({
173198
column_id: columnId,
174199
operator: "<=",
175200
value: filter.max.toISOString(),
201+
type: "condition",
202+
negate: false,
176203
});
177204
}
178205
return conditions;
179206
}
180207
case "boolean":
181208
if (filter.value) {
182-
return {
183-
column_id: columnId,
184-
operator: "is_true",
185-
value: undefined,
186-
};
209+
return [
210+
{
211+
column_id: columnId,
212+
operator: "is_true",
213+
value: undefined,
214+
type: "condition",
215+
negate: false,
216+
},
217+
];
187218
}
188219
if (!filter.value) {
189-
return {
190-
column_id: columnId,
191-
operator: "is_false",
192-
value: undefined,
193-
};
220+
return [
221+
{
222+
column_id: columnId,
223+
operator: "is_false",
224+
value: undefined,
225+
type: "condition",
226+
negate: false,
227+
},
228+
];
194229
}
195230

196231
return [];
@@ -200,16 +235,35 @@ export function filterToFilterCondition(
200235
Logger.warn("Invalid operator for select filter", {
201236
operator: filter.operator,
202237
});
203-
operator = "in"; // default to in operator
238+
operator = "in";
204239
}
205-
return {
206-
column_id: columnId,
207-
operator,
208-
value: filter.options,
209-
};
240+
return [
241+
{
242+
column_id: columnId,
243+
operator,
244+
value: filter.options,
245+
type: "condition",
246+
negate: false,
247+
},
248+
];
210249
}
211250

212251
default:
213252
assertNever(filter);
214253
}
215254
}
255+
256+
export function filtersToFilterGroup(
257+
columnFilters: ColumnFiltersState,
258+
): FilterGroupType {
259+
const conditions = columnFilters.flatMap((filter) =>
260+
filterToFilterCondition(filter.id, filter.value as ColumnFilterValue),
261+
);
262+
// To maintain existing behavior "and" all the conditions
263+
return {
264+
type: "group",
265+
operator: "and",
266+
children: conditions,
267+
negate: false,
268+
};
269+
}

frontend/src/plugins/impl/DataTablePlugin.tsx

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ import { TablePanel } from "@/components/data-table/charts/charts";
2828
import { hasChart } from "@/components/data-table/charts/storage";
2929
import { ColumnChartSpecModel } from "@/components/data-table/column-summary/chart-spec-model";
3030
import { ColumnChartContext } from "@/components/data-table/column-summary/column-summary";
31-
import {
32-
type ColumnFilterValue,
33-
filterToFilterCondition,
34-
} from "@/components/data-table/filters";
31+
import { filtersToFilterGroup } from "@/components/data-table/filters";
3532
import { usePanelOwnership } from "@/components/data-table/hooks/use-panel-ownership";
3633
import { LoadingTable } from "@/components/data-table/loading-table";
3734
import {
@@ -85,8 +82,8 @@ import { rpc } from "../core/rpc";
8582
import { Banner } from "./common/error-banner";
8683
import { Labeled } from "./common/labeled";
8784
import {
88-
ConditionSchema,
89-
type ConditionType,
85+
FilterGroupSchema,
86+
type FilterGroupType,
9087
columnToFieldTypesSchema,
9188
} from "./data-frames/schema";
9289

@@ -212,7 +209,7 @@ type DataTableFunctions = {
212209
descending: boolean;
213210
}[];
214211
query?: string;
215-
filters?: ConditionType[];
212+
filters?: FilterGroupType;
216213
page_number: number;
217214
page_size: number;
218215
max_columns?: number | null;
@@ -311,7 +308,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
311308
)
312309
.optional(),
313310
query: z.string().optional(),
314-
filters: z.array(ConditionSchema).optional(),
311+
filters: FilterGroupSchema.optional(),
315312
page_number: z.number(),
316313
page_size: z.number(),
317314
max_columns: z.number().nullable().optional(),
@@ -577,12 +574,7 @@ export const LoadingDataTableComponent = memo(
577574
query: searchQuery,
578575
page_number: paginationState.pageIndex,
579576
page_size: paginationState.pageSize,
580-
filters: filters.flatMap((filter) => {
581-
return filterToFilterCondition(
582-
filter.id,
583-
filter.value as ColumnFilterValue,
584-
);
585-
}),
577+
filters: filtersToFilterGroup(filters),
586578
});
587579

588580
if (canShowInitialPage) {
@@ -640,12 +632,7 @@ export const LoadingDataTableComponent = memo(
640632
page_size: 1,
641633
sort: sortArgs,
642634
query: searchQuery,
643-
filters: filters.flatMap((filter) => {
644-
return filterToFilterCondition(
645-
filter.id,
646-
filter.value as ColumnFilterValue,
647-
);
648-
}),
635+
filters: filtersToFilterGroup(filters),
649636
// Do not clamp number of columns since we are viewing a single row
650637
max_columns: null,
651638
});

frontend/src/plugins/impl/data-frames/DataFramePlugin.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import { LoadingDataTableComponent, TableProviders } from "../DataTablePlugin";
2929
import type { DataType } from "../vega/vega-loader";
3030
import { TransformPanel, type TransformPanelHandle } from "./panel";
3131
import {
32-
ConditionSchema,
33-
type ConditionType,
32+
FilterGroupSchema,
33+
type FilterGroupType,
3434
columnToFieldTypesSchema,
3535
type Transformations,
3636
} from "./schema";
@@ -75,7 +75,7 @@ type PluginFunctions = {
7575
descending: boolean;
7676
}[];
7777
query?: string;
78-
filters?: ConditionType[];
78+
filters?: FilterGroupType;
7979
page_number: number;
8080
page_size: number;
8181
}) => Promise<{
@@ -138,7 +138,7 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
138138
)
139139
.optional(),
140140
query: z.string().optional(),
141-
filters: z.array(ConditionSchema).optional(),
141+
filters: FilterGroupSchema.optional(),
142142
page_number: z.number(),
143143
page_size: z.number(),
144144
}),

frontend/src/plugins/impl/data-frames/schema.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,37 @@ const SortColumnTransformSchema = z.object({
7373
.default("last"),
7474
});
7575

76-
export const ConditionSchema = z
76+
export function createRangeSchema<T extends z.ZodTypeAny>(itemSchema: T) {
77+
return z.object({
78+
min: itemSchema,
79+
max: itemSchema,
80+
});
81+
}
82+
83+
export const FilterConditionSchema = z
7784
.object({
7885
column_id: column_id,
7986
operator: z
8087
.enum(Object.keys(ALL_OPERATORS) as [OperatorType, ...OperatorType[]])
8188
.describe(FieldOptions.of({ label: " " })),
89+
type: z.literal("condition").default("condition"),
8290
value: z.any().describe(FieldOptions.of({ label: "Value" })),
91+
negate: z.boolean().default(false),
8392
})
8493
.describe(FieldOptions.of({ direction: "row", special: "column_filter" }));
85-
export type ConditionType = z.infer<typeof ConditionSchema>;
94+
export type FilterConditionType = z.infer<typeof FilterConditionSchema>;
95+
96+
export const FilterGroupSchema: z.ZodType = z.lazy(() =>
97+
z.object({
98+
type: z.literal("group").default("group"),
99+
operator: z.enum(["and", "or"]).default("and"),
100+
children: z
101+
.array(z.union([FilterConditionSchema, FilterGroupSchema]))
102+
.default([]),
103+
negate: z.boolean().default(false),
104+
}),
105+
);
106+
export type FilterGroupType = z.infer<typeof FilterGroupSchema>;
86107

87108
const FilterRowsTransformSchema = z.object({
88109
type: z.literal("filter_rows"),
@@ -91,16 +112,28 @@ const FilterRowsTransformSchema = z.object({
91112
.default("keep_rows")
92113
.describe(FieldOptions.of({ special: "radio_group" })),
93114
where: z
94-
.array(ConditionSchema)
115+
.array(FilterConditionSchema)
95116
.min(1)
96117
.describe(FieldOptions.of({ label: "Value", minLength: 1 }))
97-
.transform((value) => {
98-
return value.filter((condition) => {
118+
.transform((value): FilterGroupType => {
119+
const validConditions = value.filter((condition) => {
99120
return isConditionValueValid(condition.operator, condition.value);
100121
});
122+
return {
123+
type: "group",
124+
operator: "and",
125+
children: validConditions,
126+
negate: false,
127+
};
101128
})
102129
.default(() => [
103-
{ column_id: "" as ColumnId, operator: "==" as const, value: "" },
130+
{
131+
column_id: "" as ColumnId,
132+
operator: "==" as const,
133+
value: "",
134+
type: "condition" as const,
135+
negate: false,
136+
},
104137
]),
105138
});
106139

0 commit comments

Comments
 (0)