@jobber/components-native 0.29.0 → 0.30.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.
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import { ControllerRenderProps, FieldError, FieldValues, RegisterOptions } from "react-hook-form";
3
+ interface FormFieldProps<T> {
4
+ /**
5
+ * Name of the field.
6
+ */
7
+ name: string;
8
+ /**
9
+ * The initial value of the form field.
10
+ */
11
+ readonly defaultValue?: T;
12
+ /**
13
+ * Children to render.
14
+ */
15
+ children: (field: ControllerRenderProps<FieldValues, string>, error?: FieldError) => React.ReactNode;
16
+ /**
17
+ * Rules for returning an error when validations are violated.
18
+ * WARNING: This component needs to be nested inside a FormProvider
19
+ * for validations to work.
20
+ */
21
+ readonly validations?: RegisterOptions;
22
+ }
23
+ export declare function FormField<T>({ name, children, defaultValue: value, validations, }: FormFieldProps<T>): JSX.Element;
24
+ export {};
@@ -0,0 +1 @@
1
+ export { FormField } from "./FormField";
@@ -12,6 +12,7 @@ export * from "./Divider";
12
12
  export * from "./EmptyState";
13
13
  export * from "./ErrorMessageWrapper";
14
14
  export * from "./Flex";
15
+ export * from "./FormField";
15
16
  export * from "./Heading";
16
17
  export * from "./Icon";
17
18
  export * from "./IconButton";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/src/index.js",
6
6
  "module": "dist/src/index.js",
@@ -57,5 +57,5 @@
57
57
  "react": "^18",
58
58
  "react-native": ">=0.69.2"
59
59
  },
60
- "gitHead": "14a9d66cb036dd7cf163774ff1fe980642296b8e"
60
+ "gitHead": "362d0d3914aa440bcbb40f9eed388d447cd205e0"
61
61
  }
@@ -0,0 +1,135 @@
1
+ import React from "react";
2
+ import { fireEvent, render, waitFor } from "@testing-library/react-native";
3
+ import { FormProvider, useForm } from "react-hook-form";
4
+ import { Button } from "react-native";
5
+ import { FormField } from ".";
6
+ import { InputText } from "../InputText";
7
+
8
+ const mockOnSubmit = jest.fn();
9
+ const inputAccessibilityLabel = "textInput";
10
+ const saveButtonText = "Save Me";
11
+
12
+ describe("when a component is wrapped in a FormField within a Form", () => {
13
+ function SimpleFormWithProvider({ children }) {
14
+ const formMethods = useForm();
15
+
16
+ return (
17
+ <FormProvider {...formMethods}>
18
+ {children}
19
+ <Button
20
+ onPress={formMethods.handleSubmit(values => mockOnSubmit(values))}
21
+ title={saveButtonText}
22
+ accessibilityLabel={saveButtonText}
23
+ />
24
+ </FormProvider>
25
+ );
26
+ }
27
+
28
+ describe("when the component's value is changed", () => {
29
+ it("updates the form value for that component", async () => {
30
+ const defaultInputValue = "Sonkey";
31
+ const newInputValue = "Donic";
32
+
33
+ const { getByLabelText } = render(
34
+ <SimpleFormWithProvider>
35
+ <FormField name="formTextInput" defaultValue={defaultInputValue}>
36
+ {field => {
37
+ return (
38
+ <InputText
39
+ name={field.name}
40
+ accessibilityLabel={inputAccessibilityLabel}
41
+ value={field.value}
42
+ onChangeText={field.onChange}
43
+ />
44
+ );
45
+ }}
46
+ </FormField>
47
+ </SimpleFormWithProvider>,
48
+ );
49
+ const textInput = getByLabelText(inputAccessibilityLabel);
50
+ fireEvent.changeText(textInput, newInputValue);
51
+
52
+ const saveButton = getByLabelText(saveButtonText);
53
+ await waitFor(() => {
54
+ fireEvent.press(saveButton);
55
+ });
56
+
57
+ expect(mockOnSubmit).toHaveBeenCalledWith({
58
+ formTextInput: newInputValue,
59
+ });
60
+ });
61
+ });
62
+
63
+ describe("when the validations of the component are violated", () => {
64
+ it("returns an error from the FormField wrapper", async () => {
65
+ const newInputValue = "Exceeding 5 characters";
66
+ const maxLengthErrorMessage = "error: max length exceeded";
67
+
68
+ const { getByLabelText, getAllByText } = render(
69
+ <SimpleFormWithProvider>
70
+ <FormField
71
+ name="formTextInput"
72
+ validations={{
73
+ maxLength: {
74
+ value: 5,
75
+ message: maxLengthErrorMessage,
76
+ },
77
+ }}
78
+ >
79
+ {field => {
80
+ return (
81
+ <>
82
+ <InputText
83
+ name={field.name}
84
+ accessibilityLabel={inputAccessibilityLabel}
85
+ value={field.value}
86
+ onChangeText={field.onChange}
87
+ />
88
+ </>
89
+ );
90
+ }}
91
+ </FormField>
92
+ </SimpleFormWithProvider>,
93
+ );
94
+
95
+ const textInput = getByLabelText(inputAccessibilityLabel);
96
+ fireEvent.changeText(textInput, newInputValue);
97
+
98
+ const saveButton = getByLabelText(saveButtonText);
99
+ await waitFor(() => {
100
+ fireEvent.press(saveButton);
101
+ });
102
+
103
+ expect(
104
+ getAllByText(maxLengthErrorMessage, { includeHiddenElements: true }),
105
+ ).toHaveLength(1);
106
+ });
107
+ });
108
+ });
109
+
110
+ describe("when a component is not in a Form with a Form ProviderField within a Form", () => {
111
+ describe("when the component's value is changed", () => {
112
+ it("updates the form value for that component", async () => {
113
+ const defaultInputValue = "Sonkey";
114
+ const newInputValue = "Donic";
115
+
116
+ const { getByDisplayValue, getByLabelText } = render(
117
+ <FormField name="formTextInput" defaultValue={defaultInputValue}>
118
+ {field => {
119
+ return (
120
+ <InputText
121
+ name={field.name}
122
+ accessibilityLabel={inputAccessibilityLabel}
123
+ value={field.value}
124
+ onChangeText={field.onChange}
125
+ />
126
+ );
127
+ }}
128
+ </FormField>,
129
+ );
130
+ const textInput = getByLabelText(inputAccessibilityLabel);
131
+ fireEvent.changeText(textInput, newInputValue);
132
+ expect(getByDisplayValue(newInputValue)).toBeDefined();
133
+ });
134
+ });
135
+ });
@@ -0,0 +1,50 @@
1
+ import React from "react";
2
+ import {
3
+ ControllerRenderProps,
4
+ FieldError,
5
+ FieldValues,
6
+ RegisterOptions,
7
+ } from "react-hook-form";
8
+ import { useFormController } from "../hooks";
9
+
10
+ interface FormFieldProps<T> {
11
+ /**
12
+ * Name of the field.
13
+ */
14
+ name: string;
15
+
16
+ /**
17
+ * The initial value of the form field.
18
+ */
19
+ readonly defaultValue?: T;
20
+
21
+ /**
22
+ * Children to render.
23
+ */
24
+ children: (
25
+ field: ControllerRenderProps<FieldValues, string>,
26
+ error?: FieldError,
27
+ ) => React.ReactNode;
28
+
29
+ /**
30
+ * Rules for returning an error when validations are violated.
31
+ * WARNING: This component needs to be nested inside a FormProvider
32
+ * for validations to work.
33
+ */
34
+ readonly validations?: RegisterOptions;
35
+ }
36
+
37
+ export function FormField<T>({
38
+ name,
39
+ children,
40
+ defaultValue: value,
41
+ validations,
42
+ }: FormFieldProps<T>): JSX.Element {
43
+ const { error, field } = useFormController({
44
+ name,
45
+ value,
46
+ validations,
47
+ });
48
+
49
+ return <>{children({ ...field }, error)}</>;
50
+ }
@@ -0,0 +1 @@
1
+ export { FormField } from "./FormField";
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export * from "./Divider";
12
12
  export * from "./EmptyState";
13
13
  export * from "./ErrorMessageWrapper";
14
14
  export * from "./Flex";
15
+ export * from "./FormField";
15
16
  export * from "./Heading";
16
17
  export * from "./Icon";
17
18
  export * from "./IconButton";