0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-08-06 17:37:39 +00:00
Files
homebrewery/tests/components/dropdown/dropdown.structure.test.jsx
T
2026-05-29 15:58:00 -05:00

138 lines
4.7 KiB
React

import React from 'react';
import { Dropdown } from '../../../client/components/dropdown/dropdown.jsx';
import { renderDropdown, setupDropdownTestLifecycle } from './dropdown.testUtils.js';
setupDropdownTestLifecycle();
describe('Dropdown structure', ()=>{
it('renders expected structure, classes, and accessibility roles', async ()=>{
const { host, cleanup } = await renderDropdown(
<Dropdown groupName='Actions' className='extra-class' color='blue'>
<button className='menu-item' role='menuitem'>Do Thing</button>
</Dropdown>
);
const wrapper = host.querySelector('.menu-wrapper');
const trigger = wrapper.querySelector('button.menu-item.blue');
const menu = wrapper.querySelector('.menu-list');
expect(wrapper).toBeTruthy();
expect(wrapper.className).toContain('extra-class');
expect(wrapper.getAttribute('role')).toBe('none');
expect(trigger.getAttribute('aria-haspopup')).toBe('menu');
expect(trigger.getAttribute('role')).toBe('menuitem');
expect(menu.getAttribute('role')).toBe('menu');
expect(menu.getAttribute('popover')).toBe('auto');
await cleanup();
});
it('creates ids and popovertarget values that match and are unique per instance', async ()=>{
const { host, cleanup } = await renderDropdown(
<div>
<Dropdown groupName='Repeated Label'>
<button className='menu-item' role='menuitem'>A</button>
</Dropdown>
<Dropdown groupName='Repeated Label'>
<button className='menu-item' role='menuitem'>B</button>
</Dropdown>
</div>
);
const triggers = Array.from(host.querySelectorAll('.menu-wrapper > .menu-item'));
const menus = Array.from(host.querySelectorAll('.menu-list'));
const menuIds = menus.map((menu)=>menu.id);
expect(triggers).toHaveLength(2);
expect(menus).toHaveLength(2);
expect(new Set(menuIds).size).toBe(2);
expect(triggers[0].getAttribute('popovertarget')).toBe(menuIds[0]);
expect(triggers[1].getAttribute('popovertarget')).toBe(menuIds[1]);
expect(triggers[0].id).toBe(`${menuIds[0]}-trigger`);
await cleanup();
});
it('applies css anchor styles to trigger and menu', async ()=>{
const { host, cleanup } = await renderDropdown(
<Dropdown groupName='Anchored'>
<button className='menu-item' role='menuitem'>Item</button>
</Dropdown>
);
const trigger = host.querySelector('.menu-wrapper > .menu-item');
const menu = host.querySelector('.menu-list');
const menuId = menu.id;
expect(trigger.getAttribute('style')).toBe(`anchor-name: --${menuId}`);
expect(menu.getAttribute('style')).toBe(`position-anchor: --${menuId}`);
await cleanup();
});
it('disables the trigger when no children are present', async ()=>{
const { host, cleanup } = await renderDropdown(<Dropdown groupName='Empty' />);
expect(host.querySelector('.menu-wrapper > .menu-item').disabled).toBe(true);
await cleanup();
});
it('renders icon class and default caret', async ()=>{
const { host, cleanup } = await renderDropdown(
<Dropdown groupName='Icons' icon='fas fa-star'>
<button className='menu-item' role='menuitem'>Item</button>
</Dropdown>
);
const trigger = host.querySelector('.menu-wrapper > .menu-item');
expect(trigger.querySelector('i.fas.fa-star')).toBeTruthy();
expect(trigger.querySelector('i.caret').className).toContain('fa-caret-down');
await cleanup();
});
it('renders a custom trigger when provided', async ()=>{
const { host, cleanup } = await renderDropdown(
<Dropdown groupName='Custom Trigger' customTrigger={<span className='custom-trigger'>Open Menu</span>}>
<button className='menu-item' role='menuitem'>Item</button>
</Dropdown>
);
const trigger = host.querySelector('.menu-wrapper > .menu-item');
expect(trigger.querySelector('.custom-trigger')).toBeTruthy();
expect(trigger.querySelector('.caret')).toBeFalsy();
await cleanup();
});
it('supports nested submenus to at least 3 levels deep', async ()=>{
const { host, cleanup } = await renderDropdown(
<Dropdown groupName='Level 1'>
<Dropdown groupName='Level 2'>
<Dropdown groupName='Level 3'>
<button className='menu-item' role='menuitem'>Leaf</button>
</Dropdown>
</Dropdown>
</Dropdown>
);
const triggers = Array.from(host.querySelectorAll('.menu-wrapper > .menu-item'));
const menus = Array.from(host.querySelectorAll('.menu-list'));
expect(triggers).toHaveLength(3);
expect(menus).toHaveLength(3);
for (const trigger of triggers) {
const targetId = trigger.getAttribute('popovertarget');
expect(targetId).toBeTruthy();
expect(host.querySelector(`#${targetId}`)).toBeTruthy();
}
expect(triggers[1].querySelector('.caret').className).toContain('fa-caret-right');
expect(triggers[2].querySelector('.caret').className).toContain('fa-caret-right');
await cleanup();
});
});