diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 642896c..927f363 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -8,9 +8,7 @@ jobs:
strategy:
matrix:
- # TODO get tests working in Windows too
- # windows-latest
- operating-system: [ubuntu-latest, macos-latest]
+ operating-system: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v1
@@ -18,9 +16,23 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: latest
- - name: npm install, build, and test
+ - name: install
run: |
npm i
+ - name: check formatting
+ run: |
+ npm run prettier:check
+ - name: build
+ run: |
+ npm run clean
+ npm run build
+ - name: test
+ run: |
npm test
+ - name: check repo is clean
+ # skip this check in windows for now, as the build outputs may get slightly modified in Windows, which we want to fix.
+ if: runner.os != 'Windows'
+ run: |
+ git add . && git diff --quiet && git diff --cached --quiet
env:
CI: true
diff --git a/README.md b/README.md
index eb13978..e061721 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[](https://travis-ci.org/matthewp/custom-attributes)
+[](https://github.com/lume/custom-attributes/actions/workflows/tests.yml)
[](http://badge.fury.io/js/custom-attributes)
# custom-attributes
diff --git a/dist/CustomAttributeRegistry.d.ts b/dist/CustomAttributeRegistry.d.ts
index 884e61b..a41f936 100644
--- a/dist/CustomAttributeRegistry.d.ts
+++ b/dist/CustomAttributeRegistry.d.ts
@@ -1,20 +1,10 @@
-import type { Constructor } from 'lowclass';
+import type { Constructor } from 'lowclass/dist/Constructor.js';
export declare class CustomAttributeRegistry {
+ #private;
ownerDocument: Document | ShadowRoot;
- private _attrMap;
- private _elementMap;
- private _observer;
constructor(ownerDocument: Document | ShadowRoot);
define(attrName: string, Class: Constructor): void;
get(element: Element, attrName: string): CustomAttribute | undefined;
- private _getConstructor;
- private _observe;
- private _unobserve;
- private _reobserve;
- private _upgradeAttr;
- private _elementConnected;
- private _elementDisconnected;
- private _handleChange;
}
export interface CustomAttribute {
ownerElement: Element;
diff --git a/dist/CustomAttributeRegistry.d.ts.map b/dist/CustomAttributeRegistry.d.ts.map
index 7139a91..f52c6d1 100644
--- a/dist/CustomAttributeRegistry.d.ts.map
+++ b/dist/CustomAttributeRegistry.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"CustomAttributeRegistry.d.ts","sourceRoot":"","sources":["../src/CustomAttributeRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,UAAU,CAAA;AAIzC,qBAAa,uBAAuB;IAmBhB,aAAa,EAAE,QAAQ,GAAG,UAAU;IAlBvD,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,WAAW,CAAuD;IAE1E,OAAO,CAAC,SAAS,CAaf;gBAEiB,aAAa,EAAE,QAAQ,GAAG,UAAU;IAIvD,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW;IAM3C,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM;IAMtC,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,iBAAiB,CAWxB;IAED,OAAO,CAAC,oBAAoB,CAO3B;IAED,OAAO,CAAC,aAAa;CAiCrB;AAGD,MAAM,WAAW,eAAe;IAC/B,YAAY,EAAE,OAAO,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,CAAC,IAAI,IAAI,CAAA;IAC1B,oBAAoB,CAAC,IAAI,IAAI,CAAA;IAC7B,eAAe,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1D"}
\ No newline at end of file
+{"version":3,"file":"CustomAttributeRegistry.d.ts","sourceRoot":"","sources":["../src/CustomAttributeRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAA;AAI7D,qBAAa,uBAAuB;;IAmBhB,aAAa,EAAE,QAAQ,GAAG,UAAU;gBAApC,aAAa,EAAE,QAAQ,GAAG,UAAU;IAIvD,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW;IAM3C,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM;CA8FtC;AAGD,MAAM,WAAW,eAAe;IAC/B,YAAY,EAAE,OAAO,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,CAAC,IAAI,IAAI,CAAA;IAC1B,oBAAoB,CAAC,IAAI,IAAI,CAAA;IAC7B,eAAe,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1D"}
\ No newline at end of file
diff --git a/dist/CustomAttributeRegistry.js b/dist/CustomAttributeRegistry.js
index 04194f2..af4033d 100644
--- a/dist/CustomAttributeRegistry.js
+++ b/dist/CustomAttributeRegistry.js
@@ -1,85 +1,93 @@
-var _a;
const forEach = Array.prototype.forEach;
export class CustomAttributeRegistry {
+ ownerDocument;
+ #attrMap = new Map();
+ #elementMap = new WeakMap();
+ #observer = new MutationObserver(mutations => {
+ forEach.call(mutations, (m) => {
+ if (m.type === 'attributes') {
+ const attr = this.#getConstructor(m.attributeName);
+ if (attr)
+ this.#handleChange(m.attributeName, m.target, m.oldValue);
+ }
+ // chlidList
+ else {
+ forEach.call(m.removedNodes, this.#elementDisconnected);
+ forEach.call(m.addedNodes, this.#elementConnected);
+ }
+ });
+ });
constructor(ownerDocument) {
this.ownerDocument = ownerDocument;
- this._attrMap = new Map();
- this._elementMap = new WeakMap();
- this._observer = new MutationObserver(mutations => {
- forEach.call(mutations, (m) => {
- if (m.type === 'attributes') {
- const attr = this._getConstructor(m.attributeName);
- if (attr)
- this._handleChange(m.attributeName, m.target, m.oldValue);
- }
- else {
- forEach.call(m.removedNodes, this._elementDisconnected);
- forEach.call(m.addedNodes, this._elementConnected);
- }
- });
- });
- this._elementConnected = (element) => {
- if (element.nodeType !== 1)
- return;
- forEach.call(element.attributes, (attr) => {
- if (this._getConstructor(attr.name))
- this._handleChange(attr.name, element, null);
- });
- this._attrMap.forEach((_constructor, attr) => this._upgradeAttr(attr, element));
- };
- this._elementDisconnected = (element) => {
- const map = this._elementMap.get(element);
- if (!map)
- return;
- map.forEach(inst => { var _a; return (_a = inst.disconnectedCallback) === null || _a === void 0 ? void 0 : _a.call(inst); }, this);
- this._elementMap.delete(element);
- };
if (!ownerDocument)
throw new Error('Must be given a document');
}
define(attrName, Class) {
- this._attrMap.set(attrName, Class);
- this._upgradeAttr(attrName);
- this._reobserve();
+ this.#attrMap.set(attrName, Class);
+ this.#upgradeAttr(attrName);
+ this.#reobserve();
}
get(element, attrName) {
- const map = this._elementMap.get(element);
+ const map = this.#elementMap.get(element);
if (!map)
return;
return map.get(attrName);
}
- _getConstructor(attrName) {
- return this._attrMap.get(attrName);
+ #getConstructor(attrName) {
+ return this.#attrMap.get(attrName);
}
- _observe() {
- this._observer.observe(this.ownerDocument, {
+ #observe() {
+ this.#observer.observe(this.ownerDocument, {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true,
- attributeFilter: Array.from(this._attrMap.keys()),
+ attributeFilter: Array.from(this.#attrMap.keys()),
+ // attributeFilter: [...this.#attrMap.keys()], // Broken in Oculus
+ // attributeFilter: this.#attrMap.keys(), // This works in Chrome, but TS complains, and not clear if it should work in all browsers yet: https://github.com/whatwg/dom/issues/1092
});
}
- _unobserve() {
- this._observer.disconnect();
+ #unobserve() {
+ this.#observer.disconnect();
}
- _reobserve() {
- this._unobserve();
- this._observe();
+ #reobserve() {
+ this.#unobserve();
+ this.#observe();
}
- _upgradeAttr(attrName, node = this.ownerDocument) {
+ #upgradeAttr(attrName, node = this.ownerDocument) {
const matches = node.querySelectorAll('[' + attrName + ']');
- forEach.call(matches, (element) => this._handleChange(attrName, element, null));
+ // Possibly create custom attributes that may be in the given 'node' tree.
+ // Use a forEach as Edge doesn't support for...of on a NodeList
+ forEach.call(matches, (element) => this.#handleChange(attrName, element, null));
}
- _handleChange(attrName, el, oldVal) {
- var _a, _b, _c;
- let map = this._elementMap.get(el);
+ #elementConnected = (element) => {
+ if (element.nodeType !== 1)
+ return;
+ // For each of the connected element's attribute, possibly instantiate the custom attributes.
+ // Use a forEach as Safari 10 doesn't support for...of on NamedNodeMap (attributes)
+ forEach.call(element.attributes, (attr) => {
+ if (this.#getConstructor(attr.name))
+ this.#handleChange(attr.name, element, null);
+ });
+ // Possibly instantiate custom attributes that may be in the subtree of the connected element.
+ this.#attrMap.forEach((_constructor, attr) => this.#upgradeAttr(attr, element));
+ };
+ #elementDisconnected = (element) => {
+ const map = this.#elementMap.get(element);
+ if (!map)
+ return;
+ map.forEach(inst => inst.disconnectedCallback?.(), this);
+ this.#elementMap.delete(element);
+ };
+ #handleChange(attrName, el, oldVal) {
+ let map = this.#elementMap.get(el);
if (!map)
- this._elementMap.set(el, (map = new Map()));
+ this.#elementMap.set(el, (map = new Map()));
let inst = map.get(attrName);
const newVal = el.getAttribute(attrName);
+ // Attribute is being created
if (!inst) {
- const Constructor = this._getConstructor(attrName);
+ const Constructor = this.#getConstructor(attrName);
inst = new Constructor();
map.set(attrName, inst);
inst.ownerElement = el;
@@ -87,25 +95,28 @@ export class CustomAttributeRegistry {
if (newVal == null)
throw new Error('Not possible!');
inst.value = newVal;
- (_a = inst.connectedCallback) === null || _a === void 0 ? void 0 : _a.call(inst);
+ inst.connectedCallback?.();
return;
}
+ // Attribute was removed
if (newVal == null) {
- (_b = inst.disconnectedCallback) === null || _b === void 0 ? void 0 : _b.call(inst);
+ inst.disconnectedCallback?.();
map.delete(attrName);
}
+ // Attribute changed
else if (newVal !== inst.value) {
inst.value = newVal;
if (oldVal == null)
throw new Error('Not possible!');
- (_c = inst.changedCallback) === null || _c === void 0 ? void 0 : _c.call(inst, oldVal, newVal);
+ inst.changedCallback?.(oldVal, newVal);
}
}
}
-if ((_a = globalThis.window) === null || _a === void 0 ? void 0 : _a.document) {
- const _attachShadow = Element.prototype.attachShadow;
+// Avoid errors trying to use DOM APIs in non-DOM environments (f.e. server-side rendering).
+if (globalThis.window?.document) {
+ const original = Element.prototype.attachShadow;
Element.prototype.attachShadow = function attachShadow(options) {
- const root = _attachShadow.call(this, options);
+ const root = original.call(this, options);
if (!root.customAttributes)
root.customAttributes = new CustomAttributeRegistry(root);
return root;
diff --git a/dist/CustomAttributeRegistry.js.map b/dist/CustomAttributeRegistry.js.map
index 3beace2..e450257 100644
--- a/dist/CustomAttributeRegistry.js.map
+++ b/dist/CustomAttributeRegistry.js.map
@@ -1 +1 @@
-{"version":3,"file":"CustomAttributeRegistry.js","sourceRoot":"","sources":["../src/CustomAttributeRegistry.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"CustomAttributeRegistry.js","sourceRoot":"","sources":["../src/CustomAttributeRegistry.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist/index.d.ts b/dist/index.d.ts
index 43449f0..afd94ab 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -10,5 +10,5 @@ declare global {
customAttributes: CustomAttributeRegistry;
}
}
-export declare const version = "0.2.0";
+export declare const version = "0.2.4";
//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/dist/index.js b/dist/index.js
index a783354..d2b1e36 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1,8 +1,11 @@
-var _a;
+// TODO We don't know when a ShadowRoot is no longer referenced, hence we cannot
+// unobserve them. Verify that MOs are cleaned up once ShadowRoots are no longer
+// referenced.
import { CustomAttributeRegistry } from './CustomAttributeRegistry.js';
export * from './CustomAttributeRegistry.js';
export let customAttributes;
-if ((_a = globalThis.window) === null || _a === void 0 ? void 0 : _a.document)
+// Avoid errors trying to use DOM APIs in non-DOM environments (f.e. server-side rendering).
+if (globalThis.window?.document)
customAttributes = globalThis.customAttributes = new CustomAttributeRegistry(document);
-export const version = '0.2.0';
+export const version = '0.2.4';
//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/dist/index.js.map b/dist/index.js.map
index 1ae9217..2a47348 100644
--- a/dist/index.js.map
+++ b/dist/index.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAA;AAEpE,cAAc,8BAA8B,CAAA;AAE5C,MAAM,CAAC,IAAI,gBAAyC,CAAA;AAGpD,IAAI,MAAA,UAAU,CAAC,MAAM,0CAAE,QAAQ;IAAE,gBAAgB,GAAG,UAAU,CAAC,gBAAgB,GAAG,IAAI,uBAAuB,CAAC,QAAQ,CAAC,CAAA;AAmBvH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA"}
\ No newline at end of file
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,gFAAgF;AAChF,cAAc;AAEd,OAAO,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAA;AAEpE,cAAc,8BAA8B,CAAA;AAE5C,MAAM,CAAC,IAAI,gBAAyC,CAAA;AAEpD,4FAA4F;AAC5F,IAAI,UAAU,CAAC,MAAM,EAAE,QAAQ;IAAE,gBAAgB,GAAG,UAAU,CAAC,gBAAgB,GAAG,IAAI,uBAAuB,CAAC,QAAQ,CAAC,CAAA;AAmBvH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA"}
\ No newline at end of file
diff --git a/dist/test/change.test.d.ts b/dist/test/change.test.d.ts
new file mode 100644
index 0000000..67a7576
--- /dev/null
+++ b/dist/test/change.test.d.ts
@@ -0,0 +1,2 @@
+import '../index.js';
+//# sourceMappingURL=change.test.d.ts.map
\ No newline at end of file
diff --git a/dist/test/change.test.d.ts.map b/dist/test/change.test.d.ts.map
new file mode 100644
index 0000000..361162b
--- /dev/null
+++ b/dist/test/change.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"change.test.d.ts","sourceRoot":"","sources":["../../src/test/change.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA"}
\ No newline at end of file
diff --git a/dist/test/change.test.js b/dist/test/change.test.js
new file mode 100644
index 0000000..ba7b8b5
--- /dev/null
+++ b/dist/test/change.test.js
@@ -0,0 +1,37 @@
+import '../index.js';
+describe('changedCallback()', function () {
+ async function later() {
+ await new Promise(r => setTimeout(r, 4));
+ }
+ it('Is called when an attribute changes value', async function () {
+ const { resolve, promise: changedPromise } = Promise.withResolvers();
+ class SomeAttr {
+ changedCallback() {
+ resolve();
+ }
+ }
+ customAttributes.define('some-attr', SomeAttr);
+ const el = document.createElement('article');
+ el.setAttribute('some-attr', 'foo');
+ document.body.append(el);
+ queueMicrotask(() => el.setAttribute('some-attr', 'bar'));
+ await changedPromise;
+ el.remove();
+ });
+ it("Is not called when an attribute's value remains the same", async function () {
+ let count = 0;
+ class AnotherAttr {
+ changedCallback() {
+ count++;
+ }
+ }
+ customAttributes.define('another-attr', AnotherAttr);
+ const el = document.createElement('span');
+ el.setAttribute('another-attr', 'foo');
+ document.body.append(el);
+ el.setAttribute('another-attr', 'foo');
+ await later();
+ expect(count).toBe(0);
+ });
+});
+//# sourceMappingURL=change.test.js.map
\ No newline at end of file
diff --git a/dist/test/change.test.js.map b/dist/test/change.test.js.map
new file mode 100644
index 0000000..4cc3d48
--- /dev/null
+++ b/dist/test/change.test.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"change.test.js","sourceRoot":"","sources":["../../src/test/change.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAEpB,QAAQ,CAAC,mBAAmB,EAAE;IAC7B,KAAK,UAAU,KAAK;QACnB,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACzC,CAAC;IAED,EAAE,CAAC,2CAA2C,EAAE,KAAK;QACpD,MAAM,EAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAC,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAA;QAExE,MAAM,QAAQ;YACb,eAAe;gBACd,OAAO,EAAE,CAAA;YACV,CAAC;SACD;QAED,gBAAgB,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QAE9C,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAA;QAC5C,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,CAAC,CAAA;QACnC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAExB,cAAc,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAA;QAEzD,MAAM,cAAc,CAAA;QAEpB,EAAE,CAAC,MAAM,EAAE,CAAA;IACZ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0DAA0D,EAAE,KAAK;QACnE,IAAI,KAAK,GAAG,CAAC,CAAA;QAEb,MAAM,WAAW;YAChB,eAAe;gBACd,KAAK,EAAE,CAAA;YACR,CAAC;SACD;QAED,gBAAgB,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAEpD,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QACzC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;QACtC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAExB,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;QAEtC,MAAM,KAAK,EAAE,CAAA;QAEb,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
\ No newline at end of file
diff --git a/dist/test/connect.test.d.ts b/dist/test/connect.test.d.ts
new file mode 100644
index 0000000..dc81e37
--- /dev/null
+++ b/dist/test/connect.test.d.ts
@@ -0,0 +1,5 @@
+import '../index.js';
+declare global {
+ function expect(...args: any[]): any;
+}
+//# sourceMappingURL=connect.test.d.ts.map
\ No newline at end of file
diff --git a/dist/test/connect.test.d.ts.map b/dist/test/connect.test.d.ts.map
new file mode 100644
index 0000000..3be0049
--- /dev/null
+++ b/dist/test/connect.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"connect.test.d.ts","sourceRoot":"","sources":["../../src/test/connect.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAEpB,OAAO,CAAC,MAAM,CAAC;IACd,SAAS,MAAM,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAAA;CACpC"}
\ No newline at end of file
diff --git a/dist/test/connect.test.js b/dist/test/connect.test.js
new file mode 100644
index 0000000..b708d64
--- /dev/null
+++ b/dist/test/connect.test.js
@@ -0,0 +1,47 @@
+import '../index.js';
+class BgColorAttr {
+ connectedCallback() {
+ this.setColor();
+ }
+ changedCallback(_oldValue, _newValue) {
+ this.setColor();
+ }
+ setColor() {
+ const color = this.value || '';
+ this.ownerElement.style.backgroundColor = color;
+ }
+}
+document.body.insertAdjacentHTML('beforeend',
+/*html*/ `
+
+ This article is static
+
+`);
+customAttributes.define('bg-color', BgColorAttr);
+describe('connectedCallback()', function () {
+ it('Is called when element is already in the DOM', function () {
+ const article = document.querySelector('article');
+ expect(article.style.backgroundColor).toBe('red');
+ article.remove();
+ });
+ it('Is called when an attribute is dynamically created', async function () {
+ const article = document.createElement('article');
+ article.textContent = 'hello world';
+ document.body.append(article);
+ article.setAttribute('bg-color', 'blue');
+ await new Promise(r => setTimeout(r, 4));
+ expect(article.style.backgroundColor).toBe('blue');
+ article.remove();
+ });
+ it('Is called on nested elements when root element is added to the DOM', async function () {
+ const article = document.createElement('article');
+ const header = document.createElement('header');
+ header.setAttribute('bg-color', 'blue');
+ article.append(header);
+ document.body.append(article);
+ await new Promise(r => setTimeout(r, 4));
+ expect(header.style.backgroundColor).toBe('blue');
+ article.remove();
+ });
+});
+//# sourceMappingURL=connect.test.js.map
\ No newline at end of file
diff --git a/dist/test/connect.test.js.map b/dist/test/connect.test.js.map
new file mode 100644
index 0000000..df26525
--- /dev/null
+++ b/dist/test/connect.test.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"connect.test.js","sourceRoot":"","sources":["../../src/test/connect.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAMpB,MAAM,WAAW;IAIhB,iBAAiB;QAChB,IAAI,CAAC,QAAQ,EAAE,CAAA;IAChB,CAAC;IAED,eAAe,CAAC,SAAc,EAAE,SAAc;QAC7C,IAAI,CAAC,QAAQ,EAAE,CAAA;IAChB,CAAC;IAED,QAAQ;QACP,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAA;QAC9B,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,eAAe,GAAG,KAAK,CAAA;IAChD,CAAC;CACD;AAED,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAC/B,WAAW;AACX,QAAQ,CAAC;;;;CAIT,CACA,CAAA;AAED,gBAAgB,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;AAEhD,QAAQ,CAAC,qBAAqB,EAAE;IAC/B,EAAE,CAAC,8CAA8C,EAAE;QAClD,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAE,CAAA;QAClD,MAAM,CAAC,OAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAClD,OAAO,CAAC,MAAM,EAAE,CAAA;IACjB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oDAAoD,EAAE,KAAK;QAC7D,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAA;QACjD,OAAO,CAAC,WAAW,GAAG,aAAa,CAAA;QACnC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAE7B,OAAO,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QAExC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAExC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAClD,OAAO,CAAC,MAAM,EAAE,CAAA;IACjB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oEAAoE,EAAE,KAAK;QAC7E,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAA;QACjD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAE/C,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QACvC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAEtB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAE7B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAExC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACjD,OAAO,CAAC,MAAM,EAAE,CAAA;IACjB,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
\ No newline at end of file
diff --git a/dist/test/disconnect.test.d.ts b/dist/test/disconnect.test.d.ts
new file mode 100644
index 0000000..1cd5e91
--- /dev/null
+++ b/dist/test/disconnect.test.d.ts
@@ -0,0 +1,2 @@
+import '../index.js';
+//# sourceMappingURL=disconnect.test.d.ts.map
\ No newline at end of file
diff --git a/dist/test/disconnect.test.d.ts.map b/dist/test/disconnect.test.d.ts.map
new file mode 100644
index 0000000..5b603d8
--- /dev/null
+++ b/dist/test/disconnect.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"disconnect.test.d.ts","sourceRoot":"","sources":["../../src/test/disconnect.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA"}
\ No newline at end of file
diff --git a/dist/test/disconnect.test.js b/dist/test/disconnect.test.js
new file mode 100644
index 0000000..f38108b
--- /dev/null
+++ b/dist/test/disconnect.test.js
@@ -0,0 +1,45 @@
+import '../index.js';
+describe('disconnectedCallback()', function () {
+ async function later() {
+ await new Promise(r => setTimeout(r, 4));
+ }
+ it('Is called when removeAttribute() is used', async function () {
+ const { resolve, promise: disconnectedPromise } = Promise.withResolvers();
+ let count = 0;
+ class MyAttr {
+ async connectedCallback() {
+ await later().then(() => el.removeAttribute('my-attr'));
+ }
+ changedCallback() {
+ count++;
+ }
+ disconnectedCallback() {
+ resolve();
+ }
+ }
+ customAttributes.define('my-attr', MyAttr);
+ const el = document.createElement('span');
+ document.body.append(el);
+ queueMicrotask(() => el.setAttribute('my-attr', 'test'));
+ await disconnectedPromise;
+ expect(count).toBe(0);
+ });
+ it('Is called when the element is removed', async function () {
+ const { resolve, promise: disconnectedPromise } = Promise.withResolvers();
+ class SomeAttr {
+ async connectedCallback() {
+ await later();
+ el.remove();
+ }
+ disconnectedCallback() {
+ resolve();
+ }
+ }
+ customAttributes.define('some-attr', SomeAttr);
+ const el = document.createElement('span');
+ document.body.append(el);
+ queueMicrotask(() => el.setAttribute('some-attr', 'test'));
+ await disconnectedPromise;
+ });
+});
+//# sourceMappingURL=disconnect.test.js.map
\ No newline at end of file
diff --git a/dist/test/disconnect.test.js.map b/dist/test/disconnect.test.js.map
new file mode 100644
index 0000000..987ea8e
--- /dev/null
+++ b/dist/test/disconnect.test.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"disconnect.test.js","sourceRoot":"","sources":["../../src/test/disconnect.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAEpB,QAAQ,CAAC,wBAAwB,EAAE;IAClC,KAAK,UAAU,KAAK;QACnB,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACzC,CAAC;IAED,EAAE,CAAC,0CAA0C,EAAE,KAAK;QACnD,MAAM,EAAC,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAC,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAA;QAE7E,IAAI,KAAK,GAAG,CAAC,CAAA;QAEb,MAAM,MAAM;YACX,KAAK,CAAC,iBAAiB;gBACtB,MAAM,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAA;YACxD,CAAC;YAED,eAAe;gBACd,KAAK,EAAE,CAAA;YACR,CAAC;YAED,oBAAoB;gBACnB,OAAO,EAAE,CAAA;YACV,CAAC;SACD;QAED,gBAAgB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAE1C,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QACzC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAExB,cAAc,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAA;QAExD,MAAM,mBAAmB,CAAA;QAEzB,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uCAAuC,EAAE,KAAK;QAChD,MAAM,EAAC,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAC,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAA;QAE7E,MAAM,QAAQ;YACb,KAAK,CAAC,iBAAiB;gBACtB,MAAM,KAAK,EAAE,CAAA;gBAEb,EAAE,CAAC,MAAM,EAAE,CAAA;YACZ,CAAC;YAED,oBAAoB;gBACnB,OAAO,EAAE,CAAA;YACV,CAAC;SACD;QAED,gBAAgB,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QAE9C,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QACzC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAExB,cAAc,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;QAE1D,MAAM,mBAAmB,CAAA;IAC1B,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
\ No newline at end of file
diff --git a/dist/test/get.test.d.ts b/dist/test/get.test.d.ts
new file mode 100644
index 0000000..1c06818
--- /dev/null
+++ b/dist/test/get.test.d.ts
@@ -0,0 +1,2 @@
+import '../index.js';
+//# sourceMappingURL=get.test.d.ts.map
\ No newline at end of file
diff --git a/dist/test/get.test.d.ts.map b/dist/test/get.test.d.ts.map
new file mode 100644
index 0000000..132c6d0
--- /dev/null
+++ b/dist/test/get.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"get.test.d.ts","sourceRoot":"","sources":["../../src/test/get.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA"}
\ No newline at end of file
diff --git a/dist/test/get.test.js b/dist/test/get.test.js
new file mode 100644
index 0000000..925937f
--- /dev/null
+++ b/dist/test/get.test.js
@@ -0,0 +1,37 @@
+import '../index.js';
+describe('get()', function () {
+ async function later() {
+ await new Promise(r => setTimeout(r, 4));
+ }
+ it('gets the attribute instance', async function () {
+ class Foobar {
+ }
+ customAttributes.define('foo-bar', Foobar);
+ const el = document.createElement('span');
+ el.setAttribute('foo-bar', 'baz');
+ document.body.append(el);
+ await later();
+ const fooBar = customAttributes.get(el, 'foo-bar');
+ expect(fooBar).toBeInstanceOf(Foobar);
+ });
+ it('allows passing more complex data types', async function () {
+ const { resolve, promise: bazSetPromise } = Promise.withResolvers();
+ class Foobar {
+ set baz(val) {
+ expect(val).toBe('hello world');
+ resolve();
+ }
+ }
+ customAttributes.define('foo-bar', Foobar);
+ const el = document.createElement('span');
+ el.setAttribute('foo-bar', 'bam');
+ document.body.append(el);
+ await later();
+ const fooBar = customAttributes.get(el, 'foo-bar');
+ queueMicrotask(() =>
+ // @ts-expect-error TODO make a CustomAttributes map for registering global attribute types.
+ (fooBar.baz = 'hello world'));
+ await bazSetPromise;
+ });
+});
+//# sourceMappingURL=get.test.js.map
\ No newline at end of file
diff --git a/dist/test/get.test.js.map b/dist/test/get.test.js.map
new file mode 100644
index 0000000..7bf7fd3
--- /dev/null
+++ b/dist/test/get.test.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"get.test.js","sourceRoot":"","sources":["../../src/test/get.test.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAEpB,QAAQ,CAAC,OAAO,EAAE;IACjB,KAAK,UAAU,KAAK;QACnB,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACzC,CAAC;IAED,EAAE,CAAC,6BAA6B,EAAE,KAAK;QACtC,MAAM,MAAM;SAAG;QACf,gBAAgB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAE1C,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QACzC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACjC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAExB,MAAM,KAAK,EAAE,CAAA;QAEb,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;QAClD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,KAAK;QACjD,MAAM,EAAC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAC,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAA;QAEvE,MAAM,MAAM;YACX,IAAI,GAAG,CAAC,GAAQ;gBACf,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;gBAC/B,OAAO,EAAE,CAAA;YACV,CAAC;SACD;QAED,gBAAgB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAE1C,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QACzC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACjC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAExB,MAAM,KAAK,EAAE,CAAA;QAEb,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;QAElD,cAAc,CACb,GAAG,EAAE;QACJ,4FAA4F;QAC5F,CAAC,MAAO,CAAC,GAAG,GAAG,aAAa,CAAC,CAC9B,CAAA;QAED,MAAM,aAAa,CAAA;IACpB,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
\ No newline at end of file
diff --git a/package.json b/package.json
index 42b4bd3..6276cde 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@lume/custom-attributes",
- "version": "0.2.0",
+ "version": "0.2.4",
"description": "Custom attributes: like custom elements, but for attributes",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -12,8 +12,8 @@
"dev": "lume dev",
"typecheck": "lume typecheck",
"typecheck:watch": "lume typecheckWatch",
- "test": "npm run prettier:check && npm run build && echo TODO use 'lume test'",
- "test:debug": "lume testDebug",
+ "test": "lume test",
+ "test:watch": "lume test --watch",
"prettier": "lume prettier",
"prettier:check": "lume prettierCheck",
"release:patch": "lume releasePatch",
@@ -37,12 +37,12 @@
},
"homepage": "https://github.com/matthewp/custom-attributes#readme",
"devDependencies": {
- "@lume/cli": "^0.9.0",
+ "@lume/cli": "^0.13.0",
"prettier": "3.0.3",
"rollup": "^0.41.4",
- "typescript": "4.4.3"
+ "typescript": "^5.0.0"
},
"dependencies": {
- "lowclass": "^5.0.0"
+ "lowclass": "^8.0.0"
}
}
diff --git a/src/CustomAttributeRegistry.ts b/src/CustomAttributeRegistry.ts
index 4408571..05644c1 100644
--- a/src/CustomAttributeRegistry.ts
+++ b/src/CustomAttributeRegistry.ts
@@ -1,22 +1,22 @@
-import type {Constructor} from 'lowclass'
+import type {Constructor} from 'lowclass/dist/Constructor.js'
const forEach = Array.prototype.forEach
export class CustomAttributeRegistry {
- private _attrMap = new Map()
- private _elementMap = new WeakMap>()
+ #attrMap = new Map()
+ #elementMap = new WeakMap>()
- private _observer: MutationObserver = new MutationObserver(mutations => {
+ #observer: MutationObserver = new MutationObserver(mutations => {
forEach.call(mutations, (m: MutationRecord) => {
if (m.type === 'attributes') {
- const attr = this._getConstructor(m.attributeName!)
- if (attr) this._handleChange(m.attributeName!, m.target as Element, m.oldValue)
+ const attr = this.#getConstructor(m.attributeName!)
+ if (attr) this.#handleChange(m.attributeName!, m.target as Element, m.oldValue)
}
// chlidList
else {
- forEach.call(m.removedNodes, this._elementDisconnected)
- forEach.call(m.addedNodes, this._elementConnected)
+ forEach.call(m.removedNodes, this.#elementDisconnected)
+ forEach.call(m.addedNodes, this.#elementConnected)
}
})
})
@@ -26,82 +26,82 @@ export class CustomAttributeRegistry {
}
define(attrName: string, Class: Constructor) {
- this._attrMap.set(attrName, Class)
- this._upgradeAttr(attrName)
- this._reobserve()
+ this.#attrMap.set(attrName, Class)
+ this.#upgradeAttr(attrName)
+ this.#reobserve()
}
get(element: Element, attrName: string) {
- const map = this._elementMap.get(element)
+ const map = this.#elementMap.get(element)
if (!map) return
return map.get(attrName)
}
- private _getConstructor(attrName: string) {
- return this._attrMap.get(attrName)
+ #getConstructor(attrName: string) {
+ return this.#attrMap.get(attrName)
}
- private _observe() {
- this._observer.observe(this.ownerDocument, {
+ #observe() {
+ this.#observer.observe(this.ownerDocument, {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true,
- attributeFilter: Array.from(this._attrMap.keys()),
- // attributeFilter: [...this._attrMap.keys()], // Broken in Oculus
- // attributeFilter: this._attrMap.keys(), // This works in Chrome, but TS complains, and not clear if it should work in all browsers yet: https://github.com/whatwg/dom/issues/1092
+ attributeFilter: Array.from(this.#attrMap.keys()),
+ // attributeFilter: [...this.#attrMap.keys()], // Broken in Oculus
+ // attributeFilter: this.#attrMap.keys(), // This works in Chrome, but TS complains, and not clear if it should work in all browsers yet: https://github.com/whatwg/dom/issues/1092
})
}
- private _unobserve() {
- this._observer.disconnect()
+ #unobserve() {
+ this.#observer.disconnect()
}
- private _reobserve() {
- this._unobserve()
- this._observe()
+ #reobserve() {
+ this.#unobserve()
+ this.#observe()
}
- private _upgradeAttr(attrName: string, node: Element | Document | ShadowRoot = this.ownerDocument) {
+ #upgradeAttr(attrName: string, node: Element | Document | ShadowRoot = this.ownerDocument) {
const matches = node.querySelectorAll('[' + attrName + ']')
// Possibly create custom attributes that may be in the given 'node' tree.
// Use a forEach as Edge doesn't support for...of on a NodeList
- forEach.call(matches, (element: Element) => this._handleChange(attrName, element, null))
+ forEach.call(matches, (element: Element) => this.#handleChange(attrName, element, null))
}
- private _elementConnected = (element: Element) => {
+ #elementConnected = (element: Element) => {
if (element.nodeType !== 1) return
// For each of the connected element's attribute, possibly instantiate the custom attributes.
// Use a forEach as Safari 10 doesn't support for...of on NamedNodeMap (attributes)
forEach.call(element.attributes, (attr: Attr) => {
- if (this._getConstructor(attr.name)) this._handleChange(attr.name, element, null)
+ if (this.#getConstructor(attr.name)) this.#handleChange(attr.name, element, null)
})
// Possibly instantiate custom attributes that may be in the subtree of the connected element.
- this._attrMap.forEach((_constructor, attr) => this._upgradeAttr(attr, element))
+ this.#attrMap.forEach((_constructor, attr) => this.#upgradeAttr(attr, element))
}
- private _elementDisconnected = (element: Element) => {
- const map = this._elementMap.get(element)
+ #elementDisconnected = (element: Element) => {
+ const map = this.#elementMap.get(element)
if (!map) return
map.forEach(inst => inst.disconnectedCallback?.(), this)
- this._elementMap.delete(element)
+ this.#elementMap.delete(element)
}
- private _handleChange(attrName: string, el: Element, oldVal: string | null) {
- let map = this._elementMap.get(el)
- if (!map) this._elementMap.set(el, (map = new Map()))
+ #handleChange(attrName: string, el: Element, oldVal: string | null) {
+ let map = this.#elementMap.get(el)
+ if (!map) this.#elementMap.set(el, (map = new Map()))
let inst = map.get(attrName)
const newVal = el.getAttribute(attrName)
// Attribute is being created
if (!inst) {
- const Constructor = this._getConstructor(attrName)!
+ const Constructor = this.#getConstructor(attrName)!
inst = new Constructor() as CustomAttribute
map.set(attrName, inst)
inst.ownerElement = el
@@ -139,10 +139,10 @@ export interface CustomAttribute {
// Avoid errors trying to use DOM APIs in non-DOM environments (f.e. server-side rendering).
if (globalThis.window?.document) {
- const _attachShadow = Element.prototype.attachShadow
+ const original = Element.prototype.attachShadow
Element.prototype.attachShadow = function attachShadow(options) {
- const root = _attachShadow.call(this, options)
+ const root = original.call(this, options)
if (!root.customAttributes) root.customAttributes = new CustomAttributeRegistry(root)
diff --git a/src/index.ts b/src/index.ts
index 9accf31..5d766f1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -28,4 +28,4 @@ declare global {
}
}
-export const version = '0.2.0'
+export const version = '0.2.4'
diff --git a/src/test/change.test.ts b/src/test/change.test.ts
new file mode 100644
index 0000000..d30b024
--- /dev/null
+++ b/src/test/change.test.ts
@@ -0,0 +1,51 @@
+import '../index.js'
+
+describe('changedCallback()', function () {
+ async function later() {
+ await new Promise(r => setTimeout(r, 4))
+ }
+
+ it('Is called when an attribute changes value', async function () {
+ const {resolve, promise: changedPromise} = Promise.withResolvers()
+
+ class SomeAttr {
+ changedCallback() {
+ resolve()
+ }
+ }
+
+ customAttributes.define('some-attr', SomeAttr)
+
+ const el = document.createElement('article')
+ el.setAttribute('some-attr', 'foo')
+ document.body.append(el)
+
+ queueMicrotask(() => el.setAttribute('some-attr', 'bar'))
+
+ await changedPromise
+
+ el.remove()
+ })
+
+ it("Is not called when an attribute's value remains the same", async function () {
+ let count = 0
+
+ class AnotherAttr {
+ changedCallback() {
+ count++
+ }
+ }
+
+ customAttributes.define('another-attr', AnotherAttr)
+
+ const el = document.createElement('span')
+ el.setAttribute('another-attr', 'foo')
+ document.body.append(el)
+
+ el.setAttribute('another-attr', 'foo')
+
+ await later()
+
+ expect(count).toBe(0)
+ })
+})
diff --git a/src/test/connect.test.ts b/src/test/connect.test.ts
new file mode 100644
index 0000000..23b0f7d
--- /dev/null
+++ b/src/test/connect.test.ts
@@ -0,0 +1,70 @@
+import '../index.js'
+
+declare global {
+ function expect(...args: any[]): any
+}
+
+class BgColorAttr {
+ declare value: any
+ declare ownerElement: any
+
+ connectedCallback() {
+ this.setColor()
+ }
+
+ changedCallback(_oldValue: any, _newValue: any) {
+ this.setColor()
+ }
+
+ setColor() {
+ const color = this.value || ''
+ this.ownerElement.style.backgroundColor = color
+ }
+}
+
+document.body.insertAdjacentHTML(
+ 'beforeend',
+ /*html*/ `
+
+ This article is static
+
+`,
+)
+
+customAttributes.define('bg-color', BgColorAttr)
+
+describe('connectedCallback()', function () {
+ it('Is called when element is already in the DOM', function () {
+ const article = document.querySelector('article')!
+ expect(article!.style.backgroundColor).toBe('red')
+ article.remove()
+ })
+
+ it('Is called when an attribute is dynamically created', async function () {
+ const article = document.createElement('article')
+ article.textContent = 'hello world'
+ document.body.append(article)
+
+ article.setAttribute('bg-color', 'blue')
+
+ await new Promise(r => setTimeout(r, 4))
+
+ expect(article.style.backgroundColor).toBe('blue')
+ article.remove()
+ })
+
+ it('Is called on nested elements when root element is added to the DOM', async function () {
+ const article = document.createElement('article')
+ const header = document.createElement('header')
+
+ header.setAttribute('bg-color', 'blue')
+ article.append(header)
+
+ document.body.append(article)
+
+ await new Promise(r => setTimeout(r, 4))
+
+ expect(header.style.backgroundColor).toBe('blue')
+ article.remove()
+ })
+})
diff --git a/src/test/disconnect.test.ts b/src/test/disconnect.test.ts
new file mode 100644
index 0000000..56977aa
--- /dev/null
+++ b/src/test/disconnect.test.ts
@@ -0,0 +1,63 @@
+import '../index.js'
+
+describe('disconnectedCallback()', function () {
+ async function later() {
+ await new Promise(r => setTimeout(r, 4))
+ }
+
+ it('Is called when removeAttribute() is used', async function () {
+ const {resolve, promise: disconnectedPromise} = Promise.withResolvers()
+
+ let count = 0
+
+ class MyAttr {
+ async connectedCallback() {
+ await later().then(() => el.removeAttribute('my-attr'))
+ }
+
+ changedCallback() {
+ count++
+ }
+
+ disconnectedCallback() {
+ resolve()
+ }
+ }
+
+ customAttributes.define('my-attr', MyAttr)
+
+ const el = document.createElement('span')
+ document.body.append(el)
+
+ queueMicrotask(() => el.setAttribute('my-attr', 'test'))
+
+ await disconnectedPromise
+
+ expect(count).toBe(0)
+ })
+
+ it('Is called when the element is removed', async function () {
+ const {resolve, promise: disconnectedPromise} = Promise.withResolvers()
+
+ class SomeAttr {
+ async connectedCallback() {
+ await later()
+
+ el.remove()
+ }
+
+ disconnectedCallback() {
+ resolve()
+ }
+ }
+
+ customAttributes.define('some-attr', SomeAttr)
+
+ const el = document.createElement('span')
+ document.body.append(el)
+
+ queueMicrotask(() => el.setAttribute('some-attr', 'test'))
+
+ await disconnectedPromise
+ })
+})
diff --git a/src/test/get.test.ts b/src/test/get.test.ts
new file mode 100644
index 0000000..6889291
--- /dev/null
+++ b/src/test/get.test.ts
@@ -0,0 +1,50 @@
+import '../index.js'
+
+describe('get()', function () {
+ async function later() {
+ await new Promise(r => setTimeout(r, 4))
+ }
+
+ it('gets the attribute instance', async function () {
+ class Foobar {}
+ customAttributes.define('foo-bar', Foobar)
+
+ const el = document.createElement('span')
+ el.setAttribute('foo-bar', 'baz')
+ document.body.append(el)
+
+ await later()
+
+ const fooBar = customAttributes.get(el, 'foo-bar')
+ expect(fooBar).toBeInstanceOf(Foobar)
+ })
+
+ it('allows passing more complex data types', async function () {
+ const {resolve, promise: bazSetPromise} = Promise.withResolvers()
+
+ class Foobar {
+ set baz(val: any) {
+ expect(val).toBe('hello world')
+ resolve()
+ }
+ }
+
+ customAttributes.define('foo-bar', Foobar)
+
+ const el = document.createElement('span')
+ el.setAttribute('foo-bar', 'bam')
+ document.body.append(el)
+
+ await later()
+
+ const fooBar = customAttributes.get(el, 'foo-bar')
+
+ queueMicrotask(
+ () =>
+ // @ts-expect-error TODO make a CustomAttributes map for registering global attribute types.
+ (fooBar!.baz = 'hello world'),
+ )
+
+ await bazSetPromise
+ })
+})
diff --git a/test/all.html b/test/all.html
deleted file mode 100644
index 59da15f..0000000
--- a/test/all.html
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- Codestin Search App
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/change.html b/test/change.html
deleted file mode 100644
index 751033d..0000000
--- a/test/change.html
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
- Codestin Search App
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/connect.html b/test/connect.html
deleted file mode 100644
index f35f8cd..0000000
--- a/test/connect.html
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
- Codestin Search App
-
-
-
-
-
-
-
- This article is static
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/disconnect.html b/test/disconnect.html
deleted file mode 100644
index e455cf6..0000000
--- a/test/disconnect.html
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
- Codestin Search App
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/get.html b/test/get.html
deleted file mode 100644
index 5d1e39c..0000000
--- a/test/get.html
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
- Codestin Search App
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tsconfig.json b/tsconfig.json
index 9838b4b..d7b73b0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,5 +1,3 @@
{
- "extends": "./node_modules/@lume/cli/config/ts.config.json",
- "include": ["./src/**/*"],
- "exclude": ["./src/globals.d.ts"]
+ "extends": "./node_modules/@lume/cli/config/ts.config.json"
}