fix(grid): clear filter state with empty WHERE clause

This commit is contained in:
zipg 2026-07-17 15:30:27 +08:00 committed by GitHub
parent 1a78287656
commit 097d97905e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 75 additions and 19 deletions

View File

@ -1560,11 +1560,6 @@ function onSearchKeydown(e: KeyboardEvent) {
}
}
function clearWhereFilterInput() {
whereFilterInput.value = "";
void applyWhereFilter();
}
watch(whereFilterInput, () => {
emit("update:whereInput", currentWhereInput() ?? "");
persistStructuredFilterState();
@ -7446,7 +7441,6 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
:mode-options="filterModeOptions"
:column-search="filterBuilderColumnSearch"
:apply-where="applyWhereFilter"
:clear-where="clearWhereFilterInput"
:apply-order-by="applyOrderBySearch"
:clear-order-by="clearOrderByInput"
@update:column-search="filterBuilderColumnSearch = $event"

View File

@ -9,16 +9,19 @@ import type { DataGridContextFilterMode } from "@/lib/dataGrid/dataGridSql";
import type { DataGridStructuredFilterRule } from "@/composables/useDataGridFilterBuilder";
const { t } = useI18n();
const props = defineProps<{
rules: DataGridStructuredFilterRule[];
columns: string[];
filteredColumns: string[];
modeOptions: Array<{ value: DataGridContextFilterMode; labelKey: string }>;
columnSearch: string;
disabled?: boolean;
showHeader?: boolean;
showFooter?: boolean;
}>();
const props = withDefaults(
defineProps<{
rules: DataGridStructuredFilterRule[];
columns: string[];
filteredColumns: string[];
modeOptions: Array<{ value: DataGridContextFilterMode; labelKey: string }>;
columnSearch: string;
disabled?: boolean;
showHeader?: boolean;
showFooter?: boolean;
}>(),
{ showHeader: true, showFooter: true },
);
const emit = defineEmits<{
add: [];
apply: [];

View File

@ -39,7 +39,6 @@ const props = defineProps<{
modeOptions: Array<{ value: DataGridContextFilterMode; labelKey: string }>;
columnSearch: string;
applyWhere: (value?: string) => void | boolean | Promise<void | boolean>;
clearWhere: () => void | Promise<void>;
applyOrderBy: (value?: string) => void | boolean | Promise<void | boolean>;
clearOrderBy: () => void | Promise<void>;
}>();
@ -108,6 +107,10 @@ function updateRule(id: string, patch: Partial<DataGridStructuredFilterRule>) {
emit("updateRule", id, patch);
}
function clearWhere() {
emit("clearFilters");
}
onUnmounted(onResizeEnd);
</script>
@ -162,7 +165,6 @@ onUnmounted(onResizeEnd);
:column-search="columnSearch"
:disabled="!canUseWhereSearch"
:show-header="false"
:show-footer="false"
@add="emit('addRule')"
@apply="emit('applyFilters')"
@reset="emit('resetFilters')"

View File

@ -17,7 +17,7 @@ vi.mock("vue-i18n", () => ({ useI18n: () => ({ t: (key: string) => key }) }));
vi.mock("@lucide/vue", async () => {
const { createPassthroughStub } = await import("./vueHostHarness");
const icon = createPassthroughStub("Icon", "i");
return { Check: icon, ChevronLeft: icon, ChevronRight: icon, ChevronsLeft: icon, ChevronsRight: icon, Loader2: icon, Upload: icon, Search: icon, X: icon, Code2: icon, Copy: icon, Eye: icon, EyeOff: icon, Info: icon, Pencil: icon, Plus: icon, Trash2: icon };
return { Check: icon, ChevronDown: icon, ChevronLeft: icon, ChevronRight: icon, ChevronsLeft: icon, ChevronsRight: icon, Filter: icon, Loader2: icon, Upload: icon, Search: icon, X: icon, Code2: icon, Copy: icon, Eye: icon, EyeOff: icon, Info: icon, Pencil: icon, Plus: icon, Trash2: icon };
});
vi.mock("@/components/ui/button", async () => ({ Button: (await import("./vueHostHarness")).createPassthroughStub("Button", "button") }));
@ -65,6 +65,7 @@ import DataGridCellDetailPanel from "@/components/grid/DataGridCellDetailPanel.v
import DataGridColumnHeader from "@/components/grid/DataGridColumnHeader.vue";
import DataGridFilterBuilder from "@/components/grid/DataGridFilterBuilder.vue";
import DataGridPagination from "@/components/grid/DataGridPagination.vue";
import DataGridQueryControls from "@/components/grid/DataGridQueryControls.vue";
import DataGridSearchBar from "@/components/grid/DataGridSearchBar.vue";
function detail(patch: Partial<DataGridCellDetail> = {}): DataGridCellDetail {
@ -287,6 +288,62 @@ describe("DataGridFilterBuilder", () => {
});
});
describe("DataGridQueryControls", () => {
it("keeps filter actions available in the popover", () => {
const clearFilters = vi.fn();
const applyFilters = vi.fn();
const resetFilters = vi.fn();
const mounted = mountComponent(DataGridQueryControls, {
whereInput: "id = 1",
orderByInput: "",
columns: ["id"],
conditionColumns: ["id"],
historyScope: {},
canUseWhereSearch: true,
compact: false,
leadingBorder: false,
filterBuilderOpen: true,
filterButtonActive: true,
filterButtonCount: 1,
hasLocalColumnFilters: false,
localFilterCount: 0,
localFilterSummaries: [],
rules: [{ id: "r1", columnName: "id", mode: "equals", rawValue: "1", rawEndValue: "", conjunction: "AND" }],
filteredColumns: ["id"],
modeOptions: [{ value: "equals", labelKey: "equals" }],
columnSearch: "",
applyWhere: vi.fn(),
applyOrderBy: vi.fn(),
clearOrderBy: vi.fn(),
onClearFilters: clearFilters,
onApplyFilters: applyFilters,
onResetFilters: resetFilters,
});
dispatch(
findOne(mounted.root, (node) => node.type === "button" && hostText(node) === "grid.clearFilter"),
"click",
);
dispatch(
findOne(mounted.root, (node) => node.type === "button" && hostText(node) === "grid.resetFilterBuilder"),
"click",
);
dispatch(
findOne(mounted.root, (node) => node.type === "button" && hostText(node) === "grid.applyFilter"),
"click",
);
const whereInput = findOne(mounted.root, (node) => node.type === "textarea" && node.props.placeholder === "WHERE");
const whereControl = whereInput.parent?.parent;
expect(whereControl).toBeTruthy();
const whereButtons = findAll(whereControl!, (node) => node.type === "button");
dispatch(whereButtons[whereButtons.length - 1], "click");
expect(clearFilters).toHaveBeenCalledTimes(2);
expect(resetFilters).toHaveBeenCalledOnce();
expect(applyFilters).toHaveBeenCalledOnce();
});
});
describe("cell detail surfaces", () => {
it("copies the presented value, emits edit, closes, and replaces the JSON result", async () => {
const copyText = vi.fn();