use CSS queries (#718)
* use matchMedia * use last-of-type * better type check * simplify distanceFromTopWindow * use visibility * update JSDoc
This commit is contained in:
parent
c03fcf5d03
commit
034191c78a
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"', () => {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
|
@ -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/));
|
||||||
|
|
||||||
|
|
|
@ -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%);
|
||||||
|
|
Loading…
Reference in New Issue