🔖 fix: Announce Bookmark Selection State (#11450)

* fix: bookmarks announce selection state

* chore: address Copilot comments
This commit is contained in:
Dustin Healy 2026-01-21 10:49:50 -08:00 committed by GitHub
parent 828c2b2048
commit 12ec64b988
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 40 additions and 12 deletions

View file

@ -15,6 +15,8 @@ export interface MenuItemProps {
separate?: boolean;
hideOnClick?: boolean;
dialog?: React.ReactElement;
ariaLabel?: string;
ariaChecked?: boolean;
ref?: React.Ref<any>;
className?: string;
render?:

View file

@ -99,6 +99,16 @@ const BookmarkMenu: FC = () => {
const newBookmarkRef = useRef<HTMLButtonElement>(null);
const tagsCount = tags?.length ?? 0;
const hasBookmarks = tagsCount > 0;
const buttonAriaLabel = useMemo(() => {
if (tagsCount > 0) {
return localize('com_ui_bookmarks_count_selected', { count: tagsCount });
}
return localize('com_ui_bookmarks_add');
}, [tagsCount, localize]);
const dropdownItems: t.MenuItemProps[] = useMemo(() => {
const items: t.MenuItemProps[] = [
{
@ -114,19 +124,19 @@ const BookmarkMenu: FC = () => {
if (data) {
for (const tag of data) {
const isSelected = tags?.includes(tag.tag);
const isSelected = tags?.includes(tag.tag) === true;
items.push({
id: tag.tag,
label: tag.tag,
hideOnClick: false,
icon:
isSelected === true ? (
<BookmarkFilledIcon className="size-4" />
) : (
<BookmarkIcon className="size-4" />
),
icon: isSelected ? (
<BookmarkFilledIcon className="size-4" />
) : (
<BookmarkIcon className="size-4" />
),
onClick: () => handleSubmit(tag.tag),
disabled: mutation.isLoading,
ariaChecked: isSelected,
});
}
}
@ -146,10 +156,10 @@ const BookmarkMenu: FC = () => {
if (mutation.isLoading) {
return <Spinner aria-label="Spinner" />;
}
if ((tags?.length ?? 0) > 0) {
return <BookmarkFilledIcon className="icon-lg" aria-label="Filled Bookmark" />;
if (hasBookmarks) {
return <BookmarkFilledIcon className="icon-lg" aria-hidden="true" />;
}
return <BookmarkIcon className="icon-lg" aria-label="Bookmark" />;
return <BookmarkIcon className="icon-lg" aria-hidden="true" />;
};
return (
@ -168,7 +178,8 @@ const BookmarkMenu: FC = () => {
render={
<Ariakit.MenuButton
id="bookmark-menu-button"
aria-label={localize('com_ui_bookmarks_add')}
aria-label={buttonAriaLabel}
aria-pressed={hasBookmarks}
className={cn(
'mt-text-sm flex size-10 flex-shrink-0 items-center justify-center gap-2 rounded-xl border border-border-light bg-presentation text-sm transition-colors duration-200 hover:bg-surface-hover',
isMenuOpen ? 'bg-surface-hover' : '',

View file

@ -25,6 +25,13 @@ const BookmarkNav: FC<BookmarkNavProps> = ({ tags, setTags }: BookmarkNavProps)
[tags, localize],
);
const buttonAriaLabel = useMemo(() => {
if (tags.length === 0) {
return localize('com_ui_bookmarks');
}
return localize('com_ui_bookmarks_count_selected', { count: tags.length });
}, [tags.length, localize]);
const bookmarks = useMemo(() => data?.filter((tag) => tag.count > 0) ?? [], [data]);
const handleTagClick = useCallback(
@ -73,6 +80,7 @@ const BookmarkNav: FC<BookmarkNavProps> = ({ tags, setTags }: BookmarkNavProps)
<BookmarkIcon className="size-4" />
),
onClick: () => handleTagClick(bookmark.tag),
ariaChecked: isSelected,
});
}
}
@ -96,7 +104,8 @@ const BookmarkNav: FC<BookmarkNavProps> = ({ tags, setTags }: BookmarkNavProps)
render={
<Ariakit.MenuButton
id="bookmark-nav-menu-button"
aria-label={localize('com_ui_bookmarks')}
aria-label={buttonAriaLabel}
aria-pressed={tags.length > 0}
className={cn(
'flex items-center justify-center',
'size-10 border-none text-text-primary hover:bg-accent hover:text-accent-foreground',

View file

@ -771,6 +771,7 @@
"com_ui_bookmarks_title": "Title",
"com_ui_bookmarks_update_error": "There was an error updating the bookmark",
"com_ui_bookmarks_update_success": "Bookmark updated successfully",
"com_ui_bookmarks_count_selected": "Bookmarks, {{count}} selected",
"com_ui_branch_created": "Branch created successfully",
"com_ui_branch_error": "Failed to create branch",
"com_ui_branch_message": "Create branch from this response",

View file

@ -27,6 +27,8 @@ export interface MenuItemProps {
| 'grid'
| undefined;
ariaControls?: string;
ariaLabel?: string;
ariaChecked?: boolean;
ref?: React.Ref<any>;
className?: string;
render?:

View file

@ -148,6 +148,9 @@ const Menu: React.FC<MenuProps> = ({
hideOnClick={item.hideOnClick}
aria-haspopup={item.ariaHasPopup}
aria-controls={item.ariaControls}
aria-label={item.ariaLabel}
aria-checked={item.ariaChecked}
{...(item.ariaChecked !== undefined ? { role: 'menuitemcheckbox' } : {})}
onClick={(event) => {
event.preventDefault();
if (item.onClick) {