diff --git a/canon-docs/public/components.css b/canon-docs/public/components.css
index 8889d8c978..e1cd4c9fad 100644
--- a/canon-docs/public/components.css
+++ b/canon-docs/public/components.css
@@ -281,6 +281,7 @@
.canon-input-root {
font-family: var(--canon-font-regular);
flex-direction: column;
+ width: 100%;
display: flex;
}
diff --git a/canon-docs/src/app/(docs)/components/input/page.mdx b/canon-docs/src/app/(docs)/components/input/page.mdx
new file mode 100644
index 0000000000..49cb7bd08c
--- /dev/null
+++ b/canon-docs/src/app/(docs)/components/input/page.mdx
@@ -0,0 +1,126 @@
+import { PropsTable } from '@/components/PropsTable';
+import { Snippet } from '@/components/Snippet';
+import { Tabs } from '@/components/Tabs';
+import { CodeBlock } from '@/components/CodeBlock';
+import {
+ InputPreview,
+ InputLabelAndDescription,
+ InputError,
+} from '@/snippets/input';
+import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
+
+# Input
+
+A input component tfor your forms.
+
+}
+ code={``}
+/>
+
+
+
+ Usage
+ Theming
+
+
+
+`}
+ />
+
+
+ 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.
+ `}
+ />
+
+
+
+## API reference
+
+
+
+
+
+## Examples
+
+### With label and description
+
+Here's a simple input with a label and description.
+
+}
+ code={``}
+/>
+
+### Error
+
+Here's a simple input with an error message.
+
+}
+ code={``}
+/>
diff --git a/canon-docs/src/app/(playground)/playground/page.tsx b/canon-docs/src/app/(playground)/playground/page.tsx
index c36a1d3492..9478160517 100644
--- a/canon-docs/src/app/(playground)/playground/page.tsx
+++ b/canon-docs/src/app/(playground)/playground/page.tsx
@@ -9,6 +9,7 @@ import { ButtonPlayground } from '@/snippets/button';
import { CheckboxPlayground } from '@/snippets/checkbox';
import { HeadingPlayground } from '@/snippets/heading';
import { TextPlayground } from '@/snippets/text';
+import { InputPlayground } from '@/snippets/input';
import styles from './styles.module.css';
@@ -66,6 +67,9 @@ const Content = () => {
{selectedComponents.find(c => c === 'text') && (
} title="Text" />
)}
+ {selectedComponents.find(c => c === 'input') && (
+ } title="Input" />
+ )}
);
};
diff --git a/canon-docs/src/snippets/input.tsx b/canon-docs/src/snippets/input.tsx
new file mode 100644
index 0000000000..85db7cde19
--- /dev/null
+++ b/canon-docs/src/snippets/input.tsx
@@ -0,0 +1,38 @@
+import { Input } from '@backstage/canon';
+
+export const InputPreview = () => {
+ return (
+
+
+
+ );
+};
+
+export const InputLabelAndDescription = () => {
+ return (
+
+
+
+ );
+};
+
+export const InputError = () => {
+ return (
+
+
+
+ );
+};
+
+export const InputPlayground = () => {
+ return (
+
+
+
+ );
+};
diff --git a/canon-docs/src/utils/data.ts b/canon-docs/src/utils/data.ts
index f99ef2dbe8..41ae4c0536 100644
--- a/canon-docs/src/utils/data.ts
+++ b/canon-docs/src/utils/data.ts
@@ -70,12 +70,12 @@ export const components: Page[] = [
{
title: 'Button',
slug: 'button',
- status: 'inProgress',
+ status: 'alpha',
},
{
title: 'Checkbox',
slug: 'checkbox',
- status: 'inProgress',
+ status: 'alpha',
},
{
title: 'Heading',
@@ -87,6 +87,11 @@ export const components: Page[] = [
slug: 'icon',
status: 'alpha',
},
+ {
+ title: 'Input',
+ slug: 'input',
+ status: 'alpha',
+ },
{
title: 'Table',
slug: 'table',
diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md
index fc1f773b4b..5086c16ce0 100644
--- a/packages/canon/report.api.md
+++ b/packages/canon/report.api.md
@@ -6,6 +6,7 @@
///
import type { CSSProperties } from 'react';
+import { Field } from '@base-ui-components/react/field';
import { ForwardRefExoticComponent } from 'react';
import { default as React_2 } from 'react';
import * as React_3 from 'react';
@@ -323,6 +324,13 @@ export interface InlineProps extends SpaceProps {
style?: React.CSSProperties;
}
+// Warning: (ae-forgotten-export) The symbol "InputProps" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+export const Input: React_2.ForwardRefExoticComponent<
+ InputProps & React_2.RefAttributes
+>;
+
// @public (undocumented)
export type JustifyContent =
| 'stretch'
diff --git a/packages/canon/src/components/Input/Input.stories.tsx b/packages/canon/src/components/Input/Input.stories.tsx
index 140696e0f5..a3f2f59581 100644
--- a/packages/canon/src/components/Input/Input.stories.tsx
+++ b/packages/canon/src/components/Input/Input.stories.tsx
@@ -89,13 +89,26 @@ export const Sizes: Story = {
),
};
-export const WithError: Story = {
+export const WithErrorValidation: Story = {
args: {
- invalid: true,
- validate: value => (value ? null : 'Please enter your name'),
+ validate: value =>
+ value !== 'Charles' ? 'Please enter a different name' : null,
required: true,
label: 'Name',
description: 'Visible on your profile',
- error: 'Please enter your name',
+ validationMode: 'onChange',
+ errorMatch: 'customError',
+ },
+};
+
+export const WithInvalidProp: Story = {
+ args: {
+ invalid: true,
+ required: true,
+ label: 'Name',
+ description: 'Visible on your profile',
+ validationMode: 'onChange',
+ errorForceShow: true,
+ validate: () => 'Stuff',
},
};
diff --git a/packages/canon/src/components/Input/Input.tsx b/packages/canon/src/components/Input/Input.tsx
index e62301c5cf..688ecc5c2d 100644
--- a/packages/canon/src/components/Input/Input.tsx
+++ b/packages/canon/src/components/Input/Input.tsx
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+'use client';
+
import React, { forwardRef } from 'react';
import clsx from 'clsx';
import { Field } from '@base-ui-components/react/field';
@@ -25,12 +27,14 @@ const Input = forwardRef((props, ref) => {
className,
label,
description,
- match = 'valueMissing',
+ errorMatch = 'customError',
+ errorForceShow = false,
+ errorMessage = 'An error occurred',
validationMode = 'onChange',
size = 'md',
placeholder,
- error = 'Please enter your a value',
required = false,
+ style,
...rest
} = props;
@@ -38,6 +42,7 @@ const Input = forwardRef((props, ref) => {
@@ -54,9 +59,12 @@ const Input = forwardRef((props, ref) => {
})}
/>
-
- {error}
-
+
{description && (
diff --git a/packages/canon/src/components/Input/styles.css b/packages/canon/src/components/Input/styles.css
index f8baae4ff4..ef4cfa2a07 100644
--- a/packages/canon/src/components/Input/styles.css
+++ b/packages/canon/src/components/Input/styles.css
@@ -18,6 +18,7 @@
display: flex;
flex-direction: column;
font-family: var(--canon-font-regular);
+ width: 100%;
}
.canon-input-label {
diff --git a/packages/canon/src/components/Input/types.ts b/packages/canon/src/components/Input/types.ts
index c4ebff83b5..f268273632 100644
--- a/packages/canon/src/components/Input/types.ts
+++ b/packages/canon/src/components/Input/types.ts
@@ -16,13 +16,15 @@
import { Field } from '@base-ui-components/react/field';
+/** @public */
export interface InputProps
extends React.ComponentPropsWithoutRef {
label?: string;
description?: string;
size?: 'sm' | 'md';
placeholder?: string;
- match?: Field.Error.Props['match'];
- error?: string;
+ errorMatch?: Field.Error.Props['match'];
+ errorForceShow?: boolean;
+ errorMessage?: string;
required?: boolean;
}
diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts
index f6cca4bed6..04f29c4b77 100644
--- a/packages/canon/src/index.ts
+++ b/packages/canon/src/index.ts
@@ -37,6 +37,7 @@ export * from './components/Button';
export * from './components/Icon';
export * from './components/Checkbox';
export * from './components/Table';
+export * from './components/Input';
// Types
export * from './types';