Merge pull request #29974 from backstage/canon-textfield-updates
Canon - Improve TextField
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': minor
|
||||
---
|
||||
|
||||
The icon prop in TextField now accept a ReactNode instead of an icon name. We also updated the icon sizes for each input sizes.
|
||||
@@ -33,9 +33,16 @@ A text field component for your forms.
|
||||
<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={`<TextField className="canon-TextField" />`}
|
||||
/>
|
||||
- `canon-TextField`
|
||||
- `canon-TextFieldLabel`
|
||||
- `canon-TextFieldRequired`
|
||||
- `canon-TextFieldInputWrapper`
|
||||
- `canon-TextFieldIcon`
|
||||
- `canon-TextFieldInput`
|
||||
- `canon-TextFieldClearButton`
|
||||
- `canon-TextFieldClearButtonIcon`
|
||||
- `canon-TextFieldDescription`
|
||||
- `canon-TextFieldError`
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
@@ -55,8 +62,8 @@ We support two different sizes: `small`, `medium`.
|
||||
open
|
||||
preview={<TextFieldSnippet story="Sizes" />}
|
||||
code={`<Flex direction="row" gap="4">
|
||||
<TextField size="small" placeholder="Small" />
|
||||
<TextField size="medium" placeholder="Medium" />
|
||||
<TextField size="small" placeholder="Small" icon={<Icon name="sparkling" />} />
|
||||
<TextField size="medium" placeholder="Medium" icon={<Icon name="sparkling" />} />
|
||||
</Flex>`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -5,12 +5,16 @@ export const inputPropDefs: Record<string, PropDef> = {
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['small', 'medium'],
|
||||
default: 'medium',
|
||||
responsive: false,
|
||||
default: 'small',
|
||||
responsive: true,
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
},
|
||||
icon: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
},
|
||||
|
||||
@@ -576,13 +576,20 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-TextFieldInputIcon {
|
||||
padding-right: var(--canon-space-1);
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
.canon-TextFieldIcon {
|
||||
margin-right: var(--canon-space-1);
|
||||
color: var(--canon-fg-primary);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="small"], .canon-TextFieldIcon[data-size="small"] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="medium"], .canon-TextFieldIcon[data-size="medium"] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput {
|
||||
|
||||
@@ -9800,13 +9800,20 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-TextFieldInputIcon {
|
||||
padding-right: var(--canon-space-1);
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
.canon-TextFieldIcon {
|
||||
margin-right: var(--canon-space-1);
|
||||
color: var(--canon-fg-primary);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="small"], .canon-TextFieldIcon[data-size="small"] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="medium"], .canon-TextFieldIcon[data-size="medium"] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput {
|
||||
|
||||
@@ -43,13 +43,20 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-TextFieldInputIcon {
|
||||
padding-right: var(--canon-space-1);
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
.canon-TextFieldIcon {
|
||||
margin-right: var(--canon-space-1);
|
||||
color: var(--canon-fg-primary);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="small"], .canon-TextFieldIcon[data-size="small"] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="medium"], .canon-TextFieldIcon[data-size="medium"] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ForwardRefExoticComponent } from 'react';
|
||||
import { HTMLAttributes } from 'react';
|
||||
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
||||
import { Menu as Menu_2 } from '@base-ui-components/react/menu';
|
||||
import type { MouseEventHandler } from 'react';
|
||||
import { ReactElement } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RefAttributes } from 'react';
|
||||
@@ -1220,10 +1221,10 @@ export interface TextFieldProps
|
||||
className?: string;
|
||||
description?: string;
|
||||
error?: string | null;
|
||||
icon?: IconNames;
|
||||
icon?: ReactNode;
|
||||
label?: string;
|
||||
name: string;
|
||||
onClear?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onClear?: MouseEventHandler<HTMLButtonElement>;
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { TextField } from './TextField';
|
||||
import { Flex } from '../Flex';
|
||||
import { Icon } from '../Icon';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/TextField',
|
||||
@@ -79,8 +80,8 @@ export const Sizes: Story = {
|
||||
},
|
||||
render: args => (
|
||||
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
|
||||
<TextField {...args} size="small" />
|
||||
<TextField {...args} size="medium" />
|
||||
<TextField {...args} size="small" icon={<Icon name="sparkling" />} />
|
||||
<TextField {...args} size="medium" icon={<Icon name="sparkling" />} />
|
||||
</Flex>
|
||||
),
|
||||
};
|
||||
@@ -114,7 +115,7 @@ export const WithIcon: Story = {
|
||||
args: {
|
||||
...WithLabel.args,
|
||||
placeholder: 'Search...',
|
||||
icon: 'search',
|
||||
icon: <Icon name="search" />,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -59,15 +59,24 @@
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
}
|
||||
|
||||
.canon-TextFieldInputIcon {
|
||||
display: block;
|
||||
padding-right: var(--canon-space-1);
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
.canon-TextFieldIcon {
|
||||
margin-right: var(--canon-space-1);
|
||||
color: var(--canon-fg-primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size='small'],
|
||||
.canon-TextFieldIcon[data-size='small'] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size='medium'],
|
||||
.canon-TextFieldIcon[data-size='medium'] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput {
|
||||
border: none;
|
||||
background: none;
|
||||
|
||||
@@ -27,7 +27,7 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
||||
(props: TextFieldProps, ref) => {
|
||||
const {
|
||||
className,
|
||||
size = 'medium',
|
||||
size = 'small',
|
||||
label,
|
||||
description,
|
||||
error,
|
||||
@@ -61,7 +61,15 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
||||
</Field.Label>
|
||||
)}
|
||||
<div className="canon-TextFieldInputWrapper" data-size={responsiveSize}>
|
||||
{icon && <Icon className="canon-TextFieldInputIcon" name={icon} />}
|
||||
{icon && (
|
||||
<div
|
||||
className="canon-TextFieldIcon"
|
||||
aria-hidden="true"
|
||||
data-size={responsiveSize}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
<Field.Control
|
||||
className="canon-TextFieldInput"
|
||||
required={required}
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { Breakpoint } from '../../types';
|
||||
import type { IconNames } from '../Icon';
|
||||
|
||||
import type { ReactNode, MouseEventHandler } from 'react';
|
||||
/** @public */
|
||||
export interface TextFieldProps
|
||||
extends Omit<React.ComponentPropsWithoutRef<'input'>, 'size'> {
|
||||
@@ -54,10 +53,10 @@ export interface TextFieldProps
|
||||
/**
|
||||
* An icon to render before the input
|
||||
*/
|
||||
icon?: IconNames;
|
||||
icon?: ReactNode;
|
||||
|
||||
/**
|
||||
* Handler to call when the clear button is pressed
|
||||
*/
|
||||
onClear?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onClear?: MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user