Browse Source

Update text controls

Add length prop for size.
master
TheoryOfNekomata 11 months ago
parent
commit
26a43a6a98
12 changed files with 131 additions and 24 deletions
  1. +7
    -0
      categories/choice/react/src/components/ComboBox/index.tsx
  2. +22
    -1
      categories/formatted/react/src/components/EmailInput/index.tsx
  3. +22
    -22
      categories/formatted/react/src/components/PatternTextInput/PatternTextInput.test.tsx
  4. +11
    -1
      categories/formatted/react/src/components/PatternTextInput/index.tsx
  5. +8
    -0
      categories/formatted/react/src/components/PhoneNumberInput/index.tsx
  6. +9
    -0
      categories/formatted/react/src/components/UrlInput/index.tsx
  7. +7
    -0
      categories/freeform/react/src/components/MaskedTextInput/index.tsx
  8. +7
    -0
      categories/freeform/react/src/components/TextInput/index.tsx
  9. +4
    -0
      categories/multichoice/react/src/components/TagInput/index.tsx
  10. +7
    -0
      categories/number/react/src/components/NumberSpinner/index.tsx
  11. +7
    -0
      categories/temporal/react/src/components/DateDropdown/index.tsx
  12. +20
    -0
      showcases/web-kitchensink-reactnext/src/pages/categories/formatted/index.tsx

+ 7
- 0
categories/choice/react/src/components/ComboBox/index.tsx View File

@@ -51,6 +51,10 @@ export interface ComboBoxProps extends Omit<React.HTMLProps<ComboBoxDerivedEleme
* Input mode of the component.
*/
inputMode?: TextControl.InputMode,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -74,6 +78,7 @@ export const ComboBox = React.forwardRef<ComboBoxDerivedElement, ComboBoxProps>(
inputMode = 'text' as const,
id: idProp,
style,
length,
...etcProps
}: ComboBoxProps,
forwardedRef,
@@ -112,6 +117,7 @@ export const ComboBox = React.forwardRef<ComboBoxDerivedElement, ComboBoxProps>(
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
id={id}
aria-labelledby={labelId}
@@ -245,4 +251,5 @@ ComboBox.defaultProps = {
variant: 'default' as const,
hiddenLabel: false as const,
inputMode: 'text' as const,
length: undefined,
};

+ 22
- 1
categories/formatted/react/src/components/EmailInput/index.tsx View File

@@ -4,7 +4,7 @@ import clsx from 'clsx';

export type EmailInputDerivedElement = HTMLInputElement;

export interface EmailInputProps extends Omit<React.HTMLProps<EmailInputDerivedElement>, 'size' | 'type' | 'label' | 'inputMode'> {
export interface EmailInputProps extends Omit<React.HTMLProps<EmailInputDerivedElement>, 'size' | 'type' | 'label' | 'inputMode' | 'pattern'> {
/**
* Short textual description indicating the nature of the component's value.
*/
@@ -37,6 +37,14 @@ export interface EmailInputProps extends Omit<React.HTMLProps<EmailInputDerivedE
* Is the label hidden?
*/
hiddenLabel?: boolean,
/**
* Allowed domains for emails.
*/
domains?: string[],
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -56,6 +64,8 @@ export const EmailInput = React.forwardRef<EmailInputDerivedElement, EmailInputP
className,
id: idProp,
style,
domains = [],
length,
...etcProps
}: EmailInputProps,
forwardedRef,
@@ -64,6 +74,12 @@ export const EmailInput = React.forwardRef<EmailInputDerivedElement, EmailInputP
const defaultId = React.useId();
const id = idProp ?? defaultId;

const pattern = (
Array.isArray(domains) && domains.length > 0
? `.+?@(${domains.join('|').replace(/\./g, '\\.')})$`
: undefined
);

return (
<div
className={clsx(
@@ -79,11 +95,13 @@ export const EmailInput = React.forwardRef<EmailInputDerivedElement, EmailInputP
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
id={id}
aria-labelledby={labelId}
type="email"
data-testid="input"
pattern={pattern}
className={clsx(
'bg-negative rounded-inherit w-full peer block font-inherit',
'focus:outline-0',
@@ -173,6 +191,7 @@ export const EmailInput = React.forwardRef<EmailInputDerivedElement, EmailInputP
)}
{indicator && (
<div
data-testid="indicator"
className={clsx(
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none',
{
@@ -207,4 +226,6 @@ EmailInput.defaultProps = {
block: false,
variant: 'default',
hiddenLabel: false,
domains: [],
length: undefined,
};

categories/formatted/react/src/components/PatternTextInput/TextInput.test.tsx → categories/formatted/react/src/components/PatternTextInput/PatternTextInput.test.tsx View File

@@ -15,20 +15,20 @@ import {
} from 'vitest';
import matchers from '@testing-library/jest-dom/matchers';
import {
TextInput,
TextInputDerivedElement,
PatternTextInput,
PatternTextInputDerivedElement,
} from '.';

expect.extend(matchers);

describe('TextInput', () => {
describe('PatternTextInput', () => {
afterEach(() => {
cleanup();
});

it('renders a textbox', () => {
render(
<TextInput />,
<PatternTextInput />,
);
const textbox = screen.getByRole('textbox');
expect(textbox).toBeInTheDocument();
@@ -37,7 +37,7 @@ describe('TextInput', () => {

it('renders a border', () => {
render(
<TextInput
<PatternTextInput
border
/>,
);
@@ -47,7 +47,7 @@ describe('TextInput', () => {

it('renders a label', () => {
render(
<TextInput
<PatternTextInput
label="foo"
/>,
);
@@ -59,7 +59,7 @@ describe('TextInput', () => {

it('renders a hidden label', () => {
render(
<TextInput
<PatternTextInput
label="foo"
hiddenLabel
/>,
@@ -73,7 +73,7 @@ describe('TextInput', () => {

it('renders a hint', () => {
render(
<TextInput
<PatternTextInput
hint="foo"
/>,
);
@@ -83,7 +83,7 @@ describe('TextInput', () => {

it('renders an indicator', () => {
render(
<TextInput
<PatternTextInput
indicator={
<div />
}
@@ -111,7 +111,7 @@ describe('TextInput', () => {
}) => {
it('renders input styles', () => {
render(
<TextInput
<PatternTextInput
size={size}
/>,
);
@@ -122,7 +122,7 @@ describe('TextInput', () => {

it('renders label styles with indicator', () => {
render(
<TextInput
<PatternTextInput
size={size}
label="foo"
indicator={<div />}
@@ -134,7 +134,7 @@ describe('TextInput', () => {

it('renders hint styles when indicator is present', () => {
render(
<TextInput
<PatternTextInput
size={size}
hint="hint"
indicator={<div />}
@@ -147,7 +147,7 @@ describe('TextInput', () => {

it('renders indicator styles', () => {
render(
<TextInput
<PatternTextInput
size={size}
indicator={
<div />
@@ -162,7 +162,7 @@ describe('TextInput', () => {

it('renders a block textbox', () => {
render(
<TextInput
<PatternTextInput
block
/>,
);
@@ -173,7 +173,7 @@ describe('TextInput', () => {

it.each(TextControl.AVAILABLE_INPUT_TYPES)('renders a textbox with type %s', (inputType) => {
render(
<TextInput
<PatternTextInput
type={inputType}
/>,
);
@@ -183,7 +183,7 @@ describe('TextInput', () => {

it('falls back to text input mode when it clashes with the input type', () => {
render(
<TextInput
<PatternTextInput
type="text"
inputMode="search"
/>,
@@ -207,7 +207,7 @@ describe('TextInput', () => {
}) => {
it('renders input styles', () => {
render(
<TextInput
<PatternTextInput
variant={variant}
/>,
);
@@ -218,7 +218,7 @@ describe('TextInput', () => {

it('renders hint styles', () => {
render(
<TextInput
<PatternTextInput
variant={variant}
hint="hint"
/>,
@@ -231,12 +231,12 @@ describe('TextInput', () => {

it('handles change events', async () => {
const onChange = vi.fn().mockImplementationOnce(
(e: React.ChangeEvent<TextInputDerivedElement>) => {
(e: React.ChangeEvent<PatternTextInputDerivedElement>) => {
e.preventDefault();
},
);
render(
<TextInput
<PatternTextInput
onChange={onChange}
/>,
);
@@ -247,12 +247,12 @@ describe('TextInput', () => {

it('handles input events', async () => {
const onInput = vi.fn().mockImplementationOnce(
(e: React.SyntheticEvent<TextInputDerivedElement>) => {
(e: React.SyntheticEvent<PatternTextInputDerivedElement>) => {
e.preventDefault();
},
);
render(
<TextInput
<PatternTextInput
onInput={onInput}
/>,
);

+ 11
- 1
categories/formatted/react/src/components/PatternTextInput/index.tsx View File

@@ -45,6 +45,10 @@ export interface PatternTextInputProps extends Omit<React.HTMLProps<PatternTextI
* Input mode of the component.
*/
inputMode?: TextControl.InputMode,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -52,7 +56,10 @@ export interface PatternTextInputProps extends Omit<React.HTMLProps<PatternTextI
*
* This component supports multiline input and adjusts its layout accordingly.
*/
export const PatternTextInput = React.forwardRef<PatternTextInputDerivedElement, PatternTextInputProps>((
export const PatternTextInput = React.forwardRef<
PatternTextInputDerivedElement,
PatternTextInputProps
>((
{
label,
hint,
@@ -67,6 +74,7 @@ export const PatternTextInput = React.forwardRef<PatternTextInputDerivedElement,
id: idProp,
style,
inputMode = type,
length,
...etcProps
},
forwardedRef,
@@ -98,6 +106,7 @@ export const PatternTextInput = React.forwardRef<PatternTextInputDerivedElement,
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
aria-labelledby={labelId}
id={id}
@@ -229,4 +238,5 @@ PatternTextInput.defaultProps = {
variant: 'default',
hiddenLabel: false,
inputMode: 'text',
length: undefined,
};

+ 8
- 0
categories/formatted/react/src/components/PhoneNumberInput/index.tsx View File

@@ -47,6 +47,10 @@ export interface PhoneNumberInputProps extends Omit<React.HTMLProps<PhoneNumberI
* Country where the phone number should be formatted for.
*/
country?: Country,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -73,6 +77,7 @@ export const PhoneNumberInput = React.forwardRef<
value,
onChange,
name,
length,
...etcProps
}: PhoneNumberInputProps,
forwardedRef,
@@ -140,9 +145,11 @@ export const PhoneNumberInput = React.forwardRef<
className,
)}
style={style}
data-testid="base"
>
<input
{...etcProps}
size={length}
value={value}
onChange={onChange}
ref={ref}
@@ -222,6 +229,7 @@ export const PhoneNumberInput = React.forwardRef<
)}
{indicator && (
<div
data-testid="indicator"
className={clsx(
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none',
{


+ 9
- 0
categories/formatted/react/src/components/UrlInput/index.tsx View File

@@ -37,6 +37,10 @@ export interface UrlInputProps extends Omit<React.HTMLProps<UrlInputDerivedEleme
* Is the label hidden?
*/
hiddenLabel?: boolean,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -54,6 +58,7 @@ export const UrlInput = React.forwardRef<UrlInputDerivedElement, UrlInputProps>(
hiddenLabel = false,
className,
style,
length,
id: idProp,
...etcProps
}: UrlInputProps,
@@ -75,9 +80,11 @@ export const UrlInput = React.forwardRef<UrlInputDerivedElement, UrlInputProps>(
className,
)}
style={style}
data-testid="base"
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
id={id}
aria-labelledby={labelId}
@@ -172,6 +179,7 @@ export const UrlInput = React.forwardRef<UrlInputDerivedElement, UrlInputProps>(
)}
{indicator && (
<div
data-testid="indicator"
className={clsx(
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none',
{
@@ -205,4 +213,5 @@ UrlInput.defaultProps = {
block: false,
variant: 'default',
hiddenLabel: false,
length: undefined,
};

+ 7
- 0
categories/freeform/react/src/components/MaskedTextInput/index.tsx View File

@@ -37,6 +37,10 @@ export interface MaskedTextInputProps extends Omit<React.HTMLProps<MaskedTextInp
* Is the label hidden?
*/
hiddenLabel?: boolean,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -58,6 +62,7 @@ export const MaskedTextInput = React.forwardRef<
className,
id: idProp,
style,
length,
...etcProps
}: MaskedTextInputProps,
forwardedRef,
@@ -82,6 +87,7 @@ export const MaskedTextInput = React.forwardRef<
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
id={id}
aria-labelledby={labelId}
@@ -210,4 +216,5 @@ MaskedTextInput.defaultProps = {
block: false,
variant: 'default',
hiddenLabel: false,
length: undefined,
};

+ 7
- 0
categories/freeform/react/src/components/TextInput/index.tsx View File

@@ -45,6 +45,10 @@ export interface TextInputProps extends Omit<React.HTMLProps<TextInputDerivedEle
* Input mode of the component.
*/
inputMode?: TextControl.InputMode,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -67,6 +71,7 @@ export const TextInput = React.forwardRef<TextInputDerivedElement, TextInputProp
id: idProp,
style,
inputMode = type,
length,
...etcProps
},
forwardedRef,
@@ -98,6 +103,7 @@ export const TextInput = React.forwardRef<TextInputDerivedElement, TextInputProp
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
aria-labelledby={labelId}
id={id}
@@ -229,4 +235,5 @@ TextInput.defaultProps = {
variant: 'default',
hiddenLabel: false,
inputMode: 'text',
length: undefined,
};

+ 4
- 0
categories/multichoice/react/src/components/TagInput/index.tsx View File

@@ -55,6 +55,9 @@ export interface TagInputProps extends Omit<React.HTMLProps<TagInputDerivedEleme
* Separators for splitting the input value into multiple tags.
*/
separator?: TagInputSeparator[],
/**
* Should the last tag be editable when removed?
*/
editOnRemove?: boolean,
}

@@ -221,6 +224,7 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
variant === 'alternate' && 'tag-input-alternate',
className,
)}
data-testid="base"
onFocusCapture={handleFocusCapture}
>
<textarea


+ 7
- 0
categories/number/react/src/components/NumberSpinner/index.tsx View File

@@ -37,6 +37,10 @@ export interface NumberSpinnerProps extends Omit<React.HTMLProps<NumberSpinnerDe
* Is the label hidden?
*/
hiddenLabel?: boolean,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -55,6 +59,7 @@ export const NumberSpinner = React.forwardRef<NumberSpinnerDerivedElement, Numbe
className,
id: idProp,
style,
length,
...etcProps
}: NumberSpinnerProps,
forwardedRef,
@@ -79,6 +84,7 @@ export const NumberSpinner = React.forwardRef<NumberSpinnerDerivedElement, Numbe
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
id={id}
aria-labelledby={labelId}
@@ -208,4 +214,5 @@ NumberSpinner.defaultProps = {
hiddenLabel: false,
size: 'medium',
variant: 'default',
length: undefined,
};

+ 7
- 0
categories/temporal/react/src/components/DateDropdown/index.tsx View File

@@ -37,6 +37,10 @@ export interface DateDropdownProps extends Omit<React.HTMLProps<DateDropdownDeri
* Is the label hidden?
*/
hiddenLabel?: boolean,
/**
* Visual length of the input.
*/
length?: number,
}

/**
@@ -58,6 +62,7 @@ export const DateDropdown = React.forwardRef<
className,
id: idProp,
style,
length,
...etcProps
}: DateDropdownProps,
forwardedRef,
@@ -82,6 +87,7 @@ export const DateDropdown = React.forwardRef<
>
<input
{...etcProps}
size={length}
ref={forwardedRef}
id={id}
aria-labelledby={labelId}
@@ -211,4 +217,5 @@ DateDropdown.defaultProps = {
block: false,
variant: 'default',
hiddenLabel: false,
length: undefined,
};

+ 20
- 0
showcases/web-kitchensink-reactnext/src/pages/categories/formatted/index.tsx View File

@@ -19,6 +19,26 @@ const TemporalPage: NextPage = () => {
/>
</Subsection>
</Section>
<Section title="EmailInput">
<Subsection title="Default">
<Formatted.EmailInput
label="Email"
name="email"
border
/>
</Subsection>
<Subsection title="With domains">
<Formatted.EmailInput
label="Email"
name="email"
border
variant="alternate"
domains={['gmail.com', 'yahoo.com']}
hint="Only GMail or Yahoo Mail allowed"
length={50}
/>
</Subsection>
</Section>
</DefaultLayout>
)
}


Loading…
Cancel
Save