@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,317 @@
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 { FieldError } from "react-hook-form";
11
+ import { IconNames } from "@jobber/design";
12
+ import { styles } from "./InputFieldWrapper.style";
13
+ import { PrefixIcon, PrefixLabel } from "./components/Prefix/Prefix";
14
+ import { SuffixIcon, SuffixLabel } from "./components/Suffix/Suffix";
15
+ import { ClearAction } from "./components/ClearAction";
16
+ import { ErrorMessageWrapper } from "../ErrorMessageWrapper";
17
+ import { TextVariation, typographyStyles } from "../Typography";
18
+ import { Text } from "../Text";
19
+
20
+ export type Clearable = "never" | "while-editing" | "always";
21
+
22
+ export interface InputFieldStyleOverride {
23
+ prefixLabel?: StyleProp<TextStyle>;
24
+ suffixLabel?: StyleProp<TextStyle>;
25
+ container?: StyleProp<ViewStyle>;
26
+ placeholderText?: StyleProp<TextStyle>;
27
+ }
28
+ export interface InputFieldWrapperProps {
29
+ /**
30
+ * Highlights the field red and shows message below (if string) to indicate an error
31
+ */
32
+ readonly invalid?: boolean | string;
33
+
34
+ /**
35
+ * Disable the input
36
+ */
37
+ readonly disabled?: boolean;
38
+
39
+ /**
40
+ * Hint text that goes above the value once the field is filled out
41
+ */
42
+ readonly placeholder?: string;
43
+
44
+ /**
45
+ * Text that goes below the input to help the user understand the input
46
+ */
47
+ readonly assistiveText?: string;
48
+
49
+ readonly hasMiniLabel?: boolean;
50
+
51
+ readonly hasValue?: boolean;
52
+
53
+ /**
54
+ * Symbol to display before the text input
55
+ */
56
+ readonly prefix?: {
57
+ icon?: IconNames;
58
+ label?: string;
59
+ };
60
+
61
+ /**
62
+ * Symbol to display after the text input
63
+ */
64
+ readonly suffix?: {
65
+ icon?: IconNames;
66
+ label?: string;
67
+ onPress?: () => void;
68
+ };
69
+
70
+ readonly error?: FieldError;
71
+
72
+ readonly focused?: boolean;
73
+
74
+ readonly children: React.ReactNode;
75
+
76
+ /**
77
+ * Adds the ClearAction that will call the onClear handler when pressed
78
+ */
79
+ readonly showClearAction?: boolean;
80
+
81
+ /**
82
+ * Callback called when the user clicks the ClearAction button. Should clear the value passed.
83
+ * To disallow clearing set the clearable prop to never
84
+ */
85
+ readonly onClear?: () => void;
86
+
87
+ /**
88
+ * Custom styling to override default style of the input field
89
+ */
90
+ readonly styleOverride?: InputFieldStyleOverride;
91
+ }
92
+
93
+ export function InputFieldWrapper({
94
+ invalid,
95
+ disabled,
96
+ placeholder,
97
+ assistiveText,
98
+ prefix,
99
+ suffix,
100
+ hasMiniLabel = false,
101
+ hasValue = false,
102
+ error,
103
+ focused = false,
104
+ children,
105
+ onClear,
106
+ showClearAction = false,
107
+ styleOverride,
108
+ }: InputFieldWrapperProps): JSX.Element {
109
+ fieldAffixRequiredPropsCheck([prefix, suffix]);
110
+ const handleClear = onClear ?? noopClear;
111
+ warnIfClearActionWithNoOnClear(onClear, showClearAction);
112
+ const inputInvalid = Boolean(invalid) || Boolean(error);
113
+
114
+ return (
115
+ <ErrorMessageWrapper message={getMessage({ invalid, error })}>
116
+ <View
117
+ testID={"ATL-InputFieldWrapper"}
118
+ style={[
119
+ styles.container,
120
+ focused && styles.inputFocused,
121
+ (Boolean(invalid) || error) && styles.inputInvalid,
122
+ disabled && styles.disabled,
123
+ styleOverride?.container,
124
+ ]}
125
+ >
126
+ {prefix?.icon && (
127
+ <PrefixIcon
128
+ disabled={disabled}
129
+ focused={focused}
130
+ hasMiniLabel={hasMiniLabel}
131
+ inputInvalid={inputInvalid}
132
+ icon={prefix.icon}
133
+ />
134
+ )}
135
+ <View style={[styles.inputContainer]}>
136
+ <View
137
+ style={[
138
+ !!placeholder && styles.label,
139
+ hasMiniLabel && styles.miniLabel,
140
+ disabled && styles.disabled,
141
+ hasMiniLabel &&
142
+ showClearAction &&
143
+ styles.miniLabelShowClearAction,
144
+ ]}
145
+ pointerEvents="none"
146
+ >
147
+ <Placeholder
148
+ placeholder={placeholder}
149
+ labelVariation={getLabelVariation(
150
+ error,
151
+ invalid,
152
+ focused,
153
+ disabled,
154
+ )}
155
+ hasMiniLabel={hasMiniLabel}
156
+ styleOverride={styleOverride?.placeholderText}
157
+ />
158
+ </View>
159
+ {prefix?.label && hasValue && (
160
+ <PrefixLabel
161
+ disabled={disabled}
162
+ focused={focused}
163
+ hasMiniLabel={hasMiniLabel}
164
+ inputInvalid={inputInvalid}
165
+ label={prefix.label}
166
+ styleOverride={styleOverride?.prefixLabel}
167
+ />
168
+ )}
169
+ {children}
170
+ {(showClearAction || suffix?.label || suffix?.icon) && (
171
+ <View style={styles.inputEndContainer}>
172
+ {showClearAction && (
173
+ <ClearAction
174
+ hasMarginRight={!!suffix?.icon || !!suffix?.label}
175
+ onPress={handleClear}
176
+ />
177
+ )}
178
+ {suffix?.label && hasValue && (
179
+ <SuffixLabel
180
+ disabled={disabled}
181
+ focused={focused}
182
+ hasMiniLabel={hasMiniLabel}
183
+ inputInvalid={inputInvalid}
184
+ label={suffix.label}
185
+ hasLeftMargin={!showClearAction}
186
+ styleOverride={styleOverride?.suffixLabel}
187
+ />
188
+ )}
189
+ {suffix?.icon && (
190
+ <SuffixIcon
191
+ disabled={disabled}
192
+ focused={focused}
193
+ hasMiniLabel={hasMiniLabel}
194
+ hasLeftMargin={!!(!showClearAction || suffix?.label)}
195
+ inputInvalid={inputInvalid}
196
+ icon={suffix.icon}
197
+ onPress={suffix.onPress}
198
+ />
199
+ )}
200
+ </View>
201
+ )}
202
+ </View>
203
+ </View>
204
+ {assistiveText && !error && !invalid && (
205
+ <Text
206
+ level="textSupporting"
207
+ variation={
208
+ disabled ? "disabled" : focused ? "interactive" : "subdued"
209
+ }
210
+ >
211
+ {assistiveText}
212
+ </Text>
213
+ )}
214
+ </ErrorMessageWrapper>
215
+ );
216
+ }
217
+
218
+ function getLabelVariation(
219
+ error?: FieldError,
220
+ invalid?: boolean | string,
221
+ focused?: boolean,
222
+ disabled?: boolean,
223
+ ): TextVariation {
224
+ if (invalid || error) {
225
+ return "error";
226
+ } else if (disabled) {
227
+ return "disabled";
228
+ } else if (focused) {
229
+ return "interactive";
230
+ }
231
+ return "subdued";
232
+ }
233
+
234
+ function fieldAffixRequiredPropsCheck(
235
+ affixPair: [
236
+ InputFieldWrapperProps["prefix"],
237
+ InputFieldWrapperProps["suffix"],
238
+ ],
239
+ ) {
240
+ affixPair.map(affix => {
241
+ if (typeof affix !== "undefined" && !affix.icon && !affix.label) {
242
+ throw new Error(
243
+ `One of 'label' or 'icon' is required by the field affix component.`,
244
+ );
245
+ }
246
+ });
247
+ }
248
+
249
+ function warnIfClearActionWithNoOnClear(
250
+ onClear?: () => void,
251
+ showClearAction?: boolean,
252
+ ): void {
253
+ if (showClearAction && !onClear && __DEV__) {
254
+ console.warn(
255
+ "Declare an `onClear` prop on your input. You can set `clearable` to never or `showClearAction` to false if you don't need a clearable input",
256
+ );
257
+ }
258
+ }
259
+
260
+ function noopClear() {
261
+ warnIfClearActionWithNoOnClear(undefined, true);
262
+ }
263
+
264
+ function getMessage({
265
+ invalid,
266
+ error,
267
+ }: Pick<InputFieldWrapperProps, "invalid" | "error">): string | undefined {
268
+ const messages: string[] = [];
269
+ if (error?.message) messages.push(error.message);
270
+ if (invalid && typeof invalid === "string") messages.push(invalid);
271
+
272
+ return messages.join("\n");
273
+ }
274
+
275
+ function Placeholder({
276
+ placeholder,
277
+ styleOverride,
278
+ labelVariation,
279
+ hasMiniLabel,
280
+ }: {
281
+ placeholder?: string;
282
+ styleOverride: StyleProp<TextStyle>;
283
+ labelVariation: TextVariation;
284
+ hasMiniLabel: boolean;
285
+ }) {
286
+ return (
287
+ <>
288
+ {!styleOverride ? (
289
+ <Text
290
+ hideFromScreenReader={true}
291
+ maxLines="single"
292
+ variation={labelVariation}
293
+ level={hasMiniLabel ? "textSupporting" : "text"}
294
+ >
295
+ {placeholder}
296
+ </Text>
297
+ ) : (
298
+ <RNText
299
+ accessibilityRole="none"
300
+ accessible={false}
301
+ importantForAccessibility="no-hide-descendants"
302
+ numberOfLines={1}
303
+ style={[
304
+ typographyStyles[labelVariation],
305
+ typographyStyles.baseRegularRegular,
306
+ hasMiniLabel
307
+ ? typographyStyles.smallSize
308
+ : typographyStyles.defaultSize,
309
+ styleOverride,
310
+ ]}
311
+ >
312
+ {placeholder}
313
+ </RNText>
314
+ )}
315
+ </>
316
+ );
317
+ }
@@ -0,0 +1,27 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../../../utils/design";
3
+
4
+ const width = tokens["space-smaller"] + tokens["space-larger"];
5
+
6
+ export const styles = StyleSheet.create({
7
+ container: {
8
+ width,
9
+ height: "100%",
10
+ flexDirection: "row",
11
+ justifyContent: "center",
12
+ alignItems: "center",
13
+ alignSelf: "center",
14
+ },
15
+ circle: {
16
+ backgroundColor: tokens["color-surface--background"],
17
+ borderRadius: tokens["radius-circle"],
18
+ width: tokens["space-large"],
19
+ height: tokens["space-large"],
20
+ flexDirection: "row",
21
+ justifyContent: "center",
22
+ alignItems: "center",
23
+ },
24
+ addedMargin: {
25
+ marginRight: tokens["space-small"],
26
+ },
27
+ });
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { fireEvent, render } from "@testing-library/react-native";
3
+ import { useIntl } from "react-intl";
4
+ import { ClearAction } from "./ClearAction";
5
+ import { messages } from "./messages";
6
+
7
+ it("should call the handler", () => {
8
+ const pressHandler = jest.fn();
9
+ const { formatMessage } = useIntl();
10
+
11
+ const { getByLabelText } = render(<ClearAction onPress={pressHandler} />);
12
+
13
+ fireEvent.press(getByLabelText(formatMessage(messages.clearTextLabel)));
14
+ expect(pressHandler).toHaveBeenCalled();
15
+ });
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { Pressable, View } from "react-native";
3
+ import { useIntl } from "react-intl";
4
+ import { styles } from "./ClearAction.style";
5
+ import { messages } from "./messages";
6
+ import { Icon } from "../../../Icon";
7
+
8
+ interface ClearActionProps {
9
+ /**
10
+ * Press handler
11
+ */
12
+ readonly onPress: () => void;
13
+ readonly hasMarginRight?: boolean;
14
+ }
15
+
16
+ export function ClearAction({
17
+ onPress,
18
+ hasMarginRight = false,
19
+ }: ClearActionProps): JSX.Element {
20
+ const { formatMessage } = useIntl();
21
+ return (
22
+ <Pressable
23
+ style={[styles.container, hasMarginRight && styles.addedMargin]}
24
+ onPress={onPress}
25
+ accessibilityLabel={formatMessage(messages.clearTextLabel)}
26
+ >
27
+ <View style={styles.circle}>
28
+ <Icon size="small" name="cross" color="interactiveSubtle" />
29
+ </View>
30
+ </Pressable>
31
+ );
32
+ }
@@ -0,0 +1,2 @@
1
+ export { ClearAction } from "./ClearAction";
2
+ export { messages } from "./messages";
@@ -0,0 +1,9 @@
1
+ import { defineMessages } from "react-intl";
2
+
3
+ export const messages = defineMessages({
4
+ clearTextLabel: {
5
+ id: "clearTextLabel",
6
+ defaultMessage: "Clear input",
7
+ description: "Accessiblity label for the clear input button",
8
+ },
9
+ });
@@ -0,0 +1,221 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+ import { TextStyle } from "react-native";
4
+ import { ReactTestInstance } from "react-test-renderer";
5
+ import {
6
+ PrefixIcon,
7
+ PrefixIconProps,
8
+ PrefixLabel,
9
+ PrefixLabelProps,
10
+ prefixIconTestId,
11
+ prefixLabelTestId,
12
+ } from "./Prefix";
13
+ import { typographyStyles } from "../../../Typography";
14
+ import { styles } from "../../InputFieldWrapper.style";
15
+ import { tokens } from "../../../utils/design";
16
+ import * as IconComponent from "../../../Icon/Icon";
17
+
18
+ const iconSpy = jest.spyOn(IconComponent, "Icon");
19
+
20
+ const mockLabel = "$";
21
+
22
+ function setupLabel({
23
+ disabled = false,
24
+ focused = false,
25
+ hasMiniLabel = false,
26
+ inputInvalid = false,
27
+ label = mockLabel,
28
+ styleOverride,
29
+ }: Partial<PrefixLabelProps>) {
30
+ return render(
31
+ <PrefixLabel
32
+ disabled={disabled}
33
+ focused={focused}
34
+ hasMiniLabel={hasMiniLabel}
35
+ inputInvalid={inputInvalid}
36
+ label={label}
37
+ styleOverride={styleOverride}
38
+ />,
39
+ );
40
+ }
41
+
42
+ function setupIcon({
43
+ disabled = false,
44
+ focused = false,
45
+ hasMiniLabel = false,
46
+ inputInvalid = false,
47
+ icon = "invoice",
48
+ }: Partial<PrefixIconProps>) {
49
+ return render(
50
+ <PrefixIcon
51
+ disabled={disabled}
52
+ focused={focused}
53
+ hasMiniLabel={hasMiniLabel}
54
+ inputInvalid={inputInvalid}
55
+ icon={icon}
56
+ />,
57
+ );
58
+ }
59
+
60
+ describe("Prefix", () => {
61
+ it("renders a prefix label when specified", () => {
62
+ const { getByText } = setupLabel({});
63
+ expect(getByText(mockLabel)).toBeDefined();
64
+ });
65
+
66
+ it("renders a prefix icon when specified", () => {
67
+ const { getByTestId } = setupIcon({});
68
+ expect(getByTestId("invoice")).toBeDefined();
69
+ });
70
+
71
+ describe("updates the styles when focused", () => {
72
+ it("for the label", () => {
73
+ const tree = setupLabel({
74
+ focused: true,
75
+ });
76
+ const expectedStyle = { ...styles.fieldAffix, ...styles.inputFocused };
77
+ const prefixLabel = tree.getByTestId(prefixLabelTestId);
78
+ const flattenedStyle = prefixLabel.props.style.reduce(
79
+ (style: TextStyle, additionalStyles: TextStyle) => ({
80
+ ...style,
81
+ ...additionalStyles,
82
+ }),
83
+ {},
84
+ );
85
+
86
+ expect(flattenedStyle).toEqual(expectedStyle);
87
+ });
88
+
89
+ it("for the icon", () => {
90
+ const tree = setupIcon({
91
+ focused: true,
92
+ });
93
+ const expectedStyle = { ...styles.fieldAffix, ...styles.inputFocused };
94
+ const icon = tree.getByTestId(prefixIconTestId);
95
+ const flattenedStyle = icon.props.style.reduce(
96
+ (style: TextStyle, additionalStyles: TextStyle) => ({
97
+ ...style,
98
+ ...additionalStyles,
99
+ }),
100
+ {},
101
+ );
102
+
103
+ expect(flattenedStyle).toEqual(expectedStyle);
104
+ });
105
+ });
106
+
107
+ describe("updates the styles when input is invalid", () => {
108
+ it("for the label", () => {
109
+ const tree = setupLabel({
110
+ inputInvalid: true,
111
+ });
112
+ const expectedStyle = { ...styles.fieldAffix, ...styles.inputInvalid };
113
+ const prefixLabel = tree.getByTestId(prefixLabelTestId);
114
+ const flattenedStyle = prefixLabel.props.style.reduce(
115
+ (style: TextStyle, additionalStyles: TextStyle) => ({
116
+ ...style,
117
+ ...additionalStyles,
118
+ }),
119
+ {},
120
+ );
121
+
122
+ expect(flattenedStyle).toEqual(expectedStyle);
123
+ });
124
+
125
+ it("for the icon", () => {
126
+ const tree = setupIcon({
127
+ inputInvalid: true,
128
+ });
129
+ const expectedStyle = { ...styles.fieldAffix, ...styles.inputInvalid };
130
+ const prefixIcon = tree.getByTestId(prefixIconTestId);
131
+ const flattenedStyle = prefixIcon.props.style.reduce(
132
+ (style: TextStyle, additionalStyles: TextStyle) => ({
133
+ ...style,
134
+ ...additionalStyles,
135
+ }),
136
+ {},
137
+ );
138
+
139
+ expect(flattenedStyle).toEqual(expectedStyle);
140
+ });
141
+ });
142
+
143
+ it("updates the position of the label when a value is entered", () => {
144
+ const tree = setupLabel({
145
+ hasMiniLabel: true,
146
+ });
147
+ const prefixLabel = tree.getByTestId(prefixLabelTestId);
148
+ const labelWrapper = prefixLabel.children[0] as ReactTestInstance;
149
+ const expectedStyle = [styles.prefixLabel, styles.fieldAffixMiniLabel];
150
+ expect(labelWrapper.props.style).toEqual(expectedStyle);
151
+ });
152
+
153
+ describe("when disabled", () => {
154
+ it("updates the label", () => {
155
+ const tree = setupLabel({
156
+ disabled: true,
157
+ });
158
+ const prefixLabel = tree.getByText(mockLabel);
159
+ const expectedStyle = [
160
+ typographyStyles.baseRegularRegular,
161
+ typographyStyles.disabled,
162
+ typographyStyles.startAlign,
163
+ typographyStyles.defaultSize,
164
+ typographyStyles.baseLetterSpacing,
165
+ ];
166
+ expect(prefixLabel.props.style).toEqual(expectedStyle);
167
+ });
168
+
169
+ it("updates the icon", () => {
170
+ setupIcon({
171
+ disabled: true,
172
+ });
173
+ expect(iconSpy).toHaveBeenCalledWith(
174
+ {
175
+ customColor: tokens["color-disabled"],
176
+ name: "invoice",
177
+ },
178
+ {},
179
+ );
180
+ });
181
+ });
182
+
183
+ describe("Custom Label Style", () => {
184
+ it("uses default styling when style override not used", () => {
185
+ const { getByText } = setupLabel({});
186
+ const prefix = getByText(mockLabel);
187
+ const expectedStyle = [
188
+ typographyStyles.baseRegularRegular,
189
+ typographyStyles.base,
190
+ typographyStyles.startAlign,
191
+ typographyStyles.defaultSize,
192
+ typographyStyles.baseLetterSpacing,
193
+ ];
194
+ expect(prefix.props.style).toEqual(expectedStyle);
195
+ });
196
+
197
+ it("uses style override for label when provided", () => {
198
+ const styleOverride = {
199
+ fontSize: 50,
200
+ color: "purple",
201
+ };
202
+ const { getByText } = setupLabel({ styleOverride });
203
+ const prefix = getByText(mockLabel);
204
+ const flattenedStyle = prefix.props.style.reduce(
205
+ (style: TextStyle, additionalStyles: TextStyle) => ({
206
+ ...style,
207
+ ...additionalStyles,
208
+ }),
209
+ {},
210
+ );
211
+
212
+ const expectedStyle = {
213
+ ...styleOverride,
214
+ ...typographyStyles.baseRegularRegular,
215
+ lineHeight: typographyStyles.defaultSize.lineHeight,
216
+ };
217
+
218
+ expect(flattenedStyle).toEqual(expectedStyle);
219
+ });
220
+ });
221
+ });