Add new FormInput component
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { FormInput } from './FormInput';
|
||||
import { Icon } from '../Icon';
|
||||
import { Flex } from '../Flex';
|
||||
|
||||
const meta = {
|
||||
title: 'Forms/FormInput',
|
||||
component: FormInput,
|
||||
argTypes: {
|
||||
required: {
|
||||
control: 'boolean',
|
||||
},
|
||||
icon: {
|
||||
control: 'object',
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof FormInput>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
name: 'url',
|
||||
placeholder: 'Enter a URL',
|
||||
style: {
|
||||
maxWidth: '300px',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Sizes: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
},
|
||||
render: args => (
|
||||
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
|
||||
<FormInput {...args} size="small" icon={<Icon name="sparkling" />} />
|
||||
<FormInput {...args} size="medium" icon={<Icon name="sparkling" />} />
|
||||
</Flex>
|
||||
),
|
||||
};
|
||||
|
||||
export const Filled: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
defaultValue: 'https://example.com',
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithIcon: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
placeholder: 'Search...',
|
||||
icon: <Icon name="search" />,
|
||||
},
|
||||
};
|
||||
|
||||
export const DisabledWithIcon: Story = {
|
||||
args: {
|
||||
...WithIcon.args,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.canon-FormInput {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 var(--canon-space-3);
|
||||
border-radius: var(--canon-radius-3);
|
||||
border: 1px solid var(--canon-border);
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
}
|
||||
|
||||
.canon-FormInputIcon {
|
||||
margin-right: var(--canon-space-1);
|
||||
color: var(--canon-fg-primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.canon-FormInputIcon[data-size='small'],
|
||||
.canon-FormInputIcon[data-size='small'] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-FormInputIcon[data-size='medium'],
|
||||
.canon-FormInputIcon[data-size='medium'] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.canon-Input {
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-primary);
|
||||
transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: inherit;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.canon-Input::placeholder {
|
||||
color: var(--canon-fg-secondary);
|
||||
}
|
||||
|
||||
.canon-FormInput[data-size='small'] {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.canon-FormInput[data-size='medium'] {
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.canon-Input[data-focused] {
|
||||
outline-color: var(--canon-border-pressed);
|
||||
outline-width: 0px;
|
||||
}
|
||||
|
||||
.canon-FormInput:has(> .canon-Input:hover) {
|
||||
border-color: var(--canon-border-hover);
|
||||
}
|
||||
|
||||
.canon-FormInput[data-focused] {
|
||||
border-color: var(--canon-border-pressed);
|
||||
}
|
||||
|
||||
.canon-FormInput[data-invalid] {
|
||||
border-color: var(--canon-fg-danger);
|
||||
}
|
||||
|
||||
.canon-FormInput[data-disabled] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
border: 1px solid var(--canon-border-disabled);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import { Input } from 'react-aria-components';
|
||||
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import type { FormInputProps } from './types';
|
||||
|
||||
/** @public */
|
||||
export const FormInput = forwardRef<HTMLInputElement, FormInputProps>(
|
||||
(props: FormInputProps, ref) => {
|
||||
const { className, icon, size = 'small', ...rest } = props;
|
||||
|
||||
// Get the responsive value for the variant
|
||||
const responsiveSize = useResponsiveValue(size);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx('canon-FormInput', className)}
|
||||
data-size={responsiveSize}
|
||||
>
|
||||
{icon && (
|
||||
<div className="canon-FormInputIcon" aria-hidden="true">
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
<Input className="canon-Input" {...rest} ref={ref} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
FormInput.displayName = 'FormInput';
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './TextField';
|
||||
export type { TextFieldProps } from './types';
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { InputProps } from 'react-aria-components';
|
||||
import { ReactNode } from 'react';
|
||||
import type { Breakpoint } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export interface FormInputProps extends Omit<InputProps, 'size'> {
|
||||
/**
|
||||
* An icon to render before the input
|
||||
*/
|
||||
icon?: ReactNode;
|
||||
|
||||
/**
|
||||
* The size of the text field
|
||||
* @defaultValue 'medium'
|
||||
*/
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
@import '../components/DataTable/Root/DataTableRoot.styles.css';
|
||||
@import '../components/DataTable/Pagination/DataTablePagination.styles.css';
|
||||
@import '../components/Flex/styles.css';
|
||||
@import '../components/FormInput/FormInput.styles.css';
|
||||
@import '../components/Grid/styles.css';
|
||||
@import '../components/Container/styles.css';
|
||||
@import '../components/Icon/styles.css';
|
||||
|
||||
Reference in New Issue
Block a user