Skip to content

Commit b49f128

Browse files
authored
Merge pull request #156 from alexpdraper/define-inherited-attrs
Check for inherited attrs in initializeAttrs
2 parents a5d05b9 + 359d361 commit b49f128

2 files changed

Lines changed: 44 additions & 6 deletions

File tree

src/attr.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function attr<K extends string>(proto: Record<K, attrValue>, key: K): voi
3535
* `@controller` decorator it should not call this manually.
3636
*/
3737
export function initializeAttrs(instance: HTMLElement, names?: Iterable<string>): void {
38-
if (!names) names = attrs.get(Object.getPrototypeOf(instance)) || []
38+
if (!names) names = getAttrNames(Object.getPrototypeOf(instance))
3939
for (const key of names) {
4040
const value = (<Record<PropertyKey, unknown>>(<unknown>instance))[key]
4141
const name = attrToAttributeName(key)
@@ -73,6 +73,19 @@ export function initializeAttrs(instance: HTMLElement, names?: Iterable<string>)
7373
}
7474
}
7575

76+
function getAttrNames(classObjectProto: Record<PropertyKey, unknown>): Set<string> {
77+
const names: Set<string> = new Set()
78+
let proto: Record<PropertyKey, unknown> | typeof HTMLElement = classObjectProto
79+
80+
while (proto && proto !== HTMLElement) {
81+
const attrNames = attrs.get(<Record<PropertyKey, unknown>>proto) || []
82+
for (const name of attrNames) names.add(name)
83+
proto = Object.getPrototypeOf(proto)
84+
}
85+
86+
return names
87+
}
88+
7689
function attrToAttributeName(name: string): string {
7790
return `data-${name.replace(/([A-Z]($|[a-z]))/g, '-$1')}`.replace(/--/g, '-').toLowerCase()
7891
}
@@ -81,9 +94,8 @@ export function defineObservedAttributes(classObject: CustomElement): void {
8194
let observed = classObject.observedAttributes || []
8295
Object.defineProperty(classObject, 'observedAttributes', {
8396
get() {
84-
const attrMap = attrs.get(classObject.prototype)
85-
if (!attrMap) return observed
86-
return attrMap.map(attrToAttributeName).concat(observed)
97+
const attrMap = getAttrNames(classObject.prototype)
98+
return [...attrMap].map(attrToAttributeName).concat(observed)
8799
},
88100
set(attributes: string[]) {
89101
observed = attributes

test/attr.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,15 @@ describe('initializeAttrs', () => {
155155

156156
describe('attr', () => {
157157
class AttrTestElement extends HTMLElement {}
158+
attr(AttrTestElement.prototype, 'foo')
159+
attr(AttrTestElement.prototype, 'bar')
158160
window.customElements.define('attr-test-element', AttrTestElement)
159161

162+
class ExtendedAttrTestElement extends AttrTestElement {}
163+
attr(ExtendedAttrTestElement.prototype, 'baz')
164+
window.customElements.define('extended-attr-test-element', ExtendedAttrTestElement)
165+
160166
it('populates the "default" list for initializeAttrs', () => {
161-
attr(AttrTestElement.prototype, 'foo')
162-
attr(AttrTestElement.prototype, 'bar')
163167
const instance = document.createElement('attr-test-element')
164168
instance.foo = 'hello'
165169
initializeAttrs(instance)
@@ -169,6 +173,20 @@ describe('attr', () => {
169173
expect(instance.getAttribute('data-foo')).to.equal('hello')
170174
expect(instance.getAttribute('data-bar')).to.equal('')
171175
})
176+
177+
it('includes attrs from extended elements', () => {
178+
const instance = document.createElement('extended-attr-test-element')
179+
instance.bar = 'hello'
180+
instance.baz = 'world'
181+
initializeAttrs(instance)
182+
expect(instance).to.have.property('foo', '')
183+
expect(instance).to.have.property('bar', 'hello')
184+
expect(instance).to.have.property('baz', 'world')
185+
expect(instance.getAttributeNames()).to.eql(['data-baz', 'data-foo', 'data-bar'])
186+
expect(instance.getAttribute('data-foo')).to.equal('')
187+
expect(instance.getAttribute('data-bar')).to.equal('hello')
188+
expect(instance.getAttribute('data-baz')).to.equal('world')
189+
})
172190
})
173191

174192
describe('defineObservedAttributes', () => {
@@ -200,4 +218,12 @@ describe('defineObservedAttributes', () => {
200218
TestElement.observedAttributes = ['a', 'b', 'c']
201219
expect(TestElement.observedAttributes).to.eql(['data-foo', 'a', 'b', 'c'])
202220
})
221+
222+
it('will reflect values from extended elements', () => {
223+
class TestElement extends HTMLElement {}
224+
class ExtendedTestElement extends TestElement {}
225+
defineObservedAttributes(ExtendedTestElement)
226+
attr(TestElement.prototype, 'foo')
227+
expect(ExtendedTestElement.observedAttributes).to.eql(['data-foo'])
228+
})
203229
})

0 commit comments

Comments
 (0)