Skip to content

Commit

Permalink
Merge pull request #191 from github/initialize-attrs-on-attributechan…
Browse files Browse the repository at this point in the history
…gedcallback

Initialize attrs on attributechangedcallback
  • Loading branch information
koddsson authored Mar 28, 2022
2 parents 18387a4 + 4ba1d72 commit d16d885
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/attr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export function attr<K extends string>(proto: Record<K, attrValue>, key: K): voi
* This is automatically called as part of `@controller`. If a class uses the
* `@controller` decorator it should not call this manually.
*/
const initialized = new WeakSet<Element>()
export function initializeAttrs(instance: HTMLElement, names?: Iterable<string>): void {
if (initialized.has(instance)) return
initialized.add(instance)
if (!names) names = getAttrNames(Object.getPrototypeOf(instance))
for (const key of names) {
const value = (<Record<PropertyKey, unknown>>(<unknown>instance))[key]
Expand Down
11 changes: 10 additions & 1 deletion src/controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {initializeInstance, initializeClass} from './core.js'
import {initializeInstance, initializeClass, initializeAttributeChanged} from './core.js'
import type {CustomElement} from './custom-element.js'
/**
* Controller is a decorator to be used over a class that extends HTMLElement.
Expand All @@ -11,5 +11,14 @@ export function controller(classObject: CustomElement): void {
classObject.prototype.connectedCallback = function (this: HTMLElement) {
initializeInstance(this, connect)
}
const attributeChanged = classObject.prototype.attributeChangedCallback
classObject.prototype.attributeChangedCallback = function (
this: HTMLElement,
name: string,
oldValue: unknown,
newValue: unknown
) {
initializeAttributeChanged(this, name, oldValue, newValue, attributeChanged)
}
initializeClass(classObject)
}
13 changes: 13 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ export function initializeInstance(instance: HTMLElement, connect?: (this: HTMLE
if (instance.shadowRoot) bindShadow(instance.shadowRoot)
}

export function initializeAttributeChanged(
instance: HTMLElement,
name: string,
oldValue: unknown,
newValue: unknown,
attributeChangedCallback?: (this: HTMLElement, name: string, oldValue: unknown, newValue: unknown) => void
): void {
initializeAttrs(instance)
if (name !== 'data-catalyst' && attributeChangedCallback) {
attributeChangedCallback.call(instance, name, oldValue, newValue)
}
}

export function initializeClass(classObject: CustomElement): void {
defineObservedAttributes(classObject)
register(classObject)
Expand Down
32 changes: 32 additions & 0 deletions test/controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {controller} from '../lib/controller.js'
import {attr} from '../lib/attr.js'

describe('controller', () => {
let root
Expand Down Expand Up @@ -102,4 +103,35 @@ describe('controller', () => {
// eslint-disable-next-line github/unescaped-html-literal
root.innerHTML = '<parent-element><child-element></child-element></parent-element>'
})

describe('attrs', () => {
let attrValues = []
class AttributeTestElement extends HTMLElement {
foo = 'baz'
attributeChangedCallback() {
attrValues.push(this.getAttribute('data-foo'))
attrValues.push(this.foo)
}
}
controller(AttributeTestElement)
attr(AttributeTestElement.prototype, 'foo')

beforeEach(() => {
attrValues = []
})

it('initializes attrs as attributes in attributeChangedCallback', () => {
const el = document.createElement('attribute-test')
el.foo = 'bar'
el.attributeChangedCallback()
expect(attrValues).to.eql(['bar', 'bar'])
})

it('initializes attributes as attrs in attributeChangedCallback', () => {
const el = document.createElement('attribute-test')
el.setAttribute('data-foo', 'bar')
el.attributeChangedCallback()
expect(attrValues).to.eql(['bar', 'bar'])
})
})
})

0 comments on commit d16d885

Please sign in to comment.