Creating intl expected messages from yup is simpler
This commit is contained in:
parent
f15a86ace3
commit
e8d439d63c
4 changed files with 223 additions and 15 deletions
32
NOTICE-js
32
NOTICE-js
|
@ -2706,6 +2706,8 @@ Copyright (c) Microsoft Corporation.
|
||||||
Copyright (c) Microsoft Corporation.
|
Copyright (c) Microsoft Corporation.
|
||||||
** @types/yargs-parser; version 15.0.0 --
|
** @types/yargs-parser; version 15.0.0 --
|
||||||
Copyright (c) Microsoft Corporation.
|
Copyright (c) Microsoft Corporation.
|
||||||
|
** @types/yup; version 0.29.8 --
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
** @typescript-eslint/eslint-plugin; version 4.3.0 -- https://github.com/typescript-eslint/typescript-eslint#readme
|
** @typescript-eslint/eslint-plugin; version 4.3.0 -- https://github.com/typescript-eslint/typescript-eslint#readme
|
||||||
Copyright (c) 2019 TypeScript ESLint and other contributors
|
Copyright (c) 2019 TypeScript ESLint and other contributors
|
||||||
** @typescript-eslint/experimental-utils; version 4.3.0 -- https://github.com/typescript-eslint/typescript-eslint#readme
|
** @typescript-eslint/experimental-utils; version 4.3.0 -- https://github.com/typescript-eslint/typescript-eslint#readme
|
||||||
|
@ -3087,9 +3089,6 @@ Copyright (c) 2014 Sebastien Balayn
|
||||||
Copyright (c) 2016 Michael Pratt
|
Copyright (c) 2016 Michael Pratt
|
||||||
Copyright (c) 2018 Michael Pratt
|
Copyright (c) 2018 Michael Pratt
|
||||||
Copyright (c) 2015 Alexandre Kirszenberg
|
Copyright (c) 2015 Alexandre Kirszenberg
|
||||||
** ccount; version 1.0.5 -- https://github.com/wooorm/ccount#readme
|
|
||||||
(c) Titus Wormer
|
|
||||||
Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>
|
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
@ -3129,8 +3128,6 @@ Copyright (c) 2012, 2011 Ariya Hidayat (http://ariya.ofilabs.com/about) (twitter
|
||||||
** ejs; version 3.1.3 -- https://github.com/mde/ejs
|
** ejs; version 3.1.3 -- https://github.com/mde/ejs
|
||||||
Copyright Joyent, Inc. and other Node contributors.
|
Copyright Joyent, Inc. and other Node contributors.
|
||||||
** eslint-visitor-keys; version 1.3.0 -- https://github.com/eslint/eslint-visitor-keys#readme
|
** eslint-visitor-keys; version 1.3.0 -- https://github.com/eslint/eslint-visitor-keys#readme
|
||||||
** fast-diff; version 1.1.2 -- https://github.com/jhchen/fast-diff#readme
|
|
||||||
Copyright 2006 Google Inc. http://code.google.com/p/google-diff-match-patch
|
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
|
|
||||||
|
@ -3417,6 +3414,9 @@ THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
** ccount; version 1.0.5 -- https://github.com/wooorm/ccount#readme
|
||||||
|
(c) Titus Wormer
|
||||||
|
Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>
|
||||||
** chalk; version 4.1.0 -- https://github.com/chalk/chalk#readme
|
** chalk; version 4.1.0 -- https://github.com/chalk/chalk#readme
|
||||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||||
** char-regex; version 1.0.2 --
|
** char-regex; version 1.0.2 --
|
||||||
|
@ -4023,6 +4023,8 @@ For the general privacy policy governing access to this site, see the Unicode Pr
|
||||||
4. Severability. If any provision of this Agreement is declared invalid or unenforceable, the remaining provisions of this Agreement shall remain in effect.
|
4. Severability. If any provision of this Agreement is declared invalid or unenforceable, the remaining provisions of this Agreement shall remain in effect.
|
||||||
|
|
||||||
5. Entire Agreement. This Agreement constitutes the entire agreement between the parties.
|
5. Entire Agreement. This Agreement constitutes the entire agreement between the parties.
|
||||||
|
** fast-diff; version 1.1.2 -- https://github.com/jhchen/fast-diff#readme
|
||||||
|
Copyright 2006 Google Inc. http://code.google.com/p/google-diff-match-patch
|
||||||
** fb-watchman; version 2.0.1 -- https://facebook.github.io/watchman/
|
** fb-watchman; version 2.0.1 -- https://facebook.github.io/watchman/
|
||||||
Copyright 2014-present Facebook, Inc.
|
Copyright 2014-present Facebook, Inc.
|
||||||
** filelist; version 1.0.1 -- https://github.com/mde/filelist
|
** filelist; version 1.0.1 -- https://github.com/mde/filelist
|
||||||
|
@ -4214,11 +4216,6 @@ Copyright 2012-2015, Yahoo Inc.
|
||||||
Copyright 2015 Yahoo! Inc.
|
Copyright 2015 Yahoo! Inc.
|
||||||
Copyright 2015, Yahoo Inc.
|
Copyright 2015, Yahoo Inc.
|
||||||
Copyright 2012-2015, Yahoo Inc.
|
Copyright 2012-2015, Yahoo Inc.
|
||||||
** istanbul-reports; version 3.0.2 -- https://istanbul.js.org/
|
|
||||||
(c) Sindre Sorhus
|
|
||||||
Copyright 2012-2015 Yahoo! Inc.
|
|
||||||
Copyright 2012-2015, Yahoo Inc.
|
|
||||||
Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
|
|
||||||
Copyright (c) <year> <owner> . All rights reserved.
|
Copyright (c) <year> <owner> . All rights reserved.
|
||||||
|
|
||||||
|
@ -4950,6 +4947,11 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
** istanbul-reports; version 3.0.2 -- https://istanbul.js.org/
|
||||||
|
(c) Sindre Sorhus
|
||||||
|
Copyright 2012-2015 Yahoo! Inc.
|
||||||
|
Copyright 2012-2015, Yahoo Inc.
|
||||||
|
Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
** js-base64; version 2.5.2 -- https://github.com/dankogai/js-base64#readme
|
** js-base64; version 2.5.2 -- https://github.com/dankogai/js-base64#readme
|
||||||
Copyright (c) 2014, Dan Kogai
|
Copyright (c) 2014, Dan Kogai
|
||||||
** lolex; version 2.7.5 -- http://github.com/sinonjs/lolex
|
** lolex; version 2.7.5 -- http://github.com/sinonjs/lolex
|
||||||
|
@ -5692,9 +5694,6 @@ Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||||
** os-locale; version 3.1.0 -- https://github.com/sindresorhus/os-locale#readme
|
** os-locale; version 3.1.0 -- https://github.com/sindresorhus/os-locale#readme
|
||||||
(c) Sindre Sorhus (https://sindresorhus.com)
|
(c) Sindre Sorhus (https://sindresorhus.com)
|
||||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||||
** os-tmpdir; version 1.0.2 -- https://github.com/sindresorhus/os-tmpdir#readme
|
|
||||||
(c) Sindre Sorhus (https://sindresorhus.com)
|
|
||||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
@ -6039,7 +6038,6 @@ Copyright (c) Isaac Z. Schlueter and Contributors
|
||||||
Copyright (c) 2018, Aleck Greenham
|
Copyright (c) 2018, Aleck Greenham
|
||||||
Copyright (c) Facebook, Inc. and its affiliates.
|
Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
** remove-trailing-separator; version 1.1.0 -- https://github.com/darsain/remove-trailing-separator#readme
|
** remove-trailing-separator; version 1.1.0 -- https://github.com/darsain/remove-trailing-separator#readme
|
||||||
** request-promise-core; version 1.1.4 -- https://github.com/request/promise-core#readme
|
|
||||||
|
|
||||||
ISC License
|
ISC License
|
||||||
|
|
||||||
|
@ -6053,6 +6051,9 @@ THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
** os-tmpdir; version 1.0.2 -- https://github.com/sindresorhus/os-tmpdir#readme
|
||||||
|
(c) Sindre Sorhus (https://sindresorhus.com)
|
||||||
|
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||||
** overlayscrollbars; version 1.13.0 -- https://kingsora.github.io/OverlayScrollbars
|
** overlayscrollbars; version 1.13.0 -- https://kingsora.github.io/OverlayScrollbars
|
||||||
Copyright (c) 2017 Rene Haas
|
Copyright (c) 2017 Rene Haas
|
||||||
Copyright (c) 2001 Robert Penner
|
Copyright (c) 2001 Robert Penner
|
||||||
|
@ -6696,7 +6697,6 @@ Copyright (c) 1991-2017 Unicode, Inc.
|
||||||
Copyright (c) 2018 The Khronos Group Inc.
|
Copyright (c) 2018 The Khronos Group Inc.
|
||||||
(c) Microsoft Corporation. Zhu Zuo Quan Suo
|
(c) Microsoft Corporation. Zhu Zuo Quan Suo
|
||||||
Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS
|
Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS
|
||||||
** validate-npm-package-license; version 3.0.4 -- https://github.com/kemitchell/validate-npm-package-license.js#readme
|
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
|
|
||||||
|
@ -7012,6 +7012,7 @@ For these and/or other purposes and motivations, and without any expectation of
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
** request-promise-core; version 1.1.4 -- https://github.com/request/promise-core#readme
|
||||||
** request-promise-native; version 1.0.9 -- https://github.com/request/request-promise-native#readme
|
** request-promise-native; version 1.0.9 -- https://github.com/request/request-promise-native#readme
|
||||||
** require-main-filename; version 2.0.0 -- https://github.com/yargs/require-main-filename#readme
|
** require-main-filename; version 2.0.0 -- https://github.com/yargs/require-main-filename#readme
|
||||||
Copyright (c) 2016
|
Copyright (c) 2016
|
||||||
|
@ -7649,6 +7650,7 @@ Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||||
MIT OR (CC0-1.0 AND MIT)
|
MIT OR (CC0-1.0 AND MIT)
|
||||||
** lodash-joins; version 3.0.1 -- https://github.com/mtraynham/lodash-joins#readme
|
** lodash-joins; version 3.0.1 -- https://github.com/mtraynham/lodash-joins#readme
|
||||||
Copyright (c) Microsoft Corporation.
|
Copyright (c) Microsoft Corporation.
|
||||||
|
** validate-npm-package-license; version 3.0.4 -- https://github.com/kemitchell/validate-npm-package-license.js#readme
|
||||||
** walker; version 1.0.7 -- https://github.com/daaku/nodejs-walker
|
** walker; version 1.0.7 -- https://github.com/daaku/nodejs-walker
|
||||||
Copyright 2013 Naitik Shah
|
Copyright 2013 Naitik Shah
|
||||||
** websocket-driver; version 0.7.4 -- https://github.com/faye/websocket-driver-node
|
** websocket-driver; version 0.7.4 -- https://github.com/faye/websocket-driver-node
|
||||||
|
|
13
app/javascript/common/yup/index.ts
Normal file
13
app/javascript/common/yup/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export * from 'yup';
|
||||||
|
export * from './yup';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NEVER CALL THIS FUNCTION FROM YOUR CODE. IT WILL THROW AN EXCEPTION
|
||||||
|
*
|
||||||
|
* setLocale is handled in `app/javascripts/common/yup/yup.ts`
|
||||||
|
* @throws Error
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
export function setLocale():never {
|
||||||
|
throw new Error('setLocale is handled in `app/javascripts/common/yup/yup.ts`. NEVER call this function from your code');
|
||||||
|
}
|
45
app/javascript/common/yup/yup.spec.ts
Normal file
45
app/javascript/common/yup/yup.spec.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// License: LGPL-3.0-or-later
|
||||||
|
// yup errors are super confusing until formik turns them into something logical
|
||||||
|
import { validateYupSchema, yupToFormErrors } from 'formik';
|
||||||
|
|
||||||
|
import * as yup from './';
|
||||||
|
import {createMessage, YupFail} from './';
|
||||||
|
|
||||||
|
|
||||||
|
describe("yup", () => {
|
||||||
|
describe('.createMessage', () => {
|
||||||
|
const nameLabel = 'Name';
|
||||||
|
const customTranslationId = 'translation.id.path';
|
||||||
|
it('createMessage creates the proper functions', async() => {
|
||||||
|
expect.assertions(4);
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
name: yup.string().label(nameLabel).min(20),
|
||||||
|
id: yup.string().required(createMessage(({ path}) => (new YupFail( customTranslationId, {path})))),
|
||||||
|
address: yup.object({
|
||||||
|
city: yup.string().required(),
|
||||||
|
state: yup.string(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// This is the equivalent of getting errors from the FormikContext
|
||||||
|
let errors:any = null;
|
||||||
|
try {
|
||||||
|
await validateYupSchema({name: "not 20 chars"}, schema, false);
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
//turn into useful errors
|
||||||
|
errors = yupToFormErrors(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(errors.name.message).toStrictEqual([{id:"yup.string.min"}, {path: nameLabel, min: 20}]);
|
||||||
|
expect(errors.id.message).toStrictEqual([{id:customTranslationId}, {path: 'id'}]);
|
||||||
|
expect(errors.address.city.message).toStrictEqual([{id:'yup.mixed.required'}, {path: 'address.city'}]);
|
||||||
|
expect(errors.address.state).toBeUndefined();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
148
app/javascript/common/yup/yup.ts
Normal file
148
app/javascript/common/yup/yup.ts
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { setLocale, LocaleObject, TestMessageParams } from 'yup';
|
||||||
|
import type {IntlShape} from '../../components/intl';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a validation failure from yup
|
||||||
|
* @date 2020-10-24
|
||||||
|
* @export
|
||||||
|
* @class YupFail
|
||||||
|
* @example
|
||||||
|
* const schema = yup.schema({
|
||||||
|
* // we use createMessage to make sure we provide the correct function to min
|
||||||
|
* name: yup.string.min(20, createMessage(
|
||||||
|
* //we're taking the path and min properties of the min function's result
|
||||||
|
* ({path, label, min}) => {
|
||||||
|
* if (label) {
|
||||||
|
* // in yup messages, path represents the name of the field.
|
||||||
|
* // if label is provided, that's a better name
|
||||||
|
* path = label;
|
||||||
|
* }
|
||||||
|
* return new YupFail('translation.id.path',
|
||||||
|
* {path, min} // you HAVE to pass path here, even if the message doesn't use it
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* ))
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* // the yup schema was passed into Formik and formikConfig.errors has been set for the name
|
||||||
|
* // field. We assume the `errors` const has latest formikConfig.errors
|
||||||
|
*
|
||||||
|
* // the below code is part of a TSX component
|
||||||
|
* <ul className="errorListForName">
|
||||||
|
* {
|
||||||
|
* errors.name.map((yupFail:YupFail) => (
|
||||||
|
* <li key={yupFail.id} className="errorItem">{formatMessage(...yupFail.message)</li>
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
* }
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class YupFail {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of Description.
|
||||||
|
* @date 2020-10-24
|
||||||
|
* @param id the translation ID of the failure message
|
||||||
|
* @param values the values to be interpolated into the failure message ID'd by {@link id}
|
||||||
|
*/
|
||||||
|
constructor(readonly id: string, readonly values: Parameters<ToPropResult>[0]) {
|
||||||
|
this.id = id;
|
||||||
|
this.values = values;
|
||||||
|
Object.bind(this, this.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
get message(): Parameters<IntlShape['formatMessage']> {
|
||||||
|
return [{id:this.id}, this.values];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
type RequiredValueProps = {path:string};
|
||||||
|
/**
|
||||||
|
* A spec
|
||||||
|
*/
|
||||||
|
type TestOptionsMessageFn<Extra extends Record<string, any> = Record<string, any>, R = any> =
|
||||||
|
| ((params: Extra & Partial<TestMessageParams> & RequiredValueProps) => R)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type ToPropResult<Extra extends Record<string, any> = Record<string, any>> = TestOptionsMessageFn<Extra, YupFail>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function used for improving typescript safety when manually creating your
|
||||||
|
* own validation messages
|
||||||
|
* @param fn a function
|
||||||
|
*/
|
||||||
|
export function createMessage<Extra extends Record<string, any> = Record<string, any>>(fn: ToPropResult<Extra>): ToPropResult<Extra> {
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
type HoudiniYupLocaleObject = Required<LocaleObject>;
|
||||||
|
|
||||||
|
// TODO: we could optimize this and only run it the first time the module is
|
||||||
|
// loaded
|
||||||
|
|
||||||
|
const locale: HoudiniYupLocaleObject = {
|
||||||
|
mixed: {
|
||||||
|
default: message("yup.mixed.default"),
|
||||||
|
required: message("yup.mixed.required"),
|
||||||
|
oneOf: message("yup.mixed.oneOf"),
|
||||||
|
notOneOf: message("yup.mixed.notOneOf"),
|
||||||
|
},
|
||||||
|
string: {
|
||||||
|
length: message("yup.string.length"),
|
||||||
|
min: message("yup.string.min"),
|
||||||
|
max: message("yup.string.max"),
|
||||||
|
matches: message("yup.string.regex"),
|
||||||
|
email: message("yup.string.email"),
|
||||||
|
url: message("yup.string.url"),
|
||||||
|
uuid: message("yup.string.uuid"),
|
||||||
|
trim: message("yup.string.trim"),
|
||||||
|
lowercase: message("yup.string.lowercase"),
|
||||||
|
uppercase: message("yup.string.uppercase"),
|
||||||
|
},
|
||||||
|
number: {
|
||||||
|
min: message("yup.number.min"),
|
||||||
|
max: message("yup.string.max"),
|
||||||
|
lessThan: message("yup.number.lessThan"),
|
||||||
|
moreThan: message("yup.number.moreThan"),
|
||||||
|
|
||||||
|
positive: message("yup.number.postive"),
|
||||||
|
negative: message("yup.number.negative"),
|
||||||
|
integer: message("yup.number.integer"),
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
min: message("yup.date.min"),
|
||||||
|
max: message("yup.data.max"),
|
||||||
|
},
|
||||||
|
|
||||||
|
boolean: {
|
||||||
|
|
||||||
|
},
|
||||||
|
object: {
|
||||||
|
noUnknown: message("yup.object.noUnknown"),
|
||||||
|
},
|
||||||
|
|
||||||
|
array: {
|
||||||
|
min: message("yup.array.min"),
|
||||||
|
max: message("yup.array.max"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function message<Extra extends Record<string, any> = Record<string, unknown>>(id: string): ToPropResult<Extra> {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
return createMessage(({ path, label, value, originalValue, ...other }) => {
|
||||||
|
if (label) {
|
||||||
|
path = label;
|
||||||
|
}
|
||||||
|
return new YupFail(id, { path, ...other });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setLocale(locale);
|
Loading…
Reference in a new issue