@jobber/components-native 0.47.3 → 0.48.0
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.
|
@@ -64,6 +64,10 @@ export interface SelectProps {
|
|
|
64
64
|
* The validations that will mark this component as invalid
|
|
65
65
|
*/
|
|
66
66
|
readonly validations?: RegisterOptions;
|
|
67
|
+
/**
|
|
68
|
+
* Used to locate this view in end-to-end tests.
|
|
69
|
+
*/
|
|
70
|
+
readonly testID?: string;
|
|
67
71
|
}
|
|
68
|
-
export declare function Select({ value, defaultValue, onChange, children, placeholder, label, assistiveText, disabled, invalid, validations, accessibilityLabel, name, }: SelectProps): JSX.Element;
|
|
72
|
+
export declare function Select({ value, defaultValue, onChange, children, placeholder, label, assistiveText, disabled, invalid, validations, accessibilityLabel, name, testID, }: SelectProps): JSX.Element;
|
|
69
73
|
export declare function Option({ children }: SelectOption): JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"react-native-reanimated": "^2.17.0",
|
|
85
85
|
"react-native-safe-area-context": "^4.5.2"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "faaa9887b344a34eb2760e71fee532145e2dbe81"
|
|
88
88
|
}
|
|
@@ -80,48 +80,6 @@ describe("Select", () => {
|
|
|
80
80
|
).toBeDefined();
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
describe("when invalid", () => {
|
|
84
|
-
const labelText = "labelText";
|
|
85
|
-
|
|
86
|
-
it("renders an invalid Select", () => {
|
|
87
|
-
const { getByText } = render(
|
|
88
|
-
<Select onChange={onChange} invalid={true} label={labelText}>
|
|
89
|
-
<Option value={"1"}>1</Option>
|
|
90
|
-
<Option value={"2"}>2</Option>
|
|
91
|
-
</Select>,
|
|
92
|
-
);
|
|
93
|
-
expect(
|
|
94
|
-
getByText(labelText, { includeHiddenElements: true }).props.style,
|
|
95
|
-
).toContainEqual({
|
|
96
|
-
color: tokens["color-critical"],
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("renders an invalid Select with placeholder", () => {
|
|
101
|
-
const placeholder = "Place me in the holder";
|
|
102
|
-
const { getByText } = render(
|
|
103
|
-
<Select
|
|
104
|
-
label={labelText}
|
|
105
|
-
onChange={onChange}
|
|
106
|
-
invalid={true}
|
|
107
|
-
placeholder={placeholder}
|
|
108
|
-
>
|
|
109
|
-
<Option value={"1"}>1</Option>
|
|
110
|
-
<Option value={"2"}>2</Option>
|
|
111
|
-
</Select>,
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
expect(
|
|
115
|
-
getByText(placeholder, { includeHiddenElements: true }),
|
|
116
|
-
).toBeDefined();
|
|
117
|
-
expect(
|
|
118
|
-
getByText(labelText, { includeHiddenElements: true }).props.style,
|
|
119
|
-
).toContainEqual({
|
|
120
|
-
color: tokens["color-critical"],
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
83
|
it("renders a disabled Select", () => {
|
|
126
84
|
const labelText = "labelText";
|
|
127
85
|
const { getByText } = render(
|
|
@@ -166,156 +124,172 @@ describe("Select", () => {
|
|
|
166
124
|
).toBeDefined();
|
|
167
125
|
});
|
|
168
126
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
127
|
+
it("renders a Select with custom testID", () => {
|
|
128
|
+
const testID = "testID";
|
|
129
|
+
const { getByTestId } = render(
|
|
130
|
+
<Select onChange={onChange} testID={testID}>
|
|
131
|
+
<Option value={"1"}>1</Option>
|
|
132
|
+
<Option value={"2"}>2</Option>
|
|
133
|
+
</Select>,
|
|
134
|
+
);
|
|
177
135
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
136
|
+
expect(getByTestId(`ATL-${testID}-Select`)).toBeDefined();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("renders an accessibilityLabel if provided", () => {
|
|
140
|
+
const { getByLabelText } = render(
|
|
141
|
+
<Select
|
|
142
|
+
onChange={onChange}
|
|
143
|
+
label="label"
|
|
144
|
+
accessibilityLabel="accessibilityLabel"
|
|
145
|
+
>
|
|
146
|
+
<Option value={"1"}>1</Option>
|
|
147
|
+
<Option value={"2"}>2</Option>
|
|
148
|
+
</Select>,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(getByLabelText("accessibilityLabel")).toBeTruthy();
|
|
152
|
+
});
|
|
153
|
+
it("fires the onChange callback", () => {
|
|
154
|
+
const { getByTestId } = render(
|
|
155
|
+
<Select onChange={onChange} value={"2"}>
|
|
156
|
+
<Option value={"1"}>1</Option>
|
|
157
|
+
<Option value={"2"}>2</Option>
|
|
158
|
+
</Select>,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const select = getByTestId("ATL-Select").findByType(SelectInternalPicker);
|
|
162
|
+
expect(select).toBeTruthy();
|
|
163
|
+
fireEvent(select, "onChange", "1");
|
|
164
|
+
expect(onChange).toHaveBeenCalledWith("1");
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe("when Select is invalid", () => {
|
|
169
|
+
const labelText = "labelText";
|
|
170
|
+
|
|
171
|
+
it("renders an invalid Select", () => {
|
|
172
|
+
const { getByText } = render(
|
|
173
|
+
<Select onChange={onChange} invalid={true} label={labelText}>
|
|
174
|
+
<Option value={"1"}>1</Option>
|
|
175
|
+
<Option value={"2"}>2</Option>
|
|
176
|
+
</Select>,
|
|
177
|
+
);
|
|
178
|
+
expect(
|
|
179
|
+
getByText(labelText, { includeHiddenElements: true }).props.style,
|
|
180
|
+
).toContainEqual({
|
|
181
|
+
color: tokens["color-critical"],
|
|
182
182
|
});
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
185
|
+
it("renders an invalid Select with placeholder", () => {
|
|
186
|
+
const placeholder = "Place me in the holder";
|
|
187
|
+
const { getByText } = render(
|
|
188
|
+
<Select
|
|
189
|
+
label={labelText}
|
|
190
|
+
onChange={onChange}
|
|
191
|
+
invalid={true}
|
|
192
|
+
placeholder={placeholder}
|
|
193
|
+
>
|
|
194
|
+
<Option value={"1"}>1</Option>
|
|
195
|
+
<Option value={"2"}>2</Option>
|
|
196
|
+
</Select>,
|
|
197
|
+
);
|
|
193
198
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
expect(
|
|
200
|
+
getByText(placeholder, { includeHiddenElements: true }),
|
|
201
|
+
).toBeDefined();
|
|
202
|
+
expect(
|
|
203
|
+
getByText(labelText, { includeHiddenElements: true }).props.style,
|
|
204
|
+
).toContainEqual({
|
|
205
|
+
color: tokens["color-critical"],
|
|
197
206
|
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
198
209
|
|
|
199
|
-
|
|
200
|
-
|
|
210
|
+
describe("when validations are passed to the component", () => {
|
|
211
|
+
describe("validations fail", () => {
|
|
212
|
+
let tree: RenderAPI;
|
|
213
|
+
const labelText = "labelText";
|
|
214
|
+
const errorMsg = "Too short";
|
|
215
|
+
beforeEach(() => {
|
|
216
|
+
tree = render(
|
|
201
217
|
<Select
|
|
218
|
+
label={labelText}
|
|
202
219
|
onChange={onChange}
|
|
203
|
-
value={"
|
|
204
|
-
|
|
220
|
+
value={"Watermelon"}
|
|
221
|
+
validations={{
|
|
222
|
+
minLength: { value: 60, message: errorMsg },
|
|
223
|
+
}}
|
|
205
224
|
>
|
|
206
|
-
<Option value={"
|
|
207
|
-
<Option value={"
|
|
225
|
+
<Option value={"Apple"}>Apple</Option>
|
|
226
|
+
<Option value={"Watermelon"}>Watermelon</Option>
|
|
208
227
|
</Select>,
|
|
209
228
|
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("renders the error message when there is an error", async () => {
|
|
232
|
+
const select = tree
|
|
233
|
+
.getByTestId("ATL-Select")
|
|
234
|
+
.findByType(SelectInternalPicker);
|
|
235
|
+
fireEvent(select, "onChange", "Apple");
|
|
236
|
+
expect(
|
|
237
|
+
await tree.findByText(errorMsg, { includeHiddenElements: true }),
|
|
238
|
+
).toBeTruthy();
|
|
239
|
+
});
|
|
210
240
|
|
|
241
|
+
it("shows the invalid colours", async () => {
|
|
242
|
+
const select = tree
|
|
243
|
+
.getByTestId("ATL-Select")
|
|
244
|
+
.findByType(SelectInternalPicker);
|
|
245
|
+
fireEvent(select, "onChange", "Apple");
|
|
211
246
|
expect(
|
|
212
|
-
|
|
213
|
-
|
|
247
|
+
(await tree.findByText(labelText, { includeHiddenElements: true }))
|
|
248
|
+
.props.style,
|
|
249
|
+
).toContainEqual({
|
|
250
|
+
color: tokens["color-critical"],
|
|
251
|
+
});
|
|
214
252
|
});
|
|
215
253
|
});
|
|
216
254
|
|
|
217
|
-
describe("
|
|
218
|
-
|
|
219
|
-
|
|
255
|
+
describe("validations passes", () => {
|
|
256
|
+
let tree: RenderAPI;
|
|
257
|
+
const labelText = "labelText";
|
|
258
|
+
const errorMsg = "Not too short";
|
|
259
|
+
beforeEach(() => {
|
|
260
|
+
tree = render(
|
|
220
261
|
<Select
|
|
262
|
+
label={labelText}
|
|
221
263
|
onChange={onChange}
|
|
222
|
-
|
|
223
|
-
|
|
264
|
+
value={"Watermelon"}
|
|
265
|
+
validations={{
|
|
266
|
+
minLength: { value: 4, message: errorMsg },
|
|
267
|
+
}}
|
|
224
268
|
>
|
|
225
|
-
<Option value={"
|
|
226
|
-
<Option value={"
|
|
269
|
+
<Option value={"Apple"}>Apple</Option>
|
|
270
|
+
<Option value={"Watermelon"}>Watermelon</Option>
|
|
227
271
|
</Select>,
|
|
228
272
|
);
|
|
229
|
-
|
|
230
|
-
expect(getByLabelText("accessibilityLabel")).toBeTruthy();
|
|
231
273
|
});
|
|
232
|
-
});
|
|
233
274
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<Select
|
|
242
|
-
label={labelText}
|
|
243
|
-
onChange={onChange}
|
|
244
|
-
value={"Watermelon"}
|
|
245
|
-
validations={{
|
|
246
|
-
minLength: { value: 60, message: errorMsg },
|
|
247
|
-
}}
|
|
248
|
-
>
|
|
249
|
-
<Option value={"Apple"}>Apple</Option>
|
|
250
|
-
<Option value={"Watermelon"}>Watermelon</Option>
|
|
251
|
-
</Select>,
|
|
252
|
-
);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it("renders the error message when there is an error", async () => {
|
|
256
|
-
const select = tree
|
|
257
|
-
.getByTestId("ATL-Select")
|
|
258
|
-
.findByType(SelectInternalPicker);
|
|
259
|
-
fireEvent(select, "onChange", "Apple");
|
|
260
|
-
expect(
|
|
261
|
-
await tree.findByText(errorMsg, { includeHiddenElements: true }),
|
|
262
|
-
).toBeTruthy();
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("shows the invalid colours", async () => {
|
|
266
|
-
const select = tree
|
|
267
|
-
.getByTestId("ATL-Select")
|
|
268
|
-
.findByType(SelectInternalPicker);
|
|
269
|
-
fireEvent(select, "onChange", "Apple");
|
|
270
|
-
expect(
|
|
271
|
-
(await tree.findByText(labelText, { includeHiddenElements: true }))
|
|
272
|
-
.props.style,
|
|
273
|
-
).toContainEqual({
|
|
274
|
-
color: tokens["color-critical"],
|
|
275
|
-
});
|
|
276
|
-
});
|
|
275
|
+
it("does not render any error messages", () => {
|
|
276
|
+
const select = tree
|
|
277
|
+
.getByTestId("ATL-Select")
|
|
278
|
+
.findByType(SelectInternalPicker);
|
|
279
|
+
expect(select).toBeTruthy();
|
|
280
|
+
fireEvent(select, "onChange", "Apple");
|
|
281
|
+
expect(tree.queryByText(errorMsg)).toBeNull();
|
|
277
282
|
});
|
|
278
283
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
value={"Watermelon"}
|
|
289
|
-
validations={{
|
|
290
|
-
minLength: { value: 4, message: errorMsg },
|
|
291
|
-
}}
|
|
292
|
-
>
|
|
293
|
-
<Option value={"Apple"}>Apple</Option>
|
|
294
|
-
<Option value={"Watermelon"}>Watermelon</Option>
|
|
295
|
-
</Select>,
|
|
296
|
-
);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it("does not render any error messages", () => {
|
|
300
|
-
const select = tree
|
|
301
|
-
.getByTestId("ATL-Select")
|
|
302
|
-
.findByType(SelectInternalPicker);
|
|
303
|
-
expect(select).toBeTruthy();
|
|
304
|
-
fireEvent(select, "onChange", "Apple");
|
|
305
|
-
expect(tree.queryByText(errorMsg)).toBeNull();
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it("has non-critical colours", () => {
|
|
309
|
-
const select = tree
|
|
310
|
-
.getByTestId("ATL-Select")
|
|
311
|
-
.findByType(SelectInternalPicker);
|
|
312
|
-
fireEvent(select, "onChange", "Apple");
|
|
313
|
-
expect(
|
|
314
|
-
tree.getByText(labelText, { includeHiddenElements: true }).props
|
|
315
|
-
.style,
|
|
316
|
-
).toContainEqual({
|
|
317
|
-
color: tokens["color-text--secondary"],
|
|
318
|
-
});
|
|
284
|
+
it("has non-critical colours", () => {
|
|
285
|
+
const select = tree
|
|
286
|
+
.getByTestId("ATL-Select")
|
|
287
|
+
.findByType(SelectInternalPicker);
|
|
288
|
+
fireEvent(select, "onChange", "Apple");
|
|
289
|
+
expect(
|
|
290
|
+
tree.getByText(labelText, { includeHiddenElements: true }).props.style,
|
|
291
|
+
).toContainEqual({
|
|
292
|
+
color: tokens["color-text--secondary"],
|
|
319
293
|
});
|
|
320
294
|
});
|
|
321
295
|
});
|
package/src/Select/Select.tsx
CHANGED
|
@@ -88,6 +88,11 @@ export interface SelectProps {
|
|
|
88
88
|
* The validations that will mark this component as invalid
|
|
89
89
|
*/
|
|
90
90
|
readonly validations?: RegisterOptions;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Used to locate this view in end-to-end tests.
|
|
94
|
+
*/
|
|
95
|
+
readonly testID?: string;
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
export function Select({
|
|
@@ -103,6 +108,7 @@ export function Select({
|
|
|
103
108
|
validations,
|
|
104
109
|
accessibilityLabel,
|
|
105
110
|
name,
|
|
111
|
+
testID,
|
|
106
112
|
}: SelectProps): JSX.Element {
|
|
107
113
|
const { field, error } = useFormController({
|
|
108
114
|
name,
|
|
@@ -129,7 +135,7 @@ export function Select({
|
|
|
129
135
|
}}
|
|
130
136
|
>
|
|
131
137
|
<View
|
|
132
|
-
testID=
|
|
138
|
+
testID={getTestID(testID)}
|
|
133
139
|
accessible={true}
|
|
134
140
|
accessibilityLabel={getA11yLabel()}
|
|
135
141
|
accessibilityValue={{ text: getValue() }}
|
|
@@ -187,6 +193,7 @@ export function Select({
|
|
|
187
193
|
function getA11yLabel(): string | undefined {
|
|
188
194
|
let text = [accessibilityLabel || label, assistiveText];
|
|
189
195
|
text = text.filter(Boolean);
|
|
196
|
+
|
|
190
197
|
return text.join(", ");
|
|
191
198
|
}
|
|
192
199
|
|
|
@@ -219,6 +226,7 @@ export function Select({
|
|
|
219
226
|
const options = getOptions();
|
|
220
227
|
|
|
221
228
|
const activeValue = options.find(option => option.isActive);
|
|
229
|
+
|
|
222
230
|
return activeValue?.label || placeholder || t("Select.emptyValue");
|
|
223
231
|
}
|
|
224
232
|
}
|
|
@@ -229,9 +237,18 @@ function getTextVariation({
|
|
|
229
237
|
}: Pick<SelectProps, "invalid" | "disabled">): TextVariation {
|
|
230
238
|
if (invalid) return "error";
|
|
231
239
|
if (disabled) return "disabled";
|
|
240
|
+
|
|
232
241
|
return "subdued";
|
|
233
242
|
}
|
|
234
243
|
|
|
244
|
+
function getTestID(testID?: string): string {
|
|
245
|
+
if (testID) {
|
|
246
|
+
return `ATL-${testID}-Select`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return "ATL-Select";
|
|
250
|
+
}
|
|
251
|
+
|
|
235
252
|
export function Option({ children }: SelectOption): JSX.Element {
|
|
236
253
|
return <>{children}</>;
|
|
237
254
|
}
|