use CSS queries (#718)

* use matchMedia

* use last-of-type

* better type check

* simplify distanceFromTopWindow

* use visibility

* update JSDoc
This commit is contained in:
Konstantin Vyatkin 2019-10-30 13:28:15 -04:00 committed by Josh Johnson
parent c03fcf5d03
commit 034191c78a
8 changed files with 21 additions and 65 deletions

View File

@ -42,6 +42,7 @@ global.HTMLSelectElement = window.HTMLSelectElement;
global.HTMLInputElement = window.HTMLInputElement; global.HTMLInputElement = window.HTMLInputElement;
global.DocumentFragment = window.DocumentFragment; global.DocumentFragment = window.DocumentFragment;
global.requestAnimationFrame = window.requestAnimationFrame; global.requestAnimationFrame = window.requestAnimationFrame;
window.matchMedia = () => true;
copyProps(window, global); copyProps(window, global);

View File

@ -384,7 +384,7 @@ class Choices {
requestAnimationFrame(() => { requestAnimationFrame(() => {
this.dropdown.show(); this.dropdown.show();
this.containerOuter.open(this.dropdown.distanceFromTopWindow()); this.containerOuter.open(this.dropdown.distanceFromTopWindow);
if (!preventInputFocus && this._canSearch) { if (!preventInputFocus && this._canSearch) {
this.input.focus(); this.input.focus();
@ -1463,9 +1463,9 @@ class Choices {
let nextEl; let nextEl;
if (skipKey) { if (skipKey) {
if (directionInt > 0) { if (directionInt > 0) {
nextEl = Array.from( nextEl = this.dropdown.element.querySelector(
this.dropdown.element.querySelectorAll(selectableChoiceIdentifier), `${selectableChoiceIdentifier}:last-of-type`,
).pop(); );
} else { } else {
nextEl = this.dropdown.element.querySelector( nextEl = this.dropdown.element.querySelector(
selectableChoiceIdentifier, selectableChoiceIdentifier,

View File

@ -1,4 +1,4 @@
import { getWindowHeight, wrap } from '../lib/utils'; import { wrap } from '../lib/utils';
export default class Container { export default class Container {
constructor({ element, type, classNames, position }) { constructor({ element, type, classNames, position }) {
@ -38,8 +38,8 @@ export default class Container {
* @param {Number} dropdownPos * @param {Number} dropdownPos
* @returns * @returns
*/ */
shouldFlip(dropdownPos, windowHeight = getWindowHeight()) { shouldFlip(dropdownPos) {
if (dropdownPos === undefined) { if (typeof dropdownPos !== 'number') {
return false; return false;
} }
@ -47,7 +47,8 @@ export default class Container {
// greater than the window height flip the dropdown. // greater than the window height flip the dropdown.
let shouldFlip = false; let shouldFlip = false;
if (this.position === 'auto') { if (this.position === 'auto') {
shouldFlip = dropdownPos >= windowHeight; shouldFlip = !window.matchMedia(`(min-height: ${dropdownPos + 1}px)`)
.matches;
} else if (this.position === 'top') { } else if (this.position === 'top') {
shouldFlip = true; shouldFlip = true;
} }

View File

@ -102,18 +102,6 @@ describe('components/container', () => {
beforeEach(() => { beforeEach(() => {
instance.position = 'auto'; instance.position = 'auto';
}); });
describe('dropdownPos is greater than window height', () => {
it('returns false', () => {
expect(instance.shouldFlip(100, 1000)).to.equal(false);
});
});
describe('dropdownPos is less than window height', () => {
it('returns true', () => {
expect(instance.shouldFlip(100, 50)).to.equal(true);
});
});
}); });
describe('position config option set to "top"', () => { describe('position config option set to "top"', () => {

View File

@ -6,17 +6,11 @@ export default class Dropdown {
} }
/** /**
* Determine how far the top of our element is from * Bottom position of dropdown in viewport coordinates
* the top of the window * @type {number} Vertical position
* @return {Number} Vertical position
*/ */
distanceFromTopWindow() { get distanceFromTopWindow() {
this.dimensions = this.element.getBoundingClientRect(); return this.element.getBoundingClientRect().bottom;
this.position = Math.ceil(
this.dimensions.top + window.pageYOffset + this.element.offsetHeight,
);
return this.position;
} }
/** /**

View File

@ -34,15 +34,13 @@ describe('components/dropdown', () => {
describe('distanceFromTopWindow', () => { describe('distanceFromTopWindow', () => {
let top; let top;
let offset;
let dimensions; let dimensions;
let getBoundingClientRectStub; let getBoundingClientRectStub;
beforeEach(() => { beforeEach(() => {
top = 100; top = 100;
offset = 50;
dimensions = { dimensions = {
bottom: 0, bottom: 121,
height: 0, height: 0,
left: 0, left: 0,
right: 0, right: 0,
@ -53,31 +51,17 @@ describe('components/dropdown', () => {
getBoundingClientRectStub = sinon getBoundingClientRectStub = sinon
.stub(instance.element, 'getBoundingClientRect') .stub(instance.element, 'getBoundingClientRect')
.returns(dimensions); .returns(dimensions);
window.pageYOffset = 50;
}); });
afterEach(() => { afterEach(() => {
getBoundingClientRectStub.restore(); getBoundingClientRectStub.restore();
}); });
it('determines how far the top of our element is from the top of the window', () => { it('determines how far the top of our element is from the top of the viewport', () => {
const expectedResponse = top + offset; const expectedResponse = dimensions.bottom;
const actualResponse = instance.distanceFromTopWindow(); const actualResponse = instance.distanceFromTopWindow;
expect(actualResponse).to.equal(expectedResponse); expect(actualResponse).to.equal(expectedResponse);
}); });
it('assigns dimensions to instance', () => {
instance.distanceFromTopWindow();
const expectedResponse = dimensions;
expect(instance.dimensions).to.equal(expectedResponse);
});
it('assigns posisiton to instance', () => {
instance.distanceFromTopWindow();
const expectedResponse = top + offset;
expect(instance.position).to.equal(expectedResponse);
});
}); });
describe('getChild', () => { describe('getChild', () => {

View File

@ -130,19 +130,6 @@ export const dispatchEvent = (element, type, customArgs = null) => {
return element.dispatchEvent(event); return element.dispatchEvent(event);
}; };
export const getWindowHeight = () => {
const { body } = document;
const html = document.documentElement;
return Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight,
);
};
export const isIE11 = userAgent => export const isIE11 = userAgent =>
!!(userAgent.match(/Trident/) && userAgent.match(/rv[ :]11/)); !!(userAgent.match(/Trident/) && userAgent.match(/rv[ :]11/));

View File

@ -217,7 +217,7 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
} }
.#{$choices-selector}__list--dropdown { .#{$choices-selector}__list--dropdown {
display: none; visibility: hidden;
z-index: 1; z-index: 1;
position: absolute; position: absolute;
width: 100%; width: 100%;
@ -229,8 +229,9 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
border-bottom-right-radius: $choices-border-radius; border-bottom-right-radius: $choices-border-radius;
overflow: hidden; overflow: hidden;
word-break: break-all; word-break: break-all;
will-change: visibility;
&.is-active { &.is-active {
display: block; visibility: visible;
} }
.is-open & { .is-open & {
border-color: darken($choices-keyline-color, 15%); border-color: darken($choices-keyline-color, 15%);