Skip to content

Commit

Permalink
Simplify the Input element type (#2306)
Browse files Browse the repository at this point in the history
  • Loading branch information
connor-baer authored Aug 14, 2024
1 parent f583d05 commit 8be7f3b
Show file tree
Hide file tree
Showing 19 changed files with 79 additions and 95 deletions.
5 changes: 5 additions & 0 deletions .changeset/flat-steaks-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup-oss/circuit-ui': major
---

Deprecated the `InputElement` interface and narrowed the Input's element type to `HTMLInputElement` and the TextArea's element type to `HTMLTextAreaElement`. This affects `ref`s and event handlers.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { describe, expect, it } from 'vitest';
import { createRef, useState, type ChangeEvent } from 'react';

import { render, userEvent, axe, screen } from '../../util/test-utils.js';
import type { InputElement } from '../Input/index.js';

import { CurrencyInput, type CurrencyInputProps } from './CurrencyInput.js';

Expand All @@ -30,7 +29,7 @@ const defaultProps = {

describe('CurrencyInput', () => {
it('should forward a ref', () => {
const ref = createRef<InputElement>();
const ref = createRef<HTMLInputElement>();
render(<CurrencyInput {...defaultProps} ref={ref} />);
const input: HTMLInputElement = screen.getByRole('textbox');
expect(ref.current).toBe(input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { resolveCurrencyFormat } from '@sumup-oss/intl';
import { NumericFormat, type NumericFormatProps } from 'react-number-format';

import { clsx } from '../../styles/clsx.js';
import { Input, type InputElement, type InputProps } from '../Input/index.js';
import { Input, type InputProps } from '../Input/index.js';

import { formatPlaceholder } from './CurrencyInputService.js';
import classes from './CurrencyInput.module.css';
Expand Down Expand Up @@ -73,7 +73,7 @@ const DUMMY_DELIMITER = '?';
* the symbol according to the locale. The corresponding service exports a
* parser for formatting values automatically.
*/
export const CurrencyInput = forwardRef<InputElement, CurrencyInputProps>(
export const CurrencyInput = forwardRef<HTMLInputElement, CurrencyInputProps>(
(
{
locale,
Expand Down
3 changes: 1 addition & 2 deletions packages/circuit-ui/components/DateInput/DateInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@ import { describe, expect, it } from 'vitest';
import { createRef } from 'react';

import { render, axe } from '../../util/test-utils.js';
import type { InputElement } from '../Input/index.js';

import { DateInput } from './DateInput.js';

describe('DateInput', () => {
const baseProps = { label: 'Date' };

it('should forward a ref', () => {
const ref = createRef<InputElement>();
const ref = createRef<HTMLInputElement>();
const { container } = render(<DateInput {...baseProps} ref={ref} />);
const input = container.querySelector('input');
expect(ref.current).toBe(input);
Expand Down
4 changes: 2 additions & 2 deletions packages/circuit-ui/components/DateInput/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import { forwardRef, useState, useEffect } from 'react';
import { PatternFormat } from 'react-number-format';

import { Input, type InputElement, type InputProps } from '../Input/index.js';
import { Input, type InputProps } from '../Input/index.js';
import { clsx } from '../../styles/clsx.js';

import classes from './DateInput.module.css';
Expand All @@ -42,7 +42,7 @@ export interface DateInputProps
* DateInput component for forms.
* The input value is always a string in the format `YYYY-MM-DD`.
*/
export const DateInput = forwardRef<InputElement, DateInputProps>(
export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
({ inputClassName, ...props }, ref) => {
// When server-side rendering, we assume that the user's browser supports
// the native date input.
Expand Down
6 changes: 3 additions & 3 deletions packages/circuit-ui/components/Input/Input.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { createRef } from 'react';

import { render, axe, screen } from '../../util/test-utils.js';

import { Input, type InputElement } from './Input.js';
import { Input } from './Input.js';

const defaultProps = {
label: 'Label',
Expand All @@ -35,14 +35,14 @@ describe('Input', () => {
});

it('should forward a ref to the input', () => {
const ref = createRef<InputElement>();
const ref = createRef<HTMLInputElement>();
const { container } = render(<Input ref={ref} {...defaultProps} />);
const input = container.querySelector('input');
expect(ref.current).toBe(input);
});

it('should forward a ref to the textarea', () => {
const ref = createRef<InputElement>();
const ref = createRef<HTMLInputElement>();
const { container } = render(
<Input as="textarea" ref={ref} {...defaultProps} />,
);
Expand Down
32 changes: 22 additions & 10 deletions packages/circuit-ui/components/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
useId,
type ComponentType,
type InputHTMLAttributes,
type TextareaHTMLAttributes,
} from 'react';

import {
Expand All @@ -38,19 +37,18 @@ import { clsx } from '../../styles/clsx.js';

import classes from './Input.module.css';

export type InputElement = HTMLInputElement & HTMLTextAreaElement;
type CircuitInputHTMLAttributes = InputHTMLAttributes<HTMLInputElement> &
TextareaHTMLAttributes<HTMLTextAreaElement>;
/**
* @deprecated
*
* Use the `HTMLInputElement` or `HTMLTextAreaElement` interfaces instead.
*/
export type InputElement = HTMLInputElement;

export interface InputProps extends CircuitInputHTMLAttributes {
export interface BaseInputProps {
/**
* A clear and concise description of the input purpose.
*/
label: string;
/**
* The HTML input element to render.
*/
as?: 'input' | 'textarea';
/**
* A unique identifier for the input field. If not defined, a randomly
* generated id is used.
Expand Down Expand Up @@ -106,10 +104,21 @@ export interface InputProps extends CircuitInputHTMLAttributes {
inputClassName?: string;
}

export interface InputProps
extends BaseInputProps,
InputHTMLAttributes<HTMLInputElement> {
/**
* @private
*
* Use the {@link TextArea} component.
*/
as?: 'input' | 'textarea';
}

/**
* Input component for forms. Takes optional prefix and suffix as render props.
*/
export const Input = forwardRef<InputElement, InputProps>(
export const Input = forwardRef<HTMLInputElement, InputProps>(
(
{
value,
Expand Down Expand Up @@ -175,6 +184,9 @@ export const Input = forwardRef<InputElement, InputProps>(
<Element
id={inputId}
value={value}
// @ts-expect-error The Input component renders as an `input` element
// by default. The types are overwritten as necessary in the
// TextArea component.
ref={ref}
aria-describedby={descriptionIds}
className={clsx(
Expand Down
2 changes: 1 addition & 1 deletion packages/circuit-ui/components/Input/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@

export { Input } from './Input.js';

export type { InputProps, InputElement } from './Input.js';
export type { InputProps, BaseInputProps, InputElement } from './Input.js';
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { describe, expect, it } from 'vitest';
import { createRef, useState, type ChangeEvent } from 'react';

import { render, userEvent, axe, screen } from '../../util/test-utils.js';
import type { InputElement } from '../Input/index.js';

import {
PercentageInput,
Expand All @@ -31,7 +30,7 @@ const defaultProps = {

describe('PercentageInput', () => {
it('should forward a ref', () => {
const ref = createRef<InputElement>();
const ref = createRef<HTMLInputElement>();
render(<PercentageInput {...defaultProps} ref={ref} />);
const input = screen.getByRole('textbox');
expect(ref.current).toBe(input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { resolveNumberFormat } from '@sumup-oss/intl';
import { NumericFormat, type NumericFormatProps } from 'react-number-format';

import { clsx } from '../../styles/clsx.js';
import { Input, type InputElement, type InputProps } from '../Input/index.js';
import { Input, type InputProps } from '../Input/index.js';

import { formatPlaceholder } from './PercentageInputService.js';
import classes from './PercentageInput.module.css';
Expand Down Expand Up @@ -62,7 +62,10 @@ const DEFAULT_FORMAT = {
/**
* PercentageInput component for fractional values
*/
export const PercentageInput = forwardRef<InputElement, PercentageInputProps>(
export const PercentageInput = forwardRef<
HTMLInputElement,
PercentageInputProps
>(
(
{
locale,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { describe, it, vi, expect } from 'vitest';
import { createRef } from 'react';

import { axe, render, screen, userEvent } from '../../util/test-utils.js';
import type { InputElement } from '../Input/Input.js';

import {
PhoneNumberInput,
Expand Down Expand Up @@ -67,7 +66,7 @@ describe('PhoneNumberInput', () => {
});

it('should forward a ref to the subscriber number input', () => {
const ref = createRef<InputElement>();
const ref = createRef<HTMLInputElement>();
const props = {
...defaultProps,
subscriberNumber: { ...defaultProps.subscriberNumber, ref },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from 'react';

import { Select, type SelectProps } from '../Select/index.js';
import { Input, type InputElement, type InputProps } from '../Input/index.js';
import { Input, type InputProps } from '../Input/index.js';
import {
FieldLabelText,
FieldLegend,
Expand Down Expand Up @@ -144,7 +144,7 @@ export interface PhoneNumberInputProps
/**
* The ref to the country code selector HTML DOM element.
*/
ref?: ForwardedRef<HTMLSelectElement | InputElement>;
ref?: ForwardedRef<HTMLSelectElement | HTMLInputElement>;
/**
* Render prop that should render a left-aligned overlay icon or element.
* Receives a className prop.
Expand Down Expand Up @@ -185,7 +185,7 @@ export interface PhoneNumberInputProps
/**
* The ref to the subscriber number input HTML DOM element.
*/
ref?: ForwardedRef<InputElement>;
ref?: ForwardedRef<HTMLInputElement>;
};
}

Expand Down Expand Up @@ -218,8 +218,8 @@ export const PhoneNumberInput = forwardRef<
},
ref,
) => {
const countryCodeRef = useRef<HTMLSelectElement | InputElement>(null);
const subscriberNumberRef = useRef<InputElement>(null);
const countryCodeRef = useRef<HTMLSelectElement | HTMLInputElement>(null);
const subscriberNumberRef = useRef<HTMLInputElement>(null);

const validationHintId = useId();

Expand Down Expand Up @@ -361,8 +361,8 @@ export const PhoneNumberInput = forwardRef<
readOnly={true}
onChange={() => {}}
ref={applyMultipleRefs(
countryCodeRef as RefObject<InputElement>,
countryCode.ref as ForwardedRef<InputElement>,
countryCodeRef as RefObject<HTMLInputElement>,
countryCode.ref as ForwardedRef<HTMLInputElement>,
)}
renderPrefix={countryCode.renderPrefix}
/>
Expand Down Expand Up @@ -401,7 +401,7 @@ export const PhoneNumberInput = forwardRef<
{...subscriberNumber}
invalid={invalid || subscriberNumber.invalid}
readOnly={readOnly || subscriberNumber.readonly}
onChange={eachFn<[ChangeEvent<InputElement>]>([
onChange={eachFn<[ChangeEvent<HTMLInputElement>]>([
subscriberNumber.onChange,
handleChange,
])}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { describe, expect, it, vi } from 'vitest';
import { createRef } from 'react';

import { render, axe, screen } from '../../util/test-utils.js';
import type { InputElement } from '../Input/index.js';

import { SearchInput } from './SearchInput.js';

Expand Down Expand Up @@ -45,7 +44,7 @@ describe('SearchInput', () => {
});

it('should forward a ref', () => {
const ref = createRef<InputElement>();
const ref = createRef<HTMLInputElement>();
const { container } = render(<SearchInput {...baseProps} ref={ref} />);
const input = container.querySelector('input');
expect(ref.current).toBe(input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

import { useState, type ChangeEvent } from 'react';

import type { InputElement } from '../Input/index.js';

import { SearchInput, type SearchInputProps } from './SearchInput.js';

export default {
Expand All @@ -32,7 +30,7 @@ export const Base = (args: SearchInputProps) => {

const handleChange = ({
target: { value: inputValue },
}: ChangeEvent<InputElement>) => {
}: ChangeEvent<HTMLInputElement>) => {
setValue(inputValue);
};

Expand Down
6 changes: 3 additions & 3 deletions packages/circuit-ui/components/SearchInput/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import { forwardRef, useRef } from 'react';
import { Search } from '@sumup-oss/icons';

import { Input, type InputElement, type InputProps } from '../Input/index.js';
import { Input, type InputProps } from '../Input/index.js';
import { CloseButton } from '../CloseButton/index.js';
import {
AccessibilityError,
Expand Down Expand Up @@ -49,9 +49,9 @@ export type SearchInputProps = InputProps & ClearProps;
/**
* SearchInput component for forms.
*/
export const SearchInput = forwardRef<InputElement, SearchInputProps>(
export const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(
({ value, onClear, clearLabel, inputClassName, ...props }, ref) => {
const localRef = useRef<InputElement>(null);
const localRef = useRef<HTMLInputElement>(null);

if (
process.env.NODE_ENV !== 'production' &&
Expand Down
Loading

0 comments on commit 8be7f3b

Please sign in to comment.