Skip to content

Commit ff14592

Browse files
authored
Wrap CheckBoxInput in InputWrapper to provide visual feedback (#2423)
### Changes Wrap the `CheckBoxInput` in `InputWrapper` to allow for providing feedback as to what is wrong when a user clicks submit without the checkbox being selected. Below is an example of what this looks like, rather than highlight using a border like other fields we change the text to red. **This image is outdated see below** <img width="301" alt="Screenshot 2023-08-04 at 12 03 18" src="https://github.com/auth0/lock/assets/8705251/cb346e0a-5d3e-4d24-87e8-b037b99dd34b"> I did also see what it looked like without the red text, which is a little less obvious. <img width="300" alt="Screenshot 2023-08-04 at 11 59 11" src="https://github.com/auth0/lock/assets/8705251/688ee97f-cd17-4375-80bb-b00d252529cb"> This is if we add a border around the entire element like the other components ![Screenshot 2023-08-04 at 11 05 07](https://github.com/auth0/lock/assets/8705251/a3eea2dc-c8ee-4e90-b5b9-f7fa68e3d143) I did also try a border/outline around the checkbox element but it looked bad due to inability to set the border radiu Happy to change this up if there's a general preference that looks better. Current status: Invalid hint text provided ![Invalid hint text is red and aligned with checkbox text](https://github.com/auth0/lock/assets/8705251/9ec1f0d1-9d60-46fd-b38b-9165dbc9ae71) No invalid hint text provided ![Checkbox text is red](https://github.com/auth0/lock/assets/8705251/c9452810-11f3-43e3-85e6-bc4298f5d904) ### References #2400 ### Testing <!-- Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors. --> * [x] This change adds unit test coverage * [ ] This change adds integration test coverage * [ ] This change has been tested on the latest version of the platform/language ### Checklist * [x] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md) * [x] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md) * [x] All code quality tools/guidelines have been run/followed * [x] All relevant assets have been compiled
1 parent c9a2598 commit ff14592

File tree

7 files changed

+185
-38
lines changed

7 files changed

+185
-38
lines changed

css/index.styl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,14 @@ loadingSize = 30px
491491
span
492492
display block
493493
margin-left 20px
494+
.auth0-lock-input-wrap
495+
background #ffffff
496+
border 1px solid #ffffff
497+
.auth0-lock-input-checkbox.auth0-lock-error
498+
.auth0-lock-input-wrap span.no-hint
499+
color red
500+
.auth0-lock-error-invalid-hint
501+
margin-left 20px
494502

495503
// Social
496504

src/__tests__/field/__snapshots__/custom_input.test.jsx.snap

Lines changed: 115 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,129 @@
22

33
exports[`CustomInput when type == checkbox and when placeholderHTML is set renders correctly as a CheckBoxInput 1`] = `
44
<div
5-
className="auth0-lock-input-checkbox"
5+
className="auth0-lock-input-block auth0-lock-input-custom_input auth0-lock-input-checkbox"
66
>
7-
<label>
8-
<input
9-
aria-label="Custom Input"
10-
checked={false}
11-
id="1-custom_input"
12-
name="custom_input"
13-
onChange={[Function]}
14-
type="checkbox"
15-
/>
16-
<span
17-
dangerouslySetInnerHTML={
18-
{
19-
"__html": "<b>Placeholder</b>",
7+
<div
8+
className="auth0-lock-input-wrap"
9+
>
10+
<label>
11+
<input
12+
aria-invalid={false}
13+
aria-label="Custom Input"
14+
checked={false}
15+
id="1-custom_input"
16+
name="custom_input"
17+
onChange={[Function]}
18+
type="checkbox"
19+
/>
20+
<span
21+
className=""
22+
dangerouslySetInnerHTML={
23+
{
24+
"__html": "<b>Placeholder</b>",
25+
}
2026
}
21-
}
22-
/>
23-
</label>
27+
/>
28+
</label>
29+
</div>
30+
</div>
31+
`;
32+
33+
exports[`CustomInput when type == checkbox highlights placeholder text when no invalid hint is provided 1`] = `
34+
<div
35+
className="auth0-lock-input-block auth0-lock-input-custom_input auth0-lock-error auth0-lock-input-checkbox"
36+
>
37+
<div
38+
className="auth0-lock-input-wrap"
39+
>
40+
<label>
41+
<input
42+
aria-invalid={true}
43+
aria-label="Custom Input"
44+
checked={false}
45+
id="1-custom_input"
46+
name="custom_input"
47+
onChange={[Function]}
48+
type="checkbox"
49+
/>
50+
<span
51+
className="no-hint"
52+
dangerouslySetInnerHTML={
53+
{
54+
"__html": "<b>Placeholder</b>",
55+
}
56+
}
57+
/>
58+
</label>
59+
</div>
2460
</div>
2561
`;
2662

2763
exports[`CustomInput when type == checkbox renders correctly as a CheckBoxInput 1`] = `
2864
<div
29-
className="auth0-lock-input-checkbox"
65+
className="auth0-lock-input-block auth0-lock-input-custom_input auth0-lock-input-checkbox"
3066
>
31-
<label>
32-
<input
33-
aria-label="Custom Input"
34-
checked={false}
35-
id="1-custom_input"
36-
name="custom_input"
37-
onChange={[Function]}
38-
type="checkbox"
39-
/>
40-
<span>
41-
placeholder
42-
</span>
43-
</label>
67+
<div
68+
className="auth0-lock-input-wrap"
69+
>
70+
<label>
71+
<input
72+
aria-invalid={false}
73+
aria-label="Custom Input"
74+
checked={false}
75+
id="1-custom_input"
76+
name="custom_input"
77+
onChange={[Function]}
78+
type="checkbox"
79+
/>
80+
<span
81+
className=""
82+
>
83+
placeholder
84+
</span>
85+
</label>
86+
</div>
87+
</div>
88+
`;
89+
90+
exports[`CustomInput when type == checkbox shows an error when value is incorrect 1`] = `
91+
<div
92+
className="auth0-lock-input-block auth0-lock-input-custom_input auth0-lock-error auth0-lock-input-checkbox"
93+
>
94+
<div
95+
className="auth0-lock-input-wrap"
96+
>
97+
<label>
98+
<input
99+
aria-invalid={true}
100+
aria-label="Custom Input"
101+
checked={false}
102+
id="1-custom_input"
103+
name="custom_input"
104+
onChange={[Function]}
105+
type="checkbox"
106+
/>
107+
<span
108+
className=""
109+
dangerouslySetInnerHTML={
110+
{
111+
"__html": "<b>Placeholder</b>",
112+
}
113+
}
114+
/>
115+
</label>
116+
</div>
117+
<div
118+
className="auth0-lock-error-msg"
119+
id="auth0-lock-error-msg-custom_input"
120+
role="alert"
121+
>
122+
<div
123+
className="auth0-lock-error-invalid-hint"
124+
>
125+
invalid-hint-custom_input
126+
</div>
127+
</div>
44128
</div>
45129
`;
46130

src/__tests__/field/custom_input.test.jsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,41 @@ describe('CustomInput', () => {
9797
describe('when type == checkbox', () => {
9898
beforeEach(() => (defaultProps.type = 'checkbox'));
9999
it('renders correctly as a CheckBoxInput', () => {
100+
require('field/index').isFieldVisiblyInvalid = () => false;
100101
const CustomInput = getComponent();
101102

102103
expectComponent(<CustomInput {...defaultProps} />).toMatchSnapshot();
103104
});
104105

105106
describe('and when placeholderHTML is set', () => {
106107
it('renders correctly as a CheckBoxInput', () => {
108+
require('field/index').isFieldVisiblyInvalid = () => false;
109+
107110
const CustomInput = getComponent();
108111

109112
expectComponent(
110113
<CustomInput {...defaultProps} placeholderHTML={'<b>Placeholder</b>'} />
111114
).toMatchSnapshot();
112115
});
113116
});
117+
118+
it('shows an error when value is incorrect', () => {
119+
const CustomInput = getComponent();
120+
121+
expectComponent(
122+
<CustomInput {...defaultProps} placeholderHTML={'<b>Placeholder</b>'} />
123+
).toMatchSnapshot();
124+
});
125+
126+
it('highlights placeholder text when no invalid hint is provided', () => {
127+
require('field/index').getFieldInvalidHint = () => undefined;
128+
129+
const CustomInput = getComponent();
130+
131+
expectComponent(
132+
<CustomInput {...defaultProps} placeholderHTML={'<b>Placeholder</b>'} />
133+
).toMatchSnapshot();
134+
});
114135
});
115136
describe('when type == hidden', () => {
116137
beforeEach(() => {

src/field/custom_input.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const CustomInput = ({
3939
return (
4040
<CheckboxInput
4141
lockId={l.id(model)}
42+
invalidHint={getFieldInvalidHint(model, name)}
4243
onChange={e => changeField(l.id(model), name, `${e.target.checked}`, validator)}
4344
checked={getFieldValue(model, name)}
4445
placeholderHTML={placeholderHTML}

src/ui/input/checkbox_input.jsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
import React from 'react';
2+
import InputWrap from './input_wrap';
23

34
export default class CheckboxInput extends React.Component {
45
render() {
5-
const { lockId, name, ariaLabel, placeholder, checked, placeholderHTML } = this.props;
6+
const {
7+
lockId,
8+
name,
9+
ariaLabel,
10+
placeholder,
11+
checked,
12+
placeholderHTML,
13+
isValid,
14+
invalidHint
15+
} = this.props;
16+
17+
const spanClass = invalidHint ? '' : 'no-hint'
18+
619
return (
7-
<div className="auth0-lock-input-checkbox">
20+
<InputWrap
21+
invalidHint={invalidHint}
22+
isValid={isValid}
23+
name={name}
24+
className="auth0-lock-input-checkbox"
25+
>
826
<label>
927
<input
1028
id={`${lockId}-${name}`}
@@ -13,16 +31,17 @@ export default class CheckboxInput extends React.Component {
1331
onChange={::this.handleOnChange}
1432
name={name}
1533
aria-label={ariaLabel || name}
34+
aria-invalid={!isValid}
1635
/>
1736
{placeholderHTML ? (
1837
// placeholderHTML allows raw HTML
1938
// eslint-disable-next-line react/no-danger
20-
<span dangerouslySetInnerHTML={{ __html: placeholderHTML }} />
39+
<span className={spanClass} dangerouslySetInnerHTML={{ __html: placeholderHTML }} />
2140
) : (
22-
<span>{placeholder}</span>
41+
<span className={spanClass}>{placeholder}</span>
2342
)}
2443
</label>
25-
</div>
44+
</InputWrap>
2645
);
2746
}
2847

src/ui/input/input_wrap.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ import React from 'react';
33

44
export default class InputWrap extends React.Component {
55
render() {
6-
const { after, focused, invalidHint, isValid, name, icon } = this.props;
6+
const { after, focused, invalidHint, isValid, name, icon, className } = this.props;
77
let blockClassName = `auth0-lock-input-block auth0-lock-input-${name}`;
88
if (!isValid) {
99
blockClassName += ' auth0-lock-error';
1010
}
1111

12+
if (className) {
13+
blockClassName += ` ${className}`;
14+
}
15+
1216
let wrapClassName = 'auth0-lock-input-wrap';
1317
if (focused && isValid) {
1418
wrapClassName += ' auth0-lock-focused';

support/index.html

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,17 @@ <h1 class="navbar-brand">
155155
validator: function () {
156156
return true;
157157
}
158-
}
158+
},
159+
{
160+
type: 'checkbox',
161+
name: 'newsletter',
162+
prefill: 'false',
163+
placeholder: 'I hereby agree that I want to receive marketing emails from your company',
164+
validator: (value) => ({
165+
valid: value === 'true',
166+
hint: 'This is a mandatory field',
167+
}),
168+
},
159169
],
160170
hooks: {
161171
loggingIn: function (context, done) {

0 commit comments

Comments
 (0)