fix(document): surface filter rejection reason in builder
This commit is contained in:
1 parent
d854fa2dfe
commit
f7688794f8
2 files changed
+59
-1
No files matched your search
@@ -321,6 +321,9 @@ type DocumentGridChanges = {
|
||||
rows: MongoInputValue[][];
|
||||
};
|
||||
const documentFilterBuilderOpen = ref(false);
|
||||
/// Why an Apply click did not filter. The shared `error` banner lives in the document pane, which
|
||||
/// the open filter popover covers, so a rejected rule there reads as a dead button.
|
||||
const documentFilterBuilderError = ref("");
|
||||
const documentFilterFieldPopoverOpen = ref<Record<string, boolean>>({});
|
||||
const documentFilterFieldSearch = ref<Record<string, string>>({});
|
||||
const documentFilterRules = ref<DocumentFilterRule[]>(restoredDocumentBrowserState?.documentFilterRules ?? []);
|
||||
@@ -908,12 +911,14 @@ function documentFilterFieldKindLabel(kind: DocumentFieldPathNode["kind"]): stri
|
||||
}
|
||||
|
||||
function removeDocumentFilterRule(ruleId: string) {
|
||||
documentFilterBuilderError.value = "";
|
||||
documentFilterRules.value = documentFilterRules.value.filter((rule) => rule.id !== ruleId);
|
||||
setDocumentFilterFieldPopoverOpen(ruleId, false);
|
||||
if (documentFilterRules.value.length === 0) appliedDocumentFilter.value = null;
|
||||
}
|
||||
|
||||
function updateDocumentFilterRule(ruleId: string, patch: Partial<DocumentFilterRule>) {
|
||||
documentFilterBuilderError.value = "";
|
||||
documentFilterRules.value = documentFilterRules.value.map((rule) => {
|
||||
if (rule.id !== ruleId) return rule;
|
||||
const next = { ...rule, ...patch };
|
||||
@@ -947,6 +952,7 @@ function elasticsearchQueryTypeLabel(queryType: ElasticsearchQueryType): string
|
||||
}
|
||||
|
||||
function resetDocumentFilterBuilder() {
|
||||
documentFilterBuilderError.value = "";
|
||||
appliedDocumentFilter.value = null;
|
||||
documentFilterFieldPopoverOpen.value = {};
|
||||
documentFilterFieldSearch.value = {};
|
||||
@@ -1399,9 +1405,10 @@ async function applyDocumentStructuredFilters() {
|
||||
}))
|
||||
.filter((item): item is { rule: DocumentFilterRule; condition: Record<string, unknown> } => !!item.condition);
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : String(e);
|
||||
documentFilterBuilderError.value = e instanceof Error ? e.message : String(e);
|
||||
return;
|
||||
}
|
||||
documentFilterBuilderError.value = "";
|
||||
error.value = "";
|
||||
const structured = combineDocumentFilterConditions(
|
||||
items.map((item) => item.condition),
|
||||
@@ -3186,6 +3193,10 @@ defineExpose({ focusSearch });
|
||||
{{ t("grid.filterBuilderEmpty") }}
|
||||
</div>
|
||||
|
||||
<p v-if="documentFilterBuilderError" data-document-filter-builder-error class="text-xs text-destructive">
|
||||
{{ documentFilterBuilderError }}
|
||||
</p>
|
||||
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<Button variant="ghost" size="sm" class="h-7 px-2 text-xs" @click="addDocumentFilterRule">
|
||||
<Plus class="mr-1 h-3.5 w-3.5" />
|
||||
|
||||
+47
@@ -1052,6 +1052,53 @@ describe("DocumentBrowser MongoDB filter value types", () => {
|
||||
expect(JSON.parse(filter)).toEqual({ _id: "1" });
|
||||
});
|
||||
|
||||
it("keeps the filter builder open and says why a rejected rule did not apply", async () => {
|
||||
app?.unmount();
|
||||
backend.documentFindDocuments.mockReset();
|
||||
backend.documentFindDocuments.mockResolvedValue({
|
||||
documents: [{ _id: 1, title: "Numeric id" }],
|
||||
raw_documents: [],
|
||||
total: 1,
|
||||
total_is_exact: true,
|
||||
});
|
||||
app = createApp(DocumentBrowser, {
|
||||
connectionId: "mongo-1",
|
||||
database: "test",
|
||||
collection: "numeric_ids",
|
||||
databaseType: "mongodb",
|
||||
});
|
||||
app.mount(root!);
|
||||
await flushUi();
|
||||
|
||||
root!.querySelector<HTMLButtonElement>('[data-testid="data-grid"] button')!.click();
|
||||
await flushUi();
|
||||
|
||||
const valueInput = document.body.querySelector<HTMLInputElement>('input[placeholder="grid.filterBuilderValue"]')!;
|
||||
valueInput.value = "abc";
|
||||
valueInput.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
await flushUi();
|
||||
|
||||
const callsBeforeApply = backend.documentFindDocuments.mock.calls.length;
|
||||
buttonWithText("grid.applyFilter").click();
|
||||
await flushUi();
|
||||
|
||||
// The shared error banner sits in the document pane, which this popover covers, so the
|
||||
// reason has to appear inside the builder or the click reads as a dead button.
|
||||
expect(document.body.querySelector("[data-document-filter-builder-error]")?.textContent).toContain("number");
|
||||
expect(document.body.querySelector('[data-testid="popover-content"]')).not.toBeNull();
|
||||
expect(backend.documentFindDocuments.mock.calls.length).toBe(callsBeforeApply);
|
||||
|
||||
// Correcting the rule drops the stale message.
|
||||
valueInput.value = "1";
|
||||
valueInput.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
await flushUi();
|
||||
expect(document.body.querySelector("[data-document-filter-builder-error]")).toBeNull();
|
||||
|
||||
buttonWithText("grid.applyFilter").click();
|
||||
await flushUi();
|
||||
expect(JSON.parse(backend.documentFindDocuments.mock.calls.at(-1)?.[5])).toEqual({ _id: 1 });
|
||||
});
|
||||
|
||||
it("exposes the applicable shared table view options", async () => {
|
||||
app?.unmount();
|
||||
app = createApp(DocumentBrowser, {
|
||||
|
||||
Reference in new issue
Block a user