@availity/yup 4.1.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,215 @@
1
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
+ import { MixedSchema, ValidationError } from 'yup';
3
+ import moment, { Moment, unitOfTime } from 'moment';
4
+ import get from 'lodash/get';
5
+ import merge from 'lodash/merge';
6
+
7
+ const defaultOptions = {
8
+ startKey: 'startDate',
9
+ endKey: 'endDate',
10
+ format: 'MM/DD/YYYY',
11
+ };
12
+
13
+ const formats = ['YYYY-MM-DD', 'MMDDYYYY', 'YYYYMMDD'];
14
+
15
+ export default class DateRangeSchema extends MixedSchema<DateRange> {
16
+ startKey: string;
17
+
18
+ endKey: string;
19
+
20
+ format: string;
21
+
22
+ constructor(options?: Options) {
23
+ super({
24
+ type: 'dateRange',
25
+ });
26
+
27
+ const { startKey, endKey, format } = merge({}, defaultOptions, options);
28
+
29
+ // Assign them here so we can use in schema.transform
30
+ this.startKey = startKey;
31
+ this.endKey = endKey;
32
+ this.format = format;
33
+
34
+ this.withMutation((schema) => {
35
+ schema.transform(function mutate(value) {
36
+ const start = get(value, startKey);
37
+ const end = get(value, endKey);
38
+
39
+ return {
40
+ startDate: start ? schema.getValidDate(start) : start,
41
+ endDate: end ? schema.getValidDate(end) : end,
42
+ supportedFormats: [schema.format, ...formats],
43
+ };
44
+ });
45
+ });
46
+ }
47
+
48
+ // Convert the string to a moment object
49
+ getValidDate(value: string | Date | Moment) {
50
+ return moment(value, [this.format, ...formats], true);
51
+ }
52
+
53
+ distance({
54
+ min: { value: minValue, units: minUnits = 'day', errorMessage: minErrorMessage } = { value: 0 },
55
+ max: { value: maxValue, units: maxUnits = 'day', errorMessage: maxErrorMessage } = { value: 0 },
56
+ }: DistanceOptions = {}) {
57
+ return this.test({
58
+ name: 'distance',
59
+ exclusive: true,
60
+ test({ endDate, startDate } = {}) {
61
+ // check if we have min or max set and if both dates are present
62
+ if ((!minValue && !maxValue) || !startDate || !endDate) return true;
63
+
64
+ // if we have a max then check distance between end and start
65
+ if (maxValue && endDate.isAfter(startDate.add(maxValue, maxUnits), 'day')) {
66
+ return new ValidationError(
67
+ maxErrorMessage ||
68
+ `The end date must be within ${maxValue} ${maxUnits}${maxValue > 1 ? 's' : ''} of the start date`,
69
+ {
70
+ startDate,
71
+ endDate,
72
+ },
73
+ this.path
74
+ );
75
+ }
76
+
77
+ // if we have a min the check distance between end and start
78
+ if (minValue && endDate.isBefore(startDate.add(minValue, minUnits), 'day')) {
79
+ return new ValidationError(
80
+ minErrorMessage ||
81
+ `The end date must be greater than ${minValue} ${minUnits}${minValue > 1 ? 's' : ''} of the start date`,
82
+ { startDate, endDate },
83
+ this.path
84
+ );
85
+ }
86
+
87
+ return true;
88
+ },
89
+ });
90
+ }
91
+
92
+ min(min: string, message?: string) {
93
+ // it works for date, but not daterange. maybe that can tell us more about what is going on
94
+ // const minDate = this.getValidDate(min);
95
+
96
+ return this.test({
97
+ message: message || (({ min }: { min: string }) => `Date Range must start on or after ${min}`),
98
+ name: 'min',
99
+ exclusive: true,
100
+ params: { min },
101
+ test({ startDate, supportedFormats } = {}) {
102
+ // return true when no startDate or min set
103
+ if (!startDate || !min) return true;
104
+
105
+ // otherwise check if min is correct format and is after given startDate
106
+ const minDate = moment(min, supportedFormats, true);
107
+
108
+ return minDate.isValid() && minDate.isSameOrBefore(startDate);
109
+ },
110
+ });
111
+ }
112
+
113
+ max(max: string, message?: string) {
114
+ // const maxDate = this.getValidDate(max);
115
+
116
+ return this.test({
117
+ message: message || (({ max }: { max: string }) => `Date Range must end on or before ${max}`),
118
+ name: 'max',
119
+ exclusive: true,
120
+ params: { max },
121
+ test({ endDate, supportedFormats } = {}) {
122
+ // return true when no endDate given or max set
123
+ if (!endDate || !max) return true;
124
+
125
+ // otherwise check if max is correct format and is after given endDate
126
+ const maxDate = moment(max, supportedFormats, true);
127
+
128
+ return maxDate.isValid() && maxDate.isSameOrAfter(endDate);
129
+ },
130
+ });
131
+ }
132
+
133
+ between(min: string, max: string, message?: string) {
134
+ // const minDate = this.getValidDate(min);
135
+ // const maxDate = this.getValidDate(max);
136
+
137
+ return this.test({
138
+ message:
139
+ message || (({ min, max }: { min: string; max: string }) => `Date Range must be between ${min} and ${max}`),
140
+ name: 'between',
141
+ exclusive: true,
142
+ params: { min, max },
143
+ test({ startDate, endDate, supportedFormats } = {}) {
144
+ if (!startDate || !endDate || !min || !max) return true;
145
+
146
+ const minDate = moment(min, supportedFormats, true);
147
+ const maxDate = moment(max, supportedFormats, true);
148
+
149
+ return (
150
+ maxDate.isValid() && minDate.isValid() && maxDate.isSameOrAfter(endDate) && minDate.isSameOrBefore(startDate)
151
+ );
152
+ },
153
+ });
154
+ }
155
+
156
+ isRequired(isRequired = true, msg?: string) {
157
+ return this.test({
158
+ name: 'isRequired',
159
+ exclusive: true,
160
+ message: msg || 'This field is required.',
161
+ test({ startDate, endDate } = {}) {
162
+ return !isRequired || !!(startDate && endDate);
163
+ },
164
+ });
165
+ }
166
+
167
+ typeError({ message }: { message: string }) {
168
+ return this.test({
169
+ name: 'typeError',
170
+ exclusive: true,
171
+ test({ startDate, endDate } = {}) {
172
+ // Set to `any` to pass to ValidationErrors. Docs state string[] is accepted,
173
+ // but types do not allow string[]
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
+ const errors: any = [];
176
+
177
+ if ((!startDate || !endDate) && (startDate || endDate)) {
178
+ errors.push(message || 'Start and End Date are required.');
179
+ }
180
+
181
+ if (startDate && endDate && !startDate.isSameOrBefore(endDate)) {
182
+ errors.push('Start date must come before end date.');
183
+ }
184
+
185
+ if (startDate && !startDate.isValid()) {
186
+ errors.push('Start Date is invalid.');
187
+ }
188
+
189
+ if (endDate && !endDate.isValid()) {
190
+ errors.push('End Date is invalid.');
191
+ }
192
+
193
+ return errors.length > 0 ? new ValidationError(errors, { startDate, endDate }, this.path) : true;
194
+ },
195
+ });
196
+ }
197
+
198
+ _typeCheck(range: { startDate?: Moment; endDate?: Moment } = {}): range is DateRange {
199
+ const { startDate, endDate } = range;
200
+
201
+ return !!startDate && !!endDate && startDate.isValid() && endDate.isValid();
202
+ }
203
+ }
204
+
205
+ type Options = { startKey?: string; endKey?: string; format?: string };
206
+ type DateRange = { startDate?: Moment; endDate?: Moment; supportedFormats?: string[] };
207
+ type DistanceValue = {
208
+ value: number;
209
+ // unitOfTime namespace is provided by moment library
210
+ units?: unitOfTime.DurationConstructor;
211
+ errorMessage?: string;
212
+ };
213
+ type DistanceOptions = { min?: DistanceValue; max?: DistanceValue };
214
+
215
+ export const dateRange = (opts?: Options): DateRangeSchema => new DateRangeSchema(opts);
package/src/index.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { addMethod, array, number, object, string } from 'yup';
2
+ import { AnyObject, Maybe, Optionals } from 'yup/lib/types';
3
+ import BaseSchema, { AnySchema } from 'yup/lib/schema';
4
+ import { Asserts, TypeOf } from 'yup/lib/util/types';
5
+ import { AssertsShape, ObjectShape, TypeOfShape } from 'yup/lib/object';
6
+ import Lazy from 'yup/lib/Lazy';
7
+
8
+ import { avDate } from './date';
9
+ import { dateRange } from './dateRange';
10
+ import isRequired from './isRequired';
11
+ import npi from './npi';
12
+ import phone from './phone';
13
+
14
+ export { avDate, dateRange };
15
+
16
+ addMethod(array, 'isRequired', isRequired);
17
+ addMethod(number, 'isRequired', isRequired);
18
+ addMethod(object, 'isRequired', isRequired);
19
+ addMethod(string, 'isRequired', isRequired);
20
+
21
+ addMethod(number, 'npi', npi);
22
+ addMethod(string, 'npi', npi);
23
+
24
+ addMethod(number, 'phone', phone);
25
+ addMethod(string, 'phone', phone);
26
+
27
+ // Add definitions to yup
28
+ declare module 'yup' {
29
+ interface StringSchema<
30
+ TType extends Maybe<string> = string | undefined,
31
+ TContext extends AnyObject = AnyObject,
32
+ TOut extends TType = TType
33
+ > extends BaseSchema<TType, TContext, TOut> {
34
+ isRequired(required?: boolean, errorMessage?: string): StringSchema<TType, TContext>;
35
+ npi(errorMessage?: string): StringSchema<TType, TContext>;
36
+ phone(errorMessage?: string): StringSchema<TType, TContext>;
37
+ }
38
+
39
+ interface NumberSchema<
40
+ TType extends Maybe<number> = number | undefined,
41
+ TContext extends AnyObject = AnyObject,
42
+ TOut extends TType = TType
43
+ > extends BaseSchema<TType, TContext, TOut> {
44
+ isRequired(required?: boolean, errorMessage?: string): NumberSchema<TType, TContext, TOut>;
45
+ npi(errorMessage?: string): NumberSchema<TType, TContext, TOut>;
46
+ phone(errorMessage?: string): NumberSchema<TType, TContext, TOut>;
47
+ }
48
+
49
+ interface ArraySchema<
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ T extends AnySchema | Lazy<any, any>,
52
+ C extends AnyObject = AnyObject,
53
+ TIn extends Maybe<TypeOf<T>[]> = TypeOf<T>[] | undefined,
54
+ TOut extends Maybe<Asserts<T>[]> = Asserts<T>[] | Optionals<TIn>
55
+ > extends BaseSchema<TIn, C, TOut> {
56
+ isRequired(required?: boolean, errorMessage?: string): ArraySchema<T, C, TIn, TOut>;
57
+ }
58
+
59
+ interface ObjectSchema<
60
+ TShape extends ObjectShape,
61
+ TContext extends AnyObject = AnyObject,
62
+ TIn extends Maybe<TypeOfShape<TShape>> = TypeOfShape<TShape>,
63
+ TOut extends Maybe<AssertsShape<TShape>> = AssertsShape<TShape> | Optionals<TIn>
64
+ > extends BaseSchema<TIn, TContext, TOut> {
65
+ isRequired(required?: boolean, errorMessage?: string): ObjectSchema<TShape, TContext, TIn, TOut>;
66
+ }
67
+ }
@@ -0,0 +1,26 @@
1
+ import { BaseSchema } from 'yup';
2
+
3
+ function isRequired<Schema extends BaseSchema>(this: Schema, isRequired = true, msg?: string): Schema {
4
+ return this.test({
5
+ name: 'isRequired',
6
+ exclusive: true,
7
+ message: msg || 'This field is required.',
8
+ test(value) {
9
+ if (isRequired) {
10
+ // array and string have custom logic
11
+ if (this.schema.type === 'array') {
12
+ return Array.isArray(value) ? value.length > 0 : value !== undefined;
13
+ }
14
+ if (this.schema.type === 'string') {
15
+ return value !== undefined && value !== '';
16
+ }
17
+ // default logic for all other types
18
+ return value !== undefined;
19
+ }
20
+
21
+ return true;
22
+ },
23
+ });
24
+ }
25
+
26
+ export default isRequired;
@@ -1,6 +1,8 @@
1
+ import { BaseSchema } from 'yup';
2
+
1
3
  const INTEGER_REGEX = /^\d*$/;
2
4
 
3
- function npi(msg) {
5
+ function npi<Schema extends BaseSchema>(this: Schema, msg?: string): Schema {
4
6
  return this.test({
5
7
  name: 'npi',
6
8
  exclusive: true,
@@ -10,10 +12,12 @@ function npi(msg) {
10
12
 
11
13
  value += '';
12
14
 
15
+ // is it a number and 10 digits long
13
16
  if (!INTEGER_REGEX.test(value) || value.length !== 10) {
14
17
  return false;
15
18
  }
16
19
 
20
+ // is the first digit 1-4
17
21
  const firstDigit = value.charAt(0);
18
22
  if (['1', '2', '3', '4'].indexOf(firstDigit) < 0) {
19
23
  return false;
@@ -1,6 +1,8 @@
1
+ import { BaseSchema } from 'yup';
2
+
1
3
  const NANP_REGEXP = /^(\+?1[\s.-]?)?\(?[2-9]\d{2}[\s).-]?\s?[2-9]\d{2}[\s.-]?\d{4}$/;
2
4
 
3
- function phone(msg) {
5
+ function phone<Schema extends BaseSchema>(this: Schema, msg: string): Schema {
4
6
  return this.test({
5
7
  name: 'phone',
6
8
  exclusive: true,
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "include": ["."],
4
+ "exclude": ["dist", "build", "node_modules"]
5
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "types": ["jest", "node"],
7
+ "allowJs": true
8
+ },
9
+ "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
10
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2016-present Availity, LLC
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/lib/date.js DELETED
@@ -1,172 +0,0 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
- Object.defineProperty(exports, "__esModule", {
6
- value: true
7
- });
8
- exports["default"] = void 0;
9
-
10
- require("core-js/modules/es.function.name.js");
11
-
12
- require("core-js/modules/es.array.concat.js");
13
-
14
- var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
15
-
16
- var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
17
-
18
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
19
-
20
- var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
21
-
22
- var _createSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/createSuper"));
23
-
24
- var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
25
-
26
- var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
27
-
28
- var _yup = require("yup");
29
-
30
- var _moment = _interopRequireDefault(require("moment"));
31
-
32
- var defaultOpts = {
33
- format: 'MM/DD/YYYY'
34
- };
35
- var formats = ['YYYY-MM-DD', 'MMDDYYYY', 'YYYYMMDD', 'MM-DD-YYYY'];
36
-
37
- var AvDateSchema = /*#__PURE__*/function (_MixedSchema) {
38
- (0, _inherits2["default"])(AvDateSchema, _MixedSchema);
39
-
40
- var _super = (0, _createSuper2["default"])(AvDateSchema);
41
-
42
- function AvDateSchema() {
43
- var _thisSuper, _this;
44
-
45
- var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultOpts,
46
- _ref$format = _ref.format,
47
- format = _ref$format === void 0 ? 'MM/DD/YYYY' : _ref$format;
48
-
49
- (0, _classCallCheck2["default"])(this, AvDateSchema);
50
- _this = _super.call(this, {
51
- type: 'avDate'
52
- });
53
- _this.format = format;
54
- _this.getValidDate = _this.getValidDate.bind((0, _assertThisInitialized2["default"])(_this));
55
-
56
- _this.withMutation(function (schema) {
57
- if (!schema.tests.some(function (test) {
58
- var _test$OPTIONS;
59
-
60
- return (test === null || test === void 0 ? void 0 : (_test$OPTIONS = test.OPTIONS) === null || _test$OPTIONS === void 0 ? void 0 : _test$OPTIONS.name) === 'typeError';
61
- })) {
62
- (0, _get2["default"])((_thisSuper = (0, _assertThisInitialized2["default"])(_this), (0, _getPrototypeOf2["default"])(AvDateSchema.prototype)), "typeError", _thisSuper).call(_thisSuper, 'Date is invalid.');
63
- }
64
-
65
- schema.transform(function mutate(value) {
66
- return schema.getValidDate(value);
67
- });
68
- });
69
-
70
- return _this;
71
- }
72
-
73
- (0, _createClass2["default"])(AvDateSchema, [{
74
- key: "_typeCheck",
75
- value: function _typeCheck(value) {
76
- // So as long as the passed in value is defined, moment._i will contain a string value to validate.
77
- // If user enters a date and then removes it, should not show a typeError
78
- // Note: this does not prevent other tests, like isRequired, from showing messages
79
- // If user has touched a required field, required error message should still show
80
- return value.isValid() || value._i === '';
81
- }
82
- }, {
83
- key: "getValidDate",
84
- value: function getValidDate(value) {
85
- return (0, _moment["default"])(value, [this.format].concat(formats), true);
86
- }
87
- }, {
88
- key: "min",
89
- value: function min(_min, message) {
90
- var minDate = this.getValidDate(_min);
91
- return this.test({
92
- message: message || "Date must be ".concat(minDate.format(this.format), " or later."),
93
- name: 'min',
94
- exclusive: true,
95
- params: {
96
- min: _min
97
- },
98
- test: function test(value) {
99
- if (!_min || !minDate.isValid()) {
100
- return true;
101
- }
102
-
103
- return value === null || minDate.isSameOrBefore(value, 'MM/DD/YYYY');
104
- }
105
- });
106
- }
107
- }, {
108
- key: "max",
109
- value: function max(_max, message) {
110
- var maxDate = this.getValidDate(_max);
111
- return this.test({
112
- message: message || "Date must be ".concat(maxDate.format(this.format), " or earlier."),
113
- name: 'max',
114
- exclusive: true,
115
- params: {
116
- max: _max
117
- },
118
- test: function test(value) {
119
- if (!_max || !maxDate.isValid()) {
120
- return true;
121
- }
122
-
123
- return value === null || maxDate.isSameOrAfter(value);
124
- }
125
- });
126
- }
127
- }, {
128
- key: "isRequired",
129
- value: function isRequired() {
130
- var _isRequired = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
131
-
132
- var msg = arguments.length > 1 ? arguments[1] : undefined;
133
- return this.test({
134
- name: 'isRequired',
135
- exclusive: true,
136
- message: msg || 'This field is required.',
137
- test: function test(value) {
138
- if (!_isRequired) {
139
- return true;
140
- }
141
-
142
- return value !== undefined;
143
- }
144
- });
145
- }
146
- }, {
147
- key: "between",
148
- value: function between(min, max, msg) {
149
- var inclusivity = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '()';
150
- var minDate = this.getValidDate(min);
151
- var maxDate = this.getValidDate(max); // Can't use arrow function because we rely on 'this' referencing yup's internals
152
-
153
- return this.test({
154
- name: 'between',
155
- exclusive: true,
156
- // Validation errors don't stack
157
- // NOTE: Intentional use of single quotes - yup will handle the string interpolation
158
- message: msg || "Date must be between ".concat(minDate.format(this.format), " and ").concat(maxDate.format(this.format), "."),
159
- test: function test(value) {
160
- if (!value || !min || !max || !minDate.isValid() || !maxDate.isValid()) {
161
- return true;
162
- }
163
-
164
- return value.isBetween(minDate, maxDate, undefined, inclusivity);
165
- }
166
- });
167
- }
168
- }]);
169
- return AvDateSchema;
170
- }(_yup.MixedSchema);
171
-
172
- exports["default"] = AvDateSchema;