Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/lib/components/customId.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
import { trackEvent } from '$lib/actions/analytics';
import { InnerModal } from '$lib/components';
import { InputId } from '$lib/elements/forms';

import { InputProjectId } from '$lib/elements/forms';
export let show = false;
export let name: string;
export let id: string;
export let autofocus = true;
export let fullWidth = false;

$: if (!show) {
id = null;
}
Expand All @@ -29,7 +28,11 @@
</svelte:fragment>
<svelte:fragment slot="content">
<div class="form">
<InputId bind:value={id} {autofocus} />
{#if name === 'Project'}
<InputProjectId bind:value={id} {autofocus} />
{:else}
<InputId bind:value={id} {autofocus} />
{/if}
</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 @@ -23,6 +23,7 @@ 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 InputProjectId } from './inputProjectId.svelte';
export { default as InputSecret } from './inputSecret.svelte';
export { default as Helper } from './helper.svelte';
export { default as Label } from './label.svelte';
60 changes: 60 additions & 0 deletions src/lib/elements/forms/inputProjectId.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem } from '.';
import TextCounter from './textCounter.svelte';

export let value = '';
export let autofocus = true;

let element: HTMLInputElement;
let icon = 'info';
const pattern = String.raw`^[a-z0-9][a-z0-9-]{1,35}$`;

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} />
<TextCounter count={value?.length ?? 0} max={36} />
</div>
</FormItem>
<div
class="u-flex u-gap-4 u-margin-block-start-8 u-small"
class:u-color-text-warning={icon === 'exclamation'}>
<span
class:icon-info={icon === 'info'}
class:icon-exclamation={icon === 'exclamation'}
class="u-cross-center u-line-height-1 u-color-text-gray"
aria-hidden="true" />
<span class="text u-line-height-1-5">
Allowed characters: lowercase alphanumeric character and non-leading hyphen
</span>
</div>
40 changes: 40 additions & 0 deletions tests/unit/elements/inputProjectId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { InputProjectId } from '../../../src/lib/elements/forms';

const validStrings = ['validstring', 'valid-string', 'validstring123', 'valid-'];

const invalidStrings = ['-invalid', 'Valid', '_invalid', 'Valid123', 'valid.string'];

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

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

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

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

validStrings.forEach((validString) => {
test(`validates ${validString} as valid`, () => {
const { getByPlaceholderText } = render(InputProjectId, { value: validString });
const input = getByPlaceholderText('Enter ID') as HTMLInputElement;
expect(input.checkValidity()).toBe(true);
});
});
invalidStrings.forEach((invalidString) => {
test(`validates ${invalidString} as invalid`, () => {
const { getByPlaceholderText } = render(InputProjectId, { value: invalidString });
const input = getByPlaceholderText('Enter ID') as HTMLInputElement;
expect(input.checkValidity()).toBe(false);
});
});