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;
}
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<void> {
const response = await fetch(endpoint);
const obj = await response.json();
this.hydrate(obj);
}
}
customElements.define('decla-form', Declaform);

View File

@@ -1,7 +1,6 @@
//import '../declaform';
import { Declaform } from '../declaform';
const MOVIE_FORM = `
const MOVIE_FORM_NO_SRC = `
<decla-form id="form">
<form slot="form">
<div class="input-group">
@@ -32,14 +31,49 @@ const MOVIE_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', () => {
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) {