@jobber/components-native 0.28.0 → 0.29.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.
@@ -1 +1,4 @@
1
1
  export { BottomSheet } from "./BottomSheet";
2
+ export type { BottomSheetRef } from "./BottomSheet";
3
+ export type { BottomSheetOptionProps } from "./components/BottomSheetOption";
4
+ export { BottomSheetOption } from "./components/BottomSheetOption";
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { InputTextProps, InputTextRef } from "../InputText";
3
+ export declare const InputPassword: React.ForwardRefExoticComponent<InputPasswordProps & React.RefAttributes<InputTextRef>>;
4
+ interface InputPasswordProps extends Omit<InputTextProps, "keyboard" | "secureTextEntry" | "textContentType" | "clearable"> {
5
+ /**
6
+ * Determines if InputPassword uses privacy eye suffix
7
+ *
8
+ * @default true
9
+ */
10
+ usePrivacyEye?: boolean;
11
+ }
12
+ export {};
@@ -0,0 +1 @@
1
+ export { InputPassword } from "./InputPassword";
@@ -0,0 +1,7 @@
1
+ export declare const messages: {
2
+ passwordRequired: {
3
+ id: string;
4
+ defaultMessage: string;
5
+ description: string;
6
+ };
7
+ };
@@ -16,6 +16,7 @@ export * from "./Heading";
16
16
  export * from "./Icon";
17
17
  export * from "./IconButton";
18
18
  export * from "./InputFieldWrapper";
19
+ export * from "./InputPassword";
19
20
  export * from "./InputPressable";
20
21
  export * from "./InputSearch";
21
22
  export * from "./InputText";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.28.0",
3
+ "version": "0.29.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": "dcb391abbe27fca411efd97be1c7128cde9d749f"
60
+ "gitHead": "14a9d66cb036dd7cf163774ff1fe980642296b8e"
61
61
  }
@@ -1 +1,4 @@
1
1
  export { BottomSheet } from "./BottomSheet";
2
+ export type { BottomSheetRef } from "./BottomSheet";
3
+ export type { BottomSheetOptionProps } from "./components/BottomSheetOption";
4
+ export { BottomSheetOption } from "./components/BottomSheetOption";
@@ -0,0 +1,92 @@
1
+ import React from "react";
2
+ import { fireEvent, render, waitFor } from "@testing-library/react-native";
3
+ import { InputPassword } from "./InputPassword";
4
+ import { messages } from "./messages";
5
+ import { InputFieldWrapperProps } from "../InputFieldWrapper";
6
+
7
+ const MockInputFieldWrapper = jest.fn();
8
+ jest.mock("../InputFieldWrapper", () => ({
9
+ ...jest.requireActual("../InputFieldWrapper"),
10
+ InputFieldWrapper: function Mock(props: InputFieldWrapperProps) {
11
+ MockInputFieldWrapper(props);
12
+ return jest.requireActual("../InputFieldWrapper").InputFieldWrapper(props);
13
+ },
14
+ }));
15
+ describe("InputPassword", () => {
16
+ it("renders an InputPassword", () => {
17
+ const value = "password";
18
+ const { getByDisplayValue } = render(<InputPassword value={value} />);
19
+ expect(getByDisplayValue(value)).toBeTruthy();
20
+ });
21
+
22
+ it("displays the validation message when an invalid password is entered", async () => {
23
+ const a11yLabel = "InputPasswordTest";
24
+ const { getByText, getByLabelText } = render(
25
+ <InputPassword value="" accessibilityLabel={a11yLabel} />,
26
+ );
27
+
28
+ await waitFor(() => {
29
+ fireEvent(getByLabelText(a11yLabel), "blur");
30
+ });
31
+
32
+ expect(
33
+ getByText(messages.passwordRequired.defaultMessage, {
34
+ includeHiddenElements: true,
35
+ }),
36
+ ).toBeDefined();
37
+ });
38
+
39
+ describe("InputPassword gets the expected props", () => {
40
+ it("renders an invalid InputPassword", () => {
41
+ const props = { invalid: true };
42
+ render(<InputPassword {...props} />);
43
+ expect(MockInputFieldWrapper).toHaveBeenCalledWith(
44
+ expect.objectContaining(props),
45
+ );
46
+ });
47
+ });
48
+
49
+ describe("Privacy eye", () => {
50
+ it("renders when usePrivaryEye is true", () => {
51
+ const testId = "eye";
52
+ const { getByTestId } = render(
53
+ <InputPassword
54
+ value="password"
55
+ suffix={{
56
+ icon: testId,
57
+ }}
58
+ />,
59
+ );
60
+
61
+ expect(getByTestId("eye")).toBeDefined();
62
+ });
63
+
64
+ it("does not render when usePrivaryEye is false", () => {
65
+ const testId = "eye";
66
+ const { queryByTestId } = render(
67
+ <InputPassword
68
+ value="password"
69
+ usePrivacyEye={false}
70
+ suffix={{
71
+ icon: testId,
72
+ }}
73
+ />,
74
+ );
75
+
76
+ expect(queryByTestId("eye")).toBeNull();
77
+ });
78
+
79
+ it("obscures the password by default", () => {
80
+ const { getByDisplayValue } = render(
81
+ <InputPassword
82
+ value="password"
83
+ suffix={{
84
+ icon: "eye",
85
+ }}
86
+ />,
87
+ );
88
+
89
+ expect(getByDisplayValue("password").props.secureTextEntry).toBeTruthy();
90
+ });
91
+ });
92
+ });
@@ -0,0 +1,68 @@
1
+ import React, { Ref, forwardRef, useState } from "react";
2
+ import { useIntl } from "react-intl";
3
+ import { IconNames } from "@jobber/design";
4
+ import { messages } from "./messages";
5
+ import { InputText, InputTextProps, InputTextRef } from "../InputText";
6
+
7
+ export const InputPassword = forwardRef(InputPasswordInternal);
8
+
9
+ interface InputPasswordProps
10
+ extends Omit<
11
+ InputTextProps,
12
+ "keyboard" | "secureTextEntry" | "textContentType" | "clearable"
13
+ > {
14
+ /**
15
+ * Determines if InputPassword uses privacy eye suffix
16
+ *
17
+ * @default true
18
+ */
19
+ usePrivacyEye?: boolean;
20
+ }
21
+
22
+ function InputPasswordInternal(
23
+ { usePrivacyEye = true, ...props }: InputPasswordProps,
24
+ ref: Ref<InputTextRef>,
25
+ ): JSX.Element {
26
+ const { formatMessage } = useIntl();
27
+ const [passwordHidden, setPasswordHidden] = useState(true);
28
+ const [privacyEye, setPrivacyEye] = useState<IconNames>("eye");
29
+
30
+ const handleOnPress = () => {
31
+ if (privacyEye === "eye") {
32
+ setPrivacyEye("eyeCrossed");
33
+ setPasswordHidden(false);
34
+ } else if (privacyEye === "eyeCrossed") {
35
+ setPrivacyEye("eye");
36
+ setPasswordHidden(true);
37
+ }
38
+ };
39
+
40
+ const privacyEyeSuffix = () => {
41
+ if (usePrivacyEye === true) {
42
+ return {
43
+ icon: privacyEye,
44
+ onPress: handleOnPress,
45
+ };
46
+ }
47
+ return undefined;
48
+ };
49
+
50
+ return (
51
+ <InputText
52
+ {...props}
53
+ ref={ref}
54
+ keyboard="default"
55
+ secureTextEntry={passwordHidden}
56
+ textContentType="password"
57
+ clearable="never"
58
+ suffix={privacyEyeSuffix()}
59
+ validations={{
60
+ required: {
61
+ value: true,
62
+ message: formatMessage(messages.passwordRequired),
63
+ },
64
+ ...props.validations,
65
+ }}
66
+ />
67
+ );
68
+ }
@@ -0,0 +1 @@
1
+ export { InputPassword } from "./InputPassword";
@@ -0,0 +1,9 @@
1
+ import { defineMessages } from "react-intl";
2
+
3
+ export const messages = defineMessages({
4
+ passwordRequired: {
5
+ id: "passwordRequired",
6
+ defaultMessage: "Enter a password",
7
+ description: "Validation error message for missing password",
8
+ },
9
+ });
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ export * from "./Heading";
16
16
  export * from "./Icon";
17
17
  export * from "./IconButton";
18
18
  export * from "./InputFieldWrapper";
19
+ export * from "./InputPassword";
19
20
  export * from "./InputPressable";
20
21
  export * from "./InputSearch";
21
22
  export * from "./InputText";