Skip to content

Commit

Permalink
fix(Field.Currency): set limit of safe big number
Browse files Browse the repository at this point in the history
Fixes #3124
  • Loading branch information
tujoworker committed Jan 6, 2024
1 parent 2ca216a commit cbe6950
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import DataValueReadwriteProperties from '../../data-value-readwrite-properties.
| `percent` | `boolean` | _(optional)_ Format a number as percentage. |
| `prefix` | `string` | _(optional)_ Text added before the value input. |
| `suffix` | `string` | _(optional)_ Text added after the value input. |
| `minimum` | `number` | _(optional)_ Validation for inclusive minimum number value (greater than or equal). |
| `maximum` | `number` | _(optional)_ Validation for inclusive maximum number value (less than or equal). |
| `minimum` | `number` | _(optional)_ Validation for inclusive minimum number value (greater than or equal). Defaults to `-Number.MAX_SAFE_INTEGER`. |
| `maximum` | `number` | _(optional)_ Validation for inclusive maximum number value (less than or equal). Defaults to `Number.MAX_SAFE_INTEGER`. |
| `exclusiveMinimum` | `number` | _(optional)_ Validation for exclusive minimum number value (greater than). |
| `exclusiveMaximum` | `number` | _(optional)_ Validation for exclusive maximum number value (less than). |
| `multipleOf` | `number` | _(optional)_ Validation that requires the number to be a multiple of given value. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,7 @@ They should return a transformed value: `(value) => value`
const { value } = useDataValue(props)
```

- `fromInput` transforms the value given by `handleChange` before it is used in the further process flow.

```ts
handleChange(value)
```
- `fromInput` transforms the value given by `handleChange` before it is used in the further process flow. In order to replace the current value, an object with `{ replaceValue: 'new value' }` can be returned. This will trigger a `handleChange` with the given value.

- `toEvent` transforms the internal value before it gets returned by even callbacks such as `onChange`, `onFocus` and `onBlur`.

Expand All @@ -195,3 +191,7 @@ handleChange(value)
| ------------ | --------- | -------------------------------------------------------------------------------------------------------------------------- |
| `capitalize` | `boolean` | _(optional)_ When set to `true`, it will capitalize the first letter of every word, transforming the rest to lowercase. |
| `trim` | `boolean` | _(optional)_ When `true`, it will trim leading and trailing whitespaces on blur, triggering onChange if the value changes. |

```
```
25 changes: 20 additions & 5 deletions packages/dnb-eufemia/src/extensions/forms/Field/Number/Number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,26 @@ function NumberComponent(props: Props) {
return external
}, [])
const fromInput = useCallback(
({ value, numberValue }: { value: string; numberValue: number }) => {
if (value === '') {
(
event: { value?: string; numberValue: number },
currentValue: number
) => {
if (typeof event === 'number') {
event = { numberValue: event }
}

if (event?.value === '') {
return props.emptyValue
}
return numberValue

if (
event?.numberValue > Number.MAX_SAFE_INTEGER ||
event?.numberValue < -Number.MAX_SAFE_INTEGER
) {
return { replaceValue: currentValue } as any
}

return event?.numberValue
},
[props.emptyValue]
)
Expand Down Expand Up @@ -174,8 +189,8 @@ function NumberComponent(props: Props) {
labelDescription,
labelSecondary,
value,
minimum,
maximum,
minimum = -Number.MAX_SAFE_INTEGER,
maximum = Number.MAX_SAFE_INTEGER,
disabled,
info,
warning,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,42 @@ describe('Field.Number', () => {
expect(screen.getByLabelText('Number label')).toBeInTheDocument()
})

it('corrects minimum number', () => {
render(<Field.Number value={-Number.MAX_SAFE_INTEGER} />)

const input = document.querySelector('input')

fireEvent.change(input, {
target: {
value: String(-Number.MAX_SAFE_INTEGER - 1),
},
})

expect(input).toHaveValue(String(-Number.MAX_SAFE_INTEGER - 1))

fireEvent.blur(input)

expect(input).toHaveValue(String(-Number.MAX_SAFE_INTEGER))
})

it('corrects maximum number', () => {
render(<Field.Number value={Number.MAX_SAFE_INTEGER} />)

const input = document.querySelector('input')

fireEvent.change(input, {
target: {
value: String(Number.MAX_SAFE_INTEGER + 1),
},
})

expect(input).toHaveValue(String(Number.MAX_SAFE_INTEGER + 1))

fireEvent.blur(input)

expect(input).toHaveValue(String(Number.MAX_SAFE_INTEGER))
})

it('should support disabled prop', () => {
const { rerender } = render(
<Field.Number label="Disabled label" disabled />
Expand Down
39 changes: 27 additions & 12 deletions packages/dnb-eufemia/src/extensions/forms/hooks/useDataValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,23 @@ export default function useDataValue<
return path ?? id
}, [path, id])

const externalValue = useMemo(() => {
if (props.value !== undefined) {
let value = transformers.current.fromExternal(props.value)

const correctValueInstantly = useCallback(
(value: Value) => {
if (props.capitalize) {
value = toCapitalized(String(value || '')) as Value
}

// Value-prop sent directly to the field has highest priority, overriding any surrounding source
return value
},
[props.capitalize]
)

const externalValue = useMemo(() => {
if (props.value !== undefined) {
// Value-prop sent directly to the field has highest priority, overriding any surrounding source
return correctValueInstantly(
transformers.current.fromExternal(props.value)
)
}

if (inIterate && itemPath) {
Expand All @@ -164,11 +171,11 @@ export default function useDataValue<
return undefined
}, [
props.value,
props.capitalize,
inIterate,
itemPath,
dataContext.data,
path,
correctValueInstantly,
iterateElementValue,
])

Expand Down Expand Up @@ -484,18 +491,23 @@ export default function useDataValue<
argFromInput: Value,
additionalArgs: AdditionalEventArgs = undefined
) => {
let newValue = transformers.current.fromInput(argFromInput)
let newValue = transformers.current.fromInput(
argFromInput,
valueRef.current
) as Value

if (newValue === valueRef.current) {
// Avoid triggering a change if the value was not actually changed. This may be caused by rendering components
// calling onChange even if the actual value did not change.
return
}

if (props.capitalize) {
newValue = toCapitalized(String(newValue || '')) as Value
if (typeof newValue?.['replaceValue'] !== 'undefined') {
newValue = newValue['replaceValue']
}

newValue = correctValueInstantly(newValue)

updateValue(newValue)

changedRef.current = true
Expand All @@ -516,7 +528,7 @@ export default function useDataValue<
}
},
[
props.capitalize,
correctValueInstantly,
updateValue,
onChange,
itemPath,
Expand All @@ -527,14 +539,17 @@ export default function useDataValue<

const handleFocus = useCallback(() => setHasFocus(true), [setHasFocus])

const handleBlur = useCallback(() => {
const correctValueOnBlur = useCallback(() => {
if (props.trim && /^\s|\s$/.test(String(valueRef.current))) {
const value = String(valueRef.current).trim()
handleChange(value as Value)
}
}, [handleChange, props.trim])

const handleBlur = useCallback(() => {
correctValueOnBlur()
setHasFocus(false)
}, [props.trim, setHasFocus, handleChange])
}, [correctValueOnBlur, setHasFocus])

useMountEffect(() => {
dataContext?.handleMountField(identifier)
Expand Down
11 changes: 7 additions & 4 deletions packages/dnb-eufemia/src/extensions/forms/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,13 @@ export interface FieldProps<
continuousValidation?: boolean
errorMessages?: ErrorMessages
// Derivatives
toInput?: (external: Value | undefined) => any
fromInput?: (...args: any[]) => Value | undefined
toEvent?: (internal: Value | undefined) => any
fromExternal?: (...args: any[]) => Value | undefined
toInput?: (external: Value | unknown) => Value | unknown
fromInput?: (
external: Value | unknown,
currentValue: Value
) => (Value | undefined) | { replaceValue: Value }
toEvent?: (internal: Value | undefined) => Value | undefined
fromExternal?: (external: Value | unknown) => Value | undefined
validateRequired?: (
internal: Value | undefined,
{
Expand Down

0 comments on commit cbe6950

Please sign in to comment.