open-consul/ui/packages/consul-ui/app/services/dom.js
Michael Klein 048572946c
ui: chore - upgrade ember and friends (#14518)
* v3.20.2...v3.24.0

* Fix handle undefined outlet in route component

* Don't use template helper for optional modal.open

Using the optional-helper here will trigger a computation
in the same runloop error. This is because we are setting
the `modal`-property when the `<Ref>` component gets
rendered which will update the `this.modal`-property which
will then recompute the `optional`-helper leading to this
error.

Instead we will create an action that will call the `open`-method
on the modal when it is defined. This gets rid of the double
computation error as we will not access the modal property
twice in the same runloop when `modal` is getting set.

* Fix - fn needs to be passed function tab-nav

We create functions in the component file instead
so that fn-helper stops complaining about the
need to pass a function.

* Update ember-exam to 6.1 version

"Makes it compatible" with ember-qunit v5

* scheduleOnce setMaxHeight paged-collection

We need to schedule to get around double-computation error.

* Fix - model.data is removed from ember-data

This has been private API all along - we need to
work around the removal.

Reference: https://github.com/emberjs/data/pull/7338/files#diff-9a8746fc5c86fd57e6122f00fef3155f76f0f3003a24b53fb7c4621d95dcd9bfL1310

* Fix `propContains` instead of `deepEqual` policy

Recent model.data works differently than iterating attributes.
We use `propContains` instead of `deepEqual`. We are only
interested in the properties we assert against and match
the previous behavior with this change.

* Fix `propContains` instead of `deepEqual` token

* Better handling single-records repo test-helper

`model.data` has been removed we need to handle proxies and
model instances differently.

* Fix remaining repository tests with propContains

We don't want to match entire objects - we don't care
about properties we haven't defined in the assertion.

* Don't use template helper for optional modal.open

Using a template helper will give us a recomputation error -
we work around it by creating an explicit action on
the component instead.

* Await `I $verb the $pageObject object` step

* Fix no more customization ember-can

No need to customize, the helper handles destruction
fine on its own.

* Fix - don't pass `optional` functions to fn

We will declare the functions on the component instead.
This gives us the same behavior but no error from
`fn`, which expects a function to be passed.

* Fix - handle `undefined` state on validate modifier

StateChart can yield out an undefined `state` we need
to handle that in the validate modifier

* Fix linting errors tests directory

* Warn / turn off new ember linting issues

We will tackle them one by one and don't want to
autofix issues that could be dangerous to auto-fix.

* Auto-fix linting issues

* More linting configuration

* Fix remaining linting issues

* Fix linting issues new files after rebase

* ui: Remove ember-cli-uglify config now we are using terser (#14574)

Co-authored-by: John Cowen <johncowen@users.noreply.github.com>
2022-09-15 09:43:17 +01:00

195 lines
5.3 KiB
JavaScript

import Service, { inject as service } from '@ember/service';
import { guidFor } from '@ember/object/internals';
// selecting
import qsaFactory from 'consul-ui/utils/dom/qsa-factory';
// TODO: sibling and closest seem to have 'PHP-like' guess the order arguments
// ie. one `string, element` and the other has `element, string`
// see if its possible to standardize
import sibling from 'consul-ui/utils/dom/sibling';
import closest from 'consul-ui/utils/dom/closest';
import isOutside from 'consul-ui/utils/dom/is-outside';
import getComponentFactory from 'consul-ui/utils/dom/get-component-factory';
// events
import normalizeEvent from 'consul-ui/utils/dom/normalize-event';
import createListeners from 'consul-ui/utils/dom/create-listeners';
import clickFirstAnchorFactory from 'consul-ui/utils/dom/click-first-anchor';
// ember-eslint doesn't like you using a single $ so use double
// use $_ for components
const $$ = qsaFactory();
let $_;
let inViewportCallbacks;
const clickFirstAnchor = clickFirstAnchorFactory(closest);
export default class DomService extends Service {
@service('-document') doc;
constructor(owner) {
super(...arguments);
inViewportCallbacks = new WeakMap();
$_ = getComponentFactory(owner);
}
willDestroy() {
super.willDestroy(...arguments);
inViewportCallbacks = null;
$_ = null;
}
document() {
return this.doc;
}
viewport() {
return this.doc.defaultView;
}
guid(el) {
return guidFor(el);
}
focus($el) {
if (typeof $el === 'string') {
$el = this.element($el);
}
if (typeof $el !== 'undefined') {
let previousIndex = $el.getAttribute('tabindex');
$el.setAttribute('tabindex', '0');
$el.focus();
if (previousIndex === null) {
$el.removeAttribute('tabindex');
} else {
$el.setAttribute('tabindex', previousIndex);
}
}
}
// TODO: should this be here? Needs a better name at least
clickFirstAnchor = clickFirstAnchor;
closest = closest;
sibling = sibling;
isOutside = isOutside;
normalizeEvent = normalizeEvent;
setEventTargetProperty(e, property, cb) {
const target = e.target;
return new Proxy(e, {
get: function (obj, prop, receiver) {
if (prop === 'target') {
return new Proxy(target, {
get: function (obj, prop, receiver) {
if (prop === property) {
return cb(e.target[property]);
}
return target[prop];
},
});
}
return Reflect.get(...arguments);
},
});
}
setEventTargetProperties(e, propObj) {
const target = e.target;
return new Proxy(e, {
get: function (obj, prop, receiver) {
if (prop === 'target') {
return new Proxy(target, {
get: function (obj, prop, receiver) {
if (typeof propObj[prop] !== 'undefined') {
return propObj[prop](e.target);
}
return target[prop];
},
});
}
return Reflect.get(...arguments);
},
});
}
listeners = createListeners;
root() {
return this.doc.documentElement;
}
// TODO: Should I change these to use the standard names
// even though they don't have a standard signature (querySelector*)
elementById(id) {
return this.doc.getElementById(id);
}
elementsByTagName(name, context) {
context = typeof context === 'undefined' ? this.doc : context;
return context.getElementsByTagName(name);
}
elements(selector, context) {
// don't ever be tempted to [...$$()] here
// it should return a NodeList
return $$(selector, context);
}
element(selector, context) {
if (selector.substr(0, 1) === '#') {
return this.elementById(selector.substr(1));
}
// TODO: This can just use querySelector
return [...$$(selector, context)][0];
}
// ember components aren't strictly 'dom-like'
// but if you think of them as a web component 'shim'
// then it makes more sense to think of them as part of the dom
// with traditional/standard web components you wouldn't actually need this
// method as you could just get to their methods from the dom element
component(selector, context) {
if (typeof selector !== 'string') {
return $_(selector);
}
return $_(this.element(selector, context));
}
components(selector, context) {
return [...this.elements(selector, context)]
.map(function (item) {
return $_(item);
})
.filter(function (item) {
return item != null;
});
}
isInViewport($el, cb, threshold = 0) {
inViewportCallbacks.set($el, cb);
let observer = new IntersectionObserver(
(entries, observer) => {
entries.map((item) => {
const cb = inViewportCallbacks.get(item.target);
if (typeof cb === 'function') {
cb(item.isIntersecting);
}
});
},
{
rootMargin: '0px',
threshold: threshold,
}
);
observer.observe($el); // eslint-disable-line ember/no-observers
// observer.unobserve($el);
return () => {
observer.unobserve($el); // eslint-disable-line ember/no-observers
if (inViewportCallbacks) {
inViewportCallbacks.delete($el);
}
observer.disconnect(); // eslint-disable-line ember/no-observers
observer = null;
};
}
}