Skip to content

Commit

Permalink
[feat](@svelteui/core): add Textarea component (#407)
Browse files Browse the repository at this point in the history
* [@svelteui/core]: add TextArea component

* [@svelteui/core]: use camelCase for ids

* [docs]: remove unused configurator prop
  • Loading branch information
khalibloo committed Jun 27, 2023
1 parent e552041 commit 7760d9f
Show file tree
Hide file tree
Showing 20 changed files with 478 additions and 4 deletions.
1 change: 1 addition & 0 deletions apps/docs/src/lib/components/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
<li><a href={`${base}/core/number-input`}>NumberInput</a></li>
<li><a href={`${base}/core/radio`}>Radio</a></li>
<li><a href={`${base}/core/switch`}>Switch</a></li>
<li><a href={`${base}/core/textarea`}>Textarea</a></li>
<li><a href={`${base}/core/text-input`}>TextInput</a></li>
<li><a href={`${base}/core/unstyled-button`}>UnstyledButton</a></li>
</ul>
Expand Down
18 changes: 18 additions & 0 deletions apps/docs/src/lib/data/main/homepage.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
Loader,
Code,
Text,
Textarea,
TextInput,
Title,
Box
} from '@svelteuidev/core';
Expand Down Expand Up @@ -148,6 +150,22 @@ export const components = [
color: '$blue600',
content: 'Read about me'
},
{
icon: BoxIcon,
component: TextInput,
link: 'core/text-input',
title: 'TextInput',
color: '$blue600',
content: 'Read about me'
},
{
icon: BoxIcon,
component: Textarea,
link: 'core/textarea',
title: 'Textarea',
color: '$blue600',
content: 'Read about me'
},
{
icon: BoxIcon,
component: NativeSelect,
Expand Down
65 changes: 65 additions & 0 deletions apps/docs/src/routes/core/textarea/+page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: Textarea
group: 'svelteuidev-core'
packageGroup: '@svelteuidev/core'
slug: /core/textarea/
category: 'inputs'
description: 'Multiline text input'
importCode: "import { Textarea } from '@svelteuidev/core';"
source: 'svelteui-core/src/components/Textarea/Textarea.svelte'
docs: 'core/textarea'
---

<script>
import { Demo, TextareaDemos } from '@svelteuidev/demos';
import { Heading } from "$lib/components";
</script>

<svelte:head>

<title>{title} - SvelteUI</title>
</svelte:head>

<Heading {title} {group} {packageGroup} {slug} {category} {description} {importCode} {source} {docs} />

## Usage

<Demo demo={TextareaDemos.configurator} />

## Bindings

```svelte
<script>
import { Textarea } from '@svelteuidev/core';
let value = '';
</script>
<Textarea bind:value />
```

## Invalid state and error

<Demo demo={TextareaDemos.invalid} />

## Disabled state

<Demo demo={TextareaDemos.disabled} />

## With icon

<Demo demo={TextareaDemos.icon} />

## With right section

<Demo demo={TextareaDemos.rightsection} />

## Accessibility

Provide `aria-label` in case you use component without label for screen reader support:

```svelte
<Textarea /> // -> not ok, input is not labeled
<Textarea label="Comment" /> // -> ok, input and label are connected
<Textarea aria-label="Comment" /> // -> ok, label is not visible but will be announced by screen reader
```
3 changes: 2 additions & 1 deletion packages/svelteui-core/src/components/Input/Input.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HTMLInputAttributes, HTMLSelectAttributes, HTMLTextareaAttributes } from 'svelte/elements';
import { Component } from '$lib/internal';
import { DefaultProps, SvelteUISize, SvelteUINumberSize } from '$lib/styles';
import { DefaultProps, SvelteUISize, SvelteUINumberSize, CSS } from '$lib/styles';

export type InputVariant = 'default' | 'filled' | 'unstyled' | 'headless';

Expand All @@ -25,6 +25,7 @@ export interface InputBaseProps<T = string> extends DefaultProps<InputElementTyp
variant?: InputVariant;
disabled?: boolean;
size?: SvelteUISize;
resize?: CSS['resize'];
root?: Component | keyof HTMLElementTagNameMap;
value?: T;
}
Expand Down
6 changes: 4 additions & 2 deletions packages/svelteui-core/src/components/Input/Input.styles.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { createStyles } from '$lib/styles';
import type { Component } from '$lib/internal';
import type { SvelteUINumberSize, SvelteUISize } from '$lib/styles';
import type { CSS, SvelteUINumberSize, SvelteUISize } from '$lib/styles';
import type { InputVariant } from './Input';

export interface InputStylesParams {
icon: Component | HTMLOrSVGElement;
radius: SvelteUINumberSize;
size: SvelteUISize;
resize: CSS['resize'];
variant: InputVariant;
multiline: boolean;
invalid: boolean;
Expand Down Expand Up @@ -34,6 +35,7 @@ export default createStyles(
radius,
rightSectionWidth,
size,
resize,
variant,
showRightSection
}: InputStylesParams
Expand Down Expand Up @@ -61,7 +63,7 @@ export default createStyles(
WebkitTapHighlightColor: 'transparent',
lineHeight: multiline ? '$md' : `${sizes[size] - 2}px`,
appearance: 'none',
resize: 'none',
resize,
boxSizing: 'border-box',
fontSize: typeof size === 'number' ? `${size}px` : `${size}`,
width: '100%',
Expand Down
5 changes: 4 additions & 1 deletion packages/svelteui-core/src/components/Input/Input.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
autocomplete: $$Props['autocomplete'] = 'on',
type: $$Props['type'] = 'text',
placeholder: $$Props['placeholder'] = undefined,
autofocus: $$Props['autofocus'] = undefined;
autofocus: $$Props['autofocus'] = undefined,
resize: $$Props['resize'] = 'none';
export { className as class };
/** An action that forwards inner dom node events from parent component */
Expand Down Expand Up @@ -79,6 +80,7 @@
rightSectionWidth,
showRightSection,
size,
resize,
variant
},
{ name: 'Input' }
Expand Down Expand Up @@ -149,6 +151,7 @@ Base component to create custom inputs
{required}
{disabled}
{id}
{placeholder}
{autocomplete}
{type}
{autofocus}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts">
import { Meta, Story, Template } from '@storybook/addon-svelte-csf';
import { TextInput } from './index';
</script>

<Meta title="Components/TextInput" component={TextInput} />

<Template let:args>
<TextInput {...args} />
</Template>

<Story
name="Default"
id="textInputStory"
args={{
label: 'Full name'
}}
/>

<Story
name="Description"
id="textInputDescriptionStory"
args={{
label: 'Full name',
description: 'Tell us your name',
placeholder: 'Hanazono Yurine'
}}
/>

<Story
name="Error"
id="textInputErrorStory"
args={{
label: 'Full Name',
placeholder: 'Hanazono Yurine',
error: 'Something went wrong'
}}
/>
7 changes: 7 additions & 0 deletions packages/svelteui-core/src/components/Textarea/Textarea.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { DefaultProps } from '$lib/styles';
import { InputProps } from '../Input/Input';
import { InputWrapperProps } from '../InputWrapper/InputWrapper';

export type TextareaProps = Omit<InputProps, 'size' | 'type'> &
Omit<InputWrapperProps, 'size'> &
DefaultProps<HTMLTextAreaElement>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script lang="ts">
import { Meta, Story, Template } from '@storybook/addon-svelte-csf';
import { Textarea } from './index';
</script>

<Meta title="Components/Textarea" component={Textarea} />

<Template let:args>
<Textarea {...args} />
</Template>

<Story
name="Default"
id="textareaStory"
args={{
label: 'Your story',
placeholder: 'Once upon a time'
}}
/>

<Story
name="Description"
id="textareaDescriptionStory"
args={{
label: 'Your story',
description: 'Tell us about yourself',
placeholder: 'Once upon a time'
}}
/>

<Story
name="Resizeable"
id="textareaResizeableStory"
args={{
label: 'Your story',
placeholder: 'Once upon a time',
rows: 4,
resize: 'vertical'
}}
/>

<Story
name="Error"
id="textareaErrorStory"
args={{
label: 'Your story',
placeholder: 'Once upon a time',
error: "That's boring"
}}
/>
85 changes: 85 additions & 0 deletions packages/svelteui-core/src/components/Textarea/Textarea.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<script lang="ts">
import { get_current_component } from 'svelte/internal';
import { createEventForwarder, useActions } from '$lib/internal';
import { randomID } from '$lib/styles';
import { Input } from '../Input';
import { InputWrapper } from '../InputWrapper';
import type { TextareaProps as $$TextareaProps } from './Textarea';
interface $$Props extends $$TextareaProps {}
export let use: $$Props['use'] = [],
element: $$Props['element'] = undefined,
className: $$Props['className'] = '',
override: $$Props['override'] = {},
label: $$Props['label'] = '',
description: $$Props['description'] = null,
error: $$Props['error'] = null,
required: $$Props['required'] = false,
labelProps: $$Props['labelProps'] = {},
descriptionProps: $$Props['descriptionProps'] = {},
errorProps: $$Props['errorProps'] = {},
invalid: $$Props['invalid'] = false,
id: $$Props['id'] = 'input-id',
labelElement: $$Props['labelElement'] = 'label',
showRightSection: $$Props['showRightSection'] = undefined,
value: $$Props['value'] = '',
placeholder: $$Props['placeholder'] = '';
export { className as class };
/** An action that forwards inner dom node events from parent component */
const forwardEvents = createEventForwarder(get_current_component());
const baseId = randomID(id);
// Flag that enables the override of the right section slot
// of the Input component only if it was provided
const _showRightSection =
showRightSection === undefined ? !!$$slots.rightSection : showRightSection;
$: _invalid = invalid || !!error;
</script>

<!--
@component
Multiline text input.
@see https://svelteui.org/core/textarea
@example
```tsx
<Textarea
label='Comment'
description="Tell us what's on your mind"
placeholder='Blah blah blah'
required
/>
```
-->

<InputWrapper
bind:element
class={className}
{override}
{label}
{description}
{error}
{required}
{labelProps}
{descriptionProps}
{errorProps}
id={baseId}
{labelElement}
>
<Input
bind:value
{required}
id={baseId}
{placeholder}
{...$$restProps}
use={[forwardEvents, [useActions, use]]}
invalid={_invalid}
showRightSection={_showRightSection}
root="textarea"
multiline
>
<slot slot="rightSection" name="rightSection" />
</Input>
</InputWrapper>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { describe } from 'vitest';
import { itSupportsClassName } from '@svelteuidev/tests';

import { default as Textarea } from './Textarea.svelte';

describe('Textarea', () => {
itSupportsClassName(Textarea);
});
2 changes: 2 additions & 0 deletions packages/svelteui-core/src/components/Textarea/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Textarea } from './Textarea.svelte';
export type { TextareaProps } from './Textarea';
1 change: 1 addition & 0 deletions packages/svelteui-core/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export * from './Stack';
export * from './Switch';
export * from './Tabs';
export * from './Text';
export * from './Textarea';
export * from './TextInput';
export * from './ThemeIcon';
export * from './Timeline';
Expand Down
Loading

0 comments on commit 7760d9f

Please sign in to comment.