Skip to content

Commit

Permalink
Changed props validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Silen committed Aug 20, 2019
1 parent 3f5db0c commit e440b0e
Show file tree
Hide file tree
Showing 9 changed files with 385 additions and 172 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
.idea
*.iml
*
.iml

node_modules/

lib/

flow-typed

TODO.txt
6 changes: 3 additions & 3 deletions demo/demo.bundle.js

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions src/NumberInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,21 @@ import ButtonUtils from './ButtonUtils';
// noinspection JSUnusedGlobalSymbols
export default class NumberInput extends React.Component<Props, {}> {
static propTypes = {
// eslint-disable-next-line react/require-default-props
value: PropValidators.validateValue,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
allowEmptyValue: PropTypes.bool,
buttonPlacement: PropTypes.oneOf(['right', 'leftAndRight']),
className: PropTypes.string,
defaultValue: PropValidators.validateDefaultValue,
defaultValue: PropTypes.number,
id: PropTypes.string,
minValue: PropValidators.validateMinValue,
maxValue: PropValidators.validateMaxValue,
maxLength: PropValidators.validateMaxLength,
minValue: PropTypes.number,
maxValue: PropTypes.number,
maxLength: PropTypes.number,
placeholder: PropTypes.string,
precision: PropValidators.validatePrecision,
precision: PropTypes.number,
showError: PropTypes.bool,
size: PropTypes.oneOf(['mini', 'small', 'large', 'big', 'huge', 'massive']),
stepAmount: PropValidators.validateStepAmount,
stepAmount: PropTypes.number,
valueType: PropTypes.oneOf(['integer', 'decimal'])
};

Expand Down Expand Up @@ -143,6 +142,7 @@ export default class NumberInput extends React.Component<Props, {}> {
};

render(): Element<*> {
PropValidators.validatePropsInDevelopmentMode(this.props);
const { buttonPlacement, className, id } = this.props;

if (buttonPlacement === 'leftAndRight') {
Expand Down
32 changes: 15 additions & 17 deletions src/NumberInput.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import { mount, shallow as renderShallow } from 'enzyme';
import NumberInput from './NumberInput';
import PropValidators from './PropValidators';

let onChangeMock;

process.env.NODE_ENV = 'development';

beforeEach(() => {
onChangeMock = jest.fn();
});
Expand All @@ -15,7 +16,7 @@ describe('propTypes', () => {
const { propTypes } = NumberInput;

it('should validate value prop', () => {
expect(propTypes.value).toBe(PropValidators.validateValue);
expect(propTypes.value).toBe(PropTypes.string.isRequired);
});

it('should validate onChange prop', () => {
Expand All @@ -35,31 +36,31 @@ describe('propTypes', () => {
});

it('should validate defaultValue prop', () => {
expect(propTypes.defaultValue).toBe(PropValidators.validateDefaultValue);
expect(propTypes.defaultValue).toBe(PropTypes.number);
});

it('should validate id prop', () => {
expect(propTypes.id).toBe(PropTypes.string);
});

it('should validate minValue prop', () => {
expect(propTypes.minValue).toBe(PropValidators.validateMinValue);
expect(propTypes.minValue).toBe(PropTypes.number);
});

it('should validate maxValue prop', () => {
expect(propTypes.maxValue).toBe(PropValidators.validateMaxValue);
expect(propTypes.maxValue).toBe(PropTypes.number);
});

it('should validate maxLength prop', () => {
expect(propTypes.maxLength).toBe(PropValidators.validateMaxLength);
expect(propTypes.maxLength).toBe(PropTypes.number);
});

it('should validate placeholder prop', () => {
expect(propTypes.placeholder).toBe(PropTypes.string);
});

it('should validate precision prop', () => {
expect(propTypes.precision).toBe(PropValidators.validatePrecision);
expect(propTypes.precision).toBe(PropTypes.number);
});

it('should validate showError prop', () => {
Expand All @@ -71,7 +72,7 @@ describe('propTypes', () => {
});

it('should validate stepAmount prop', () => {
expect(propTypes.stepAmount).toBe(PropValidators.validateStepAmount);
expect(propTypes.stepAmount).toBe(PropTypes.number);
});

it('should validate valueType prop', () => {
Expand Down Expand Up @@ -265,15 +266,6 @@ describe('changeValue()', () => {
input.simulate('change', { target: { value: '' } });
expect(onChangeMock).toHaveBeenCalledWith('');
});

test('it should not allow changing value beyond maxLength', () => {
const numberInput = mount(<NumberInput value="99" maxLength={2} onChange={onChangeMock} />);
const input = numberInput.find('input');
input.simulate('focus');
input.simulate('keypress', { key: '9' });
expect(input.props().value).toBe('99');
expect(onChangeMock).not.toHaveBeenCalled();
});
});

describe('onInputBlur()', () => {
Expand Down Expand Up @@ -304,6 +296,12 @@ describe('onInputBlur()', () => {
});

describe('render()', () => {
it('should validate props and throw upon invalid prop in development mode', () => {
expect(() =>
renderShallow(<NumberInput allowEmptyValue={false} onChange={onChangeMock} value="" />)
).toThrow();
});

it('should render component correctly when buttonPlacement is "leftAndRight"', () => {
const numberInput = renderShallow(
<NumberInput
Expand Down
67 changes: 37 additions & 30 deletions src/PropValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,73 @@ import Validators from './Validators';
import NumberUtils from './NumberUtils';

export default class PropValidators {
static validateValue(props: Props): ?Error {
if (!props.allowEmptyValue && !props.value) {
return new Error('value is required');
}
static validateValue(props: Props) {
PropValidators.validateEmptyValue(props.allowEmptyValue, props.value);
if (props.allowEmptyValue && !props.value) {
return null;
return;
}
const value = NumberUtils.getParsedValue(props.value, props.valueType);
if (!Validators.isValidValue(value, props.valueType)) {
return new Error(
throw new Error(
'value must be a string that can be parsed to integer/decimal number depending on valueType'
);
}
return null;
}

static validateDefaultValue(props: Props): ?Error {
static validateEmptyValue(allowEmptyValue: boolean, value: string) {
if (!allowEmptyValue && !value) {
throw new Error('value is required');
}
}

static validateDefaultValue(props: Props) {
if (props.defaultValue !== undefined && !Validators.isValidValue(props.defaultValue, props.valueType)) {
return new Error('defaultValue must be integer/decimal number depending on valueType');
throw new Error('defaultValue must be integer/decimal number depending on valueType');
}
return null;
}

static validateMinValue(props: Props): ?Error {
static validateMinValue(props: Props) {
if (props.minValue > props.maxValue) {
return new Error('maxValue must greater than or equal to minValue');
throw new Error('maxValue must greater than or equal to minValue');
}
return PropValidators.validateMinOrMaxValue(props.valueType, props.minValue, 'minValue', props.maxLength);
PropValidators.validateMinOrMaxValue(props.valueType, props.minValue, 'minValue', props.maxLength);
}

static validateMaxValue = (props: Props): ?Error =>
static validateMaxValue = (props: Props) =>
PropValidators.validateMinOrMaxValue(props.valueType, props.maxValue, 'maxValue', props.maxLength);

static validateMinOrMaxValue(
valueType: ValueType,
value: number,
valueName: string,
maxLength: number
): ?Error {
static validateMinOrMaxValue(valueType: ValueType, value: number, valueName: string, maxLength: number) {
if (!Validators.isValidValue(value, valueType)) {
return new Error(`${valueName} must be integer/decimal number depending on valueType`);
throw new Error(`${valueName} must be integer/decimal number depending on valueType`);
}
if (value.toString().length > maxLength) {
return new Error(`${valueName} does not fit in maxLength`);
throw new Error(`${valueName} does not fit in maxLength`);
}
return null;
}

static validateMaxLength(props: Props): ?Error {
return Validators.validatePositiveInteger(props.maxLength, 'maxLength');
static validateMaxLength(props: Props) {
Validators.validatePositiveInteger(props.maxLength, 'maxLength');
}

static validatePrecision(props: Props): ?Error {
return Validators.validatePositiveInteger(props.precision, 'precision');
static validatePrecision(props: Props) {
Validators.validatePositiveInteger(props.precision, 'precision');
}

static validateStepAmount(props: Props): ?Error {
static validateStepAmount(props: Props) {
if (!Validators.isValidValue(props.stepAmount, props.valueType) || props.stepAmount <= 0) {
return new Error('stepAmount must be a positive integer/decimal number depending on valueType');
throw new Error('stepAmount must be a positive integer/decimal number depending on valueType');
}
}

static validatePropsInDevelopmentMode(props: Props) {
if (process.env.NODE_ENV === 'development') {
PropValidators.validateValue(props);
PropValidators.validateDefaultValue(props);
PropValidators.validateMinValue(props);
PropValidators.validateMaxValue(props);
PropValidators.validateMaxLength(props);
PropValidators.validatePrecision(props);
PropValidators.validateStepAmount(props);
}
return null;
}
}
Loading

0 comments on commit e440b0e

Please sign in to comment.