Add ability to hydrate form from an http endpoint.

This commit is contained in:
Austin Smith
2025-10-20 23:16:17 -04:00
parent ff44ef1b67
commit 234fa6e0ef
2 changed files with 72 additions and 10 deletions

View File

@@ -111,7 +111,7 @@ export class Declaform extends HTMLElement {
this._form = null; this._form = null;
} }
connectedCallback() { async connectedCallback() {
const formSlot = this.shadowRoot?.querySelector('slot[name="form"]') as HTMLSlotElement; const formSlot = this.shadowRoot?.querySelector('slot[name="form"]') as HTMLSlotElement;
if (!formSlot) { if (!formSlot) {
throw new Error('[Declaform] Error: "form" slot is unassigned.') 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()) { for (const [name, inputs] of inputGroups.entries()) {
this._fields.push(new Field(name, inputs)) this._fields.push(new Field(name, inputs))
} }
/*
const src = this.getAttribute('src'); const src = this.getAttribute('src');
if (src) { if (src) {
this.hydrateForm(src); this.hydrateFromEndpoint(src);
} }
*/
} }
get form() { get form() {
@@ -197,6 +196,12 @@ export class Declaform extends HTMLElement {
}; };
populateFields(obj, ''); populateFields(obj, '');
} }
private async hydrateFromEndpoint(endpoint: string): Promise<void> {
const response = await fetch(endpoint);
const obj = await response.json();
this.hydrate(obj);
}
} }
customElements.define('decla-form', Declaform); customElements.define('decla-form', Declaform);

View File

@@ -1,7 +1,6 @@
//import '../declaform';
import { Declaform } from '../declaform'; import { Declaform } from '../declaform';
const MOVIE_FORM = ` const MOVIE_FORM_NO_SRC = `
<decla-form id="form"> <decla-form id="form">
<form slot="form"> <form slot="form">
<div class="input-group"> <div class="input-group">
@@ -32,14 +31,49 @@ const MOVIE_FORM = `
</decla-form> </decla-form>
`; `;
const MOVIE_FORM_HTTP = `
<decla-form id="form" src="get-movie/jaws">
<form slot="form">
<div class="input-group">
<label for="name">Name</label>
<input id="name" name="name" />
</div>
<select id="genre" name="genre">
<option value="comedy">Comedy</option>
<option value="horror">Horror</option>
</select>
<fieldset>
<legend>Rating</legend>
<div class="input-group">
<label for="rating-type-user">Type</label>
<input id="rating-type-user" name="rating.type" type="radio" value="user" />
</div>
<div class="input-group">
<label for="rating-type-critic">Type</label>
<input id="rating-type-critic" name="rating.type" type="radio" value="critic" />
</div>
<div class="input-group">
<label for="rating-score">Score</label>
<input id="rating-score" name="rating.score" type="number" />
</div>
</fieldset>
<button type="submit">Submit</button>
</form>
</decla-form>
`;
describe('Declaform', () => { describe('Declaform', () => {
afterEach(() => {
document.body.innerHTML = '';
})
it('Should mount.', () => { it('Should mount.', () => {
document.body.innerHTML = MOVIE_FORM; document.body.innerHTML = MOVIE_FORM_NO_SRC;
expect(document.getElementById('form')).toBeTruthy(); expect(document.getElementById('form')).toBeTruthy();
}) })
it('.toObject() should serialize form into a JS object', () => { 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; const form = document.getElementById('form') as Declaform;
setInputValue('name', 'Monty Python'); setInputValue('name', 'Monty Python');
setInputValue('genre', 'comedy'); setInputValue('genre', 'comedy');
@@ -57,7 +91,7 @@ describe('Declaform', () => {
}); });
it('.hydrate() should populate input fields from a JS object', () => { 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; const form = document.getElementById('form') as Declaform;
form.hydrate({ form.hydrate({
name: 'Jaws', name: 'Jaws',
@@ -71,7 +105,30 @@ describe('Declaform', () => {
expect(getInputValue('genre')).toBe('horror') expect(getInputValue('genre')).toBe('horror')
expect(getInputValue('rating-score')).toBe('5'); expect(getInputValue('rating-score')).toBe('5');
expect(document.getElementById('rating-type-critic')?.hasAttribute('checked')).toBe(true) 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) { function setInputValue(id: string, value: string) {