@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,145 @@
1
+ import React, { ReactNode } from "react";
2
+ import {
3
+ LayoutChangeEvent,
4
+ Pressable,
5
+ PressableStateCallbackType,
6
+ View,
7
+ } from "react-native";
8
+ import { IconNames } from "@jobber/design";
9
+ import { XOR } from "ts-xor";
10
+ import { styles } from "./Card.style";
11
+ // eslint-disable-next-line import/no-internal-modules
12
+ import { InternalCardHeader } from "./components/InternalCardHeader";
13
+ import { ErrorMessageWrapper } from "../ErrorMessageWrapper";
14
+ import { ActionLabel } from "../ActionLabel";
15
+ import { Typography } from "../Typography";
16
+ import { Icon } from "../Icon";
17
+
18
+ interface CardProps {
19
+ /**
20
+ * @deprecated Use <ActionItem /> with the title and onPress properties instead
21
+ */
22
+ readonly header?: HeaderProps;
23
+
24
+ readonly footer?: FooterProps;
25
+ readonly children?: ReactNode;
26
+ readonly reportCardHeight?: (height: number) => void;
27
+ readonly testID?: string;
28
+ readonly error?: string;
29
+ readonly elevation?: elevationProp;
30
+ }
31
+
32
+ type elevationProp = "none" | "low" | "base" | "high";
33
+
34
+ export type HeaderProps = HeaderCommonProps & HeaderActionProps;
35
+
36
+ interface FooterProps {
37
+ readonly onPress: () => void;
38
+ readonly title: string;
39
+ }
40
+
41
+ interface HeaderCommonProps {
42
+ readonly title: string;
43
+ }
44
+
45
+ type HeaderActionProps =
46
+ | {
47
+ readonly onPress?: never;
48
+ readonly actionItem?: never;
49
+ }
50
+ | {
51
+ readonly onPress: () => void;
52
+ readonly actionItem: ActionItem;
53
+ };
54
+
55
+ interface IconAction {
56
+ readonly iconName: IconNames;
57
+ }
58
+
59
+ interface ButtonAction {
60
+ readonly label: string;
61
+ }
62
+
63
+ export type ActionItem = XOR<IconAction, ButtonAction>;
64
+
65
+ function getElevationStyle(elevation: elevationProp) {
66
+ if (elevation === "none") {
67
+ return undefined;
68
+ }
69
+ return styles[`${elevation}Elevation`];
70
+ }
71
+
72
+ export function Card({
73
+ header,
74
+ footer,
75
+ children,
76
+ reportCardHeight: onCardHeightChange,
77
+ testID = "card",
78
+ error,
79
+ elevation = "none",
80
+ }: CardProps): JSX.Element {
81
+ return (
82
+ <ErrorMessageWrapper message={error} wrapFor="card">
83
+ <View
84
+ onLayout={handleLayoutChange}
85
+ style={[styles.container, getElevationStyle(elevation)]}
86
+ testID={testID}
87
+ >
88
+ {header && (
89
+ <>
90
+ <InternalCardHeader
91
+ onPress={header.onPress}
92
+ testID={`${testID}Header`}
93
+ collapsable={!!children}
94
+ >
95
+ <View style={styles.headerTitle}>
96
+ <Typography
97
+ color="heading"
98
+ fontFamily="base"
99
+ fontWeight="bold"
100
+ size="default"
101
+ lineHeight="base"
102
+ accessibilityRole="header"
103
+ >
104
+ {header.title}
105
+ </Typography>
106
+ </View>
107
+ <View style={styles.actionItem}>
108
+ {!!header.actionItem?.label && (
109
+ <View style={styles.actionLabel}>
110
+ <ActionLabel type="cardTitle">
111
+ {header.actionItem.label}
112
+ </ActionLabel>
113
+ </View>
114
+ )}
115
+
116
+ {header.actionItem?.iconName && (
117
+ <Icon name={header.actionItem.iconName} color="interactive" />
118
+ )}
119
+ </View>
120
+ </InternalCardHeader>
121
+ </>
122
+ )}
123
+ {children}
124
+ {footer && (
125
+ <Pressable
126
+ testID={`${testID}Footer`}
127
+ onPress={footer.onPress}
128
+ style={({ pressed }: PressableStateCallbackType) => [
129
+ styles.footer,
130
+ pressed && styles.pressed,
131
+ ]}
132
+ accessibilityRole={"button"}
133
+ >
134
+ <ActionLabel>{footer.title}</ActionLabel>
135
+ </Pressable>
136
+ )}
137
+ </View>
138
+ </ErrorMessageWrapper>
139
+ );
140
+
141
+ function handleLayoutChange(event: LayoutChangeEvent) {
142
+ const { height } = event.nativeEvent.layout;
143
+ onCardHeightChange?.(height);
144
+ }
145
+ }
@@ -0,0 +1,19 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../../utils/design";
3
+
4
+ export const styles = StyleSheet.create({
5
+ header: {
6
+ flexDirection: "row",
7
+ alignItems: "flex-start",
8
+ paddingTop: tokens["space-small"] + tokens["space-smaller"],
9
+ paddingHorizontal: tokens["space-base"],
10
+ },
11
+
12
+ pressed: {
13
+ opacity: tokens["opacity-pressed"],
14
+ },
15
+
16
+ noChildren: {
17
+ paddingBottom: tokens["space-small"] + tokens["space-smaller"],
18
+ },
19
+ });
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import { fireEvent, render } from "@testing-library/react-native";
3
+ import { InternalCardHeader } from "./InternalCardHeader";
4
+
5
+ it("should render a pressable header", () => {
6
+ const handlePress = jest.fn();
7
+ const screen = render(
8
+ <InternalCardHeader collapsable onPress={handlePress} testID="cardHeader">
9
+ 🍩
10
+ </InternalCardHeader>,
11
+ );
12
+
13
+ const header = screen.getByTestId("cardHeader");
14
+ expect(header).toBeDefined();
15
+ expect(header.props.accessibilityRole).toBe("button");
16
+
17
+ fireEvent.press(header);
18
+ expect(handlePress).toHaveBeenCalled();
19
+ });
20
+
21
+ it("should render an un-pressable header", () => {
22
+ const screen = render(
23
+ <InternalCardHeader collapsable testID="cardHeader">
24
+ 🌞
25
+ </InternalCardHeader>,
26
+ );
27
+
28
+ const header = screen.getByTestId("cardHeader");
29
+ expect(header).toBeDefined();
30
+ expect(header.props.onPress).toBeUndefined();
31
+ });
@@ -0,0 +1,41 @@
1
+ import React, { ReactNode } from "react";
2
+ import { Pressable, View } from "react-native";
3
+ import { styles } from "./InternalCardHeader.style";
4
+
5
+ interface InternalCardHeaderProps {
6
+ readonly children: ReactNode[] | ReactNode;
7
+ readonly onPress?: () => void;
8
+ readonly testID?: string;
9
+ readonly collapsable: boolean;
10
+ }
11
+
12
+ export function InternalCardHeader({
13
+ onPress,
14
+ children,
15
+ testID,
16
+ collapsable,
17
+ }: InternalCardHeaderProps): JSX.Element {
18
+ const conditionalChildStyling = collapsable ? undefined : styles.noChildren;
19
+ if (onPress) {
20
+ return (
21
+ <Pressable
22
+ testID={testID}
23
+ onPress={onPress}
24
+ style={({ pressed }) => [
25
+ styles.header,
26
+ pressed && styles.pressed,
27
+ conditionalChildStyling,
28
+ ]}
29
+ accessibilityRole={"button"}
30
+ >
31
+ {children}
32
+ </Pressable>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <View testID={testID} style={[styles.header, conditionalChildStyling]}>
38
+ {children}
39
+ </View>
40
+ );
41
+ }
@@ -0,0 +1 @@
1
+ export { InternalCardHeader } from "./InternalCardHeader";
@@ -0,0 +1,2 @@
1
+ export { Card } from "./Card";
2
+ export type { ActionItem, HeaderProps } from "./Card";
@@ -0,0 +1,37 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { typographyStyles } from "../Typography";
3
+ import { tokens } from "../utils/design";
4
+
5
+ export const commonInputStyles = StyleSheet.create({
6
+ input: {
7
+ width: "100%",
8
+ flexShrink: 1,
9
+ flexGrow: 1,
10
+ color: tokens["color-text"],
11
+ fontFamily: typographyStyles.baseRegularRegular.fontFamily,
12
+ fontSize: typographyStyles.defaultSize.fontSize,
13
+ letterSpacing: typographyStyles.baseLetterSpacing.letterSpacing,
14
+ minHeight: tokens["space-largest"],
15
+ padding: 0,
16
+ },
17
+
18
+ inputEmpty: {
19
+ paddingTop: 0,
20
+ },
21
+
22
+ inputDisabled: {
23
+ color: typographyStyles.disabled.color,
24
+ },
25
+
26
+ container: {
27
+ marginVertical: tokens["space-smaller"],
28
+ backgroundColor: tokens["color-surface"],
29
+ minHeight: tokens["space-largest"],
30
+ flexDirection: "row",
31
+ justifyContent: "space-between",
32
+ width: "100%",
33
+ borderColor: tokens["color-grey"],
34
+ borderStyle: "solid",
35
+ borderBottomWidth: tokens["border-base"],
36
+ },
37
+ });
@@ -0,0 +1,93 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { commonInputStyles } from "./CommonInputStyles.style";
3
+ import { tokens } from "../utils/design";
4
+ import { typographyStyles } from "../Typography";
5
+
6
+ export const styles = StyleSheet.create({
7
+ container: StyleSheet.flatten([commonInputStyles.container]),
8
+
9
+ inputContainer: {
10
+ flexDirection: "row",
11
+ flex: 1,
12
+ justifyContent: "flex-start",
13
+ },
14
+
15
+ inputFocused: {
16
+ borderColor: tokens["color-interactive"],
17
+ },
18
+
19
+ inputInvalid: {
20
+ borderColor: tokens["color-critical"],
21
+ },
22
+
23
+ label: {
24
+ // for placeholder
25
+ position: "absolute",
26
+ top: typographyStyles.smallSize.fontSize,
27
+ right: 0,
28
+ bottom: 0,
29
+ left: 0,
30
+ },
31
+
32
+ miniLabel: {
33
+ top: 0,
34
+ paddingTop: tokens["space-smallest"],
35
+ backgroundColor: tokens["color-surface"],
36
+ maxHeight:
37
+ (typographyStyles.smallSize.lineHeight || 0) + tokens["space-smaller"],
38
+ zIndex: 1,
39
+ },
40
+ // Prevents the miniLabel from cutting off the ClearAction button
41
+ miniLabelShowClearAction: {
42
+ backgroundColor: "transparent",
43
+ },
44
+
45
+ disabled: {
46
+ backgroundColor: tokens["color-disabled--secondary"],
47
+ borderTopLeftRadius: tokens["radius-large"],
48
+ borderTopRightRadius: tokens["radius-large"],
49
+ },
50
+
51
+ fieldAffix: {
52
+ flexDirection: "row",
53
+ },
54
+
55
+ fieldAffixMiniLabel: {
56
+ paddingTop: 0,
57
+ // @ts-expect-error tsc-ci
58
+ top: typographyStyles.smallSize.fontSize / 2,
59
+ right: 0,
60
+ bottom: 0,
61
+ left: 0,
62
+ },
63
+
64
+ prefixIcon: {
65
+ justifyContent: "center",
66
+ paddingRight: tokens["space-small"],
67
+ },
68
+
69
+ prefixLabel: {
70
+ justifyContent: "center",
71
+ paddingTop: tokens["space-minuscule"],
72
+ paddingRight: tokens["space-minuscule"],
73
+ },
74
+
75
+ suffixIcon: {
76
+ justifyContent: "center",
77
+ },
78
+
79
+ suffixLabel: {
80
+ justifyContent: "center",
81
+ paddingTop: tokens["space-minuscule"],
82
+ },
83
+ suffixIconMargin: {
84
+ marginLeft: tokens["space-small"] + tokens["space-smaller"],
85
+ },
86
+ suffixLabelMargin: {
87
+ marginLeft: tokens["space-smallest"],
88
+ },
89
+ inputEndContainer: {
90
+ flexDirection: "row",
91
+ zIndex: 1,
92
+ },
93
+ });
@@ -0,0 +1,243 @@
1
+ import React from "react";
2
+ import { RenderAPI, fireEvent, render } from "@testing-library/react-native";
3
+ // eslint-disable-next-line no-restricted-imports
4
+ import { Text, ViewStyle } from "react-native";
5
+ import { useIntl } from "react-intl";
6
+ import {
7
+ InputFieldWrapper,
8
+ InputFieldWrapperProps,
9
+ commonInputStyles,
10
+ } from ".";
11
+ import { messages as clearMessages } from "./components/ClearAction";
12
+ import { styles } from "./InputFieldWrapper.style";
13
+ import { typographyStyles } from "../Typography";
14
+
15
+ const mockLabel = { label: "$" };
16
+
17
+ type InputFieldWrapperTestProps = Omit<InputFieldWrapperProps, "children">;
18
+
19
+ function renderInputFieldWrapper(props: InputFieldWrapperTestProps): RenderAPI {
20
+ return render(
21
+ <InputFieldWrapper {...props}>
22
+ <Text>Test</Text>
23
+ </InputFieldWrapper>,
24
+ );
25
+ }
26
+
27
+ function renderWithPrefixLabel(hasValue: boolean): RenderAPI {
28
+ return renderInputFieldWrapper({ prefix: mockLabel, hasValue });
29
+ }
30
+
31
+ function renderWithSuffixLabel(hasValue: boolean): RenderAPI {
32
+ return renderInputFieldWrapper({ suffix: mockLabel, hasValue });
33
+ }
34
+
35
+ describe("InputFieldWrapper", () => {
36
+ it("renders an invalid InputFieldWrapper", () => {
37
+ const { getByTestId } = renderInputFieldWrapper({ invalid: true });
38
+
39
+ expect(getByTestId("ATL-InputFieldWrapper").props.style).toContainEqual(
40
+ styles.inputInvalid,
41
+ );
42
+ });
43
+
44
+ it("renders an invalid InputFieldWrapper with non-empty string", () => {
45
+ const invalid = "invalid string test";
46
+ const { getByText, getByTestId } = renderInputFieldWrapper({
47
+ invalid,
48
+ });
49
+
50
+ expect(getByTestId("ATL-InputFieldWrapper").props.style).toContainEqual(
51
+ styles.inputInvalid,
52
+ );
53
+ expect(getByText(invalid, { includeHiddenElements: true })).toBeDefined();
54
+ });
55
+
56
+ it("renders a valid InputFieldWrapper with empty string", () => {
57
+ const { getByTestId } = renderInputFieldWrapper({ invalid: "" });
58
+
59
+ expect(getByTestId("ATL-InputFieldWrapper").props.style).not.toContainEqual(
60
+ styles.inputInvalid,
61
+ );
62
+ });
63
+
64
+ it("renders a disabled InputFieldWrapper", () => {
65
+ const { getByTestId } = renderInputFieldWrapper({ disabled: true });
66
+
67
+ expect(getByTestId("ATL-InputFieldWrapper").props.style).toContainEqual({
68
+ backgroundColor: "rgb(225, 225, 225)",
69
+ borderTopLeftRadius: 4,
70
+ borderTopRightRadius: 4,
71
+ });
72
+ });
73
+
74
+ it("renders a InputFieldWrapper with placeholder", () => {
75
+ const placeholder = "test";
76
+ const { getByText } = renderInputFieldWrapper({ placeholder });
77
+
78
+ expect(
79
+ getByText(placeholder, { includeHiddenElements: true }),
80
+ ).toBeTruthy();
81
+ });
82
+
83
+ it("renders a prefix icon when specified", () => {
84
+ const { getByTestId } = renderInputFieldWrapper({
85
+ prefix: { icon: "invoice" },
86
+ });
87
+ expect(getByTestId("invoice")).toBeDefined();
88
+ });
89
+
90
+ it("renders a suffix icon when specified", () => {
91
+ const { getByTestId } = renderInputFieldWrapper({
92
+ suffix: { icon: "invoice" },
93
+ });
94
+ expect(getByTestId("invoice")).toBeDefined();
95
+ });
96
+
97
+ describe("when hasValue", () => {
98
+ it("renders a prefix label", () => {
99
+ const { getByText } = renderWithPrefixLabel(true);
100
+ expect(getByText(mockLabel.label)).toBeDefined();
101
+ });
102
+
103
+ it("renders a suffix label", () => {
104
+ const { getByText } = renderWithSuffixLabel(true);
105
+ expect(getByText(mockLabel.label)).toBeDefined();
106
+ });
107
+ });
108
+
109
+ describe("when not hasValue", () => {
110
+ it("does not render a prefix label", () => {
111
+ const { queryByText } = renderWithPrefixLabel(false);
112
+ expect(queryByText(mockLabel.label)).toBeNull();
113
+ });
114
+
115
+ it("does not render a suffix label", () => {
116
+ const { queryByText } = renderWithSuffixLabel(false);
117
+ expect(queryByText(mockLabel.label)).toBeNull();
118
+ });
119
+ });
120
+
121
+ describe("ClearAction", () => {
122
+ it("renders the Clear Action Button when showClearAction is true", () => {
123
+ const { formatMessage } = useIntl();
124
+ const { getByLabelText } = renderInputFieldWrapper({
125
+ showClearAction: true,
126
+ });
127
+ expect(
128
+ getByLabelText(formatMessage(clearMessages.clearTextLabel)),
129
+ ).toBeDefined();
130
+ });
131
+
132
+ it("does not render the Clear Action Button when showClearAction is false", () => {
133
+ const { formatMessage } = useIntl();
134
+ const { queryByLabelText } = renderInputFieldWrapper({
135
+ showClearAction: false,
136
+ });
137
+ expect(
138
+ queryByLabelText(formatMessage(clearMessages.clearTextLabel)),
139
+ ).toBeNull();
140
+ });
141
+
142
+ it("calls onClear when the Clear Action button is pressed", () => {
143
+ const { formatMessage } = useIntl();
144
+ const onClear = jest.fn();
145
+ const { getByLabelText } = renderInputFieldWrapper({
146
+ showClearAction: true,
147
+ onClear: onClear,
148
+ });
149
+ fireEvent(
150
+ getByLabelText(formatMessage(clearMessages.clearTextLabel)),
151
+ "press",
152
+ );
153
+ expect(onClear).toHaveBeenCalled();
154
+ });
155
+ });
156
+
157
+ describe("Custom Styling", () => {
158
+ it("has the default container styling when no style override is provided", () => {
159
+ const { getByTestId } = renderInputFieldWrapper({});
160
+ const container = getByTestId("ATL-InputFieldWrapper");
161
+ expect(container.props.style).toContainEqual({
162
+ ...commonInputStyles.container,
163
+ });
164
+ });
165
+
166
+ it("shows the container style override when provided", () => {
167
+ const styleOverride = {
168
+ container: {
169
+ backgroundColor: "purple",
170
+ width: "50%",
171
+ },
172
+ };
173
+ const { getByTestId } = renderInputFieldWrapper({
174
+ styleOverride,
175
+ });
176
+ const container = getByTestId("ATL-InputFieldWrapper");
177
+
178
+ const flattenedStyle = container.props.style.reduce(
179
+ (style: ViewStyle, additionalStyles: ViewStyle) => ({
180
+ ...style,
181
+ ...additionalStyles,
182
+ }),
183
+ {},
184
+ );
185
+ expect(flattenedStyle.backgroundColor).toEqual(
186
+ styleOverride.container.backgroundColor,
187
+ );
188
+ expect(flattenedStyle.width).toEqual(styleOverride.container.width);
189
+ expect(flattenedStyle.borderColor).toEqual(
190
+ commonInputStyles.container.borderColor,
191
+ );
192
+ expect(flattenedStyle.marginVertical).toEqual(
193
+ commonInputStyles.container.marginVertical,
194
+ );
195
+ });
196
+ it("has the default placeholder styling when no style override is provided", () => {
197
+ const placeholderText = "myplaceholder";
198
+ const { getByText } = renderInputFieldWrapper({
199
+ placeholder: placeholderText,
200
+ focused: true,
201
+ });
202
+ const placeholder = getByText(placeholderText, {
203
+ includeHiddenElements: true,
204
+ });
205
+
206
+ expect(placeholder.props.style).toContainEqual(
207
+ typographyStyles.interactive,
208
+ );
209
+ expect(placeholder.props.style).toContainEqual(
210
+ typographyStyles.defaultSize,
211
+ );
212
+ });
213
+
214
+ it("shows the placeholder style override when provided", () => {
215
+ const placeholderText = "myplaceholder";
216
+ const styleOverride = {
217
+ placeholderText: {
218
+ color: "purple",
219
+ fontSize: 30,
220
+ },
221
+ };
222
+ const { getByText } = renderInputFieldWrapper({
223
+ placeholder: placeholderText,
224
+ styleOverride,
225
+ });
226
+ const placeholder = getByText(placeholderText, {
227
+ includeHiddenElements: true,
228
+ });
229
+
230
+ const flattenedStyle = placeholder.props.style.reduce(
231
+ (style: ViewStyle, additionalStyles: ViewStyle) => ({
232
+ ...style,
233
+ ...additionalStyles,
234
+ }),
235
+ {},
236
+ );
237
+ expect(flattenedStyle.color).toEqual(styleOverride.placeholderText.color);
238
+ expect(flattenedStyle.fontSize).toEqual(
239
+ styleOverride.placeholderText.fontSize,
240
+ );
241
+ });
242
+ });
243
+ });