import { Chain, Cursors, NamedChain } from '@ephox/agar'; import { UnitTest } from '@ephox/bedrock-client'; import { Optional, Result } from '@ephox/katamari'; import { Css, Html, Scroll, SugarElement } from '@ephox/sugar'; import { assert } from 'chai'; import * as GuiFactory from 'ephox/alloy/api/component/GuiFactory'; import { Container } from 'ephox/alloy/api/ui/Container'; import * as ChainUtils from 'ephox/alloy/test/ChainUtils'; import * as GuiSetup from 'ephox/alloy/test/GuiSetup'; import * as PositionTestUtils from 'ephox/alloy/test/PositionTestUtils'; import { toDomRange } from 'ephox/alloy/test/RangeUtils'; import * as Sinks from 'ephox/alloy/test/Sinks'; UnitTest.asynctest('SelectionInDocPositionTest', (success, failure) => { GuiSetup.setup((_store, _doc, _body) => { let content = ''; for (let i = 0; i < 20; i++) { content += '

paragraph ' + i + '

'; } const editor = SugarElement.fromTag('div'); Html.set(editor, content); const inlineEditor = GuiFactory.build( GuiFactory.external({ uid: 'inline-editor', element: editor }) ); Css.setAll(inlineEditor.element, { 'margin-top': '300px', 'height': '200px', 'overflow': 'scroll', 'border': '1px solid red' }); return GuiFactory.build( Container.sketch({ components: [ GuiFactory.premade(Sinks.fixedSink()), GuiFactory.premade(Sinks.relativeSink()), GuiFactory.premade(Sinks.popup()), GuiFactory.premade(inlineEditor) ] }) ); }, (_doc, _body, gui, _component, _store) => { const cSetupAnchor = Chain.mapper((data: any) => ({ type: 'selection', root: data.inline.element, getSelection: () => { return Optional.some( Cursors.calculate(data.inline.element, data.path) ); } })); const cAssertBelowSelection = NamedChain.bundle((data: any) => { const root = data.inline.element; const popup = data.popup; const path = data.path; const range = toDomRange(Cursors.calculate(root, path)); const selectionBox = range.getBoundingClientRect(); const popupBox = popup.element.dom.getBoundingClientRect(); assert.isAtLeast(popupBox.top, selectionBox.bottom); assert.approximately(popupBox.top, selectionBox.bottom, 5); return Result.value(data); }); return [ Chain.asStep({}, [ NamedChain.asChain([ ChainUtils.cFindUids(gui, { fixed: 'fixed-sink', relative: 'relative-sink', popup: 'popup', inline: 'inline-editor' }), ChainUtils.cLogging( 'Setting selection path to 3rd paragraph', [ NamedChain.writeValue('path', Cursors.path({ startPath: [ 2 ], soffset: 0, finishPath: [ 3 ], foffset: 0 })) ] ), NamedChain.write('anchor', cSetupAnchor), PositionTestUtils.cTestSink( 'Relative, Selected: 3rd paragraph, no page scroll, no editor scroll', 'relative' ), PositionTestUtils.cTestSink( 'Fixed, Selected: 3rd paragraph, no page scroll, no editor scroll', 'fixed' ), PositionTestUtils.cScrollDown( 'inline', '2000px' ), PositionTestUtils.cTestSink( 'Relative, Selected: 3rd paragraph, large scroll, no editor scroll', 'relative' ), PositionTestUtils.cTestSink( 'Fixed, Selected: 3rd paragraph, large scroll, no editor scroll', 'fixed' ), ChainUtils.cLogging( 'Setting selection to 13th paragraph and scrolling there', [ NamedChain.writeValue('path', Cursors.path({ startPath: [ 12 ], soffset: 0, finishPath: [ 13 ], foffset: 0 })), NamedChain.bundle((data: any) => { const root = data.inline.element; const path = data.path; const range = Cursors.calculate(root, path); (range.start as SugarElement).dom.scrollIntoView(); return Result.value(data); }), // Update the anchor NamedChain.write('anchor', cSetupAnchor) ] ), PositionTestUtils.cTestSink( 'Relative, Selected: 13rd paragraph, large scroll, no editor scroll', 'relative' ), PositionTestUtils.cTestSink( 'Fixed, Selected: 13rd paragraph, large scroll, no editor scroll', 'fixed' ), ChainUtils.cLogging( 'Setting selection to 5th-7th paragraph and scrolling there', [ NamedChain.writeValue('path', Cursors.path({ startPath: [ 5 ], soffset: 0, finishPath: [ 7 ], foffset: 1 })), NamedChain.bundle((data: any) => { const root = data.inline.element; // Scroll so the 7th paragraph is at the top const range = Cursors.calculate(root, Cursors.path({ startPath: [ 7 ], soffset: 0, finishPath: [ 7 ], foffset: 1 })); Scroll.intoView(range.start as SugarElement, true); return Result.value(data); }), // Update the anchor NamedChain.write('anchor', cSetupAnchor) ] ), PositionTestUtils.cTestSink( 'Relative, Selected: 5th-7th paragraph, no scroll, some editor scroll', 'relative' ), ChainUtils.cLogging( 'Relative, Selected: 5th-7th paragraph, assert below selection', [ cAssertBelowSelection ] ), PositionTestUtils.cTestSink( 'Fixed, Selected: 5th-7th paragraph, no scroll, some editor scroll', 'fixed' ), ChainUtils.cLogging( 'Fixed, Selected: 5th-7th paragraph, assert below selection', [ cAssertBelowSelection ] ) ]) ]) ]; }, success, failure); });