Skip to content

Commit aa3024e

Browse files
Merge pull request #34 from appwrite/draft-feat-form-frontend-error-handling
feat: error handling in forms
2 parents d288635 + f325358 commit aa3024e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1126
-1035
lines changed

package-lock.json

Lines changed: 437 additions & 577 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"dev": "vite dev",
99
"build": "vite build",
1010
"preview": "vite preview",
11-
"prepare": "svelte-kit sync",
1211
"check": "svelte-check --tsconfig ./tsconfig.json",
1312
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
1413
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint .",
@@ -26,33 +25,33 @@
2625
"tippy.js": "^6.3.7"
2726
},
2827
"devDependencies": {
29-
"@playwright/test": "^1.24.0",
30-
"@sveltejs/adapter-auto": "1.0.0-next.63",
31-
"@sveltejs/kit": "1.0.0-next.390",
28+
"@playwright/test": "^1.25.0",
29+
"@sveltejs/adapter-auto": "1.0.0-next.64",
30+
"@sveltejs/kit": "1.0.0-next.405",
3231
"@sveltejs/vite-plugin-svelte": "^1.0.1",
33-
"@testing-library/dom": "^8.16.0",
34-
"@testing-library/jest-dom": "^5.16.4",
32+
"@testing-library/dom": "^8.17.1",
33+
"@testing-library/jest-dom": "^5.16.5",
3534
"@testing-library/svelte": "^3.1.3",
3635
"@testing-library/user-event": "^14.3.0",
3736
"@types/gtag.js": "^0.0.10",
38-
"@typescript-eslint/eslint-plugin": "^5.30.7",
39-
"@typescript-eslint/parser": "^5.30.7",
40-
"@vitest/ui": "^0.18.1",
41-
"eslint": "^8.20.0",
37+
"@typescript-eslint/eslint-plugin": "^5.33.1",
38+
"@typescript-eslint/parser": "^5.33.1",
39+
"@vitest/ui": "^0.22.0",
40+
"eslint": "^8.22.0",
4241
"eslint-config-prettier": "^8.5.0",
4342
"eslint-plugin-svelte3": "^4.0.0",
4443
"pre-commit": "^1.2.2",
4544
"prettier": "^2.7.1",
4645
"prettier-plugin-svelte": "^2.7.0",
47-
"sass": "^1.53.0",
46+
"sass": "^1.54.4",
4847
"svelte": "^3.49.0",
4948
"svelte-check": "^2.8.0",
5049
"svelte-jester": "^2.3.2",
5150
"svelte-preprocess": "^4.10.7",
5251
"tslib": "^2.4.0",
5352
"typescript": "^4.7.4",
54-
"vite": "^3.0.0",
55-
"vitest": "^0.18.1"
53+
"vite": "^3.0.8",
54+
"vitest": "^0.22.0"
5655
},
5756
"type": "module",
5857
"pre-commit": [

src/lib/components/InnerModal.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<span class="icon-x" aria-hidden="true" />
1818
</button>
1919
<p class="u-flex-basis-100-percent">
20-
<slot />
20+
<slot name="subtitle" />
2121
</p>
2222
</header>
2323
<div class="modal-content">

src/lib/components/alert.svelte

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
import type { Buttons } from '../stores/notifications';
44
55
export let dismissible = false;
6-
export let type: '' | 'info' | 'success' | 'warning' | 'danger' = 'info';
6+
export let type: 'info' | 'success' | 'warning' | 'error' = 'info';
77
export let buttons: Buttons[] = [];
88
99
const dispatch = createEventDispatcher();
1010
</script>
1111

12-
<section class={`alert is-${type} `}>
12+
<section
13+
class="alert"
14+
class:is-success={type === 'success'}
15+
class:is-warning={type === 'warning'}
16+
class:is-danger={type === 'error'}
17+
class:is-info={type === 'info'}>
1318
<div class="alert-grid">
1419
{#if dismissible}
1520
<button
@@ -19,7 +24,12 @@
1924
<span class="icon-x" aria-hidden="true" />
2025
</button>
2126
{/if}
22-
<span class="icon-info" aria-hidden="true" />
27+
<span
28+
class:icon-info={type === 'info'}
29+
class:icon-check-circle={type === 'success'}
30+
class:icon-exclamation={type === 'warning'}
31+
class:icon-exclamation-circle={type === 'error'}
32+
aria-hidden="true" />
2333
<div class="content">
2434
{#if $$slots.title}
2535
<h6 class="title">

src/lib/components/modal.svelte

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
<script lang="ts">
2-
import { createEventDispatcher, onMount } from 'svelte';
2+
import { browser } from '$app/env';
3+
import { createEventDispatcher } from 'svelte';
34
import { fade, fly, type FadeParams, type FlyParams } from 'svelte/transition';
5+
import { Alert } from '$lib/components';
46
57
export let show = false;
68
export let size: 'small' | 'big' = null;
79
export let warning = false;
8-
let browser = false;
9-
//TODO: explore other solutions compatible with testing library
10-
onMount(() => {
11-
browser = true;
12-
});
10+
export let error: string = null;
1311
1412
const dispatch = createEventDispatcher();
1513
const transitionFly: FlyParams = {
@@ -26,19 +24,20 @@
2624
closeModal();
2725
}
2826
};
29-
3027
const handleBLur = (event: MouseEvent) => {
3128
const target: Partial<HTMLElement> = event.target;
3229
if (target.hasAttribute('data-curtain')) {
3330
closeModal();
3431
}
3532
};
36-
3733
const closeModal = () => {
3834
show = false;
3935
dispatch('close');
4036
};
4137
38+
/**
39+
* Workaround until https://github.com/sveltejs/svelte/issues/3105 is resolved.
40+
*/
4241
$: if (browser) {
4342
if (show) {
4443
document.body.classList.add('u-overflow-hidden');
@@ -76,6 +75,16 @@
7675
</button>
7776
</header>
7877
<div class="modal-content">
78+
{#if error}
79+
<Alert
80+
dismissible
81+
type="warning"
82+
on:dismiss={() => {
83+
error = null;
84+
}}>
85+
{error}
86+
</Alert>
87+
{/if}
7988
<slot />
8089
</div>
8190
<div class="modal-footer">

src/lib/elements/forms/form.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<form class="form" on:submit|preventDefault>
1+
<form class="form common-section" on:submit|preventDefault>
22
<slot />
33
</form>
Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
11
<script lang="ts">
2-
export let type: false | 'success' | 'warning' | 'error' = 'warning';
3-
4-
let icon = '';
5-
switch (type) {
6-
case 'success':
7-
icon = 'check-circle';
8-
break;
9-
case 'warning':
10-
icon = 'exclamation';
11-
break;
12-
13-
case 'error':
14-
icon = 'exclamation-circle';
15-
break;
16-
17-
default:
18-
icon = '';
19-
break;
20-
}
2+
export let type: 'success' | 'warning' | 'error' | 'info' | 'neutral' = 'warning';
213
</script>
224

23-
<li class={`helper u-${type} u-margin-block-start-8"`}>
24-
{#if icon !== ''}
25-
<span class={`icon-${icon}`} aria-hidden="true" />
5+
<p
6+
class="helper u-margin-block-start-12"
7+
class:u-info={type === 'info'}
8+
class:u-error={type === 'error'}
9+
class:u-success={type === 'success'}
10+
class:u-warning={type === 'warning'}
11+
class:u-neutral={type === 'neutral'}>
12+
{#if type}
13+
<span
14+
class:icon-info={type === 'info'}
15+
class:icon-error={type === 'error'}
16+
class:icon-success={type === 'success'}
17+
class:icon-warning={type === 'warning'}
18+
class:icon-neutral={type === 'neutral'}
19+
aria-hidden="true" />
2620
{/if}
2721
<span class="text">
2822
<slot />
2923
</span>
30-
</li>
24+
</p>
Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,43 @@
11
<script lang="ts">
2-
import { FormItem } from '.';
2+
import { FormItem, Helper } from '.';
33
44
export let label: string;
55
export let showLabel = true;
66
export let id: string;
77
export let value = false;
88
export let required = false;
99
export let disabled = false;
10+
11+
let element: HTMLInputElement;
12+
let error: string;
13+
14+
const handleInvalid = (event: Event) => {
15+
event.preventDefault();
16+
if (element.validity.valueMissing) {
17+
error = 'This field is required';
18+
return;
19+
}
20+
error = element.validationMessage;
21+
};
22+
23+
$: if (value) {
24+
error = null;
25+
}
1026
</script>
1127

1228
<FormItem>
1329
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
1430
<div class="input-text-wrapper">
15-
<input {id} {disabled} {required} type="checkbox" bind:checked={value} />
31+
<input
32+
{id}
33+
{disabled}
34+
{required}
35+
type="checkbox"
36+
bind:this={element}
37+
bind:checked={value}
38+
on:invalid={handleInvalid} />
1639
</div>
40+
{#if error}
41+
<Helper type="warning">{error}</Helper>
42+
{/if}
1743
</FormItem>

src/lib/elements/forms/inputCustomId.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
export let placeholder = '';
88
export let required = false;
99
export let autofocus = false;
10+
1011
let element: HTMLInputElement;
1112
let unique = true;
1213
let bench = '';
14+
1315
const toggle = async () => {
1416
unique = !unique;
1517
if (unique) {

src/lib/elements/forms/inputEmail.svelte

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts">
22
import { onMount } from 'svelte';
3-
import { FormItem } from '.';
3+
import { FormItem, Helper } from '.';
44
55
export let label: string;
66
export let showLabel = true;
@@ -13,12 +13,30 @@
1313
export let autocomplete = false;
1414
1515
let element: HTMLInputElement;
16+
let error: string;
1617
1718
onMount(() => {
1819
if (element && autofocus) {
1920
element.focus();
2021
}
2122
});
23+
24+
const handleInvalid = (event: Event) => {
25+
event.preventDefault();
26+
if (element.validity.typeMismatch) {
27+
error = 'Your email should be formatted as: [email protected]';
28+
return;
29+
}
30+
if (element.validity.valueMissing) {
31+
error = 'This field is required';
32+
return;
33+
}
34+
error = element.validationMessage;
35+
};
36+
37+
$: if (value) {
38+
error = null;
39+
}
2240
</script>
2341

2442
<FormItem>
@@ -29,10 +47,14 @@
2947
{placeholder}
3048
{disabled}
3149
{required}
32-
autocomplete={autocomplete ? 'on' : 'off'}
3350
type="email"
3451
class="input-text"
52+
autocomplete={autocomplete ? 'on' : 'off'}
3553
bind:value
36-
bind:this={element} />
54+
bind:this={element}
55+
on:invalid={handleInvalid} />
3756
</div>
57+
{#if error}
58+
<Helper type="warning">{error}</Helper>
59+
{/if}
3860
</FormItem>

0 commit comments

Comments
 (0)