Skip to content

Commit 45e7e0a

Browse files
authored
[fix]: menu-items programmatically added to menu-list do not properl… (#35765)
1 parent cdded25 commit 45e7e0a

3 files changed

Lines changed: 104 additions & 6 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[fix]: menu-items programmatically added to menu-list do not properly get assigned data indent.",
4+
"packageName": "@fluentui/web-components",
5+
"email": "jes@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

packages/web-components/src/menu-list/menu-list.spec.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,99 @@ test.describe('Menu', () => {
493493
}
494494
});
495495

496+
test('should set the data-indent attribute correctly when menu items are dynamically appended', async ({
497+
fastPage,
498+
}) => {
499+
const { element } = fastPage;
500+
501+
await fastPage.setTemplate({ innerHTML: '' });
502+
503+
await element.evaluate(node => {
504+
const items = ['item 1', 'item 2', 'item 3'];
505+
506+
items.forEach(item => {
507+
const menuItem = document.createElement('fluent-menu-item');
508+
menuItem.role = 'menuitemradio';
509+
menuItem.textContent = item;
510+
node.append(menuItem);
511+
});
512+
});
513+
514+
const menuItems = element.locator('fluent-menu-item');
515+
await expect(menuItems).toHaveCount(3);
516+
517+
for (const item of await menuItems.all()) {
518+
await expect(item).toHaveAttribute('data-indent', '1');
519+
}
520+
});
521+
522+
test('should set the data-indent attribute correctly when menu items are appended via a DocumentFragment', async ({
523+
fastPage,
524+
}) => {
525+
const { element } = fastPage;
526+
527+
await fastPage.setTemplate({ innerHTML: '' });
528+
529+
await element.evaluate(node => {
530+
const fragment = document.createDocumentFragment();
531+
const items = ['item 1', 'item 2', 'item 3'];
532+
533+
items.forEach(item => {
534+
const menuItem = document.createElement('fluent-menu-item');
535+
menuItem.role = 'menuitemradio';
536+
menuItem.textContent = item;
537+
fragment.append(menuItem);
538+
});
539+
540+
node.append(fragment);
541+
});
542+
543+
const menuItems = element.locator('fluent-menu-item');
544+
await expect(menuItems).toHaveCount(3);
545+
546+
for (const item of await menuItems.all()) {
547+
await expect(item).toHaveAttribute('data-indent', '1');
548+
}
549+
});
550+
551+
test('should update data-indent on existing items when a menuitemradio is appended and removed', async ({
552+
fastPage,
553+
}) => {
554+
const { element } = fastPage;
555+
const menuItems = element.locator('fluent-menu-item');
556+
557+
await test.step('all plain menuitems should start with data-indent 0', async () => {
558+
for (const item of await menuItems.all()) {
559+
await expect(item).toHaveAttribute('data-indent', '0');
560+
}
561+
});
562+
563+
await test.step('appending a menuitemradio should update all items to data-indent 1', async () => {
564+
await element.evaluate(node => {
565+
const menuItem = document.createElement('fluent-menu-item');
566+
menuItem.role = 'menuitemradio';
567+
menuItem.textContent = 'Radio item';
568+
node.append(menuItem);
569+
});
570+
571+
await expect(menuItems).toHaveCount(5);
572+
573+
for (const item of await menuItems.all()) {
574+
await expect(item).toHaveAttribute('data-indent', '1');
575+
}
576+
});
577+
578+
await test.step('removing the menuitemradio should revert all items to data-indent 0', async () => {
579+
await menuItems.last().evaluate(node => node.remove());
580+
581+
await expect(menuItems).toHaveCount(4);
582+
583+
for (const item of await menuItems.all()) {
584+
await expect(item).toHaveAttribute('data-indent', '0');
585+
}
586+
});
587+
});
588+
496589
test.describe('`change` event', () => {
497590
test('should emit `change` event when `checked` property changed', async ({ fastPage }) => {
498591
const { element } = fastPage;

packages/web-components/src/menu-list/menu-list.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export class MenuList extends FASTElement {
161161
}
162162

163163
private static elementIndent(el: HTMLElement): MenuItemColumnCount {
164-
const role = el.getAttribute('role');
164+
const role = el.role;
165165
const startSlot = el.querySelector('[slot=start]');
166166

167167
if (role && role !== MenuItemRole.menuitem) {
@@ -237,7 +237,7 @@ export class MenuList extends FASTElement {
237237
if (changedMenuItem.role === 'menuitemradio' && changedMenuItem.checked === true) {
238238
for (let i = changeItemIndex - 1; i >= 0; --i) {
239239
const item: Element = this.menuItems[i];
240-
const role: string | null = item.getAttribute('role');
240+
const role: string | null = (item as HTMLElement).role;
241241
if (role === MenuItemRole.menuitemradio) {
242242
(item as MenuItem).checked = false;
243243
}
@@ -248,7 +248,7 @@ export class MenuList extends FASTElement {
248248
const maxIndex: number = this.menuItems.length - 1;
249249
for (let i = changeItemIndex + 1; i <= maxIndex; ++i) {
250250
const item: Element = this.menuItems[i];
251-
const role: string | null = item.getAttribute('role');
251+
const role: string | null = (item as HTMLElement).role;
252252
if (role === MenuItemRole.menuitemradio) {
253253
(item as MenuItem).checked = false;
254254
}
@@ -263,9 +263,7 @@ export class MenuList extends FASTElement {
263263
* check if the item is a menu item
264264
*/
265265
protected isMenuItemElement = (el: Element): el is HTMLElement => {
266-
return (
267-
isMenuItem(el) || (isHTMLElement(el) && (el.getAttribute('role') as string) in MenuList.focusableElementRoles)
268-
);
266+
return isMenuItem(el) || (isHTMLElement(el) && !!el.role && el.role in MenuList.focusableElementRoles);
269267
};
270268

271269
/**

0 commit comments

Comments
 (0)