Skip to content

feat(components): the filter panel offers relative dates on date rows - #29263

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/project-thread-bga9c5
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/project-thread-bga9c5

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #29115

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:列表筛选面板里日期/日期时间字段的「介于」新增 13 个相对区间(今天、本周、本月、近 30 天……),「早于/晚于」新增今天、昨天、明天;选中后存的是宏(如 {week_start}),不再是当天的固定日期。
  • 为什么改:销售按「本周」或「早于今天」筛选并保存后,第二天、下周一筛选结果就悄悄错了,没有任何提示。
  • 风险与回滚:只改共享的筛选组件 FilterBuilder(列表、看板、日历、Studio 视图筛选、数据集和共享规则条件都用它);存储形状仍是普通 ViewFilterRule,没有新运算符、新键。回滚即 revert 本 PR,已存的宏值服务端照常解析。
  • 先看:ui/packages/components/src/custom/filter-builder.tsx(renderValueInput 外包一层选择、changeOperator 切换运算符时清空不匹配的相对值)、ui/packages/components/src/custom/filterRelativeDates.ts(宏对 → 预设的反查,读 spec 表不留副本)。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/components/src/__tests__/filter-builder-relative-dates-29115.test.tsx,预期 49 个测试全绿。
  • 需要你做的:无,席位落地。

What changes

On a date or datetime row of the filter builder:

  • between offers the spec's DATE_RANGE_PRESETS (all 13, in spec order) beside Custom…, which keeps today's two date inputs. A chosen preset is stored as its window from DATE_RANGE_PRESET_MACRO_WINDOWS, e.g. ['{week_start}', '{week_end}'].
  • before / after offer today, yesterday and tomorrow beside Custom…. A chosen point is stored as the wrapped token, e.g. '{today}'.
  • Read back: a stored pair shows its preset's label after folding the {current_*} aliases (DATE_MACRO_ALIAS_TOKENS), so ['{current_week_start}', '{current_week_end}'] shows "This week". A placeholder value no preset or point matches (['{7_days_ago}', '{tomorrow}']) is kept, shown as its own text, and still filters. A fixed date shows as Custom… with its date, as before.
  • Switches: an operator change carries a relative value only to an operator offering the same kind (before today → after today); otherwise the row starts empty, so half a window never becomes a point and no token lands in a date input that cannot show it. A field switch between two date columns keeps the value (convertScalarToFamily holds a date macro in the date / datetime families).
  • Labels reuse keys every pack already has: dashboard.filters.range.* (the dashboard filter bar's 13 preset labels), dashboard.filters.custom and fields.datePicker.tomorrow. No i18n key is added; each is copied into the builder's defaults table verbatim from the en pack.

The reverse lookup lives in a new module beside the builder, filterRelativeDates.ts. It is not re-exported from the package entry (custom/index.ts lists files by name), so nothing is added to @object-ui/components' published surface. It reads DATE_RANGE_PRESETS, DATE_RANGE_PRESET_MACRO_WINDOWS, DATE_MACRO_ALIAS_TOKENS, DATE_MACRO_WRAPPED_RE and isDateMacroToken from @objectstack/spec/data and keeps no copy; packages/spec is not edited.

Not in this PR, per the card: a "last N days" input, relative values on equals, future-facing presets, a week-start setting, the record form's shortcuts, migrating stored absolute dates, the author date-range chip, the dashboard's bounds table (#29116) and the browser-versus-server clock (#29117).

Tests

New filter-builder-relative-dates-29115.test.tsx, 49 tests:

  • the 13 presets in spec order plus Custom…; each preset sends its spec window (and never a preset name), on date and on datetime; This week sends ['{week_start}', '{week_end}'] and the rule passes ViewFilterRuleSchema;
  • before → Today sends '{today}'; after → Tomorrow on a datetime row sends '{tomorrow}'; the three points are spec instant tokens;
  • a stored {current_week_*} pair shows "This week" without an emit; a stored ['{7_days_ago}', '{tomorrow}'] shows as text, keeps no date inputs, and is re-emitted unchanged; the zh pack shows 本周;
  • Custom… keeps the date inputs and they still write dates; choosing Custom… from a preset clears to [];
  • operator and field switches as above;
  • controls: equals on a date row, text and number rows draw no relative choice; every label key exists in all ten packs.

Ablation (committed fix first, scripts/ablation-replace.mjs, test imports src, no dist in the path): the window leg was changed to store today's resolved date pair instead of the macro window. Result: 16 failed / 33 passed of 49, then restored (blob == HEAD, git diff HEAD empty).

Suite readings
  • the 23 existing filter-builder* files in packages/components: 23/23 files, 397/397 tests green with the change (locked, --maxWorkers=2).
  • the new file: 49/49 green.
  • @object-ui/components type-check (tsc --noEmit && tsc -p tsconfig.test.json): exit 0.
  • the full packages/components suite: 471 files passed, 1 skipped (4530 tests passed, 24 skipped), in four locked batches.

Acceptance notes

  • URL, stored preference and save-as-view were measured with a throwaway probe (not committed) on userFilterUrlState and foldFilterGroupToSpecRules: for ['{week_start}', '{week_end}'], ['{current_week_start}', '{current_week_end}'], '{today}' on before and ['{7_days_ago}', '{tomorrow}'] the uf__filter write → read round trip and the fold return the value unchanged, and each folded rule passes ViewFilterRuleSchema. The live query AST is ['close_date', 'between', ['{week_start}', '{week_end}']], lowered by the spec to { close_date: { $between: [...] } }; before today lowers to $lt: '{today}'. "Reset view" was not separately measured; it clears the panel through the existing path, which is value-agnostic.
  • Kanban and calendar hosts were measured in jsdom with a throwaway ListView probe (not committed), seeding the panel with ['{current_week_start}', '{current_week_end}']: on grid, kanban and calendar the panel shows "This week" and the data source find carries the macro pair unchanged (kanban also hands it to the lane component). The calendar ANDs its own visible-window node computed from browser dates beside it, which is existing behaviour and the subject of [finding] Date macros resolve on two clocks: an authored view filter in the browser, a panel filter in the workspace timezone — they can disagree about today #29117. A real Console run against a server (REST row counts, datetime last-day coverage) is NOT MEASURED here; the card's own REST readings show the server resolves these tokens, including a datetime column's last day.
  • Other builder surfaces: the builder is shared, so Studio's view filter, the dataset filter inspector and sharing-rule criteria get the same choices. Each stores the same spec tokens, and the server resolves them on every ObjectQL read and write path and in the analytics executor (resolveFilterTokens), which a sharing rule's engine.find goes through.
  • No docs page documents the builder's value editors (content/docs/frontend/renderers.mdx lists the renderer only), so no docs edit.

Verification

The 34 derived gates (dispatch-gates --commands) ran at 35916c61 (after merging origin/main): 34/34 exit 0, --ran reconciliation 34 run, 0 NOT-MEASURED. pnpm --dir ui exec vitest run scripts/ at the same head: 169 files passed, 2 skipped, exit 0. The full packages/components suite at 007ea328 (before that merge): 471 files passed, 1 skipped, 4530 tests, in four locked batches.

origin/main was merged again before opening (head 64a3459f; incoming commits touch none of this PR's files, and leave the reused i18n keys unchanged). Re-run there: the 24 filter-builder* files (446 tests) green, @object-ui/components type-check exit 0, check-changeset-no-major, check-sdui-manifest, check-empty-changeset and check:nul-bytes exit 0. Every family CI runs is read from this PR's check-runs.


Generated by Claude Code

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling labels Nov 2, 2026
…eside field and operator

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jSjVXJnFT3sLMzwrKq
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m

席位核对 · ACCEPT · 2026-11-02T11:12Z

Head-sha: e1926801aa69df8fa0cf660318112fcdf034af44

  • Shape: draft → main, Fixes #29115 in the body only. 5 files, +602 / −8 (610 lines ≤ 3000). No governed path: check-governed-merges --pr 29263 reports NOT governed. Clause-②: no and Metadata-protocol: none are line-initial in the body and the changeset, as claimed (6573207355).
  • Contract review: none owed (Clause-②: no; the new helper filterRelativeDates.ts is not re-exported, no exported prop or type changes, no new i18n key, no metadata key; the stored values are the spec's existing date macro tokens).
  • File surface: inside the claim: filter-builder.tsx (+156 / −7), the new filterRelativeDates.ts, the new filter-builder-relative-dates-29115 test, the changeset, plus one accepted test repoint from REWORK round 1: FilterConditionField.betweenBlankBound-9914.test.tsx now expects the third combobox (the preset picker, reading Custom…) on a date between row.
  • Read in the diff: date rows offer the 13 DATE_RANGE_PRESETS on between (stored as the spec macro window) and today / yesterday / tomorrow on before / after, with Custom… beside them keeping the typed date path. Labels reuse dashboard.filters.range.*, dashboard.filters.custom and fields.datePicker.tomorrow. Because the builder is shared, the Studio view filter, dataset filter and sharing criteria get the same choices; the server already resolves these tokens on each of those paths.
  • Tests (dev): new test file 49 / 49; 24 filter-builder files 446 / 446; ablation reds 16 of 49, restored. REWORK round 1 (the CI red on Test UI shard 15/16 at the 9914 pin): 67 consumer files, 2934 tests green. Derived gates 34 of 34.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending. mergeable_state is clean.
  • NOT MEASURED: the picker in a running Console; covered by the component tests only.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #29115 closes the card on merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

List filter panel: offer relative dates on date rows — 本周 and 今天 today store a fixed date, so a saved filter silently goes wrong

1 participant