import { ApproxStructure, Assertions, type StructAssert, type TestStore, UiFinder } from '@ephox/agar'; import { beforeEach, describe, it } from '@ephox/bedrock-client'; import { Arr } from '@ephox/katamari'; import type { SugarElement } from '@ephox/sugar'; import * as AddEventsBehaviour from 'ephox/alloy/api/behaviour/AddEventsBehaviour'; import * as Behaviour from 'ephox/alloy/api/behaviour/Behaviour'; import { Toggling } from 'ephox/alloy/api/behaviour/Toggling'; import * as GuiFactory from 'ephox/alloy/api/component/GuiFactory'; import * as AlloyEvents from 'ephox/alloy/api/events/AlloyEvents'; import * as AlloyTriggers from 'ephox/alloy/api/events/AlloyTriggers'; import * as SystemEvents from 'ephox/alloy/api/events/SystemEvents'; import { Menu } from 'ephox/alloy/api/ui/Menu'; import * as MenuEvents from 'ephox/alloy/menu/util/MenuEvents'; import * as TestDropdownMenu from 'ephox/alloy/test/dropdown/TestDropdownMenu'; import * as GuiSetup from 'ephox/alloy/test/GuiSetup'; describe('MenuRadioTest', () => { const togglingConfig = (store: TestStore, name: string) => ({ toggleClass: 'checked', toggleOnExecute: false, selected: false, exclusive: true, onToggled: store.adder(name) }); const hook = GuiSetup.bddSetup((store, _doc, _body) => GuiFactory.build( Menu.sketch({ value: 'test-menu-1', items: Arr.map([ { type: 'item', data: { value: 'alpha', meta: { text: 'Alpha' }}, hasSubmenu: false, toggling: togglingConfig(store, 'alpha') }, { type: 'item', data: { value: 'beta', meta: { text: 'Beta' }}, hasSubmenu: false, toggling: togglingConfig(store, 'beta') } ], TestDropdownMenu.renderItem), dom: { tag: 'ol', classes: [ 'test-menu' ] }, components: [ Menu.parts.items({ }) ], markers: { item: TestDropdownMenu.markers().item, selectedItem: TestDropdownMenu.markers().selectedItem }, menuBehaviours: Behaviour.derive([ AddEventsBehaviour.config('menu-test-behaviour', [ AlloyEvents.run(MenuEvents.focus(), store.adder('menu.events.focus')) ]) ]) }) )); beforeEach(() => { hook.store().clear(); }); const triggerFocusItem = (target: SugarElement) => { AlloyTriggers.dispatch(hook.component(), target, SystemEvents.focusItem()); }; const itemStructure = (expected: { focused: boolean; checked: boolean }): ApproxStructure.Builder => (s, str, arr) => s.element('li', { classes: [ expected.focused ? arr.has('selected-item') : arr.not('selected-item'), expected.checked ? arr.has('checked') : arr.not('checked'), ], attrs: { 'aria-checked': str.is(String(expected.checked)), 'role': str.is('menuitemradio') } }); it('TINY-8602: Focus first menu item and check structure', () => { const component = hook.component(); const store = hook.store(); const alpha = UiFinder.findIn(component.element, 'li[data-value="alpha"]').getOrDie(); store.assertEq('Before focusItem event', []); triggerFocusItem(alpha); Assertions.assertStructure( 'After focusing item on alpha', ApproxStructure.build((s, str, arr) => s.element('ol', { classes: [ arr.has('test-menu') ], children: [ itemStructure({ focused: true, checked: false })(s, str, arr), itemStructure({ focused: false, checked: false })(s, str, arr) ] })), component.element ); store.assertEq('After focusItem event (alpha)', [ 'menu.events.focus' ]); }); it('TINY-8602: Focus second menu item and check structure', () => { const component = hook.component(); const store = hook.store(); const beta = UiFinder.findIn(component.element, 'li[data-value="beta"]').getOrDie(); triggerFocusItem(beta); Assertions.assertStructure( 'After focusing item on beta', ApproxStructure.build((s, str, arr) => s.element('ol', { classes: [ arr.has('test-menu') ], children: [ itemStructure({ focused: false, checked: false })(s, str, arr), itemStructure({ focused: true, checked: false })(s, str, arr) ] })), component.element ); store.assertEq('After focusItem event (beta)', [ 'menu.events.focus' ]); }); it('TINY-8602: Toggling between the 2 items ensures only one item is toggled', () => { const component = hook.component(); const store = hook.store(); const [ alphaComp, betaComp ] = component.components(); triggerFocusItem(alphaComp.element); store.assertEq('Before toggled event', [ 'menu.events.focus' ]); Toggling.on(alphaComp); Assertions.assertStructure( 'After toggling alpha item', ApproxStructure.build((s, str, arr) => s.element('ol', { classes: [ arr.has('test-menu') ], children: [ itemStructure({ focused: true, checked: true })(s, str, arr), itemStructure({ focused: false, checked: false })(s, str, arr) ] })), component.element ); store.assertEq('After first toggled event', [ 'menu.events.focus', 'alpha' ]); Toggling.on(betaComp); Assertions.assertStructure( 'After toggling beta item', ApproxStructure.build((s, str, arr) => s.element('ol', { classes: [ arr.has('test-menu') ], children: [ itemStructure({ focused: true, checked: false })(s, str, arr), itemStructure({ focused: false, checked: true })(s, str, arr) ] })), component.element ); // Note: Alpha is listed twice as it should have been toggled off when beta was toggled store.assertEq('After second toggled event', [ 'menu.events.focus', 'alpha', 'beta', 'alpha' ]); }); });