Conversation
Reaching `selectionLimit` used to disable the close button on every rendered chip, so a user could not undo a selection without first clearing the field. That is no longer the case here: `maxSelectionLimitReached()` feeds only `isOptionDisabled()`, which gates unselected options in the dropdown, and the chip is bound to `[removable]="!$disabled() && !readonly"` with no limit check anywhere near it or in `removeOption()`. Nothing guarded that, though. The existing coverage only asserts what `maxSelectionLimitReached()` returns and never renders a chip, so wiring the limit back into the chip's `removable` or `disabled` binding would go unnoticed. The new test fills the field to the limit, checks the remove icons are still rendered and focusable, and removes one through a real click. Two selections rather than three: the shared host binds `maxSelectedLabels` to 3, at which point the chips collapse into the summary label. Fixes openng-org#810
The chip hardcoded `[removable]="true"`, so the remove icon rendered even when the component was readonly. Clicking it discarded the chip without discarding the value: the projected icon's own handler is guarded by `!readonly`, so it no-ops and never calls `stopPropagation()`, the click then reaches the wrapper Chip renders around the remove slot, and `close()` sets `visible` to false. The chip style turns that signal into `display: none`, so the entry vanished from the field while the model kept it. `removable` is now derived from `readonly` and the disabled state, which is what MultiSelect already does. The `(onRemove)` guard goes with it: the output can no longer fire in either state. Only reachable when readonly. Disabled was already safe, because `.p-disabled` sets `pointer-events: none` on its descendants.
In multiple mode the chips live in a `<ul role="listbox">` that carried no `aria-label` and no `aria-labelledby`, so axe reported "ARIA input fields must have an accessible name" and screen readers announced it as an unnamed listbox. The suggestions list next to it has been labelled with `aria.listLabel` all along; only the chip container was missed. It gets its own key rather than borrowing one. Reusing `ariaLabel` would give the combobox and the chip list the same name, and reusing `aria.listLabel` would put "Option List" on two different listboxes in one component, so `aria.selectedItems` is added to the locale API and defaults to "Selected Items". The nested-interactive half of the report is untouched: the text input carries `role="combobox"` inside an `<li role="option">`, and separating them means giving up either the chip `option` semantics or the single listbox that `aria-activedescendant` navigation is built on. That needs an ARIA pattern rework, not an attribute. Fixes openng-org#625
The separator is a delimiter, but it was only treated as one on the path that actually added a value. `handleSeparatorKey` called `preventDefault()` and cleared the input inside `if (inputValue && !isSelected(inputValue))`, so pressing it on an empty field or on a value that is already a chip let the character through into the input. `onInputPaste` had the same shape, guarded on whether the model grew, so pasting only-duplicate text left the raw string with its separators sitting in the field. Neither entry point split on the separator either. Both added the trimmed input verbatim, which is why a field reading `,foo` became one chip `",foo"`, and why repeatedly pressing the separator on an empty field produced `","`, `",,"`, `",,,"` instead of nothing. Both now bail out early when this is not separator mode, then unconditionally swallow the event, split the text, add whatever is new, and clear the input. Splitting and adding is shared by the key and paste paths, so a paste of `a,a` is deduplicated within the batch as well as against the model when `unique` is set, which it was not before. No new input: `unique` already expresses whether duplicates are allowed. Fixes openng-org#267
…tely
A `#chipicon` template was projected into the chip's remove slot, so an icon
meant to sit next to the label replaced the close button instead. The name
suggested one thing and the markup did another, and there was no way to reach
the chip's own icon slot at all.
`chipicon` now renders as chip content and a new `chipremoveicon` template
feeds the remove slot. `removetokenicon` keeps working and is re-pointed at
`chipremoveicon`; its deprecation used to name `chipicon`, which is the
opposite of what it should have said.
The `chipIcon` string input keeps styling the remove icon so nothing breaks
today, but it is deprecated in favour of the new `chipRemoveIcon`, which wins
when both are set. Re-pointing `chipIcon` at the chip's icon is left for a
major.
Two things go with the rewrite. The projected remove icon no longer carries its
own click handler: the wrapper Chip already renders calls `close()`, which
emits `onRemove`, so removal still happens exactly once and now takes the same
path as the keyboard. And the outlet context passes `cx('chipIcon')` instead of
a hardcoded class, so it survives `unstyled`.
The pass-through options gain the `chipIcon` entry the template has been using
untyped.
BREAKING CHANGE: `#chipicon` and `pTemplate="chipicon"` now render inside the
chip next to its label instead of replacing the remove icon. Use
`chipremoveicon` to customize the remove icon.
Fixes openng-org#399
The separator rewrite cleared the multiple-mode input on every paste, so a paste whose values were all duplicates discarded whatever the user had already typed. Pasting is not a commit: only the pasted text is parsed, so there is no reason for it to consume unrelated input. The input is now cleared only once the paste has actually added something. `preventDefault` stays unconditional, so pasted separators still never reach the field, which is what the original fix was for. The existing test set the field to the same string it pasted, which made the wipe look correct; it now pastes into an empty field, and two new tests cover a paste that adds nothing next to one that does. Fixes openng-org#267
This was referenced Sep 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes 4 chip-related bugs in MultiSelect and AutoComplete, plus 1 bug found while working on the code:
#chipiconwas projected into the chip's remove slot, so an icon meant for the label replaced the close button, and the chip's own icon slot was unreachable.#chipiconnow renders as chip content and a new#chipremoveiconowns the remove slot. NewchipRemoveIconinput deprecates the misnamedchipIcon, whose behaviour is unchanged.removetokeniconstill works; its deprecation now points atchipremoveiconinstead ofchipicon.,on an empty field inserted the character (repeats built,,,,,,,,), pressing it on an existing chip left the text in the field, and,foobecame one chip named",foo". The separator is now always swallowed, the text is split and trimmed, and a batch is deduplicated against itself whenuniqueis set. No new input;uniquealready covers this.[removable]="true"was hardcoded, so readonly fields rendered a remove icon that hid the chip while the model kept the value. Now derived fromreadonlyand the disabled state, matching MultiSelect.<ul role="listbox">had no accessible name (axe: "ARIA input fields must have an accessible name"). Adds anaria.selectedItemslocale key defaulting to "Selected Items".Related issues
Fixes #399
Fixes #267
Closes #810 (already correct; adds the regression test)
Relates to #625. The fix doesn't fully cover the reported bug, but that would require sweeping changes to ARIA in the library,
Type of change
Breaking changes
#chipicon/pTemplate="chipicon"on MultiSelect now render inside the chip next to the label instead of replacing the remove icon. Fails silently: anyone using it as a close button gets two icons per chip.Migration is a rename, not a rewrite. Rename rather than duplicate, or the icon renders twice:
<ng-template #chipicon>to<ng-template #chipremoveicon>pTemplate="chipicon"topTemplate="chipremoveicon"Smaller behaviour changes: readonly/disabled AutoComplete now renders zero
.p-chip-remove-iconnodes; under[unstyled], the chip-icon template contextclassis now empty instead of leakingp-multiselect-chip-icon, so[class]="class + ' pi pi-times'"should becomeclass="pi pi-times" [ngClass]="class".None
Test plan
pnpm run build:libpnpm run test:unit(vitest)pnpm run lint(broken repo-wide, unrelated to this PR)pnpm run format:checkChecklist