Skip to content

Add BitTagsInput component (#11074)#12263

Open
msynk wants to merge 6 commits intobitfoundation:developfrom
msynk:11074-blazorui-tagsinput
Open

Add BitTagsInput component (#11074)#12263
msynk wants to merge 6 commits intobitfoundation:developfrom
msynk:11074-blazorui-tagsinput

Conversation

@msynk
Copy link
Copy Markdown
Member

@msynk msynk commented Apr 15, 2026

closes #11074

Summary by CodeRabbit

  • New Features
    • Added BitTagsInput component for collecting and managing multiple tags with customizable templates, event callbacks (add/remove/exists handling), and configuration options (max tags, per-tag length limit, custom separators, duplicate prevention, auto-focus).
    • Supports read-only and disabled states, form validation, and custom styling.
    • Includes comprehensive documentation with 14+ usage examples.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 15, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 81ba36e8-7e72-42f3-9e6e-9bfb63e9b217

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This pull request introduces a new BitTagsInput Blazor component for managing comma-separated or custom-separated tag input. The component includes the core Razor component and code-behind logic, supporting data classes for arguments and styling customization, SCSS styling, comprehensive demo pages with multiple usage examples, and navigation integration.

Changes

Cohort / File(s) Summary
Core Component
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor, BitTagsInput.razor.cs
New BitTagsInput component inheriting from BitInputBase<ICollection<string>?> with support for tag input via keyboard/paste, event callbacks (OnBeforeAdd, OnBeforeRemove, OnAdd, OnRemove), focus tracking, configurable separators, max tags/length, duplicate handling, auto-focus, and templating. Includes ~20 component parameters and handlers for keydown, input, focus, and tag removal.
Supporting Classes
BitTagsInputBeforeArgs.cs, BitTagsInputClassStyles.cs
New data classes: BitTagsInputBeforeArgs for callback arguments with Tag and Cancel properties; BitTagsInputClassStyles for CSS class/style customization of component parts.
Styling
BitTagsInput.scss
SCSS stylesheet defining component appearance including container layout, tag chips, delete buttons, input field, and modifier classes for disabled/invalid/focused/no-border states.
SCSS Integration
src/BlazorUI/Bit.BlazorUI/Styles/components.scss
Added import for BitTagsInput.scss to global component bundle.
Demo Pages
src/BlazorUI/Demo/Client/.../TagsInput/BitTagsInputDemo.razor, BitTagsInputDemo.razor.cs, BitTagsInputDemo.razor.samples.cs
Demo page with multiple sections showcasing: basic usage, read-only/disabled states, no-border styling, binding, event handling, duplicates, max limits, custom separators/templates, validation, paste splitting, and styling customization. Includes 14 sample code snippets and metadata for component parameters/members.
Validation Model
ValidationTagsInputModel.cs
New model class with Tags property decorated with [Required] and [MinLength(1)] for demo validation scenarios.
Navigation
src/BlazorUI/Demo/Client/.../MainLayout.razor.NavItems.cs
Added navigation item for TagsInput demo with routes /components/tagsinput and /components/tags-input.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The primary complexity stems from the component code-behind, which implements intricate event handling for keyboard input, focus management, tag validation (duplicates, max length, max tags), value parsing/formatting, and multiple conditional callback flows. Supporting classes and styling are straightforward, but the comprehensive demo with 14 examples and extensive component parameters across multiple event callbacks warrant careful review of interaction logic and edge cases.

Poem

🐰 A new input blooms, for tags we collect,
With keyboard and paste, each entry's correct,
Remove with a click, or add with great ease,
This component will surely the users all please!
Enter and tab bring the magic alive,

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a new BitTagsInput component to the codebase.
Linked Issues check ✅ Passed The PR implements a complete BitTagsInput component addressing issue #11074's requirement for a TagsInput component similar to bootstrap-tagsinput examples.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the BitTagsInput component; no unrelated or out-of-scope modifications detected.

✏️ 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new BitTagsInput component to Bit.BlazorUI along with demo documentation and styling, addressing the missing TagsInput component request (#11074).

Changes:

  • Introduces BitTagsInput component (API surface, rendering, styles, and callback args types).
  • Adds a full demo page (samples, parameters/subclasses/public members documentation, navigation entry).
  • Registers component SCSS in the global components stylesheet.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs Adds TagsInput to demo navigation.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/ValidationTagsInputModel.cs Adds demo validation model for tags.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/BitTagsInputDemo.razor.samples.cs Adds demo code snippets for samples.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/BitTagsInputDemo.razor.cs Adds demo metadata (parameters/subclasses/public members) and handlers.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/BitTagsInputDemo.razor Adds the demo UI page and examples.
src/BlazorUI/Bit.BlazorUI/Styles/components.scss Registers TagsInput SCSS in the component bundle.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInputClassStyles.cs Adds class/style customization contract.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInputBeforeArgs.cs Adds before-add/remove callback args type.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.scss Adds component styling.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor.cs Implements component logic (add/remove, separators, validation integration).
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor Implements component markup and accessibility attributes.

Comment thread src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor Outdated
Comment thread src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor.cs Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor.cs (1)

328-361: OnAdd callback fires before value is committed.

In TryAddTags, OnAdd.InvokeAsync(text) is called inside the loop (line 353) before SetCurrentValueAsync(list) (line 359). If a subscriber reads CurrentValue in the OnAdd handler, the newly added tag won't be present yet.

Consider whether this timing is intentional. The single-tag TryAddTag method has the same pattern (lines 323-325), so this appears to be by design. If so, document this behavior; otherwise, move OnAdd invocations after the value update.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor.cs`
around lines 328 - 361, The OnAdd callback is invoked inside TryAddTags before
the component's value is committed, so move the OnAdd calls until after
SetCurrentValueAsync to ensure CurrentValue reflects the new tags when
subscribers run; implement by collecting added tags (e.g., a local List<string>
addedTags) during the loop in TryAddTags and after SetCurrentValueAsync call
await OnAdd.InvokeAsync(tag) for each tag in addedTags, and apply the same
change to TryAddTag so both paths consistently invoke OnAdd only after
SetCurrentValueAsync updates CurrentValue.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.scss`:
- Line 1: In BitTagsInput.scss fix the `@import` to remove the explicit .scss
extension: change the `@import` "../../../Styles/functions.scss"; statement to
import the partial without the extension so it complies with the
scss/load-partial-extension rule (keep the same relative path but omit ".scss").
This targets the import line in BitTagsInput.scss that currently references
"../../../Styles/functions.scss".

In
`@src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/BitTagsInputDemo.razor.cs`:
- Around line 350-355: In HandleBeforeRemove, remove the duplicated assignment
to tagExistsMsg (the second tagExistsMsg = null) so the method only sets
eventsStatus and clears tagExistsMsg once; locate the HandleBeforeRemove method
and delete the redundant tagExistsMsg = null line to avoid the duplicate
operation.

---

Nitpick comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor.cs`:
- Around line 328-361: The OnAdd callback is invoked inside TryAddTags before
the component's value is committed, so move the OnAdd calls until after
SetCurrentValueAsync to ensure CurrentValue reflects the new tags when
subscribers run; implement by collecting added tags (e.g., a local List<string>
addedTags) during the loop in TryAddTags and after SetCurrentValueAsync call
await OnAdd.InvokeAsync(tag) for each tag in addedTags, and apply the same
change to TryAddTag so both paths consistently invoke OnAdd only after
SetCurrentValueAsync updates CurrentValue.
🪄 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: 614765b0-1ce9-4038-958d-f9191c6d2a45

📥 Commits

Reviewing files that changed from the base of the PR and between 92115fb and 2c41ff9.

📒 Files selected for processing (11)
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.scss
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInputBeforeArgs.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInputClassStyles.cs
  • src/BlazorUI/Bit.BlazorUI/Styles/components.scss
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/BitTagsInputDemo.razor
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/BitTagsInputDemo.razor.cs
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/BitTagsInputDemo.razor.samples.cs
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TagsInput/ValidationTagsInputModel.cs
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comment on lines +265 to +280
if (Separators is not null)
{
foreach (var separator in Separators)
{
if (_inputText.Contains(separator))
{
var textWithoutSeparator = _inputText.Replace(separator, string.Empty).Trim();
await TryAddTags(_inputText.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
// If add was rejected (e.g. duplicate), _inputText still has the separator; strip it
if (_inputText.Length > 0)
{
_inputText = textWithoutSeparator;
}
return;
}
}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

HandleOnInput only processes the first separator it finds and returns. With multiple separators configured (e.g., [",", ";"] as shown in the demo), inputs/pastes that contain more than one separator (e.g., "a,b;c") will be split only on the first match, leaving partially-separated tags ("b;c") instead of producing three tags. Consider splitting once using all separators (e.g., string.Split(string[] separators, ...)) so any configured separator is honored in the same input value; adding a unit test for a mixed-separator paste would help prevent regressions.

Suggested change
if (Separators is not null)
{
foreach (var separator in Separators)
{
if (_inputText.Contains(separator))
{
var textWithoutSeparator = _inputText.Replace(separator, string.Empty).Trim();
await TryAddTags(_inputText.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
// If add was rejected (e.g. duplicate), _inputText still has the separator; strip it
if (_inputText.Length > 0)
{
_inputText = textWithoutSeparator;
}
return;
}
}
if (Separators is not null && Separators.Any(separator => _inputText.Contains(separator)))
{
var textWithoutSeparators = _inputText;
foreach (var separator in Separators)
{
textWithoutSeparators = textWithoutSeparators.Replace(separator, string.Empty);
}
textWithoutSeparators = textWithoutSeparators.Trim();
await TryAddTags(_inputText.Split([.. Separators], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
// If add was rejected (e.g. duplicate), _inputText still has separators; strip them
if (_inputText.Length > 0)
{
_inputText = textWithoutSeparators;
}

Copilot uses AI. Check for mistakes.
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.

The BlazorUI is missing a TagsInput component

2 participants