Browse Source

use CSS queries (#718)

* use matchMedia

* use last-of-type

* better type check

* simplify distanceFromTopWindow

* use visibility

* update JSDoc
pull/726/head
Konstantin Vyatkin 2 years ago
committed by Josh Johnson
parent
commit
034191c78a
  1. 1
      config/jsdom.js
  2. 8
      src/scripts/choices.js
  3. 9
      src/scripts/components/container.js
  4. 12
      src/scripts/components/container.test.js
  5. 14
      src/scripts/components/dropdown.js
  6. 24
      src/scripts/components/dropdown.test.js
  7. 13
      src/scripts/lib/utils.js
  8. 5
      src/styles/choices.scss

1
config/jsdom.js

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

8
src/scripts/choices.js

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

9
src/scripts/components/container.js

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

12
src/scripts/components/container.test.js

@ -102,18 +102,6 @@ describe('components/container', () => {
beforeEach(() => {
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"', () => {

14
src/scripts/components/dropdown.js

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

24
src/scripts/components/dropdown.test.js

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

13
src/scripts/lib/utils.js

@ -130,19 +130,6 @@ export const dispatchEvent = (element, type, customArgs = null) => {
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 =>
!!(userAgent.match(/Trident/) && userAgent.match(/rv[ :]11/));

5
src/styles/choices.scss

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

Loading…
Cancel
Save