@jobber/components-native 0.8.0 → 0.10.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.
Files changed (63) hide show
  1. package/dist/src/Card/Card.js +38 -0
  2. package/dist/src/Card/Card.style.js +31 -0
  3. package/dist/src/Card/components/InternalCardHeader.js +14 -0
  4. package/dist/src/Card/components/InternalCardHeader.style.js +16 -0
  5. package/dist/src/Card/components/index.js +1 -0
  6. package/dist/src/Card/index.js +1 -0
  7. package/dist/src/InputFieldWrapper/CommonInputStyles.style.js +33 -0
  8. package/dist/src/InputFieldWrapper/InputFieldWrapper.js +88 -0
  9. package/dist/src/InputFieldWrapper/InputFieldWrapper.style.js +79 -0
  10. package/dist/src/InputFieldWrapper/components/ClearAction/ClearAction.js +12 -0
  11. package/dist/src/InputFieldWrapper/components/ClearAction/ClearAction.style.js +25 -0
  12. package/dist/src/InputFieldWrapper/components/ClearAction/index.js +2 -0
  13. package/dist/src/InputFieldWrapper/components/ClearAction/messages.js +8 -0
  14. package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +34 -0
  15. package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +35 -0
  16. package/dist/src/InputFieldWrapper/hooks/useShowClear.js +15 -0
  17. package/dist/src/InputFieldWrapper/index.js +3 -0
  18. package/dist/src/index.js +2 -0
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/dist/types/src/Card/Card.d.ts +40 -0
  21. package/dist/types/src/Card/Card.style.d.ts +56 -0
  22. package/dist/types/src/Card/components/InternalCardHeader.d.ts +9 -0
  23. package/dist/types/src/Card/components/InternalCardHeader.style.d.ts +14 -0
  24. package/dist/types/src/Card/components/index.d.ts +1 -0
  25. package/dist/types/src/Card/index.d.ts +2 -0
  26. package/dist/types/src/InputFieldWrapper/CommonInputStyles.style.d.ts +30 -0
  27. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +63 -0
  28. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.style.d.ts +82 -0
  29. package/dist/types/src/InputFieldWrapper/components/ClearAction/ClearAction.d.ts +10 -0
  30. package/dist/types/src/InputFieldWrapper/components/ClearAction/ClearAction.style.d.ts +22 -0
  31. package/dist/types/src/InputFieldWrapper/components/ClearAction/index.d.ts +2 -0
  32. package/dist/types/src/InputFieldWrapper/components/ClearAction/messages.d.ts +7 -0
  33. package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +23 -0
  34. package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +25 -0
  35. package/dist/types/src/InputFieldWrapper/hooks/useShowClear.d.ts +10 -0
  36. package/dist/types/src/InputFieldWrapper/index.d.ts +4 -0
  37. package/dist/types/src/index.d.ts +2 -0
  38. package/package.json +4 -2
  39. package/src/Card/Card.style.ts +46 -0
  40. package/src/Card/Card.test.tsx +128 -0
  41. package/src/Card/Card.tsx +145 -0
  42. package/src/Card/components/InternalCardHeader.style.ts +19 -0
  43. package/src/Card/components/InternalCardHeader.test.tsx +31 -0
  44. package/src/Card/components/InternalCardHeader.tsx +41 -0
  45. package/src/Card/components/index.ts +1 -0
  46. package/src/Card/index.ts +2 -0
  47. package/src/InputFieldWrapper/CommonInputStyles.style.ts +37 -0
  48. package/src/InputFieldWrapper/InputFieldWrapper.style.ts +93 -0
  49. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +243 -0
  50. package/src/InputFieldWrapper/InputFieldWrapper.tsx +317 -0
  51. package/src/InputFieldWrapper/components/ClearAction/ClearAction.style.ts +27 -0
  52. package/src/InputFieldWrapper/components/ClearAction/ClearAction.test.tsx +15 -0
  53. package/src/InputFieldWrapper/components/ClearAction/ClearAction.tsx +32 -0
  54. package/src/InputFieldWrapper/components/ClearAction/index.ts +2 -0
  55. package/src/InputFieldWrapper/components/ClearAction/messages.ts +9 -0
  56. package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +221 -0
  57. package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +104 -0
  58. package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +101 -0
  59. package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +113 -0
  60. package/src/InputFieldWrapper/hooks/useShowClear.test.ts +158 -0
  61. package/src/InputFieldWrapper/hooks/useShowClear.ts +31 -0
  62. package/src/InputFieldWrapper/index.ts +4 -0
  63. package/src/index.ts +2 -0
@@ -0,0 +1,104 @@
1
+ import React from "react";
2
+ import {
3
+ // eslint-disable-next-line no-restricted-imports
4
+ Text as RNText,
5
+ StyleProp,
6
+ TextStyle,
7
+ View,
8
+ ViewStyle,
9
+ } from "react-native";
10
+ import { IconNames } from "@jobber/design";
11
+ import { Icon } from "../../../Icon";
12
+ import { Text } from "../../../Text";
13
+ import { tokens } from "../../../utils/design";
14
+ import { typographyStyles } from "../../../Typography";
15
+ import { styles } from "../../InputFieldWrapper.style";
16
+
17
+ export interface PrefixLabelProps {
18
+ focused: boolean;
19
+ disabled?: boolean;
20
+ hasMiniLabel: boolean;
21
+ inputInvalid: boolean;
22
+ label: string;
23
+ styleOverride?: StyleProp<TextStyle>;
24
+ }
25
+
26
+ export const prefixLabelTestId = "ATL-InputFieldWrapper-PrefixLabel";
27
+ export const prefixIconTestId = "ATL-InputFieldWrapper-PrefixIcon";
28
+
29
+ export function PrefixLabel({
30
+ focused,
31
+ disabled,
32
+ hasMiniLabel,
33
+ inputInvalid,
34
+ label,
35
+ styleOverride,
36
+ }: PrefixLabelProps): JSX.Element {
37
+ return (
38
+ <View
39
+ style={[
40
+ styles.fieldAffix,
41
+ focused && styles.inputFocused,
42
+ inputInvalid && styles.inputInvalid,
43
+ ]}
44
+ testID={prefixLabelTestId}
45
+ >
46
+ <View
47
+ style={[styles.prefixLabel, hasMiniLabel && styles.fieldAffixMiniLabel]}
48
+ >
49
+ {!styleOverride ? (
50
+ <Text variation={disabled ? "disabled" : "base"}>{label}</Text>
51
+ ) : (
52
+ <RNText
53
+ allowFontScaling={true}
54
+ style={[
55
+ typographyStyles.baseRegularRegular,
56
+ typographyStyles.base,
57
+ typographyStyles.defaultSize,
58
+ disabled ? typographyStyles.subdued : typographyStyles.base,
59
+ styleOverride,
60
+ ]}
61
+ >
62
+ {label}
63
+ </RNText>
64
+ )}
65
+ </View>
66
+ </View>
67
+ );
68
+ }
69
+
70
+ export interface PrefixIconProps {
71
+ focused: boolean;
72
+ disabled?: boolean;
73
+ hasMiniLabel: boolean;
74
+ inputInvalid?: boolean;
75
+ icon: IconNames;
76
+ styleOverride?: StyleProp<ViewStyle>;
77
+ }
78
+
79
+ export function PrefixIcon({
80
+ focused,
81
+ disabled,
82
+ inputInvalid,
83
+ icon,
84
+ }: PrefixIconProps): JSX.Element {
85
+ return (
86
+ <View
87
+ testID={prefixIconTestId}
88
+ style={[
89
+ styles.fieldAffix,
90
+ focused && styles.inputFocused,
91
+ inputInvalid && styles.inputInvalid,
92
+ ]}
93
+ >
94
+ <View style={styles.prefixIcon}>
95
+ <Icon
96
+ customColor={
97
+ disabled ? tokens["color-disabled"] : tokens["color-greyBlue"]
98
+ }
99
+ name={icon}
100
+ />
101
+ </View>
102
+ </View>
103
+ );
104
+ }
@@ -0,0 +1,101 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+ import { TextStyle } from "react-native";
4
+ import {
5
+ SuffixIcon,
6
+ SuffixIconProps,
7
+ SuffixLabel,
8
+ SuffixLabelProps,
9
+ } from "./Suffix";
10
+ import { typographyStyles } from "../../../Typography";
11
+
12
+ const mockLabel = "$";
13
+
14
+ function setupLabel({
15
+ disabled = false,
16
+ focused = false,
17
+ hasMiniLabel = false,
18
+ inputInvalid = false,
19
+ label = mockLabel,
20
+ styleOverride,
21
+ }: Partial<SuffixLabelProps>) {
22
+ return render(
23
+ <SuffixLabel
24
+ disabled={disabled}
25
+ focused={focused}
26
+ hasMiniLabel={hasMiniLabel}
27
+ inputInvalid={inputInvalid}
28
+ label={label}
29
+ styleOverride={styleOverride}
30
+ />,
31
+ );
32
+ }
33
+
34
+ function setupIcon({
35
+ disabled = false,
36
+ focused = false,
37
+ hasMiniLabel = false,
38
+ inputInvalid = false,
39
+ icon = "invoice",
40
+ }: Partial<SuffixIconProps>) {
41
+ return render(
42
+ <SuffixIcon
43
+ disabled={disabled}
44
+ focused={focused}
45
+ hasMiniLabel={hasMiniLabel}
46
+ inputInvalid={inputInvalid}
47
+ icon={icon}
48
+ />,
49
+ );
50
+ }
51
+
52
+ describe("Suffix", () => {
53
+ it("renders a suffix label when specified", () => {
54
+ const { getByText } = setupLabel({});
55
+ expect(getByText(mockLabel)).toBeDefined();
56
+ });
57
+
58
+ it("renders a suffix icon when specified", () => {
59
+ const { getByTestId } = setupIcon({});
60
+ expect(getByTestId("invoice")).toBeDefined();
61
+ });
62
+
63
+ describe("Custom Label Style", () => {
64
+ it("uses default styling when no style override is provided", () => {
65
+ const { getByText } = setupLabel({});
66
+ const suffix = getByText(mockLabel);
67
+ const expectedStyle = [
68
+ typographyStyles.baseRegularRegular,
69
+ typographyStyles.base,
70
+ typographyStyles.startAlign,
71
+ typographyStyles.defaultSize,
72
+ typographyStyles.baseLetterSpacing,
73
+ ];
74
+ expect(suffix.props.style).toEqual(expectedStyle);
75
+ });
76
+
77
+ it("uses style override for label when provided", () => {
78
+ const styleOverride = {
79
+ fontSize: 50,
80
+ color: "purple",
81
+ };
82
+ const { getByText } = setupLabel({ styleOverride });
83
+ const suffix = getByText(mockLabel);
84
+ const flattenedStyle = suffix.props.style.reduce(
85
+ (style: TextStyle, additionalStyles: TextStyle) => ({
86
+ ...style,
87
+ ...additionalStyles,
88
+ }),
89
+ {},
90
+ );
91
+
92
+ const expectedStyle = {
93
+ ...typographyStyles.baseRegularRegular,
94
+ lineHeight: typographyStyles.defaultSize.lineHeight,
95
+ ...styleOverride,
96
+ };
97
+
98
+ expect(flattenedStyle).toEqual(expectedStyle);
99
+ });
100
+ });
101
+ });
@@ -0,0 +1,113 @@
1
+ import React from "react";
2
+ import {
3
+ Pressable,
4
+ // eslint-disable-next-line no-restricted-imports
5
+ Text as RNText,
6
+ StyleProp,
7
+ TextStyle,
8
+ View,
9
+ } from "react-native";
10
+ import { IconNames } from "@jobber/design";
11
+ import { tokens } from "../../../utils/design";
12
+ import { Icon } from "../../../Icon";
13
+ import { Text } from "../../../Text";
14
+ import { typographyStyles } from "../../../Typography";
15
+ import { styles } from "../../InputFieldWrapper.style";
16
+
17
+ export interface SuffixLabelProps {
18
+ focused: boolean;
19
+ disabled?: boolean;
20
+ hasMiniLabel: boolean;
21
+ inputInvalid?: boolean;
22
+ label: string;
23
+ hasLeftMargin?: boolean;
24
+ styleOverride?: StyleProp<TextStyle>;
25
+ }
26
+
27
+ export const suffixLabelTestId = "ATL-InputFieldWrapper-SuffixLabel";
28
+ export const suffixIconTestId = "ATL-InputFieldWrapper-SuffixIcon";
29
+
30
+ export function SuffixLabel({
31
+ focused,
32
+ disabled,
33
+ hasMiniLabel,
34
+ inputInvalid,
35
+ label,
36
+ hasLeftMargin = true,
37
+ styleOverride,
38
+ }: SuffixLabelProps): JSX.Element {
39
+ return (
40
+ <View
41
+ testID={suffixLabelTestId}
42
+ style={[
43
+ styles.fieldAffix,
44
+ focused && styles.inputFocused,
45
+ inputInvalid && styles.inputInvalid,
46
+ hasLeftMargin && styles.suffixLabelMargin,
47
+ ]}
48
+ >
49
+ <View
50
+ style={[styles.suffixLabel, hasMiniLabel && styles.fieldAffixMiniLabel]}
51
+ >
52
+ {!styleOverride ? (
53
+ <Text variation={disabled ? "disabled" : "base"}>{label}</Text>
54
+ ) : (
55
+ <RNText
56
+ allowFontScaling={true}
57
+ style={[
58
+ typographyStyles.baseRegularRegular,
59
+ typographyStyles.base,
60
+ typographyStyles.defaultSize,
61
+ disabled ? typographyStyles.subdued : typographyStyles.base,
62
+ styleOverride,
63
+ ]}
64
+ >
65
+ {label}
66
+ </RNText>
67
+ )}
68
+ </View>
69
+ </View>
70
+ );
71
+ }
72
+
73
+ export interface SuffixIconProps {
74
+ focused: boolean;
75
+ disabled?: boolean;
76
+ hasMiniLabel: boolean;
77
+ inputInvalid?: boolean;
78
+ icon: IconNames;
79
+ hasLeftMargin?: boolean;
80
+ onPress?: () => void;
81
+ }
82
+
83
+ export function SuffixIcon({
84
+ focused,
85
+ disabled,
86
+ inputInvalid,
87
+ icon,
88
+ hasLeftMargin = false,
89
+ onPress,
90
+ }: SuffixIconProps): JSX.Element {
91
+ return (
92
+ <View
93
+ testID={suffixIconTestId}
94
+ style={[
95
+ styles.fieldAffix,
96
+ focused && styles.inputFocused,
97
+ inputInvalid && styles.inputInvalid,
98
+ ]}
99
+ >
100
+ <Pressable
101
+ style={[styles.suffixIcon, hasLeftMargin && styles.suffixIconMargin]}
102
+ onPress={onPress}
103
+ >
104
+ <Icon
105
+ customColor={
106
+ disabled ? tokens["color-disabled"] : tokens["color-greyBlue"]
107
+ }
108
+ name={icon}
109
+ />
110
+ </Pressable>
111
+ </View>
112
+ );
113
+ }
@@ -0,0 +1,158 @@
1
+ import { useShowClear } from "./useShowClear";
2
+ import { Clearable } from "..";
3
+
4
+ interface UseShowClearParameters {
5
+ clearable: Clearable;
6
+ hasValue: boolean;
7
+ focused: boolean;
8
+ multiline: boolean;
9
+ disabled: boolean;
10
+ expected: boolean;
11
+ }
12
+
13
+ describe("useShowClear", () => {
14
+ describe.each<UseShowClearParameters>([
15
+ {
16
+ clearable: "always",
17
+ hasValue: true,
18
+ focused: false,
19
+ multiline: false,
20
+ disabled: false,
21
+ expected: true,
22
+ },
23
+ {
24
+ clearable: "always",
25
+ hasValue: true,
26
+ focused: true,
27
+ multiline: false,
28
+ disabled: false,
29
+ expected: true,
30
+ },
31
+ {
32
+ clearable: "always",
33
+ hasValue: true,
34
+ focused: false,
35
+ multiline: false,
36
+ disabled: true,
37
+ expected: false,
38
+ },
39
+ {
40
+ clearable: "always",
41
+ hasValue: false,
42
+ focused: false,
43
+ multiline: false,
44
+ disabled: true,
45
+ expected: false,
46
+ },
47
+ {
48
+ clearable: "while-editing",
49
+ hasValue: true,
50
+ focused: false,
51
+ multiline: false,
52
+ disabled: false,
53
+ expected: false,
54
+ },
55
+ {
56
+ clearable: "while-editing",
57
+ hasValue: true,
58
+ focused: true,
59
+ multiline: false,
60
+ disabled: false,
61
+ expected: true,
62
+ },
63
+ {
64
+ clearable: "while-editing",
65
+ hasValue: false,
66
+ focused: false,
67
+ multiline: false,
68
+ disabled: true,
69
+ expected: false,
70
+ },
71
+ {
72
+ clearable: "while-editing",
73
+ hasValue: true,
74
+ focused: false,
75
+ multiline: false,
76
+ disabled: true,
77
+ expected: false,
78
+ },
79
+ {
80
+ clearable: "never",
81
+ hasValue: true,
82
+ focused: false,
83
+ multiline: false,
84
+ disabled: false,
85
+ expected: false,
86
+ },
87
+ {
88
+ clearable: "never",
89
+ hasValue: true,
90
+ focused: true,
91
+ multiline: false,
92
+ disabled: false,
93
+ expected: false,
94
+ },
95
+ {
96
+ clearable: "never",
97
+ hasValue: false,
98
+ focused: false,
99
+ multiline: false,
100
+ disabled: true,
101
+ expected: false,
102
+ },
103
+ {
104
+ clearable: "never",
105
+ hasValue: true,
106
+ focused: false,
107
+ multiline: false,
108
+ disabled: true,
109
+ expected: false,
110
+ },
111
+ ])(
112
+ "%j",
113
+ ({
114
+ clearable,
115
+ hasValue,
116
+ focused,
117
+ multiline,
118
+ disabled,
119
+ expected,
120
+ }: UseShowClearParameters) => {
121
+ it(`returns ${expected}`, () => {
122
+ expect(
123
+ useShowClear({ clearable, multiline, focused, hasValue, disabled }),
124
+ ).toEqual(expected);
125
+ });
126
+ },
127
+ );
128
+
129
+ it("throws an error if multiline is true and clearable isn't never", () => {
130
+ expect(() => {
131
+ useShowClear({
132
+ clearable: "always",
133
+ multiline: true,
134
+ focused: true,
135
+ hasValue: true,
136
+ disabled: false,
137
+ });
138
+ }).toThrow();
139
+ expect(() => {
140
+ useShowClear({
141
+ clearable: "while-editing",
142
+ multiline: true,
143
+ focused: true,
144
+ hasValue: true,
145
+ disabled: false,
146
+ });
147
+ }).toThrow();
148
+ expect(() => {
149
+ useShowClear({
150
+ clearable: "never",
151
+ multiline: true,
152
+ focused: true,
153
+ hasValue: true,
154
+ disabled: false,
155
+ });
156
+ }).not.toThrow();
157
+ });
158
+ });
@@ -0,0 +1,31 @@
1
+ import { Clearable } from "../InputFieldWrapper";
2
+
3
+ interface UseShowClearParameters {
4
+ clearable: Clearable;
5
+ multiline: boolean;
6
+ focused: boolean;
7
+ hasValue: boolean;
8
+ disabled?: boolean;
9
+ }
10
+
11
+ export function useShowClear({
12
+ clearable,
13
+ multiline,
14
+ focused,
15
+ hasValue,
16
+ disabled = false,
17
+ }: UseShowClearParameters): boolean | undefined {
18
+ if (multiline && clearable !== "never") {
19
+ throw new Error("Multiline inputs can not be clearable");
20
+ }
21
+ // Do not show if there is no value
22
+ if (!hasValue || clearable === "never" || disabled) {
23
+ return false;
24
+ }
25
+ switch (clearable) {
26
+ case "while-editing":
27
+ return focused;
28
+ case "always":
29
+ return true;
30
+ }
31
+ }
@@ -0,0 +1,4 @@
1
+ export { commonInputStyles } from "./CommonInputStyles.style";
2
+ export { InputFieldWrapper } from "./InputFieldWrapper";
3
+ export type { InputFieldWrapperProps, Clearable } from "./InputFieldWrapper";
4
+ export { useShowClear } from "./hooks/useShowClear";
package/src/index.ts CHANGED
@@ -6,5 +6,7 @@ export * from "./ErrorMessageWrapper";
6
6
  export * from "./ActionLabel";
7
7
  export * from "./Content";
8
8
  export * from "./ActivityIndicator";
9
+ export * from "./Card";
9
10
  export * from "./StatusLabel";
10
11
  export * from "./AtlantisContext";
12
+ export * from "./InputFieldWrapper";