Skip to content

Commit

Permalink
Merge pull request #91 from edx/mroytman/asInput-danger-theme
Browse files Browse the repository at this point in the history
fix(asinput): Add danger theme to asInput component
  • Loading branch information
jaebradley committed Dec 6, 2017
2 parents 1d25dfc + d545540 commit 4f253ff
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 22 deletions.
57 changes: 57 additions & 0 deletions .storybook/__snapshots__/Storyshots.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ exports[`Storyshots InputText focus test 1`] = `
onChange={[Function]}
placeholder={undefined}
required={false}
themes={Array []}
type="text"
value=""
/>
Expand Down Expand Up @@ -527,6 +528,7 @@ exports[`Storyshots InputText minimal usage 1`] = `
onChange={[Function]}
placeholder={undefined}
required={false}
themes={Array []}
type="text"
value="Foo Bar"
/>
Expand Down Expand Up @@ -554,6 +556,7 @@ exports[`Storyshots InputText validation 1`] = `
onChange={[Function]}
placeholder={undefined}
required={false}
themes={Array []}
type="text"
value=""
/>
Expand All @@ -566,6 +569,44 @@ exports[`Storyshots InputText validation 1`] = `
</div>
`;

exports[`Storyshots InputText validation with danger theme 1`] = `
<div
className="form-group"
>
<label
htmlFor="asInput3"
id="label-asInput3"
>
Username
</label>
<input
aria-describedby="undefined description-asInput3"
aria-invalid={false}
className="form-control"
disabled={false}
id="asInput3"
name="username"
onBlur={[Function]}
onChange={[Function]}
placeholder={undefined}
required={false}
themes={
Array [
"danger",
]
}
type="text"
value=""
/>
<small
className="form-text"
id="description-asInput3"
>
The unique name that identifies you throughout the site.
</small>
</div>
`;

exports[`Storyshots Modal basic usage 1`] = `
<div
aria-labelledby="id5"
Expand Down Expand Up @@ -1058,6 +1099,7 @@ exports[`Storyshots Modal modal with element body 1`] = `
onChange={[Function]}
placeholder={undefined}
required={false}
themes={Array []}
type="text"
value=""
/>
Expand Down Expand Up @@ -2044,6 +2086,11 @@ exports[`Storyshots Textarea minimal usage 1`] = `
onChange={[Function]}
placeholder={undefined}
required={false}
themes={
Array [
"danger",
]
}
value="Foo Bar"
/>
</div>
Expand All @@ -2070,6 +2117,11 @@ exports[`Storyshots Textarea scrollable 1`] = `
onChange={[Function]}
placeholder={undefined}
required={false}
themes={
Array [
"danger",
]
}
value="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
/>
</div>
Expand All @@ -2096,6 +2148,11 @@ exports[`Storyshots Textarea validation 1`] = `
onChange={[Function]}
placeholder={undefined}
required={false}
themes={
Array [
"danger",
]
}
value=""
/>
<small
Expand Down
18 changes: 18 additions & 0 deletions src/InputText/InputText.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ storiesOf('InputText', module)
}}
/>
))
.add('validation with danger theme', () => (
<InputText
name="username"
label="Username"
description="The unique name that identifies you throughout the site."
validator={(value) => {
let feedback = { isValid: true };
if (value.length < 3) {
feedback = {
isValid: false,
validationMessage: 'Username must be at least 3 characters in length.',
};
}
return feedback;
}}
themes={['danger']}
/>
))
.add('focus test', () => (
<FocusInputWrapper />
));
1 change: 1 addition & 0 deletions src/InputText/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function Text(props) {
disabled={props.disabled}
required={props.required}
ref={props.inputRef}
themes={props.themes}
/>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/TextArea/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ function Text(props) {
disabled={props.disabled}
required={props.required}
ref={props.inputRef}
themes={['danger']}
/>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/asInput/asInput.scss
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
@import "~bootstrap/scss/_forms";
@import "~bootstrap/scss/mixins/_forms";

.fa-icon-spacing {
padding: 0px 5px 0px 0px;
}
58 changes: 41 additions & 17 deletions src/asInput/asInput.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,47 @@ describe('asInput()', () => {
expect(spy).toHaveBeenCalledTimes(1);
});

it('and displays error message when invalid', () => {
const spy = jest.fn();
const validationResult = {
isValid: false,
validationMessage: 'Invalid!!1',
};
spy.mockReturnValueOnce(validationResult);
const props = {
...baseProps,
validator: spy,
};
const wrapper = mount(<InputTestComponent {...props} />);
wrapper.find('input').simulate('blur');
expect(spy).toHaveBeenCalledTimes(1);
const err = wrapper.find('.form-control-feedback');
expect(err.exists()).toEqual(true);
expect(err.text()).toEqual(validationResult.validationMessage);
describe('and display error message when invalid', () => {
let spy;
let validationResult;
let wrapper;

beforeEach(() => {
spy = jest.fn();
validationResult = {
isValid: false,
validationMessage: 'Invalid!!1',
};
spy.mockReturnValueOnce(validationResult);
const props = {
...baseProps,
validator: spy,
};
wrapper = mount(<InputTestComponent {...props} />);
});
it('without theme', () => {
wrapper.find('input').simulate('blur');
expect(spy).toHaveBeenCalledTimes(1);
const err = wrapper.find('.form-control-feedback');
expect(err.exists()).toEqual(true);
expect(err.text()).toEqual(validationResult.validationMessage);
});
it('with danger theme', () => {
wrapper.setProps({ themes: ['danger'] });
wrapper.find('input').simulate('blur');
expect(spy).toHaveBeenCalledTimes(1);
const err = wrapper.find('.form-control-feedback');
expect(err.exists()).toEqual(true);
expect(err.text()).toEqual(validationResult.validationMessage);
// expect(err.hasClass('invalid-feedback')).toEqual(true);

const dangerIcon = wrapper.find('.fa-exclamation-circle');
expect(dangerIcon.exists()).toEqual(true);
expect(dangerIcon.hasClass('fa')).toEqual(true);

const inputElement = wrapper.find('.form-control');
expect(inputElement.hasClass('is-invalid')).toEqual(true);
});
});
});
});
Expand Down
29 changes: 24 additions & 5 deletions src/asInput/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable react/no-unused-prop-types */
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';

import newId from '../utils/newId';
import styles from './asInput.scss';
Expand All @@ -23,6 +25,7 @@ export const inputProps = {
onBlur: PropTypes.func,
validator: PropTypes.func,
className: PropTypes.arrayOf(PropTypes.string),
themes: PropTypes.arrayOf(PropTypes.string),
};

const asInput = (WrappedComponent, labelFirst = true) => {
Expand Down Expand Up @@ -50,9 +53,19 @@ const asInput = (WrappedComponent, labelFirst = true) => {
const desc = {};

if (!this.state.isValid) {
const hasDangerTheme = this.hasDangerTheme();

desc.error = (
<div className={styles['form-control-feedback']} id={errorId} key="0">
{this.state.validationMessage}
<div className={classNames(styles['form-control-feedback'], { [styles['invalid-feedback']]: hasDangerTheme })} id={errorId} key="0">
{ hasDangerTheme &&
<span
className={classNames(FontAwesomeStyles.fa, FontAwesomeStyles['fa-exclamation-circle'], styles['fa-icon-spacing'])}
aria-hidden
/>
}
<span>
{this.state.validationMessage}
</span>
</div>
);
desc.describedBy = errorId;
Expand All @@ -70,6 +83,10 @@ const asInput = (WrappedComponent, labelFirst = true) => {
return desc;
}

hasDangerTheme() {
return this.props.themes.includes('danger');
}

handleBlur(event) {
const val = event.target.value;

Expand All @@ -95,10 +112,11 @@ const asInput = (WrappedComponent, labelFirst = true) => {
<WrappedComponent
{...this.props}
{...this.state}
className={[
className={[classNames(
styles['form-control'],
...this.props.className,
]}
{ [styles['is-invalid']]: !this.state.isValid && this.hasDangerTheme() },
{ ...this.props.className },
)]}
describedBy={describedBy}
onChange={this.handleChange}
onBlur={this.handleBlur}
Expand All @@ -125,6 +143,7 @@ const asInput = (WrappedComponent, labelFirst = true) => {
required: false,
validator: undefined,
className: [],
themes: [],
};

return NewComponent;
Expand Down

0 comments on commit 4f253ff

Please sign in to comment.