Skip to content

fix: Sender skill closable.onClose not triggered when deleting skill via Backspace#1964

Open
nikzart wants to merge 2 commits into
ant-design:mainfrom
nikzart:fix/sender-skill-backspace-onclose
Open

fix: Sender skill closable.onClose not triggered when deleting skill via Backspace#1964
nikzart wants to merge 2 commits into
ant-design:mainfrom
nikzart:fix/sender-skill-backspace-onclose

Conversation

@nikzart

@nikzart nikzart commented Jul 6, 2026

Copy link
Copy Markdown

🤔 This is a ...

  • 🆕 New feature
  • 🐞 Bug fix
  • 📝 Site / documentation improvement
  • 📽️ Demo improvement
  • 💄 Component style improvement
  • 🤖 TypeScript definition improvement
  • 📦 Bundle size optimization
  • ⚡️ Performance optimization
  • ⭐️ Feature enhancement
  • 🌐 Internationalization
  • 🛠 Refactoring
  • 🎨 Code style optimization
  • ✅ Test Case
  • 🔀 Branch merge
  • ⏩ Workflow
  • ⌨️ Accessibility improvement
  • ❓ Other (about what?)

🔗 Related Issues

close #1955, close #1938

💡 Background and Solution

Deleting an inserted skill tag with Backspace removed the tag but never fired closable.onClose, so external code could not observe the removal. The close-button click path already fires it (Skill.tsxhandleClose).

This PR fires closable.onClose in the skillKey branch of handleDeleteOperation in SlotTextArea.tsx, right after removeSkill(), matching the click path's order (remove, then notify).

Notes:

  • Per review feedback, closable.onClose is now typed (event: React.SyntheticEvent<HTMLDivElement>) => void (previously React.MouseEventHandler<HTMLDivElement>), since the callback now also receives the keyboard event on Backspace removal — no event cast needed. Docs updated in both languages.
  • Deletion behavior itself is unchanged: keyboard deletion still occurs regardless of closable.disabled, as before this PR. If Backspace deletion should also be blocked for disabled, I'm happy to follow up — kept out of scope here to stay minimal.
  • Per the acceptance criteria in [Good First Issue] Sender 用 Backspace 删除 skill 时未触发 onClose #1955, the new unit test simulates Backspace deleting the skill and asserts the callback fires (and the tag is removed from the DOM). The test fails without the fix.

📝 Change Log

Language Changelog
🇺🇸 English Fix the issue that Sender skill closable.onClose was not triggered when deleting the skill tag with Backspace. The onClose event parameter is now typed as React.SyntheticEvent<HTMLDivElement>.
🇨🇳 Chinese 修复 Sender 使用 Backspace 删除 skill 标签时不触发 closable.onClose 回调的问题。onClose 的事件参数类型调整为 React.SyntheticEvent<HTMLDivElement>

Summary by CodeRabbit

  • Bug Fixes
    • 修复了当可关闭的 skill 使用 Backspace 删除时,关闭回调不触发的问题;删除后界面移除效果与点击关闭保持一致。
  • Documentation
    • 更新了 skill 的 onClose 回调说明与事件类型,明确其在“点击关闭”及“Backspace 删除”时触发。
  • Tests
    • 新增覆盖 Backspace 删除可关闭 skill 的用例,校验关闭回调调用次数与移除结果。

…via Backspace

Fire closable.onClose in the keyboard deletion branch of
handleDeleteOperation, matching the close-button click behavior, so
external code can observe skill removal via Backspace.

close ant-design#1955, close ant-design#1938
@dosubot dosubot Bot added bug Something isn't working javascript Pull requests that update Javascript code labels Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

更新了 Sender 的 skill 关闭契约与删除流程:Backspace 删除 skill 时会补发 onClose,同时把相关类型定义、文档和测试同步到新的事件签名。

Changes

Backspace 删除 skill 触发 onClose

Layer / File(s) Summary
类型与文档契约
packages/x/components/sender/components/Skill.tsx, packages/x/components/sender/interface.ts, packages/x/components/sender/index.en-US.md, packages/x/components/sender/index.zh-CN.md
closable.onClose 的参数签名改为 React.SyntheticEvent<HTMLDivElement>,并更新中英文文档中的触发说明。
Backspace 删除流程与测试
packages/x/components/sender/components/SlotTextArea.tsx, packages/x/components/sender/__tests__/slot.test.tsx
handleDeleteOperation 在 Backspace 删除 skill 后补发 onClose,新增测试验证回调触发与 skill 元素移除。

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested labels: lgtm

Suggested reviewers: kimteayon

Poem

兔子轻敲退格键,
skill 退场也会应一声。
回调轻轻把门关,
文档类型都对齐,
DOM 里空空更安宁。

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 实现补发了 Backspace 删除 skill 时的 onClose,并新增了对应单测,符合 #1955/#1938 的验收要求。
Out of Scope Changes check ✅ Passed 未见明显无关改动;类型与文档更新都直接服务于 Backspace 删除触发 onClose 的修复。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了核心修复:Backspace 删除 Sender skill 时未触发 closable.onClose。
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces support for triggering the closable.onClose callback when a skill is removed via the Backspace key in the SlotTextArea component, and adds a corresponding unit test. The review feedback points out a type-safety issue where a KeyboardEvent is cast directly to a React.MouseEvent, which could cause runtime errors if mouse-specific properties are accessed. Additionally, it suggests strengthening the unit test assertions by replacing weak toBeDefined() checks with not.toBeNull() and toBeInTheDocument().

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +543 to +544
expect(ref.current).toBeDefined();
expect(skillDom).toBeDefined();

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.

medium

Using toBeDefined() on ref.current and skillDom is a weak assertion because null is considered defined in Jest. Since querySelector returns null when an element is not found, expect(skillDom).toBeDefined() will pass even if the element does not exist.

We should use not.toBeNull() for the ref and toBeInTheDocument() for the DOM element to ensure they actually exist and are rendered correctly.

Suggested change
expect(ref.current).toBeDefined();
expect(skillDom).toBeDefined();
expect(ref.current).not.toBeNull();
expect(skillDom).toBeInTheDocument();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 523a199not.toBeNull() for the ref and toBeInTheDocument() for the skill element.

removeSkill();
// 与点击关闭行为保持一致,删除后补发 closable.onClose
if (skill?.closable && typeof skill.closable === 'object') {
skill.closable.onClose?.(e as unknown as React.MouseEvent<HTMLDivElement>);

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.

medium

Casting a KeyboardEvent (e) directly to React.MouseEvent is type-unsafe. If the consumer's onClose callback accesses mouse-specific properties (such as clientX, clientY, or button), it will result in undefined values or potential runtime errors.

Since onClose is defined as a MouseEventHandler, we should consider if we can avoid passing the keyboard event directly, or at least document this behavior. If possible, we could also update the onClose type definition in interface.ts to accept React.MouseEvent | React.KeyboardEvent in a future PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 523a199 — took the wider-type route: closable.onClose is now typed (event: React.SyntheticEvent<HTMLDivElement>) => void (interface, local ClosableConfig, and both docs), and the keyboard path passes the real event with no cast.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/x/components/sender/__tests__/slot.test.tsx`:
- Line 544: The current assertion in the sender slot test uses toBeDefined() on
the result of container.querySelector, which can still pass when the selector
returns null and does not prove the .ant-sender-skill element rendered. Update
the assertion in slot.test.tsx to use a null-safe presence check such as
not.toBeNull(), or match the existing jest-dom style used elsewhere in the same
test file by asserting with toBeInTheDocument() on the skillDom query result.

In `@packages/x/components/sender/components/SlotTextArea.tsx`:
- Around line 525-533: The Backspace delete path in SlotTextArea is forcing a
KeyboardEvent into the closable.onClose callback as a MouseEvent, which does not
match the public SkillType.closable.onClose contract. Update the onClose
signature to accept a more general React.SyntheticEvent<HTMLDivElement> (or
another shared event type) and make the SlotTextArea delete/close flow pass the
real event type through without casting, so both keyboard and mouse close paths
stay aligned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0c4a48ae-97f0-4397-8c59-a8ecefdc8195

📥 Commits

Reviewing files that changed from the base of the PR and between 841f8ed and 22b114b.

📒 Files selected for processing (2)
  • packages/x/components/sender/__tests__/slot.test.tsx
  • packages/x/components/sender/components/SlotTextArea.tsx

Comment thread packages/x/components/sender/__tests__/slot.test.tsx Outdated
Comment thread packages/x/components/sender/components/SlotTextArea.tsx
… removal

Address review feedback:

- Widen closable.onClose (interface, ClosableConfig, docs) from
  React.MouseEventHandler<HTMLDivElement> to
  (event: React.SyntheticEvent<HTMLDivElement>) => void, since the
  callback now also receives the keyboard event on Backspace removal.
  Removes the KeyboardEvent-to-MouseEvent cast.
- Strengthen test assertions (not.toBeNull / toBeInTheDocument).
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 635.41kB (18.29%) ⬆️⚠️, exceeding the configured threshold of 5%.

Bundle name Size Change
x-markdown-array-push 132.28kB -1.23MB (-90.32%) ⬇️
antdx-array-push 3.98MB 1.87MB (88.63%) ⬆️⚠️

Affected Assets, Files, and Routes:

view changes for bundle: antdx-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
antdx.js (New) 3.98MB 3.98MB 100.0% 🚀
antdx.min.js (Deleted) -2.11MB 0 bytes -100.0% 🗑️
view changes for bundle: x-markdown-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
x-markdown.min.js (New) 124.36kB 124.36kB 100.0% 🚀
x-markdown.min.css (New) 7.91kB 7.91kB 100.0% 🚀
latex.min.js (Deleted) -264.89kB 0 bytes -100.0% 🗑️
static/KaTeX_AMS-Regular.*.ttf (Deleted) -63.63kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Regular.*.ttf (Deleted) -53.58kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Bold.*.ttf (Deleted) -51.34kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Italic.*.ttf (Deleted) -33.58kB 0 bytes -100.0% 🗑️
static/KaTeX_AMS-Regular.*.woff (Deleted) -33.52kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-BoldItalic.*.ttf (Deleted) -32.97kB 0 bytes -100.0% 🗑️
static/KaTeX_Math-Italic.*.ttf (Deleted) -31.31kB 0 bytes -100.0% 🗑️
static/KaTeX_Math-BoldItalic.*.ttf (Deleted) -31.2kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Regular.*.woff (Deleted) -30.77kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Bold.*.woff (Deleted) -29.91kB 0 bytes -100.0% 🗑️
static/KaTeX_AMS-Regular.*.woff2 (Deleted) -28.08kB 0 bytes -100.0% 🗑️
static/KaTeX_Typewriter-Regular.*.ttf (Deleted) -27.56kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Regular.*.woff2 (Deleted) -26.27kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Bold.*.woff2 (Deleted) -25.32kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Bold.*.ttf (Deleted) -24.5kB 0 bytes -100.0% 🗑️
latex.min.css (Deleted) -24.39kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Italic.*.ttf (Deleted) -22.36kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Italic.*.woff (Deleted) -19.68kB 0 bytes -100.0% 🗑️
static/KaTeX_Fraktur-Bold.*.ttf (Deleted) -19.58kB 0 bytes -100.0% 🗑️
static/KaTeX_Fraktur-Regular.*.ttf (Deleted) -19.57kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Regular.*.ttf (Deleted) -19.44kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-BoldItalic.*.woff (Deleted) -19.41kB 0 bytes -100.0% 🗑️
static/KaTeX_Math-Italic.*.woff (Deleted) -18.75kB 0 bytes -100.0% 🗑️
static/KaTeX_Math-BoldItalic.*.woff (Deleted) -18.67kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-Italic.*.woff2 (Deleted) -16.99kB 0 bytes -100.0% 🗑️
static/KaTeX_Main-BoldItalic.*.woff2 (Deleted) -16.78kB 0 bytes -100.0% 🗑️
static/KaTeX_Script-Regular.*.ttf (Deleted) -16.65kB 0 bytes -100.0% 🗑️
static/KaTeX_Math-Italic.*.woff2 (Deleted) -16.44kB 0 bytes -100.0% 🗑️
static/KaTeX_Math-BoldItalic.*.woff2 (Deleted) -16.4kB 0 bytes -100.0% 🗑️
static/KaTeX_Typewriter-Regular.*.woff (Deleted) -16.03kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Bold.*.woff (Deleted) -14.41kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Italic.*.woff (Deleted) -14.11kB 0 bytes -100.0% 🗑️
static/KaTeX_Typewriter-Regular.*.woff2 (Deleted) -13.57kB 0 bytes -100.0% 🗑️
static/KaTeX_Fraktur-Bold.*.woff (Deleted) -13.3kB 0 bytes -100.0% 🗑️
static/KaTeX_Fraktur-Regular.*.woff (Deleted) -13.21kB 0 bytes -100.0% 🗑️
static/KaTeX_Caligraphic-Bold.*.ttf (Deleted) -12.37kB 0 bytes -100.0% 🗑️
static/KaTeX_Caligraphic-Regular.*.ttf (Deleted) -12.34kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Regular.*.woff (Deleted) -12.32kB 0 bytes -100.0% 🗑️
static/KaTeX_Size1-Regular.*.ttf (Deleted) -12.23kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Bold.*.woff2 (Deleted) -12.22kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Italic.*.woff2 (Deleted) -12.03kB 0 bytes -100.0% 🗑️
static/KaTeX_Size2-Regular.*.ttf (Deleted) -11.51kB 0 bytes -100.0% 🗑️
static/KaTeX_Fraktur-Bold.*.woff2 (Deleted) -11.35kB 0 bytes -100.0% 🗑️
static/KaTeX_Fraktur-Regular.*.woff2 (Deleted) -11.32kB 0 bytes -100.0% 🗑️
static/KaTeX_Script-Regular.*.woff (Deleted) -10.59kB 0 bytes -100.0% 🗑️
static/KaTeX_Size4-Regular.*.ttf (Deleted) -10.36kB 0 bytes -100.0% 🗑️
static/KaTeX_SansSerif-Regular.*.woff2 (Deleted) -10.34kB 0 bytes -100.0% 🗑️
static/KaTeX_Script-Regular.*.woff2 (Deleted) -9.64kB 0 bytes -100.0% 🗑️
static/KaTeX_Caligraphic-Bold.*.woff (Deleted) -7.72kB 0 bytes -100.0% 🗑️
static/KaTeX_Caligraphic-Regular.*.woff (Deleted) -7.66kB 0 bytes -100.0% 🗑️
static/KaTeX_Size3-Regular.*.ttf (Deleted) -7.59kB 0 bytes -100.0% 🗑️
static/KaTeX_Caligraphic-Bold.*.woff2 (Deleted) -6.91kB 0 bytes -100.0% 🗑️
static/KaTeX_Caligraphic-Regular.*.woff2 (Deleted) -6.91kB 0 bytes -100.0% 🗑️
static/KaTeX_Size1-Regular.*.woff (Deleted) -6.5kB 0 bytes -100.0% 🗑️
static/KaTeX_Size2-Regular.*.woff (Deleted) -6.19kB 0 bytes -100.0% 🗑️
static/KaTeX_Size4-Regular.*.woff (Deleted) -5.98kB 0 bytes -100.0% 🗑️
static/KaTeX_Size1-Regular.*.woff2 (Deleted) -5.47kB 0 bytes -100.0% 🗑️
static/KaTeX_Size2-Regular.*.woff2 (Deleted) -5.21kB 0 bytes -100.0% 🗑️
static/KaTeX_Size4-Regular.*.woff2 (Deleted) -4.93kB 0 bytes -100.0% 🗑️
static/KaTeX_Size3-Regular.*.woff (Deleted) -4.42kB 0 bytes -100.0% 🗑️
static/KaTeX_Size3-Regular.*.woff2 (Deleted) -3.62kB 0 bytes -100.0% 🗑️

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.07%. Comparing base (841f8ed) to head (523a199).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1964   +/-   ##
=======================================
  Coverage   97.07%   97.07%           
=======================================
  Files         159      159           
  Lines        5748     5750    +2     
  Branches     1687     1686    -1     
=======================================
+ Hits         5580     5582    +2     
  Misses        166      166           
  Partials        2        2           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

bug Something isn't working javascript Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Good First Issue] Sender 用 Backspace 删除 skill 时未触发 onClose sender Backspace 键删除 skill,onClose 事件未触发

1 participant