Skip to content

Commit

Permalink
Merge pull request #322 from nginformatica/feat/update-textfield
Browse files Browse the repository at this point in the history
feat: add the select by options
  • Loading branch information
HederAlves authored Sep 6, 2023
2 parents a66a530 + cd62460 commit c537a8a
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flipper-ui",
"version": "0.29.5",
"version": "0.29.6",
"description": "",
"main": "dist/index.js",
"homepage": "https://flipper-ui.ngi.com.br/",
Expand Down
59 changes: 56 additions & 3 deletions src/core/inputs/text-field/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import {
InputAdornment,
TextField as MuiTextField,
TextFieldProps as MuiTextFieldProps,
IconButton as MuiButton
IconButton as MuiButton,
ListItem
} from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import React, {
Expand All @@ -21,11 +23,21 @@ import {
} from '@material-ui/icons'
import IconButton from '../icon-button'
import styled from 'styled-components'
import { when, is, pipe, split, map, zipObj, reject, propEq } from 'ramda'

export interface IOption {
label: string
name?: string
disabled?: boolean
value: string | number
options?: IOption[]
}

export interface TextFieldProps
extends DefaultProps,
Omit<MuiTextFieldProps, 'margin' | 'variant'> {
autoComplete?: string
options?: IOption[] | string
autoFocus?: boolean
defaultValue?: string | number
disabled?: boolean
Expand Down Expand Up @@ -125,13 +137,50 @@ export const useStyles = makeStyles({
}
})

export const coerceComboOptions: (input: string) => IOption[] = when(
is(String),
pipe<string, string[], object, IOption[]>(
split(';'),
map(pipe(split('='), zipObj(['value', 'label']))),
reject(propEq('value', ''))
)
)

export const toLispCase = (name: string) =>
name
.replace(
/([a-z])([A-Z])/g,
(_, first: string, second: string) => first + '-' + second
)
.toLowerCase()

export const HelperBox = (props: IHelperProps) => (
<Helper role='helper-box'>
<IconButton padding='6px 2px' onClick={props.onHelperClick}>
{props.helperIcon || <ContactSupportIcon color='primary' />}
</IconButton>
</Helper>
)
/* Jest-ignore-start ignore next */
export const renderOptions = (options: TextFieldProps['options']) => {
console.log(options)
const comboOptions =
typeof options === 'string' ? coerceComboOptions(options) : options

return (
options &&
// @ts-ignore
comboOptions.map((option: TextFieldProps) => (
<ListItem
id={toLispCase(`option-${option.value}`)}
key={option.value}
disabled={option.disabled}
value={option.value}>
{option.label}
</ListItem>
))
)
}

export const EditBox = (props: IEditProps) => {
return (
Expand Down Expand Up @@ -164,6 +213,7 @@ const renderEndAdornment = (onClear?: () => void) => (
)

export const TextField = ({
options,
margin,
padding,
style = {},
Expand All @@ -178,6 +228,7 @@ export const TextField = ({
fullWidth,
hasClear,
onClear,
children,
...otherProps
}: TextFieldProps) => {
const clearStyle = makeStyles({
Expand Down Expand Up @@ -209,6 +260,7 @@ export const TextField = ({
return (
<Wrapper>
<MuiTextField
select={!!options?.length}
fullWidth={fullWidth}
autoComplete={autoComplete}
error={error}
Expand Down Expand Up @@ -248,8 +300,9 @@ export const TextField = ({
...endAdornment,
...SelectProps
}}
{...otherProps}
/>
{...otherProps}>
{options ? renderOptions(options) : children}
</MuiTextField>
{onHelperClick && (
<HelperBox
helperIcon={helperIcon}
Expand Down
40 changes: 40 additions & 0 deletions src/core/inputs/text-field/text-field.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import * as React from 'react'
import { fireEvent, render, screen } from '@testing-library/react'
import TextField from '@/test/mocks/text-field-mock'
import TextFieldOptions from '@/test/mocks/text-field-options-mock'
import userEvent from '@testing-library/user-event'
import { act } from 'react-dom/test-utils'

const LIST = [
{ label: 'Elm', value: 'elm' },
{ label: 'ReasonML', value: 'reasonml' },
{ label: 'Purescript', value: 'purescript' },
{ label: 'Fable', value: 'fable' }
]

describe('TextField', () => {
it('should render', () => {
render(<TextField inputProps={{ placeholder: 'Description' }} />)
Expand Down Expand Up @@ -92,4 +100,36 @@ describe('TextField', () => {

expect(textField.value).toBe('Hello')
})

it('should update on type', () => {
render(
<TextFieldOptions
inputProps={{ placeholder: 'Description' }}
options={LIST}
/>
)
const textField = screen.getByPlaceholderText(
'Description'
) as HTMLInputElement

fireEvent.change(textField, { target: { value: '' } })

expect(textField.value).toBe('')
})

it('should update on type', () => {
render(
<TextFieldOptions
inputProps={{ placeholder: 'Description' }}
options={JSON.stringify(LIST)}
/>
)
const textField = screen.getByPlaceholderText(
'Description'
) as HTMLInputElement

fireEvent.change(textField, { target: { value: '' } })

expect(textField.value).toBe('')
})
})
37 changes: 37 additions & 0 deletions src/core/inputs/text-field/text-field.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export default {
} as Meta<typeof TextField>

const Template: StoryFn<typeof TextField> = args => <TextField {...args} />
const LIST = [
{ label: 'Elm', value: 'elm' },
{ label: 'ReasonML', value: 'reasonml' },
{ label: 'Purescript', value: 'purescript' },
{ label: 'Fable', value: 'fable' }
]

export const Default = () => <TextField placeholder='Description' />

Expand Down Expand Up @@ -92,3 +98,34 @@ export const useWithSelectAndClear = () => {
</div>
)
}

export const combobox = () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [value, setValue] = useState('fable')

const onClear = () => {
setValue('')
}

const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value)
}

return (
<div>
<TextField
options={LIST}
value={value}
hasClear
onClear={onClear}
onChange={handleChange}
/>
<TextField
value={value}
hasClear
onClear={onClear}
onChange={handleChange}
/>
</div>
)
}
32 changes: 32 additions & 0 deletions src/test/mocks/text-field-options-mock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import TextField, { IOption, TextFieldProps } from '@/core/inputs/text-field'
import * as React from 'react'

interface IProps {
initialOption?: string
inputProps?: TextFieldProps
options?: IOption[] | string
}

const Default = ({ inputProps, initialOption, options }: IProps) => {
const [value, setValue] = React.useState(initialOption ? initialOption : '')

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value)
}

const handleClear = () => {
setValue('')
}

return (
<TextField
value={value}
onChange={handleChange}
onClear={handleClear}
{...inputProps}
options={options}
/>
)
}

export default Default

0 comments on commit c537a8a

Please sign in to comment.