Skip to content

Commit

Permalink
fix: Input now support refs (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
tibuurcio authored Mar 25, 2024
1 parent 6b52fea commit 20a7e47
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 11 deletions.
66 changes: 65 additions & 1 deletion src/components/data-entry/Input/Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { Input } from 'src/components/data-entry/Input/Input'
import { Input, type InputRef } from 'src/components'
import { Button } from 'src/components/general/Button/Button'
import { type Meta, type StoryObj } from '@storybook/react'
import { ExampleStory } from 'src/utils/ExampleStory'
import { useRef } from 'react'
import { Space } from 'src/components'
import { expect, userEvent } from '@storybook/test'

const meta: Meta<typeof Input> = {
title: 'Aquarium/Data Entry/Input',
component: Input,
Expand Down Expand Up @@ -125,3 +131,61 @@ export const WithPrefixAndSuffix: Story = {
placeholder: 'Email',
},
}

export const WithFocusManagement: Story = {
args: {
value: 'Test value',
},
render: (props, meta) => {
const inputRef = useRef<InputRef>(null)

const focus = (cursor: 'start' | 'end' | 'all' = 'start') => {
inputRef.current?.focus({
cursor,
})
}

return (
<ExampleStory title={meta.name}>
<Space direction="vertical" style={{ width: '100%' }}>
<Space wrap>
<Button
onClick={() => {
focus('start')
}}>
Focus at start
</Button>
<Button
onClick={() => {
focus('end')
}}>
Focus at last
</Button>
<Button
onClick={() => {
focus('all')
}}>
Focus to select all
</Button>
</Space>
<br />
<Input {...props} defaultValue="Welcome to the Aquarium" ref={inputRef} />
</Space>
</ExampleStory>
)
},
play: async story => {
const input = story.canvasElement.querySelector('input')
await expect(input).toBeInTheDocument()

const buttons = story.canvasElement.querySelectorAll('button')
await expect(buttons.length).toBe(3)

for (const button of buttons) {
input?.blur()
await userEvent.click(button)
await expect(input).toHaveFocus()
input?.blur()
}
},
}
19 changes: 15 additions & 4 deletions src/components/data-entry/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@ import { Input as AntInput } from 'antd'
import { type InputProps as AntInputProps } from 'antd'
import { ConfigProvider } from 'src/components'
import { type InputRef } from 'antd'
import { forwardRef, type ForwardRefExoticComponent, type Ref, type RefAttributes } from 'react'

export interface IInputProps extends AntInputProps {}
export { Input, type InputRef, type IInputProps }

export { type InputRef }
interface IInputProps extends AntInputProps {}

export const Input = (props: IInputProps) => {
type CompoundedComponent = ForwardRefExoticComponent<IInputProps & RefAttributes<InputRef>> & {
Group: typeof AntInput.Group
Search: typeof AntInput.Search
TextArea: typeof AntInput.TextArea
Password: typeof AntInput.Password
}

const InternalInput = (props: IInputProps, ref: Ref<InputRef>) => {
return (
<ConfigProvider>
<AntInput {...props} />
<AntInput {...props} ref={ref} />
</ConfigProvider>
)
}

const InternalInputWithRef = forwardRef(InternalInput)
const Input = InternalInputWithRef as CompoundedComponent

Input.Group = AntInput.Group
Input.Password = AntInput.Password
Input.Search = AntInput.Search
Expand Down
2 changes: 2 additions & 0 deletions src/components/data-entry/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ export const Radio = (props: IRadioProps) => {
)
}

// TODO Is there a way to type the props from Radio.Group better so that value types are inferred?
// This happens with ant as well. <Radio.Group value={string} /> doesn't get properly propagated to the change event
Radio.Group = AntRadio.Group
Radio.Button = AntRadio.Button
2 changes: 1 addition & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export { Slider, type ISliderProps } from './data-entry/Slider/Slider'
export { Cascader, type ICascaderProps } from './data-entry/Cascader/Cascader'
export { DatePicker, type IDatePickerProps } from './data-entry/DatePicker/DatePicker'
export { Checkbox, type ICheckboxProps } from './data-entry/Checkbox/Checkbox'
export { Input, type IInputProps } from './data-entry/Input/Input'
export { Input, type IInputProps, type InputRef } from './data-entry/Input/Input'
export { InputNumber, type IInputNumberProps } from './data-entry/InputNumber/InputNumber'
export { Switch, type ISwitchProps } from './data-entry/Switch/Switch'
export { Upload, type IUploadProps } from './data-entry/Upload/Upload'
Expand Down
5 changes: 3 additions & 2 deletions src/components/layout/Flex/Flex.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ export const ExampleBasic: Story = {
<Radio.Group
value={value}
onChange={e => {
setValue(e.target.value)
// TODO added a todo in Radio component to see if we can better type this
setValue(e.target.value as string)
}}>
<Radio value="horizontal">horizontal</Radio>
<Radio value="vertical">vertical</Radio>
Expand Down Expand Up @@ -198,7 +199,7 @@ export const ExampleGap: Story = {
<Radio.Group
value={gapSize}
onChange={e => {
setGapSize(e.target.value)
setGapSize(e.target.value as SizeType | 'customize')
}}>
{['small', 'middle', 'large', 'customize'].map(size => (
<Radio key={size} value={size}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { type ChangeEvent, type RefObject } from 'react'
import { Input, type InputRef } from 'antd'
import type { IWorkspaceSelectorProps } from 'src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceSelector'
import type { IWorkspaceSelectorDisplayItem } from 'src/components'
import { Input, type InputRef, type IWorkspaceSelectorDisplayItem } from 'src/components'
import { WorkspaceSelectorContentItems } from 'src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceSelectorContentItems'
import { WorkspaceNoResults } from 'src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceNoResults'
import { WorkspaceSignout } from 'src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceSignout'
Expand Down Expand Up @@ -29,7 +28,7 @@ export function WorkspaceSelectorContent(props: WorkspaceSelectorContentProps) {
onChange={onSearch}
value={searchTerm}
ref={inputRef}
onClick={e => {
onClick={(e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
}}
Expand Down

0 comments on commit 20a7e47

Please sign in to comment.