docs(cabana): widget element contract and payload fixture (quick-261006-eyj)
- acme-demo-lookup fixture is a reorder widget: a click reverses its items, sends the new id order as detail.payload and renders the returned items from the data attribute and the summer-result event with textContent, no HTTP - partials-and-widgets gains a Widget element contract subsection listing the attributes the SPA sets and the summer-action / summer-result events - cabana README names both events in the form widgets bullet
This commit is contained in:
@@ -1,16 +1,60 @@
|
||||
// A plain custom element: one light-DOM button that asks the admin SPA to
|
||||
// run the field's action. It makes no request and reads no cookie or
|
||||
// storage; the SPA owns HTTP.
|
||||
// A plain custom element that demonstrates a reorder widget: one light-DOM
|
||||
// button and an ordered list. The SPA owns HTTP; this element only speaks the
|
||||
// summer-action / summer-result events. It makes no request and reads no
|
||||
// cookie or storage. A click reverses the current items (the simplest stand-in
|
||||
// for a drag reorder) and sends the new id order as the action's payload; the
|
||||
// returned data (its items array) arrives both as the `data` attribute and as
|
||||
// the summer-result event's detail.data, and is rendered with textContent.
|
||||
class AcmeDemoLookup extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['data']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.items = []
|
||||
this.list = null
|
||||
this.addEventListener('summer-result', (event) => this.receive(event.detail && event.detail.data))
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.firstChild) return
|
||||
const button = document.createElement('button')
|
||||
button.type = 'button'
|
||||
button.textContent = this.getAttribute('label') || ''
|
||||
button.addEventListener('click', () => {
|
||||
this.dispatchEvent(new CustomEvent('summer-action', { bubbles: true, composed: true }))
|
||||
this.items = this.items.slice().reverse()
|
||||
this.render()
|
||||
const payload = { order: this.items.map((item) => item.id) }
|
||||
this.dispatchEvent(new CustomEvent('summer-action', { bubbles: true, composed: true, detail: { payload } }))
|
||||
})
|
||||
this.append(button)
|
||||
this.list = document.createElement('ol')
|
||||
this.append(button, this.list)
|
||||
this.render()
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, _old, value) {
|
||||
if (name !== 'data') return
|
||||
try {
|
||||
this.receive(value === null ? null : JSON.parse(value))
|
||||
} catch {
|
||||
this.receive(null)
|
||||
}
|
||||
}
|
||||
|
||||
receive(data) {
|
||||
this.items = data && Array.isArray(data.items) ? data.items.filter((item) => item && typeof item === 'object') : []
|
||||
this.render()
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.list) return
|
||||
this.list.textContent = ''
|
||||
for (const item of this.items) {
|
||||
const li = document.createElement('li')
|
||||
li.textContent = String(item.title == null ? item.id : item.title)
|
||||
this.list.append(li)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!customElements.get('acme-demo-lookup')) {
|
||||
|
||||
Reference in New Issue
Block a user