Skip to content

Commit

Permalink
[WNMGDS-2928] Follow up to improve the standardized inputRef types (#…
Browse files Browse the repository at this point in the history
…3361)

* The types I added previously didn't cover mutable refs

Also, if you look at the types we were using before [my last change](https://github.com/CMSgov/design-system/pull/3356/files), they used a lot of `any` types. I'm nervous that if we start introducing really specific types like `React.Ref<HTMLInputElement | HTMLTextAreaElement>`, we're going to start to get a lot of complaints from down stream.

* Update storybook doc snapshots

* Add unit tests to some of them. `TextInput` is giving me grief

* Doh! This default prop was overriding my test prop

* Add `inputRef` tests for Autocomplete
  • Loading branch information
pwolfert authored Dec 23, 2024
1 parent f9be0de commit 2a0d36e
Show file tree
Hide file tree
Showing 17 changed files with 113 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Autocomplete from './Autocomplete';
import TextField from '../TextField/TextField';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createRef, useEffect, useRef } from 'react';

const defaultItems = [{ id: 'kRf6c2fY', name: 'Cook County, IL' }];
const updatedItems = [{ id: 'Yf2c6fRk', name: 'Marion County, OR' }];
Expand Down Expand Up @@ -479,4 +480,35 @@ describe('Autocomplete', () => {
const clearButton = screen.getByText('Clear search');
expect(clearButton).toHaveAttribute('disabled');
});

it('forwards an object inputRef', () => {
const inputRef = createRef<HTMLInputElement>();
renderAutocomplete({ inputRef });
expect(inputRef.current).toBeInTheDocument();
expect(inputRef.current.tagName).toEqual('INPUT');
});

it('forwards a mutable object inputRef', () => {
const MyComponent = () => {
const inputRef = useRef<HTMLButtonElement>();
useEffect(() => {
expect(inputRef.current).toBeInTheDocument();
expect(inputRef.current.tagName).toEqual('INPUT');
}, []);
return (
<Autocomplete items={defaultItems} inputRef={inputRef}>
<TextField label="autocomplete" name="autocomplete_field" />
</Autocomplete>
);
};

render(<MyComponent />);
});

it('forwards a function inputRef', () => {
const inputRef = jest.fn();
renderAutocomplete({ inputRef });
expect(inputRef).toHaveBeenCalled();
expect(inputRef.mock.lastCall[0].tagName).toEqual('INPUT');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export interface AutocompleteProps {
/**
* Access a reference to the child `TextField`'s `input` element
*/
inputRef?: React.Ref<HTMLInputElement>;
inputRef?: React.Ref<any> | React.MutableRefObject<any>;
/**
* Used to determine the string value for the selected item (which is used to compute the `inputValue`).
* @deprecated Please provide a `name` property to each item instead.
Expand Down
15 changes: 14 additions & 1 deletion packages/design-system/src/components/Button/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Button from './Button';
import { UtagContainer } from '../analytics';
import { config } from '../config';
import { fireEvent, render, screen } from '@testing-library/react';
import { createRef } from 'react';
import { createRef, useEffect, useRef } from 'react';

const defaultProps = {
children: 'Foo',
Expand Down Expand Up @@ -90,6 +90,19 @@ describe('Button', () => {
expect(inputRef.current.tagName).toEqual('BUTTON');
});

it('forwards a mutable object inputRef', () => {
const MyComponent = () => {
const inputRef = useRef<HTMLButtonElement>();
useEffect(() => {
expect(inputRef.current).toBeInTheDocument();
expect(inputRef.current.tagName).toEqual('BUTTON');
}, []);
return <Button inputRef={inputRef}>Hello world</Button>;
};

render(<MyComponent />);
});

it('forwards a function inputRef', () => {
const inputRef = jest.fn();
renderButton({ inputRef });
Expand Down
2 changes: 1 addition & 1 deletion packages/design-system/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type ButtonSize = 'small' | 'big';

export type ButtonVariation = 'solid' | 'ghost';

export type ButtonRef = React.Ref<HTMLButtonElement | HTMLAnchorElement>;
export type ButtonRef = React.Ref<any> | React.MutableRefObject<any>;

interface CommonButtonProps extends AnalyticsOverrideProps, AnalyticsParentDataProps {
/**
Expand Down
15 changes: 14 additions & 1 deletion packages/design-system/src/components/ChoiceList/Choice.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Choice, { ChoiceProps, ChoiceType } from './Choice';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createRef } from 'react';
import { createRef, useEffect, useRef } from 'react';

const defaultProps = {
name: 'foo',
Expand Down Expand Up @@ -138,6 +138,19 @@ describe('Choice', () => {
expect(inputRef.current.tagName).toEqual('INPUT');
});

it('forwards a mutable object inputRef', () => {
const MyComponent = () => {
const inputRef = useRef<HTMLInputElement>();
useEffect(() => {
expect(inputRef.current).toBeInTheDocument();
expect(inputRef.current.tagName).toEqual('INPUT');
}, []);
return <Choice inputRef={inputRef} {...defaultProps} />;
};

render(<MyComponent />);
});

it('forwards a function inputRef', () => {
const inputRef = jest.fn();
renderChoice({ inputRef });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export interface BaseChoiceProps {
/**
* Access a reference to the `input` element
*/
inputRef?: React.Ref<HTMLInputElement>;
inputRef?: React.Ref<any> | React.MutableRefObject<any>;
/**
* A unique ID to be used for the input field, as well as the label's
* `for` attribute. A unique ID will be generated if one isn't provided.
Expand Down
15 changes: 14 additions & 1 deletion packages/design-system/src/components/Dropdown/Dropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Dropdown from './Dropdown';
import { createRef } from 'react';
import { createRef, useEffect, useRef } from 'react';

const defaultProps = {
name: 'dropdown',
Expand Down Expand Up @@ -223,6 +223,19 @@ describe('Dropdown', () => {
expect(inputRef.current.tagName).toEqual('BUTTON');
});

it('forwards a mutable object inputRef', () => {
const MyComponent = () => {
const inputRef = useRef<HTMLButtonElement>();
useEffect(() => {
expect(inputRef.current).toBeInTheDocument();
expect(inputRef.current.tagName).toEqual('BUTTON');
}, []);
return <Dropdown inputRef={inputRef} {...defaultProps} options={generateOptions(2)} />;
};

render(<MyComponent />);
});

it('forwards a function inputRef', () => {
const inputRef = jest.fn();
makeDropdown({ inputRef });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export interface BaseDropdownProps {
/**
* Access a reference to the `button` element
*/
inputRef?: React.Ref<HTMLButtonElement>;
inputRef?: React.Ref<any> | React.MutableRefObject<any>;
/**
* Set to `true` to apply the "inverse" color scheme
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import TextField from './TextField';
import { render, screen } from '@testing-library/react';
import { DATE_MASK } from './useLabelMask';
import userEvent from '@testing-library/user-event';
import { createRef } from 'react';
import { createRef, useEffect, useRef } from 'react';

const defaultProps = {
label: 'Foo',
Expand Down Expand Up @@ -116,6 +116,19 @@ describe('TextField', function () {
expect(inputRef.current.tagName).toEqual('INPUT');
});

it('forwards a mutable object inputRef', () => {
const MyComponent = () => {
const inputRef = useRef<HTMLInputElement>();
useEffect(() => {
expect(inputRef.current).toBeInTheDocument();
expect(inputRef.current.tagName).toEqual('INPUT');
}, []);
return <TextField inputRef={inputRef} {...defaultProps} />;
};

render(<MyComponent />);
});

it('forwards a function inputRef', () => {
const inputRef = jest.fn();
renderTextField({ inputRef });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ interface BaseTextFieldProps {
/**
* Access a reference to the `input` or `textarea` element
*/
inputRef?: React.Ref<HTMLInputElement | HTMLTextAreaElement>;
inputRef?: React.Ref<any> | React.MutableRefObject<any>;
/**
* Set to `true` to apply the "inverse" color scheme
*/
Expand Down
16 changes: 14 additions & 2 deletions packages/design-system/src/components/TextField/TextInput.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type * as React from 'react';
import { createRef } from 'react';
import { createRef, useEffect, useRef } from 'react';
import TextInput, { OmitProps, TextInputProps } from './TextInput';
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
Expand All @@ -8,7 +8,6 @@ const defaultProps: Omit<React.ComponentPropsWithRef<'textarea'>, OmitProps> &
Omit<React.ComponentPropsWithRef<'input'>, OmitProps> &
TextInputProps = {
name: 'spec-field',
inputRef: jest.fn(),
id: 'static-id',
type: 'text',
};
Expand Down Expand Up @@ -139,6 +138,19 @@ describe('TextInput', function () {
expect(inputRef.current.tagName).toEqual('INPUT');
});

it('forwards a mutable object inputRef', () => {
const MyComponent = () => {
const inputRef = useRef<HTMLInputElement>();
useEffect(() => {
expect(inputRef.current).toBeInTheDocument();
expect(inputRef.current.tagName).toEqual('INPUT');
}, []);
return <TextInput inputRef={inputRef} {...defaultProps} />;
};

render(<MyComponent />);
});

it('forwards a function inputRef', () => {
const inputRef = jest.fn();
renderInput({ inputRef });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export type TextInputProps = Omit<React.ComponentPropsWithoutRef<'input'>, OmitP
* applicable if this is a multiline field.
*/
rows?: TextInputRows;
inputRef?: React.Ref<any>;
inputRef?: React.Ref<any> | React.MutableRefObject<any>;
/**
* Set the max-width of the input either to `'small'` or `'medium'`.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
],
[
"inputRef",
"Access a reference to the child TextField's input element\nRef<HTMLInputElement>",
"Access a reference to the child TextField's input element\nRef<any> | MutableRefObject<any>",
"-"
],
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
],
[
"inputRef",
"Access a reference to the input element\nRef<HTMLInputElement>",
"Access a reference to the input element\nRef<any> | MutableRefObject<any>",
"-"
],
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
],
[
"inputRef",
"Access a reference to the button element\nRef<HTMLButtonElement>",
"Access a reference to the button element\nRef<any> | MutableRefObject<any>",
"-"
],
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
],
[
"inputRef",
"Ref<any>",
"Ref<any> | MutableRefObject<any>",
"-"
],
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
],
[
"inputRef",
"Access a reference to the input or textarea element\nRef<HTMLInputElement | HTMLTextAreaElement>",
"Access a reference to the input or textarea element\nRef<any> | MutableRefObject<any>",
"-"
],
[
Expand Down

0 comments on commit 2a0d36e

Please sign in to comment.