@jobber/components-native 0.27.0 → 0.28.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 { InputTextProps, InputTextRef } from "../InputText";
3
+ export declare const InputSearch: React.ForwardRefExoticComponent<InputSearchProps & React.RefAttributes<InputTextRef>>;
4
+ export interface InputSearchProps extends Pick<InputTextProps, "accessibilityHint" | "accessibilityLabel" | "autoFocus" | "placeholder" | "prefix"> {
5
+ /**
6
+ * A callback function that handles the update of the new value of the property value.
7
+ */
8
+ readonly onChange: (newValue: string) => void;
9
+ /**
10
+ * A callback function that handles the API call to search the value. This is where the
11
+ * wait value is applied to the debounce function to give a delay in each input and API request.
12
+ */
13
+ readonly onDebouncedChange: (searchValue: string) => void;
14
+ /**
15
+ * Set the component to a given value
16
+ */
17
+ readonly value: string;
18
+ /**
19
+ * A numeric value to represents the milliseconds in delaying the function to populate
20
+ * the data source when the 'value' changed.
21
+ * @default 300
22
+ */
23
+ readonly wait?: number;
24
+ }
@@ -0,0 +1,6 @@
1
+ export declare const styles: {
2
+ container: {
3
+ width: string;
4
+ paddingHorizontal: number;
5
+ };
6
+ };
@@ -0,0 +1,7 @@
1
+ /// <reference types="react" />
2
+ interface FilterButtonProps {
3
+ readonly accessibilityLabel: string;
4
+ readonly onClick: () => void;
5
+ }
6
+ export declare function FilterButton({ accessibilityLabel, onClick, }: FilterButtonProps): JSX.Element;
7
+ export {};
@@ -0,0 +1,3 @@
1
+ export { InputSearch } from "./InputSearch";
2
+ export type { InputSearchProps } from "./InputSearch";
3
+ export { FilterButton } from "./components/FilterButton";
@@ -17,6 +17,7 @@ export * from "./Icon";
17
17
  export * from "./IconButton";
18
18
  export * from "./InputFieldWrapper";
19
19
  export * from "./InputPressable";
20
+ export * from "./InputSearch";
20
21
  export * from "./InputText";
21
22
  export * from "./TextList";
22
23
  export * from "./ProgressBar";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/src/index.js",
6
6
  "module": "dist/src/index.js",
@@ -23,6 +23,7 @@
23
23
  "dependencies": {
24
24
  "@jobber/design": "^0.41.3",
25
25
  "lodash.chunk": "^4.2.0",
26
+ "lodash.debounce": "^4.0.8",
26
27
  "lodash.identity": "^3.0.0",
27
28
  "react-hook-form": "^7.30.0",
28
29
  "react-intl": "^6.4.2",
@@ -42,6 +43,7 @@
42
43
  "@testing-library/react-hooks": "^7.0.2",
43
44
  "@testing-library/react-native": "^12.0.1",
44
45
  "@types/lodash.chunk": "^4.2.7",
46
+ "@types/lodash.debounce": "^4.0.7",
45
47
  "@types/lodash.identity": "^3.0.7",
46
48
  "@types/react": "^18.0.28",
47
49
  "@types/react-native": "^0.71.6",
@@ -55,5 +57,5 @@
55
57
  "react": "^18",
56
58
  "react-native": ">=0.69.2"
57
59
  },
58
- "gitHead": "4cac408646d885509b88823f5186df1a9534b641"
60
+ "gitHead": "dcb391abbe27fca411efd97be1c7128cde9d749f"
59
61
  }
@@ -0,0 +1,9 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../utils/design";
3
+
4
+ export const styles = StyleSheet.create({
5
+ container: {
6
+ width: "100%",
7
+ paddingHorizontal: tokens["space-base"],
8
+ },
9
+ });
@@ -0,0 +1,77 @@
1
+ import React from "react";
2
+ import {
3
+ cleanup,
4
+ fireEvent,
5
+ render,
6
+ waitFor,
7
+ } from "@testing-library/react-native";
8
+ import { useIntl } from "react-intl";
9
+ import { InputSearch } from "./InputSearch";
10
+ import { messages as clearMessages } from "../InputFieldWrapper/components/ClearAction";
11
+
12
+ const accessibilityLabelSearch = "Search";
13
+ const accessibilityHint = "Search clients, properties, quotes, etc.";
14
+ const mockOnChange = jest.fn();
15
+ const mockOnDebouncedChange = jest.fn();
16
+ let searchValue: string;
17
+
18
+ beforeEach(() => {
19
+ mockOnChange.mockReset();
20
+ mockOnDebouncedChange.mockReset();
21
+ searchValue = "";
22
+ });
23
+ afterEach(cleanup);
24
+
25
+ function setup() {
26
+ return render(
27
+ <InputSearch
28
+ accessibilityHint={accessibilityHint}
29
+ accessibilityLabel={accessibilityLabelSearch}
30
+ placeholder={accessibilityHint}
31
+ value={searchValue}
32
+ onChange={mockOnChange}
33
+ onDebouncedChange={mockOnDebouncedChange}
34
+ />,
35
+ );
36
+ }
37
+
38
+ describe("InputSearch", () => {
39
+ it("renders the search input text", () => {
40
+ const { getByHintText, getByLabelText } = setup();
41
+
42
+ expect(getByLabelText(accessibilityLabelSearch)).toBeDefined();
43
+ expect(getByHintText(accessibilityHint)).toBeDefined();
44
+ });
45
+
46
+ it("passes the correct search value to onChange when the value changed", () => {
47
+ const { getByLabelText } = setup();
48
+ const inputSearch = getByLabelText(accessibilityLabelSearch);
49
+
50
+ fireEvent.changeText(inputSearch, "Apollo Client");
51
+ expect(mockOnChange).toHaveBeenCalledWith("Apollo Client");
52
+ });
53
+
54
+ it("passes the correcet search value to onDebouncedChange when the value changed", async () => {
55
+ const { getByLabelText } = setup();
56
+ const inputSearch = getByLabelText(accessibilityLabelSearch);
57
+
58
+ await waitFor(() => {
59
+ fireEvent.changeText(inputSearch, "Update Apollo Client");
60
+ expect(mockOnDebouncedChange).toHaveBeenCalled();
61
+ });
62
+ });
63
+
64
+ it("clears the search value when the clear button is pressed", () => {
65
+ searchValue = "test search value";
66
+ const { formatMessage } = useIntl();
67
+ const { getByLabelText } = setup();
68
+
69
+ fireEvent(getByLabelText(accessibilityLabelSearch), "focus");
70
+
71
+ const inputSearchClearButton = getByLabelText(
72
+ formatMessage(clearMessages.clearTextLabel),
73
+ );
74
+ fireEvent.press(inputSearchClearButton);
75
+ expect(mockOnChange).toHaveBeenCalledWith("");
76
+ });
77
+ });
@@ -0,0 +1,71 @@
1
+ import React, { Ref, forwardRef, useEffect } from "react";
2
+ import { View } from "react-native";
3
+ import debounce from "lodash.debounce";
4
+ import { styles } from "./InputSearch.style";
5
+ import { InputText, InputTextProps, InputTextRef } from "../InputText";
6
+
7
+ export const InputSearch = forwardRef(SearchInputInternal);
8
+
9
+ export interface InputSearchProps
10
+ extends Pick<
11
+ InputTextProps,
12
+ | "accessibilityHint"
13
+ | "accessibilityLabel"
14
+ | "autoFocus"
15
+ | "placeholder"
16
+ | "prefix"
17
+ > {
18
+ /**
19
+ * A callback function that handles the update of the new value of the property value.
20
+ */
21
+ readonly onChange: (newValue: string) => void;
22
+
23
+ /**
24
+ * A callback function that handles the API call to search the value. This is where the
25
+ * wait value is applied to the debounce function to give a delay in each input and API request.
26
+ */
27
+ readonly onDebouncedChange: (searchValue: string) => void;
28
+
29
+ /**
30
+ * Set the component to a given value
31
+ */
32
+ readonly value: string;
33
+
34
+ /**
35
+ * A numeric value to represents the milliseconds in delaying the function to populate
36
+ * the data source when the 'value' changed.
37
+ * @default 300
38
+ */
39
+ readonly wait?: number;
40
+ }
41
+
42
+ function SearchInputInternal(
43
+ {
44
+ onChange,
45
+ onDebouncedChange,
46
+ wait = 300,
47
+ value,
48
+ ...inputTextProps
49
+ }: InputSearchProps,
50
+ ref: Ref<InputTextRef>,
51
+ ) {
52
+ const delayedSearch = debounce(onDebouncedChange, wait);
53
+ const handleChange = (newValue = "") => onChange(newValue);
54
+
55
+ useEffect(() => {
56
+ delayedSearch(value);
57
+ return delayedSearch.cancel;
58
+ }, [value, delayedSearch]);
59
+
60
+ return (
61
+ <View style={styles.container}>
62
+ <InputText
63
+ {...inputTextProps}
64
+ autoCorrect={false}
65
+ onChangeText={handleChange}
66
+ ref={ref}
67
+ value={value}
68
+ />
69
+ </View>
70
+ );
71
+ }
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import { Keyboard } from "react-native";
3
+ import { IconButton } from "../../IconButton";
4
+
5
+ interface FilterButtonProps {
6
+ readonly accessibilityLabel: string;
7
+ readonly onClick: () => void;
8
+ }
9
+
10
+ export function FilterButton({
11
+ accessibilityLabel,
12
+ onClick,
13
+ }: FilterButtonProps): JSX.Element {
14
+ return (
15
+ <IconButton
16
+ onPress={() => {
17
+ onClick();
18
+ Keyboard.dismiss();
19
+ }}
20
+ name="filter"
21
+ accessibilityLabel={accessibilityLabel}
22
+ />
23
+ );
24
+ }
@@ -0,0 +1,3 @@
1
+ export { InputSearch } from "./InputSearch";
2
+ export type { InputSearchProps } from "./InputSearch";
3
+ export { FilterButton } from "./components/FilterButton";
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from "./Icon";
17
17
  export * from "./IconButton";
18
18
  export * from "./InputFieldWrapper";
19
19
  export * from "./InputPressable";
20
+ export * from "./InputSearch";
20
21
  export * from "./InputText";
21
22
  export * from "./TextList";
22
23
  export * from "./ProgressBar";