From 234fa6e0ef339d2488ec496b51058e8598fee7ca Mon Sep 17 00:00:00 2001 From: Austin Smith Date: Mon, 20 Oct 2025 23:16:17 -0400 Subject: [PATCH] Add ability to hydrate form from an http endpoint. --- src/declaform/declaform.ts | 13 +++-- src/declaform/tests/declaform.test.ts | 69 ++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/declaform/declaform.ts b/src/declaform/declaform.ts index f310565..50abff8 100644 --- a/src/declaform/declaform.ts +++ b/src/declaform/declaform.ts @@ -111,7 +111,7 @@ export class Declaform extends HTMLElement { this._form = null; } - connectedCallback() { + async connectedCallback() { const formSlot = this.shadowRoot?.querySelector('slot[name="form"]') as HTMLSlotElement; if (!formSlot) { throw new Error('[Declaform] Error: "form" slot is unassigned.') @@ -145,12 +145,11 @@ export class Declaform extends HTMLElement { for (const [name, inputs] of inputGroups.entries()) { this._fields.push(new Field(name, inputs)) } - /* + const src = this.getAttribute('src'); if (src) { - this.hydrateForm(src); + this.hydrateFromEndpoint(src); } - */ } get form() { @@ -197,6 +196,12 @@ export class Declaform extends HTMLElement { }; populateFields(obj, ''); } + + private async hydrateFromEndpoint(endpoint: string): Promise { + const response = await fetch(endpoint); + const obj = await response.json(); + this.hydrate(obj); + } } customElements.define('decla-form', Declaform); diff --git a/src/declaform/tests/declaform.test.ts b/src/declaform/tests/declaform.test.ts index c35a7be..830327f 100644 --- a/src/declaform/tests/declaform.test.ts +++ b/src/declaform/tests/declaform.test.ts @@ -1,7 +1,6 @@ -//import '../declaform'; import { Declaform } from '../declaform'; -const MOVIE_FORM = ` +const MOVIE_FORM_NO_SRC = `
@@ -32,14 +31,49 @@ const MOVIE_FORM = ` `; +const MOVIE_FORM_HTTP = ` + + +
+ + +
+ +
+ Rating +
+ + +
+
+ + +
+
+ + +
+
+ + +
+`; + describe('Declaform', () => { + afterEach(() => { + document.body.innerHTML = ''; + }) + it('Should mount.', () => { - document.body.innerHTML = MOVIE_FORM; + document.body.innerHTML = MOVIE_FORM_NO_SRC; expect(document.getElementById('form')).toBeTruthy(); }) it('.toObject() should serialize form into a JS object', () => { - document.body.innerHTML = MOVIE_FORM; + document.body.innerHTML = MOVIE_FORM_NO_SRC; const form = document.getElementById('form') as Declaform; setInputValue('name', 'Monty Python'); setInputValue('genre', 'comedy'); @@ -57,7 +91,7 @@ describe('Declaform', () => { }); it('.hydrate() should populate input fields from a JS object', () => { - document.body.innerHTML = MOVIE_FORM; + document.body.innerHTML = MOVIE_FORM_NO_SRC; const form = document.getElementById('form') as Declaform; form.hydrate({ name: 'Jaws', @@ -71,7 +105,30 @@ describe('Declaform', () => { expect(getInputValue('genre')).toBe('horror') expect(getInputValue('rating-score')).toBe('5'); expect(document.getElementById('rating-type-critic')?.hasAttribute('checked')).toBe(true) - }) + }); + + it('Supplying "src" attribute should hydrate form with the supplied endpoint.', async () => { + global.fetch = jest.fn().mockResolvedValue({ + status: 200, + ok: true, + json: () => Promise.resolve({ + name: 'Jaws', + genre: 'horror', + rating: { + score: 5, + type: 'critic', + } + }) + }) + document.body.innerHTML = MOVIE_FORM_HTTP; + + await new Promise((resolve) => setTimeout(resolve, 0)) + + expect(getInputValue('name')).toBe('Jaws') + expect(getInputValue('genre')).toBe('horror') + expect(getInputValue('rating-score')).toBe('5'); + expect(document.getElementById('rating-type-critic')?.hasAttribute('checked')).toBe(true) + }); }); function setInputValue(id: string, value: string) {