├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── .vscode
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __test__
├── .eslintrc
├── .setup.js
├── AvBaseInput.spec.js
├── AvCheckbox.spec.js
├── AvCheckboxGroup.spec.js
├── AvFeedback.spec.js
├── AvField.spec.js
├── AvForm.spec.js
├── AvGroup.spec.js
├── AvInput.spec.js
├── AvInputContainer.spec.js
├── AvRadio.spec.js
├── AvRadioGroup.spec.js
├── AvValidator.date.spec.js
├── AvValidator.dateRange.spec.js
├── AvValidator.email.spec.js
├── AvValidator.match.spec.js
├── AvValidator.max.spec.js
├── AvValidator.maxchecked.spec.js
├── AvValidator.maxlength.spec.js
├── AvValidator.min.spec.js
├── AvValidator.minchecked.spec.js
├── AvValidator.minlength.spec.js
├── AvValidator.npi.spec.js
├── AvValidator.number.spec.js
├── AvValidator.pattern.spec.js
├── AvValidator.phone.spec.js
├── AvValidator.required.spec.js
├── AvValidator.step.spec.js
└── AvValidator.url.spec.js
├── docs
├── lib
│ ├── Components
│ │ ├── CheckboxPage.js
│ │ ├── FormPage.js
│ │ ├── ValidatorsPage.js
│ │ └── index.js
│ ├── Home
│ │ └── index.js
│ ├── NotFound
│ │ └── index.js
│ ├── UI
│ │ ├── Footer.js
│ │ ├── Layout.js
│ │ ├── Nav.js
│ │ └── index.js
│ ├── app.js
│ ├── examples
│ │ ├── Checkbox.js
│ │ ├── CheckboxDefault.js
│ │ ├── CheckboxFalseValue.js
│ │ ├── CheckboxTrueValue.js
│ │ ├── Form.js
│ │ ├── FormFeedback.js
│ │ ├── FormModel.js
│ │ ├── FormOnInvalidSubmit.js
│ │ ├── FormOnSubmit.js
│ │ ├── FormOnValidSubmit.js
│ │ ├── ValidationAsync.js
│ │ ├── ValidationCustomMessage.js
│ │ ├── ValidationDate.js
│ │ ├── ValidationDateRange.js
│ │ ├── ValidationDateTime.js
│ │ ├── ValidationEmail.js
│ │ ├── ValidationMatch.js
│ │ ├── ValidationMax.js
│ │ ├── ValidationMaxChecked.js
│ │ ├── ValidationMaxLength.js
│ │ ├── ValidationMin.js
│ │ ├── ValidationMinChecked.js
│ │ ├── ValidationMinLength.js
│ │ ├── ValidationNpi.js
│ │ ├── ValidationNumber.js
│ │ ├── ValidationPattern.js
│ │ ├── ValidationPhone.js
│ │ ├── ValidationRequired.js
│ │ ├── ValidationStep.js
│ │ └── import-basic.js
│ └── routes.js
└── static
│ ├── docs.css
│ ├── favicon.ico
│ ├── logo.png
│ └── prism.js
├── mocha-webpack.opts
├── package-lock.json
├── package.json
├── scripts
├── docs
├── publish
└── release
├── src
├── AvBaseInput.js
├── AvCheckbox.js
├── AvCheckboxGroup.js
├── AvFeedback.js
├── AvField.js
├── AvForm.js
├── AvGroup.js
├── AvInput.js
├── AvInputContainer.js
├── AvRadio.js
├── AvRadioGroup.js
├── AvValidator
│ ├── date.js
│ ├── dateRange.js
│ ├── email.js
│ ├── index.js
│ ├── match.js
│ ├── max.js
│ ├── maxchecked.js
│ ├── maxlength.js
│ ├── min.js
│ ├── minchecked.js
│ ├── minlength.js
│ ├── npi.js
│ ├── number.js
│ ├── pattern.js
│ ├── phone.js
│ ├── required.js
│ ├── step.js
│ ├── url.js
│ └── utils.js
└── index.js
├── webpack.base.config.js
├── webpack.config.js
├── webpack.dev.config.js
└── webpack.test.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015-loose", "stage-0", "react"],
3 | "plugins": ["transform-runtime", "transform-proto-to-assign", "transform-class-properties"],
4 | "env": {
5 | "test": {
6 | "plugins": [
7 | ["istanbul", {
8 | "exclude": [
9 | "**/*.spec.js",
10 | ".tmp/**/*"
11 | ]
12 | }]
13 | ]
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | test/coverage/
2 | dist/
3 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends" : "eslint-config-availity/react",
3 | "rules": {
4 | "semi" : [2, "always"],
5 | "no-unused-vars": ["error", { "varsIgnorePattern": "^(unused|omit)" }],
6 | "comma-dangle": [2, "always-multiline"]
7 | },
8 | "parserOptions": {
9 | "sourceType": "module",
10 | "ecmaFeatures": {
11 | "jsx": true
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /__test__/coverage
2 | node_modules
3 | /dist
4 | /build
5 | /lib
6 | /.tmp
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 10
4 | cache:
5 | directories:
6 | - node_modules
7 | script:
8 | - npm run ci
9 | after_success:
10 | - npm run report-coverage
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "jira-plugin.workingProject": ""
3 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Availity
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.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/Availity/availity-reactstrap-validation) [](https://coveralls.io/github/Availity/availity-reactstrap-validation?branch=master)
2 |
3 | # availity-reactstrap-validation
4 |
5 | Easy to use React validation components compatible for reactstrap.
6 |
7 | ## Important Update - 4th January, 2021
8 |
9 | This library has been **depreciated** in the favour of [another one](https://availity.github.io/availity-react/form/index). Please, plan your next projects and update your previous ones accordingly.
10 | [This Guide](https://availity.github.io/availity-react/form/migrating) might be useful for you in order to migrate to the new library.
11 |
12 | ## Installation
13 |
14 | Install `availity-reactstrap-validation` and `reactstrap` via NPM
15 |
16 | ```sh
17 | npm install --save availity-reactstrap-validation reactstrap
18 | ```
19 |
20 | If applicable, install a `Promise` polyfill. For example:
21 |
22 | ```sh
23 | npm install es6-promise --save
24 | ```
25 |
26 | The polyfill can be applied into your web application by using tools like Webpack or Babel.
27 |
28 | Import the components you need, example:
29 |
30 | ```js
31 | import { AvField } from 'availity-reactstrap-validation';
32 | ```
33 |
34 | ## Development
35 |
36 | Install dependencies:
37 |
38 | ```sh
39 | npm install
40 | ```
41 |
42 | Run examples at [http://localhost:8080/](http://localhost:8080/) with Webpack dev server:
43 |
44 | ```sh
45 | npm start
46 | ```
47 |
48 | Run tests:
49 |
50 | ```sh
51 | npm test
52 | ```
53 |
54 | Run tests & coverage report:
55 |
56 | ```sh
57 | npm run test:coverage
58 | ```
59 |
60 | Watch tests:
61 |
62 | ```sh
63 | npm run test:watch
64 | ```
65 |
66 | ## Disclaimer
67 | Open source software components distributed or made available in the Availity Materials are licensed to Company under the terms of the applicable open source license agreements, which may be found in text files included in the Availity Materials.
68 |
69 | ## LICENSE [MIT](LICENSE)
70 |
--------------------------------------------------------------------------------
/__test__/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends" : "eslint-config-availity/react",
3 | "env": {
4 | "mocha": true,
5 | "node": true
6 | },
7 | "globals": {
8 | "sinon": true,
9 | "expect": true
10 | },
11 | "rules": {
12 | "semi" : [2, "always"],
13 | "comma-dangle": [2, "always-multiline"]
14 | },
15 | "parserOptions": {
16 | "sourceType": "module",
17 | "ecmaFeatures": {
18 | "jsx": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/__test__/.setup.js:
--------------------------------------------------------------------------------
1 | var jsdom = require('jsdom');
2 | const { JSDOM } = jsdom;
3 | var chai = require('chai');
4 | var sinon = require('sinon');
5 | var sinonChai = require('sinon-chai');
6 | var chaiAsPromised = require('chai-as-promised');
7 | var chaiEnzyme = require('chai-enzyme');
8 |
9 | var exposedProperties = ['window', 'navigator', 'document'];
10 |
11 | chai.use(sinonChai);
12 | chai.use(chaiAsPromised);
13 | chai.use(chaiEnzyme());
14 |
15 | chai.config.includeStack = true;
16 | global.expect = chai.expect;
17 | global.AssertionError = chai.AssertionError;
18 | global.Assertion = chai.Assertion;
19 | global.assert = chai.assert;
20 | global.expect = chai.expect;
21 | global.sinon = sinon;
22 |
23 | const { document } = new JSDOM('', {
24 | url: 'http://localhost/'
25 | }).window;
26 | global.document = document;
27 | global.window = document.defaultView;
28 | Object.keys(document.defaultView).forEach(property => {
29 | if (typeof global[property] === 'undefined') {
30 | exposedProperties.push(property);
31 | global[property] = document.defaultView[property];
32 | }
33 | });
34 |
35 | global.navigator = {
36 | userAgent: 'node.js'
37 | };
38 |
--------------------------------------------------------------------------------
/__test__/AvCheckbox.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { AvCheckbox } from 'availity-reactstrap-validation';
4 | import { Input } from 'reactstrap';
5 |
6 | let options;
7 | let props;
8 | let inputState;
9 | let component;
10 |
11 | describe('AvCheckbox', () => {
12 | let touched;
13 | let dirty;
14 | let bad;
15 | let error;
16 |
17 | beforeEach(() => {
18 | touched = false;
19 | dirty = false;
20 | bad = false;
21 | error = false;
22 | options = {
23 | context: {
24 | Group: {
25 | getProps: () => ({
26 | name: 'test',
27 | }),
28 | },
29 | FormCtrl: {
30 | inputs: {},
31 | getDefaultValue: ()=> {},
32 | getInputState: ()=> ({}),
33 | hasError: () => error,
34 | isDirty: () => dirty,
35 | isTouched: () => touched,
36 | isBad: () => bad,
37 | isDisabled: () => false,
38 | isReadOnly: () => false,
39 | setDirty: ()=> {},
40 | setTouched: ()=> {},
41 | setBad: ()=> {},
42 | register: ()=> {},
43 | unregister: ()=> {},
44 | validate: ()=> {},
45 | getValidationEvent: ()=> {},
46 | validation: {},
47 | parent: null,
48 | },
49 | },
50 | };
51 | });
52 |
53 | it('should render a reactstrap Input', () => {
54 | const wrapper = shallow( , options);
55 |
56 | expect(wrapper.type()).to.not.be.undefined;
57 | });
58 |
59 | it('should have "is-untouched" class when untouched', () => {
60 | const wrapper = shallow( , options);
61 |
62 | expect(wrapper.find(Input).hasClass('is-untouched')).to.be.true;
63 | expect(wrapper.find(Input).hasClass('is-touched')).to.be.false;
64 | });
65 |
66 | it('should have "is-pristine" class when not dirty', () => {
67 | const wrapper = shallow( , options);
68 |
69 | expect(wrapper.find(Input).hasClass('is-pristine')).to.be.true;
70 | expect(wrapper.find(Input).hasClass('is-dirty')).to.be.false;
71 | });
72 |
73 | it('should have "av-valid" not "is-invalid" class when valid', () => {
74 | const wrapper = shallow( , options);
75 |
76 | expect(wrapper.find(Input).hasClass('av-valid')).to.be.true;
77 | expect(wrapper.find(Input).hasClass('is-invalid')).to.be.false;
78 | });
79 |
80 | it('should have "is-touched" class when touched', () => {
81 | touched = true;
82 | const wrapper = shallow( , options);
83 |
84 | expect(wrapper.find(Input).hasClass('is-untouched')).to.be.false;
85 | expect(wrapper.find(Input).hasClass('is-touched')).to.be.true;
86 | });
87 |
88 | it('should have "is-pristine" class when not dirty', () => {
89 | dirty = true;
90 | const wrapper = shallow( , options);
91 |
92 | expect(wrapper.find(Input).hasClass('is-pristine')).to.be.false;
93 | expect(wrapper.find(Input).hasClass('is-dirty')).to.be.true;
94 | });
95 |
96 | it('should have "is-invalid" not "av-valid" class when invalid and touched', () => {
97 | error = true;
98 | touched = true;
99 | const wrapper = shallow( , options);
100 |
101 | expect(wrapper.find(Input).hasClass('av-valid')).to.be.false;
102 | expect(wrapper.find(Input).hasClass('is-invalid')).to.be.true;
103 | });
104 |
105 | it('should toString the value to add it to the DOM via Input', () => {
106 | const wrapper = shallow( , options);
107 | expect(wrapper.find(Input).prop('value')).to.eql('yes');
108 | });
109 |
110 | describe('on change handler', () => {
111 | beforeEach(() => {
112 | touched = false;
113 | dirty = false;
114 | bad = false;
115 | error = false;
116 | inputState = 'danger';
117 | props = {
118 | name: 'fieldName',
119 | value: 'testValue',
120 | };
121 | options = {
122 | context: {
123 | Group: {
124 | getProps: () => ({
125 | name: 'test',
126 | }),
127 | update: sinon.spy(),
128 | },
129 | FormCtrl: {
130 | inputs: {},
131 | getDefaultValue: sinon.spy(),
132 | getInputState: sinon.stub().returns(inputState),
133 | hasError: () => error,
134 | isDirty: () => dirty,
135 | isTouched: () => touched,
136 | isBad: () => bad,
137 | setDirty: sinon.spy(),
138 | setTouched: sinon.spy(),
139 | setBad: sinon.spy(),
140 | register: sinon.spy(),
141 | unregister: sinon.spy(),
142 | validate: sinon.spy(),
143 | getValidationEvent: () => 'formCtrlValidationEvent',
144 | validation: {},
145 | parent: null,
146 | },
147 | },
148 | };
149 |
150 | component = new AvCheckbox(props);
151 | component.context = options.context;
152 | });
153 |
154 | it('should update group value on change', () => {
155 | const event = {};
156 | component.onChangeHandler(event);
157 | expect(options.context.Group.update).to.have.been.calledWith(event, props.value);
158 | });
159 |
160 | it('should run props on change if it\'s there', () => {
161 | props.onChange = sinon.spy();
162 | component.onChangeHandler();
163 | expect(props.onChange).to.have.been.called;
164 | });
165 | });
166 |
167 | });
168 |
--------------------------------------------------------------------------------
/__test__/AvCheckboxGroup.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { AvCheckboxGroup, AvFeedback } from 'availity-reactstrap-validation';
4 |
5 | let options;
6 |
7 | describe('AvCheckboxGroup', () => {
8 | let touched;
9 | let dirty;
10 | let bad;
11 | let error;
12 |
13 | beforeEach(() => {
14 | touched = false;
15 | dirty = false;
16 | bad = false;
17 | error = false;
18 | options = {
19 | context: {
20 | FormCtrl: {
21 | inputs: {},
22 | getDefaultValue: ()=> {},
23 | getInputState: ()=> ({}),
24 | hasError: () => error,
25 | isDirty: () => dirty,
26 | isTouched: () => touched,
27 | isBad: () => bad,
28 | isDisabled: () => false,
29 | isReadOnly: () => false,
30 | setDirty: sinon.spy(),
31 | setTouched: sinon.spy(),
32 | setBad: sinon.spy(),
33 | register: sinon.spy(),
34 | unregister: sinon.spy(),
35 | validate: sinon.spy(),
36 | getValidationEvent: ()=> {},
37 | validation: {},
38 | parent: null,
39 | },
40 | },
41 | };
42 | });
43 |
44 | it('should render a reactstrap Input', () => {
45 | const wrapper = shallow( , options);
46 |
47 | expect(wrapper.type()).to.not.eql(undefined);
48 | });
49 |
50 | it('should register a validation', () => {
51 | const wrapper = shallow( , options);
52 | const instance = wrapper.instance();
53 | expect(instance.validations.required.value).to.be.true;
54 | });
55 |
56 | it('should register then remove a disabled validation', () => {
57 | const wrapper = shallow( , options);
58 | const instance = wrapper.instance();
59 | expect(instance.validations.required.value).to.be.true;
60 | wrapper.setProps({required: false});
61 | expect(instance.validations.required).to.be.undefined;
62 | });
63 |
64 | it('should return the set value', ()=> {
65 | const wrapper = shallow( , options);
66 | const component = wrapper.instance();
67 | component.value = 'boop';
68 | expect(component.getValue()).to.equal('boop');
69 | });
70 |
71 | it('should unregister when unmounted', ()=> {
72 | const wrapper = shallow( , options);
73 | wrapper.unmount();
74 | expect(options.context.FormCtrl.unregister).to.have.been.called;
75 | });
76 |
77 | it('should give default value from value prop', () => {
78 | const wrapper = shallow( , options);
79 | const component = wrapper.instance();
80 | expect(component.value).to.eql('momo;jojo;bobo');
81 | });
82 |
83 | it('should give default value from defaultValue prop when there is no value prop', () => {
84 | const wrapper = shallow( , options);
85 | const component = wrapper.instance();
86 | expect(component.value).to.eql('momo');
87 | });
88 |
89 | it('should update the value when the value prop changes', () => {
90 | const wrapper = shallow( , options);
91 | const component = wrapper.instance();
92 | expect(component.getValue()).to.equal('momo');
93 | wrapper.setProps({value: 'yoyo'});
94 | expect(component.getValue()).to.equal('yoyo');
95 | });
96 |
97 | it('should update the validations when the props change', () => {
98 | const wrapper = shallow( , options);
99 | const component = wrapper.instance();
100 | const spy = sinon.spy(component, 'updateValidations');
101 | wrapper.setProps({required: true});
102 | expect(spy).to.have.been.called;
103 | });
104 |
105 | it('should not update the validations when the props did not change', () => {
106 | const wrapper = shallow( , options);
107 | const component = wrapper.instance();
108 | const spy = sinon.spy(component, 'updateValidations');
109 | wrapper.setProps({defaultValue: 'momo'});
110 | expect(spy).to.not.have.been.called;
111 | });
112 |
113 | it('should give default value from context', () => {
114 | const wrapper = shallow( , options);
115 | const component = wrapper.instance();
116 | component.context.FormCtrl.getDefaultValue = () => {
117 | return 'jiri';
118 | };
119 | expect(component.getDefaultValue()).to.eql({key: 'defaultValue', value: 'jiri'});
120 | });
121 |
122 | it('should give default fallback when no one set up their stuff', () => {
123 | const wrapper = shallow( , options);
124 | const component = wrapper.instance();
125 | expect(component.getDefaultValue()).to.eql({key: 'defaultValue', value: []});
126 | });
127 |
128 | it('should reset properly', () => {
129 | const wrapper = shallow( , options);
130 | const component = wrapper.instance();
131 | component.setState = sinon.spy();
132 | component.reset();
133 | expect(component.value).to.equal('momo');
134 | expect(component.setState).to.have.been.calledWith({value: 'momo'});
135 | expect(options.context.FormCtrl.setDirty).to.have.been.calledWith('test', false);
136 | expect(options.context.FormCtrl.setTouched).to.have.been.calledWith('test', false);
137 | expect(options.context.FormCtrl.setBad).to.have.been.calledWith('test', false);
138 | expect(options.context.FormCtrl.validate).to.have.been.calledWith('test');
139 | });
140 |
141 | it('should reset properly and call props reset', () => {
142 | const spy = sinon.spy();
143 | const wrapper = shallow( , options);
144 | const component = wrapper.instance();
145 | component.reset();
146 | expect(spy).to.have.been.calledWith('momo');
147 | });
148 |
149 | it('should disconnect child context from form registration and validation', () => {
150 | const wrapper = shallow( , options);
151 | const component = wrapper.instance();
152 | options.context.FormCtrl.register.reset();
153 | options.context.FormCtrl.validate.reset();
154 | component.getChildContext().FormCtrl.register('charmander');
155 | component.getChildContext().FormCtrl.validate('squirtle');
156 | expect(options.context.FormCtrl.register).to.not.have.been.called;
157 | expect(options.context.FormCtrl.validate).to.not.have.been.called;
158 | });
159 |
160 | it('should update the group via child context', () => {
161 | const wrapper = shallow( , options);
162 | const component = wrapper.instance();
163 | component.setState = sinon.spy();
164 | component.getChildContext().Group.update({ target: { checked: true } }, 'momo');
165 | expect(component.value).to.deep.equal(['momo']);
166 | expect(component.setState).to.have.been.calledWith({value: ['momo']});
167 | expect(options.context.FormCtrl.validate).to.have.been.called;
168 | });
169 |
170 | it('should trigger the change callback when the value is updated', () => {
171 | const spy = sinon.spy();
172 | const wrapper = shallow( , options);
173 | const component = wrapper.instance();
174 | const event = {};
175 | component.getChildContext().Group.update(event, 'momo').then(() => {
176 | expect(spy).to.have.been.calledWith(event, 'momo');
177 | });
178 | });
179 |
180 | it('should render validation message when sent', () => {
181 | options.context.FormCtrl.getInputState = () => {
182 | return {'errorMessage': 'WHAT ARE YOU DOING?!'};
183 | };
184 | const wrapper = shallow( , options);
185 | expect(wrapper.find(AvFeedback).prop('children')).to.equal('WHAT ARE YOU DOING?!');
186 | });
187 |
188 | it('should show a legend if we defined a label', () => {
189 | const wrapper = shallow( , options);
190 | expect(wrapper.contains(test )).to.be.true;
191 | });
192 | });
193 |
--------------------------------------------------------------------------------
/__test__/AvFeedback.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { AvFeedback } from 'availity-reactstrap-validation';
4 | import { FormFeedback } from 'reactstrap';
5 |
6 | const state = {};
7 | const options = {
8 | context: {
9 | FormCtrl: {},
10 | Group: {
11 | getInputState: () => state,
12 | },
13 | },
14 | };
15 |
16 | describe('AvFeedback', () => {
17 | describe('when there is an error', () => {
18 | beforeEach(() => {
19 | state.color = 'danger';
20 | });
21 |
22 | it('should render with "FormFeedback"', () => {
23 | const wrapper = shallow(Yo! , options);
24 |
25 | expect(wrapper.type()).to.equal(FormFeedback);
26 | });
27 |
28 | it('should render children inside the FormFeedback', () => {
29 | const wrapper = shallow(Yo! , options);
30 |
31 | expect(wrapper.prop('children')).to.equal('Yo!');
32 | });
33 |
34 | it('should render with the props passed in', () => {
35 | const wrapper = shallow(
36 | Yo! ,
37 | options
38 | );
39 |
40 | expect(wrapper.prop('style').textAlign).to.equal('center');
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/__test__/AvGroup.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import { AvGroup } from 'availity-reactstrap-validation';
4 | import { FormGroup } from 'reactstrap';
5 |
6 | describe('AvGroup', function() {
7 | beforeEach(() => {
8 | this.inputState = {color:'danger'};
9 | this.props = {
10 | name: 'fieldName',
11 | };
12 | this.registerSpy = sinon.spy();
13 | this.context = {
14 | FormCtrl: {
15 | getInputState: sinon.stub().returns(this.inputState),
16 | register: this.registerSpy,
17 | },
18 | };
19 | this.options = {context: this.context};
20 | });
21 |
22 | it('should render with "FormGroup"', () => {
23 | const wrapper = shallow(Yo! , this.options);
24 |
25 | expect(wrapper.type()).to.equal(FormGroup);
26 | });
27 |
28 | it('should render color prop based on inputState', () => {
29 | const wrapper = shallow(Yo! , this.options);
30 |
31 | expect(wrapper.prop('className')).to.equal(`text-${this.inputState.color}`);
32 | });
33 |
34 | it('should render children inside the FormGroup', () => {
35 | const wrapper = shallow(Yo! , this.options);
36 |
37 | expect(wrapper.prop('children')).to.equal('Yo!');
38 | });
39 |
40 | it('should render with the props passed in', () => {
41 | const wrapper = shallow(Yo! , this.options);
42 |
43 | expect(wrapper.prop('style').textAlign).to.equal('center');
44 | });
45 |
46 | it('should intercept an input registration', () => {
47 | const wrapper = mount(Yo! , this.options);
48 | expect(wrapper.node.FormCtrl.register).to.not.equal(this.registerSpy);
49 | const input = {props: this.props};
50 | wrapper.node.FormCtrl.register(input);
51 | expect(wrapper.state('input')).to.equal(input);
52 | expect(this.registerSpy).to.have.been.calledWith(input);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/__test__/AvInput.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { AvInput } from 'availity-reactstrap-validation';
4 | import { Input } from 'reactstrap';
5 |
6 | let options;
7 |
8 | describe('AvInput', () => {
9 | let touched;
10 | let dirty;
11 | let bad;
12 | let error;
13 |
14 | beforeEach(() => {
15 | touched = false;
16 | dirty = false;
17 | bad = false;
18 | error = false;
19 | options = {
20 | context: {
21 | FormCtrl: {
22 | inputs: {},
23 | getDefaultValue: ()=> {},
24 | getInputState: ()=> {},
25 | hasError: () => error,
26 | isDirty: () => dirty,
27 | isTouched: () => touched,
28 | isBad: () => bad,
29 | isDisabled: () => false,
30 | isReadOnly: () => false,
31 | setDirty: ()=> {},
32 | setTouched: ()=> {},
33 | setBad: ()=> {},
34 | register: ()=> {},
35 | unregister: ()=> {},
36 | validate: ()=> {},
37 | validationEvent: ()=> {},
38 | parent: null,
39 | },
40 | },
41 | };
42 | });
43 |
44 | it('should render a reactstrap Input', () => {
45 | const wrapper = shallow( , options);
46 |
47 | expect(wrapper.type()).to.equal(Input);
48 | });
49 |
50 | it('should have "is-untouched" class when untouched', () => {
51 | const wrapper = shallow( , options);
52 | expect(wrapper.hasClass('is-untouched')).to.be.true;
53 | expect(wrapper.hasClass('is-touched')).to.be.false;
54 | });
55 |
56 | it('should have "is-pristine" class when not dirty', () => {
57 | const wrapper = shallow( , options);
58 |
59 | expect(wrapper.hasClass('is-pristine')).to.be.true;
60 | expect(wrapper.hasClass('is-dirty')).to.be.false;
61 | });
62 |
63 | it('should have "av-valid" not "is-invalid" class when valid', () => {
64 | const wrapper = shallow( , options);
65 |
66 | expect(wrapper.hasClass('av-valid')).to.be.true;
67 | expect(wrapper.hasClass('is-invalid')).to.be.false;
68 | });
69 |
70 | it('should not have "is-bad-input" class when the input is not "bad"', () => {
71 | const wrapper = shallow( , options);
72 |
73 | expect(wrapper.hasClass('is-bad-input')).to.be.false;
74 | });
75 |
76 | it('should have "is-touched" class when touched', () => {
77 | touched = true;
78 | const wrapper = shallow( , options);
79 |
80 | expect(wrapper.hasClass('is-untouched')).to.be.false;
81 | expect(wrapper.hasClass('is-touched')).to.be.true;
82 | });
83 |
84 | it('should have "is-pristine" class when not dirty', () => {
85 | dirty = true;
86 | const wrapper = shallow( , options);
87 |
88 | expect(wrapper.hasClass('is-pristine')).to.be.false;
89 | expect(wrapper.hasClass('is-dirty')).to.be.true;
90 | });
91 |
92 | it('should have "is-invalid" not "av-valid" class when invalid and touched', () => {
93 | error = true;
94 | touched = true;
95 | const wrapper = shallow( , options);
96 |
97 | expect(wrapper.hasClass('av-valid')).to.be.false;
98 | expect(wrapper.hasClass('is-invalid')).to.be.true;
99 | });
100 |
101 | it('should not have "is-bad-input" class when the input is not "bad"', () => {
102 | bad = true;
103 | const wrapper = shallow( , options);
104 |
105 | expect(wrapper.hasClass('is-bad-input')).to.be.true;
106 | });
107 |
108 | it('should allow custom classes', () => {
109 | const wrapper = shallow( , options);
110 |
111 | expect(wrapper.hasClass('yo-yo')).to.be.true;
112 | });
113 |
114 | it('should pass props through to reactstrap\'s Input', () => {
115 | const wrapper = shallow( , options);
116 |
117 | expect(wrapper.prop('type')).to.equal('number');
118 | });
119 | });
120 |
--------------------------------------------------------------------------------
/__test__/AvInputContainer.spec.js:
--------------------------------------------------------------------------------
1 | import { AvInputContainer } from 'availity-reactstrap-validation';
2 |
3 | describe('BaseInput', function() {
4 | beforeEach(() => {
5 | this.inputs = {};
6 | this.updaters = {};
7 | this.component = new AvInputContainer();
8 | this.component._inputs = this.inputs;
9 | this.component._updaters = this.inputs;
10 | });
11 |
12 | describe('component will mount', () => {
13 | it('should get the default value', () => {
14 | this.component.componentWillMount();
15 | expect(this.component._inputs).to.eql({});
16 | });
17 | });
18 |
19 | describe('register input', () => {
20 | it('should throw if the input does not have name', () => {
21 | expect(
22 | this.component.registerInput.bind(this.component, {
23 | props: { type: 'text' },
24 | })
25 | ).to.throw('no "name" prop');
26 | });
27 |
28 | it('should throw if the input does not have props', () => {
29 | expect(this.component.registerInput.bind(this.component, {})).to.throw(
30 | 'no "name" prop'
31 | );
32 | });
33 |
34 | it('should throw if the input is undefined', () => {
35 | expect(this.component.registerInput.bind(this.component)).to.throw(
36 | 'no "name" prop'
37 | );
38 | });
39 |
40 | describe('other input types', () => {
41 | it('should add the input to the inputs object', () => {
42 | const input = { props: { name: 'name', type: 'text' } };
43 | this.component.registerInput(input);
44 | expect(this.inputs[input.props.name]).to.equal(input);
45 | });
46 | });
47 | });
48 |
49 | describe('unregister input', () => {
50 | it('should throw if the input does not have name', () => {
51 | expect(
52 | this.component.unregisterInput.bind(this.component, {
53 | props: { type: 'text' },
54 | })
55 | ).to.throw('no "name" prop');
56 | });
57 |
58 | describe('other input types', () => {
59 | it('should remove the input to the inputs object', () => {
60 | const input = { props: { name: 'name', type: 'text' } };
61 | this.inputs[input.props.name] = input;
62 | this.component.unregisterInput(input);
63 | expect(this.inputs[input.props.name]).to.not.exist;
64 | });
65 |
66 | it('should not care that the input is not registered', () => {
67 | const input = { props: { name: 'name', type: 'text' } };
68 | delete this.inputs[input.props.name];
69 | this.component.unregisterInput(input);
70 | expect(this.inputs[input.props.name]).to.not.exist;
71 | });
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/__test__/AvRadio.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { AvRadio } from 'availity-reactstrap-validation';
4 | import { Input, Label, FormGroup } from 'reactstrap';
5 |
6 | let options;
7 | let props;
8 | let inputState;
9 | let component;
10 |
11 | describe('AvRadio', () => {
12 | let touched;
13 | let dirty;
14 | let bad;
15 | let error;
16 |
17 | beforeEach(() => {
18 | touched = false;
19 | dirty = false;
20 | bad = false;
21 | error = false;
22 | options = {
23 | context: {
24 | Group: {
25 | getProps: () => ({
26 | name: 'test',
27 | }),
28 | },
29 | FormCtrl: {
30 | inputs: {},
31 | getDefaultValue: ()=> {},
32 | getInputState: ()=> ({}),
33 | hasError: () => error,
34 | isDirty: () => dirty,
35 | isTouched: () => touched,
36 | isBad: () => bad,
37 | isDisabled: () => false,
38 | isReadOnly: () => false,
39 | setDirty: ()=> {},
40 | setTouched: ()=> {},
41 | setBad: ()=> {},
42 | register: ()=> {},
43 | unregister: ()=> {},
44 | validate: ()=> {},
45 | getValidationEvent: ()=> {},
46 | validation: {},
47 | parent: null,
48 | },
49 | },
50 | };
51 | });
52 |
53 | it('should render a reactstrap Input', () => {
54 | const wrapper = shallow( , options);
55 |
56 | expect(wrapper.type()).to.not.be.undefined;
57 | });
58 |
59 | it('should have "is-untouched" class when untouched', () => {
60 | const wrapper = shallow( , options);
61 |
62 | expect(wrapper.find(Input).hasClass('is-untouched')).to.be.true;
63 | expect(wrapper.find(Input).hasClass('is-touched')).to.be.false;
64 | });
65 |
66 | it('should have "is-pristine" class when not dirty', () => {
67 | const wrapper = shallow( , options);
68 |
69 | expect(wrapper.find(Input).hasClass('is-pristine')).to.be.true;
70 | expect(wrapper.find(Input).hasClass('is-dirty')).to.be.false;
71 | });
72 |
73 | it('should have "av-valid" not "is-invalid" class when valid', () => {
74 | const wrapper = shallow( , options);
75 |
76 | expect(wrapper.find(Input).hasClass('av-valid')).to.be.true;
77 | expect(wrapper.find(Input).hasClass('is-invalid')).to.be.false;
78 | });
79 |
80 | it('should have "is-touched" class when touched', () => {
81 | touched = true;
82 | const wrapper = shallow( , options);
83 |
84 | expect(wrapper.find(Input).hasClass('is-untouched')).to.be.false;
85 | expect(wrapper.find(Input).hasClass('is-touched')).to.be.true;
86 | });
87 |
88 | it('should have "is-pristine" class when not dirty', () => {
89 | dirty = true;
90 | const wrapper = shallow( , options);
91 |
92 | expect(wrapper.find(Input).hasClass('is-pristine')).to.be.false;
93 | expect(wrapper.find(Input).hasClass('is-dirty')).to.be.true;
94 | });
95 |
96 | it('should have "is-invalid" not "av-valid" class when invalid and touched', () => {
97 | error = true;
98 | touched = true;
99 | const wrapper = shallow( , options);
100 |
101 | expect(wrapper.find(Input).hasClass('av-valid')).to.be.false;
102 | expect(wrapper.find(Input).hasClass('is-invalid')).to.be.true;
103 | });
104 |
105 | it('should toString the value to add it to the DOM via Input', () => {
106 | const wrapper = shallow( , options);
107 | expect(wrapper.find(Input).prop('value')).to.eql('yes');
108 | });
109 |
110 | describe('on change handler', () => {
111 | beforeEach(() => {
112 | touched = false;
113 | dirty = false;
114 | bad = false;
115 | error = false;
116 | inputState = 'danger';
117 | props = {
118 | name: 'fieldName',
119 | value: 'testValue',
120 | };
121 | options = {
122 | context: {
123 | Group: {
124 | getProps: () => ({
125 | name: 'test',
126 | }),
127 | update: sinon.spy(),
128 | },
129 | FormCtrl: {
130 | inputs: {},
131 | getDefaultValue: sinon.spy(),
132 | getInputState: sinon.stub().returns(inputState),
133 | hasError: () => error,
134 | isDirty: () => dirty,
135 | isTouched: () => touched,
136 | isBad: () => bad,
137 | setDirty: sinon.spy(),
138 | setTouched: sinon.spy(),
139 | setBad: sinon.spy(),
140 | register: sinon.spy(),
141 | unregister: sinon.spy(),
142 | validate: sinon.spy(),
143 | getValidationEvent: () => 'formCtrlValidationEvent',
144 | validation: {},
145 | parent: null,
146 | },
147 | },
148 | };
149 |
150 | component = new AvRadio(props);
151 | component.context = options.context;
152 | });
153 |
154 | it('should update group value on change', () => {
155 | const event = {};
156 | component.onChangeHandler(event);
157 | expect(options.context.Group.update).to.have.been.calledWith(event, props.value);
158 | });
159 |
160 | it('should run props on change if it\'s there', () => {
161 | props.onChange = sinon.spy();
162 | component.onChangeHandler();
163 | expect(props.onChange).to.have.been.called;
164 | });
165 | });
166 |
167 | });
168 |
--------------------------------------------------------------------------------
/__test__/AvRadioGroup.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { AvRadioGroup, AvFeedback } from 'availity-reactstrap-validation';
4 | import { FormGroup } from 'reactstrap';
5 |
6 | let options;
7 |
8 | describe('AvRadioGroup', () => {
9 | let touched;
10 | let dirty;
11 | let bad;
12 | let error;
13 |
14 | beforeEach(() => {
15 | touched = false;
16 | dirty = false;
17 | bad = false;
18 | error = false;
19 | options = {
20 | context: {
21 | FormCtrl: {
22 | inputs: {},
23 | getDefaultValue: ()=> {},
24 | getInputState: ()=> ({}),
25 | hasError: () => error,
26 | isDirty: () => dirty,
27 | isTouched: () => touched,
28 | isBad: () => bad,
29 | isDisabled: () => false,
30 | isReadOnly: () => false,
31 | setDirty: sinon.spy(),
32 | setTouched: sinon.spy(),
33 | setBad: sinon.spy(),
34 | register: sinon.spy(),
35 | unregister: sinon.spy(),
36 | validate: sinon.spy(),
37 | getValidationEvent: ()=> {},
38 | validation: {},
39 | parent: null,
40 | },
41 | },
42 | };
43 | });
44 |
45 | it('should render a reactstrap Input', () => {
46 | const wrapper = shallow( , options);
47 |
48 | expect(wrapper.type()).to.not.eql(undefined);
49 | });
50 |
51 | it('should register a validation', () => {
52 | const wrapper = shallow( , options);
53 | const instance = wrapper.instance();
54 | expect(instance.validations.required.value).to.be.true;
55 | });
56 |
57 | it('should register then remove a disabled validation', () => {
58 | const wrapper = shallow( , options);
59 | const instance = wrapper.instance();
60 | expect(instance.validations.required.value).to.be.true;
61 | wrapper.setProps({required: false});
62 | expect(instance.validations.required).to.be.undefined;
63 | });
64 |
65 | it('should return the set value', ()=> {
66 | const wrapper = shallow( , options);
67 | const component = wrapper.instance();
68 | component.value = 'boop';
69 | expect(component.getValue()).to.equal('boop');
70 | });
71 |
72 | it('should unregister when unmounted', ()=> {
73 | const wrapper = shallow( , options);
74 | wrapper.unmount();
75 | expect(options.context.FormCtrl.unregister).to.have.been.called;
76 | });
77 |
78 | it('should give default value from value prop', () => {
79 | const wrapper = shallow( , options);
80 | const component = wrapper.instance();
81 | expect(component.value).to.eql('momo');
82 | });
83 |
84 | it('should give default value from defaultValue prop when there is no value prop', () => {
85 | const wrapper = shallow( , options);
86 | const component = wrapper.instance();
87 | expect(component.value).to.eql('momo');
88 | });
89 |
90 | it('should update the value when the value prop changes', () => {
91 | const wrapper = shallow( , options);
92 | const component = wrapper.instance();
93 | expect(component.getValue()).to.equal('momo');
94 | wrapper.setProps({value: 'yoyo'});
95 | expect(component.getValue()).to.equal('yoyo');
96 | });
97 |
98 | it('should update the validations when the props change', () => {
99 | const wrapper = shallow( , options);
100 | const component = wrapper.instance();
101 | const spy = sinon.spy(component, 'updateValidations');
102 | wrapper.setProps({required: true});
103 | expect(spy).to.have.been.called;
104 | });
105 |
106 | it('should not update the validations when the props did not change', () => {
107 | const wrapper = shallow( , options);
108 | const component = wrapper.instance();
109 | const spy = sinon.spy(component, 'updateValidations');
110 | wrapper.setProps({defaultValue: 'momo'});
111 | expect(spy).to.not.have.been.called;
112 | });
113 |
114 | it('should give default value from context', () => {
115 | const wrapper = shallow( , options);
116 | const component = wrapper.instance();
117 | component.context.FormCtrl.getDefaultValue = () => {
118 | return 'jiri';
119 | };
120 | expect(component.getDefaultValue()).to.eql({key: 'defaultValue', value: 'jiri'});
121 | });
122 |
123 | it('should give default fallback when no one set up their stuff', () => {
124 | const wrapper = shallow( , options);
125 | const component = wrapper.instance();
126 | expect(component.getDefaultValue()).to.eql({key: 'defaultValue', value: ''});
127 | });
128 |
129 | it('should reset properly', () => {
130 | const wrapper = shallow( , options);
131 | const component = wrapper.instance();
132 | component.setState = sinon.spy();
133 | component.reset();
134 | expect(component.value).to.equal('momo');
135 | expect(component.setState).to.have.been.calledWith({value: 'momo'});
136 | expect(options.context.FormCtrl.setDirty).to.have.been.calledWith('test', false);
137 | expect(options.context.FormCtrl.setTouched).to.have.been.calledWith('test', false);
138 | expect(options.context.FormCtrl.setBad).to.have.been.calledWith('test', false);
139 | expect(options.context.FormCtrl.validate).to.have.been.calledWith('test');
140 | });
141 |
142 | it('should reset properly and call props reset', () => {
143 | const spy = sinon.spy();
144 | const wrapper = shallow( , options);
145 | const component = wrapper.instance();
146 | component.reset();
147 | expect(spy).to.have.been.calledWith('momo');
148 | });
149 |
150 | it('should disconnect child context from form registration and validation', () => {
151 | const wrapper = shallow( , options);
152 | const component = wrapper.instance();
153 | options.context.FormCtrl.register.reset();
154 | options.context.FormCtrl.validate.reset();
155 | component.getChildContext().FormCtrl.register('charmander');
156 | component.getChildContext().FormCtrl.validate('squirtle');
157 | expect(options.context.FormCtrl.register).to.not.have.been.called;
158 | expect(options.context.FormCtrl.validate).to.not.have.been.called;
159 | });
160 |
161 | it('should update the group via child context', () => {
162 | const wrapper = shallow( , options);
163 | const component = wrapper.instance();
164 | component.setState = sinon.spy();
165 | component.getChildContext().Group.update({}, 'momo');
166 | expect(component.value).to.equal('momo');
167 | expect(component.setState).to.have.been.calledWith({value: 'momo'});
168 | expect(options.context.FormCtrl.validate).to.have.been.called;
169 | });
170 |
171 | it('should trigger the change callback when the value is updated', () => {
172 | const spy = sinon.spy();
173 | const wrapper = shallow( , options);
174 | const component = wrapper.instance();
175 | const event = {};
176 | component.getChildContext().Group.update(event, 'momo').then(() => {
177 | expect(spy).to.have.been.calledWith(event, 'momo');
178 | });
179 | });
180 |
181 | it('should render validation message when sent', () => {
182 | options.context.FormCtrl.getInputState = () => {
183 | return {'errorMessage': 'WHAT ARE YOU DOING?!'};
184 | };
185 | const wrapper = shallow( , options);
186 | expect(wrapper.find(AvFeedback).prop('children')).to.equal('WHAT ARE YOU DOING?!');
187 | });
188 |
189 | it('should show a legend if we defined a label', () => {
190 | const wrapper = shallow( , options);
191 | expect(wrapper.contains(test )).to.be.true;
192 | });
193 | });
194 |
--------------------------------------------------------------------------------
/__test__/AvValidator.date.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 | import {inputTypeOverride} from 'availity-reactstrap-validation/AvValidator/utils';
3 |
4 | const fn = AvValidator.date;
5 | const input = {props: {type: 'text'}};
6 | const context = {};
7 |
8 | describe('Date Validation', () => {
9 | it('should not require a value', () => {
10 | expect(fn('', context, undefined, input)).to.be.true;
11 | });
12 |
13 | describe('error message', () => {
14 | it('should allow the error message to be overridden', () => {
15 | expect(fn('abc 123', context, {errorMessage: 'Custom'}, input)).to.equal('Custom');
16 | });
17 |
18 | it('should use the custom format in the default message', () => {
19 | expect(fn('abc 123', context, {format: 'YYYY-MM-DD'}, input)).to.equal('Format needs to be YYYY-MM-DD');
20 | });
21 |
22 | it('should use the default format in the default message', () => {
23 | expect(fn('abc 123', context, undefined, input)).to.equal('Format needs to be MM/DD/YYYY');
24 | });
25 |
26 | });
27 |
28 | describe('non-date input type', () => {
29 | beforeEach(() => {
30 | input.props.type = 'text';
31 | });
32 |
33 | it('should accept MM/DD/YYYY format by default', () => {
34 | expect(fn('10/12/2014', context, undefined, input)).to.be.true;
35 | });
36 |
37 | it('should accept ISO (YYYY-MM-DD) format by default', () => {
38 | expect(fn('2014-10-12', context, undefined, input)).to.be.true;
39 | });
40 |
41 | it('should allow format to be customized', () => {
42 | expect(fn('2014-12-14', context, {format: 'YYYY-MM-DD'}, input)).to.be.true;
43 | });
44 |
45 | it('should ensure the format matches the customized format', () => {
46 | expect(fn('10/12/2014', context, {format: 'YYYY-MM-DD'}, input)).to.equal('Format needs to be YYYY-MM-DD');
47 | });
48 | });
49 |
50 | describe('date input type', () => {
51 | describe('without browser date input type support', () => {
52 | beforeEach(() => {
53 | inputTypeOverride('date', false);
54 | input.props.type = 'date';
55 | });
56 |
57 | it('should accept MM/DD/YYYY format by default', () => {
58 | expect(fn('10/12/2014', context, undefined, input)).to.be.true;
59 | });
60 |
61 | it('should accept ISO (YYYY-MM-DD) format by default', () => {
62 | expect(fn('2014-10-12', context, undefined, input)).to.be.true;
63 | });
64 |
65 | it('should allow format to be customized', () => {
66 | expect(fn('2014-12-14', context, {format: 'YYYY-MM-DD'}, input)).to.be.true;
67 | });
68 |
69 | it('should ensure the format matches the customized format', () => {
70 | expect(fn('10/12/2014', context, {format: 'YYYY-MM-DD'}, input)).to.equal('Format needs to be YYYY-MM-DD');
71 | });
72 | });
73 |
74 | describe('with browser date input type support', () => {
75 | beforeEach(() => {
76 | inputTypeOverride('date', true);
77 | input.props.type = 'date';
78 | });
79 |
80 | it('should accept ISO (YYYY-MM-DD) format by default', () => {
81 | expect(fn('2014-10-12', context, undefined, input)).to.be.true;
82 | });
83 |
84 | it('should accept MM/DD/YYYY format by default', () => {
85 | expect(fn('10/12/2014', context, undefined, input)).to.be.true;
86 | });
87 |
88 | it('should allow format to be customized', () => {
89 | expect(fn('10-12-2014', context, {format: 'DD-MM-YYYY'}, input)).to.be.true;
90 | });
91 | });
92 | });
93 | });
94 |
--------------------------------------------------------------------------------
/__test__/AvValidator.dateRange.spec.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import {AvValidator} from 'availity-reactstrap-validation';
3 | import {inputTypeOverride} from 'availity-reactstrap-validation/AvValidator/utils';
4 |
5 | const fn = AvValidator.dateRange;
6 | const input = {props: {type: 'text'}};
7 | const context = {};
8 | let date0;
9 | let date1;
10 | let date2;
11 |
12 | describe('Date Range Validation', () => {
13 | beforeEach(() => {
14 | date0 = moment(new Date());
15 | date1 = moment(new Date());
16 | date2 = moment(new Date());
17 | });
18 |
19 | it('should not require a value', () => {
20 | expect(fn('', context, undefined, input)).to.be.true;
21 | });
22 |
23 | describe('error message', () => {
24 | it('should allow the error message to be overridden', () => {
25 | expect(fn('abc 123', context, {errorMessage: 'Custom'}, input)).to.equal('Custom');
26 | });
27 |
28 | it('should use the custom format in the default message', () => {
29 | expect(fn('abc 123', context, {
30 | format: 'YYYY-MM-DD',
31 | displayFormat: 'YYYY-MM-DD',
32 | start: {value: '2014-10-12'},
33 | end: {value: '2015-10-12'}
34 | }, input)).to.equal('Date must be between 2014-10-12 and 2015-10-12');
35 | });
36 |
37 | it('should use the default format in the default message', () => {
38 | expect(fn('abc 123', context, {
39 | format: 'YYYY-MM-DD',
40 | start: {value: '2014-10-12'},
41 | end: {value: '2015-10-12'}
42 | }, input)).to.equal('Date must be between 10/12/2014 and 10/12/2015');
43 | });
44 |
45 | });
46 |
47 | describe('with units', () => {
48 | it('should return true when date is within range of the current date', () => {
49 | expect(fn(date0.format('YYYY-MM-DD'), context, {
50 | format: 'YYYY-MM-DD',
51 | start: {value: -1, units: 'day'},
52 | end: {value: 1, units: 'day'}
53 | }, input)).to.be.true;
54 | });
55 |
56 | it('should return true when date is the same as the start date', () => {
57 | expect(fn(date0.format('YYYY-MM-DD'), context, {
58 | format: 'YYYY-MM-DD',
59 | start: {value: 0, units: 'day'},
60 | end: {value: 1, units: 'day'}
61 | }, input)).to.be.true;
62 | });
63 |
64 | it('should return true when date is the same as the end date', () => {
65 | expect(fn(date0.format('YYYY-MM-DD'), context, {
66 | format: 'YYYY-MM-DD',
67 | start: {value: -1, units: 'day'},
68 | end: {value: 0, units: 'day'}
69 | }, input)).to.be.true;
70 | });
71 |
72 | it('should return an error message when date is not within range of the current date', () => {
73 | expect(fn(date0.format('YYYY-MM-DD'), context, {
74 | format: 'YYYY-MM-DD',
75 | start: {value: -5, units: 'day'},
76 | end: {value: -1, units: 'day'}
77 | }, input)).to.equal(`Date must be between ${date1.add(-5, 'day').format('MM/DD/YYYY')} and ${date2.add(-1, 'day').format('MM/DD/YYYY')}`);
78 | });
79 | });
80 |
81 | describe('without units', () => {
82 | it('should return true when date is within range of the current date', () => {
83 | expect(fn(date0.format('YYYY-MM-DD'), context, {
84 | format: 'YYYY-MM-DD',
85 | start: {value: date1.add(-1, 'day').format('YYYY-MM-DD')},
86 | end: {value: date2.add(1, 'day').format('YYYY-MM-DD')}
87 | }, input)).to.be.true;
88 | });
89 |
90 | it('should return true when date is the same as the start date', () => {
91 | expect(fn(date0.format('YYYY-MM-DD'), context, {
92 | format: 'YYYY-MM-DD',
93 | start: {value: date1.format('YYYY-MM-DD')},
94 | end: {value: date2.add(1, 'day').format('YYYY-MM-DD')}
95 | }, input)).to.be.true;
96 | });
97 |
98 | it('should return true when date is the same as the end date', () => {
99 | expect(fn(date0.format('YYYY-MM-DD'), context, {
100 | format: 'YYYY-MM-DD',
101 | start: {value: date1.add(-1, 'day').format('YYYY-MM-DD')},
102 | end: {value: date2.format('YYYY-MM-DD')}
103 | }, input)).to.be.true;
104 | });
105 |
106 | it('should return an error message when date is not within range of the current date', () => {
107 | expect(fn(date0.format('YYYY-MM-DD'), context, {
108 | format: 'YYYY-MM-DD',
109 | start: {value: date1.add(-5, 'day').format('YYYY-MM-DD')},
110 | end: {value: date2.add(-1, 'day').format('YYYY-MM-DD')}
111 | }, input)).to.equal(`Date must be between ${date1.format('MM/DD/YYYY')} and ${date2.format('MM/DD/YYYY')}`);
112 | });
113 |
114 | it('should allow the start and end formats to be different than the user format', () => {
115 | expect(fn(date0.format('YYYY-MM-DD'), context, {
116 | format: 'YYYY-MM-DD',
117 | start: {value: date1.add(-5, 'day').format('DD-MM-YYYY'), format: 'DD-MM-YYYY'},
118 | end: {value: date2.add(-1, 'day').format('YYYY/MM/DD'), format: 'YYYY/MM/DD'}
119 | }, input)).to.equal(`Date must be between ${date1.format('MM/DD/YYYY')} and ${date2.format('MM/DD/YYYY')}`);
120 | });
121 | });
122 |
123 | describe('non-date input type', () => {
124 | beforeEach(() => {
125 | input.props.type = 'text';
126 | });
127 |
128 | it('should accept MM/DD/YYYY by default', () => {
129 | expect(fn(date0.format('MM/DD/YYYY'), context, {
130 | start: {value: -1, units: 'day'},
131 | end: {value: 1, units: 'day'}
132 | }, input)).to.be.true;
133 | });
134 |
135 | it('should accept YYYY-MM-DD by default', () => {
136 | expect(fn(date0.format('YYYY-MM-DD'), context, {
137 | start: {value: -1, units: 'day'},
138 | end: {value: 1, units: 'day'}
139 | }, input)).to.be.true;
140 | });
141 |
142 | it('should allow the format to be overridden', () => {
143 | expect(fn(date0.format('DD-MM-YYYY'), context, {
144 | format: 'DD-MM-YYYY',
145 | start: {value: -1, units: 'day'},
146 | end: {value: 1, units: 'day'}
147 | }, input)).to.be.true;
148 | });
149 | });
150 |
151 | describe('date input type', () => {
152 | describe('without browser date input type support', () => {
153 | beforeEach(() => {
154 | inputTypeOverride('date', false);
155 | input.props.type = 'date';
156 | });
157 |
158 | it('should accept MM/DD/YYYY by default', () => {
159 | expect(fn(date0.format('MM/DD/YYYY'), context, {
160 | start: {value: -1, units: 'day'},
161 | end: {value: 1, units: 'day'}
162 | }, input)).to.be.true;
163 | });
164 |
165 | it('should accept YYYY-MM-DD by default', () => {
166 | expect(fn(date0.format('YYYY-MM-DD'), context, {
167 | start: {value: -1, units: 'day'},
168 | end: {value: 1, units: 'day'}
169 | }, input)).to.be.true;
170 | });
171 |
172 | it('should allow the format to be overridden', () => {
173 | expect(fn(date0.format('DD-MM-YYYY'), context, {
174 | format: 'DD-MM-YYYY',
175 | start: {value: -1, units: 'day'},
176 | end: {value: 1, units: 'day'}
177 | }, input)).to.be.true;
178 | });
179 | });
180 |
181 | describe('with browser date input type support', () => {
182 | beforeEach(() => {
183 | inputTypeOverride('date', true);
184 | input.props.type = 'date';
185 | });
186 |
187 | it('should accept YYYY-MM-DD by default', () => {
188 | expect(fn(date0.format('YYYY-MM-DD'), context, {
189 | start: {value: -1, units: 'day'},
190 | end: {value: 1, units: 'day'}
191 | }, input)).to.be.true;
192 | });
193 |
194 | it('should accept MM/DD/YYYY by default', () => {
195 | expect(fn(date0.format('MM/DD/YYYY'), context, {
196 | start: {value: -1, units: 'day'},
197 | end: {value: 1, units: 'day'}
198 | }, input)).to.be.true;
199 | });
200 |
201 | it('should allow the format to be overridden', () => {
202 | expect(fn(date0.format('DD-MM-YYYY'), context, {
203 | format: 'DD-MM-YYYY',
204 | start: {value: -1, units: 'day'},
205 | end: {value: 1, units: 'day'}
206 | }, input)).to.be.true;
207 | });
208 | });
209 | });
210 | });
211 |
--------------------------------------------------------------------------------
/__test__/AvValidator.email.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.email;
4 |
5 | describe('Email Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('should return true for a valid email', () => {
11 | expect(fn('evan.sharp@availity.com')).to.be.true;
12 | expect(fn('evan.sharp+more-things@availity.com')).to.be.true;
13 | expect(fn('evan.sharp@availity.com.co')).to.be.true;
14 | expect(fn('evan.sharp@development.availity.com')).to.be.true;
15 | expect(fn('Evan.Sharp@Availity.com')).to.be.true;
16 | });
17 |
18 | it('should return false for an invalid email', () => {
19 | expect(fn('evan.sharp@availity')).to.be.false;
20 | expect(fn('evan.sharp@')).to.be.false;
21 | expect(fn('@availity.com')).to.be.false;
22 | expect(fn('evan.sharp@.com')).to.be.false;
23 | expect(fn('evan.sharp')).to.be.false;
24 | expect(fn('availity.com')).to.be.false;
25 | expect(fn('Evan@Sharp@Availity.com')).to.be.false;
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/__test__/AvValidator.match.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.match;
4 | const now = new Date();
5 | const context = {
6 | field1: "",
7 | field2: "something",
8 | field3: now,
9 | field4: 4,
10 | field5: {value: "something"},
11 | };
12 |
13 | describe('Match Validation', () => {
14 | it('should not require a value', () => {
15 | expect(fn('')).to.be.true;
16 | });
17 |
18 | it('should return false by default when fields do not match', () => {
19 | expect(fn('something', context, {value: 'field1'})).to.be.false;
20 | });
21 |
22 | it('should return custom error message if provided when fields do not match', () => {
23 | expect(fn('something', context, {value: 'field1', errorMessage: 'No match!'})).to.equal('No match!');
24 | });
25 |
26 | it('should match a string to a string', () => {
27 | expect(fn(context.field2, context, {value: 'field2'})).to.be.true;
28 | });
29 |
30 | it('should match an object to an object', () => {
31 | expect(fn(context.field3, context, {value: 'field3'})).to.be.true;
32 | });
33 |
34 | it('should match a number to a number', () => {
35 | expect(fn(context.field4, context, {value: 'field4'})).to.be.true;
36 | });
37 |
38 | it('should not match a string to a number', () => {
39 | expect(fn('4', context, {value: 'field4'})).to.be.false;
40 | });
41 |
42 | it('should not match a string to an object', () => {
43 | expect(fn('something', context, {value: 'field5'})).to.be.false;
44 | });
45 |
46 | it('should not match something to nothing', () => {
47 | expect(fn('something', context, {value: 'field1'})).to.be.false;
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/__test__/AvValidator.max.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.max;
4 |
5 | describe('Max Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('should accept input array as an alias for maxChecked', () => {
11 | expect(fn(undefined, undefined, {value: 1}, {value: ['a', 'b']})).to.be.false;
12 | expect(fn(undefined, undefined, {value: 2}, {value: ['a']})).to.be.true;
13 | });
14 |
15 | it('should return true if the value is less than the constraint', () => {
16 | expect(fn(1, undefined, {value: 5})).to.be.true;
17 | });
18 |
19 | it('should true if the value is the same as than the constraint', () => {
20 | expect(fn(5, undefined, {value: 5})).to.be.true;
21 | });
22 |
23 | it('should return false by default when field is over the max', () => {
24 | expect(fn(10, undefined, {value: 5})).to.be.false;
25 | });
26 |
27 | it('should return custom error message if provided when field is over the max', () => {
28 | expect(fn(10, undefined, {value: 5, errorMessage: 'Too much!'})).to.equal('Too much!');
29 | });
30 |
31 | it('should accept string input', () => {
32 | expect(fn('1', undefined, {value: '2'})).to.be.true;
33 | expect(fn('1', undefined, {value: 2})).to.be.true;
34 | expect(fn('1', undefined, {value: '0'})).to.be.false;
35 | expect(fn('1', undefined, {value: 0})).to.be.false;
36 | });
37 |
38 | it('should accept string constraint input', () => {
39 | expect(fn('1', undefined, {value: '2'})).to.be.true;
40 | expect(fn(1, undefined, {value: '2'})).to.be.true;
41 | expect(fn('3', undefined, {value: '2'})).to.be.false;
42 | expect(fn(3, undefined, {value: '2'})).to.be.false;
43 | });
44 |
45 | it('should accept numeric input', () => {
46 | expect(fn(1, undefined, {value: '2'})).to.be.true;
47 | expect(fn(1, undefined, {value: 2})).to.be.true;
48 | expect(fn(1, undefined, {value: '0'})).to.be.false;
49 | expect(fn(1, undefined, {value: 0})).to.be.false;
50 | });
51 |
52 | it('should accept numeric constraint input', () => {
53 | expect(fn('1', undefined, {value: 2})).to.be.true;
54 | expect(fn(1, undefined, {value: 2})).to.be.true;
55 | expect(fn('3', undefined, {value: 2})).to.be.false;
56 | expect(fn(3, undefined, {value: 2})).to.be.false;
57 | });
58 |
59 | it('should not accept non numeric input', () => {
60 | expect(fn('1a', undefined, {value: 2})).to.be.false;
61 | expect(fn('a1', undefined, {value: 2})).to.be.false;
62 | expect(fn('1.1.1', undefined, {value: 2})).to.be.false;
63 | });
64 |
65 | it('should accept decimal input', () => {
66 | expect(fn('1.5', undefined, {value: 2})).to.be.true;
67 | expect(fn(1.5, undefined, {value: 2})).to.be.true;
68 | expect(fn('1.5', undefined, {value: 1})).to.be.false;
69 | expect(fn(1.5, undefined, {value: 1})).to.be.false;
70 | });
71 |
72 | it('should accept decimal constraint input', () => {
73 | expect(fn(1.5, undefined, {value: 2.2})).to.be.true;
74 | expect(fn(1.5, undefined, {value: '2.2'})).to.be.true;
75 | expect(fn(3.5, undefined, {value: 2.2})).to.be.false;
76 | expect(fn(3.5, undefined, {value: '2.2'})).to.be.false;
77 | });
78 |
79 | it('should compare dates when date validation is on', () => {
80 | expect(fn('2016-10-09', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.true;
81 | expect(fn('2016-10-10', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.true;
82 | expect(fn('2016-10-11', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.false;
83 | expect(fn('2016-10-12', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.false;
84 | });
85 |
86 | it('should compare dates when the field is of type date', () => {
87 | expect(fn('2016-10-09', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.true;
88 | expect(fn('2016-10-10', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.true;
89 | expect(fn('2016-10-11', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.false;
90 | expect(fn('2016-10-12', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.false;
91 | });
92 | });
93 |
--------------------------------------------------------------------------------
/__test__/AvValidator.maxchecked.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.maxChecked;
4 |
5 | describe('Max Checked Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('have an alias of maxChecked', () => {
11 | expect(fn).to.equal(AvValidator.maxChecked);
12 | });
13 |
14 | it('should return false if the value is greater than the constraint', () => {
15 | expect(fn(undefined, undefined, {value: 1}, {value: ['a', 'b']})).to.be.false;
16 | });
17 |
18 | it('should return true if the value is the same as the constraint', () => {
19 | expect(fn(undefined, undefined, {value: 2}, {value: ['a', 'b']})).to.be.true;
20 | });
21 |
22 | it('should return false when field is over the max', () => {
23 | expect(fn(undefined, undefined, {value: 1}, {value: ['a', 'b']})).to.be.false;
24 | });
25 |
26 | it('should return custom error message if provided when field is over the max', () => {
27 | expect(fn(undefined, undefined, {value: 1, errorMessage: 'Too much!'}, {value: ['a', 'b']})).to.equal('Too much!');
28 | });
29 |
30 | it('should accept string constraint input', () => {
31 | expect(fn(undefined, undefined, {value: '2'}, {value: ['a']})).to.be.true;
32 | expect(fn(undefined, undefined, {value: '1'}, {value: ['a', 'b']})).to.be.false;
33 | });
34 |
35 | it('should accept numeric constraint input', () => {
36 | expect(fn(undefined, undefined, {value: 2}, {value: ['a', 'b']})).to.be.true;
37 | expect(fn(undefined, undefined, {value: 1}, {value: ['a', 'b']})).to.be.false;
38 | });
39 |
40 | it('should not accept non-numeric constraint input', () => {
41 | expect(fn(undefined, undefined, {value: '1a'}, {value: ['a']})).to.be.false;
42 | expect(fn(undefined, undefined, {value: 'a1'}, {value: ['a']})).to.be.false;
43 | expect(fn(undefined, undefined, {value: '1.1.1'}, {value: ['a']})).to.be.false;
44 | });
45 |
46 | it('should not accept decimal constraint input', () => {
47 | expect(fn(undefined, undefined, {value: 20.2}, {value: ['a', 'b', 'c']})).to.be.false;
48 | expect(fn(undefined, undefined, {value: '20.2'}, {value: ['a', 'b', 'c']})).to.be.false;
49 | expect(fn(undefined, undefined, {value: 2.2}, {value: ['a']})).to.be.false;
50 | expect(fn(undefined, undefined, {value: '2.2'}, {value: ['a']})).to.be.false;
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/__test__/AvValidator.maxlength.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.maxlength;
4 |
5 | describe('Max Length Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('have an alias of maxLength', () => {
11 | expect(fn).to.equal(AvValidator.maxLength);
12 | });
13 |
14 | it('should return true if the value length is greater than the constraint', () => {
15 | expect(fn('123', undefined, {value: 5})).to.be.true;
16 | });
17 |
18 | it('should true if the value is the same as than the constraint', () => {
19 | expect(fn('12345', undefined, {value: 5})).to.be.true;
20 | });
21 |
22 | it('should return false by default when field is over the max', () => {
23 | expect(fn('123456', undefined, {value: 5})).to.be.false;
24 | });
25 |
26 | it('should return custom error message if provided when field is over the max', () => {
27 | expect(fn('123456', undefined, {value: 5, errorMessage: 'Not enough!'})).to.equal('Not enough!');
28 | });
29 |
30 | it('should accept string input', () => {
31 | expect(fn('12345', undefined, {value: '2'})).to.be.false;
32 | expect(fn('12345', undefined, {value: 2})).to.be.false;
33 | expect(fn('1', undefined, {value: '2'})).to.be.true;
34 | expect(fn('1', undefined, {value: 2})).to.be.true;
35 | });
36 |
37 | it('should accept string constraint input', () => {
38 | expect(fn('12345', undefined, {value: '2'})).to.be.false;
39 | expect(fn('1', undefined, {value: '2'})).to.be.true;
40 | });
41 |
42 | it('should not accept numeric input', () => {
43 | expect(fn(10, undefined, {value: '3'})).to.be.false;
44 | expect(fn(2, undefined, {value: 3})).to.be.false;
45 | });
46 |
47 | it('should accept numeric constraint input', () => {
48 | expect(fn('123456', undefined, {value: 2})).to.be.false;
49 | expect(fn('123', undefined, {value: 6})).to.be.true;
50 | });
51 |
52 | it('should accept numeric input', () => {
53 | expect(fn('0p5p', undefined, {value: 5})).to.be.true;
54 | expect(fn('0001', undefined, {value: 5})).to.be.true;
55 | expect(fn('1.1.', undefined, {value: 6})).to.be.true;
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/__test__/AvValidator.min.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.min;
4 |
5 | describe('Min Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('should accept input array as an alias for minChecked', () => {
11 | expect(fn(undefined, undefined, {value: 1}, {value: ['a', 'b']})).to.be.true;
12 | expect(fn(undefined, undefined, {value: 2}, {value: ['a']})).to.be.false;
13 | });
14 |
15 | it('should return true if the value is greater than the constraint', () => {
16 | expect(fn(10, undefined, {value: 5})).to.be.true;
17 | });
18 |
19 | it('should true if the value is the same as than the constraint', () => {
20 | expect(fn(5, undefined, {value: 5})).to.be.true;
21 | });
22 |
23 | it('should return false by default when field is over the max', () => {
24 | expect(fn(1, undefined, {value: 5})).to.be.false;
25 | });
26 |
27 | it('should return custom error message if provided when field is over the max', () => {
28 | expect(fn(1, undefined, {value: 5, errorMessage: 'Too much!'})).to.equal('Too much!');
29 | });
30 |
31 | it('should accept string input', () => {
32 | expect(fn('12', undefined, {value: '2'})).to.be.true;
33 | expect(fn('12', undefined, {value: 2})).to.be.true;
34 | expect(fn('12', undefined, {value: '20'})).to.be.false;
35 | expect(fn('12', undefined, {value: 20})).to.be.false;
36 | });
37 |
38 | it('should accept string constraint input', () => {
39 | expect(fn('10', undefined, {value: '2'})).to.be.true;
40 | expect(fn(10, undefined, {value: '2'})).to.be.true;
41 | expect(fn('3', undefined, {value: '20'})).to.be.false;
42 | expect(fn(3, undefined, {value: '20'})).to.be.false;
43 | });
44 |
45 | it('should accept numeric input', () => {
46 | expect(fn(10, undefined, {value: '2'})).to.be.true;
47 | expect(fn(10, undefined, {value: 2})).to.be.true;
48 | expect(fn(2, undefined, {value: '10'})).to.be.false;
49 | expect(fn(2, undefined, {value: 10})).to.be.false;
50 | });
51 |
52 | it('should accept numeric constraint input', () => {
53 | expect(fn('10', undefined, {value: 2})).to.be.true;
54 | expect(fn(10, undefined, {value: 2})).to.be.true;
55 | expect(fn('3', undefined, {value: 20})).to.be.false;
56 | expect(fn(3, undefined, {value: 20})).to.be.false;
57 | });
58 |
59 | it('should not accept non numeric input', () => {
60 | expect(fn('1a', undefined, {value: 2})).to.be.false;
61 | expect(fn('a1', undefined, {value: 2})).to.be.false;
62 | expect(fn('1.1.1', undefined, {value: 2})).to.be.false;
63 | });
64 |
65 | it('should accept decimal input', () => {
66 | expect(fn('10.5', undefined, {value: 2})).to.be.true;
67 | expect(fn(10.5, undefined, {value: 2})).to.be.true;
68 | expect(fn('1.5', undefined, {value: 10})).to.be.false;
69 | expect(fn(1.5, undefined, {value: 10})).to.be.false;
70 | });
71 |
72 | it('should accept decimal constraint input', () => {
73 | expect(fn(10.5, undefined, {value: 2.2})).to.be.true;
74 | expect(fn(10.5, undefined, {value: '2.2'})).to.be.true;
75 | expect(fn(3.5, undefined, {value: 20.2})).to.be.false;
76 | expect(fn(3.5, undefined, {value: '20.2'})).to.be.false;
77 | });
78 |
79 |
80 | it('should compare dates when date validation is on', () => {
81 | expect(fn('2016-10-08', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.false;
82 | expect(fn('2016-10-09', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.false;
83 | expect(fn('2016-10-10', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.true;
84 | expect(fn('2016-10-11', undefined, {value: '2016-10-10'}, {validations: {date: {value: true}}})).to.be.true;
85 | });
86 |
87 | it('should compare dates when the field is of type date', () => {
88 | expect(fn('2016-10-08', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.false;
89 | expect(fn('2016-10-09', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.false;
90 | expect(fn('2016-10-10', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.true;
91 | expect(fn('2016-10-11', undefined, {value: '2016-10-10'}, {props: {type: 'date'}})).to.be.true;
92 | });
93 | });
94 |
--------------------------------------------------------------------------------
/__test__/AvValidator.minchecked.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.minChecked;
4 |
5 | describe('Min Checked Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('have an alias of minChecked', () => {
11 | expect(fn).to.equal(AvValidator.minChecked);
12 | });
13 |
14 | it('should return true if the value is greater than the constraint', () => {
15 | expect(fn(undefined, undefined, {value: 1}, {value: ['a', 'b']})).to.be.true;
16 | });
17 |
18 | it('should return true if the value is the same as than the constraint', () => {
19 | expect(fn(undefined, undefined, {value: 1}, {value: ['a']})).to.be.true;
20 | });
21 |
22 | it('should return false when field is under the min', () => {
23 | expect(fn(undefined, undefined, {value: 2}, {value: ['a']})).to.be.false;
24 | });
25 |
26 | it('should return custom error message if provided when field is under the min', () => {
27 | expect(fn(undefined, undefined, {value: 5, errorMessage: 'Too much!'}, {value: ['a']})).to.equal('Too much!');
28 | });
29 |
30 | it('should accept string constraint input', () => {
31 | expect(fn(undefined, undefined, {value: '1'}, {value: ['a']})).to.be.true;
32 | expect(fn(undefined, undefined, {value: '20'}, {value: ['a']})).to.be.false;
33 | });
34 |
35 | it('should accept numeric constraint input', () => {
36 | expect(fn(undefined, undefined, {value: 1}, {value: ['a']})).to.be.true;
37 | expect(fn(undefined, undefined, {value: 20}, {value: ['a']})).to.be.false;
38 | });
39 |
40 | it('should not accept non-numeric constraint input', () => {
41 | expect(fn(undefined, undefined, {value: '1a'}, {value: ['a']})).to.be.false;
42 | expect(fn(undefined, undefined, {value: 'a1'}, {value: ['a']})).to.be.false;
43 | expect(fn(undefined, undefined, {value: '1.1.1'}, {value: ['a']})).to.be.false;
44 | });
45 |
46 | it('should not accept decimal constraint input', () => {
47 | expect(fn(undefined, undefined, {value: 0.2}, {value: ['a']})).to.be.false;
48 | expect(fn(undefined, undefined, {value: '2.2'}, {value: ['a']})).to.be.false;
49 | expect(fn(undefined, undefined, {value: 20.2}, {value: ['a', 'b', 'c']})).to.be.false;
50 | expect(fn(undefined, undefined, {value: '20.2'}, {value: ['a', 'b', 'c']})).to.be.false;
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/__test__/AvValidator.minlength.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.minlength;
4 |
5 | describe('Min Length Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('have an alias of minLength', () => {
11 | expect(fn).to.equal(AvValidator.minLength);
12 | });
13 |
14 | it('should return true if the value length is greater than the constraint', () => {
15 | expect(fn('123456', undefined, {value: 5})).to.be.true;
16 | });
17 |
18 | it('should true if the value is the same as than the constraint', () => {
19 | expect(fn('12345', undefined, {value: 5})).to.be.true;
20 | });
21 |
22 | it('should return false by default when field is over the max', () => {
23 | expect(fn('1234', undefined, {value: 5})).to.be.false;
24 | });
25 |
26 | it('should return custom error message if provided when field is over the max', () => {
27 | expect(fn('1234', undefined, {value: 5, errorMessage: 'Too much!'})).to.equal('Too much!');
28 | });
29 |
30 | it('should accept string input', () => {
31 | expect(fn('123', undefined, {value: '5'})).to.be.false;
32 | expect(fn('123', undefined, {value: 5})).to.be.false;
33 | expect(fn('123', undefined, {value: '2'})).to.be.true;
34 | expect(fn('123', undefined, {value: 2})).to.be.true;
35 | });
36 |
37 | it('should accept string constraint input', () => {
38 | expect(fn('1', undefined, {value: '2'})).to.be.false;
39 | expect(fn('12345', undefined, {value: '2'})).to.be.true;
40 | });
41 |
42 | it('should not accept numeric input', () => {
43 | expect(fn(10, undefined, {value: '1'})).to.be.false;
44 | expect(fn(2, undefined, {value: 1})).to.be.false;
45 | });
46 |
47 | it('should accept numeric constraint input', () => {
48 | expect(fn('12345', undefined, {value: 20})).to.be.false;
49 | expect(fn('12345', undefined, {value: 3})).to.be.true;
50 | });
51 |
52 | it('should accept non numeric input', () => {
53 | expect(fn('1a0', undefined, {value: 2})).to.be.true;
54 | expect(fn('001', undefined, {value: 2})).to.be.true;
55 | expect(fn('1.1.1', undefined, {value: 3})).to.be.true;
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/__test__/AvValidator.npi.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.npi;
4 |
5 | describe('NPI Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('should return custom error message if provided when field is not valid', () => {
11 | expect(fn('1234', undefined, {errorMessage: 'Not an NPI!'})).to.equal('Not an NPI!');
12 | });
13 |
14 | it('should return false if NPI contains non-digits', () => {
15 | expect(fn('i2eh56789o')).to.be.false;
16 | });
17 |
18 | it('should return false if NPI is not 10 digits in length', () => {
19 | expect(fn('123456789')).to.be.false;
20 | expect(fn('12345678901')).to.be.false;
21 | });
22 |
23 | it('should return false if NPI does not start with a 1, 2, 3, or 4', () => {
24 | expect(fn('5678901234')).to.be.false;
25 | });
26 |
27 | it('should return false if NPI checksum does not match check digit', () => {
28 | expect(fn('1234567890')).to.be.false;
29 | });
30 |
31 | it('should return true if NPI is valid', () => {
32 | expect(fn('1234567893')).to.be.true;
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/__test__/AvValidator.number.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.number;
4 |
5 | describe('Number Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('should return custom error message if provided when field is not valid', () => {
11 | expect(fn('a4', undefined, {errorMessage: 'NaN!'})).to.equal('NaN!');
12 | });
13 |
14 | it('should accept numbers', () => {
15 | expect(fn(0)).to.be.true;
16 | expect(fn(-1)).to.be.true;
17 | expect(fn(1)).to.be.true;
18 | expect(fn(10)).to.be.true;
19 | });
20 |
21 | it('should accept decimals', () => {
22 | expect(fn(0.1)).to.be.true;
23 | expect(fn(-1.1)).to.be.true;
24 | expect(fn(1.1)).to.be.true;
25 | expect(fn(10.1)).to.be.true;
26 | });
27 |
28 | it('should accept number like strings', () => {
29 | expect(fn('0')).to.be.true;
30 | expect(fn('-1')).to.be.true;
31 | expect(fn('1')).to.be.true;
32 | expect(fn('10')).to.be.true;
33 | });
34 |
35 | it('should accept decimal like strings', () => {
36 | expect(fn('0.1')).to.be.true;
37 | expect(fn('-1.1')).to.be.true;
38 | expect(fn('1.1')).to.be.true;
39 | expect(fn('10.1')).to.be.true;
40 | });
41 |
42 | it('should not accept strings that are not like numbers', () => {
43 | expect(fn('a0')).to.be.false;
44 | expect(fn('-1b')).to.be.false;
45 | expect(fn('1c')).to.be.false;
46 | expect(fn('1d0')).to.be.false;
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/__test__/AvValidator.pattern.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.pattern;
4 |
5 | describe('Pattern Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('should return custom error message if provided when field is not valid', () => {
11 | expect(fn('at3', undefined, {value: /^\d$/, errorMessage: 'Invalid!'})).to.equal('Invalid!');
12 | });
13 |
14 | it('should accept regex objects', () => {
15 | expect(fn('3', undefined, {value: /^\d$/})).to.be.true;
16 | expect(fn('a', undefined, {value: /^\d$/})).to.be.false;
17 | });
18 |
19 | it('should accept regex like strings', () => {
20 | expect(fn('3', undefined, {value: '/^\\d$/'})).to.be.true;
21 | expect(fn('a', undefined, {value: '/^\\d$/'})).to.be.false;
22 | });
23 |
24 | it('should accept somewhat regex like strings', () => {
25 | expect(fn('3', undefined, {value: '^\\d$'})).to.be.true;
26 | expect(fn('a', undefined, {value: '^\\d$'})).to.be.false;
27 | });
28 |
29 | it('should accept and array of regex to match at least one of', () => {
30 | expect(fn('3.4', undefined, {value: ['^\\d$', /^\d\.\d$/, '/^\\d\.\\d\.\\d$/']})).to.be.true;
31 | expect(fn('3.4', undefined, {value: ['^\\d$', /^\d\.\d$/, '/^\\d\.\\d\.\\d$/']})).to.be.true;
32 | expect(fn('3.4.5', undefined, {value: ['^\\d$', /^\d\.\d$/, '/^\\d\.\\d\.\\d$/']})).to.be.true;
33 | expect(fn('3.a.b', undefined, {value: ['^\\d$', /^\d\.\d$/, '/^\\d\.\\d\.\\d$/']})).to.be.false;
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/__test__/AvValidator.phone.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.phone;
4 |
5 | describe('Phone Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | it('should have an alias of "tel"', () => {
11 | expect(fn).to.equal(AvValidator.tel);
12 | });
13 |
14 | it('should return custom error message if provided when field is not valid', () => {
15 | expect(fn('at3', undefined, {errorMessage: 'Invalid!'})).to.equal('Invalid!');
16 | });
17 |
18 | it('should accept a regex to validate with', () => {
19 | expect(fn('3', undefined, {pattern: /^\d$/})).to.be.true;
20 | expect(fn('3', undefined, {pattern: '/^\\d$/'})).to.be.true;
21 | });
22 |
23 | it('should not accept less than 10 digits', () => {
24 | expect(fn('123456789')).to.be.false;
25 | });
26 |
27 | it('country code of "+1" is allowed', () => {
28 | expect(fn('+14444444444')).to.be.true;
29 | expect(fn('14444444444')).to.be.true;
30 | expect(fn('+1 4444444444')).to.be.true;
31 | expect(fn('1 4444444444')).to.be.true;
32 | });
33 |
34 | it('must be 10 digits (minus country code)', () => {
35 | expect(fn('4444444444')).to.be.true;
36 | expect(fn('44444444445')).to.be.false;
37 | expect(fn('444 444 4444')).to.be.true;
38 | });
39 |
40 | it('can be formatted', () => {
41 | expect(fn('(444) 444-4444')).to.be.true;
42 | expect(fn('+1 (444) 444-4444')).to.be.true;
43 | expect(fn('444-444-4444')).to.be.true;
44 | expect(fn('+1 444-444-4444')).to.be.true;
45 | expect(fn('444.444.4444')).to.be.true;
46 | expect(fn('(444) 444 4444')).to.be.true;
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/__test__/AvValidator.required.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.required;
4 |
5 | describe('Required Validation', () => {
6 | it('should be invalid with ""', () => {
7 | expect(fn('')).to.be.false;
8 | });
9 |
10 | it('should be invalid with false', () => {
11 | expect(fn(false)).to.be.false;
12 | });
13 |
14 | it('should return custom error message if provided when field is not valid', () => {
15 | expect(fn('', undefined, {errorMessage: 'Invalid!'})).to.equal('Invalid!');
16 | });
17 |
18 | it('should be valid with 0', () => {
19 | expect(fn(0)).to.be.true;
20 | });
21 |
22 | it('should be valid with -1', () => {
23 | expect(fn(-1)).to.be.true;
24 | });
25 |
26 | it('should be valid with true', () => {
27 | expect(fn(true)).to.be.true;
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/__test__/AvValidator.step.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.step;
4 |
5 | describe('Step Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 |
11 | it('should return custom error message if provided when field is not valid', () => {
12 | expect(fn(12, undefined, {value: 7, errorMessage: 'Get to steppin!'})).to.equal('Get to steppin!');
13 | });
14 |
15 | it('should accept numbers', () => {
16 | expect(fn(10, undefined, {value: 5})).to.be.true;
17 | expect(fn(11, undefined, {value: 5})).to.be.false;
18 | expect(fn(10, undefined, {value: 6})).to.be.false;
19 | expect(fn(10, undefined, {value: 2})).to.be.true;
20 |
21 | expect(fn('10', undefined, {value: 5})).to.be.true;
22 | expect(fn('11', undefined, {value: 5})).to.be.false;
23 | expect(fn('10', undefined, {value: 6})).to.be.false;
24 | expect(fn('10', undefined, {value: 2})).to.be.true;
25 |
26 | expect(fn(10, undefined, {value: '5'})).to.be.true;
27 | expect(fn(11, undefined, {value: '5'})).to.be.false;
28 | expect(fn(10, undefined, {value: '6'})).to.be.false;
29 | expect(fn(10, undefined, {value: '2'})).to.be.true;
30 | });
31 |
32 | it('should accept decimals', () => {
33 | expect(fn(10.2, undefined, {value: 5.1})).to.be.true;
34 | expect(fn(11.5, undefined, {value: 5.3})).to.be.false;
35 | expect(fn(10.5, undefined, {value: 6.7})).to.be.false;
36 | expect(fn(10.5, undefined, {value: 2.1})).to.be.true;
37 | expect(fn(0.00000002, undefined, {value: 0.00000001})).to.be.true;
38 | expect(fn(0.000000011, undefined, {value: 0.00000001})).to.be.false;
39 |
40 | expect(fn('10.2', undefined, {value: 5.1})).to.be.true;
41 | expect(fn('11.5', undefined, {value: 5.55})).to.be.false;
42 | expect(fn('10.55', undefined, {value: 6.2})).to.be.false;
43 | expect(fn('10.5', undefined, {value: 2.1})).to.be.true;
44 | expect(fn('0.00000002', undefined, {value: 0.00000001})).to.be.true;
45 | expect(fn('0.000000011', undefined, {value: 0.00000001})).to.be.false;
46 |
47 | expect(fn(10.2, undefined, {value: '5.1'})).to.be.true;
48 | expect(fn(11.5, undefined, {value: '5.4'})).to.be.false;
49 | expect(fn(10.5, undefined, {value: '6.7'})).to.be.false;
50 | expect(fn(10.5, undefined, {value: '2.1'})).to.be.true;
51 | expect(fn(0.00000002, undefined, {value: '0.00000001'})).to.be.true;
52 | expect(fn(0.000000011, undefined, {value: '0.00000001'})).to.be.false;
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/__test__/AvValidator.url.spec.js:
--------------------------------------------------------------------------------
1 | import {AvValidator} from 'availity-reactstrap-validation';
2 |
3 | const fn = AvValidator.url;
4 |
5 | describe('URL Validation', () => {
6 | it('should not require a value', () => {
7 | expect(fn('')).to.be.true;
8 | });
9 |
10 | describe('protocols', () => {
11 | it('should allow http://', () => {
12 | expect(fn('http://foo.com')).to.be.true;
13 | });
14 |
15 | it('should allow https://', () => {
16 | expect(fn('https://foo.com')).to.be.true;
17 | });
18 |
19 | it('should allow ftp://', () => {
20 | expect(fn('ftp://foo.com')).to.be.true;
21 | });
22 |
23 | it('should allow sftp://', () => {
24 | expect(fn('sftp://foo.com')).to.be.true;
25 | });
26 |
27 | it('should allow ftps://', () => {
28 | expect(fn('ftps://foo.com')).to.be.true;
29 | });
30 |
31 | it('should not allow sftps://', () => {
32 | expect(fn('sftps://foo.com')).to.be.false;
33 | });
34 |
35 | it('should not allow //', () => {
36 | expect(fn('//foo.com')).to.be.false;
37 | });
38 |
39 | it('should not allow no protocol', () => {
40 | expect(fn('foo.com')).to.be.false;
41 | });
42 | });
43 |
44 | describe('domain', () => {
45 | it('should allow a domain', () => {
46 | expect(fn('http://foo.com')).to.be.true;
47 | });
48 |
49 | it('should allow a subdomain', () => {
50 | expect(fn('http://www.foo.com')).to.be.true;
51 | expect(fn('http://bar.foo.com')).to.be.true;
52 | });
53 |
54 | it('should allow a multiple subdomains', () => {
55 | expect(fn('http://app.bar.foo.com')).to.be.true;
56 | expect(fn('http://deep.app.bar.foo.com')).to.be.true;
57 | });
58 |
59 | it('should now allow domains starting with a "."', () => {
60 | expect(fn('http://.foo.com')).to.be.false;
61 | expect(fn('http://.bar.foo.com')).to.be.false;
62 | });
63 |
64 | it('should now allow domain names starting with a "-"', () => {
65 | expect(fn('http://-foo.com')).to.be.false;
66 | expect(fn('http://-bar.foo.com')).to.be.false;
67 | });
68 |
69 | it('should now allow domains ending with a "-"', () => {
70 | expect(fn('http://foo-.com')).to.be.false;
71 | expect(fn('http://bar-.foo.com')).to.be.false;
72 | });
73 | });
74 |
75 | it('should allow port', () => {
76 | expect(fn('http://foo.com:8080')).to.be.true;
77 | expect(fn('http://bar.foo.com:1337/')).to.be.true;
78 | });
79 |
80 | it('should allow userid', () => {
81 | expect(fn('http://userid@foo.com')).to.be.true;
82 | expect(fn('http://userid@bar.foo.com:1337/')).to.be.true;
83 | });
84 |
85 | it('should allow userid with password', () => {
86 | expect(fn('http://userid:password@foo.com')).to.be.true;
87 | expect(fn('http://userid:password@bar.foo.com:1337/')).to.be.true;
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/docs/lib/Components/CheckboxPage.js:
--------------------------------------------------------------------------------
1 | /* eslint react/no-multi-comp: 0, react/prop-types: 0 */
2 | import React from 'react';
3 | import { PrismCode } from 'react-prism';
4 | import Helmet from 'react-helmet';
5 |
6 | import CheckboxExample from '../examples/Checkbox';
7 | const CheckboxExampleSource = require('!!raw!../examples/Checkbox.js');
8 | import CheckboxTrueValueExample from '../examples/CheckboxTrueValue';
9 | const CheckboxTrueValueExampleSource = require('!!raw!../examples/CheckboxTrueValue.js');
10 | import CheckboxFalseValueExample from '../examples/CheckboxFalseValue';
11 | const CheckboxFalseValueExampleSource = require('!!raw!../examples/CheckboxFalseValue.js');
12 | import CheckboxDefaultExample from '../examples/CheckboxDefault';
13 | const CheckboxDefaultExampleSource = require('!!raw!../examples/CheckboxDefault.js');
14 |
15 | export default class CheckboxPage extends React.Component {
16 | render() {
17 | return (
18 |
19 |
20 |
Checkboxes
21 |
22 | Checkboxes are slightly special as the user cannot define a value, but only check and uncheck the box.
23 | There are special props, trueValue
and falseValue
which allow you to determine what
24 | the value returned will be when the box is check or not checked respectfully. trueValue
will
25 | default to true
and falseValue
will default to false
.
26 |
27 |
28 |
29 |
30 |
31 |
32 | {CheckboxExampleSource}
33 |
34 |
35 |
36 |
True Value
37 |
38 | Checking the boxes and submitting the form, you will see the value passed is the trueValue
for
39 | the checkbox; true
by default.
40 |
41 |
42 |
43 |
44 |
45 |
46 | {CheckboxTrueValueExampleSource}
47 |
48 |
49 |
50 |
False Value
51 |
52 | Leaving the boxes unchecked and submitting the form, you will see the value passed is the
53 | falseValue
for the checkbox; false
by default.
54 |
55 |
56 |
57 |
58 |
59 |
60 | {CheckboxFalseValueExampleSource}
61 |
62 |
63 |
64 |
Model Default Values
65 |
66 | Using the model prop on the form, you can indicate if the checkboxes should be checked or unchecked when
67 | initialized by providing the trueValue or falseValue in the model prop on the form.
68 |
69 |
70 |
71 |
72 |
73 |
74 | {CheckboxDefaultExampleSource}
75 |
76 |
77 |
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/docs/lib/Components/FormPage.js:
--------------------------------------------------------------------------------
1 | /* eslint react/no-multi-comp: 0, react/prop-types: 0 */
2 | import React from 'react';
3 | import { PrismCode } from 'react-prism';
4 | import Helmet from 'react-helmet';
5 |
6 | import FormExample from '../examples/Form';
7 | const FormExampleSource = require('!!raw!../examples/Form.js');
8 | import FormOnSubmitExample from '../examples/FormOnSubmit';
9 | const FormOnSubmitExampleSource = require('!!raw!../examples/FormOnSubmit.js');
10 | import FormOnValidSubmitExample from '../examples/FormOnValidSubmit';
11 | const FormOnValidSubmitExampleSource = require('!!raw!../examples/FormOnValidSubmit.js');
12 | import FormOnInvalidSubmitExample from '../examples/FormOnInvalidSubmit';
13 | const FormOnInvalidSubmitExampleSource = require('!!raw!../examples/FormOnInvalidSubmit.js');
14 | import FormModelExample from '../examples/FormModel';
15 | const FormModelExampleSource = require('!!raw!../examples/FormModel.js');
16 |
17 | export default class FormPage extends React.Component {
18 | render() {
19 | return (
20 |
21 |
22 |
AvForm
23 |
The AvForm component wraps reactstrap's form to add context that the other Av components know about to help share validation state
24 |
25 |
26 |
27 |
28 |
29 | {FormExampleSource}
30 |
31 |
32 |
33 |
OnSubmit
34 |
This callback is called with every submission of the form
35 |
36 |
37 |
38 |
39 |
40 | {FormOnSubmitExampleSource}
41 |
42 |
43 |
44 |
OnValidSubmit
45 |
This callback is called only when every field is valid when submitted
46 |
47 |
48 |
49 |
50 |
51 | {FormOnValidSubmitExampleSource}
52 |
53 |
54 |
55 |
OnInvalidSubmit
56 |
This callback is called only when any field is invalid when submitted
57 |
58 |
59 |
60 |
61 |
62 | {FormOnInvalidSubmitExampleSource}
63 |
64 |
65 |
66 |
Model (Easy default values)
67 |
68 | Pass an object in which the keys correspond to the name props of the form's input to set their initial value.
69 | Nested objects can be accessed via dot notation. Individual array indexes can be accessed via bracket notation.
70 | The values object returned to the various submissions handlers will reflex the object shape.
71 | Behind the scenes, lodash's get and set are being used,
72 | look at lodash's documentation to learn more about how to access complex data in the model.
73 |
74 |
75 |
76 |
77 |
78 |
79 | {FormModelExampleSource}
80 |
81 |
82 |
83 | );
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/docs/lib/Components/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import { Container, Row, Col, Nav, NavItem, NavLink } from 'reactstrap';
4 |
5 | const ComponentLink = (props) => {
6 | return (
7 |
8 |
9 | {props.item.name}
10 |
11 |
12 | );
13 | };
14 |
15 |
16 | class Components extends React.Component {
17 | constructor(props) {
18 | super(props);
19 |
20 | this.state = {
21 | navItems: [
22 | {
23 | name: 'AvForm',
24 | to: '/components/avform/',
25 | },
26 | {
27 | name: 'Validators',
28 | to: '/components/validators/',
29 | },
30 | {
31 | name: 'Checkboxes',
32 | to: '/components/checkboxes/',
33 | },
34 | ],
35 | };
36 | }
37 | render() {
38 | return (
39 |
40 |
41 |
42 |
43 |
Components
44 |
45 | {this.state.navItems.map((item, i) => {
46 | return ;
47 | })}
48 |
49 |
50 |
51 |
52 | {this.props.children}
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default Components;
61 |
--------------------------------------------------------------------------------
/docs/lib/Home/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { PrismCode } from 'react-prism';
3 | import { Jumbotron, Button, Container, Row, Col } from 'reactstrap';
4 | import { Link } from 'react-router';
5 | import Example from '../examples/import-basic';
6 |
7 | const importBasic = require('!!raw!../examples/import-basic.js');
8 |
9 | export default () => {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Availity reactstrap Validation
20 |
21 | Easy to use form validation for reactstrap
22 |
23 |
24 | View on GitHub
25 | View Components
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Installation
35 |
36 | NPM
37 | Install reactstrap and peer dependencies via NPM
38 |
39 | npm install --save availity-reactstrap-validation react react-dom
40 |
41 | ES6 - import the components you need
42 |
43 |
44 |
45 |
46 |
47 | {importBasic}
48 |
49 |
50 | About the Project
51 |
52 | This library contains helper components that extend the form capabilities provided by reactstrap. The library does not depend on jQuery or Bootstrap javascript, only reactstrap.
53 | Use the form and input components provided by this library directly instead of the ones provided by reactstrap. You can use the components provided by reactstrap in conjunction with the components this library provides without an problem, but validation will not work for those particular components.
54 |
55 |
56 |
57 |
58 | );
59 | };
60 |
--------------------------------------------------------------------------------
/docs/lib/NotFound/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Container, Row, Col } from 'reactstrap';
3 | import { Link } from 'react-router';
4 | import Helmet from 'react-helmet';
5 |
6 | export default () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 404 - Not Found
18 |
19 | Can't find what you're looking for? Open up an issue.
20 |
21 |
22 | Get Started
23 | View Components
24 |
25 |
26 |
27 |
28 |
29 |
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/docs/lib/UI/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Container, Row, Col } from 'reactstrap';
3 |
4 | export default () => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | © 2016 - present Availity
12 | {' '}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/docs/lib/UI/Layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Helmet from 'react-helmet';
3 | import Footer from './Footer';
4 | import Nav from './Nav';
5 |
6 | export default (props) => {
7 | return (
8 |
9 |
17 |
18 | {props.children}
19 |
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/docs/lib/UI/Nav.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link } from 'react-router';
3 | import {
4 | NavbarToggler,
5 | Container,
6 | Collapse,
7 | Navbar,
8 | NavbarBrand,
9 | Nav,
10 | NavItem,
11 | NavLink,
12 | } from 'reactstrap';
13 |
14 | export default class UINav extends Component {
15 | constructor(props) {
16 | super(props);
17 |
18 | this.toggleNavbar = this.toggleNavbar.bind(this);
19 | this.state = {
20 | showNavbar: false,
21 | };
22 | }
23 | toggleNavbar(e) {
24 | e.preventDefault();
25 | this.setState({
26 | showNavbar: !this.state.showNavbar,
27 | });
28 | }
29 | render() {
30 | return (
31 |
32 |
33 |
34 | Availity reactstrap Validation
35 |
36 |
37 |
38 |
39 |
40 |
46 | Components
47 |
48 |
49 |
50 |
51 | GitHub
52 |
53 |
54 |
55 |
56 |
57 |
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/docs/lib/UI/index.js:
--------------------------------------------------------------------------------
1 | import Footer from './Footer';
2 | import Nav from './Nav';
3 | import Layout from './Layout';
4 |
5 | export default {
6 | Nav,
7 | Layout,
8 | Footer
9 | };
10 |
--------------------------------------------------------------------------------
/docs/lib/app.js:
--------------------------------------------------------------------------------
1 | import 'bootstrap-css';
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 | import ReactDOMServer from 'react-dom/server';
6 | import { createHistory } from 'history'
7 | import { Router, RouterContext, match, useRouterHistory, createMemoryHistory } from 'react-router';
8 | import routes from './routes';
9 | import Helmet from 'react-helmet';
10 |
11 | // Client render (optional):
12 | if (typeof document !== 'undefined') {
13 | const history = useRouterHistory(createHistory)({ basename: window.basename });
14 | const outlet = document.getElementById('app');
15 | ReactDOM.render( window.scrollTo(0, 0)} history={history} routes={routes} />, outlet)
16 | }
17 |
18 | // Exported static site renderer:
19 | export default (locals, callback) => {
20 | const basename = locals.basename.substr(0, locals.basename.length - 1);
21 | match({ routes, location: locals.path, basename }, (error, redirectLocation, renderProps) => {
22 | var url;
23 |
24 | if (redirectLocation && redirectLocation.pathname) {
25 | url = redirectLocation.pathname;
26 | callback(null, `
27 |
28 |
29 |
30 |
31 |
32 | `);
33 | }
34 | const body = ReactDOMServer.renderToString( );
35 | const head = Helmet.rewind();
36 | let markup = `
37 |
38 |
39 |
40 |
41 |
42 | ${head.title.toString()}
43 | ${head.meta.toString()}
44 |
45 |
46 |
47 |
48 |
49 | ${body}
50 |
51 |
52 |
53 |
54 | `;
55 | callback(null, markup);
56 | });
57 | };
58 |
--------------------------------------------------------------------------------
/docs/lib/examples/Checkbox.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvGroup, AvInput } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleSubmit(event, errors, values) {
14 | this.setState({errors, values});
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
21 |
22 |
23 | Check it Out
24 |
25 |
26 |
27 |
28 | Are You In?
29 |
30 |
31 |
32 | Submit
33 |
34 |
35 | {this.state.values &&
36 |
Submission values
37 | Invalid: {this.state.errors.join(', ')}
38 | Values:
{JSON.stringify(this.state.values, null, 2)}
39 |
}
40 |
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/docs/lib/examples/CheckboxDefault.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvGroup, AvInput } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleSubmit(event, errors, values) {
14 | this.setState({errors, values});
15 | }
16 |
17 | render() {
18 | const model = {
19 | checkbox1: true,
20 | checkbox2: false,
21 | checkbox3: false,
22 | checkbox4: true,
23 | checkbox5: 'yes',
24 | checkbox6: 'yes',
25 | checkbox7: 'no',
26 | checkbox8: 'no',
27 | checkbox9: 'other?',
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 |
35 | true is "checked" (default)
36 |
37 |
38 |
39 |
40 | false is "unchecked" (default)
41 |
42 |
43 |
44 |
45 | false can be "checked"
46 |
47 |
48 |
49 |
50 | true can be "unchecked"
51 |
52 |
53 |
54 |
55 | make "yes" checked
56 |
57 |
58 |
59 |
60 | make "yes" unchecked
61 |
62 |
63 |
64 |
65 | make "no" checked
66 |
67 |
68 |
69 |
70 | make "no" unchecked
71 |
72 |
73 |
74 |
75 | any values that are not the trueValue is unchecked
76 |
77 |
78 |
79 | Submit
80 |
81 |
82 | {this.state.values &&
83 |
Submission values
84 | Invalid: {this.state.errors.join(', ')}
85 | Values:
{JSON.stringify(this.state.values, null, 2)}
86 |
}
87 |
88 | );
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/docs/lib/examples/CheckboxFalseValue.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvGroup, AvInput } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleSubmit(event, errors, values) {
14 | this.setState({errors, values});
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
21 |
22 |
23 | Agree to this!
24 |
25 |
26 |
27 | Submit
28 |
29 |
30 | {this.state.values &&
31 |
Submission values
32 | Invalid: {this.state.errors.join(', ')}
33 | Values:
{JSON.stringify(this.state.values, null, 2)}
34 |
}
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/docs/lib/examples/CheckboxTrueValue.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvGroup, AvInput } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleSubmit(event, errors, values) {
14 | this.setState({errors, values});
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
21 |
22 |
23 | Agree to this!
24 |
25 |
26 |
27 | Submit
28 |
29 |
30 | {this.state.values &&
31 |
Submission values
32 | Invalid: {this.state.errors.join(', ')}
33 | Values:
{JSON.stringify(this.state.values, null, 2)}
34 |
}
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/docs/lib/examples/Form.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField, AvGroup, AvInput, AvFeedback, AvRadioGroup, AvRadio, AvCheckboxGroup, AvCheckbox } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup, CustomInput } from 'reactstrap';
4 |
5 | export default class FormExample extends React.Component {
6 | render() {
7 | return (
8 |
9 | {/* With AvField */}
10 |
11 | {/* With AvGroup AvInput and AvFeedback to build your own */}
12 |
13 | Rank
14 |
15 | This is an error!
16 |
17 | {/* Radios */}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {/* checkboxes */}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | {/* With select and AvField */}
76 |
77 | 1
78 | 2
79 | 3
80 | 4
81 | 5
82 |
83 |
84 |
85 | 1
86 | 2
87 | 3
88 | 4
89 | 5
90 |
91 |
92 |
93 |
94 |
95 | Check it out
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Submit
107 |
108 |
109 | );
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/docs/lib/examples/FormFeedback.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Form, FormGroup, Label, Input, FormFeedback, FormText } from 'reactstrap';
3 |
4 | export default class Example extends React.Component {
5 | render() {
6 | return (
7 |
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/docs/lib/examples/FormModel.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField, AvGroup, AvInput, AvFeedback, AvRadioGroup, AvRadio, AvCheckboxGroup, AvCheckbox } from 'availity-reactstrap-validation';
3 | import { Button, Row, Col, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleSubmit(event, errors, values) {
14 | this.setState({errors, values});
15 | }
16 |
17 | render() {
18 | const defaultValues = {
19 | locationType: 'work',
20 | locationQualities: [
21 | 'beautiful',
22 | 'awesome',
23 | 'wonderful',
24 | ],
25 | name: 'Availity',
26 | location: {
27 | street: '10752 Deerwood Park Blvd',
28 | suite: '110',
29 | city: 'Jacksonville',
30 | state: 'Florida',
31 | zip: '32256',
32 | },
33 | checkItOut: true,
34 | };
35 |
36 | return (
37 |
38 |
39 | {/* Radios */}
40 |
41 |
42 |
43 |
44 |
45 | {/* Checkboxes */}
46 |
47 |
48 |
49 |
50 |
51 | {/* With AvField */}
52 |
53 |
54 |
55 | {/* With AvGroup AvInput and AvFeedback to build your own */}
56 |
57 | Street
58 | {/* dot notation for the name to access deep values. The shape is also the same in the submit callbacks */}
59 |
60 | {/* this only shows when there is an error, use reactstrap's FormFeedback if you want is to always be displayed */}
61 | This is an error!
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | {/* With select and AvField */}
74 |
75 | Something
76 | Something else
77 | Florida
78 | This is just an exmaple
79 | Not Florida
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | Check it out!
89 |
90 |
91 | Submit
92 |
93 |
94 | {this.state.values &&
95 |
Submission values
96 | Invalid: {this.state.errors.join(', ')}
97 | Values:
{JSON.stringify(this.state.values, null, 2)}
98 |
}
99 |
100 | );
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/docs/lib/examples/FormOnInvalidSubmit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField, AvGroup, AvInput, AvFeedback, AvRadioGroup, AvRadio, AvCheckboxGroup, AvCheckbox } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleInvalidSubmit = this.handleInvalidSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleInvalidSubmit(event, errors, values) {
14 | this.setState({errors, values});
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
21 | {/* With AvField */}
22 |
23 | {/* With AvGroup AvInput and AvFeedback to build your own */}
24 |
25 | Rank
26 |
27 | {/* this only shows when there is an error, use reactstrap's FormFeedback if you want is to always be displayed */}
28 | This is an error!
29 |
30 | {/* Radios */}
31 |
32 |
33 |
34 |
35 |
36 |
37 | {/* Radios */}
38 |
39 |
40 |
41 |
42 |
43 |
44 | {/* With select and AvField */}
45 |
46 | 1
47 | 2
48 | 3
49 | 4
50 | 5
51 |
52 |
53 | Submit
54 |
55 |
56 | {this.state.values &&
57 |
Submission values
58 | Invalid: {this.state.errors.join(', ')}
59 | Values:
{JSON.stringify(this.state.values, null, 2)}
60 |
}
61 |
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/docs/lib/examples/FormOnSubmit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField, AvGroup, AvInput, AvFeedback, AvRadioGroup, AvRadio, AvCheckboxGroup, AvCheckbox } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleSubmit(event, errors, values) {
14 | this.setState({errors, values});
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
21 | {/* With AvField */}
22 |
23 | {/* With AvGroup AvInput and AvFeedback to build your own */}
24 |
25 | Rank
26 |
27 | {/* this only shows when there is an error, use reactstrap's FormFeedback if you want is to always be displayed */}
28 | This is an error!
29 |
30 | {/* Radios */}
31 |
32 |
33 |
34 |
35 |
36 |
37 | {/* Checkboxes */}
38 |
39 |
40 |
41 |
42 |
43 |
44 | {/* With select and AvField */}
45 |
46 | 1
47 | 2
48 | 3
49 | 4
50 | 5
51 |
52 |
53 |
54 | 1
55 | 2
56 | 3
57 | 4
58 | 5
59 |
60 |
61 | Submit
62 |
63 |
64 | {this.state.values &&
65 |
Submission values
66 | Invalid: {this.state.errors.join(', ')}
67 | Values:
{JSON.stringify(this.state.values, null, 2)}
68 |
}
69 |
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/docs/lib/examples/FormOnValidSubmit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField, AvGroup, AvInput, AvFeedback, AvRadioGroup, AvRadio, AvCheckboxGroup, AvCheckbox } from 'availity-reactstrap-validation';
3 | import { Button, Label, FormGroup } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.handleValidSubmit = this.handleValidSubmit.bind(this);
10 | this.state = {};
11 | }
12 |
13 | handleValidSubmit(event, values) {
14 | this.setState({values});
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
21 | {/* With AvField */}
22 |
23 | {/* With AvGroup AvInput and AvFeedback to build your own */}
24 |
25 | Rank
26 |
27 | {/* this only shows when there is an error, use reactstrap's FormFeedback if you want is to always be displayed */}
28 | This is an error!
29 |
30 | {/* Radios */}
31 |
32 |
33 |
34 |
35 |
36 |
37 | {/* Checkboxes */}
38 |
39 |
40 |
41 |
42 |
43 |
44 | {/* With select and AvField */}
45 |
46 | 1
47 | 2
48 | 3
49 | 4
50 | 5
51 |
52 |
53 | Submit
54 |
55 |
56 | {this.state.values &&
57 |
Submission values
58 | Values:
{JSON.stringify(this.state.values, null, 2)}
59 |
}
60 |
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationAsync.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import _debounce from 'lodash.debounce';
4 | import { Button } from 'reactstrap';
5 |
6 | export default class Example extends React.Component {
7 | constructor (props) {
8 | super(props);
9 |
10 | this.state = {
11 | valid: 'true'
12 | }
13 | }
14 |
15 | // debounce to not pound the 'server'
16 | validate = _debounce((value, ctx, input, cb) => {
17 |
18 | // cancel pending 'network call'
19 | clearTimeout(this.timeout);
20 |
21 | // simulate network call
22 | this.timeout = setTimeout(() => {
23 | cb(value === 'valid' || value === '');
24 | }, 500);
25 |
26 | }, 300);
27 |
28 | render() {
29 | return (
30 |
31 |
32 | Submit
33 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationCustomMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
15 |
21 | Submit
22 |
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationDate.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationDateRange.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationDateTime.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationEmail.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationMatch.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationMax.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 | Submit
13 |
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationMaxChecked.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvCheckbox, AvCheckboxGroup } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Submit
17 |
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationMaxLength.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 | Submit
13 |
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationMin.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 | Submit
13 |
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationMinChecked.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvCheckbox, AvCheckboxGroup } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Submit
17 |
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationMinLength.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 | Submit
13 |
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationNpi.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 | Submit
11 |
12 | );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationNumber.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationPattern.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 | Submit
13 |
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationPhone.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationRequired.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/ValidationStep.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | Submit
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/docs/lib/examples/import-basic.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AvForm, AvField } from 'availity-reactstrap-validation';
3 | import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
4 |
5 | export default class Example extends React.Component {
6 | render() {
7 | const modalError = this.state.error ? 'not' : ''; // This is just for the modal
8 | return (
9 |
10 |
11 |
12 | Submit
13 |
14 |
15 | {/* below this is just for show, it's not needed unless you want a modal upon form submission */}
16 |
17 | Form is {modalError} valid!
18 |
19 | You have {modalError} successfully filled out the form and submitted it. Your email ({this.state.email}) is {modalError} valid!
20 |
21 |
22 | Ok, got it!
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | constructor(props) {
30 | super(props);
31 |
32 | this.handleValidSubmit = this.handleValidSubmit.bind(this);
33 | this.handleInvalidSubmit = this.handleInvalidSubmit.bind(this);
34 | this.closeModal = this.closeModal.bind(this);
35 | this.state = {email: false};
36 | }
37 |
38 | handleValidSubmit(event, values) {
39 | this.setState({email: values.email});
40 | }
41 |
42 | handleInvalidSubmit(event, errors, values) {
43 | this.setState({email: values.email, error: true});
44 | }
45 |
46 | closeModal() {
47 | this.setState({email: false, error: false});
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/docs/lib/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, IndexRoute, IndexRedirect } from 'react-router';
3 | import Home from './Home';
4 | import FormPage from './Components/FormPage';
5 | import ValidatorsPage from './Components/ValidatorsPage';
6 | import CheckboxPage from './Components/CheckboxPage';
7 | import NotFound from './NotFound';
8 | import Components from './Components';
9 | import UI from './UI';
10 |
11 | const routes = (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 |
24 | export default routes;
25 |
--------------------------------------------------------------------------------
/docs/static/docs.css:
--------------------------------------------------------------------------------
1 | .container-fluid {
2 | max-width: 1200px;
3 | }
4 |
5 | @media (max-width: 575px) {
6 | .navbar-toggleable > .container {
7 | width: 100%;
8 | padding-right: 15px;
9 | padding-left: 15px;
10 | }
11 | }
12 |
13 | .jumbotron-header, .jumbotron-header p:last-child, .navbar {
14 | margin-bottom: 0
15 | }
16 |
17 | .jumbotron-header .btn {
18 | margin-right: 1rem;
19 | }
20 |
21 | .jumbotron-header .btn + .btn {
22 | margin-right: 0;
23 | }
24 |
25 | @media (max-width: 543px) {
26 | .jumbotron-header .btn {
27 | display: block;
28 | margin: 0 0 1rem 0;
29 | }
30 | }
31 |
32 | .content {
33 | margin-top: 2rem;
34 | }
35 |
36 | .header .navbar-nav .nav-link.active {
37 | color: #333;
38 | }
39 |
40 | .header.navbar {
41 | padding-left: 0;
42 | padding-right: 0;
43 | }
44 |
45 | .footer {
46 | margin-top: 2rem;
47 | padding: 2rem 0;
48 | border-top: 1px solid #e7e7e7;
49 | }
50 |
51 | .footer .social {
52 | margin-bottom: 0;
53 | }
54 |
55 | .footer .social a {
56 | display: inline-block;
57 | }
58 |
59 | .footer .social img {
60 | display: block;
61 | }
62 |
63 | .navbar-collapse .container-fluid {
64 | padding: 2rem 2.5rem;
65 | border-bottom: 1px solid #55595c
66 | }
67 | .navbar-collapse .text-muted, .navbar-collapse h4 {
68 | color: #818a91
69 | }
70 | .about {
71 | float: left;
72 | max-width: 30rem;
73 | margin-right: 3rem
74 | }
75 | .social a {
76 | font-weight: 500;
77 | color: #eceeef
78 | }
79 | .social a:hover {
80 | color: #fff
81 | }
82 | .jumbotron-header {
83 | padding-top: 2rem;
84 | padding-bottom: 2rem;
85 | background-color: #fff
86 | }
87 | .jumbotron-heading {
88 | font-weight: 300
89 | }
90 |
91 | footer {
92 | padding-top: 3rem;
93 | padding-bottom: 3rem
94 | }
95 | footer p {
96 | margin-bottom: .25rem
97 | }
98 |
99 | h1,h2,h3,h4,h5,h6,strong,b {
100 | font-weight: 300;
101 | }
102 |
103 | h1,h2,h3 {
104 | margin-bottom: 1rem;
105 | }
106 |
107 | .docs-example {
108 | position: relative;
109 | padding: 3em 1em 1em;
110 | border: 1px solid #d8d8d8;
111 | margin: 1.5rem 0 0;
112 | }
113 |
114 | .docs-example::before {
115 | position: absolute;
116 | left: 0;
117 | top: 0;
118 | padding: 1em;
119 | color: #aaa;
120 | font-size: smaller;
121 | font-weight: 500;
122 | content: 'EXAMPLE';
123 | }
124 |
125 | .docs-example::after {
126 | clear: both;
127 | content: " ";
128 | display: table;
129 | }
130 |
131 | .docs-example .card {
132 | max-width: 320px;
133 | }
134 |
135 | pre, code {
136 | background: #f5f5f5;
137 | border-radius: 0;
138 | border-color: #d8d8d8;
139 | margin: 0;
140 | }
141 |
142 | .docs-example + pre {
143 | border: 1px solid #d8d8d8;
144 | border-top: none;
145 | padding: 1em;
146 | margin-top: 0 !important;
147 | clear: both;
148 | }
149 |
150 | .docs-sidebar .nav-item {
151 | padding: .2rem 0 .2rem 0;
152 | }
153 |
154 | .docs-sidebar .nav-link {
155 | color: #888;
156 | padding: .3rem .5rem .3rem 1rem;
157 | border-left: 2px solid transparent;
158 | }
159 |
160 | .docs-sidebar .nav-link:hover {
161 | color: #777
162 | }
163 |
164 | .docs-sidebar .nav-link.active {
165 | color: #666;
166 | border-left: 2px solid #d9534f;
167 | }
168 |
169 | .docs-example > .container .row {
170 | margin-bottom: 1rem;
171 | }
172 |
173 | .docs-example > .container .row > [class^="col"] {
174 | padding-top: .75rem;
175 | padding-bottom: .75rem;
176 | background-color: #E5EDF5;
177 | border: 1px solid #C9C1D5;
178 | color: #5F5F5F;
179 | }
180 |
181 | .docs-example .form-control + .form-control {
182 | margin-top: .5rem;
183 | }
184 |
185 | .docs-example .nav-tabs {
186 | margin-bottom: 1rem;
187 | }
188 |
189 | .docs-example .progress {
190 | margin-bottom: 1rem;
191 | }
192 |
193 |
194 | /* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+bash+jsx&plugins=autolinker */
195 | /**
196 | * okaidia theme for JavaScript, CSS and HTML
197 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/
198 | * @author ocodia
199 | */
200 |
201 | code[class*="language-"],
202 | pre[class*="language-"] {
203 | color: #f8f8f2;
204 | background: none;
205 | text-shadow: 0 1px rgba(0, 0, 0, 0.3);
206 | text-align: left;
207 | white-space: pre;
208 | word-spacing: normal;
209 | word-break: normal;
210 | word-wrap: normal;
211 | line-height: 1.5;
212 |
213 | -moz-tab-size: 4;
214 | -o-tab-size: 4;
215 | tab-size: 4;
216 |
217 | -webkit-hyphens: none;
218 | -moz-hyphens: none;
219 | -ms-hyphens: none;
220 | hyphens: none;
221 | }
222 |
223 | /* Code blocks */
224 | pre[class*="language-"] {
225 | padding: 1em;
226 | margin: 1.5em 0;
227 | overflow: auto;
228 | }
229 |
230 | :not(pre) > code[class*="language-"],
231 | pre[class*="language-"] {
232 | background: #272822;
233 | }
234 |
235 | /* Inline code */
236 | :not(pre) > code[class*="language-"] {
237 | padding: .1em;
238 | border-radius: .3em;
239 | white-space: normal;
240 | }
241 |
242 | .token.comment,
243 | .token.prolog,
244 | .token.doctype,
245 | .token.cdata {
246 | color: slategray;
247 | }
248 |
249 | .token.punctuation {
250 | color: #f8f8f2;
251 | }
252 |
253 | .namespace {
254 | opacity: .7;
255 | }
256 |
257 | .token.property,
258 | .token.tag,
259 | .token.constant,
260 | .token.symbol,
261 | .token.deleted {
262 | color: #f92672;
263 | }
264 |
265 | .token.boolean,
266 | .token.number {
267 | color: #ae81ff;
268 | }
269 |
270 | .token.selector,
271 | .token.attr-name,
272 | .token.string,
273 | .token.char,
274 | .token.builtin,
275 | .token.inserted {
276 | color: #a6e22e;
277 | }
278 |
279 | .token.operator,
280 | .token.entity,
281 | .token.url,
282 | .language-css .token.string,
283 | .style .token.string,
284 | .token.variable {
285 | color: #f8f8f2;
286 | }
287 |
288 | .token.atrule,
289 | .token.attr-value,
290 | .token.function {
291 | color: #e6db74;
292 | }
293 |
294 | .token.keyword {
295 | color: #66d9ef;
296 | }
297 |
298 | .token.regex,
299 | .token.important {
300 | color: #fd971f;
301 | }
302 |
303 | .token.important,
304 | .token.bold {
305 | font-weight: bold;
306 | }
307 | .token.italic {
308 | font-style: italic;
309 | }
310 |
311 | .token.entity {
312 | cursor: help;
313 | }
314 |
315 | .token a {
316 | color: inherit;
317 | }
318 |
319 | code .tag {
320 | font-size: inherit;
321 | padding: 0;
322 | display: inherit;
323 | }
324 |
--------------------------------------------------------------------------------
/docs/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Availity/availity-reactstrap-validation/896c2e0aaa2e044720ae58ad5eed1d58ede5347b/docs/static/favicon.ico
--------------------------------------------------------------------------------
/docs/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Availity/availity-reactstrap-validation/896c2e0aaa2e044720ae58ad5eed1d58ede5347b/docs/static/logo.png
--------------------------------------------------------------------------------
/mocha-webpack.opts:
--------------------------------------------------------------------------------
1 | --colors
2 | --require __test__/.setup.js
3 | --webpack-config webpack.test.config.js
4 | --timeout 0
5 | --inline-diffs
6 | __test__/**/*.spec.js
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "availity-reactstrap-validation",
3 | "version": "2.7.1",
4 | "author": "Evan Sharp ",
5 | "description": "Form validation helpers for reactstrap",
6 | "main": "lib/index.js",
7 | "engines": {
8 | "node": ">= 5.0.0"
9 | },
10 | "scripts": {
11 | "ci": "npm run lint && cross-env BABEL_ENV=test nyc mocha-webpack",
12 | "report-coverage": "coveralls < ./__test__/coverage/lcov.info",
13 | "test:coverage": "cross-env BABEL_ENV=test nyc mocha-webpack && npm run cleanup",
14 | "test": "mocha-webpack && npm run cleanup",
15 | "test:watch": "mocha-webpack --watch && npm run cleanup",
16 | "cleanup": "rimraf .nyc_output .tmp",
17 | "start": "webpack-dev-server --config ./webpack.dev.config.js --watch",
18 | "build:docs": "cross-env WEBPACK_BUILD=production webpack --config ./webpack.dev.config.js --colors",
19 | "build": "cross-env WEBPACK_BUILD=production webpack --progress --colors && webpack --colors",
20 | "prebuild": "babel src --out-dir lib",
21 | "lint": "eslint src",
22 | "create-release": "npm test && sh ./scripts/release",
23 | "publish-release": "npm test && sh ./scripts/publish"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/Availity/availity-reactstrap-validation.git"
28 | },
29 | "files": [
30 | "LICENSE",
31 | "README.md",
32 | "CHANGELOG.md",
33 | "lib",
34 | "dist"
35 | ],
36 | "keywords": [
37 | "forms",
38 | "validation",
39 | "bootstrap",
40 | "react",
41 | "component",
42 | "components",
43 | "react-component",
44 | "availity",
45 | "ui"
46 | ],
47 | "contributors": [
48 | "Evan Sharp "
49 | ],
50 | "license": "MIT",
51 | "bugs": {
52 | "url": "https://github.com/Availity/availity-reactstrap-validation/issues"
53 | },
54 | "homepage": "https://github.com/Availity/availity-reactstrap-validation",
55 | "dependencies": {
56 | "babel-runtime": "^6.26.0",
57 | "classnames": "^2.2.6",
58 | "lodash": "^4.17.10",
59 | "moment": "^2.22.2",
60 | "prop-types": "^15.6.2"
61 | },
62 | "peerDependencies": {
63 | "react": "^0.14.9 || ^15.3.0 || ^16.0.0",
64 | "react-dom": "^0.14.9 || ^15.3.0 || ^16.0.0",
65 | "reactstrap": ">= 5.0.0-alpha.4"
66 | },
67 | "devDependencies": {
68 | "babel-cli": "^6.10.1",
69 | "babel-core": "^6.26.3",
70 | "babel-eslint": "^7.2.2",
71 | "babel-loader": "^6.2.5",
72 | "babel-plugin-istanbul": "^4.1.1",
73 | "babel-plugin-transform-class-properties": "^6.18.0",
74 | "babel-plugin-transform-proto-to-assign": "^6.9.0",
75 | "babel-plugin-transform-runtime": "^6.15.0",
76 | "babel-preset-es2015": "^6.13.2",
77 | "babel-preset-es2015-loose": "^7.0.0",
78 | "babel-preset-react": "^6.5.0",
79 | "babel-preset-stage-0": "^6.5.0",
80 | "babel-register": "^6.11.6",
81 | "bootstrap": "^4.1.3",
82 | "chai": "^3.5.0",
83 | "chai-as-promised": "^6.0.0",
84 | "chai-enzyme": "^0.6.1",
85 | "cheerio": "^0.22.0",
86 | "clean-webpack-plugin": "^0.1.8",
87 | "conventional-changelog-cli": "^1.3.22",
88 | "conventional-recommended-bump": "^1.0.0",
89 | "copy-webpack-plugin": "^3.0.1",
90 | "coveralls": "^3.0.0",
91 | "cross-env": "^5.2.0",
92 | "css-loader": "^0.28.0",
93 | "ejs": "^2.6.1",
94 | "enzyme": "^2.8.2",
95 | "eslint": "^4.18.2",
96 | "eslint-config-availity": "^2.1.0",
97 | "eslint-plugin-import": "^2.13.0",
98 | "eslint-plugin-jsx-a11y": "^5.0.3",
99 | "eslint-plugin-react": "^6.10.3",
100 | "extract-text-webpack-plugin": "^1.0.1",
101 | "history": "^2.0.0",
102 | "jsdom": "^13.1.0",
103 | "json-loader": "^0.5.4",
104 | "lodash.debounce": "^4.0.8",
105 | "mocha": "^5.2.0",
106 | "mocha-webpack": "^0.6.0",
107 | "nyc": "^8.1.0",
108 | "raw-loader": "^0.5.1",
109 | "react": "^15.5.4",
110 | "react-addons-pure-render-mixin": "^15.5.2",
111 | "react-addons-test-utils": "^15.5.1",
112 | "react-dom": "^15.5.4",
113 | "react-helmet": "^5.0.3",
114 | "react-highlight": "^0.9.0",
115 | "react-prism": "3.2.1",
116 | "react-router": "^2.0.1",
117 | "react-test-renderer": "^15.5.4",
118 | "react-transition-group": "^1.1.3",
119 | "reactstrap": "^6.0.0",
120 | "rimraf": "^2.5.4",
121 | "sinon": "^2.1.0",
122 | "sinon-chai": "^2.8.0",
123 | "static-site-generator-webpack-plugin": "^2.0.1",
124 | "style-loader": "^0.16.1",
125 | "webpack": "^1.13.2",
126 | "webpack-dev-server": "^3.1.11"
127 | },
128 | "nyc": {
129 | "include": [
130 | "src/**/*.js"
131 | ],
132 | "reporter": [
133 | "lcov",
134 | "html",
135 | "text-summary"
136 | ],
137 | "sourceMap": false,
138 | "instrument": false,
139 | "report-dir": "./__test__/coverage"
140 | },
141 | "prettier": {
142 | "printWidth": 120,
143 | "singleQuote": true,
144 | "trailingComma": "none"
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/scripts/docs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | BRANCH='master'
6 | BUILD_DIR='build/'
7 | REPOSITORY=`mktemp -d /tmp/availity-reactstrap-validation.XXXXXX`
8 | GITHUB_REPO='availity/availity-reactstrap-validation.github.io'
9 |
10 | success() {
11 | echo -e "\033[32;1m$1"
12 | }
13 |
14 | error() {
15 | echo -e "\033[31;1m$1"
16 | }
17 |
18 | if [ "$TRAVIS_BRANCH" != "master" ]; then
19 | success "Not building master branch. Skipping deploy."
20 | exit 0
21 | fi
22 |
23 | if [ -z "$GITHUB_TOKEN" ]; then
24 | error "Environment variable GITHUB_TOKEN does not exist. Stopping deploy."
25 | exit 1
26 | fi
27 |
28 | npm run build-docs
29 |
30 | if [ ! -d $BUILD_DIR ]; then
31 | error "Build directory does not exist. Stopping deploy."
32 | exit 1
33 | fi
34 |
35 | echo "Cloning ${GITHUB_REPO} & applying changes"
36 | git clone --branch $BRANCH https://${GITHUB_TOKEN}@github.com/${GITHUB_REPO}.git $REPOSITORY
37 | rsync -rt --del --exclude=".git" $BUILD_DIR $REPOSITORY
38 | cd $REPOSITORY
39 |
40 | if [ -z "$(git status --porcelain)" ]; then
41 | success "No documentation changes to publish. Skipping deploy."
42 | exit 0
43 | fi
44 |
45 | git config user.name "Travis-CI"
46 | git config user.email "availity@github.io"
47 | git add --all
48 | git commit -m "docs(travis): publish documentation for $TRAVIS_COMMIT"
49 | git push origin $BRANCH > /dev/null 2>&1
50 |
51 | success "Successfully published documentation for $TRAVIS_COMMIT!"
52 |
--------------------------------------------------------------------------------
/scripts/publish:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | VERSION=$(node -p -e "require('./package.json').version")
6 | CURRENT_BRANCH="$(git symbolic-ref --short -q HEAD)"
7 |
8 | success() {
9 | echo -e "\033[32;1m$1"
10 | }
11 |
12 | error() {
13 | echo -e "\033[31;1m$1"
14 | }
15 |
16 | if [ -z "$CURRENT_BRANCH" ]; then
17 | error "Not in a branch. Stopping deploy."
18 | exit 1
19 | fi
20 |
21 | if [ -z "$VERSION" ]; then
22 | error "Unable to get current npm version of this package"
23 | exit 1
24 | fi
25 |
26 | git checkout master
27 | git pull
28 | npm run build
29 | git tag -a $VERSION -m "release $VERSION"
30 | git push --set-upstream origin $CURRENT_BRANCH
31 | git push --tags
32 |
33 | npm publish
34 |
35 | success "published $VERSION to npm"
36 |
--------------------------------------------------------------------------------
/scripts/release:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | BUMP_TYPE=$1
6 |
7 | git checkout master
8 | git pull
9 |
10 | CURRENT_BRANCH="$(git symbolic-ref --short -q HEAD)"
11 |
12 | success() {
13 | echo -e "\033[32;1m$1"
14 | }
15 |
16 | error() {
17 | echo -e "\033[31;1m$1"
18 | }
19 |
20 | if [ -z "$CURRENT_BRANCH" ]; then
21 | error "Not in a branch. Stopping release."
22 | exit 1
23 | fi
24 |
25 | if [ -z "$BUMP_TYPE" ]; then
26 | echo "Grabbing recommended bump type..."
27 | BUMP_TYPE="$(node_modules/.bin/conventional-recommended-bump -p angular)"
28 | fi
29 |
30 | if [ -z "$BUMP_TYPE" ]; then
31 | error "Unable to set the type of version bump"
32 | exit 1
33 | fi
34 |
35 | echo "==> Bumping version"
36 | VERSION="$(npm version --no-git-tag-version $BUMP_TYPE | sed 's/v//g')"
37 |
38 | echo "==> Updating Changelog"
39 | node_modules/.bin/conventional-changelog -i CHANGELOG.md -o CHANGELOG.md -p angular
40 |
41 | echo "==> Cleaning Build directory"
42 | rm -rf ./dist
43 |
44 | echo "==> Creating build files"
45 | npm run build
46 |
47 | echo "==> Committing changes"
48 |
49 | git checkout -b "release-$VERSION"
50 | git add --all
51 | git commit --message "chore(release): adding $VERSION"
52 | git push --set-upstream origin "release-$VERSION"
53 |
54 | success "release-$VERSION branch has been pushed"
55 |
--------------------------------------------------------------------------------
/src/AvCheckbox.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import find from 'lodash/find';
5 | import {Input, FormGroup, Label, CustomInput} from 'reactstrap';
6 | import AvInput from './AvInput';
7 |
8 | const checkPropTypes = Object.assign({}, AvInput.propTypes, {customInput: PropTypes.bool});
9 | delete checkPropTypes.name;
10 |
11 | export default class AvCheckbox extends Component {
12 |
13 | static contextTypes = Object.assign({}, AvInput.contextTypes, {
14 | Group: PropTypes.object.isRequired,
15 | });
16 |
17 | static propTypes = checkPropTypes;
18 |
19 | componentDidMount() {
20 | this.context.FormCtrl && this.context.FormCtrl.register(this);
21 | }
22 |
23 | componentWillUnmount() {
24 | this.context.FormCtrl && this.context.FormCtrl.unregister(this);
25 | }
26 |
27 | onChangeHandler = (event, ...args) => {
28 | this.context.Group.update(event, this.props.value);
29 | if (this.props.onChange) {
30 | this.props.onChange(event, ...args);
31 | }
32 | };
33 |
34 | isDefaultChecked(valueArr) {
35 | return Array.isArray(valueArr) && valueArr.length > 0 && find(valueArr, item => item === this.props.value);
36 | }
37 |
38 | render() {
39 | const {
40 | className,
41 | id,
42 | customInput,
43 | ...attributes} = this.props;
44 |
45 | const groupProps = this.context.Group.getProps();
46 |
47 | const touched = this.context.FormCtrl.isTouched(groupProps.name);
48 | const hasError = this.context.FormCtrl.hasError(groupProps.name);
49 |
50 | const classes = classNames(
51 | className,
52 | touched ? 'is-touched' : 'is-untouched',
53 | this.context.FormCtrl.isDirty(groupProps.name) ? 'is-dirty' : 'is-pristine',
54 | this.context.FormCtrl.isBad(groupProps.name) ? 'is-bad-input' : null,
55 | hasError ? 'av-invalid' : 'av-valid',
56 | touched && hasError && 'is-invalid',
57 | );
58 |
59 | if (this.props.disabled === undefined && this.context.FormCtrl.isDisabled() !== undefined) {
60 | attributes.disabled = this.context.FormCtrl.isDisabled();
61 | }
62 |
63 | if (this.props.readOnly === undefined && this.context.FormCtrl.isReadOnly() !== undefined) {
64 | attributes.disabled = attributes.disabled || this.context.FormCtrl.isReadOnly();
65 | }
66 |
67 | if (customInput) {
68 | return (
69 |
82 | );
83 | }
84 |
85 | return (
86 |
87 |
98 | {this.props.label}
99 |
100 | );
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/AvCheckboxGroup.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { findDOMNode } from 'react-dom';
3 | import PropTypes from 'prop-types';
4 | import isEqual from 'lodash/isEqual';
5 | import isUndefined from 'lodash/isUndefined';
6 | import { FormGroup } from 'reactstrap';
7 | import classNames from 'classnames';
8 | import AvFeedback from './AvFeedback';
9 |
10 | const htmlValidationAttrs = ['required'];
11 |
12 | const noop = () => {};
13 |
14 | export default class AvCheckboxGroup extends Component {
15 | static propTypes = Object.assign({}, FormGroup.propTypes, {
16 | name: PropTypes.string.isRequired,
17 | });
18 |
19 | static contextTypes = {
20 | FormCtrl: PropTypes.object.isRequired,
21 | };
22 |
23 | static childContextTypes = {
24 | Group: PropTypes.object.isRequired,
25 | FormCtrl: PropTypes.object.isRequired,
26 | };
27 |
28 | state = {
29 | invalidInputs: {},
30 | dirtyInputs: {},
31 | touchedInputs: {},
32 | badInputs: {},
33 | validate: {},
34 | value: [],
35 | };
36 |
37 | getChildContext() {
38 | if (!this.FormCtrl) {
39 | this.FormCtrl = { ...this.context.FormCtrl };
40 | this.FormCtrl.register = this.registerInput.bind(this);
41 | this.FormCtrl.unregister = this.unregisterInput.bind(this);
42 | this.FormCtrl.validate = noop;
43 | }
44 |
45 | const updateGroup = async(e, value) => {
46 | if (e.target.checked) {
47 | this.value.push(value);
48 | } else {
49 | this.value = this.value.filter(item => item !== value);
50 | }
51 |
52 | this.setState({value: this.value});
53 |
54 | await this.validate();
55 | !this.context.FormCtrl.isTouched(this.props.name) &&
56 | this.context.FormCtrl.setTouched(this.props.name);
57 | !this.context.FormCtrl.isDirty(this.props.name) &&
58 | this.context.FormCtrl.setDirty(this.props.name);
59 | this.props.onChange && this.props.onChange(e, this.value);
60 | };
61 |
62 | return {
63 | Group: {
64 | getProps: () => ({
65 | name: this.props.name,
66 | inline: this.props.inline,
67 | required:
68 | this.props.required ||
69 | !!(this.validations.required && this.validations.required.value),
70 | value: this.value,
71 | }),
72 | update: updateGroup,
73 | getValue: () => this.value,
74 | getInputState: this.getInputState.bind(this),
75 | },
76 | FormCtrl: this.FormCtrl,
77 | };
78 | }
79 |
80 | componentWillMount() {
81 | this.value = this.props.value || this.getDefaultValue().value;
82 | this.setState({ value: this.value });
83 | this.updateValidations();
84 | }
85 |
86 | componentWillReceiveProps(nextProps) {
87 | if (nextProps.name !== this.props.name) {
88 | this.context.FormCtrl.unregister(this);
89 | }
90 | if (nextProps.value !== this.props.value) {
91 | this.value = nextProps.value;
92 | this.setState({ value: nextProps.value });
93 | }
94 | if (!isEqual(nextProps, this.props)) {
95 | this.updateValidations(nextProps);
96 | }
97 | }
98 |
99 | componentWillUnmount() {
100 | this.context.FormCtrl.unregister(this);
101 | }
102 |
103 | getValue() {
104 | return this.value;
105 | }
106 |
107 | getInputState() {
108 | return this.context.FormCtrl.getInputState(this.props.name);
109 | }
110 |
111 | getDefaultValue() {
112 | const key = 'defaultValue';
113 |
114 | let value = [];
115 | if (!isUndefined(this.props[key])) {
116 | value = this.props[key];
117 | } else if (!isUndefined(this.context.FormCtrl.getDefaultValue(this.props.name))) {
118 | value = this.context.FormCtrl.getDefaultValue(this.props.name);
119 | }
120 |
121 | return { key, value };
122 | }
123 |
124 | async validate() {
125 | await this.context.FormCtrl.validate(this.props.name);
126 | this.updateInputs();
127 | }
128 |
129 | update() {
130 | this.setState({});
131 | this.updateInputs();
132 | }
133 |
134 | _inputs = [];
135 |
136 | value = [];
137 |
138 | updateValidations(props = this.props) {
139 | this.validations = Object.assign({}, props.validate);
140 |
141 | Object.keys(props)
142 | .filter(val => htmlValidationAttrs.indexOf(val) > -1)
143 | .forEach(attr => {
144 | if (props[attr]) {
145 | this.validations[attr] = this.validations[attr] || {
146 | value: props[attr],
147 | };
148 | } else {
149 | delete this.validations[attr];
150 | }
151 | });
152 |
153 | this.context.FormCtrl.register(this, this.update.bind(this));
154 | this.validate();
155 | }
156 |
157 | updateInputs() {
158 | this._inputs.forEach(input =>
159 | findDOMNode(input).firstChild.setCustomValidity('Invalid.') &&
160 | input.setState.call(input, {})
161 | );
162 |
163 | this.setState({});
164 | }
165 |
166 | reset() {
167 | this.value = this.getDefaultValue().value;
168 | this.context.FormCtrl.setDirty(this.props.name, false);
169 | this.context.FormCtrl.setTouched(this.props.name, false);
170 | this.context.FormCtrl.setBad(this.props.name, false);
171 | this.setState({ value: this.value });
172 | this.validate();
173 | this.props.onReset && this.props.onReset(this.value);
174 | }
175 |
176 | registerInput(input) {
177 | if (this._inputs.indexOf(input) < 0) {
178 | this._inputs.push(input);
179 | }
180 | }
181 |
182 | unregisterInput(input) {
183 | this._inputs = this._inputs.filter(ipt => {
184 | return ipt !== input;
185 | });
186 | }
187 |
188 | render() {
189 | const legend = this.props.label ? {this.props.label} : '';
190 | const validation = this.getInputState();
191 | const {
192 | errorMessage: omit1,
193 | validate: omit2,
194 | validationEvent: omit3,
195 | state: omit4,
196 | label: omit5,
197 | required: omit6,
198 | inline: omit7,
199 | children,
200 | ...attributes
201 | } = this.props;
202 |
203 | const touched = this.context.FormCtrl.isTouched(this.props.name);
204 | const hasError = this.context.FormCtrl.hasError(this.props.name);
205 |
206 | const classes = classNames(
207 | 'form-control border-0 p-0 h-auto',
208 | touched ? 'is-touched' : 'is-untouched',
209 | this.context.FormCtrl.isDirty(this.props.name)
210 | ? 'is-dirty'
211 | : 'is-pristine',
212 | this.context.FormCtrl.isBad(this.props.name) ? 'is-bad-input' : null,
213 | hasError ? 'av-invalid' : 'av-valid',
214 | touched && hasError && 'is-invalid'
215 | );
216 |
217 | const groupClass = classNames(
218 | attributes.className,
219 | touched && hasError && 'was-validated'
220 | );
221 |
222 | return (
223 |
224 | {legend}
225 | {children}
226 | {validation.errorMessage}
227 |
228 | );
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/src/AvFeedback.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import { FormFeedback } from 'reactstrap';
4 |
5 | export default class AvFeedback extends Component {
6 | static propTypes = Object.assign({}, FormFeedback.propTypes);
7 |
8 | static contextTypes = {
9 | FormCtrl: PropTypes.object.isRequired,
10 | Group: PropTypes.object.isRequired,
11 | };
12 |
13 | render() {
14 | const validation = this.context.Group.getInputState();
15 | return ;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/AvField.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import AvInput from './AvInput';
4 | import AvGroup from './AvGroup';
5 | import AvFeedback from './AvFeedback';
6 | import {Col, FormText, Label, CustomInput} from 'reactstrap';
7 |
8 | const colSizes = ['xs', 'sm', 'md', 'lg', 'xl'];
9 |
10 | export default class AvField extends Component {
11 | static propTypes = Object.assign({}, AvInput.propTypes, {
12 | label: PropTypes.node,
13 | labelHidden: PropTypes.bool,
14 | disabled: PropTypes.bool,
15 | readOnly: PropTypes.bool,
16 | id: PropTypes.string,
17 | inputClass: PropTypes.string,
18 | labelClass: PropTypes.string,
19 | helpMessage: PropTypes.oneOfType([
20 | PropTypes.string,
21 | PropTypes.object,
22 | ]),
23 | errorMessage: PropTypes.oneOfType([
24 | PropTypes.string,
25 | PropTypes.object,
26 | ]),
27 | labelAttrs: PropTypes.object,
28 | groupAttrs: PropTypes.object,
29 | grid: PropTypes.object,
30 | });
31 |
32 | static contextTypes = {
33 | FormCtrl: PropTypes.object.isRequired,
34 | };
35 |
36 | static childContextTypes = {
37 | FormCtrl: PropTypes.object.isRequired,
38 | };
39 |
40 | getChildContext() {
41 | this.FormCtrl = {...this.context.FormCtrl};
42 | const registerValidator = this.FormCtrl.register;
43 | this.FormCtrl.register = (input, updater = input && input.setState && input.setState.bind(input)) => {
44 | registerValidator(input, () => {
45 | this.setState({});
46 | if (updater) updater({});
47 | });
48 | };
49 | return {
50 | FormCtrl: this.FormCtrl,
51 | };
52 | }
53 |
54 | render() {
55 | let row = false;
56 | const col = {};
57 | const labelCol = {};
58 | const {
59 | helpMessage,
60 | label,
61 | labelHidden,
62 | inputClass,
63 | labelClass,
64 | children,
65 | id = this.props.name,
66 | size,
67 | disabled,
68 | readOnly,
69 | grid,
70 | labelAttrs,
71 | groupAttrs,
72 | ...attributes
73 | } = this.props;
74 |
75 | if (grid) {
76 | colSizes.forEach(colSize => {
77 | if (grid[colSize]) {
78 | row = true;
79 | const sizeNum = parseInt(grid[colSize], 10);
80 | col[colSize] = sizeNum;
81 | labelCol[colSize] = 12 - sizeNum;
82 | }
83 | });
84 | }
85 |
86 | const input = (
94 | {children}
95 | );
96 |
97 | const validation = this.context.FormCtrl.getInputState(this.props.name);
98 |
99 | const feedback = validation.errorMessage ? ({validation.errorMessage} ) : null;
100 | const help = helpMessage ? ({helpMessage} ) : null;
101 | const inputRow = row ? {input}{feedback}{help} : input;
102 | const check = attributes.type === 'checkbox';
103 |
104 | if (
105 | (check || attributes.type === 'radio' || attributes.type === 'switch') &&
106 | (attributes.tag === CustomInput ||
107 | (Array.isArray(attributes.tag) && attributes.tag[0] === CustomInput))
108 | ) {
109 | return {feedback}{help} ;
110 | }
111 |
112 | return (
113 |
114 | {check && inputRow}
115 | {label &&
123 | {label}
124 | }
125 | {!check && inputRow}
126 | {!row && feedback}
127 | {!row && help}
128 |
129 | );
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/AvGroup.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { FormGroup } from 'reactstrap';
5 |
6 | export default class AvGroup extends Component {
7 | static propTypes = Object.assign({}, FormGroup.propTypes);
8 |
9 | static contextTypes = {
10 | FormCtrl: PropTypes.object.isRequired,
11 | };
12 |
13 | static childContextTypes = {
14 | Group: PropTypes.object.isRequired,
15 | FormCtrl: PropTypes.object.isRequired,
16 | };
17 |
18 | constructor(props) {
19 | super(props);
20 |
21 | this.state = {input: {props: {}}};
22 | }
23 |
24 | getChildContext() {
25 | this.FormCtrl = {...this.context.FormCtrl};
26 | const registerValidator = this.FormCtrl.register;
27 | this.FormCtrl.register = (input) => {
28 | this.setState({input});
29 | registerValidator(input, this.update.bind(this, input));
30 | };
31 | return {
32 | Group: {
33 | getInput: () => this.state.input,
34 | getInputState: ::this.getInputState,
35 | },
36 | FormCtrl: this.FormCtrl,
37 | };
38 | }
39 |
40 | getInputState() {
41 | return this.context.FormCtrl.getInputState(this.state.input.props.name);
42 | }
43 |
44 | update(input){
45 | if (input && input.setState) input.setState.call(input, {});
46 | this.setState({});
47 | }
48 |
49 | render() {
50 | const validation = this.getInputState();
51 | const classname = classNames(this.props.className, validation.color && `text-${validation.color}`);
52 | return (
53 |
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/AvInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import {Input} from 'reactstrap';
4 | import AvBaseInput from './AvBaseInput';
5 |
6 |
7 | export default class AvInput extends AvBaseInput {
8 | static defaultProps = Object.assign({}, AvBaseInput.defaultProps, {
9 | tag: Input,
10 | });
11 |
12 | static contextTypes = AvBaseInput.contextTypes;
13 |
14 | getValue() {
15 | return this.props.valueParser ? this.props.valueParser(this.value) : this.value;
16 | }
17 |
18 | getViewValue() {
19 | return this.props.valueFormatter ? this.props.valueFormatter(this.value) : this.value;
20 | }
21 |
22 | render() {
23 | const {
24 | errorMessage: omit1,
25 | validate: omit2,
26 | validationEvent: omit3,
27 | state: omit4,
28 | trueValue: omit5,
29 | falseValue: omit6,
30 | valueParser: omit7,
31 | valueFormatter: omit8,
32 | className,
33 | tag,
34 | getRef,
35 | id = this.props.name,
36 | ...attributes
37 | } = this.props;
38 |
39 | const touched = this.context.FormCtrl.isTouched(this.props.name);
40 | const hasError = this.context.FormCtrl.hasError(this.props.name);
41 | let Tag = tag;
42 |
43 | if (Array.isArray(tag)) {
44 | let tags;
45 | // eslint-disable-next-line prefer-const
46 | [Tag, ...tags] = tag;
47 | attributes.tag = tags;
48 | if (attributes.tag.length <= 1) {
49 | attributes.tag = attributes.tag[0];
50 | }
51 | }
52 |
53 | const classes = classNames(
54 | className,
55 | touched ? 'is-touched' : 'is-untouched',
56 | this.context.FormCtrl.isDirty(this.props.name) ? 'is-dirty' : 'is-pristine',
57 | this.context.FormCtrl.isBad(this.props.name) ? 'is-bad-input' : null,
58 | hasError ? 'av-invalid' : 'av-valid',
59 | touched && hasError && 'is-invalid',
60 | attributes.type === 'checkbox' && touched && hasError && 'was-validated'
61 | );
62 |
63 | const value = this.getViewValue();
64 |
65 | return (
66 |
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/AvInputContainer.js:
--------------------------------------------------------------------------------
1 | import {Component} from 'react';
2 |
3 | function validComponent(input) {
4 | const name = input && input.props ? input.props.name : undefined;
5 |
6 | if (!name) {
7 | throw new Error(`Input ${input} has no "name" prop`);
8 | }
9 |
10 | return {name};
11 | }
12 |
13 | export default class InputContainer extends Component {
14 | componentWillMount() {
15 | this._updaters = {};
16 | this._inputs = {};
17 | }
18 |
19 | getOldInputName(input) {
20 | for (const key in this._inputs) {
21 | if (this._inputs[key] === input) {
22 | return key;
23 | }
24 | }
25 | }
26 |
27 | registerInput(input, updater = input && input.setState && input.setState.bind(input)) {
28 | const {name} = validComponent(input, updater);
29 | const oldName = this.getOldInputName(input);
30 | if (oldName !== name) {
31 | if (oldName) {
32 | this.unregisterInput({props: {name: oldName}});
33 | }
34 | this._updaters[name] = updater;
35 | this._inputs[name] = input;
36 | }
37 | }
38 |
39 | unregisterInput(input) {
40 | const {name} = validComponent(input);
41 | delete this._updaters[name];
42 | delete this._inputs[name];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/AvRadio.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import {Input, FormGroup, Label, CustomInput} from 'reactstrap';
5 | import AvInput from './AvInput';
6 |
7 | const radioPropTypes = Object.assign({}, AvInput.propTypes, {customInput: PropTypes.bool});
8 | delete radioPropTypes.name;
9 |
10 | export default class AvRadio extends Component {
11 |
12 | static contextTypes = Object.assign({}, AvInput.contextTypes, {
13 | Group: PropTypes.object.isRequired,
14 | });
15 |
16 | static propTypes = radioPropTypes;
17 |
18 | componentDidMount() {
19 | this.context.FormCtrl && this.context.FormCtrl.register(this);
20 | }
21 |
22 | componentWillUnmount() {
23 | this.context.FormCtrl && this.context.FormCtrl.unregister(this);
24 | }
25 |
26 | onChangeHandler = (event, ...args) => {
27 | this.context.Group.update(event, this.props.value);
28 | if (this.props.onChange) {
29 | this.props.onChange(event, ...args);
30 | }
31 | };
32 |
33 | render() {
34 | const {
35 | className,
36 | id,
37 | customInput,
38 | ...attributes} = this.props;
39 |
40 | const groupProps = this.context.Group.getProps();
41 |
42 | const touched = this.context.FormCtrl.isTouched(groupProps.name);
43 | const hasError = this.context.FormCtrl.hasError(groupProps.name);
44 |
45 | const classes = classNames(
46 | className,
47 | touched ? 'is-touched' : 'is-untouched',
48 | this.context.FormCtrl.isDirty(groupProps.name) ? 'is-dirty' : 'is-pristine',
49 | this.context.FormCtrl.isBad(groupProps.name) ? 'is-bad-input' : null,
50 | hasError ? 'av-invalid' : 'av-valid',
51 | touched && hasError && 'is-invalid'
52 | );
53 |
54 | if (this.props.disabled === undefined && this.context.FormCtrl.isDisabled() !== undefined) {
55 | attributes.disabled = this.context.FormCtrl.isDisabled();
56 | }
57 |
58 | if (this.props.readOnly === undefined && this.context.FormCtrl.isReadOnly() !== undefined) {
59 | attributes.disabled = attributes.disabled || this.context.FormCtrl.isReadOnly();
60 | }
61 |
62 | if (customInput) {
63 | return (
64 |
76 | );
77 | }
78 |
79 | return (
80 |
81 |
92 | {this.props.label}
93 |
94 | );
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/AvRadioGroup.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import isEqual from 'lodash/isEqual';
4 | import isUndefined from 'lodash/isUndefined';
5 | import { FormGroup } from 'reactstrap';
6 | import classNames from 'classnames';
7 | import AvFeedback from './AvFeedback';
8 |
9 | const htmlValidationAttrs = ['required'];
10 |
11 | const noop = () => {};
12 |
13 | export default class AvRadioGroup extends Component {
14 | static propTypes = Object.assign({}, FormGroup.propTypes, {
15 | name: PropTypes.string.isRequired,
16 | });
17 |
18 | static contextTypes = {
19 | FormCtrl: PropTypes.object.isRequired,
20 | };
21 |
22 | static childContextTypes = {
23 | Group: PropTypes.object.isRequired,
24 | FormCtrl: PropTypes.object.isRequired,
25 | };
26 |
27 | state = {
28 | invalidInputs: {},
29 | dirtyInputs: {},
30 | touchedInputs: {},
31 | badInputs: {},
32 | validate: {},
33 | value: '',
34 | };
35 |
36 | getChildContext() {
37 | if (!this.FormCtrl) {
38 | this.FormCtrl = { ...this.context.FormCtrl };
39 | this.FormCtrl.register = this.registerInput.bind(this);
40 | this.FormCtrl.unregister = this.unregisterInput.bind(this);
41 | this.FormCtrl.validate = noop;
42 | }
43 |
44 | const updateGroup = async(e, value) => {
45 | this.setState({ value });
46 | this.value = value;
47 | await this.validate();
48 | !this.context.FormCtrl.isTouched(this.props.name) &&
49 | this.context.FormCtrl.setTouched(this.props.name);
50 | !this.context.FormCtrl.isDirty(this.props.name) &&
51 | this.context.FormCtrl.setDirty(this.props.name);
52 | this.props.onChange && this.props.onChange(e, value);
53 | };
54 |
55 | return {
56 | Group: {
57 | getProps: () => ({
58 | name: this.props.name,
59 | inline: this.props.inline,
60 | required:
61 | this.props.required ||
62 | !!(this.validations.required && this.validations.required.value),
63 | value: this.value,
64 | }),
65 | update: updateGroup,
66 | getValue: () => this.value,
67 | getInputState: this.getInputState.bind(this),
68 | },
69 | FormCtrl: this.FormCtrl,
70 | };
71 | }
72 |
73 | componentWillMount() {
74 | this.value = this.props.value || this.getDefaultValue().value;
75 | this.setState({ value: this.value });
76 | this.updateValidations();
77 | }
78 |
79 | componentWillReceiveProps(nextProps) {
80 | if (nextProps.name !== this.props.name) {
81 | this.context.FormCtrl.unregister(this);
82 | }
83 | if (nextProps.value !== this.props.value) {
84 | this.value = nextProps.value;
85 | this.setState({ value: nextProps.value });
86 | }
87 | if (!isEqual(nextProps, this.props)) {
88 | this.updateValidations(nextProps);
89 | }
90 | }
91 |
92 | componentWillUnmount() {
93 | this.context.FormCtrl.unregister(this);
94 | }
95 |
96 | getValue() {
97 | return this.value;
98 | }
99 |
100 | getInputState() {
101 | return this.context.FormCtrl.getInputState(this.props.name);
102 | }
103 |
104 | getDefaultValue() {
105 | const key = 'defaultValue';
106 |
107 | let value = '';
108 | if (!isUndefined(this.props[key])) {
109 | value = this.props[key];
110 | } else if (!isUndefined(this.context.FormCtrl.getDefaultValue(this.props.name))) {
111 | value = this.context.FormCtrl.getDefaultValue(this.props.name);
112 | }
113 |
114 | return { key, value };
115 | }
116 |
117 | async validate() {
118 | await this.context.FormCtrl.validate(this.props.name);
119 | this.updateInputs();
120 | }
121 |
122 | update() {
123 | this.setState({});
124 | this.updateInputs();
125 | }
126 |
127 | _inputs = [];
128 |
129 | value = '';
130 |
131 | updateValidations(props = this.props) {
132 | this.validations = Object.assign({}, props.validate);
133 |
134 | Object.keys(props)
135 | .filter(val => htmlValidationAttrs.indexOf(val) > -1)
136 | .forEach(attr => {
137 | if (props[attr]) {
138 | this.validations[attr] = this.validations[attr] || {
139 | value: props[attr],
140 | };
141 | } else {
142 | delete this.validations[attr];
143 | }
144 | });
145 |
146 | this.context.FormCtrl.register(this, this.update.bind(this));
147 | this.validate();
148 | }
149 |
150 | updateInputs() {
151 | this._inputs.forEach(input => input.setState.call(input, {}));
152 | this.setState({});
153 | }
154 |
155 | reset() {
156 | this.value = this.getDefaultValue().value;
157 | this.context.FormCtrl.setDirty(this.props.name, false);
158 | this.context.FormCtrl.setTouched(this.props.name, false);
159 | this.context.FormCtrl.setBad(this.props.name, false);
160 | this.setState({ value: this.value });
161 | this.validate();
162 | this.props.onReset && this.props.onReset(this.value);
163 | }
164 |
165 | registerInput(input) {
166 | if (this._inputs.indexOf(input) < 0) {
167 | this._inputs.push(input);
168 | }
169 | }
170 |
171 | unregisterInput(input) {
172 | this._inputs = this._inputs.filter(ipt => {
173 | return ipt !== input;
174 | });
175 | }
176 |
177 | render() {
178 | const legend = this.props.label ? {this.props.label} : '';
179 | const validation = this.getInputState();
180 | const {
181 | errorMessage: omit1,
182 | validate: omit2,
183 | validationEvent: omit3,
184 | state: omit4,
185 | label: omit5,
186 | required: omit6,
187 | inline: omit7,
188 | children,
189 | ...attributes
190 | } = this.props;
191 |
192 | const touched = this.context.FormCtrl.isTouched(this.props.name);
193 | const hasError = this.context.FormCtrl.hasError(this.props.name);
194 |
195 | const classes = classNames(
196 | 'form-control border-0 p-0 h-auto',
197 | touched ? 'is-touched' : 'is-untouched',
198 | this.context.FormCtrl.isDirty(this.props.name)
199 | ? 'is-dirty'
200 | : 'is-pristine',
201 | this.context.FormCtrl.isBad(this.props.name) ? 'is-bad-input' : null,
202 | hasError ? 'av-invalid' : 'av-valid',
203 | touched && hasError && 'is-invalid'
204 | );
205 |
206 | const groupClass = classNames(
207 | attributes.className,
208 | touched && hasError && 'was-validated'
209 | );
210 |
211 | return (
212 |
213 | {legend}
214 | {children}
215 | {validation.errorMessage}
216 |
217 | );
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/AvValidator/date.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import { isEmpty, isoDateFormat } from './utils';
3 |
4 | export default function validate(value, context, {format = 'MM/DD/YYYY', errorMessage = `Format needs to be ${format}`} = {}) {
5 | if (isEmpty(value)) return true;
6 |
7 | const date = moment(value, [isoDateFormat, format], true);
8 |
9 | return date.isValid() || errorMessage;
10 | }
11 |
--------------------------------------------------------------------------------
/src/AvValidator/dateRange.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import { isEmpty, isoDateFormat } from './utils';
3 |
4 | function setMin(value) {
5 | value.set('hours', 0);
6 | value.set('minutes', 0);
7 | value.set('seconds', 0);
8 |
9 | return value;
10 | }
11 |
12 | function setMax(value) {
13 | value.set('hours', 23);
14 | value.set('minutes', 59);
15 | value.set('seconds', 59);
16 |
17 | return value;
18 | }
19 |
20 |
21 | function getStartDate(start) {
22 | return setMin(moment().add(start.value, start.units));
23 | }
24 |
25 | function getEndDate(end) {
26 | return setMax(moment().add(end.value, end.units));
27 | }
28 |
29 | export default function validate(value, context, { format = 'MM/DD/YYYY', displayFormat = 'MM/DD/YYYY', start = {}, end = {}, errorMessage } = {}) {
30 | if (isEmpty(value)) return true;
31 |
32 | let startDate;
33 | let endDate;
34 |
35 | const date = moment(value, [isoDateFormat, format], true);
36 | setMin(date);
37 |
38 | if (!isEmpty(start.units) && !isEmpty(end.units)) {
39 | startDate = getStartDate(start);
40 | endDate = getEndDate(end);
41 | } else {
42 | startDate = moment(start.value, start.format || format);
43 | endDate = setMax(moment(end.value, end.format || format));
44 | }
45 | errorMessage = errorMessage || `Date must be between ${startDate.format(displayFormat)} and ${endDate.format(displayFormat)}`;
46 | return (date.isValid() &&
47 | (date.isBetween(startDate, endDate, 'day') || date.isSame(startDate, 'day') || date.isSame(endDate, 'day'))) ||
48 | errorMessage;
49 | }
50 |
--------------------------------------------------------------------------------
/src/AvValidator/email.js:
--------------------------------------------------------------------------------
1 | import patternValidation from './pattern';
2 |
3 | const EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
4 |
5 | export default function validate(value, context, { pattern = EMAIL_REGEXP, errorMessage = false } = {}) {
6 | return patternValidation(value, context, { value: pattern, errorMessage });
7 | }
8 |
--------------------------------------------------------------------------------
/src/AvValidator/index.js:
--------------------------------------------------------------------------------
1 | import date from './date';
2 | import dateRange from './dateRange';
3 | import email from './email';
4 | import match from './match';
5 | import max from './max';
6 | import maxlength from './maxlength';
7 | import maxchecked from './maxchecked';
8 | import min from './min';
9 | import minlength from './minlength';
10 | import minchecked from './minchecked';
11 | import number from './number';
12 | import npi from './npi';
13 | import pattern from './pattern';
14 | import phone from './phone';
15 | import required from './required';
16 | import step from './step';
17 | import url from './url';
18 |
19 | export default {
20 | date,
21 | datetime: date,
22 | dateRange,
23 | email,
24 | match,
25 | max,
26 | maxlength,
27 | maxLength: maxlength,
28 | maxChecked: maxchecked,
29 | min,
30 | minlength,
31 | minLength: minlength,
32 | minChecked: minchecked,
33 | number,
34 | npi,
35 | pattern,
36 | phone,
37 | tel: phone,
38 | required,
39 | step,
40 | url,
41 | };
42 |
--------------------------------------------------------------------------------
/src/AvValidator/match.js:
--------------------------------------------------------------------------------
1 | import { isEmpty } from './utils';
2 | import get from 'lodash/get';
3 |
4 | export default function validate(value, context, constraint = {}) {
5 | return isEmpty(value) || value === get(context, constraint.value) || constraint.errorMessage || false;
6 | }
7 |
--------------------------------------------------------------------------------
/src/AvValidator/max.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import toNumber from 'lodash/toNumber';
3 | import { isEmpty, isoDateFormat } from './utils';
4 | import maxchecked from './maxchecked';
5 |
6 | export default function validate(value, context, constraint = {}, input = {}) {
7 | if (Array.isArray(input.value)) {
8 | return maxchecked(value, context, constraint, input);
9 | }
10 |
11 | if (isEmpty(value)) return true;
12 |
13 | if ((input.validations && input.validations.date) ||
14 | (input.props && input.props.type && input.props.type.toLowerCase() === 'date')) {
15 | return moment(value, [isoDateFormat, constraint.format || 'MM/DD/YYYY'], true).isSameOrBefore(constraint.value, 'day') || constraint.errorMessage || false;
16 | }
17 |
18 | const number = toNumber(value);
19 |
20 | return (!isNaN(number) && isFinite(number) && number <= toNumber(constraint.value)) || constraint.errorMessage || false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/AvValidator/maxchecked.js:
--------------------------------------------------------------------------------
1 | import toNumber from 'lodash/toNumber';
2 | import { isEmpty, isDecimal } from './utils';
3 |
4 | export default function validate(value, context, constraint = {}, input = {}) {
5 | if (isEmpty(input.value)) return true;
6 |
7 | const max = toNumber(constraint.value);
8 |
9 | return (
10 | (!isNaN(max) && isFinite(max) && !isDecimal(max) && max >= input.value.length) ||
11 | constraint.errorMessage ||
12 | false
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/AvValidator/maxlength.js:
--------------------------------------------------------------------------------
1 | import toNumber from 'lodash/toNumber';
2 | import { isEmpty } from './utils';
3 |
4 | export default function validate(value, context, constraint = {}) {
5 | if (isEmpty(value)) return true;
6 |
7 | const length = value.length;
8 |
9 | return length <= toNumber(constraint.value) || constraint.errorMessage || false;
10 | }
11 |
--------------------------------------------------------------------------------
/src/AvValidator/min.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import toNumber from 'lodash/toNumber';
3 | import { isEmpty, isoDateFormat } from './utils';
4 | import minchecked from './minchecked';
5 |
6 | export default function validate(value, context, constraint = {}, input = {}) {
7 | if (Array.isArray(input.value)) {
8 | return minchecked(value, context, constraint, input);
9 | }
10 |
11 | if (isEmpty(value)) return true;
12 |
13 | if ((input.validations && input.validations.date) ||
14 | (input.props && input.props.type && input.props.type.toLowerCase() === 'date')) {
15 | return moment(value, [isoDateFormat, constraint.format || 'MM/DD/YYYY'], true).isSameOrAfter(constraint.value, 'day') || constraint.errorMessage || false;
16 | }
17 |
18 | const number = toNumber(value);
19 |
20 | return (!isNaN(number) && isFinite(number) && number >= toNumber(constraint.value)) || constraint.errorMessage || false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/AvValidator/minchecked.js:
--------------------------------------------------------------------------------
1 | import toNumber from 'lodash/toNumber';
2 | import { isEmpty, isDecimal } from './utils';
3 |
4 | export default function validate(value, context, constraint = {}, input = {}) {
5 | if (isEmpty(input.value)) return true;
6 |
7 | const min = toNumber(constraint.value);
8 |
9 | return (
10 | (!isNaN(min) && isFinite(min) && !isDecimal(min) && min <= input.value.length) ||
11 | constraint.errorMessage ||
12 | false
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/AvValidator/minlength.js:
--------------------------------------------------------------------------------
1 | import toNumber from 'lodash/toNumber';
2 | import { isEmpty } from './utils';
3 |
4 | export default function validate(value, context, constraint = {}) {
5 | if (isEmpty(value)) return true;
6 |
7 | const length = value.length;
8 |
9 | return length >= toNumber(constraint.value) || constraint.errorMessage || false;
10 | }
11 |
--------------------------------------------------------------------------------
/src/AvValidator/npi.js:
--------------------------------------------------------------------------------
1 | import { isEmpty } from './utils';
2 |
3 | const INTEGER_REGEX = /^\d*$/;
4 |
5 | export default function validate(value, context, {errorMessage = false} = {}) {
6 | if (isEmpty(value)) return true;
7 |
8 | value = value + '';
9 |
10 | if (!INTEGER_REGEX.test(value) || value.length !== 10) {
11 | return errorMessage;
12 | }
13 |
14 | const firstDigit = value.charAt(0);
15 | if (['1', '2', '3', '4'].indexOf(firstDigit) < 0) {
16 | return errorMessage;
17 | }
18 |
19 | const digit = parseInt(value.charAt(9), 10);
20 | value = value.substring(0, 9);
21 | value = `80840${value}`;
22 |
23 | let alternate = true;
24 | let total = 0;
25 |
26 | for (let i = value.length; i > 0; i--) {
27 | let next = parseInt(value.charAt(i - 1), 10);
28 | if (alternate) {
29 | next = next * 2;
30 | if (next > 9) {
31 | next = (next % 10) + 1;
32 | }
33 | }
34 | total += next;
35 | alternate = !alternate;
36 | }
37 |
38 | const roundUp = Math.ceil(total / 10) * 10;
39 | const calculatedCheck = roundUp - total;
40 |
41 | return calculatedCheck === digit || errorMessage;
42 | }
43 |
--------------------------------------------------------------------------------
/src/AvValidator/number.js:
--------------------------------------------------------------------------------
1 | import isNumber from 'lodash/isNumber';
2 | import toNumber from 'lodash/toNumber';
3 | import { isEmpty } from './utils';
4 |
5 | export default function validate(value, context, {errorMessage = false} = {}) {
6 | if (isEmpty(value)) return true;
7 |
8 | const number = toNumber(value);
9 |
10 | return (isNumber(number) && !isNaN(number)) || errorMessage;
11 | }
12 |
--------------------------------------------------------------------------------
/src/AvValidator/pattern.js:
--------------------------------------------------------------------------------
1 | import isRegExp from 'lodash/isRegExp';
2 | import { isEmpty } from './utils';
3 |
4 | const REGEX = /^\/(.*)\/([gim]*)$/; // regular expression to test a regular expression
5 |
6 | function asRegExp(pattern) {
7 | // if regex then return it
8 | if (isRegExp(pattern)) {
9 | return pattern;
10 | }
11 |
12 | // if string then test for valid regex then convert to regex and return
13 | const match = pattern.match(REGEX);
14 | if (match) {
15 | return new RegExp(match[1], match[2]);
16 | }
17 |
18 | return new RegExp(pattern);
19 | }
20 |
21 | export default function validate(value, context, constraint = {}) {
22 | if (isEmpty(value)) return true;
23 |
24 | const values = Array.isArray(constraint.value) ? constraint.value : [constraint.value];
25 |
26 | return values.some((expression) => asRegExp(expression).test(value)) || constraint.errorMessage || false;
27 | }
28 |
--------------------------------------------------------------------------------
/src/AvValidator/phone.js:
--------------------------------------------------------------------------------
1 | import patternValidation from './pattern';
2 |
3 | const NANP_REGEXP = /^(\+?1[\.\-\s]?)?\(?[2-9]\d{2}[\)\.\-\s]?\s?[2-9]\d{2}[\.\-\s]?\d{4}$/;
4 |
5 | export default function validate(value, context, { pattern = NANP_REGEXP, errorMessage = false } = {}) {
6 | return patternValidation(value, context, { value: pattern, errorMessage });
7 | }
8 |
--------------------------------------------------------------------------------
/src/AvValidator/required.js:
--------------------------------------------------------------------------------
1 | import { isEmpty } from './utils';
2 |
3 | export default function validate(value, context, {value: enabled = true, errorMessage = false} = {}) {
4 | return !enabled || !isEmpty(value) || errorMessage || false;
5 | }
6 |
--------------------------------------------------------------------------------
/src/AvValidator/step.js:
--------------------------------------------------------------------------------
1 | import toNumber from 'lodash/toNumber';
2 | import { isEmpty } from './utils';
3 |
4 | function getDecCount(val) {
5 | const valStr = val.toString();
6 | if (valStr.indexOf('e-') > -1) {
7 | const valArr = valStr.split('e-');
8 | return parseInt((valArr[0].split('.')[1] || '').length, 10) + parseInt(valArr[1], 10);
9 | }
10 | return (valStr.split('.')[1] || '').length;
11 | }
12 |
13 | // http://stackoverflow.com/a/31711034/1873485
14 | function floatSafeRemainder(val, step) {
15 | const valDecCount = getDecCount(val);
16 | const stepDecCount = getDecCount(step);
17 | const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
18 | const valInt = parseInt(val.toFixed(decCount).replace('.', ''), 10);
19 | const stepInt = parseInt(step.toFixed(decCount).replace('.', ''), 10);
20 | return (valInt % stepInt) / Math.pow(10, decCount);
21 | }
22 |
23 | export default function validate(value, context, constraint = {}) {
24 | if (isEmpty(value)) return true;
25 |
26 | return floatSafeRemainder(toNumber(value), toNumber(constraint.value)) === 0 || constraint.errorMessage || false;
27 | }
28 |
--------------------------------------------------------------------------------
/src/AvValidator/url.js:
--------------------------------------------------------------------------------
1 | import patternValidation from './pattern';
2 |
3 | // https://gist.github.com/dperini/729294
4 | const URL_REGEXP = new RegExp(
5 | '^' +
6 | // protocol identifier
7 | '(?:(?:https?|ftps?|sftp)://)' +
8 | // user:pass authentication
9 | '(?:\\S+(?::\\S*)?@)?' +
10 | '(?:' +
11 | // IP address exclusion
12 | // private & local networks
13 | '(?!(?:10|127)(?:\\.\\d{1,3}){3})' +
14 | '(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' +
15 | '(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' +
16 | // IP address dotted notation octets
17 | // excludes loopback network 0.0.0.0
18 | // excludes reserved space >= 224.0.0.0
19 | // excludes network & broacast addresses
20 | // (first & last IP address of each class)
21 | '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' +
22 | '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' +
23 | '(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' +
24 | '|' +
25 | // host name
26 | '(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' +
27 | // domain name
28 | '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' +
29 | // TLD identifier
30 | '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' +
31 | // TLD may end with dot
32 | '\\.?' +
33 | ')' +
34 | // port number
35 | '(?::\\d{2,5})?' +
36 | // resource path
37 | '(?:[/?#]\\S*)?' +
38 | '$', 'i'
39 | );
40 |
41 | export default function validate(value, context, { pattern = URL_REGEXP, errorMessage = false } = {}) {
42 | return patternValidation(value, context, { value: pattern, errorMessage });
43 | }
44 |
--------------------------------------------------------------------------------
/src/AvValidator/utils.js:
--------------------------------------------------------------------------------
1 | import isString from 'lodash/isString';
2 | import isUndefined from 'lodash/isUndefined';
3 | /* global document */
4 | export const isoDateFormat = 'YYYY-MM-DD';
5 |
6 | export function isEmpty(value) {
7 | return isUndefined(value) || value === null || (isString(value) && value.trim() === '') || value === false || (Array.isArray(value) && value.length === 0 );
8 | }
9 |
10 | export function isDecimal(value) {
11 | return value % 1 !== 0;
12 | }
13 |
14 | export const inputType = {date: false, number: false, time: false, month: false, week: false};
15 |
16 | export const inputTypeOverride = (key, value) => {
17 | inputType[key] = value;
18 | };
19 |
20 | /* istanbul ignore next */
21 | if (typeof document !== 'undefined' && typeof document.createElement === 'function') {
22 | const tester = document.createElement('input');
23 |
24 | for (const i in inputType) {
25 | if (inputType.hasOwnProperty(i)) {
26 | tester.setAttribute('type', i);
27 | tester.value = ':(';
28 |
29 | if (tester.type === i && tester.value === '') {
30 | inputType[i] = true;
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import AvCheckbox from './AvCheckbox';
2 | import AvCheckboxGroup from './AvCheckboxGroup';
3 | import AvFeedback from './AvFeedback';
4 | import AvField from './AvField';
5 | import AvForm from './AvForm';
6 | import AvGroup from './AvGroup';
7 | import AvRadioGroup from './AvRadioGroup';
8 | import AvRadio from './AvRadio';
9 | import AvInput from './AvInput';
10 | import AvBaseInput from './AvBaseInput';
11 | import AvInputContainer from './AvInputContainer';
12 | import AvValidator from './AvValidator';
13 |
14 | export {
15 | AvCheckbox,
16 | AvCheckboxGroup,
17 | AvFeedback,
18 | AvField,
19 | AvForm,
20 | AvGroup,
21 | AvRadioGroup,
22 | AvRadio,
23 | AvInput,
24 | AvBaseInput,
25 | AvInputContainer,
26 | AvValidator,
27 | };
28 |
--------------------------------------------------------------------------------
/webpack.base.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | var libraryName = 'AvailityReactstrapValidation';
5 |
6 | module.exports = function(env) {
7 | var outputFile;
8 | var plugins = [
9 | new webpack.NoErrorsPlugin(),
10 | new webpack.DefinePlugin({
11 | 'process.env.NODE_ENV': JSON.stringify(env),
12 | }),
13 | new webpack.optimize.DedupePlugin(),
14 | new webpack.optimize.OccurenceOrderPlugin(),
15 | ];
16 |
17 | if (env === 'production') {
18 | plugins.push(new webpack.optimize.UglifyJsPlugin(
19 | {
20 | minimize: true,
21 | compress: {
22 | warnings: false,
23 | },
24 | mangle: true,
25 | }
26 | ));
27 | outputFile = libraryName.toLowerCase() + '.min.js';
28 | } else {
29 | outputFile = libraryName.toLowerCase() + '.js';
30 | }
31 |
32 | var config = {
33 | devtool: 'source-map',
34 | entry: [__dirname + '/src/index.js'],
35 | output: {
36 | path: __dirname + '/dist',
37 | filename: outputFile,
38 | library: libraryName,
39 | libraryTarget: 'umd',
40 | umdNamedDefine: true,
41 | },
42 | externals: [
43 | {
44 | react: {
45 | root: 'React',
46 | commonjs2: 'react',
47 | commonjs: 'react',
48 | amd: 'react',
49 | },
50 | },
51 | {
52 | 'react-dom': {
53 | root: 'ReactDOM',
54 | commonjs2: 'react-dom',
55 | commonjs: 'react-dom',
56 | amd: 'react-dom',
57 | },
58 | },
59 | {
60 | 'react-addons-transition-group': {
61 | commonjs: 'react-addons-transition-group',
62 | commonjs2: 'react-addons-transition-group',
63 | amd: 'react-addons-transition-group',
64 | root: ['React', 'addons', 'TransitionGroup'],
65 | },
66 | },
67 | ],
68 | module: {
69 | loaders: [
70 | {
71 | test: /\.(json)$/,
72 | loaders: [
73 | 'json-loader?cacheDirectory',
74 | ],
75 | },
76 | {
77 | test: /\.(js|jsx)$/,
78 | exclude: /node_modules/,
79 | loaders: [
80 | 'babel-loader?cacheDirectory',
81 | ],
82 | },
83 | ],
84 | },
85 | resolve: {
86 | alias: {
87 | 'avility-reactstrap-validation': 'src/index',
88 | },
89 | extensions: ['', '.js', '.jsx', '.json'],
90 | root: [
91 | path.resolve('./src'),
92 | ],
93 | },
94 | plugins: plugins,
95 | };
96 |
97 | return config;
98 | };
99 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var env = process.env.WEBPACK_BUILD || 'development';
2 | var webpackConfig = require('./webpack.base.config')(env);
3 |
4 | module.exports = webpackConfig;
5 |
--------------------------------------------------------------------------------
/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin');
4 | var env = process.env.WEBPACK_BUILD || 'development';
5 |
6 | var CleanWebpackPlugin = require('clean-webpack-plugin');
7 | var CopyWebpackPlugin = require('copy-webpack-plugin');
8 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
9 | var webpackDevConfig = require('./webpack.base.config')('development');
10 | var webpackProdConfig = require('./webpack.base.config')('production');
11 |
12 | var paths = [
13 | '/',
14 | '/components/',
15 | '/components/validators/',
16 | '/components/checkboxes/',
17 | '/components/avform/',
18 | '/404.html',
19 | ];
20 |
21 | var basepath = env === 'production' ? process.env.BASEPATH || '/availity-reactstrap-validation/' : '/';
22 |
23 | var config = [{
24 | devtool: 'source-map',
25 | devServer: {
26 | contentBase: './build',
27 | stats: {
28 | chunks: false,
29 | },
30 | },
31 | entry: {
32 | main: './docs/lib/app.js',
33 | },
34 | node: {
35 | fs: 'empty',
36 | },
37 | output: {
38 | filename: 'bundle.js',
39 | publicPath: basepath,
40 | path: './build',
41 | libraryTarget: 'umd',
42 | },
43 | plugins: [
44 | new CleanWebpackPlugin(['build']),
45 | new CopyWebpackPlugin([{ from: './docs/static', to: 'assets' }]),
46 | new webpack.DefinePlugin({
47 | 'process.env.NODE_ENV': JSON.stringify(env),
48 | }),
49 | new webpack.optimize.DedupePlugin(),
50 | new webpack.optimize.OccurenceOrderPlugin(),
51 | new StaticSiteGeneratorPlugin('main', paths, {basename: basepath}),
52 | new webpack.NoErrorsPlugin(),
53 | new ExtractTextPlugin('/assets/style.css'),
54 | ],
55 | module: {
56 | loaders: [
57 | {
58 | test: /\.(json)$/,
59 | loaders: [
60 | 'json-loader?cacheDirectory',
61 | ],
62 | },
63 | {
64 | test: /\.(js|jsx)$/,
65 | exclude: /node_modules/,
66 | loaders: [
67 | 'babel-loader?cacheDirectory',
68 | ],
69 | },
70 | {
71 | test: /\.css$/,
72 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader'),
73 | },
74 | ],
75 | },
76 | resolve: {
77 | extensions: ['', '.js', '.jsx', '.json'],
78 | alias: {
79 | 'bootstrap-css': path.join(__dirname,'node_modules/bootstrap/dist/css/bootstrap.css'),
80 | 'availity-reactstrap-validation': path.resolve('./src'),
81 | },
82 | },
83 | }];
84 |
85 | if (env === 'development') {
86 | config.push(webpackDevConfig);
87 | config.push(webpackProdConfig);
88 | } else {
89 | config[0].plugins.push(new webpack.optimize.UglifyJsPlugin(
90 | {
91 | minimize: true,
92 | compress: {
93 | warnings: false,
94 | },
95 | mangle: true,
96 | }
97 | ));
98 | }
99 |
100 | module.exports = config;
101 |
--------------------------------------------------------------------------------
/webpack.test.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | var webpackConfig = {
5 | target: 'node',
6 | context: path.join(__dirname, './src'),
7 | devtool: '#cheap-module-source-map',
8 | entry: {
9 | 'availity-reactstrap-validation': ['./index.js']
10 | },
11 | node: {
12 | fs: 'empty',
13 | },
14 | plugins: [
15 | new webpack.NoErrorsPlugin(),
16 | ],
17 | module: {
18 | loaders: [
19 | {
20 | test: /\.(json)$/,
21 | loaders: [
22 | 'json-loader?cacheDirectory',
23 | ],
24 | },
25 | {
26 | test: /\.(js|jsx)$/,
27 | exclude: /node_modules/,
28 | loaders: [
29 | 'babel-loader?cacheDirectory',
30 | ],
31 | },
32 | ],
33 | },
34 | resolve: {
35 | alias: {
36 | 'availity-reactstrap-validation': path.resolve('./src'),
37 | },
38 | extensions: ['', '.js', '.jsx', '.json'],
39 | },
40 | externals: {
41 | 'react/lib/ExecutionEnvironment': true,
42 | 'react/lib/ReactContext': true,
43 | 'react/addons': true,
44 | },
45 | output: {
46 | devtoolModuleFilenameTemplate: '[absolute-resource-path]',
47 | devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
48 | },
49 | webpackServer: {
50 | noInfo: true,
51 | },
52 | };
53 |
54 | module.exports = webpackConfig;
55 |
--------------------------------------------------------------------------------