@@ -65,6 +65,7 @@ import {
6565} from '../Selector/utils' ;
6666import { useMultiCombobox } from './hooks' ;
6767import { mergeProps } from '../utils' ;
68+ import { useAnnounce } from '../hooks/useAnnounce' ;
6869import type { BaseProps } from '../BaseProps' ;
6970import type { SizeValue } from '../utils/types' ;
7071import { useSize } from '../SizeContext/SizeContext' ;
@@ -607,6 +608,25 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
607608 [ options ] ,
608609 ) ;
609610
611+ // Announce selection-count changes politely (comboboxes-7 announce path).
612+ // Toggling options / select-all previously produced no audible feedback.
613+ const announce = useAnnounce ( ) ;
614+ const announceSelection = useCallback (
615+ ( nextValue : string [ ] ) => {
616+ const total = selectableItems . length ;
617+ const selectableSet = new Set ( selectableItems . map ( item => item . value ) ) ;
618+ const selectedCount = nextValue . filter ( v => selectableSet . has ( v ) ) . length ;
619+ if ( selectedCount === 0 ) {
620+ announce ( 'Selection cleared' ) ;
621+ } else if ( total > 0 && selectedCount === total ) {
622+ announce ( 'All selected' ) ;
623+ } else {
624+ announce ( `${ selectedCount } of ${ total } selected` ) ;
625+ }
626+ } ,
627+ [ announce , selectableItems ] ,
628+ ) ;
629+
610630 // Filter items by search query
611631 const filteredItems = useMemo ( ( ) => {
612632 if ( ! searchQuery ) {
@@ -715,14 +735,21 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
715735 ( e : React . MouseEvent ) => {
716736 e . stopPropagation ( ) ; // Don't open dropdown
717737 onChange ( [ ] ) ;
738+ announceSelection ( [ ] ) ;
718739 if ( changeAction ) {
719740 startTransition ( async ( ) => {
720741 setOptimisticValue ( [ ] ) ;
721742 await changeAction ( [ ] ) ;
722743 } ) ;
723744 }
724745 } ,
725- [ onChange , changeAction , startTransition , setOptimisticValue ] ,
746+ [
747+ onChange ,
748+ changeAction ,
749+ startTransition ,
750+ setOptimisticValue ,
751+ announceSelection ,
752+ ] ,
726753 ) ;
727754
728755 const handleToggle = useCallback (
@@ -732,6 +759,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
732759 : [ ...optimisticValue , itemValue ] ;
733760
734761 onChange ( newValue ) ;
762+ announceSelection ( newValue ) ;
735763 if ( changeAction ) {
736764 startTransition ( async ( ) => {
737765 setOptimisticValue ( newValue ) ;
@@ -745,6 +773,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
745773 changeAction ,
746774 startTransition ,
747775 setOptimisticValue ,
776+ announceSelection ,
748777 ] ,
749778 ) ;
750779
@@ -790,6 +819,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
790819 }
791820
792821 onChange ( newValue ) ;
822+ announceSelection ( newValue ) ;
793823 if ( changeAction ) {
794824 startTransition ( async ( ) => {
795825 setOptimisticValue ( newValue ) ;
@@ -804,6 +834,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
804834 changeAction ,
805835 startTransition ,
806836 setOptimisticValue ,
837+ announceSelection ,
807838 ] ) ;
808839
809840 // Route toggle: select-all sentinel → handleSelectAll, everything else → handleToggle
0 commit comments