From 8be7f3b36e22135f6f312ea2aa4b582c252eb161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Connor=20B=C3=A4r?= Date: Wed, 14 Aug 2024 20:55:39 +0200 Subject: [PATCH] Simplify the Input element type (#2306) --- .changeset/flat-steaks-itch.md | 5 +++ .../CurrencyInput/CurrencyInput.spec.tsx | 3 +- .../CurrencyInput/CurrencyInput.tsx | 4 +-- .../components/DateInput/DateInput.spec.tsx | 3 +- .../components/DateInput/DateInput.tsx | 4 +-- .../components/Input/Input.spec.tsx | 6 ++-- .../circuit-ui/components/Input/Input.tsx | 32 +++++++++++------ packages/circuit-ui/components/Input/index.ts | 2 +- .../PercentageInput/PercentageInput.spec.tsx | 3 +- .../PercentageInput/PercentageInput.tsx | 7 ++-- .../PhoneNumberInput.spec.tsx | 3 +- .../PhoneNumberInput/PhoneNumberInput.tsx | 16 ++++----- .../SearchInput/SearchInput.spec.tsx | 3 +- .../SearchInput/SearchInput.stories.tsx | 4 +-- .../components/SearchInput/SearchInput.tsx | 6 ++-- .../components/TextArea/TextArea.tsx | 34 ++++++++++--------- .../circuit-ui/components/TextArea/types.ts | 29 ---------------- .../TextArea/useAutoExpand.spec.tsx | 5 ++- .../components/TextArea/useAutoExpand.ts | 5 ++- 19 files changed, 79 insertions(+), 95 deletions(-) create mode 100644 .changeset/flat-steaks-itch.md delete mode 100644 packages/circuit-ui/components/TextArea/types.ts diff --git a/.changeset/flat-steaks-itch.md b/.changeset/flat-steaks-itch.md new file mode 100644 index 0000000000..60d85ef200 --- /dev/null +++ b/.changeset/flat-steaks-itch.md @@ -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. diff --git a/packages/circuit-ui/components/CurrencyInput/CurrencyInput.spec.tsx b/packages/circuit-ui/components/CurrencyInput/CurrencyInput.spec.tsx index 709c3b9402..ebadbf14fd 100644 --- a/packages/circuit-ui/components/CurrencyInput/CurrencyInput.spec.tsx +++ b/packages/circuit-ui/components/CurrencyInput/CurrencyInput.spec.tsx @@ -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'; @@ -30,7 +29,7 @@ const defaultProps = { describe('CurrencyInput', () => { it('should forward a ref', () => { - const ref = createRef(); + const ref = createRef(); render(); const input: HTMLInputElement = screen.getByRole('textbox'); expect(ref.current).toBe(input); diff --git a/packages/circuit-ui/components/CurrencyInput/CurrencyInput.tsx b/packages/circuit-ui/components/CurrencyInput/CurrencyInput.tsx index c5e75954ef..3b4d040334 100644 --- a/packages/circuit-ui/components/CurrencyInput/CurrencyInput.tsx +++ b/packages/circuit-ui/components/CurrencyInput/CurrencyInput.tsx @@ -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'; @@ -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( +export const CurrencyInput = forwardRef( ( { locale, diff --git a/packages/circuit-ui/components/DateInput/DateInput.spec.tsx b/packages/circuit-ui/components/DateInput/DateInput.spec.tsx index 267bd4f751..aad82917a0 100644 --- a/packages/circuit-ui/components/DateInput/DateInput.spec.tsx +++ b/packages/circuit-ui/components/DateInput/DateInput.spec.tsx @@ -17,7 +17,6 @@ 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'; @@ -25,7 +24,7 @@ describe('DateInput', () => { const baseProps = { label: 'Date' }; it('should forward a ref', () => { - const ref = createRef(); + const ref = createRef(); const { container } = render(); const input = container.querySelector('input'); expect(ref.current).toBe(input); diff --git a/packages/circuit-ui/components/DateInput/DateInput.tsx b/packages/circuit-ui/components/DateInput/DateInput.tsx index a20a3343c6..e116066719 100644 --- a/packages/circuit-ui/components/DateInput/DateInput.tsx +++ b/packages/circuit-ui/components/DateInput/DateInput.tsx @@ -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'; @@ -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( +export const DateInput = forwardRef( ({ inputClassName, ...props }, ref) => { // When server-side rendering, we assume that the user's browser supports // the native date input. diff --git a/packages/circuit-ui/components/Input/Input.spec.tsx b/packages/circuit-ui/components/Input/Input.spec.tsx index 7bad82ff23..f90978596e 100644 --- a/packages/circuit-ui/components/Input/Input.spec.tsx +++ b/packages/circuit-ui/components/Input/Input.spec.tsx @@ -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', @@ -35,14 +35,14 @@ describe('Input', () => { }); it('should forward a ref to the input', () => { - const ref = createRef(); + const ref = createRef(); const { container } = render(); const input = container.querySelector('input'); expect(ref.current).toBe(input); }); it('should forward a ref to the textarea', () => { - const ref = createRef(); + const ref = createRef(); const { container } = render( , ); diff --git a/packages/circuit-ui/components/Input/Input.tsx b/packages/circuit-ui/components/Input/Input.tsx index 4d411287b2..b84be9128c 100644 --- a/packages/circuit-ui/components/Input/Input.tsx +++ b/packages/circuit-ui/components/Input/Input.tsx @@ -20,7 +20,6 @@ import { useId, type ComponentType, type InputHTMLAttributes, - type TextareaHTMLAttributes, } from 'react'; import { @@ -38,19 +37,18 @@ import { clsx } from '../../styles/clsx.js'; import classes from './Input.module.css'; -export type InputElement = HTMLInputElement & HTMLTextAreaElement; -type CircuitInputHTMLAttributes = InputHTMLAttributes & - TextareaHTMLAttributes; +/** + * @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. @@ -106,10 +104,21 @@ export interface InputProps extends CircuitInputHTMLAttributes { inputClassName?: string; } +export interface InputProps + extends BaseInputProps, + InputHTMLAttributes { + /** + * @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( +export const Input = forwardRef( ( { value, @@ -175,6 +184,9 @@ export const Input = forwardRef( { it('should forward a ref', () => { - const ref = createRef(); + const ref = createRef(); render(); const input = screen.getByRole('textbox'); expect(ref.current).toBe(input); diff --git a/packages/circuit-ui/components/PercentageInput/PercentageInput.tsx b/packages/circuit-ui/components/PercentageInput/PercentageInput.tsx index 617cab8fc6..ea40ee9001 100644 --- a/packages/circuit-ui/components/PercentageInput/PercentageInput.tsx +++ b/packages/circuit-ui/components/PercentageInput/PercentageInput.tsx @@ -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'; @@ -62,7 +62,10 @@ const DEFAULT_FORMAT = { /** * PercentageInput component for fractional values */ -export const PercentageInput = forwardRef( +export const PercentageInput = forwardRef< + HTMLInputElement, + PercentageInputProps +>( ( { locale, diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx index 4fa77700bf..ba0b2a9dee 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx @@ -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, @@ -67,7 +66,7 @@ describe('PhoneNumberInput', () => { }); it('should forward a ref to the subscriber number input', () => { - const ref = createRef(); + const ref = createRef(); const props = { ...defaultProps, subscriberNumber: { ...defaultProps.subscriberNumber, ref }, diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx index 3aa8bcc1c6..f3d8ef9f21 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx @@ -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, @@ -144,7 +144,7 @@ export interface PhoneNumberInputProps /** * The ref to the country code selector HTML DOM element. */ - ref?: ForwardedRef; + ref?: ForwardedRef; /** * Render prop that should render a left-aligned overlay icon or element. * Receives a className prop. @@ -185,7 +185,7 @@ export interface PhoneNumberInputProps /** * The ref to the subscriber number input HTML DOM element. */ - ref?: ForwardedRef; + ref?: ForwardedRef; }; } @@ -218,8 +218,8 @@ export const PhoneNumberInput = forwardRef< }, ref, ) => { - const countryCodeRef = useRef(null); - const subscriberNumberRef = useRef(null); + const countryCodeRef = useRef(null); + const subscriberNumberRef = useRef(null); const validationHintId = useId(); @@ -361,8 +361,8 @@ export const PhoneNumberInput = forwardRef< readOnly={true} onChange={() => {}} ref={applyMultipleRefs( - countryCodeRef as RefObject, - countryCode.ref as ForwardedRef, + countryCodeRef as RefObject, + countryCode.ref as ForwardedRef, )} renderPrefix={countryCode.renderPrefix} /> @@ -401,7 +401,7 @@ export const PhoneNumberInput = forwardRef< {...subscriberNumber} invalid={invalid || subscriberNumber.invalid} readOnly={readOnly || subscriberNumber.readonly} - onChange={eachFn<[ChangeEvent]>([ + onChange={eachFn<[ChangeEvent]>([ subscriberNumber.onChange, handleChange, ])} diff --git a/packages/circuit-ui/components/SearchInput/SearchInput.spec.tsx b/packages/circuit-ui/components/SearchInput/SearchInput.spec.tsx index 71a9b63b31..f987235fb0 100644 --- a/packages/circuit-ui/components/SearchInput/SearchInput.spec.tsx +++ b/packages/circuit-ui/components/SearchInput/SearchInput.spec.tsx @@ -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'; @@ -45,7 +44,7 @@ describe('SearchInput', () => { }); it('should forward a ref', () => { - const ref = createRef(); + const ref = createRef(); const { container } = render(); const input = container.querySelector('input'); expect(ref.current).toBe(input); diff --git a/packages/circuit-ui/components/SearchInput/SearchInput.stories.tsx b/packages/circuit-ui/components/SearchInput/SearchInput.stories.tsx index abaafde30c..576b0ba68a 100644 --- a/packages/circuit-ui/components/SearchInput/SearchInput.stories.tsx +++ b/packages/circuit-ui/components/SearchInput/SearchInput.stories.tsx @@ -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 { @@ -32,7 +30,7 @@ export const Base = (args: SearchInputProps) => { const handleChange = ({ target: { value: inputValue }, - }: ChangeEvent) => { + }: ChangeEvent) => { setValue(inputValue); }; diff --git a/packages/circuit-ui/components/SearchInput/SearchInput.tsx b/packages/circuit-ui/components/SearchInput/SearchInput.tsx index 3446570515..1ade2ca9eb 100644 --- a/packages/circuit-ui/components/SearchInput/SearchInput.tsx +++ b/packages/circuit-ui/components/SearchInput/SearchInput.tsx @@ -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, @@ -49,9 +49,9 @@ export type SearchInputProps = InputProps & ClearProps; /** * SearchInput component for forms. */ -export const SearchInput = forwardRef( +export const SearchInput = forwardRef( ({ value, onClear, clearLabel, inputClassName, ...props }, ref) => { - const localRef = useRef(null); + const localRef = useRef(null); if ( process.env.NODE_ENV !== 'production' && diff --git a/packages/circuit-ui/components/TextArea/TextArea.tsx b/packages/circuit-ui/components/TextArea/TextArea.tsx index c06db133f4..4e20029711 100644 --- a/packages/circuit-ui/components/TextArea/TextArea.tsx +++ b/packages/circuit-ui/components/TextArea/TextArea.tsx @@ -15,34 +15,35 @@ 'use client'; -import { forwardRef, useRef } from 'react'; +import { forwardRef, useRef, type TextareaHTMLAttributes } from 'react'; -import { Input, type InputElement, type InputProps } from '../Input/index.js'; +import { Input, type BaseInputProps } from '../Input/index.js'; import { applyMultipleRefs } from '../../util/refs.js'; import { clsx } from '../../styles/clsx.js'; import { useAutoExpand } from './useAutoExpand.js'; import classes from './TextArea.module.css'; -export type TextAreaProps = Omit & { - /** - * The number of visible text lines for the control. - * If set to `auto`, the control will auto-expand vertically to show the whole value. - */ - rows?: InputProps['rows'] | 'auto'; - /** - * Define the minimum number of visible text lines for the control. - * Works only when `rows` is set to `auto`. - */ - minRows?: InputProps['rows']; -}; +export type TextAreaProps = BaseInputProps & + Omit, 'rows'> & { + /** + * The number of visible text lines for the control. + * If set to `auto`, the control will auto-expand vertically to show the whole value. + */ + rows?: number | 'auto'; + /** + * Define the minimum number of visible text lines for the control. + * Works only when `rows` is set to `auto`. + */ + minRows?: number; + }; /** * TextArea component for forms. */ -export const TextArea = forwardRef( +export const TextArea = forwardRef( ({ inputClassName, ...props }, ref) => { - const localRef = useRef(null); + const localRef = useRef(null); const modifiedProps = useAutoExpand(localRef, props); return ( @@ -50,6 +51,7 @@ export const TextArea = forwardRef( {...modifiedProps} inputClassName={clsx(classes.base, inputClassName)} as="textarea" + // @ts-expect-error The input is rendered as a `textarea` element above. ref={applyMultipleRefs(localRef, ref)} /> ); diff --git a/packages/circuit-ui/components/TextArea/types.ts b/packages/circuit-ui/components/TextArea/types.ts deleted file mode 100644 index 0dcf4fd793..0000000000 --- a/packages/circuit-ui/components/TextArea/types.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2023, SumUp Ltd. - * 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 { InputProps } from '../Input/index.js'; - -export type TextAreaProps = Omit & { - /** - * The number of visible text lines for the control. - * If set to `auto`, the control will auto-expand vertically to show the whole value. - */ - rows?: InputProps['rows'] | 'auto'; - /** - * Define the minimum number of visible text lines for the control. - * Works only when `rows` is set to `auto`. - */ - minRows?: InputProps['rows']; -}; diff --git a/packages/circuit-ui/components/TextArea/useAutoExpand.spec.tsx b/packages/circuit-ui/components/TextArea/useAutoExpand.spec.tsx index cd5bc77be3..f8b9eaf097 100644 --- a/packages/circuit-ui/components/TextArea/useAutoExpand.spec.tsx +++ b/packages/circuit-ui/components/TextArea/useAutoExpand.spec.tsx @@ -22,7 +22,6 @@ import { render, screen, } from '../../util/test-utils.js'; -import type { InputElement } from '../Input/Input.js'; import { TextArea, type TextAreaProps } from './TextArea.js'; import { useAutoExpand } from './useAutoExpand.js'; @@ -35,7 +34,7 @@ const createTextAreaRef = (props = {}) => { render(