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
38 changes: 38 additions & 0 deletions .changeset/filter-builder-field-switch-retypes-value.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
'@object-ui/components': patch
---

`FilterBuilder` settles a row's **value** when its field changes, instead of leaving a value the new column's input cannot show.

objectui#4768 / PR #4779 settled the row's operator on a field switch and
re-shaped the value only when the operator's family changed — scalar to scalar
has no shape question, so what the user typed was carried through on purpose.
But the field's **type** changed too, and the value input is redrawn from it: a
browser renders a non-numeric value in `<input type="number">` as **blank**. A
`text` row filtered `equals "acme"`, pointed at a number column, showed an empty
box while the row went on carrying `"acme"` — `foldFilterGroupToSpecRules`
persisted it and the live grid queried `amount equals "acme"`. The same
invisible-value shape as objectui#4768, one column over.

Changing the field is now one edit with the operator, the value's shape **and**
the value's type. Convertible values are carried, the rest clear to the family's
empty shape (scalar `''`, list `[]`, range `[]`):

- `"42"` on a number column becomes the number `42`; `"acme"`, `"42abc"` and
`"1,000"` clear. The reading is deliberately stricter than `parseFloat`, which
would turn `"acme"` into `0` — a filter the user never wrote;
- `"true"` / `"false"` convert on a boolean column, and a boolean becomes
`"true"` / `"false"` on a text column, so the round trip closes; `1` and
`"yes"` are conventions rather than readings, and clear;
- date-like columns take only what their own input can render, plus the one
truncation that loses nothing it could have shown (`"2024-03-05T14:30"` →
`"2024-03-05"` on a date column). A bare date does **not** gain a midnight to
fit a `datetime` column: `equals 2024-03-05T00:00` is a filter that looks
answered and matches almost nothing;
- a value the new column can already hold is left alone — switching between two
text columns, or two numeric ones, still keeps what the user typed, and an
unfilled row stays unfilled.

The convertibility judgement is defined once, next to `reshapeFilterValue`,
and `getInputType` now reads the same family table it does — so the type a value
is converted **to** and the input it is edited **in** cannot drift apart.
Original file line number Diff line number Diff line change
Expand Up @@ -193,17 +193,32 @@ describe('the value is re-shaped for the family the operator lands in', () => {
operator: 'between',
value: ['2024-01-01', '2024-12-31'],
});
await pick(0, 'Amount');
// Retargeted from `Amount` to `Title` by objectui#4781: the fact under test
// is the pair → scalar collapse, and a text column forces the same operator
// reset (its bucket has no `between`) while being able to HOLD the date
// string the collapse produces. On a number column the collapse still
// happens and #4781 then clears `"2024-01-01"`, which would have hidden
// this pin behind a value judgement that is not what it is here to state.
await pick(0, 'Title');

expect(lastRow(onChange).value).toBe('2024-01-01');
});

it('a scalar value is carried through untouched — only the SHAPE is settled', async () => {
// Deliberately not blanked: the reset answers the operator's family, and
// scalar-to-scalar has no shape question to answer. What the user typed is
// theirs; a field switch is not a licence to discard it.
it('a scalar the new column can hold is carried through — only the SHAPE is settled', async () => {
// The reset answers the operator's family, and scalar-to-scalar has no
// shape question to answer. What the user typed is theirs; a field switch
// is not a licence to discard it.
//
// This case used to point at `Amount` and assert that `"acme"` survived
// onto a NUMBER column. That is the defect objectui#4781 reported — the
// number input renders `"acme"` blank while the row keeps filtering by it —
// and the maintainer ruled the value must clear there. The pin's own
// subject (the shape reset does not blank a value) is unchanged, so it is
// asserted on a target that can still hold the value; the number target now
// has its own, opposite pin in
// `filter-builder-field-switch-value.test.tsx`.
const { onChange } = renderRow({ field: 'title', operator: 'contains', value: 'acme' });
await pick(0, 'Amount');
await pick(0, 'Stage');

expect(lastRow(onChange)).toMatchObject({ operator: 'equals', value: 'acme' });
});
Expand Down
Loading
Loading