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

t/173: Created various DOM utilities to allow the view document scroll to the selection #174

Merged
merged 23 commits into from
Aug 15, 2017

Conversation

oleq
Copy link
Member

@oleq oleq commented Aug 1, 2017

Suggested merge commit message (convention)

Feature: Created various DOM utilities to allow the view document scroll to the selection. Closes ckeditor/ckeditor5#4966.

BREAKING CHANGE: The static Rect.getViewportRect method has been removed. Use new Rect( window ) instead.

import global from './global';

/**
* Returns an object containing CSS border withs of a specified `HTMLElement`.
Copy link
Member

Choose a reason for hiding this comment

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

widths*

HTML element

left: computedStyles.borderLeftWidth
};

for ( const width in borderWidths ) {
Copy link
Member

Choose a reason for hiding this comment

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

Just use parseInt() above and return the object without iterating over it. That will be shorter and faster.

src/dom/rect.js Outdated
@@ -234,21 +247,63 @@ export default class Rect {
}

/**
* Checks if all properties ({@link #top}, {@link #left}, {@link #right},
* {@link #bottom}, {@link #width} and {@link #height}) are the same as in the other `Rect`.
Copy link
Member

Choose a reason for hiding this comment

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

are the same -> equal? properties are "the same" has a bit different meaning than "values of these properties equal ...". It's a detail and shouldn't be confusing, but I found the wording a bit unusual.

src/dom/rect.js Outdated
}

/**
* Checks whether a `Rect` fully contains another `Rect` instance.
Copy link
Member

Choose a reason for hiding this comment

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

The same issue as everywhere else – I don't think that it makes sense to abuse preformatted text and type names. IMO, this makes reading the descriptions harder because the text is less heterogeneous. Plus, it's semantically incorrect as well because you're not checking Rect (type) here but a rect instance.

For me, it should be: "Checks whether this rect fully contains another rect instance."

The same applies to many other places in these docs.

Copy link
Member

Choose a reason for hiding this comment

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

src/dom/rect.js Outdated
contains( anotherRect ) {
const intersectRect = this.getIntersection( anotherRect );

if ( !intersectRect || !intersectRect.isEqual( anotherRect ) ) {
Copy link
Member

Choose a reason for hiding this comment

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

return intersectRect && intersectRect.isEqual( anotherRect );?

src/dom/rect.js Outdated
*/
static getViewportRect() {
static getViewportRect( options = {} ) {
Copy link
Member

Choose a reason for hiding this comment

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

Why does this method look like one big hack? There's something wrong with this method's constructor if you need to override stuff like _source and call private methods once an instance was initialised.

Copy link
Member Author

Choose a reason for hiding this comment

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

I removed the method and allowed new Rect( window ). See the updated docs.

src/dom/rect.js Outdated
rect._source = global.window;

if ( options.excludeScrollbars ) {
rect._excludeScrollbarsAndBorders();
Copy link
Member

Choose a reason for hiding this comment

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

Couldn't this be a public method? It'd make more sense to call it manually, rather than add the options param everywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

I made a public method out of this thing.

* by keeping the `target` some distance from the edge of the viewport and thus making it easier to
* read or edit by the user.
*/
function scrollViewportToShowTarget( { target, viewportOffset = 0 } ) {
Copy link
Member

Choose a reason for hiding this comment

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

won't scrollViewportToShowTarget( { target } ) crash? It will leave viewportOffset set to undefined or not? I can never remember;|

Copy link
Member Author

Choose a reason for hiding this comment

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

It won't. See the tests and tutorials.

Copy link
Member

Choose a reason for hiding this comment

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

OK, the problems I remembered were with this notation: function x( a = { x: 1, y: 2 } ).

//
// To prevent this, we're checking the rects moved by the viewportOffset to cover
// the upper/lower edge of the viewport.
const rects = [ targetShiftedUpRect, targetShiftedDownRect ];
Copy link
Member

Choose a reason for hiding this comment

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

Yyyy... Are you sure about this method's code? I don't want to analyse it :D

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. It's a pure magic 😄

} while ( ( parent = parent.parentNode ) );
}

export default {
Copy link
Member

Choose a reason for hiding this comment

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

Why exporting this as an object? For testing purposes? Please explain it then. And in fact, it's better to expose these functions separately and then expose a test object with the two of them. So that object will be protected, while the functions will be nice and clean and exposed separately.

Copy link
Member Author

@oleq oleq Aug 8, 2017

Choose a reason for hiding this comment

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

Yes. It's for testing purposes, e.g. to mock scrollAncestorsToShowTarget() and check if scrollViewportToShowTarget() always calls it first.

Without the mock, the test becomes ridiculously complex because both viewport and ancestors are to be scrolled and it's fairly obvious that scrollAncestorsToShowTarget() is already tested extensively in the other place.

Copy link
Member

@Reinmar Reinmar Aug 9, 2017

Choose a reason for hiding this comment

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

OK, but you can still code this in a clearer way:

const module = { a, b };

export function a() {
	module.b();
}

export function b() {
	...
}

export { module as _test };

Thanks to the fact that a() uses b() through module you can still mock the stuff.

/* global Node */

/**
* @module utils/dom/scroll
Copy link
Member

Choose a reason for hiding this comment

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

Do I see correctly that there are no tests for this module?

Copy link
Member Author

Choose a reason for hiding this comment

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

There are lots of them.

Copy link
Member

Choose a reason for hiding this comment

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

OK, they got folded so I missed them.

@Reinmar
Copy link
Member

Reinmar commented Aug 9, 2017

I love the manual test :D The description is unclear (it's too complicated – it's hard to figure out what you're actually supposed to do) but you can play with these elements in various ways.

I'd only do one change – make the squares borders' rounded. That will help in recognizing whether the entire square is visible. Something like 5px will be enough. You can also think about different style which might help. 1px border would also be good.

enumerable: false
} );

if ( isElement( source ) ) {
copyRectProperties( this, source.getBoundingClientRect() );
} else if ( isRange( source ) ) {
copyRectProperties( this, Rect.getDomRangeRects( source )[ 0 ] );
} else if ( source === global.window ) {
Copy link
Member

Choose a reason for hiding this comment

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

What about other windows? Iframes will have their own. This deserves a followup.

PS. The next line is not optimal (you could use source).

let scrollBarWidth, scrollBarHeight;

if ( source === global.window ) {
scrollBarWidth = global.window.innerWidth - global.document.documentElement.clientWidth;
Copy link
Member

Choose a reason for hiding this comment

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

Just like I commented above – depending on the main window and document is unsafe.

* @returns {Boolean} `true` if contains, `false` otherwise.
*/
contains( anotherRect ) {
const intersectRect = this.getIntersection( anotherRect );
Copy link
Member

Choose a reason for hiding this comment

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

What about two rects next to each other?

Copy link
Member

Choose a reason for hiding this comment

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

OK, nvmnd. I didn't ask :D

@Reinmar Reinmar merged commit b143e9c into master Aug 15, 2017
@Reinmar Reinmar deleted the t/173 branch August 15, 2017 16:50
@Reinmar
Copy link
Member

Reinmar commented Aug 15, 2017

And here's the followup: https://github.com/ckeditor/ckeditor5-utils/issues/175.

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.

Create DOM utils to help implementing the viewport scrolling helper
2 participants