basicui-design

Basic schema validation JavaScript library (supports Shamsi / Jalaali date)

Usage no npm install needed!

<script type="module">
  import basicuiDesign from 'https://cdn.skypack.dev/basicui-design';
</script>

README

Basic-Data-Validator

Basic-Data-Validator is a JavaScript library for data validation. At first it was built to be used in React-Final-Form, but it is not limited to React and you can use it in every javascript project.

Basic-Data-Validator contains some general Api to create your own validation schema. If you want more, you can easily define your custom rule.

For Persian users it fully supports Shamsi (Jalaali) date.

Also you can customize all messages or overwrite your message in every rule.

CONTENT

  1. BasicValidator
  2. Schema Builder
  3. API
  4. Custom Messages
  5. EXAMPLES

INSTALL

Install via npm:

npm install basic-data-validator

Basic-Data-Validator supports Jalaali date validation that uses Basic-Shamsi package.

npm install basic-shamsi

USAGE

Import BasicValidator class from the Basic-Data-Validator package. BasicValidator accepts an schema (or schema generator) as input argument and validate values.

Define schema

Lets start with an example:

import BasicValidator from "basic-data-validator";

const myValidator = BasicValidator((builder) =>
    builder.object({
        userName: builder.label("First Name").string().required().userName(),

        password: builder.string().required().password(),

        confirmPassword: builder
            .string()
            .required()
            .equals((data) => data.password, "must be equal to password"),

        address: builder
            .object({
                city: builder.label("CITY").string().required(),

                postalCode: builder
                    .label("POSTAL CODE")
                    .string()
                    .required()
                    .digits(),
            })
            .required(),
    })
);

The above schema defines the following constraints:

  • userName
    • a required string
    • a valid username: may contains any symboles of _ – . \ / @ or digits or letters, having a length of 3 to 20 characters
  • password
    • a required string
    • requires a password rule: must not contains space and ' symbol, having a length of 3 to 20 characters
  • confirmPassword
    • a required string
    • must be equal to value of password field
  • address
    • a required object
  • city
    • a required string
  • postalCode
    • a required string
    • just contains digits

Use in Fnal Form

You can easily use myValidator.validate in React-Final-Form .

<Form
    onSubmit={onSubmit}
    validate={myValidator.validate}
    ...
/>

Using validateAll method enforces all rules to be validated, so you can show all input errors.

<Form
    onSubmit={onSubmit}
    validate={myValidator.validateAll}
    ...
/>

Use in code

Also you can use myValidator in your code:

const data = { userName: "Mahdi", password: null, confirmPassword: "*" };
const is_valid = myValidator.isValid(data); // false
const errors = myValidator.validate(data);
/*
output:
    {
      userName: null,
      password: 'password is a required field',
      confirmPassword: 'must be equal to password',
      address: {
        city: 'CITY is a required field',
        postalCode: 'POSTAL CODE is a required field'
      }
    }
*/

Next section will explains more about BasicValidator

BasicValidator

BasicValidator generates a schema using schema builder.

const myValidator = BasicValidator(SCHEMA, CUSTOM_MESSAGES);

myValidator.isValid(DATA); // returns boolean (true | false)
myValidator.vaidate(DATA); // returns first error for each schema class
myValidator.vaidateAll(DATA); // returns all errors

Define schema

You can define your own schema using schema builder:

const mySchema = builder => builder.object({
    userName: builder.string().required().notContains([" ","'"]),
    password: builder.string().required().notContains([" ","'"])
})

const myValidator = BasicValidator( mySchema );
...

Validate values

BasicValidator returns a validator object with folowing methods:

  • isValid(value): Returns true if the value matches the schema.

  • validate(value) This method match the value and schema. For each schema class it will stop at first error.

  • validateAll(value) This method match the value and schema. It checks all validation rules and extract all errors in each validation schema.

Schema Builder

Helps you to define validation schema. It contains folowing methods:

  • label( text )

    Labels the name of property. Use this method when you are using validator level messages.

    (builder) => builder.label("User Name");
    
  • string( message )

    Generates an instance of StringSchema and also adds a rule that will be matched when value is undefined, null or string.

    (builder) => builder.stirng("JUST STRING VALUE ALLOWED");
    
  • inSensitive()

    All string rules are case sensitive. Using this method right before string() method, makes them case in-sensitive.

    (builder) => builder.inSensitive().stirng();
    
  • number( message )

    Generates an instance of NumberSchema and also adds a rule that will be matched when value is undefined, null or number.

    (builder) => builder.number();
    
  • bool( message )

    Generates an instance of BooleanSchema and also adds a rule that will be matched when value is undefined, null or boolean.

    (builder) => builder.bool();
    
  • shamsi( message )

    Generates an instance of ShamsiSchema and also adds a rule that will be matched when value is undefined, null or valid Shamsi (Jalaali) date.

    (builder) => builder.shamsi();
    
  • object( DEFINED-SCHEMA-OBJECT)

    Generates an instance of ObjectSchema and also adds a rule that will be matched when value is undefined, null or object.

    (builder) => builder.object({});
    

API

There are 5 classes of schema StringSchema, NumberSchema, BooleanSchema, ShamsiSchema and ObjectSchema

StringSchema

This schema class contains folowing methods:

  • custom(fn, message)

    Allows you to write your custom match function to validate a string. This function takes 2 argument: the value (current evaluation value) and the data (current evaluation object)

    var schema = builder => builder.string()
            .custom((value, data) => value > data.startDate, "CUSTOM ERROR MESSAGE"));
    
      Default vaildation error: DefaultMessages.Invalid
    
  • required(message)

    Will not allow undefined or null as value.

    var schema = (builder) => builder.string().required();
    
     Default vaildation error: DefaultMessages.Required
    
  • equals(value, message)

    Just the specified value is allwed.

    var schema = (builder) => builder.string().equals("Mahdi");
    

    Late binding: The value argument can be a function, so it will be calculated each time it is accessed.

    var schema = builder => builder.object({
      ...
      confirmPassword: builder.string()
          .equals(data => data.password)
      ...
    });
    
    Default validation error: DefaultMessages.Equals
    
  • notEquals(value, message)

    The specified value is not allwed.

    var schema = (builder) => builder.string().notEquals("Mahdi");
    

    Late binding: The value argument can be a function, so it will be calculated each time it is accessed.

    var schema = builder => builder.object({
          ...
          lastName: builder.string()
              .notEquals(data => data.firstName)
          ...
      });
    
     Default validation error: DefaultMessages.NotEquals
    
  • email(message)

    Requires the string value to be a valid email address.

    var schema = (builder) => builder.string().email();
    
     Default validation error: DefaultMessages.Email
    
  • url(message)

    Requires the string value to be a valid url address.

    var schema = (builder) => builder.string().url();
    
     Default validation error: DefaultMessages.Url
    
  • match(regex, message)

    Requires the regex pattern to match the string value.

    var schema = (builder) => builder.string().match(/^[0-9]+$/);
    

    Late binding: The regex argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) => builder.string()
            .equals((data) => (data.typeId == 1 ? /^[0-9]+$/ : /^[a-zA-Z]+$/));
    
     Default validation error: DefaultMessages.Matches
    
  • userName(message)

    Requires the string value to be a valid user name (Contains any symboles of _ – . \ / @ or digits or letters, having a length of 3 to 20 characters)

    var schema = (builder) => builder.string().userName();
    
     Default validation error: DefaultMessages.UserName
    
  • strongPassword(message)

    Requires the string value to be a strong password that has at least one lowercase letter, one uppercase letter, one digit, one special character, and is at least 8 characters long.

    var schema = (builder) => builder.string().strongPassword();
    
     Default validation error: DefaultMessages.StrongPassword
    
  • mediumPassword(message)

    Requires the string value to be a medium password that has at least one lowercase letter, one uppercase letter, one digit, one special character, and is at least 6 characters long.

    var schema = (builder) => builder.string().mediumPassword();
    
     Default validation error: DefaultMessages.MediumPassword
    
  • password(message)

    Requires the string value to be a valid password that has at 3 and characters and not contains space and ' (single quotation)

    var schema = (builder) => builder.string().password();
    

    This is a shortcut for .notContains([" ", "'"]).min(3).max(20).

  • digits(message)

    Requires the string value to only contain 0-9.

    var schema = (builder) => builder.string().digits();
    
     Default validation error: DefaultMessages.Digits
    
  • letters(message)

    Requires the string value to only contain a-z or A-Z.

    var schema = (builder) => builder.string().letters();
    
     Default validation error: DefaultMessages.Letters
    
  • alphanum(message)

    Requires the string value to only contain a-z, A-Z or 0-9.

    var schema = (builder) => builder.string().alphanum();
    
     Default validation error: DefaultMessages.AlphaNum
    
  • min(min, message)

    Specifies the minimum length limit for the string value.

    var schema = (builder) => builder.string().min(3);
    

    Late binding: The min argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder.string().min((data) => (data.typeId == 1 ? 3 : 10));
    
     Default validation error: DefaultMessages.MinLen
    
  • max(max, message)

    Specifies the maximum length limit for the string value.

    var schema = (builder) => builder.string().max(3);
    

    Late binding: The max argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder.string().max((data) => (data.typeId == 1 ? 3 : 10));
    
     Default validation error: DefaultMessages.MaxLen
    
  • length(length, message)

    Specifies the length limit for the string value.

    var schema = (builder) => builder.string().length(3);
    

    Late binding: The length argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder.string().length((data) => (data.typeId == 1 ? 3 : 10));
    
     Default validation error: DefaultMessages.Length
    
  • trim(message)

    Requires the string value to be trimmed.

    var schema = (builder) => builder.string().trim();
    
     Default validation error: DefaultMessages.Trim
    
  • lowerCase(lowerCase, message)

    Requires the string value to be lowercase.

    var schema = (builder) => builder.string().lowerCase();
    
     Default validation error: DefaultMessages.LowerCase
    
  • upperCase(upperCase, message)

    Requires the string value to be uppercase.

    var schema = (builder) => builder.string().upperCase();
    
     Default validation error: DefaultMessages.UpperCase
    
  • contains(values, message)

    Requires the string value at least contains one of values.

    var schema = (builder) => builder.string().contains(["US", "UK"]);
    

    Late binding: The values argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) => builder.string()
        .contains((data) => data.typeId == 1 ? ["US", "UK"] : ["RUSSIA", "CHINA"]);
    

    Also the items of values can be a function.

    var schema = builder => builder.string()
          .contains(['US', 'UK', data => data.typeId == 1?  'RUSSIA', 'CHINA']);
    
     Default validation error: DefaultMessages.Contains
    
  • notContains(values, message)

    Requires the string value does not contain any of values.

    var schema = (builder) => builder.string().notContains(["RUSSIA", "CHINA"]);
    

    Late binding: The values argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) => builder.string()
            .notContains((data) => data.typeId == 1 ? ["US", "UK"] : ["RUSSIA", "CHINA"]);
    

    Also the items of values can be a function.

    var schema = builder => builder.string()
          .notContains(['US', 'UK', data => data.typeId == 1?  'RUSSIA', 'CHINA']);
    
    Default validation error: DefaultMessages.NotContains
    
  • oneOf(values, message)

    Requires the string value to be one of values.

    var schema = (builder) => builder.string().oneOf(["RUSSIA", "CHINA"]);
    

    Late binding: The values argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) => builder.string()
            .oneOf((data) => data.typeId == 1 ? ["US", "UK"] : ["RUSSIA", "CHINA"]);
    

    Also the items of values can be a function.

    var schema = builder => builder.string()
          .oneOf(['US', 'UK', data => data.typeId == 1?  'RUSSIA', 'CHINA']);
    

    Default validation error: DefaultMessages.OneOf

  • notOneOf(values, message)

    Requires the string value not to be one of values.

    var schema = (builder) => builder.string().notOneOf(["RUSSIA", "CHINA"]);
    

    Late binding: The values argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) => builder.string()
            .notOneOf((data) => data.typeId == 1 ? ["US", "UK"] : ["RUSSIA", "CHINA"]);
    

    Also the items of values can be a function.

    var schema = builder => builder.string()
          .notOneOf(['US', 'UK', data => data.typeId == 1?  'RUSSIA', 'CHINA']);
    
     Default validation error: DefaultMessages.NotOneOf
    

NumberSchema

This class contains folowing methods:

  • custom(fn, message)

    Refer to string.custom()

  • required(message)

    Refer to string.required()

  • equals(value, message)

    Refer to string.equals()

  • notEquals(value, message)

    Refer to string.notEquals()

  • integer(message)

    Requires the value to be an integer or null or undefined

    var schema = (builder) => builder.number().integer();
    
    Default validation error: DefaultMessages.Integer
    
  • min(min, message)

    Set the minimum value allowed.

    var schema = (builder) => builder.number().min(5);
    

    Late binding: The min argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder.number().min((data) => (data.typeId == 1 ? 5 : 10));
    
    Default validation error: DefaultMessages.Min
    
  • max(max, message)

    Set the maximum value allowed.

    var schema = (builder) => builder.number().max(15);
    

    Late binding: The max argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder.number().max((data) => (data.typeId == 1 ? 15 : 10));
    
    Default validation error: DefaultMessages.Max
    
  • lessThan(max, message)

    Value must be less than max.

    var schema = (builder) => builder.number().lessThan(15);
    

    Late binding: The max argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder.number().lessThan((data) => (data.typeId == 1 ? 15 : 10));
    
    Default validation error: DefaultMessages.LessThan
    
  • moreThan(min, message)

    Value must be more than min.

    var schema = (builder) => builder.number().moreThan(5);
    

    Late binding: The min argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder.number().moreThan((data) => (data.typeId == 1 ? 5 : 10));
    
    Default validation error: DefaultMessages.MoreThan
    
  • positive(message)

    Value must be positive number.

    var schema = (builder) => builder.number().positive();
    
    Default validation error: DefaultMessages.Positive
    
  • negative(message)

    Value must be negative number.

    var schema = (builder) => builder.number().negative();
    
    Default validation error: DefaultMessages.Negative
    
  • oneOf(values, message)

    Refer to string.oneOf()

  • notOneOf(values, message)

    Refer to string.notOneOf()

BooleanSchema

This class contains folowing methods:

ShamsiSchema

This class contains folowing methods:

  • custom(fn, message)

    Refer to string.custom()

  • required(message)

    Refer to string.required()

  • equals(value, message)

    Refer to string.equals()

  • notEquals(value, message)

    Refer to string.notEquals()

  • min(min, message)

    Set the minimum Shamsi date allowed.

    var schema = (builder) => builder.number().min("1400/01/01");
    

    Late binding: The min argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) => builder.string()
            .min((data) => (data.typeId == 1 ? "1400/01/01" : "1400/07/01"));
    
    Default validation error: DefaultMessages.MinShamsi
    
  • max(max, message)

    Set the maximum Shamsi date allowed.

    var schema = (builder) => builder.number().max("1400/01/01");
    

    Late binding: The max argument can be a function, so it will be calculated each time it is accessed.

    var schema = (builder) =>
        builder
            .string()
            .max((data) => (data.typeId == 1 ? "1400/01/01" : "1400/07/01"));
    
    Default validation error: DefaultMessages.MaxShamsi
    

ObjectSchema

This class contains folowing methods:

Custom Messages

In BasicValidator you can custimize error messages in 2 ways.

Rule Level

In each rule you can optinally define your error message.

var schema = (builder) =>
    builder
        .number("VALUE MUST BE A NUMBER")
        .required("CAN NOT BE EMPTY")
        .max(10, "VALUE <= 10");

Validator Level

Also you can customize all or some of messages in each validator. You can find list of all messages in the file basic-data-validator/lib/messages.js. like this:

export const DefaultMessages = {
    Invalid: "{path} is invalid",
    Required: "{path} is a required field",
    Equals: "{path} field must be equal to {value}",
    NotEquals: "{path} field must be not equal to {value}",

    String: "{path} must be a string",
    Email: "{path} must be a valid email",
    Url: "{path} must be a valid URL",
    Matches: '{path} must match the following: "{regex}"',
    UserName: "{path} is not a valid user name",
    StrongPassword: "{path} is not a strong password",
    MediumPassword: "{path} is not a medium password",
    Digits: '{path} must contains only numbers"',
    Letters: '{path} must contains only letters"',
    AlphaNum: '{path} must contains letters or numbers"',

    MinLen: "{path} must be at least {min} characters",
    MaxLen: "{path} must be at most {max} characters",
    Length: "{path} must be exactly {length} characters",
    Trim: "{path} must be a trimmed string",
    LowerCase: "{path} must be a lowercase string",
    UpperCase: "{path} must be a upper case string",
    Contains: "{path} must contains one of the following values: {values}",
    NotContains:
        "{path} must not contains one of the following values: {values}",
    OneOf: "{path} must be one of the following values: {values}",
    NotOneOf: "{path} must not be one of the following values: {values}",

    Number: "{path} must be a number",
    Integer: "{path} must be an integer",
    Min: "{path} must be greater than or equal to {min}",
    Max: "{path} must be less than or equal to {max}",
    LessThan: "{path} must be less than {less}",
    MoreThan: "{path} must be greater than {more}",
    Positive: "{path} must be a positive number",
    Negative: "{path} must be a negative number",

    Boolean: "{path} field must be boolean",

    Shamsi: "{path} field must be Shamsi date",
    MinShamsi: "{path} field must be later than {min}",
    MaxShamsi: "{path} field must be at earlier than {max}",
};

Define your new messages and pass it as second argument in basic-data-validator

const MyValidatorMessages = {
    Required: "CAN NOT BE EMPTY",
    Number: "VALUE MUST BE A NUMBER",
    Max: "VALUE <= {max}",
};

var mySchemaBuilder = (builder) => builder.number().required().max(10, "10");

const myValidator = BasicValidator(mySchemaBuilder, MyValidatorMessages);

Basic-Data-Validator will use default message if you miss to overwrite it.

EXAMPLES

Example 1: validate simple value

const validator = BasicValidator((b) => b.string().strongPassword());

validator.isValid("do,p!#32Z?"); // true
validator.validate("do,p!#32Z?"); // null

validator.isValid("123"); // false
validator.validate("123"); // ? is not a strong password

Example 2: password must not contains user name

const validator = BasicValidator(b => b.object({
    ...
    userName: b.string().required(),
    password: b.inSensitive().string().required(),
        .notContains(d => d.userName, 'password must not contains user name'),
    ...
}));

Example 3: confirm password must be equal to password

const validator = BasicValidator(b => b.object({
    ...
    password: b.string().required(),
    confirmPassword: b.string().required()
        .equals(d => d.password, 'must be equal to password'),
    ...
}));

Example 4: End date must be earlier than start date

const validator = BasicValidator(b => b.object({
    ...
    startDate: b.shamsi().required(),
    endDate: b.shamsi().required()
        .moreThan(d => d.startDate, 'End date must be earlier than start date'),
    ...
}));