Skip to content

feat: add TagGroup component#2684

Open
yan-ad wants to merge 5 commits into
unovue:v2from
yan-ad:feat/tag-group
Open

feat: add TagGroup component#2684
yan-ad wants to merge 5 commits into
unovue:v2from
yan-ad:feat/tag-group

Conversation

@yan-ad

@yan-ad yan-ad commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

🔗 Linked issue

Closes #2525

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Adds a new TagGroup primitive for rendering removable selected values, useful for filters and multiselect selections.

This includes:

  • TagGroupRoot with controlled/uncontrolled model support and form integration
  • TagGroupItem, TagGroupItemText, and TagGroupItemDelete
  • keyboard removal with Delete and Backspace
  • disabled state handling at both root and item/delete levels
  • exports and namespaced plugin support
  • unit coverage for rendering, removal, disabled behavior, and axe accessibility

📸 Screenshots (if appropriate)

N/A

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • New Features
    • Added a TagGroup component suite for managing removable tags with v-model support, selectable state, disabled items, keyboard/pointer removal, and form integration.
  • Tests
    • Added accessibility and behavior tests covering rendering, keyboard/click deletion, disabled behavior, focus navigation, and state updates.
  • Documentation
    • Added docs, demos (CSS & Tailwind), meta/API pages, and updated sidebar (Tag Group listed as Alpha).
  • Chores
    • Re-exported TagGroup components and added namespaced plugin exports.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5bef9a07-0178-4a40-97b7-e9d30017fd11

📥 Commits

Reviewing files that changed from the base of the PR and between ed48736 and 53bd2b6.

📒 Files selected for processing (1)
  • packages/core/src/TagGroup/TagGroup.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/TagGroup/TagGroup.test.ts

📝 Walkthrough

Walkthrough

Adds a new TagGroup UI: Root (v-model, form integration), Item (selection/keyboard removal), ItemText, ItemDelete, barrel exports, plugin wiring, docs/demos, and tests verifying accessibility, pointer/keyboard removal, and disabled behavior.

Changes

TagGroup Component Implementation

Layer / File(s) Summary
Root component and context contract
packages/core/src/TagGroup/TagGroupRoot.vue
TagGroupRoot defines typed props/emits, v-model syncing, removeTag logic, and provides TagGroupRootContext to descendants; renders role="list" and optionally a VisuallyHiddenInput for form integration.
Item and delete sub-components
packages/core/src/TagGroup/TagGroupItem.vue, packages/core/src/TagGroup/TagGroupItemDelete.vue
TagGroupItem computes effective disabled/selected state, exposes remove(), and handles Delete/Backspace keyboard removal. TagGroupItemDelete injects item context, forwards ref, handles disabled state, and invokes remove on click with event.stop.
Text display sub-component
packages/core/src/TagGroup/TagGroupItemText.vue
Renders tag content (default span), forwards ref, and supports as/asChild passthrough for slot content.
Public API, exports, and registry
packages/core/src/TagGroup/index.ts, packages/core/src/index.ts, packages/plugins/src/namespaced/index.ts, packages/core/constant/components.ts
Adds barrel exports for TagGroup parts and context helpers, re-exports from core index, wires plugin namespaced TagGroup object, and registers tagGroup in components constant.
Comprehensive test coverage
packages/core/src/TagGroup/TagGroup.test.ts
Mounts a demo TagGroup, runs accessibility checks, asserts list/listitem roles and tag text, verifies click and keyboard removal, checks disabled-item and disabled-delete behavior, and asserts selection data-state.
Documentation and demos
docs/components/demo/TagGroup/*, docs/content/docs/components/tag-group.md, docs/content/meta/*, docs/.vitepress/config.ts
Adds component docs (API, accessibility), autogenerated meta pages for subcomponents, demo SFCs (CSS and Tailwind), Tailwind config for demo, and sidebar entry.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant TagGroupRoot
  participant TagGroupItem
  participant TagGroupItemDelete
  participant Model
  User->>TagGroupRoot: focus/interact with list
  TagGroupRoot->>TagGroupItem: render items from modelValue
  User->>TagGroupItemDelete: click delete or press Delete/Backspace on focused item
  TagGroupItemDelete->>TagGroupItem: call remove()
  TagGroupItem->>TagGroupRoot: invoke removeTag(value)
  TagGroupRoot->>Model: update modelValue (filter out value) and emit update:modelValue
  TagGroupRoot->>TagGroupItem: re-render without removed item
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

v2

Suggested reviewers

  • zernonia

Poem

🐰 I nibble tags and hop with glee,
I press Delete and set them free,
Context whispers, "remove with care,"
Disabled ones stay rooted there,
A little rabbit cheers the tree.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add TagGroup component' clearly describes the main change—introducing a new TagGroup component with core functionality.
Linked Issues check ✅ Passed The PR successfully implements the TagGroup component with controlled/uncontrolled modes, keyboard removal (Delete/Backspace), item/delete disabling, form integration, and comprehensive tests addressing issue #2525 requirements.
Out of Scope Changes check ✅ Passed All changes are within scope: TagGroup components, context/injection helpers, exports, namespaced plugin support, unit tests, demo files, and documentation—all directly related to the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@pkg-pr-new

pkg-pr-new Bot commented Jun 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/reka-ui@2684

commit: 53bd2b6

@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: 1

🧹 Nitpick comments (1)
packages/core/src/TagGroup/TagGroup.test.ts (1)

44-49: ⚡ Quick win

Consider verifying the removeTag event emission.

The test correctly validates that clicking the delete button updates the model and DOM. However, it doesn't assert that the removeTag event is emitted with the correct payload. Adding an event assertion would provide more complete coverage of the component contract.

✅ Example event assertion

You can add an event listener before the click and assert it was called:

 it('should remove a tag from model value when delete button is clicked', async () => {
+  const removeSpy = vi.fn()
+  wrapper.vm.$on('removeTag', removeSpy)
+
   await wrapper.findAll('button[aria-label^="Remove"]')[1].trigger('click')

   expect(wrapper.vm.tags).toEqual(['Vue', 'Accessibility'])
   expect(wrapper.findAll('[role="listitem"]').map(item => item.text())).toEqual(['Vue', 'Accessibility'])
+  expect(removeSpy).toHaveBeenCalledWith('Reka UI')
 })
🤖 Prompt for 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.

In `@packages/core/src/TagGroup/TagGroup.test.ts` around lines 44 - 49, After the
click assertion, also assert the component emitted the removeTag event with the
expected payload: call wrapper.emitted() and verify there is a 'removeTag' entry
and that its first emission payload matches the removed tag (or index) — e.g.
check wrapper.emitted('removeTag')[0] equals the expected value corresponding to
the button you clicked (use the same target used in the test: the second remove
button from wrapper.findAll('button[aria-label^="Remove"]')[1]).
🤖 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/core/src/TagGroup/TagGroupItemDelete.vue`:
- Around line 24-35: The click handler is directly bound to itemContext.remove
so clicks still remove tags even when the component is disabled (especially for
non-button/asChild elements); update TagGroupItemDelete to use an internal click
handler (e.g. onClick) instead of binding itemContext.remove directly, have that
handler read the computed disabled (from disabled or props.disabled) and return
early when disabled is true, otherwise call itemContext.remove; then change the
template `@click.stop` to call the new handler (e.g. `@click.stop`="onClick") so
disabled state properly guards the delete action.

---

Nitpick comments:
In `@packages/core/src/TagGroup/TagGroup.test.ts`:
- Around line 44-49: After the click assertion, also assert the component
emitted the removeTag event with the expected payload: call wrapper.emitted()
and verify there is a 'removeTag' entry and that its first emission payload
matches the removed tag (or index) — e.g. check wrapper.emitted('removeTag')[0]
equals the expected value corresponding to the button you clicked (use the same
target used in the test: the second remove button from
wrapper.findAll('button[aria-label^="Remove"]')[1]).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 478aea6f-24df-4360-a66b-ec632bb15664

📥 Commits

Reviewing files that changed from the base of the PR and between bb0c552 and ceebf07.

📒 Files selected for processing (9)
  • packages/core/constant/components.ts
  • packages/core/src/TagGroup/TagGroup.test.ts
  • packages/core/src/TagGroup/TagGroupItem.vue
  • packages/core/src/TagGroup/TagGroupItemDelete.vue
  • packages/core/src/TagGroup/TagGroupItemText.vue
  • packages/core/src/TagGroup/TagGroupRoot.vue
  • packages/core/src/TagGroup/index.ts
  • packages/core/src/index.ts
  • packages/plugins/src/namespaced/index.ts

Comment thread packages/core/src/TagGroup/TagGroupItemDelete.vue Outdated
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.

[Feature]: Add TagGroup component

1 participant