diff --git a/packages/netlify-cms-widget-number/src/NumberControl.js b/packages/netlify-cms-widget-number/src/NumberControl.js index 96ddd6e6..e6b314c4 100644 --- a/packages/netlify-cms-widget-number/src/NumberControl.js +++ b/packages/netlify-cms-widget-number/src/NumberControl.js @@ -8,6 +8,46 @@ const ValidationErrorTypes = { CUSTOM: 'CUSTOM', }; +export function validateMinMax(value, min, max, field, t) { + let error; + + switch (true) { + case value !== '' && min !== false && max !== false && (value < min || value > max): + error = { + type: ValidationErrorTypes.RANGE, + message: t('editor.editorControlPane.widget.range', { + fieldLabel: field.get('label', field.get('name')), + minValue: min, + maxValue: max, + }), + }; + break; + case value !== '' && min !== false && value < min: + error = { + type: ValidationErrorTypes.RANGE, + message: t('editor.editorControlPane.widget.min', { + fieldLabel: field.get('label', field.get('name')), + minValue: min, + }), + }; + break; + case value !== '' && max !== false && value > max: + error = { + type: ValidationErrorTypes.RANGE, + message: t('editor.editorControlPane.widget.max', { + fieldLabel: field.get('label', field.get('name')), + maxValue: max, + }), + }; + break; + default: + error = null; + break; + } + + return error; +} + export default class NumberControl extends React.Component { static propTypes = { field: ImmutablePropTypes.map.isRequired, @@ -45,47 +85,14 @@ export default class NumberControl extends React.Component { const hasPattern = !!field.get('pattern', false); const min = field.get('min', false); const max = field.get('max', false); - let error; // Pattern overrides min/max logic always: if (hasPattern) { return true; } - switch (true) { - case min !== false && max !== false && (value < min || value > max): - error = { - type: ValidationErrorTypes.RANGE, - message: t('editor.editorControlPane.widget.range', { - fieldLabel: field.get('label', field.get('name')), - minValue: min, - maxValue: max, - }), - }; - break; - case min !== false && value < min: - error = { - type: ValidationErrorTypes.RANGE, - message: t('editor.editorControlPane.widget.min', { - fieldLabel: field.get('label', field.get('name')), - minValue: min, - }), - }; - break; - case max !== false && value > max: - error = { - type: ValidationErrorTypes.RANGE, - message: t('editor.editorControlPane.widget.max', { - fieldLabel: field.get('label', field.get('name')), - maxValue: max, - }), - }; - break; - default: - return true; - } - - return { error }; + const error = validateMinMax(value, min, max, field, t); + return error ? { error } : true; }; render() { diff --git a/packages/netlify-cms-widget-number/src/__tests__/number.spec.js b/packages/netlify-cms-widget-number/src/__tests__/number.spec.js index 4c1663ad..d4610b31 100644 --- a/packages/netlify-cms-widget-number/src/__tests__/number.spec.js +++ b/packages/netlify-cms-widget-number/src/__tests__/number.spec.js @@ -4,6 +4,7 @@ import { render, fireEvent } from 'react-testing-library'; import 'react-testing-library/cleanup-after-each'; import 'jest-dom/extend-expect'; import { NetlifyCmsWidgetNumber } from '../'; +import { validateMinMax } from '../NumberControl'; const NumberControl = NetlifyCmsWidgetNumber.controlComponent; @@ -142,4 +143,73 @@ describe('Number widget', () => { expect(input.value).toBe('0'); }); + + describe('validateMinMax', () => { + const field = { get: jest.fn() }; + field.get.mockReturnValue('label'); + const t = jest.fn(); + t.mockImplementation((_, params) => params); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return error when min max are defined and value is out of range', () => { + const error = validateMinMax(5, 0, 1, field, t); + const expectedMessage = { + fieldLabel: 'label', + minValue: 0, + maxValue: 1, + }; + expect(error).not.toBeNull(); + expect(error).toEqual({ + type: 'RANGE', + message: expectedMessage, + }); + expect(t).toHaveBeenCalledTimes(1); + expect(t).toHaveBeenCalledWith('editor.editorControlPane.widget.range', expectedMessage); + }); + + it('should return error when min is defined and value is out of range', () => { + const error = validateMinMax(5, 6, false, field, t); + const expectedMessage = { + fieldLabel: 'label', + minValue: 6, + }; + expect(error).not.toBeNull(); + expect(error).toEqual({ + type: 'RANGE', + message: expectedMessage, + }); + expect(t).toHaveBeenCalledTimes(1); + expect(t).toHaveBeenCalledWith('editor.editorControlPane.widget.min', expectedMessage); + }); + + it('should return error when max is defined and value is out of range', () => { + const error = validateMinMax(5, false, 3, field, t); + const expectedMessage = { + fieldLabel: 'label', + maxValue: 3, + }; + expect(error).not.toBeNull(); + expect(error).toEqual({ + type: 'RANGE', + message: expectedMessage, + }); + expect(t).toHaveBeenCalledTimes(1); + expect(t).toHaveBeenCalledWith('editor.editorControlPane.widget.max', expectedMessage); + }); + + it('should not return error when min max are defined and value is empty', () => { + const error = validateMinMax('', 0, 1, field, t); + + expect(error).toBeNull(); + }); + + it('should not return error when min max are defined and value is in range', () => { + const error = validateMinMax(0, -1, 1, field, t); + + expect(error).toBeNull(); + }); + }); });