@khanacademy/wonder-blocks-form 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/es/index.js +1100 -0
- package/dist/index.js +1419 -0
- package/dist/index.js.flow +2 -0
- package/docs.md +1 -0
- package/package.json +35 -0
- package/src/__tests__/__snapshots__/custom-snapshot.test.js.snap +1349 -0
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +6126 -0
- package/src/__tests__/custom-snapshot.test.js +66 -0
- package/src/__tests__/generated-snapshot.test.js +654 -0
- package/src/components/__tests__/checkbox-group.test.js +84 -0
- package/src/components/__tests__/field-heading.test.js +182 -0
- package/src/components/__tests__/labeled-text-field.test.js +442 -0
- package/src/components/__tests__/radio-group.test.js +84 -0
- package/src/components/__tests__/text-field.test.js +424 -0
- package/src/components/checkbox-core.js +201 -0
- package/src/components/checkbox-group.js +161 -0
- package/src/components/checkbox-group.md +200 -0
- package/src/components/checkbox.js +94 -0
- package/src/components/checkbox.md +134 -0
- package/src/components/choice-internal.js +206 -0
- package/src/components/choice.js +104 -0
- package/src/components/field-heading.js +157 -0
- package/src/components/field-heading.md +43 -0
- package/src/components/group-styles.js +35 -0
- package/src/components/labeled-text-field.js +265 -0
- package/src/components/labeled-text-field.md +535 -0
- package/src/components/labeled-text-field.stories.js +359 -0
- package/src/components/radio-core.js +176 -0
- package/src/components/radio-group.js +142 -0
- package/src/components/radio-group.md +129 -0
- package/src/components/radio.js +93 -0
- package/src/components/radio.md +26 -0
- package/src/components/text-field.js +326 -0
- package/src/components/text-field.md +770 -0
- package/src/components/text-field.stories.js +513 -0
- package/src/index.js +18 -0
- package/src/util/types.js +77 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//@flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {mount} from "enzyme";
|
|
4
|
+
|
|
5
|
+
import CheckboxGroup from "../checkbox-group.js";
|
|
6
|
+
import Choice from "../choice.js";
|
|
7
|
+
|
|
8
|
+
describe("CheckboxGroup", () => {
|
|
9
|
+
let group;
|
|
10
|
+
const onChange = jest.fn();
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
group = mount(
|
|
14
|
+
<CheckboxGroup
|
|
15
|
+
label="Test"
|
|
16
|
+
description="test description"
|
|
17
|
+
groupName="test"
|
|
18
|
+
onChange={onChange}
|
|
19
|
+
selectedValues={["a", "b"]}
|
|
20
|
+
>
|
|
21
|
+
<Choice label="a" value="a" aria-labelledby="test-a" />
|
|
22
|
+
<Choice label="b" value="b" aria-labelledby="test-b" />
|
|
23
|
+
<Choice label="c" value="c" aria-labelledby="test-c" />
|
|
24
|
+
</CheckboxGroup>,
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("has the correct items checked", () => {
|
|
29
|
+
const a = group.find(Choice).at(0);
|
|
30
|
+
const b = group.find(Choice).at(1);
|
|
31
|
+
const c = group.find(Choice).at(2);
|
|
32
|
+
|
|
33
|
+
// a starts off checked
|
|
34
|
+
expect(a.prop("checked")).toEqual(true);
|
|
35
|
+
expect(b.prop("checked")).toEqual(true);
|
|
36
|
+
expect(c.prop("checked")).toEqual(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("changes selection when selectedValue changes", () => {
|
|
40
|
+
group.setProps({selectedValues: ["b"]});
|
|
41
|
+
const a = group.find(Choice).at(0);
|
|
42
|
+
const b = group.find(Choice).at(1);
|
|
43
|
+
const c = group.find(Choice).at(2);
|
|
44
|
+
|
|
45
|
+
// now only b is checked
|
|
46
|
+
expect(a.prop("checked")).toEqual(false);
|
|
47
|
+
expect(b.prop("checked")).toEqual(true);
|
|
48
|
+
expect(c.prop("checked")).toEqual(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("displays error state for all Choice children", () => {
|
|
52
|
+
group.setProps({errorMessage: "there's an error"});
|
|
53
|
+
const a = group.find(Choice).at(0);
|
|
54
|
+
const b = group.find(Choice).at(1);
|
|
55
|
+
const c = group.find(Choice).at(2);
|
|
56
|
+
|
|
57
|
+
expect(a.prop("error")).toEqual(true);
|
|
58
|
+
expect(b.prop("error")).toEqual(true);
|
|
59
|
+
expect(c.prop("error")).toEqual(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("calls onChange for each new selection", () => {
|
|
63
|
+
// a is clicked
|
|
64
|
+
const a = group.find(Choice).at(0);
|
|
65
|
+
const aTarget = a.find("ClickableBehavior");
|
|
66
|
+
aTarget.simulate("click");
|
|
67
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
68
|
+
|
|
69
|
+
// now b is clicked, onChange should also be called
|
|
70
|
+
const b = group.find(Choice).at(1);
|
|
71
|
+
const bTarget = b.find("ClickableBehavior");
|
|
72
|
+
bTarget.simulate("click");
|
|
73
|
+
expect(onChange).toHaveBeenCalledTimes(2);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("checks that aria attributes have been added correctly", () => {
|
|
77
|
+
const a = group.find(Choice).at(0);
|
|
78
|
+
const b = group.find(Choice).at(1);
|
|
79
|
+
const c = group.find(Choice).at(2);
|
|
80
|
+
expect(a.find("input").prop("aria-labelledby")).toEqual("test-a");
|
|
81
|
+
expect(b.find("input").prop("aria-labelledby")).toEqual("test-b");
|
|
82
|
+
expect(c.find("input").prop("aria-labelledby")).toEqual("test-c");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {mount} from "enzyme";
|
|
4
|
+
import {StyleSheet} from "aphrodite";
|
|
5
|
+
|
|
6
|
+
import FieldHeading from "../field-heading.js";
|
|
7
|
+
import TextField from "../text-field.js";
|
|
8
|
+
|
|
9
|
+
describe("FieldHeading", () => {
|
|
10
|
+
it("fieldheading renders the label text", () => {
|
|
11
|
+
// Arrange
|
|
12
|
+
const label = "Label";
|
|
13
|
+
|
|
14
|
+
// Act
|
|
15
|
+
const wrapper = mount(
|
|
16
|
+
<FieldHeading
|
|
17
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
18
|
+
label={label}
|
|
19
|
+
/>,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Assert
|
|
23
|
+
expect(wrapper).toIncludeText(label);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("fieldheading renders the description text", () => {
|
|
27
|
+
// Arrange
|
|
28
|
+
const description = "Description";
|
|
29
|
+
|
|
30
|
+
// Act
|
|
31
|
+
const wrapper = mount(
|
|
32
|
+
<FieldHeading
|
|
33
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
34
|
+
label="Label"
|
|
35
|
+
description={description}
|
|
36
|
+
/>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Assert
|
|
40
|
+
expect(wrapper).toIncludeText(description);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("fieldheading renders the error text", () => {
|
|
44
|
+
// Arrange
|
|
45
|
+
const error = "Error";
|
|
46
|
+
|
|
47
|
+
// Act
|
|
48
|
+
const wrapper = mount(
|
|
49
|
+
<FieldHeading
|
|
50
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
51
|
+
label="Label"
|
|
52
|
+
error={error}
|
|
53
|
+
/>,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Assert
|
|
57
|
+
expect(wrapper).toIncludeText(error);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("fieldheading adds testId to label", () => {
|
|
61
|
+
// Arrange
|
|
62
|
+
const testId = "testid";
|
|
63
|
+
|
|
64
|
+
// Act
|
|
65
|
+
const wrapper = mount(
|
|
66
|
+
<FieldHeading
|
|
67
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
68
|
+
label="Label"
|
|
69
|
+
testId={testId}
|
|
70
|
+
/>,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Assert
|
|
74
|
+
const label = wrapper.find(`[data-test-id="${testId}-label"]`);
|
|
75
|
+
expect(label).toExist();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("fieldheading adds testId to description", () => {
|
|
79
|
+
// Arrange
|
|
80
|
+
const testId = "testid";
|
|
81
|
+
|
|
82
|
+
// Act
|
|
83
|
+
const wrapper = mount(
|
|
84
|
+
<FieldHeading
|
|
85
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
86
|
+
label="Label"
|
|
87
|
+
description="Description"
|
|
88
|
+
testId={testId}
|
|
89
|
+
/>,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Assert
|
|
93
|
+
const description = wrapper.find(
|
|
94
|
+
`[data-test-id="${testId}-description"]`,
|
|
95
|
+
);
|
|
96
|
+
expect(description).toExist();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("fieldheading adds testId to error", () => {
|
|
100
|
+
// Arrange
|
|
101
|
+
const testId = "testid";
|
|
102
|
+
|
|
103
|
+
// Act
|
|
104
|
+
const wrapper = mount(
|
|
105
|
+
<FieldHeading
|
|
106
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
107
|
+
label="Label"
|
|
108
|
+
error="Error"
|
|
109
|
+
testId={testId}
|
|
110
|
+
/>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Assert
|
|
114
|
+
const error = wrapper.find(`[data-test-id="${testId}-error"]`);
|
|
115
|
+
expect(error).toExist();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("fieldheading adds the correctly formatted id to label's htmlFor", () => {
|
|
119
|
+
// Arrange
|
|
120
|
+
const id = "exampleid";
|
|
121
|
+
const testId = "testid";
|
|
122
|
+
|
|
123
|
+
// Act
|
|
124
|
+
const wrapper = mount(
|
|
125
|
+
<FieldHeading
|
|
126
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
127
|
+
label="Label"
|
|
128
|
+
id={id}
|
|
129
|
+
testId={testId}
|
|
130
|
+
/>,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Assert
|
|
134
|
+
const label = wrapper.find(`[data-test-id="${testId}-label"]`);
|
|
135
|
+
expect(label).toContainMatchingElement(`[htmlFor="${id}-field"]`);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("fieldheading adds the correctly formatted id to error's id", () => {
|
|
139
|
+
// Arrange
|
|
140
|
+
const id = "exampleid";
|
|
141
|
+
const testId = "testid";
|
|
142
|
+
|
|
143
|
+
// Act
|
|
144
|
+
const wrapper = mount(
|
|
145
|
+
<FieldHeading
|
|
146
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
147
|
+
label="Label"
|
|
148
|
+
error="Error"
|
|
149
|
+
id={id}
|
|
150
|
+
testId={testId}
|
|
151
|
+
/>,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Assert
|
|
155
|
+
const error = wrapper.find(`[data-test-id="${testId}-error"]`);
|
|
156
|
+
expect(error).toContainMatchingElement(`[id="${id}-error"]`);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("stype prop applies to the fieldheading container", () => {
|
|
160
|
+
// Arrange
|
|
161
|
+
const styles = StyleSheet.create({
|
|
162
|
+
style1: {
|
|
163
|
+
flexGrow: 1,
|
|
164
|
+
background: "blue",
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Act
|
|
169
|
+
const wrapper = mount(
|
|
170
|
+
<FieldHeading
|
|
171
|
+
field={<TextField id="tf-1" value="" onChange={() => {}} />}
|
|
172
|
+
label="Label"
|
|
173
|
+
error="Error"
|
|
174
|
+
style={styles.style1}
|
|
175
|
+
/>,
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Assert
|
|
179
|
+
const container = wrapper.find("View").at(0);
|
|
180
|
+
expect(container).toHaveStyle(styles.style1);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
//@flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {mount} from "enzyme";
|
|
4
|
+
|
|
5
|
+
import {StyleSheet} from "aphrodite";
|
|
6
|
+
import LabeledTextField from "../labeled-text-field.js";
|
|
7
|
+
|
|
8
|
+
const wait = (delay: number = 0) =>
|
|
9
|
+
new Promise((resolve, reject) => {
|
|
10
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
11
|
+
return setTimeout(resolve, delay);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("LabeledTextField", () => {
|
|
15
|
+
it("labeledtextfield becomes focused", () => {
|
|
16
|
+
// Arrange
|
|
17
|
+
const wrapper = mount(
|
|
18
|
+
<LabeledTextField label="Label" value="" onChange={() => {}} />,
|
|
19
|
+
);
|
|
20
|
+
const field = wrapper.find("TextFieldInternal");
|
|
21
|
+
|
|
22
|
+
// Act
|
|
23
|
+
field.simulate("focus");
|
|
24
|
+
|
|
25
|
+
// Assert
|
|
26
|
+
expect(wrapper.find("LabeledTextFieldInternal")).toHaveState(
|
|
27
|
+
"focused",
|
|
28
|
+
true,
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("labeledtextfield becomes blurred", async () => {
|
|
33
|
+
// Arrange
|
|
34
|
+
const wrapper = mount(
|
|
35
|
+
<LabeledTextField label="Label" value="" onChange={() => {}} />,
|
|
36
|
+
);
|
|
37
|
+
const field = wrapper.find("TextFieldInternal");
|
|
38
|
+
|
|
39
|
+
// Act
|
|
40
|
+
field.simulate("focus");
|
|
41
|
+
await wait(0);
|
|
42
|
+
field.simulate("blur");
|
|
43
|
+
|
|
44
|
+
// Assert
|
|
45
|
+
expect(wrapper.find("LabeledTextFieldInternal")).toHaveState(
|
|
46
|
+
"focused",
|
|
47
|
+
false,
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("id prop is passed to input", () => {
|
|
52
|
+
// Arrange
|
|
53
|
+
const id = "exampleid";
|
|
54
|
+
|
|
55
|
+
// Act
|
|
56
|
+
const wrapper = mount(
|
|
57
|
+
<LabeledTextField
|
|
58
|
+
id={id}
|
|
59
|
+
label="Label"
|
|
60
|
+
value=""
|
|
61
|
+
onChange={() => {}}
|
|
62
|
+
disabled={true}
|
|
63
|
+
/>,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Assert
|
|
67
|
+
const input = wrapper.find("input");
|
|
68
|
+
expect(input).toContainMatchingElement(`[id="${id}-field"]`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("auto-generated id is passed to input when id prop is not set", () => {
|
|
72
|
+
// Arrange
|
|
73
|
+
|
|
74
|
+
// Act
|
|
75
|
+
const wrapper = mount(
|
|
76
|
+
<LabeledTextField label="Label" value="" onChange={() => {}} />,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Assert
|
|
80
|
+
// Since the generated id is unique, we cannot know what it will be.
|
|
81
|
+
// We only test if the id attribute starts with "uid-" and ends with "-field".
|
|
82
|
+
const input = wrapper.find("input");
|
|
83
|
+
expect(input.props()["id"]).toMatch(/uid-.*-field/);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("type prop is passed to input", () => {
|
|
87
|
+
// Arrange
|
|
88
|
+
const type = "email";
|
|
89
|
+
|
|
90
|
+
// Act
|
|
91
|
+
const wrapper = mount(
|
|
92
|
+
<LabeledTextField
|
|
93
|
+
type={type}
|
|
94
|
+
label="Label"
|
|
95
|
+
value=""
|
|
96
|
+
onChange={() => {}}
|
|
97
|
+
/>,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Assert
|
|
101
|
+
const input = wrapper.find("input");
|
|
102
|
+
expect(input).toContainMatchingElement(`[type="${type}"]`);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("label prop is rendered", () => {
|
|
106
|
+
// Arrange
|
|
107
|
+
const label = "Label";
|
|
108
|
+
|
|
109
|
+
// Act
|
|
110
|
+
const wrapper = mount(
|
|
111
|
+
<LabeledTextField label={label} value="" onChange={() => {}} />,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Assert
|
|
115
|
+
expect(wrapper).toIncludeText(label);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("description prop is rendered", () => {
|
|
119
|
+
// Arrange
|
|
120
|
+
const description = "Description";
|
|
121
|
+
|
|
122
|
+
// Act
|
|
123
|
+
const wrapper = mount(
|
|
124
|
+
<LabeledTextField
|
|
125
|
+
label="Label"
|
|
126
|
+
description={description}
|
|
127
|
+
value=""
|
|
128
|
+
onChange={() => {}}
|
|
129
|
+
/>,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Assert
|
|
133
|
+
expect(wrapper).toIncludeText(description);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("value prop is set on mount", () => {
|
|
137
|
+
// Arrange
|
|
138
|
+
const value = "Value";
|
|
139
|
+
|
|
140
|
+
// Act
|
|
141
|
+
const wrapper = mount(
|
|
142
|
+
<LabeledTextField
|
|
143
|
+
label="Label"
|
|
144
|
+
value={value}
|
|
145
|
+
onChange={() => {}}
|
|
146
|
+
/>,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Assert
|
|
150
|
+
const input = wrapper.find("input");
|
|
151
|
+
expect(input).toHaveValue(value);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("value prop change from parent reflects on input value", async () => {
|
|
155
|
+
// Arrange
|
|
156
|
+
const handleChange = jest.fn((newValue: string) => {});
|
|
157
|
+
|
|
158
|
+
const wrapper = mount(
|
|
159
|
+
<LabeledTextField label="Label" value="" onChange={handleChange} />,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Act
|
|
163
|
+
const newValue = "new value";
|
|
164
|
+
wrapper.setProps({value: newValue});
|
|
165
|
+
|
|
166
|
+
// Assert
|
|
167
|
+
const input = wrapper.find("input");
|
|
168
|
+
expect(input).toHaveValue(newValue);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("disabled prop disables the input", () => {
|
|
172
|
+
// Arrange
|
|
173
|
+
|
|
174
|
+
// Act
|
|
175
|
+
const wrapper = mount(
|
|
176
|
+
<LabeledTextField
|
|
177
|
+
label="Label"
|
|
178
|
+
value=""
|
|
179
|
+
onChange={() => {}}
|
|
180
|
+
disabled={true}
|
|
181
|
+
/>,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Assert
|
|
185
|
+
const input = wrapper.find("input");
|
|
186
|
+
expect(input).toBeDisabled();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("validate prop is called when input changes", () => {
|
|
190
|
+
// Arrange
|
|
191
|
+
const validate = jest.fn((value: string): ?string => {});
|
|
192
|
+
const wrapper = mount(
|
|
193
|
+
<LabeledTextField
|
|
194
|
+
label="Label"
|
|
195
|
+
value=""
|
|
196
|
+
onChange={() => {}}
|
|
197
|
+
validate={validate}
|
|
198
|
+
/>,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Act
|
|
202
|
+
const newValue = "New Value";
|
|
203
|
+
const input = wrapper.find("input");
|
|
204
|
+
input.simulate("change", {target: {value: newValue}});
|
|
205
|
+
|
|
206
|
+
// Assert
|
|
207
|
+
expect(validate).toHaveBeenCalledWith(newValue);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("onValidate prop is called on new validated input", () => {
|
|
211
|
+
// Arrange
|
|
212
|
+
const handleValidate = jest.fn((errorMessage: ?string) => {});
|
|
213
|
+
const errorMessage = "Password must be at least 8 characters long";
|
|
214
|
+
|
|
215
|
+
const validate = (value: string): ?string => {
|
|
216
|
+
if (value.length < 8) {
|
|
217
|
+
return errorMessage;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const wrapper = mount(
|
|
222
|
+
<LabeledTextField
|
|
223
|
+
label="Label"
|
|
224
|
+
value="LongerThan8Chars"
|
|
225
|
+
onChange={() => {}}
|
|
226
|
+
validate={validate}
|
|
227
|
+
onValidate={handleValidate}
|
|
228
|
+
/>,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// Act
|
|
232
|
+
const input = wrapper.find("input");
|
|
233
|
+
input.simulate("change", {target: {value: "Short"}});
|
|
234
|
+
|
|
235
|
+
// Assert
|
|
236
|
+
expect(handleValidate).toHaveBeenCalledWith(errorMessage);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("onChange prop is called on input change", () => {
|
|
240
|
+
// Arrange
|
|
241
|
+
const handleChange = jest.fn((newValue: string) => {});
|
|
242
|
+
|
|
243
|
+
const wrapper = mount(
|
|
244
|
+
<LabeledTextField label="Label" value="" onChange={handleChange} />,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Act
|
|
248
|
+
const newValue = "new value";
|
|
249
|
+
const input = wrapper.find("input");
|
|
250
|
+
input.simulate("change", {target: {value: newValue}});
|
|
251
|
+
|
|
252
|
+
// Assert
|
|
253
|
+
expect(handleChange).toHaveBeenCalledWith(newValue);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("onKeyDown prop is called on keyboard keypress", () => {
|
|
257
|
+
// Arrange
|
|
258
|
+
const handleKeyDown = jest.fn(
|
|
259
|
+
(event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
260
|
+
return event.key;
|
|
261
|
+
},
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const wrapper = mount(
|
|
265
|
+
<LabeledTextField
|
|
266
|
+
label="Label"
|
|
267
|
+
value=""
|
|
268
|
+
onChange={() => {}}
|
|
269
|
+
onKeyDown={handleKeyDown}
|
|
270
|
+
/>,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Act
|
|
274
|
+
const key = "Enter";
|
|
275
|
+
const input = wrapper.find("input");
|
|
276
|
+
input.simulate("keyDown", {key: key});
|
|
277
|
+
|
|
278
|
+
// Assert
|
|
279
|
+
expect(handleKeyDown).toHaveReturnedWith(key);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("onFocus prop is called when field is focused", () => {
|
|
283
|
+
// Arrange
|
|
284
|
+
const handleFocus = jest.fn(() => {});
|
|
285
|
+
const wrapper = mount(
|
|
286
|
+
<LabeledTextField
|
|
287
|
+
label="Label"
|
|
288
|
+
value=""
|
|
289
|
+
onChange={() => {}}
|
|
290
|
+
onFocus={handleFocus}
|
|
291
|
+
/>,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Act
|
|
295
|
+
const field = wrapper.find("TextFieldInternal");
|
|
296
|
+
field.simulate("focus");
|
|
297
|
+
|
|
298
|
+
// Assert
|
|
299
|
+
expect(handleFocus).toHaveBeenCalled();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("onBlur prop is called when field is blurred", async () => {
|
|
303
|
+
// Arrange
|
|
304
|
+
const handleBlur = jest.fn(() => {});
|
|
305
|
+
const wrapper = mount(
|
|
306
|
+
<LabeledTextField
|
|
307
|
+
label="Label"
|
|
308
|
+
value=""
|
|
309
|
+
onChange={() => {}}
|
|
310
|
+
onBlur={handleBlur}
|
|
311
|
+
/>,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
// Act
|
|
315
|
+
const field = wrapper.find("TextFieldInternal");
|
|
316
|
+
field.simulate("focus");
|
|
317
|
+
await wait(0);
|
|
318
|
+
field.simulate("blur");
|
|
319
|
+
|
|
320
|
+
// Assert
|
|
321
|
+
expect(handleBlur).toHaveBeenCalled();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("placeholder prop is passed to input", async () => {
|
|
325
|
+
// Arrange
|
|
326
|
+
const placeholder = "Placeholder";
|
|
327
|
+
|
|
328
|
+
// Act
|
|
329
|
+
const wrapper = mount(
|
|
330
|
+
<LabeledTextField
|
|
331
|
+
label="Label"
|
|
332
|
+
value=""
|
|
333
|
+
onChange={() => {}}
|
|
334
|
+
placeholder={placeholder}
|
|
335
|
+
/>,
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Assert
|
|
339
|
+
const input = wrapper.find("input");
|
|
340
|
+
expect(input).toContainMatchingElement(
|
|
341
|
+
`[placeholder="${placeholder}"]`,
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("light prop is passed to textfield", async () => {
|
|
346
|
+
// Arrange
|
|
347
|
+
|
|
348
|
+
// Act
|
|
349
|
+
const wrapper = mount(
|
|
350
|
+
<LabeledTextField
|
|
351
|
+
label="Label"
|
|
352
|
+
value=""
|
|
353
|
+
onChange={() => {}}
|
|
354
|
+
light={true}
|
|
355
|
+
/>,
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
// Assert
|
|
359
|
+
const textField = wrapper.find("TextFieldInternal");
|
|
360
|
+
expect(textField).toHaveProp("light", true);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("style prop is passed to fieldheading", async () => {
|
|
364
|
+
// Arrange
|
|
365
|
+
const styles = StyleSheet.create({
|
|
366
|
+
style1: {
|
|
367
|
+
minWidth: 250,
|
|
368
|
+
background: "blue",
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Act
|
|
373
|
+
const wrapper = mount(
|
|
374
|
+
<LabeledTextField
|
|
375
|
+
label="Label"
|
|
376
|
+
value=""
|
|
377
|
+
onChange={() => {}}
|
|
378
|
+
style={styles.style1}
|
|
379
|
+
/>,
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
// Assert
|
|
383
|
+
const fieldHeading = wrapper.find("FieldHeading");
|
|
384
|
+
expect(fieldHeading).toHaveStyle(styles.style1);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("testId prop is passed to textfield", async () => {
|
|
388
|
+
// Arrange
|
|
389
|
+
const testId = "example-testid";
|
|
390
|
+
|
|
391
|
+
// Act
|
|
392
|
+
const wrapper = mount(
|
|
393
|
+
<LabeledTextField
|
|
394
|
+
label="Label"
|
|
395
|
+
value=""
|
|
396
|
+
onChange={() => {}}
|
|
397
|
+
testId={testId}
|
|
398
|
+
/>,
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// Assert
|
|
402
|
+
const textField = wrapper.find(`[data-test-id="${testId}-field"]`);
|
|
403
|
+
expect(textField).toExist();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it("readOnly prop is passed to textfield", async () => {
|
|
407
|
+
// Arrange
|
|
408
|
+
|
|
409
|
+
// Act
|
|
410
|
+
const wrapper = mount(
|
|
411
|
+
<LabeledTextField
|
|
412
|
+
label="Label"
|
|
413
|
+
value=""
|
|
414
|
+
onChange={() => {}}
|
|
415
|
+
readOnly={true}
|
|
416
|
+
/>,
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// Assert
|
|
420
|
+
const textField = wrapper.find("TextFieldInternal");
|
|
421
|
+
expect(textField).toHaveProp("readOnly", true);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("autoComplete prop is passed to textfield", async () => {
|
|
425
|
+
// Arrange
|
|
426
|
+
const autoComplete = "name";
|
|
427
|
+
|
|
428
|
+
// Act
|
|
429
|
+
const wrapper = mount(
|
|
430
|
+
<LabeledTextField
|
|
431
|
+
label="Label"
|
|
432
|
+
value=""
|
|
433
|
+
onChange={() => {}}
|
|
434
|
+
autoComplete={autoComplete}
|
|
435
|
+
/>,
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
// Assert
|
|
439
|
+
const textField = wrapper.find("TextFieldInternal");
|
|
440
|
+
expect(textField).toHaveProp("autoComplete", autoComplete);
|
|
441
|
+
});
|
|
442
|
+
});
|