Skip to content

Commit d8d55f8

Browse files
committed
Move aria-labelledby
1 parent 8aa580e commit d8d55f8

2 files changed

Lines changed: 75 additions & 33 deletions

File tree

packages/module/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ describe('ChatbotConversationHistoryNav', () => {
749749
expect(screen.getByRole('button', { name: 'Chats' })).toBeInTheDocument();
750750
});
751751

752-
it('labels each MenuGroup with aria-labelledby referencing its visible label', () => {
752+
it('labels each MenuList with aria-labelledby referencing its visible label', () => {
753753
const groups: ConversationGroup[] = [
754754
{
755755
id: 'pinned',
@@ -767,7 +767,7 @@ describe('ChatbotConversationHistoryNav', () => {
767767
}
768768
];
769769

770-
const { container } = render(
770+
render(
771771
<ChatbotConversationHistoryNav
772772
onDrawerToggle={onDrawerToggle}
773773
isDrawerOpen={true}
@@ -781,17 +781,40 @@ describe('ChatbotConversationHistoryNav', () => {
781781
expect(pinnedHeading).toHaveAttribute('id', 'chatbot-nav-group-pinned-label');
782782
expect(pinnedHeading.closest('section')).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-pinned-label');
783783

784-
const staticMenu = container.querySelector('.pf-v6-c-menu.pf-chatbot__history-menu');
785-
expect(staticMenu).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-pinned-label');
784+
expect(screen.getByRole('menu', { name: 'Pinned chats' })).toHaveAttribute(
785+
'aria-labelledby',
786+
'chatbot-nav-group-pinned-label'
787+
);
788+
expect(document.querySelector('ul.pf-v6-c-menu__list[aria-labelledby="chatbot-nav-group-pinned-label"]')).toBeTruthy();
786789

787790
const chatsToggle = screen.getByRole('button', { name: 'Chats' });
788791
expect(chatsToggle).toHaveAttribute('id', 'chatbot-nav-group-chats-toggle');
789792

790-
const expandableMenu = container.querySelectorAll('.pf-v6-c-menu.pf-chatbot__history-menu')[1];
791-
expect(expandableMenu).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-chats-toggle');
793+
expect(screen.getByRole('menu', { name: 'Chats' })).toHaveAttribute(
794+
'aria-labelledby',
795+
'chatbot-nav-group-chats-toggle'
796+
);
797+
expect(document.querySelector('ul.pf-v6-c-menu__list[aria-labelledby="chatbot-nav-group-chats-toggle"]')).toBeTruthy();
798+
});
799+
800+
it('labels grouped object conversations on the menu list element', () => {
801+
render(
802+
<ChatbotConversationHistoryNav
803+
onDrawerToggle={onDrawerToggle}
804+
isDrawerOpen={true}
805+
displayMode={ChatbotDisplayMode.fullscreen}
806+
setIsDrawerOpen={jest.fn()}
807+
conversations={{ Today: initialConversations }}
808+
/>
809+
);
810+
811+
expect(document.querySelector('ul.pf-v6-c-menu__list')).toHaveAttribute(
812+
'aria-labelledby',
813+
'chatbot-nav-group-Today-label'
814+
);
792815
});
793816

794-
it('labels a shared static menu with all group title ids', () => {
817+
it('labels each menu list in a shared static menu with its group title id', () => {
795818
const groups: ConversationGroup[] = [
796819
{
797820
id: 'pinned',
@@ -805,7 +828,7 @@ describe('ChatbotConversationHistoryNav', () => {
805828
}
806829
];
807830

808-
const { container } = render(
831+
render(
809832
<ChatbotConversationHistoryNav
810833
onDrawerToggle={onDrawerToggle}
811834
isDrawerOpen={true}
@@ -815,9 +838,13 @@ describe('ChatbotConversationHistoryNav', () => {
815838
/>
816839
);
817840

818-
expect(container.querySelector('.pf-v6-c-menu.pf-chatbot__history-menu')).toHaveAttribute(
841+
expect(screen.getByRole('menu', { name: 'Pinned chats' })).toHaveAttribute(
842+
'aria-labelledby',
843+
'chatbot-nav-group-pinned-label'
844+
);
845+
expect(screen.getByRole('menu', { name: 'Recent chats' })).toHaveAttribute(
819846
'aria-labelledby',
820-
'chatbot-nav-group-pinned-label chatbot-nav-group-recent-label'
847+
'chatbot-nav-group-recent-label'
821848
);
822849
});
823850

@@ -883,7 +910,7 @@ describe('ChatbotConversationHistoryNav', () => {
883910
}
884911
];
885912

886-
const { container } = render(
913+
render(
887914
<ChatbotConversationHistoryNav
888915
onDrawerToggle={onDrawerToggle}
889916
isDrawerOpen={true}
@@ -895,8 +922,8 @@ describe('ChatbotConversationHistoryNav', () => {
895922

896923
// Collapsed expandable groups render their toggle outside the menu and omit the menu
897924
// entirely until expanded, so their items are not part of arrow-key navigation.
898-
expect(container.querySelectorAll('.pf-v6-c-menu.pf-chatbot__history-menu')).toHaveLength(1);
899-
expect(container.querySelector('.pf-v6-c-menu.pf-chatbot__history-menu')).toHaveAttribute(
925+
expect(screen.getAllByRole('menu')).toHaveLength(1);
926+
expect(screen.getByRole('menu', { name: 'Chats' })).toHaveAttribute(
900927
'aria-labelledby',
901928
'chatbot-nav-group-chats-label'
902929
);

packages/module/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,35 @@ const getExpandableGroupToggleId = (groupId: string) => `chatbot-nav-group-${gro
7272

7373
const getShowAllToggleId = (groupId: string) => `chatbot-nav-group-${groupId}-show-all-toggle`;
7474

75+
const getStaticMenuLabelId = (group: { id: string; menuGroupProps?: MenuGroupProps }) =>
76+
group.menuGroupProps?.titleId ?? getGroupLabelId(group.id);
77+
78+
const getMenuListLabelledBy = (group: {
79+
id: string;
80+
expandable?: { isExpanded: boolean; onToggle: (isExpanded: boolean) => void };
81+
menuGroupProps?: MenuGroupProps;
82+
}) => {
83+
if (group.expandable) {
84+
return group.menuGroupProps?.['aria-labelledby'] ?? getExpandableGroupToggleId(group.id);
85+
}
86+
87+
return group.menuGroupProps?.['aria-labelledby'] ?? getStaticMenuLabelId(group);
88+
};
89+
90+
const getMenuListLabelText = (label: ReactNode) =>
91+
typeof label === 'string' || typeof label === 'number' ? String(label) : undefined;
92+
93+
const getMenuListProps = (group: {
94+
id: string;
95+
label: ReactNode;
96+
expandable?: { isExpanded: boolean; onToggle: (isExpanded: boolean) => void };
97+
menuGroupProps?: MenuGroupProps;
98+
menuListProps?: Omit<MenuListProps, 'children'>;
99+
}): Omit<MenuListProps, 'children'> => ({
100+
...group.menuListProps,
101+
'aria-labelledby': group.menuListProps?.['aria-labelledby'] ?? getMenuListLabelledBy(group)
102+
});
103+
75104
const focusElementById = (id: string) => {
76105
document.getElementById(id)?.focus();
77106
};
@@ -128,7 +157,7 @@ const ShowAllGroupBody: FunctionComponent<ShowAllGroupBodyProps> = ({ group, get
128157

129158
return (
130159
<>
131-
<MenuList {...group.menuListProps}>
160+
<MenuList {...getMenuListProps(group)}>
132161
{alwaysVisibleItems.map((chat) => (
133162
<Fragment key={chat.id}>{getNavItem(chat)}</Fragment>
134163
))}
@@ -212,14 +241,6 @@ const buildConversationMenuSegments = (groups: ConversationGroup[]): Conversatio
212241
return [...segments, { type: 'static', groups: [group] }];
213242
}, []);
214243

215-
const getStaticMenuLabelId = (group: ConversationGroup) => group.menuGroupProps?.titleId ?? getGroupLabelId(group.id);
216-
217-
const getStaticMenuLabelledBy = (groups: ConversationGroup[]) =>
218-
groups
219-
.map((group) => group.menuGroupProps?.['aria-labelledby'] ?? getStaticMenuLabelId(group))
220-
.join(' ')
221-
.trim();
222-
223244
export interface Conversation {
224245
/** Conversation id */
225246
id: string;
@@ -477,7 +498,9 @@ export const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversatio
477498

478499
return (
479500
<>
480-
<MenuList {...group.menuListProps}>{renderConversationItems(group.items, group.id)}</MenuList>
501+
<MenuList {...getMenuListProps(group)}>
502+
{renderConversationItems(group.items, group.id)}
503+
</MenuList>
481504
{group.footer}
482505
</>
483506
);
@@ -510,14 +533,13 @@ export const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversatio
510533
);
511534
};
512535

513-
const renderConversationMenu = (labelledBy: string | undefined, content: ReactNode, key?: string) => (
536+
const renderConversationMenu = (content: ReactNode, key?: string) => (
514537
<Menu
515538
key={key}
516539
className="pf-chatbot__history-menu"
517540
isPlain
518541
onSelect={onSelectActiveItem}
519542
activeItemId={activeItemId}
520-
{...(labelledBy ? { 'aria-labelledby': labelledBy } : {})}
521543
{...menuProps}
522544
>
523545
<MenuContent {...menuContentProps}>{content}</MenuContent>
@@ -527,20 +549,14 @@ export const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversatio
527549
const renderConversationMenuSegment = (segment: ConversationMenuSegment) => {
528550
if (segment.type === 'static') {
529551
return renderConversationMenu(
530-
getStaticMenuLabelledBy(segment.groups),
531552
<>{segment.groups.map(renderConversationGroup)}</>,
532553
segment.groups.map((group) => group.id).join('-')
533554
);
534555
}
535556

536-
const toggleId = getExpandableGroupToggleId(segment.group.id);
537-
538557
return (
539558
<ExpandableConversationGroup group={segment.group} key={segment.group.id}>
540-
{renderConversationMenu(
541-
segment.group.menuGroupProps?.['aria-labelledby'] ?? toggleId,
542-
renderGroupBody(segment.group)
543-
)}
559+
{renderConversationMenu(renderGroupBody(segment.group))}
544560
</ExpandableConversationGroup>
545561
);
546562
};
@@ -567,7 +583,6 @@ export const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversatio
567583
}
568584

569585
return renderConversationMenu(
570-
undefined,
571586
<MenuList {...menuListProps}>{renderConversationItems(conversations)}</MenuList>
572587
);
573588
}

0 commit comments

Comments
 (0)