Skip to content

Commit

Permalink
[TextInput] Create new components
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks committed Oct 29, 2024
1 parent 7445aad commit a664c32
Show file tree
Hide file tree
Showing 23 changed files with 188 additions and 23 deletions.
14 changes: 14 additions & 0 deletions docs/data/api/text-field.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"props": {},
"name": "TextField",
"imports": ["import { TextField } from '@base_ui/react/TextField';"],
"classes": [],
"spread": true,
"themeDefaultProps": true,
"muiName": "TextField",
"forwardsRefTo": "HTMLInputElement",
"filename": "/packages/mui-base/src/TextField/TextField.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/react-text-field/\">Text Field</a></li></ul>",
"cssComponent": false
}
14 changes: 14 additions & 0 deletions docs/data/api/text-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"props": {},
"name": "TextInput",
"imports": ["import { TextInput } from '@base_ui/react/TextInput';"],
"classes": [],
"spread": true,
"themeDefaultProps": true,
"muiName": "TextInput",
"forwardsRefTo": "HTMLInputElement",
"filename": "/packages/mui-base/src/TextInput/TextInput.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/react-text-input/\">Text Input</a></li></ul>",
"cssComponent": false
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
'use client';
import * as React from 'react';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

export default function UnstyledFieldIntroduction() {
return (
<FieldRoot validate={(value) => (value === 'admin' ? 'Name not allowed' : null)}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Field.Label>Name</Field.Label>
<FieldControl required pattern="[a-zA-Z0-9]+" />
<Input required pattern="[a-zA-Z0-9]+" />
</div>
<Field.Validity>
{({ validity, value }) => {
Expand Down Expand Up @@ -40,7 +41,7 @@ const FieldRoot = styled(Field.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
'use client';
import * as React from 'react';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

export default function UnstyledFieldIntroduction() {
return (
<FieldRoot validate={(value) => (value === 'admin' ? 'Name not allowed' : null)}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Field.Label>Name</Field.Label>
<FieldControl required pattern="[a-zA-Z0-9]+" />
<Input required pattern="[a-zA-Z0-9]+" />
</div>
<Field.Validity>
{({ validity, value }) => {
Expand Down Expand Up @@ -40,7 +41,7 @@ const FieldRoot = styled(Field.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
5 changes: 3 additions & 2 deletions docs/data/components/field/UnstyledFieldPassword.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';
import * as React from 'react';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

function validate(value) {
Expand Down Expand Up @@ -31,7 +32,7 @@ export default function UnstyledFieldPassword() {
return (
<FieldRoot invalid={errors.length > 0}>
<Field.Label>Password</Field.Label>
<FieldControl
<Input
type="password"
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
Expand All @@ -51,7 +52,7 @@ const FieldRoot = styled(Field.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
5 changes: 3 additions & 2 deletions docs/data/components/field/UnstyledFieldPassword.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';
import * as React from 'react';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

function validate(value: string) {
Expand Down Expand Up @@ -31,7 +32,7 @@ export default function UnstyledFieldPassword() {
return (
<FieldRoot invalid={errors.length > 0}>
<Field.Label>Password</Field.Label>
<FieldControl
<Input
type="password"
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
Expand All @@ -51,7 +52,7 @@ const FieldRoot = styled(Field.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<FieldRoot invalid={errors.length > 0}>
<Field.Label>Password</Field.Label>
<FieldControl
<Input
type="password"
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
Expand Down
5 changes: 3 additions & 2 deletions docs/data/components/field/UnstyledFieldServerError.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';
import * as React from 'react';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

export default function UnstyledFieldServerError() {
Expand Down Expand Up @@ -40,7 +41,7 @@ export default function UnstyledFieldServerError() {
<form onSubmit={handleSubmit} noValidate>
<FieldRoot invalid={error} name="email">
<Field.Label>Email address</Field.Label>
<FieldControl
<Input
ref={controlRef}
type="email"
required
Expand Down Expand Up @@ -89,7 +90,7 @@ const FieldRoot = styled(Field.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
5 changes: 3 additions & 2 deletions docs/data/components/field/UnstyledFieldServerError.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';
import * as React from 'react';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

type Status = 'initial' | 'loading' | 'success' | 'error';
Expand Down Expand Up @@ -42,7 +43,7 @@ export default function UnstyledFieldServerError() {
<form onSubmit={handleSubmit} noValidate>
<FieldRoot invalid={error} name="email">
<Field.Label>Email address</Field.Label>
<FieldControl
<Input
ref={controlRef}
type="email"
required
Expand Down Expand Up @@ -91,7 +92,7 @@ const FieldRoot = styled(Field.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
4 changes: 2 additions & 2 deletions docs/data/components/field/field.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ packageName: '@base_ui/react'
Fields are implemented using a collection of related components:

- `<Field.Root />` is a top-level component that wraps all other components.
- `<Field.Control />` renders the control when not using a native Base UI input component.
- `<Field.Control />` renders a control when not using a native Base UI input component.
- `<Field.Label />` renders a label for the control.
- `<Field.Description />` renders an optional description for the control to provide additional information.
- `<Field.Error />` renders error messages for the control.
Expand Down Expand Up @@ -54,7 +54,7 @@ All Base UI input components are aware of Base UI's `Field` component. The lab
</Field.Root>
```

When using a native control like `input` or a custom component which is not aware of Base UI's `Field`, use `Field.Control`:
When using a custom component which is not aware of Base UI's `Field`, use `Field.Control`:

```jsx
<Field.Root>
Expand Down
7 changes: 4 additions & 3 deletions docs/data/components/form/FormIntroduction/system/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react';
import { Form } from '@base_ui/react/Form';
import { Fieldset } from '@base_ui/react/Fieldset';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

export default function FormIntroduction() {
Expand Down Expand Up @@ -53,12 +54,12 @@ export default function FormIntroduction() {
</p>
<Field.Root name="username">
<Field.Label>Username</Field.Label>
<FieldControl required />
<Input required />
<FieldError />
</Field.Root>
<Field.Root name="password">
<Field.Label>Password</Field.Label>
<FieldControl type="password" required />
<Input type="password" required />
<FieldError />
</Field.Root>
</FieldsetRoot>
Expand All @@ -78,7 +79,7 @@ const FormRoot = styled(Form.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
7 changes: 4 additions & 3 deletions docs/data/components/form/FormIntroduction/system/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react';
import { Form } from '@base_ui/react/Form';
import { Fieldset } from '@base_ui/react/Fieldset';
import { Field } from '@base_ui/react/Field';
import { TextInput } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

type Status = 'initial' | 'loading' | 'success' | 'error';
Expand Down Expand Up @@ -55,12 +56,12 @@ export default function FormIntroduction() {
</p>
<Field.Root name="username">
<Field.Label>Username</Field.Label>
<FieldControl required />
<Input required />
<FieldError />
</Field.Root>
<Field.Root name="password">
<Field.Label>Password</Field.Label>
<FieldControl type="password" required />
<Input type="password" required />
<FieldError />
</Field.Root>
</FieldsetRoot>
Expand All @@ -80,7 +81,7 @@ const FormRoot = styled(Form.Root)`
width: 275px;
`;

const FieldControl = styled(Field.Control)`
const Input = styled(TextInput)`
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client';
import * as React from 'react';
import { TextInput as TextInputPrimitive } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

export default function TextFieldIntroduction() {
return <TextInput />;
}

const TextInput = styled(TextInputPrimitive)`
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
font-size: 16px;
width: 200px;
&:focus {
outline: 0;
border-color: #0070f3;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client';
import * as React from 'react';
import { TextInput as TextInputPrimitive } from '@base_ui/react/TextInput';
import { styled } from '@mui/system';

export default function TextFieldIntroduction() {
return <TextInput />;
}

const TextInput = styled(TextInputPrimitive)`
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
font-size: 16px;
width: 200px;
&:focus {
outline: 0;
border-color: #0070f3;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<TextInput />
28 changes: 28 additions & 0 deletions docs/data/components/text-input/text-field.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
productId: base-ui
title: React TextInput component
description: Text Input is a UI element that lets users input single line text.
components: TextInput
githubLabel: 'component: text-input'
packageName: '@base_ui/react'
---

# Text Input

<Description />

<ComponentLinkHeader design={false} />

<Demo demo="TextInputIntroduction" defaultCodeOpen="false" bg="gradient" />

## Installation

<InstallationInstructions componentName="TextInput" />

## Anatomy

`<TextInput />` renders an `<input>`, enhanced with field context when placed inside a [`Field`](/components/react-field/).

```tsx
<TextInput />
```
1 change: 1 addition & 0 deletions docs/data/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const pages: readonly RouteMetadata[] = [
{ pathname: '/components/react-slider', title: 'Slider' },
{ pathname: '/components/react-switch', title: 'Switch' },
{ pathname: '/components/react-tabs', title: 'Tabs' },
{ pathname: '/components/react-text-field', title: 'Text Field' },
{ pathname: '/components/react-tooltip', title: 'Tooltip' },
],
},
Expand Down
1 change: 1 addition & 0 deletions docs/data/translations/api-docs/text-input/text-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "componentDescription": "", "propDescriptions": {}, "classDescriptions": {} }
4 changes: 2 additions & 2 deletions packages/mui-base/src/Field/Control/FieldControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const FieldControl = React.forwardRef(function FieldControl(
ownerState: fieldOwnerState,
name: fieldName,
disabled: fieldDisabled,
} = useFieldRootContext(false);
} = useFieldRootContext();

const disabled = fieldDisabled || disabledProp;
const name = fieldName ?? nameProp;
Expand Down Expand Up @@ -77,7 +77,7 @@ const FieldControl = React.forwardRef(function FieldControl(
namespace FieldControl {
export type OwnerState = FieldRoot.OwnerState;

export interface Props extends BaseUIComponentProps<'input', OwnerState> {
export interface Props extends BaseUIComponentProps<'input' | 'textarea' | 'select', OwnerState> {
/**
* Callback fired when the `value` changes. Use when controlled.
*/
Expand Down
13 changes: 13 additions & 0 deletions packages/mui-base/src/TextInput/TextInput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from 'react';
import { TextInput } from '@base_ui/react/TextInput';
import { createRenderer } from '@mui/internal-test-utils';
import { describeConformance } from '../../test/describeConformance';

describe('<TextInput />', () => {
const { render } = createRenderer();

describeConformance(<TextInput />, () => ({
refInstanceof: window.HTMLInputElement,
render,
}));
});
Loading

0 comments on commit a664c32

Please sign in to comment.