From 75e936985cc1cd2f2052f902f0806c58446aca30 Mon Sep 17 00:00:00 2001 From: Andrew Nelson Date: Tue, 6 Jun 2023 09:42:32 -0700 Subject: [PATCH] fix: Creates new Select component to eventually replace Dropdown (#2415) Co-authored-by: Brandon Lenz --- .../forms/Dropdown/Dropdown.stories.tsx | 68 +++++++++---------- .../forms/Dropdown/Dropdown.test.tsx | 58 ++++------------ src/components/forms/Dropdown/Dropdown.tsx | 59 +++------------- .../forms/Select/Select.stories.tsx | 67 ++++++++++++++++++ src/components/forms/Select/Select.test.tsx | 58 ++++++++++++++++ src/components/forms/Select/Select.tsx | 50 ++++++++++++++ src/index.ts | 1 + 7 files changed, 228 insertions(+), 133 deletions(-) create mode 100644 src/components/forms/Select/Select.stories.tsx create mode 100644 src/components/forms/Select/Select.test.tsx create mode 100644 src/components/forms/Select/Select.tsx diff --git a/src/components/forms/Dropdown/Dropdown.stories.tsx b/src/components/forms/Dropdown/Dropdown.stories.tsx index 05eb774398..12a65db07d 100644 --- a/src/components/forms/Dropdown/Dropdown.stories.tsx +++ b/src/components/forms/Dropdown/Dropdown.stories.tsx @@ -2,66 +2,60 @@ import React from 'react' import { Dropdown } from './Dropdown' import { Label } from '../Label/Label' -import { ComponentMeta, ComponentStory } from '@storybook/react' export default { title: 'Components/Dropdown', - component: Dropdown, + component: 'Dropdown', parameters: { docs: { description: { component: ` -### USWDS 3.0 Dropdown component +### Deprecated USWDS 2.x Dropdown component -Source: https://designsystem.digital.gov/components/select/ +⚠️ Dropdown is deprecated and will be removed in the future. Please use the Select component instead. + +Source: https://designsystem.digital.gov/components/dropdown/ `, }, }, }, - argTypes: { - validationStatus: { - options: ['error', 'success'], - control: 'radio', - }, - disabled: { control: 'boolean' }, - }, -} as ComponentMeta +} -const options = ( - <> +export const defaultDropdown = (): React.ReactElement => ( + - + ) -const Template: ComponentStory = (args) => ( - {options} +export const withDefaultValue = (): React.ReactElement => ( + + + + + + ) -export const Default = Template.bind({}) -Default.args = { id: 'input-dropdown', name: 'input-dropdown' } - -export const WithDefaultValue = Template.bind({}) -WithDefaultValue.args = { - id: 'input-dropdown', - name: 'input-dropdown', - defaultValue: 'value2', -} - -export const Disabled = Template.bind({}) -Disabled.args = { - id: 'input-dropdown', - name: 'input-dropdown', - disabled: true, -} - -export const WithLabel = () => ( +export const withLabel = (): React.ReactElement => ( <> - + - {options} + + + + ) + +export const disabled = (): React.ReactElement => ( + + + + + + +) diff --git a/src/components/forms/Dropdown/Dropdown.test.tsx b/src/components/forms/Dropdown/Dropdown.test.tsx index e98363c914..505baff170 100644 --- a/src/components/forms/Dropdown/Dropdown.test.tsx +++ b/src/components/forms/Dropdown/Dropdown.test.tsx @@ -1,58 +1,24 @@ -import React, { ComponentProps } from 'react' -import { render, screen } from '@testing-library/react' +import React from 'react' +import { render } from '@testing-library/react' +jest.mock('../../../deprecation') +import { deprecationWarning } from '../../../deprecation' import { Dropdown } from './Dropdown' describe('Dropdown component', () => { - const renderDropdown = ( - props?: Omit, 'id' | 'name' | 'children'> - ) => { - render( - + it('renders without errors, displaying a deprecation warning', () => { + const { queryByTestId } = render( + ) - - const queryForDropdown = () => screen.queryByRole('combobox') - - return { - queryForDropdown, - } - } - - it('renders without errors', () => { - const { queryForDropdown } = renderDropdown() - - const dropdown = queryForDropdown() - - expect(dropdown).toBeInTheDocument() - expect(dropdown).toHaveClass('usa-select') - }) - - describe('validationStatus', () => { - it('renders with error styling', () => { - const { queryForDropdown } = renderDropdown({ validationStatus: 'error' }) - - const dropdown = queryForDropdown() - - expect(dropdown).toBeInTheDocument() - expect(dropdown).toHaveClass('usa-select') - expect(dropdown).toHaveClass('usa-input--error') - }) - - it('renders with success styling', () => { - const { queryForDropdown } = renderDropdown({ - validationStatus: 'success', - }) - - const dropdown = queryForDropdown() - - expect(dropdown).toBeInTheDocument() - expect(dropdown).toHaveClass('usa-select') - expect(dropdown).toHaveClass('usa-input--success') - }) + expect(queryByTestId('Select')).toBeInTheDocument() + expect(deprecationWarning).toHaveBeenCalledTimes(1) + expect(deprecationWarning).toHaveBeenCalledWith( + 'Dropdown is deprecated and will be removed in the future. Please use the Select component instead.' + ) }) }) diff --git a/src/components/forms/Dropdown/Dropdown.tsx b/src/components/forms/Dropdown/Dropdown.tsx index 0b137a4502..0d162e5769 100644 --- a/src/components/forms/Dropdown/Dropdown.tsx +++ b/src/components/forms/Dropdown/Dropdown.tsx @@ -1,52 +1,11 @@ -import React from 'react' -import classnames from 'classnames' -import { ValidationStatus } from '../../../types/validationStatus' +/* + TODO: Remove this component +*/ -type DropdownProps = { - id: string - name: string - className?: string - children: React.ReactNode - validationStatus?: ValidationStatus - inputRef?: - | string - | ((instance: HTMLSelectElement | null) => void) - | React.RefObject - | null - | undefined -} +import { Select } from '../Select/Select' +import { withDeprecationWarning } from '../../hoc/withDeprecationWarning' -export const Dropdown = ({ - id, - name, - className, - inputRef, - children, - validationStatus, - ...inputProps -}: DropdownProps & JSX.IntrinsicElements['select']): React.ReactElement => { - const isError = validationStatus === 'error' - const isSuccess = validationStatus === 'success' - const classes = classnames( - 'usa-select', - { - 'usa-input--error': isError, - 'usa-input--success': isSuccess, - }, - className - ) - - return ( - - ) -} - -export default Dropdown +export const Dropdown = withDeprecationWarning( + Select, + 'Dropdown is deprecated and will be removed in the future. Please use the Select component instead.' +) diff --git a/src/components/forms/Select/Select.stories.tsx b/src/components/forms/Select/Select.stories.tsx new file mode 100644 index 0000000000..fc83cafdc3 --- /dev/null +++ b/src/components/forms/Select/Select.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react' + +import { Select } from './Select' +import { Label } from '../Label/Label' +import { ComponentMeta, ComponentStory } from '@storybook/react' + +export default { + title: 'Components/Select', + component: Select, + parameters: { + docs: { + description: { + component: ` + ### USWDS 3.0 Select component + + Source: https://designsystem.digital.gov/components/select/ + `, + }, + }, + }, + argTypes: { + validationStatus: { + options: ['error', 'success'], + control: 'radio', + }, + disabled: { control: 'boolean' }, + }, +} as ComponentMeta + +const options = ( + <> + + + + + +) + +const Template: ComponentStory = (args) => ( + +) + +export const Default = Template.bind({}) +Default.args = { id: 'input-select', name: 'input-select' } + +export const WithDefaultValue = Template.bind({}) +WithDefaultValue.args = { + id: 'input-select', + name: 'input-select', + defaultValue: 'value2', +} + +export const Disabled = Template.bind({}) +Disabled.args = { + id: 'input-select', + name: 'input-select', + disabled: true, +} + +export const WithLabel = () => ( + <> + + + +) diff --git a/src/components/forms/Select/Select.test.tsx b/src/components/forms/Select/Select.test.tsx new file mode 100644 index 0000000000..c8699306e4 --- /dev/null +++ b/src/components/forms/Select/Select.test.tsx @@ -0,0 +1,58 @@ +import React, { ComponentProps } from 'react' +import { render, screen } from '@testing-library/react' + +import { Select } from './Select' + +describe('Select component', () => { + const renderSelect = ( + props?: Omit, 'id' | 'name' | 'children'> + ) => { + render( + + ) + + const queryForSelect = () => screen.queryByRole('combobox') + + return { + queryForSelect, + } + } + + it('renders without errors', () => { + const { queryForSelect } = renderSelect() + + const select = queryForSelect() + + expect(select).toBeInTheDocument() + expect(select).toHaveClass('usa-select') + }) + + describe('validationStatus', () => { + it('renders with error styling', () => { + const { queryForSelect } = renderSelect({ validationStatus: 'error' }) + + const select = queryForSelect() + + expect(select).toBeInTheDocument() + expect(select).toHaveClass('usa-select') + expect(select).toHaveClass('usa-input--error') + }) + + it('renders with success styling', () => { + const { queryForSelect } = renderSelect({ + validationStatus: 'success', + }) + + const select = queryForSelect() + + expect(select).toBeInTheDocument() + expect(select).toHaveClass('usa-select') + expect(select).toHaveClass('usa-input--success') + }) + }) +}) diff --git a/src/components/forms/Select/Select.tsx b/src/components/forms/Select/Select.tsx new file mode 100644 index 0000000000..1b76a1bfda --- /dev/null +++ b/src/components/forms/Select/Select.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import classnames from 'classnames' +import { ValidationStatus } from '../../../types/validationStatus' + +type SelectProps = { + id: string + name: string + className?: string + children: React.ReactNode + validationStatus?: ValidationStatus + inputRef?: + | string + | ((instance: HTMLSelectElement | null) => void) + | React.RefObject + | null + | undefined +} + +export const Select = ({ + id, + name, + className, + inputRef, + children, + validationStatus, + ...inputProps +}: SelectProps & JSX.IntrinsicElements['select']): React.ReactElement => { + const isError = validationStatus === 'error' + const isSuccess = validationStatus === 'success' + const classes = classnames( + 'usa-select', + { + 'usa-input--error': isError, + 'usa-input--success': isSuccess, + }, + className + ) + + return ( + + ) +} diff --git a/src/index.ts b/src/index.ts index 600bc57bca..dede489259 100644 --- a/src/index.ts +++ b/src/index.ts @@ -64,6 +64,7 @@ export { InputSuffix } from './components/forms/InputSuffix/InputSuffix' export { Label } from './components/forms/Label/Label' export { Radio } from './components/forms/Radio/Radio' export { RangeInput } from './components/forms/RangeInput/RangeInput' +export { Select } from './components/forms/Select/Select' export { Textarea } from './components/forms/Textarea/Textarea' export { TextInput } from './components/forms/TextInput/TextInput' export { TimePicker } from './components/forms/TimePicker/TimePicker'