Revert "Fix docs on TextField"

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-04-03 11:37:27 +01:00
parent b04552ab5b
commit 293d08cd25
8 changed files with 212 additions and 45 deletions
@@ -0,0 +1,99 @@
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>`}
/>
@@ -0,0 +1,69 @@
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,
};
@@ -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 { TextFieldSnippet } from '@/snippets/stories-snippets';
import { InputSnippet } from '@/snippets/stories-snippets';
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
import { inputPropDefs } from './props';
# TextField
# Input
A text field component for your forms.
A input component tfor your forms.
<Snippet
align="center"
py={4}
preview={<TextFieldSnippet story="WithLabel" />}
code={`<TextField label="Label" placeholder="Enter a URL" />`}
preview={<InputSnippet story="Primary" />}
code={`<Input label="Name" placeholder="Enter your name" />`}
/>
<Tabs.Root>
@@ -24,9 +24,9 @@ A text field component for your forms.
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { TextField } from '@backstage/canon';
code={`import { Input } from '@backstage/canon';
<TextField />
<Input />
`}
/>
</Tabs.Panel>
@@ -34,40 +34,30 @@ A text field component for 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={`<TextField className="canon-TextField" />`}
code={`<Input className="canon-Input" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<BaseUI href="https://base-ui.com/react/components/input" />
<PropsTable data={inputPropDefs} />
## Examples
### Sizes
We support two different sizes: `small`, `medium`.
Here's a simple input with a label and description.
<Snippet
align="center"
py={4}
open
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" />`}
preview={<InputSnippet story="Sizes" />}
code={`<Grid>
<Input size="sm" placeholder="Small" />
<Input size="md" placeholder="Medium" />
</Grid>`}
/>
+16 -4
View File
@@ -5,11 +5,12 @@ 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 TextFieldStories from '../../../packages/canon/src/components/TextField/TextField.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 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';
@@ -61,12 +62,12 @@ export const FlexSnippet = ({ story }: { story: keyof typeof FlexStories }) => {
return StoryComponent ? <StoryComponent /> : null;
};
export const TextFieldSnippet = ({
export const FieldSnippet = ({
story,
}: {
story: keyof typeof TextFieldStories;
story: keyof typeof FieldStories;
}) => {
const stories = composeStories(TextFieldStories);
const stories = composeStories(FieldStories);
const StoryComponent = stories[story as keyof typeof stories];
return StoryComponent ? <StoryComponent /> : null;
@@ -108,6 +109,17 @@ 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];
+10 -5
View File
@@ -76,6 +76,11 @@ export const components: Page[] = [
slug: 'checkbox',
status: 'alpha',
},
{
title: 'Field',
slug: 'field',
status: 'alpha',
},
{
title: 'Heading',
slug: 'heading',
@@ -91,6 +96,11 @@ export const components: Page[] = [
slug: 'icon-button',
status: 'alpha',
},
{
title: 'Input',
slug: 'input',
status: 'alpha',
},
{
title: 'Select',
slug: 'select',
@@ -106,11 +116,6 @@ export const components: Page[] = [
slug: 'text',
status: 'alpha',
},
{
title: 'TextField',
slug: 'text-field',
status: 'alpha',
},
];
export type ScreenSize = {
@@ -33,9 +33,6 @@ export const Default: Story = {
args: {
name: 'url',
placeholder: 'Enter a URL',
style: {
maxWidth: '300px',
},
},
};
@@ -74,7 +71,7 @@ export const Sizes: Story = {
description: 'Description',
},
render: args => (
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
<Flex direction="row" gap="4">
<TextField {...args} size="small" />
<TextField {...args} size="medium" />
</Flex>
@@ -31,7 +31,6 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
label,
description,
name,
style,
...rest
} = props;
@@ -39,11 +38,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
const responsiveSize = useResponsiveValue(size);
return (
<Field.Root
className={clsx('canon-FieldRoot', className)}
name={name}
style={style}
>
<Field.Root className={clsx('canon-FieldRoot', className)} name={name}>
{label && (
<Field.Label className="canon-FieldLabel">{label}</Field.Label>
)}