Skip to content

feat/migrate react-number-format #356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ const rulesTypescript = {
'@typescript-eslint/no-confusing-void-expression': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',

'@typescript-eslint/ban-types': 'warn',
'@typescript-eslint/no-var-requires': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',
'@typescript-eslint/no-non-null-assertion': 'warn',
Expand Down
30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flipper-ui",
"version": "0.34.3",
"version": "0.34.4",
"description": "React UI based on the @mui/material toolkit for the web",
"main": "dist/index.js",
"author": "NG",
Expand Down Expand Up @@ -35,41 +35,41 @@
"dependencies": {
"@emotion/react": "11.13.0",
"@emotion/styled": "11.13.0",
"@mui/icons-material": "5.16.5",
"@mui/material": "5.16.5",
"@mui/styles": "5.16.5",
"@mui/x-date-pickers": "7.11.1",
"@mui/icons-material": "5.16.6",
"@mui/material": "5.16.6",
"@mui/styles": "5.16.6",
"@mui/x-date-pickers": "7.12.0",
"date-fns": "2.30.0",
"ramda": "0.25.0",
"ramda": "0.30.1",
"react-loading-skeleton": "3.4.0",
"react-number-format": "4.4.1",
"react-number-format": "5.4.0",
"sprintf-js": "1.1.3",
"uuid": "10.0.0"
},
"devDependencies": {
"@babel/cli": "7.24.8",
"@babel/core": "7.24.9",
"@babel/core": "7.25.2",
"@babel/plugin-transform-runtime": "7.24.7",
"@babel/preset-env": "7.25.0",
"@babel/preset-env": "7.25.3",
"@babel/preset-typescript": "7.24.7",
"@faker-js/faker": "8.4.1",
"@storybook/addon-essentials": "7.6.17",
"@storybook/addon-styling-webpack": "1.0.0",
"@storybook/react": "7.6.17",
"@storybook/react-webpack5": "7.6.17",
"@stylistic/eslint-plugin": "2.4.0",
"@stylistic/eslint-plugin": "2.6.1",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/react": "16.0.0",
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.12",
"@types/node": "22.0.0",
"@types/ramda": "0.25.36",
"@types/node": "22.1.0",
"@types/ramda": "0.30.1",
"@types/react": "18.3.3",
"@types/sprintf-js": "1.1.4",
"@types/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"@typescript-eslint/eslint-plugin": "8.0.0",
"@typescript-eslint/parser": "8.0.0",
"babel-loader": "9.1.3",
"babel-plugin-import": "1.13.8",
"babel-plugin-module-resolver": "5.0.2",
Expand All @@ -89,7 +89,7 @@
"react-dom": "18.3.1",
"storybook": "7.6.17",
"styled-components": "6.1.12",
"ts-jest": "29.2.3",
"ts-jest": "29.2.4",
"ts-loader": "9.5.1",
"typescript": "5.5.4",
"webpack": "5.93.0"
Expand Down
4 changes: 2 additions & 2 deletions src/core/data-display/data-table/rows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ const renderEditMode = <D extends Data>(
decimalScale={decimalScale}
value={numeric}
name={column.field.toString()}
onChange={event => {
updateRow(column.field, event.target.value as D[keyof D])
onValueChange={values => {
updateRow(column.field, values.value as D[keyof D])
}}
/>
)
Expand Down
52 changes: 42 additions & 10 deletions src/core/inputs/mask-field/index.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,61 @@
import React from 'react'
import type { ComponentType } from 'react'
import type { NumberFormatProps } from 'react-number-format'
import NumberFormat from 'react-number-format'
import type { ComponentType, SyntheticEvent } from 'react'
import type { NumberFormatValues, SourceInfo } from 'react-number-format'
import { NumericFormat, PatternFormat } from 'react-number-format'
import type { ITextFieldProps } from '@/core/inputs/text-field'
import type { SourceType } from 'react-number-format/types/types'
import TextField from '@/core/inputs/text-field'

export interface MaskFieldProps extends NumberFormatProps {
mask?: string
export interface MaskFieldProps extends Omit<ITextFieldProps, 'size'> {
mask?: string | string[]
type?: 'text' | 'tel' | 'password'
decimalSeparator?: string
format?: string
decimalScale?: number
thousandSeparator?: boolean | string
fixedDecimalScale?: boolean
customInput?: ComponentType<ITextFieldProps>
hasFormat?: boolean
onValueChange?: (values: NumberFormatValues, sourceInfo: SourceInfo) => void
}

export interface IValues {
value: string
floatValue?: number
formattedValue?: string
}

export interface ISource {
event?: SyntheticEvent<HTMLInputElement>
source: SourceType
}

const MaskField = (props: MaskFieldProps) => {
const { customInput, ...otherProps } = props
const {
customInput,
hasFormat,
format = '######',
style,
...otherProps
} = props

return (
// Although react-number-format allow use of additional props,
// shows problem with some props like have been do this
// actually on flipper-ui. (e.g. errors treatment)
<NumberFormat {...otherProps} customInput={customInput || TextField} />
<>
{hasFormat ? (
<PatternFormat
{...otherProps}
format={format}
style={{ ...style }}
customInput={customInput || TextField}
/>
) : (
<NumericFormat
{...otherProps}
style={{ ...style }}
customInput={customInput || TextField}
/>
)}
</>
)
}

Expand Down
30 changes: 30 additions & 0 deletions src/core/inputs/mask-field/mask-field.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { act } from 'react'
import { render, screen } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { MaskFieldWrapper } from '@/test/mocks/mask-field'
import MaskField from '.'

describe('MaskField', () => {
Expand All @@ -16,6 +17,35 @@ describe('MaskField', () => {
expect(input.value).toBe('123')
})

it('should render with format', async () => {
render(
<MaskField hasFormat placeholder='Description' format='#####-##' />
)

const input = screen.getByPlaceholderText(
'Description'
) as HTMLInputElement

await act(async () => await userEvent.type(input, '1234567'))

expect(input.value).toBe('12345-67')
})

it('should render with thousand and decimal separators', async () => {
render(<MaskFieldWrapper />)

const input = screen.getByPlaceholderText(
'Description'
) as HTMLInputElement

expect(input.value).toBe('1.234.567,89')

await act(async () => await userEvent.clear(input))
await act(async () => await userEvent.type(input, '9876543,21'))

expect(input.value).toBe('9.876.543,21')
})

it('should match snapshot', () => {
const { container } = render(<MaskField placeholder='Description' />)

Expand Down
15 changes: 7 additions & 8 deletions src/core/inputs/text-field/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import MuiInputAdornment from '@mui/material/InputAdornment'
import MuiMenuItem from '@mui/material/MenuItem'
import MuiTextField from '@mui/material/TextField'
import { makeStyles } from '@mui/styles'
import { when, is, pipe, split, map, zipObj, reject, propEq } from 'ramda'
import { pipe, split, map, reject } from 'ramda'
import type { DefaultProps } from '../../types'
import type { InputBaseComponentProps } from '@mui/material/InputBase'
import type { TextFieldProps } from '@mui/material/TextField'
Expand Down Expand Up @@ -90,14 +90,13 @@ const useStyles = makeStyles({
}
})

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

const toLispCase = (name: string) =>
name
Expand Down
22 changes: 22 additions & 0 deletions src/test/mocks/mask-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { useState } from 'react'
import MaskField from '@/core/inputs/mask-field'

export const MaskFieldWrapper = () => {
const [value, setValue] = useState<string>('1234567,89')

const handleChange = (value: string) => {
setValue(value)
}

return (
<MaskField
fixedDecimalScale
value={value}
decimalScale={2}
decimalSeparator=','
thousandSeparator='.'
placeholder='Description'
onValueChange={values => handleChange(values.value)}
/>
)
}
Loading