Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom ID validation and char length counter #167

Merged
merged 9 commits into from
Feb 8, 2023
19 changes: 2 additions & 17 deletions src/lib/components/customId.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { trackEvent } from '$lib/actions/analytics';
import { InnerModal } from '$lib/components';
import { InputText } from '$lib/elements/forms';
import { InputId } from '$lib/elements/forms';

export let show = false;
export let name: string;
Expand All @@ -27,22 +27,7 @@
</svelte:fragment>
<svelte:fragment slot="content">
<div class="form">
<InputText
id="id"
label="Custom ID"
showLabel={false}
placeholder="Enter ID"
autofocus={true}
bind:value={id} />

<div class="u-flex u-gap-4 u-margin-block-start-8 u-small">
<span
class="icon-info u-cross-center u-margin-block-start-2 u-line-height-1 u-icon-small"
aria-hidden="true" />
<span class="text u-line-height-1-5">
Allowed characters: alphanumeric, hyphen, non-leading underscore, period
</span>
</div>
<InputId bind:value={id} />
</div>
</svelte:fragment>
</InnerModal>
1 change: 1 addition & 0 deletions src/lib/elements/forms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export { default as InputChoice } from './inputChoice.svelte';
export { default as InputPhone } from './inputPhone.svelte';
export { default as InputCron } from './inputCron.svelte';
export { default as InputURL } from './inputURL.svelte';
export { default as InputId } from './inputId.svelte';
export { default as InputSecret } from './inputSecret.svelte';
export { default as Helper } from './helper.svelte';
61 changes: 61 additions & 0 deletions src/lib/elements/forms/inputId.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem } from '.';

export let value = '';

let element: HTMLInputElement;
let icon = 'info';
const pattern = String.raw`/^[^._-][a-zA-Z0-9._-]*$/`;
const autofocus = true;

onMount(() => {
if (element && autofocus) {
element.focus();
}
});

const handleInvalid = (event: Event) => {
event.preventDefault();

if (element.validity.patternMismatch) {
icon = 'exclamation';
return;
}
};

$: if (value) {
icon = 'info';
}
</script>

<FormItem>
<div class="input-text-wrapper">
<input
id="id"
placeholder="Enter ID"
maxlength={36}
{pattern}
autocomplete="off"
type="text"
class="input-text"
bind:value
bind:this={element}
on:invalid={handleInvalid} />
<span class="text-counter">
<span class="text-counter-count">{value?.length ?? 0}</span>
<span class="text-counter-separator" />
<span class="text-counter-max">36</span>
</span>
</div>
</FormItem>
<div class="u-flex u-gap-4 u-margin-block-start-8 u-small" class:u-warning={icon === 'exclamation'}>
<span
class:icon-info={icon === 'info'}
class:icon-exclamation={icon === 'exclamation'}
class="u-cross-center u-margin-block-start-2 u-line-height-1 u-icon-small"
aria-hidden="true" />
<span class="text u-line-height-1-5">
Allowed characters: alphanumeric, non-leading hyphen, underscore, period
</span>
</div>
22 changes: 22 additions & 0 deletions tests/unit/elements/inputId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { InputId } from '../../../src/lib/elements/forms';

test('shows id input', () => {
const { getByPlaceholderText } = render(InputId);
const input = getByPlaceholderText('Enter ID');

expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('type', 'text');
});

test('state', async () => {
const { component, getByPlaceholderText } = render(InputId, { value: '' });
const input = getByPlaceholderText('Enter ID');

console.log(component);
expect(component.value).toEqual('');
await userEvent.type(input, 'lorem');
expect(component.value).toEqual('lorem');
});