Skip to content

fix(app-shell): keep the Tab visibility name input across keystrokes - #27025

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5siu9r
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5siu9r

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread

Fixes #26748

维护者速读

  • 改了什么:Studio Access › 标签页可见性的每一行改按位置作 React key,名称输入框改为行内缓冲,只在新名称不与其它行重名时写回草稿。
  • 为什么改:行 key 原是输入框正在编辑的标签名,每按一键整行重挂载,焦点丢失,只留下首字符。
  • 风险与回滚:只动 Tab Visibility 区块与一个新测试文件;回滚即 revert 本 PR。
  • 先看:PermissionAdvancedFacets.tsx 的 tabEntries.map 与文件末尾的 TabNameInput。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tabName-26748.test.tsx。
  • 需要你做的:无,等待 seat 复核。

What changes

  • Each Tab Visibility row is keyed by its position in tabEntries, not by the tab name its input edits. A rename keeps insertion order, so the row and the input's focus survive each keystroke.
  • The name input (TabNameInput) buffers what is typed and writes the rename only while the name is no other row's. Before, a name that passed through or ended at another row's name merged the two rows in tabPermissions and dropped one. While the typed name collides, the input carries aria-invalid.
  • FacetPicker testId is unchanged (tab-visibility-${tab}).

Why

Typing showcase_app into a new row stored {"s":"hidden"}: the row remounted after the first key.

Verification

  • New PermissionAdvancedFacets.tabName-26748.test.tsx: userEvent.type of showcase_app key by key keeps focus and stores { showcase_app: 'hidden' }; renaming an existing row keeps the other row and order; typing through another row's name merges nothing; remove drops only its own row.
  • Ablation: restoring key={tab} turns the typed-name test red.
  • PermissionAdvancedFacets* + PermissionMatrixEditor*: 35 files, 256 tests pass. The 24 derived gates, app-shell type-check, ui check:spec-symbols and eslint on both files exit 0.
  • Known limit, unchanged: a tab name JS orders as an integer (e.g. 1) moves to the front of tabPermissions, so its row moves too.

Clause-②: no
Metadata-protocol: none

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (@object-ui/app-shell is private: skip-changeset.)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English. (The dispatch order asks for the Chinese 维护者速读 block.)
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JQwxbXbEWQehrHjkZUViYN

Each row was keyed by the tab name its input edits, so every keystroke
remounted the row and the input lost focus after the first character.
Rows are now keyed by position, and the name input buffers what is typed,
writing the rename only while the name is no other row's (a name passing
through another row's merged the two rows and dropped one).

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

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · head 961fd9dc · 2026-10-28T02:16Z

席位意见:Tab 可见性的行改按位置做 key,输入框不再每敲一个字就重挂载、丢焦点;改名先存在行内缓冲里,只在不与别的行重名时写回,避免两行合并丢一行。

  1. Shape: Fixes #26748; 2 files, +156/−7: PermissionAdvancedFacets.tsx +50/−7 (the Tab Visibility block plus a file-local TabNameInput) and one new test file (4 tests). Labelled skip-changeset (@object-ui/app-shell is private).
  2. Premise measured: the seat read the source diff on the head. Rows key on their index in tabEntries, and a rename keeps insertion order, so the row and its input's focus survive each keystroke. TabNameInput holds the typed text, re-syncs from name when the draft changes, and calls onRename only while taken(next) is false (no other row has that name). While the text collides, the draft keeps the old name and the input reads aria-invalid. The dev measured that typing through another row's name used to merge the two rows and drop one. Ablation: restoring key={tab} turns the typed-name test red.
  3. Gates / check-runs: PR check-runs on 961fd9dc: 43 success, 15 skipped, 0 failure; mergeable_state clean. Dev's local run: 24/24 derived gates exit 0, plus type-check, spec-symbols and eslint.
  4. Accepted deviations: TabNameInput sits at the end of the file, used only by the block, inside the ordered file surface. A trial merge with PR fix(app-shell): an expression field shows the draft's server verdict at its own path #26999's branch (Studio: an expression field that says "Valid CEL" should read the draft's server verdict at its own path (RLS predicate, flow edge condition) #26858, RLS verdictPath props in the same file) auto-merges this file.
  5. Out of scope: noted, not filed: a tab name that looks like an integer is reordered by JS object key order, so its row moves. Skills-docs: none — UI editor only.
  6. Landing: not Tier H; the seat readies and arms auto-merge.

Generated by Claude Code

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

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio Access › Tab visibility: the tab-name input keeps only the first character typed (typing showcase_app saves {"s":"hidden"})

2 participants