Skip to content

Commit

Permalink
refactor(number-input): migrate to TypeScript (#1875)
Browse files Browse the repository at this point in the history
* refactor(number-input): migrate to TypeScript

* refactor(number-input): use typecasting, accept `string` type

* fix(number-input): set focus-event-handlers as optional
  • Loading branch information
adnasa committed Apr 28, 2021
1 parent 3ff440f commit d8530a8
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 125 deletions.
5 changes: 5 additions & 0 deletions .changeset/eight-donkeys-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@commercetools-uikit/number-input': patch
---

Migrate `<NumberInput />` to TypeScript
38 changes: 19 additions & 19 deletions packages/components/inputs/number-input/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,25 @@ export default Example;

## Properties

| Props | Type | Required | Default | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------- | :------: | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | | | Used as HTML id property. An id is auto-generated when it is not specified. |
| `name` | `string` | | | Used as HTML name of the input component. property |
| `autoComplete` | `string` | | | Used as HTML `autocomplete` of the input component. property |
| `value` | `<string, number>` | | | Value of the input component. |
| `min` | `number` | | | Value is used as `min` property on input field |
| `max` | `number` | | | Value is used as `max` property on input field |
| `step` | `<number, enum>` | | | Value is used as `step` property on input field&#xA;<br />&#xA;Use the value `any` for inputs which accept an unpredictable amount of decimals. |
| `onChange` | `custom` | | | Called with an event containing the new value. Required when input is not read only. Parent should pass it back as value.&#xA;<br />&#xA;Signature: `(event) => void` |
| `onBlur` | `func` | | | Called when input is blurred&#xA;<br />&#xA;Signature: `(event) => void` |
| `onFocus` | `func` | | | Called when input is focused&#xA;<br />&#xA;Signature: `(event) => void` |
| `isAutofocussed` | `bool` | | | Focus the input on initial render |
| `isDisabled` | `bool` | | | Indicates that the input cannot be modified (e.g not authorized, or changes currently saving). |
| `isReadOnly` | `bool` | | | Indicates that the field is displaying read-only content |
| `placeholder` | `string` | | | Placeholder text for the input |
| `hasError` | `bool` | | | Indicates that input has errors |
| `hasWarning` | `bool` | | | Control to indicate on the input if there are selected values that are potentially invalid |
| `horizontalConstraint` | `enum`<br/>Possible values:<br/>`1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 'scale', 'auto'` | | `'scale'` | Horizontal size limit of the input fields. |
| Props | Type | Required | Default | Description |
| ---------------------- | ----------------------------------------------------------------------------------------------------------- | :------: | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | | | Used as HTML id property. An id is auto-generated when it is not specified. |
| `name` | `string` | | | Used as HTML name of the input component. property |
| `autoComplete` | `string` | | | Used as HTML `autocomplete` of the input component. property |
| `placeholder` | `string` | | | Placeholder text for the input |
| `value` | `union`<br/>Possible values:<br/>`string , number` | | | Value of the input component. |
| `min` | `number` | | | Value is used as `min` property on input field |
| `max` | `number` | | | Value is used as `max` property on input field |
| `step` | `union`<br/>Possible values:<br/>`number , 'any'` | | | Value is used as `step` property on input field&#xA;<br />&#xA;Use the value `any` for inputs which accept an unpredictable amount of decimals. |
| `onChange` | `ChangeEventHandler` | | | Called with an event containing the new value. Required when input is not read only. Parent should pass it back as value. |
| `onBlur` | `FocusEventHandler` | | | Called when input is blurred |
| `onFocus` | `FocusEventHandler` | | | Called when input is focused |
| `isAutofocussed` | `boolean` | | | Focus the input on initial render |
| `isDisabled` | `boolean` | | | Indicates that the input cannot be modified (e.g not authorized, or changes currently saving). |
| `isReadOnly` | `boolean` | | | Indicates that the field is displaying read-only content |
| `hasError` | `boolean` | | | Indicates that input has errors |
| `hasWarning` | `boolean` | | | Control to indicate on the input if there are selected values that are potentially invalid |
| `horizontalConstraint` | `union`<br/>Possible values:<br/>`, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 'scale', 'auto'` | | `'scale'` | Horizontal size limit of the input fields. |

## `onChange`

Expand Down
3 changes: 1 addition & 2 deletions packages/components/inputs/number-input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
"@commercetools-uikit/utils": "12.0.0",
"@emotion/react": "^11.1.1",
"@emotion/styled": "^11.0.0",
"prop-types": "15.7.2",
"react-required-if": "1.0.3"
"prop-types": "15.7.2"
},
"devDependencies": {
"react": "17.0.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,147 +1,145 @@
import React from 'react';
import PropTypes from 'prop-types';
import requiredIf from 'react-required-if';
import React, { ChangeEventHandler, FocusEventHandler } from 'react';
import { useTheme } from '@emotion/react';
import { filterDataAttributes, warning } from '@commercetools-uikit/utils';
import Constraints from '@commercetools-uikit/constraints';
import { getInputStyles } from '@commercetools-uikit/input-utils';

const NumberInput = (props) => {
const theme = useTheme();
return (
<Constraints.Horizontal max={props.horizontalConstraint}>
<input
id={props.id}
name={props.name}
type="number"
autoComplete={props.autoComplete}
value={props.value}
min={props.min}
max={props.max}
step={props.step}
onChange={props.onChange}
onBlur={props.onBlur}
onFocus={props.onFocus}
disabled={props.isDisabled}
placeholder={props.placeholder}
css={getInputStyles(props, theme)}
readOnly={props.isReadOnly}
autoFocus={props.isAutofocussed}
{...filterDataAttributes(props)}
/* ARIA */
aria-readonly={props.isReadOnly}
role="textbox"
contentEditable={!props.isReadOnly}
/>
</Constraints.Horizontal>
);
};

NumberInput.displayName = 'NumberInput';

NumberInput.propTypes = {
type TNumberProps = {
/**
* Used as HTML id property. An id is auto-generated when it is not specified.
*/
id: PropTypes.string,
id?: string;
/**
* Used as HTML name of the input component. property
*/
name: PropTypes.string,
name?: string;
/**
* Used as HTML `autocomplete` of the input component. property
*/
autoComplete: PropTypes.string,
autoComplete?: string;
/**
* Placeholder text for the input
*/
placeholder?: string;
/**
* Value of the input component.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
value: string | number;
/**
* Value is used as `min` property on input field
*/
min: PropTypes.number,
min?: number;
/**
* Value is used as `max` property on input field
*/
max: PropTypes.number,
max?: number;
/**
* Value is used as `step` property on input field
* <br />
* Use the value `any` for inputs which accept an unpredictable amount of decimals.
*/
step: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf(['any'])]),
step?: number | 'any';
/**
* Called with an event containing the new value. Required when input is not read only. Parent should pass it back as value.
* <br />
* Signature: `(event) => void`
*/
onChange: requiredIf(PropTypes.func, (props) => !props.isReadOnly),
onChange?: ChangeEventHandler;
/**
* Called when input is blurred
* <br />
* Signature: `(event) => void`
*/
onBlur: PropTypes.func,
onBlur?: FocusEventHandler;
/**
* Called when input is focused
* <br />
* Signature: `(event) => void`
*/
onFocus: PropTypes.func,
onFocus?: FocusEventHandler;
/**
* Focus the input on initial render
*/
isAutofocussed: PropTypes.bool,
isAutofocussed?: boolean;
/**
* Indicates that the input cannot be modified (e.g not authorized, or changes currently saving).
*/
isDisabled: PropTypes.bool,
isDisabled?: boolean;
/**
* Indicates that the field is displaying read-only content
*/
isReadOnly: PropTypes.bool,
/**
* Placeholder text for the input
*/
placeholder: PropTypes.string,
isReadOnly?: boolean;
/**
* Indicates that input has errors
*/
hasError: PropTypes.bool,
hasError?: boolean;
/**
* Control to indicate on the input if there are selected values that are potentially invalid
*/
hasWarning: PropTypes.bool,
hasWarning?: boolean;
/**
* Horizontal size limit of the input fields.
*/
horizontalConstraint: PropTypes.oneOf([
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
'scale',
'auto',
]),
horizontalConstraint?:
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 16
| 'scale'
| 'auto';
};

NumberInput.defaultProps = {
const defaultProps: Pick<TNumberProps, 'horizontalConstraint'> = {
horizontalConstraint: 'scale',
};

NumberInput.toFormValue = (numberOrString) => {
const NumberInput = (props: TNumberProps) => {
const theme = useTheme();
if (!props.isReadOnly) {
warning(
Boolean(props.onChange),
'NumberInput: `onChange` is required when input is not read only.'
);
}
return (
<Constraints.Horizontal max={props.horizontalConstraint}>
<input
id={props.id}
name={props.name}
type="number"
autoComplete={props.autoComplete}
value={props.value}
min={props.min}
max={props.max}
step={props.step}
onChange={props.onChange}
onBlur={props.onBlur}
onFocus={props.onFocus}
disabled={props.isDisabled}
placeholder={props.placeholder}
css={getInputStyles(props, theme)}
readOnly={props.isReadOnly}
autoFocus={props.isAutofocussed}
{...filterDataAttributes(props)}
/* ARIA */
aria-readonly={props.isReadOnly}
role="textbox"
contentEditable={!props.isReadOnly}
/>
</Constraints.Horizontal>
);
};

NumberInput.displayName = 'NumberInput';
NumberInput.defaultProps = defaultProps;

NumberInput.toFormValue = (numberOrString: number | string) => {
if (
typeof numberOrString === 'number' ||
typeof numberOrString === 'string'
Expand All @@ -151,14 +149,14 @@ NumberInput.toFormValue = (numberOrString) => {
return '';
};

NumberInput.isEmpty = (value) => {
NumberInput.isEmpty = (value: number | string): boolean => {
if (typeof value === 'string') return value.trim().length === 0;
if (typeof value === 'number') return isNaN(value);
return true;
};

NumberInput.hasFractionDigits = (number) => {
const fraction = number % 1;
NumberInput.hasFractionDigits = (number: number | string) => {
const fraction = Number(number) % 1;
warning(
!isNaN(fraction),
'NumberInput.hasFractionDigits may only be called with valid numbers (either as string or number).'
Expand Down
Loading

1 comment on commit d8530a8

@vercel
Copy link

@vercel vercel bot commented on d8530a8 Apr 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.