diff options
Diffstat (limited to '')
| -rw-r--r-- | TODO.norg | 6 | ||||
| -rw-r--r-- | src/eval.ts | 35 | ||||
| -rw-r--r-- | src/renderer.ts | 13 | ||||
| -rw-r--r-- | tests/fixtures/signup/index.html | 35 | ||||
| -rw-r--r-- | tests/signup.spec.ts | 14 |
5 files changed, 76 insertions, 27 deletions
@@ -8,12 +8,12 @@ - (x) attributes - (x) `--cssx-text` (and maybe `--cssx-html`?) - (x) dom tests + - (x) `attr` function + - (x) `set-attr` should allow specifying id? + - (x) `set-attr` + remove attribute? - ( ) keyboard events - - ( ) `attr` function - - ( ) j - ( ) Evaluate `calc` - ( ) Additional events - ( ) Improve error messages - - ( ) `set-attr` should allow specifying id? - ( ) `add-class` & `remove-class` should use self if id is not specified? - ( ) `list` & `tuple` data structures? diff --git a/src/eval.ts b/src/eval.ts index 8aeaf09..9f053c2 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -9,7 +9,15 @@ export interface EvalActions { loadCssx(id: string, url: string): Promise<string> getVariable(name: string): Promise<string | undefined> updateVariable(id: string, varName: string, value: string): Promise<void> - setAttribute(name: string, value: string): Promise<void> + getAttribute( + id: string | undefined, + name: string, + ): Promise<string | undefined> + setAttribute( + id: string | undefined, + name: string, + value: string, + ): Promise<void> withEvent(fn: (e: any) => void): Promise<void> getFormData(): Promise<FormData | undefined> sendRequest(_: { @@ -88,10 +96,21 @@ const getFunctions = (name: string, args: Expr[], actions: EvalActions) => }, 'set-attr': async () => { - const name = await evalExpr(args[0], actions) - const value = await evalExpr(args[1], actions) - if (name && value) { - actions.setAttribute(name, value) + const [id, name, value] = + args.length >= 3 + ? await evalArgs(args, 3, actions) + : [undefined, ...(await evalArgs(args, 2, actions))] + if (name) { + actions.setAttribute(id as string | undefined, name, value ?? '') + } + }, + attr: async () => { + const [id, name] = + args.length >= 2 + ? await evalArgs(args, 2, actions) + : [undefined, await evalExpr(args[0], actions)] + if (name) { + return actions.getAttribute(id as string | undefined, name) } }, 'prevent-default': async () => actions.withEvent(e => e.preventDefault()), @@ -108,3 +127,9 @@ const getFunctions = (name: string, args: Expr[], actions: EvalActions) => _: () => Promise.reject(new Error('not supposed to be here')), }) + +export const evalArgs = ( + args: Array<Expr>, + count: number, + actions: EvalActions, +) => Promise.all(args.slice(0, count).map(e => evalExpr(e, actions))) diff --git a/src/renderer.ts b/src/renderer.ts index 19cd4d0..1c8184a 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -73,8 +73,17 @@ const getEvalActions = ($element: Element, event: any): EvalActions => ({ $el.style.setProperty(varName, JSON.stringify(value)) } }, - setAttribute: async (name, value) => { - $element.setAttribute(name, value) + setAttribute: async (id, name, value) => { + const $el = id ? document.getElementById(id) : $element + if (value) { + $el?.setAttribute(name, value) + } else { + $el?.removeAttribute(name) + } + }, + getAttribute: async (id, name) => { + const $el = id ? document.getElementById(id) : $element + return $el?.getAttribute(name) ?? undefined }, withEvent: async fn => fn(event), getFormData: async () => diff --git a/tests/fixtures/signup/index.html b/tests/fixtures/signup/index.html index df69508..690e52c 100644 --- a/tests/fixtures/signup/index.html +++ b/tests/fixtures/signup/index.html @@ -11,8 +11,13 @@ content: 'Show form'; } #show-form-btn { - --cssx-on-click: add-class(signup-form, 'visible') - add-class(show-form-btn, 'active'); + /* prettier-ignore */ + --cssx-on-click: + add-class(signup-form, 'visible') + add-class(show-form-btn, 'hidden'); + } + #show-form-btn.hidden { + display: none; } #signup-form { @@ -20,22 +25,32 @@ --cssx-children: input#email input#password button#submit-btn; - --cssx-on-submit: prevent-default() add-class(signup-form, 'submitting') - delay(0.2s) request('http://example.com/submit/api', POST) - remove-class(signup-form, 'submitting') - add-class(signup-form, 'submitted'); + /* prettier-ignore */ + --cssx-on-submit: prevent-default() + set-attr(submit-btn, 'disabled', 'disabled') /* disable submit button */ + add-class(signup-form, 'submitting') /* enable state "submitting" */ + delay(0.2s) /* fake delay */ + request('http://example.com/submit/api', POST) /* post form data to api */ + remove-class(signup-form, 'submitting') /* disable state "submitting" */ + set-attr(submit-btn, 'disabled', '') /* re-enable submit button */ + add-class(signup-form, 'submitted') /* show user that the form is submitted */ + ; } #signup-form.visible { display: block; } #email { - --cssx-on-mount: set-attr('name', 'email') - set-attr('data-testid', 'email'); + /* prettier-ignore */ + --cssx-on-mount: + set-attr('name', 'email') + set-attr('data-testid', attr('name')); } #password { - --cssx-on-mount: set-attr('name', 'password') - set-attr('data-testid', 'password'); + /* prettier-ignore */ + --cssx-on-mount: + set-attr('name', 'password') + set-attr('data-testid', attr('name')); } #submit-btn { --cssx-on-mount: set-attr('type', 'submit'); diff --git a/tests/signup.spec.ts b/tests/signup.spec.ts index c2cec0b..beede57 100644 --- a/tests/signup.spec.ts +++ b/tests/signup.spec.ts @@ -34,12 +34,10 @@ describe('signup example', () => { expect($form).not.toBeVisible() expect($form.nodeName).toBe('FORM') - // Click and wait for button to get active class (handles delay) + // Click and wait for button to get hidden class (handles delay) fireEvent.click($showFormBtn) - await waitFor(() => - expect($showFormBtn.classList.contains('active')).toBe(true), - ) + await waitFor(() => expect($showFormBtn).not.toBeVisible()) expect($form).toBeVisible() }) @@ -47,9 +45,7 @@ describe('signup example', () => { beforeEach(async () => { const $showFormBtn = document.getElementById('show-form-btn')! fireEvent.click($showFormBtn) - await waitFor(() => - expect($showFormBtn.classList.contains('active')).toBe(true), - ) + await waitFor(() => expect($showFormBtn).not.toBeVisible()) const $form = document.getElementById('signup-form')! expect($form).toBeVisible() await delay(100) // Wait for mounting @@ -64,6 +60,8 @@ describe('signup example', () => { const $password = getByTestId<HTMLInputElement>(document.body, 'password') $password.value = 'password' + await delay(2000) + // Submit form const $submitBtn = getByText(document.body, 'Submit') fireEvent.click($submitBtn) @@ -72,12 +70,14 @@ describe('signup example', () => { await waitFor(() => expect($form.classList.contains('submitting')).toBe(true), ) + expect($submitBtn).toBeDisabled() // Should add submitted class to form and remove submitting await waitFor(() => expect($form.classList.contains('submitted')).toBe(true), ) expect($form.classList.contains('submitting')).toBe(false) + expect($submitBtn).toBeEnabled() // Should have made a request to post form data expect(window.fetch).toHaveBeenCalledTimes(1) |
