Repository navigation
Commit 3ec2647
fix(studio): hold a dashboard Global Filter whose Options From is begun but not finished (#29300)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbYbdRKSREVcvQNjdMZC3NnM)_
Fixes #29006
Clause-②: no
Metadata-protocol: none
Claim 6574490931.
## 维护者速读
- **改了什么**:Studio
仪表盘属性里的「全局筛选」条目,如果「选项来源」只填了对象、还没填值字段和标签字段,这个条目现在会先保留在编辑器里、不自动保存;顶部出现一行中性提示(英文界面为
“Not saved yet: Options From on item #1 of “Global Filters” needs a
value”),填完后一次性保存期间的所有修改。
- **为什么改**:原来空条目会被保留,但「填了一半」的嵌套输入会被当成已完成直接自动保存,服务端 422
拒收;之后改描述、改标签每次都带着这个条目被拒,刷新时没有离开提示,修改全部丢失。
- **风险与回滚**:只改一个判断函数
`listItemHeldInput`:条目里任何一层「尚未填写」的输入都算未完成;作者已经填了值但值不合法的情况照常发送并显示红色拒收,不会被藏起来。仪表盘与报表的列表条目都走这条规则。回滚即
revert 本 PR。
-
**先看**:`ui/packages/app-shell/src/views/studio-design/metadataError.ts`
的
`listItemHeldInput`(向下逐层找未填写值的循环);`metadataError.heldNestedInput-29006.test.tsx`(保留与不保留两侧,以及真实自动保存路径)。
- **两分钟验证**:Studio 打开一个可写包里的仪表盘 → 属性 → Filters → Global Filters → Add
item,填 Field、Input Type 选 select、Options From 只填 Object,再改一下标签:网络里没有仪表盘的
PUT,顶部出现上述提示;再填好 Value Field 和 Label Field,出现一次 200 的 PUT,带着标签修改。
- **需要你做的**:无,席位落地。
## What changes
- **`listItemHeldInput` follows each refusal down from the item.** It
held an item only when every issue named an unset input directly below
it (`path[at + 1]`). A Global Filter with `optionsFrom: { object }`
draws issues at `optionsFrom.valueField` and `optionsFrom.labelField`,
whose direct input `optionsFrom` holds a value, so the item read as
finished but wrong and was sent. The rule now walks the issue's path
down from the item, through objects and list indexes, and holds the item
when it meets a value left unset (absent, or a text cleared to blank) at
any depth. A step through a value the author gave that has no such key
(a text, a number) meets none.
- **The input named stays the item's own form row** (`optionsFrom`,
shown as "Options From"), so `heldListItem` and the clause are
unchanged: "Options From on item #1 of “Global Filters” needs a value".
- **Finished but wrong work is still sent.** An item whose issues
include a value the author gave that the spec refuses, at any depth, is
not held, so its refusal shows as before.
- The dashboard list callers (`dashboardHeldEdit`) and the report list
callers (`reportHeldEdit`) get it through the same function. No
per-field case for `optionsFrom`. `GlobalFilterSchema.optionsFrom` in
`packages/spec` is unchanged. `StudioDesignSurface.tsx` is unchanged:
the surface's existing hold (`leafIncomplete`, the autosave's `blocked`,
the held line, the leave guard, Publish) applies as it does to the empty
item.
## Tests
- `metadataError.heldNestedInput-29006.test.tsx` (new). Unit cases on
`listItemHeldInput` / `dashboardHeldEdit`, each paired with
`DashboardSchema`'s own verdict on the same body:
- held: Options From with its Object only (issues at
`optionsFrom.valueField` and `labelField`), named "Options From";
Options From missing only its Label Field;
- still held: a direct unset input (`field`), the empty-item case;
- not held: a nested value the author gave that the spec refuses
(`labelField: 42`); an unset nested input beside a refused nested value;
`optionsFrom` given a text where the spec wants an object; a Label Field
cleared to blank, which the spec takes; a finished Options From.
- The same file mounts the Interfaces pillar with the real
`DashboardDefaultInspector` (as
`InterfacesPillar.heldListItem-27459.test.tsx` does): a saved select
filter, then Options From → Object and a Label edit send no dashboard
save, show no red strip, show the held line and report the clause to the
surface; filling Value Field and Label Field sends one save carrying the
Label edit and the finished filter, which `DashboardSchema` accepts.
## Acceptance notes
- The card's Expected reads "the partly filled item is held like the
empty one (or the refused edits are guarded on leave)". This PR does the
first. While held, the surface's leave guard is armed (measured below),
so a reload asks first.
- Deviation from the dispatch wording: it described the held outcome as
"the draft autosave leaves the item out and the other edits save". The
existing hold does not save around the item: nothing is sent while the
item is held, and the next save after it is finished carries every edit
kept meanwhile (as the empty-item hold behaves). The surface test pins
that behaviour.
- The doc comment on `HeldListItem.input` ("the item's input the spec
refuses it without") and the section header above it still read as
direct-input wording; both are outside this card's surface
(`listItemHeldInput` and its doc comment only) and were left as they
are.
## Verification
<details><summary>Live, on the showcase (before = main's
metadataError.ts, after = this branch)</summary>
Scratch writable package with a dashboard holding one metric widget, the
console on source via Vite. Steps 3 to 5 of the card, then a reload:
| Step | main | this branch |
|---|---|---|
| Field, Input Type = select | PUT 200, PUT 200 | PUT 200, PUT 200 |
| Options From → Object | PUT 422 `INVALID_METADATA`
(`optionsFrom.valueField`, `labelField`), red strip | no PUT, held line
|
| Description, then Label | PUT 422, PUT 422 (same item) | no PUT, held
line |
| leave guard after step 5 | not armed | armed (reload shows the leave
prompt) |
| reload without finishing | Label and Description back to their old
values, no prompt | leave prompt |
| finish Value Field + Label Field, reload | one PUT 200 | one PUT 200
carrying the Label and Description edits; both survive the reload |
</details>
<details><summary>Ablation</summary>
`scripts/ablation-replace.mjs` put back the direct-input check
(`!itemInputUnset(item[input])` for `!unset`; anchor 1 to 0, blob
`60f02752` to `63e45cd0`) and ran the new file with
`metadataError.heldListItem-27459.test.ts`: 3 failed, 20 passed. The
failures are the two nested held cases and the pillar case; the controls
and every empty-item case pass. Restored: blob equals HEAD and `git diff
HEAD` is empty.
</details>
<details><summary>Suites, type-check and gates</summary>
All on head `7559e61b` (this branch merged with `origin/main`
`47ee57fe`; base `45e9b2f1`).
- New file alone: 8/8 passed.
- Neighbour sweep under the verify lock: every app-shell test file
naming `metadataError`, a held-edit function or `heldListItem` (21
files, including both 27459 files and the 27785 and 29022 report files):
202/202 passed.
- `turbo run type-check --filter=@object-ui/app-shell` (tsc for src and
the test tsconfig): 65/65 tasks, exit 0. `eslint` on both touched files:
exit 0.
- `dispatch-gates --commands`: 34 derived families, all exit 0,
including `check-sdui-manifest`, `check-changeset-no-major`,
`check-adr-0087-registration`, `check:type-check-debt` and `pnpm --dir
ui exec vitest run scripts/` (169 files passed, 2 skipped).
`dispatch-gates --ran`: 34 derived, 34 run, 0 NOT-MEASURED.
`check-closing-keyword-parity` and the changeset gates were also run
against this body.
- Head `36e3a484` merges 20 more `origin/main` commits (among them
`StudioDesignSurface.tsx` and `packages-io.ts`), none touching this
diff's files. The 21-file neighbour sweep was re-run there: 202/202
passed.
- Narrowing: the whole app-shell vitest suite is CI's Test UI; it was
not run locally.
</details>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QsR2jSRsNRkCr8sZjG6Cpz
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d5ab8fc commit 3ec2647
3 files changed
Lines changed: 277 additions & 3 deletions
File tree
- .changeset
- ui/packages/app-shell/src/views/studio-design
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1124 | 1124 | | |
1125 | 1125 | | |
1126 | 1126 | | |
1127 | | - | |
1128 | | - | |
| 1127 | + | |
| 1128 | + | |
| 1129 | + | |
| 1130 | + | |
| 1131 | + | |
1129 | 1132 | | |
1130 | 1133 | | |
1131 | 1134 | | |
| |||
1154 | 1157 | | |
1155 | 1158 | | |
1156 | 1159 | | |
1157 | | - | |
| 1160 | + | |
| 1161 | + | |
| 1162 | + | |
| 1163 | + | |
| 1164 | + | |
| 1165 | + | |
| 1166 | + | |
| 1167 | + | |
| 1168 | + | |
| 1169 | + | |
| 1170 | + | |
1158 | 1171 | | |
1159 | 1172 | | |
1160 | 1173 | | |
| |||
0 commit comments