Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Added keepAttachedTo method to the BalloonPanelView. #172

Merged
merged 20 commits into from
Apr 5, 2017
Merged

Conversation

oskarwrobel
Copy link
Contributor

@oskarwrobel oskarwrobel commented Mar 20, 2017

Suggested merge commit message (convention)

Feature: Added keepAttachedTo() method to the BalloonPanelView. Closes ckeditor/ckeditor5#5320.

Follow-ups:

this.attachTo( options );

// Then we need to listen to scroll of eny element in the document and update position of the balloon panel.
this.listenTo( document, 'scroll', () => this.attachTo( options ), { useCapture: true } );
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same should be for resize event.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right.

@oskarwrobel oskarwrobel requested a review from oleq March 22, 2017 16:00
this.attachTo( options );

// Then we need to listen to scroll of eny element in the document and update position of the balloon panel.
this.listenTo( document, 'scroll', () => this.attachTo( options ), { useCapture: true } );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This useCapture that you put in here makes the listener very generic. Because of that, events fired in the document will trigger the callback in the capture phase. And since the listener is attached to document, every single scroll event coming from whatever element with overflow: scroll will trigger it. Such element doesn't even need to have anything in common with CKEditor. document aggregates all events with useCapture because it's a root.

For one thing, it's OK. Because the balloon will re–position when elements with overflow: scroll will scroll. But still, it's too much. We need to filter out the events that have no impact on balloon panel, which are events comming from elements that are neither:

  1. the ancestor of the ck-body (or wherever the balloon is located in DOM),
  2. nor the ancestor of the target in attachTo().

Node.contains() will help a lot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I limited updating position to scroll events from elements containing target and limiter elements.

@@ -409,6 +409,55 @@ describe( 'BalloonPanelView', () => {
} );
} );
} );

describe( 'stickTo()', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We need a test to verify that use of useCapture makes sense and actually works.

  2. We need a manual test for BalloonPanel because it's getting quite complex. I think we could develop it on top of http://localhost:8125/ckeditor5-ui/tests/manual/tickets/126/1.html.

Copy link
Contributor Author

@oskarwrobel oskarwrobel Mar 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prepared dedicated test for this ticket: http://localhost:8125/ckeditor5-ui/tests/manual/tickets/170/1.html this test compares attachTo vs keepAttachedTo.

* {@link module:utils/dom/position~getOptimalPosition}. Default `positions` array is
* {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
*/
stickTo( options ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should reference the attachTo method in this name because it's essentially the same thing. Like keepAttachedTo albo attachPermanentlyTo.

This could even become a parameter of attachTo options. I wonder which naming/grouping strategy is the best /cc @Reinmar.

Copy link
Contributor Author

@oskarwrobel oskarwrobel Mar 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to keepAttachedTo.

}, { useCapture: true } );

// We need to listen on window resize event and update position.
this.listenTo( window, 'resize', () => this.attachTo( options ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use https://github.com/ckeditor/ckeditor5-utils/blob/master/src/dom/global.js#L1-L26 instead. It helps a lot stubbing things when writing tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

this.once( 'change:isVisible', () => this.stopListening( document, 'scroll' ) );
this.once( 'change:isVisible', () => {
this.stopListening( document, 'scroll' );
this.stopListening( window, 'resize' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use https://github.com/ckeditor/ckeditor5-utils/blob/master/src/dom/global.js#L1-L26 instead. It helps a lot stubbing things when writing tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@oleq oleq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor fixes needed.

The more important question is: does the new method get the work done for you @oskarwrobel?

Also: I wonder if method renaming would make sense: attachTo() => setPosition(), keepAttachedTo() => attachTo()?

this.attachTo( options );

const target = options.target;
const limiter = options.limiter || defaultLimiterElement;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line lacks test coverage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

<p></p>
<p></p>
<p></p>
<p>Balloon is attached to the <strong>TARGET</strong> element.</p>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use position: absolute or simply large margin and drop these paragraphs. It just looks weird :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

import Template from '@ckeditor/ckeditor5-ui/src/template';

// Content of the balloon panel.
class BalloonContentView extends View {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KISS. Add simple styles

.ck-balloon-panel {
    padding: 2em;
}

and then balloonPanel.innerHTML = 'Balloon'. It should be about what is important in that particular test case, not about balloon content, view nesting and the such. It's just a noise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

expect( attachToSpy.lastCall.args[ 0 ] ).to.deep.equal( { target, limiter } );
} );

it( 'should stop attach when balloon is hidden', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this test could be merged with 'should stop attach when balloon is hidden'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

expect( attachToSpy.lastCall.args[ 0 ] ).to.deep.equal( { target, limiter } );
} );

it( 'should stop attach when balloon is hidden', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/attach/attaching,
s/balloon/the balloon,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/**
* Works exactly the same as {module:ui/panel/balloon/balloonpanelview~BalloonPanelView.attachTo} with one exception.
* Position of attached panel is constantly updated when any of parents of the target or limiter elements are scrolled
* or when browser window is resized. Thanks to this panel always sticks to the target element. See #170.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full link to the issue would be safer. We had code moving between repos and such hashes became outdated more than once already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@oleq
Copy link
Member

oleq commented Apr 3, 2017

Afterthought: Scroll/resize listeners should be killed on component destroy().

@oskarwrobel
Copy link
Contributor Author

Also: I wonder if method renaming would make sense: attachTo() => setPosition(), keepAttachedTo() => attachTo()?

I was thinking about one attachTo method which should always update position on scroll and resize.

// Then we need to listen on scroll event of eny element in the document.
this.listenTo( global.document, 'scroll', ( evt, domEvt ) => {
// And update position if scrolled element contains related to the balloon elements.
if ( domEvt.target.contains( target ) || domEvt.target.contains( limiter ) ) {
Copy link
Member

@oleq oleq Apr 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would happen if options.target or options.limiter are Range or ClientRect? I suppose it's not gonna work and it may throw errors.

For the DOM Range we can do it by obtaining range ancestor Node. For ClientRect, there's nothing we can do because it's an abstract geometric entity, I guess. Right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@oskarwrobel oskarwrobel changed the title Added stickTo method to the BalloonPanelView. Added keepAttachedTo method to the BalloonPanelView. Apr 4, 2017
@oleq
Copy link
Member

oleq commented Apr 5, 2017

OK. It's R+ but since the master is locked we postpone the merge.

@oskarwrobel oskarwrobel merged this pull request into master Apr 5, 2017
@oskarwrobel oskarwrobel deleted the t/170 branch April 5, 2017 18:25
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BalloonPanel doesn't follow its target as the webpage scrolls
2 participants