Skip to content

feat(CommandTextArea): add component#1222

Open
tenphi wants to merge 3 commits into
mainfrom
feat-command-text-area
Open

feat(CommandTextArea): add component#1222
tenphi wants to merge 3 commits into
mainfrom
feat-command-text-area

Conversation

@tenphi

@tenphi tenphi commented Jul 1, 2026

Copy link
Copy Markdown
Member

Note

Medium Risk
ComboBox behavior is consolidated behind a shared popover module (regression surface), and CommandTextArea adds combobox ARIA and caret-positioning logic on a new public form control.

Overview
Adds CommandTextArea, a multiline field that keeps focus in the textarea while showing a ListBox popover when the caret sits on a configurable trigger token (default / at line start, or e.g. @ after whitespace). Options filter on textValue, plain-text children, and description; matches are highlighted in label and description. Picking an option replaces the active token with the item’s textValue (optional trailing space) and calls onCommand. The popover is caret-anchored via useCaretAnchor / getCaretRect, with keyboard list navigation while open.

Refactor: Extracts shared ListBoxPopover (overlay, portal, virtual-focus list) plus listNavigation / useCompositeFocus from ComboBox, which now consumes those modules instead of inline overlay code.

Supporting tweaks: Item applies highlight to string descriptions; ListBox gains optionHighlight wired through options and sections. Package export from fields/index, plus Storybook docs, stories, and tests.

Reviewed by Cursor Bugbot for commit 69b395f. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cube-ui-kit Ready Ready Preview, Comment Jul 1, 2026 3:46pm

Request Review

@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 69b395f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

📦 NPM canary release

Deployed canary version 0.0.0-canary-009d53f.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🧪 Storybook is successfully deployed!

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🏋️ Size limit report

Name Size Passed?
All 401.96 KB (+0.88% 🔺) No 👎
Tree shaking (just a Button) 114.36 KB (+0.02% 🔺) Yes 🎉

Click here if you want to find out what is changed in this build

if (focused != null) {
e.preventDefault();
commit(focused);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale focus commits wrong command

High Severity

While the popover stays open, narrowing the typed token does not update virtual focus when the highlighted key is no longer in the filtered list. Enter/Tab still commits that stale key via getItemTextValue, so the inserted command can disagree with the visible options (e.g. after / then adding h, Enter can insert /clear while only /help and /share match).

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 44be071. Configure here.

const popoverRef = useCombinedRefs(propsPopoverRef);
const listBoxRef = useCombinedRefs(propsListBoxRef);
const listStateRef = (propsListStateRef ??
useRef<ListStateLike | null>(null)) as RefObject<ListStateLike | null>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditional useRef breaks hooks

High Severity

listStateRef is assigned with propsListStateRef ?? useRef(...), so useRef runs only when the optional listStateRef prop is omitted. If a parent later passes listStateRef (or removes it), the hook call order changes and React can throw or behave unpredictably.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 44be071. Configure here.

// ---- trigger detection ------------------------------------------------
const activeToken = useMemo(
() => parseActiveToken(effectiveValue, caret, triggers),
[effectiveValue, caret, triggers],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caret stale after value updates

Medium Severity

Caret position lives in React state and is updated mainly via syncCaret on user events. When the textarea value is updated from outside (controlled mode or form reset), caret is not synced to the DOM selection, so parseActiveToken can use a stale index and open, hide, or parse the wrong command token.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 44be071. Configure here.

return textFilterFn(node.textValue || '', term) ? node : null;
})
.filter(Boolean);
return filterCollectionNodes(nodes, term, textFilterFn);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ComboBox filters non textValue fields

Medium Severity

ComboBox filtering now goes through filterCollectionNodes, which matches plain-text children and description as well as textValue. ComboBox documents filtering on textValue only, so options can appear or disappear based on description text that users cannot see in the input.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4fe7d81. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 5 total unresolved issues (including 4 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 69b395f. Configure here.

let { labelProps, inputProps } = useTextField(
{
...otherProps,
value: effectiveValue,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultValue ignored on textarea

Medium Severity

Uncontrolled CommandTextArea always seeds and drives the field with effectiveValue starting at '', so a defaultValue from props (e.g. the Multiline story) never appears in the textarea or in trigger parsing until the user types.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 69b395f. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant