Add TextField docs
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -1,99 +0,0 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { FieldSnippet } from '@/snippets/stories-snippets';
|
||||
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
|
||||
import {
|
||||
fieldRootPropDefs,
|
||||
fieldLabelPropDefs,
|
||||
fieldDescriptionPropDefs,
|
||||
fieldErrorPropDefs,
|
||||
} from './props';
|
||||
|
||||
# Field
|
||||
|
||||
A wrapper around `Input` or `Select` component to add label, description and error messages..
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<FieldSnippet story="Default" />}
|
||||
code={`<Field />`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
<Tabs.Tab>Theming</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Field } from '@backstage/canon';
|
||||
|
||||
<Field.Root>
|
||||
<Field.Label>Name</Field.Label>
|
||||
<Input placeholder="Enter your name" />
|
||||
<Field.Description>Visible on your profile</Field.Description>
|
||||
<Field.Error />
|
||||
</Field.Root>`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel>
|
||||
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
|
||||
your brand. For additional flexibility, you can use the provided class names for each element listed below.
|
||||
<CodeBlock
|
||||
code={`<Field.Root className='canon-FieldRoot'>
|
||||
<Field.Label className='canon-FieldLabel' />
|
||||
<Field.Description className='canon-FieldDescription' />
|
||||
<Field.Error className='canon-FieldError' />
|
||||
</Field.Root>`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<BaseUI href="https://base-ui.com/react/components/field" />
|
||||
|
||||
### Field.Root
|
||||
|
||||
Groups all parts of the field. Renders a `<div>` element.
|
||||
|
||||
<PropsTable data={fieldRootPropDefs} />
|
||||
|
||||
### Field.Label
|
||||
|
||||
An accessible label that is automatically associated with the field control. Renders a `<label>` element.
|
||||
|
||||
<PropsTable data={fieldLabelPropDefs} />
|
||||
|
||||
### Field.Description
|
||||
|
||||
A paragraph with additional information about the field. Renders a `<p>` element.
|
||||
|
||||
<PropsTable data={fieldDescriptionPropDefs} />
|
||||
|
||||
### Field.Error
|
||||
|
||||
An error message displayed if the field control fails validation. Renders a `<div>` element.
|
||||
|
||||
<PropsTable data={fieldErrorPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### With Label and Description
|
||||
|
||||
Here's a simple input with a label and description.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<FieldSnippet story="WithLabelAndDescription" />}
|
||||
code={`<Field>
|
||||
<Field.Label>Name</Field.Label>
|
||||
<Field.Description>Visible on your profile</Field.Description>
|
||||
<Input placeholder="Enter your name" />
|
||||
</Field>`}
|
||||
/>
|
||||
@@ -1,69 +0,0 @@
|
||||
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
|
||||
import type { PropDef } from '../../../../utils/propDefs';
|
||||
|
||||
export const fieldRootPropDefs: Record<string, PropDef> = {
|
||||
name: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
disabled: {
|
||||
type: 'boolean',
|
||||
responsive: false,
|
||||
},
|
||||
invalid: {
|
||||
type: 'boolean',
|
||||
responsive: false,
|
||||
},
|
||||
validate: {
|
||||
type: 'enum',
|
||||
values: ['(value) => string | string[] | null | Promise'],
|
||||
responsive: false,
|
||||
},
|
||||
validationMode: {
|
||||
type: 'enum',
|
||||
values: ['onBlur', 'onChange'],
|
||||
responsive: false,
|
||||
},
|
||||
validationDebounceTime: {
|
||||
type: 'number',
|
||||
responsive: false,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const fieldLabelPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const fieldDescriptionPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const fieldErrorPropDefs: Record<string, PropDef> = {
|
||||
match: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'badInput',
|
||||
'customError',
|
||||
'patternMismatch',
|
||||
'rangeOverflow',
|
||||
'rangeUnderflow',
|
||||
'stepMismatch',
|
||||
'tooLong',
|
||||
'tooShort',
|
||||
'typeMismatch',
|
||||
'valid',
|
||||
'valueMissing',
|
||||
],
|
||||
responsive: false,
|
||||
},
|
||||
forceShow: {
|
||||
type: 'boolean',
|
||||
responsive: false,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
+26
-16
@@ -2,19 +2,19 @@ import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { InputSnippet } from '@/snippets/stories-snippets';
|
||||
import { TextFieldSnippet } from '@/snippets/stories-snippets';
|
||||
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
|
||||
import { inputPropDefs } from './props';
|
||||
|
||||
# Input
|
||||
# TextField
|
||||
|
||||
A input component tfor your forms.
|
||||
A text field component for your forms.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<InputSnippet story="Primary" />}
|
||||
code={`<Input label="Name" placeholder="Enter your name" />`}
|
||||
preview={<TextFieldSnippet story="WithLabel" />}
|
||||
code={`<TextField label="Label" placeholder="Enter a URL" />`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
@@ -24,9 +24,9 @@ A input component tfor your forms.
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Input } from '@backstage/canon';
|
||||
code={`import { TextField } from '@backstage/canon';
|
||||
|
||||
<Input />
|
||||
<TextField />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
@@ -34,30 +34,40 @@ A input component tfor your forms.
|
||||
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
|
||||
your brand. For additional flexibility, you can use the provided class names for each element listed below.
|
||||
<CodeBlock
|
||||
code={`<Input className="canon-Input" />`}
|
||||
code={`<TextField className="canon-TextField" />`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<BaseUI href="https://base-ui.com/react/components/input" />
|
||||
|
||||
<PropsTable data={inputPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
Here's a simple input with a label and description.
|
||||
We support two different sizes: `small`, `medium`.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<InputSnippet story="Sizes" />}
|
||||
code={`<Grid>
|
||||
<Input size="sm" placeholder="Small" />
|
||||
<Input size="md" placeholder="Medium" />
|
||||
</Grid>`}
|
||||
preview={<TextFieldSnippet story="Sizes" />}
|
||||
code={`<Flex direction="row" gap="4">
|
||||
<TextField size="small" placeholder="Small" />
|
||||
<TextField size="medium" placeholder="Medium" />
|
||||
</Flex>`}
|
||||
/>
|
||||
|
||||
### With description
|
||||
|
||||
Here's a simple TextField with a description.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<TextFieldSnippet story="WithDescription" />}
|
||||
code={`<TextField label="Label" description="Description" placeholder="Enter a URL" />`}
|
||||
/>
|
||||
+12
-2
@@ -4,10 +4,20 @@ import type { PropDef } from '../../../../utils/propDefs';
|
||||
export const inputPropDefs: Record<string, PropDef> = {
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['sm', 'md'],
|
||||
default: 'md',
|
||||
values: ['small', 'medium'],
|
||||
default: 'medium',
|
||||
responsive: false,
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -5,12 +5,11 @@ import * as BoxStories from '../../../packages/canon/src/components/Box/Box.stor
|
||||
import * as ButtonStories from '../../../packages/canon/src/components/Button/Button.stories';
|
||||
import * as CheckboxStories from '../../../packages/canon/src/components/Checkbox/Checkbox.stories';
|
||||
import * as ContainerStories from '../../../packages/canon/src/components/Container/Container.stories';
|
||||
import * as FieldStories from '../../../packages/canon/src/components/Field/Field.stories';
|
||||
import * as GridStories from '../../../packages/canon/src/components/Grid/Grid.stories';
|
||||
import * as HeadingStories from '../../../packages/canon/src/components/Heading/Heading.stories';
|
||||
import * as IconButtonStories from '../../../packages/canon/src/components/IconButton/IconButton.stories';
|
||||
import * as IconStories from '../../../packages/canon/src/components/Icon/Icon.stories';
|
||||
import * as InputStories from '../../../packages/canon/src/components/Input/Input.stories';
|
||||
import * as TextFieldStories from '../../../packages/canon/src/components/TextField/TextField.stories';
|
||||
import * as TextStories from '../../../packages/canon/src/components/Text/Text.stories';
|
||||
import * as FlexStories from '../../../packages/canon/src/components/Flex/Flex.stories';
|
||||
import * as SelectStories from '../../../packages/canon/src/components/Select/Select.stories';
|
||||
@@ -62,12 +61,12 @@ export const FlexSnippet = ({ story }: { story: keyof typeof FlexStories }) => {
|
||||
return StoryComponent ? <StoryComponent /> : null;
|
||||
};
|
||||
|
||||
export const FieldSnippet = ({
|
||||
export const TextFieldSnippet = ({
|
||||
story,
|
||||
}: {
|
||||
story: keyof typeof FieldStories;
|
||||
story: keyof typeof TextFieldStories;
|
||||
}) => {
|
||||
const stories = composeStories(FieldStories);
|
||||
const stories = composeStories(TextFieldStories);
|
||||
const StoryComponent = stories[story as keyof typeof stories];
|
||||
|
||||
return StoryComponent ? <StoryComponent /> : null;
|
||||
@@ -109,17 +108,6 @@ export const IconSnippet = ({ story }: { story: keyof typeof IconStories }) => {
|
||||
return StoryComponent ? <StoryComponent /> : null;
|
||||
};
|
||||
|
||||
export const InputSnippet = ({
|
||||
story,
|
||||
}: {
|
||||
story: keyof typeof InputStories;
|
||||
}) => {
|
||||
const stories = composeStories(InputStories);
|
||||
const StoryComponent = stories[story as keyof typeof stories];
|
||||
|
||||
return StoryComponent ? <StoryComponent /> : null;
|
||||
};
|
||||
|
||||
export const TextSnippet = ({ story }: { story: keyof typeof TextStories }) => {
|
||||
const stories = composeStories(TextStories);
|
||||
const StoryComponent = stories[story as keyof typeof stories];
|
||||
|
||||
@@ -33,6 +33,9 @@ export const Default: Story = {
|
||||
args: {
|
||||
name: 'url',
|
||||
placeholder: 'Enter a URL',
|
||||
style: {
|
||||
maxWidth: '300px',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -71,7 +74,7 @@ export const Sizes: Story = {
|
||||
description: 'Description',
|
||||
},
|
||||
render: args => (
|
||||
<Flex direction="row" gap="4">
|
||||
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
|
||||
<TextField {...args} size="small" />
|
||||
<TextField {...args} size="medium" />
|
||||
</Flex>
|
||||
|
||||
@@ -31,6 +31,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
||||
label,
|
||||
description,
|
||||
name,
|
||||
style,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@@ -38,7 +39,11 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
||||
const responsiveSize = useResponsiveValue(size);
|
||||
|
||||
return (
|
||||
<Field.Root className={clsx('canon-FieldRoot', className)} name={name}>
|
||||
<Field.Root
|
||||
className={clsx('canon-FieldRoot', className)}
|
||||
name={name}
|
||||
style={style}
|
||||
>
|
||||
{label && (
|
||||
<Field.Label className="canon-FieldLabel">{label}</Field.Label>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user