import { FocusTools, Keyboard, Keys, type TestStore } from '@ephox/agar'; import { context, describe, it } from '@ephox/bedrock-client'; import { Arr } from '@ephox/katamari'; import { SugarDocument, type SugarElement } from '@ephox/sugar'; import * as Behaviour from 'ephox/alloy/api/behaviour/Behaviour'; import { Focusing } from 'ephox/alloy/api/behaviour/Focusing'; import { Keying } from 'ephox/alloy/api/behaviour/Keying'; import * as GuiFactory from 'ephox/alloy/api/component/GuiFactory'; import type { SketchSpec } from 'ephox/alloy/api/component/SpecTypes'; import * as AlloyEvents from 'ephox/alloy/api/events/AlloyEvents'; import { Container } from 'ephox/alloy/api/ui/Container'; import * as GuiSetup from 'ephox/alloy/test/GuiSetup'; describe('browser.alloy.behaviour.keying.FlowKeyingTest', () => { const makeFlow = (store: TestStore, cycles: boolean): SketchSpec => { const item = (classes: string[], name: string) => Container.sketch({ dom: { tag: 'span', styles: { display: 'inline-block', width: '20px', height: '20px', margin: '2px', border: '1px solid ' + (Arr.contains(classes, 'stay') ? 'blue' : 'yellow') }, classes }, events: AlloyEvents.derive([ AlloyEvents.runOnExecute( store.adder('item.execute: ' + name) ) ]), containerBehaviours: Behaviour.derive([ Focusing.config({ }) ]) }); return Container.sketch({ dom: { classes: [ 'flow-keying-test' ], styles: { background: 'white', width: '200px', height: '200px' } }, uid: 'custom-uid', containerBehaviours: Behaviour.derive([ Keying.config({ mode: 'flow', selector: '.stay', onEscape: store.adderH('flow.onEscape'), cycles }) ]), components: [ item([ 'stay', 'one' ], 'one'), item([ 'stay', 'two' ], 'two'), item([ 'skip', 'three' ], 'three'), item([ 'skip', 'four' ], 'four'), item([ 'stay', 'five' ], 'five') ] }); }; GuiSetup.bddAddStyles(SugarDocument.getDocument(), [ `.selected-item { background-color: #cadbee; }` ]); const sequence = async (doc: SugarElement, key: number, modifiers: { }, identifiers: Array<{ label: string; selector: string }>) => { const cases = Arr.range(identifiers.length, async (i: number) => { Keyboard.activeKeydown(doc, key, modifiers); await FocusTools.pTryOnSelector( 'Focus should move from ' + (i > 0 ? identifiers[i - 1].label : '(start)') + ' to ' + identifiers[i].label, doc, identifiers[i].selector ); }); for (const c of cases) { await c; } }; const hook = GuiSetup.bddSetup( (store) => GuiFactory.build(makeFlow(store, true)) ); const hookWithoutCycles = GuiSetup.bddSetup( (store) => GuiFactory.build(makeFlow(store, false)) ); const targets = { one: { label: 'one', selector: '.one' }, two: { label: 'two', selector: '.two' }, five: { label: 'five', selector: '.five' } }; context('Testing FlowKeying', () => { it('Flow Keying Skip Element Test', async () => { const container = hook.component(); const doc = hook.root(); const store = hook.store(); const keyLoger = GuiSetup.setupKeyLogger(hook.body()); FocusTools.setFocus(container.element, '.one'); await sequence( doc, Keys.right(), {}, [ targets.two, targets.five, targets.one, targets.two, targets.five, targets.one ] ); await sequence( doc, Keys.left(), { }, [ targets.five, targets.two, targets.one, targets.five, targets.two, targets.one ] ); await sequence( doc, Keys.up(), { }, [ targets.five, targets.two, targets.one, targets.five, targets.two, targets.one ] ); await sequence( doc, Keys.down(), { }, [ targets.two, targets.five, targets.one, targets.two, targets.five, targets.one ] ); // Test execute Keyboard.activeKeydown(doc, Keys.enter(), {}); store.assertEq('Check that execute has fired on the right target', [ 'item.execute: one' ]); store.clear(); Keyboard.activeKeyup(doc, Keys.escape(), {}); store.assertEq('Check that escape handler has fired', [ 'flow.onEscape' ]); GuiSetup.teardownKeyLogger(keyLoger, [ ]); }); it('TINY-9429: Flow with cycles false should stop on the first element when left is pressed and on the last when right is pressed', async () => { const container = hookWithoutCycles.component(); const doc = hookWithoutCycles.root(); const store = hookWithoutCycles.store(); const keyLoger = GuiSetup.setupKeyLogger(hookWithoutCycles.body()); FocusTools.setFocus(container.element, '.one'); await sequence( doc, Keys.right(), { }, [ targets.two, targets.five, targets.five, targets.five, targets.five, targets.five ] ); GuiSetup.teardownKeyLogger(keyLoger, [ ]); await sequence( doc, Keys.left(), { }, [ targets.two, targets.one, targets.one, targets.one, targets.one, targets.one ] ); GuiSetup.teardownKeyLogger(keyLoger, [ ]); // Test execute Keyboard.activeKeydown(doc, Keys.enter(), {}); store.assertEq('Check that execute has fired on the right target', [ 'item.execute: one' ]); store.clear(); Keyboard.activeKeyup(doc, Keys.escape(), {}); store.assertEq('Check that escape handler has fired', [ 'flow.onEscape' ]); GuiSetup.teardownKeyLogger(keyLoger, [ ]); }); }); });