@jobber/components-native 0.75.5 → 0.76.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,3 +1,4 @@
1
+ import { StyleProp, ViewStyle } from "react-native";
1
2
  import { IconNames } from "@jobber/design";
2
3
  import { XOR } from "ts-xor";
3
4
  import { ButtonSize, ButtonType, ButtonVariation } from "./types";
@@ -58,6 +59,18 @@ interface CommonButtonProps {
58
59
  * Used to locate this view in end-to-end tests.
59
60
  */
60
61
  readonly testID?: string;
62
+ /**
63
+ * **Use at your own risk:** Custom style for specific elements. This should only be used as a
64
+ * **last resort**. Using this may result in unexpected side effects.
65
+ * More information [here](https://atlantis.getjobber.com/storybook/?path=/docs/guides-customizing-components--docs#unsafe_-props).
66
+ */
67
+ readonly UNSAFE_style?: ButtonUnsafeStyle;
68
+ }
69
+ export interface ButtonUnsafeStyle {
70
+ container?: StyleProp<ViewStyle>;
71
+ contentContainer?: StyleProp<ViewStyle>;
72
+ iconContainer?: StyleProp<ViewStyle>;
73
+ actionLabelContainer?: StyleProp<ViewStyle>;
61
74
  }
62
75
  interface LabelButton extends CommonButtonProps {
63
76
  /**
@@ -70,5 +83,5 @@ interface IconButton extends CommonButtonProps {
70
83
  readonly accessibilityLabel: string;
71
84
  }
72
85
  export type ButtonProps = XOR<LabelButton, IconButton>;
73
- export declare function Button({ label, onPress, variation, type, fullHeight, fullWidth, disabled, loading, size, accessibilityLabel, accessibilityHint, icon, testID, }: ButtonProps): JSX.Element;
86
+ export declare function Button({ label, onPress, variation, type, fullHeight, fullWidth, disabled, loading, size, accessibilityLabel, accessibilityHint, icon, testID, UNSAFE_style, }: ButtonProps): JSX.Element;
74
87
  export {};
@@ -1,5 +1,6 @@
1
1
  import { TextAlign, TextVariation, TruncateLength, TypographyProps } from "../Typography";
2
- interface TextProps extends Pick<TypographyProps<"base">, "maxFontScaleSize" | "selectable"> {
2
+ import { TypographyUnsafeStyle } from "../Typography/Typography";
3
+ export interface TextProps extends Pick<TypographyProps<"base">, "maxFontScaleSize" | "selectable"> {
3
4
  /**
4
5
  * Visual hierarchy of the text
5
6
  */
@@ -59,8 +60,13 @@ interface TextProps extends Pick<TypographyProps<"base">, "maxFontScaleSize" | "
59
60
  * of the TextInput
60
61
  */
61
62
  readonly hideFromScreenReader?: boolean;
63
+ /**
64
+ * **Use at your own risk:** Custom style for specific elements. This should only be used as a
65
+ * **last resort**. Using this may result in unexpected side effects.
66
+ * More information [here](https://atlantis.getjobber.com/storybook/?path=/docs/guides-customizing-components--docs#unsafe_-props).
67
+ */
68
+ readonly UNSAFE_style?: TypographyUnsafeStyle;
62
69
  }
63
70
  export type TextLevel = "text" | "textSupporting";
64
71
  export declare const TEXT_MAX_SCALED_FONT_SIZES: Record<TextLevel, number>;
65
- export declare function Text({ level, variation, emphasis, allowFontScaling, adjustsFontSizeToFit, maxLines, align, children, reverseTheme, strikeThrough, italic, hideFromScreenReader, maxFontScaleSize, underline, selectable, }: TextProps): JSX.Element;
66
- export {};
72
+ export declare function Text({ level, variation, emphasis, allowFontScaling, adjustsFontSizeToFit, maxLines, align, children, reverseTheme, strikeThrough, italic, hideFromScreenReader, maxFontScaleSize, UNSAFE_style, underline, selectable, }: TextProps): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { TextProps } from "react-native";
2
+ import { StyleProp, TextProps, TextStyle } from "react-native";
3
3
  export interface TypographyProps<T extends FontFamily> extends Pick<TextProps, "selectable"> {
4
4
  /**
5
5
  * Text capitalization
@@ -85,9 +85,13 @@ export interface TypographyProps<T extends FontFamily> extends Pick<TextProps, "
85
85
  * Have text styled with strike through
86
86
  */
87
87
  readonly strikeThrough?: boolean;
88
+ readonly UNSAFE_style?: TypographyUnsafeStyle;
89
+ }
90
+ export interface TypographyUnsafeStyle {
91
+ textStyle?: StyleProp<TextStyle>;
88
92
  }
89
93
  export declare const Typography: React.MemoExoticComponent<typeof InternalTypography>;
90
- declare function InternalTypography<T extends FontFamily = "base">({ fontFamily, fontStyle, fontWeight, transform, color, align, size, children, maxLines, allowFontScaling, maxFontScaleSize, adjustsFontSizeToFit, lineHeight, letterSpacing, reverseTheme, hideFromScreenReader, accessibilityRole, strikeThrough, underline, selectable, }: TypographyProps<T>): JSX.Element;
94
+ declare function InternalTypography<T extends FontFamily = "base">({ fontFamily, fontStyle, fontWeight, transform, color, align, size, children, maxLines, allowFontScaling, maxFontScaleSize, adjustsFontSizeToFit, lineHeight, letterSpacing, reverseTheme, hideFromScreenReader, accessibilityRole, strikeThrough, underline, UNSAFE_style, selectable, }: TypographyProps<T>): JSX.Element;
91
95
  export type FontFamily = "base" | "display";
92
96
  export type FontStyle = "regular" | "italic";
93
97
  export type FontWeight = "regular" | "medium" | "bold" | "semiBold" | "extraBold" | "black";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.75.5",
3
+ "version": "0.76.0",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -80,5 +80,5 @@
80
80
  "react-native-safe-area-context": "^4.5.2",
81
81
  "react-native-svg": ">=12.0.0"
82
82
  },
83
- "gitHead": "baec68accebefa70b2f1234d757473e67a713ced"
83
+ "gitHead": "3dbb361d102c6ae833d1dce373125b7bf68508c0"
84
84
  }
@@ -44,7 +44,7 @@ function renderButton(element: ReactElement) {
44
44
  expect(instance.getByText(element.props.label)).toBeDefined();
45
45
 
46
46
  const buttonStyleEl = button.children[0] as ReactTestInstance;
47
- const buttonStyle = buttonStyleEl.props.style.reduce(
47
+ const buttonStyle = buttonStyleEl.props.style.flat().reduce(
48
48
  (mergedStyles: CSSProperties, additionalStyles: CSSProperties) => ({
49
49
  ...mergedStyles,
50
50
  ...additionalStyles,
@@ -298,4 +298,91 @@ describe("Button", () => {
298
298
  expect(handlePress).toHaveBeenCalledTimes(1);
299
299
  });
300
300
  });
301
+
302
+ describe("UNSAFE_style", () => {
303
+ it("should apply the container style override", () => {
304
+ const containerStyle = {
305
+ backgroundColor: "red",
306
+ borderColor: tokens["color-base-red--500"],
307
+ };
308
+
309
+ const { buttonStyle } = renderButton(
310
+ <Button
311
+ label="Override"
312
+ onPress={jest.fn()}
313
+ UNSAFE_style={{ container: containerStyle }}
314
+ />,
315
+ );
316
+
317
+ expect(buttonStyle).toMatchObject(containerStyle);
318
+ });
319
+
320
+ it("should apply the contentContainer style override to labels", () => {
321
+ const contentContainerStyle = { padding: 10 };
322
+
323
+ const { getByTestId } = renderButton(
324
+ <Button
325
+ label="Override"
326
+ onPress={jest.fn()}
327
+ UNSAFE_style={{ contentContainer: contentContainerStyle }}
328
+ />,
329
+ );
330
+
331
+ const contentContainer = getByTestId("contentContainer");
332
+ expect(contentContainer.props.style).toContainEqual(
333
+ contentContainerStyle,
334
+ );
335
+ });
336
+
337
+ it("should apply the contentContainer style override to icons", () => {
338
+ const contentContainerStyle = { padding: 10 };
339
+
340
+ const { getByTestId } = renderButton(
341
+ <Button
342
+ label="Cog"
343
+ icon="cog"
344
+ onPress={jest.fn()}
345
+ UNSAFE_style={{ contentContainer: contentContainerStyle }}
346
+ />,
347
+ );
348
+
349
+ const contentContainer = getByTestId("contentContainer");
350
+ expect(contentContainer.props.style).toContainEqual(
351
+ contentContainerStyle,
352
+ );
353
+ });
354
+
355
+ it("should apply the iconContainer style override", () => {
356
+ const iconContainerStyle = { margin: 5 };
357
+
358
+ const { getByTestId } = renderButton(
359
+ <Button
360
+ label="Cog"
361
+ icon="cog"
362
+ onPress={jest.fn()}
363
+ UNSAFE_style={{ iconContainer: iconContainerStyle }}
364
+ />,
365
+ );
366
+
367
+ const iconContainer = getByTestId("iconContainer");
368
+ expect(iconContainer.props.style).toContainEqual(iconContainerStyle);
369
+ });
370
+
371
+ it("should apply the actionLabelContainer style override", () => {
372
+ const actionLabelContainerStyle = { margin: 5 };
373
+
374
+ const { getByTestId } = renderButton(
375
+ <Button
376
+ label="Override"
377
+ onPress={jest.fn()}
378
+ UNSAFE_style={{ actionLabelContainer: actionLabelContainerStyle }}
379
+ />,
380
+ );
381
+
382
+ const actionLabelContainer = getByTestId("actionLabelContainer");
383
+ expect(actionLabelContainer.props.style).toContainEqual(
384
+ actionLabelContainerStyle,
385
+ );
386
+ });
387
+ });
301
388
  });
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { TouchableHighlight, View } from "react-native";
2
+ import { StyleProp, TouchableHighlight, View, ViewStyle } from "react-native";
3
3
  import { IconColorNames, IconNames } from "@jobber/design";
4
4
  import { XOR } from "ts-xor";
5
5
  import { styles } from "./Button.style";
@@ -77,6 +77,20 @@ interface CommonButtonProps {
77
77
  * Used to locate this view in end-to-end tests.
78
78
  */
79
79
  readonly testID?: string;
80
+
81
+ /**
82
+ * **Use at your own risk:** Custom style for specific elements. This should only be used as a
83
+ * **last resort**. Using this may result in unexpected side effects.
84
+ * More information [here](https://atlantis.getjobber.com/storybook/?path=/docs/guides-customizing-components--docs#unsafe_-props).
85
+ */
86
+ readonly UNSAFE_style?: ButtonUnsafeStyle;
87
+ }
88
+
89
+ export interface ButtonUnsafeStyle {
90
+ container?: StyleProp<ViewStyle>;
91
+ contentContainer?: StyleProp<ViewStyle>;
92
+ iconContainer?: StyleProp<ViewStyle>;
93
+ actionLabelContainer?: StyleProp<ViewStyle>;
80
94
  }
81
95
 
82
96
  interface LabelButton extends CommonButtonProps {
@@ -107,6 +121,7 @@ export function Button({
107
121
  accessibilityHint,
108
122
  icon,
109
123
  testID,
124
+ UNSAFE_style,
110
125
  }: ButtonProps): JSX.Element {
111
126
  const buttonStyle = [
112
127
  styles.button,
@@ -141,11 +156,20 @@ export function Button({
141
156
  fullHeight && styles.fullHeight,
142
157
  ]}
143
158
  >
144
- <View style={buttonStyle}>
159
+ <View style={[buttonStyle, UNSAFE_style?.container]}>
145
160
  {loading && <InternalButtonLoading variation={variation} type={type} />}
146
- <View style={getContentStyles(label, icon)}>
161
+ <View
162
+ style={[
163
+ getContentStyles(label, icon),
164
+ UNSAFE_style?.contentContainer,
165
+ ]}
166
+ testID="contentContainer"
167
+ >
147
168
  {icon && (
148
- <View style={styles.iconStyle}>
169
+ <View
170
+ style={[styles.iconStyle, UNSAFE_style?.iconContainer]}
171
+ testID="iconContainer"
172
+ >
149
173
  <Icon
150
174
  name={icon}
151
175
  color={getIconColorVariation(variation, type, disabled)}
@@ -153,7 +177,10 @@ export function Button({
153
177
  </View>
154
178
  )}
155
179
  {label && (
156
- <View style={styles.labelStyle}>
180
+ <View
181
+ style={[styles.labelStyle, UNSAFE_style?.actionLabelContainer]}
182
+ testID="actionLabelContainer"
183
+ >
157
184
  <ActionLabel
158
185
  variation={getActionLabelVariation(variation, type)}
159
186
  disabled={disabled}
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { render } from "@testing-library/react-native";
3
+ import { tokens } from "@jobber/design";
3
4
  import { Text } from ".";
4
5
 
5
6
  it("renders text with no additional props", () => {
@@ -148,3 +149,22 @@ it("renders text with underline styling", () => {
148
149
 
149
150
  expect(text.toJSON()).toMatchSnapshot();
150
151
  });
152
+
153
+ describe("UNSAFE_style", () => {
154
+ it("applies custom styles via UNSAFE_style prop", () => {
155
+ const customStyle = {
156
+ textStyle: {
157
+ fontSize: 20,
158
+ color: tokens["color-blue--dark"],
159
+ },
160
+ };
161
+
162
+ const { getByRole } = render(
163
+ <Text UNSAFE_style={customStyle}>Test Text</Text>,
164
+ );
165
+ const textElement = getByRole("text");
166
+ expect(textElement.props.style).toContainEqual(
167
+ expect.objectContaining(customStyle.textStyle),
168
+ );
169
+ });
170
+ });
package/src/Text/Text.tsx CHANGED
@@ -11,8 +11,9 @@ import {
11
11
  TypographyProps,
12
12
  } from "../Typography";
13
13
  import { tokens } from "../utils/design";
14
+ import { TypographyUnsafeStyle } from "../Typography/Typography";
14
15
 
15
- interface TextProps
16
+ export interface TextProps
16
17
  extends Pick<TypographyProps<"base">, "maxFontScaleSize" | "selectable"> {
17
18
  /**
18
19
  * Visual hierarchy of the text
@@ -85,6 +86,13 @@ interface TextProps
85
86
  * of the TextInput
86
87
  */
87
88
  readonly hideFromScreenReader?: boolean;
89
+
90
+ /**
91
+ * **Use at your own risk:** Custom style for specific elements. This should only be used as a
92
+ * **last resort**. Using this may result in unexpected side effects.
93
+ * More information [here](https://atlantis.getjobber.com/storybook/?path=/docs/guides-customizing-components--docs#unsafe_-props).
94
+ */
95
+ readonly UNSAFE_style?: TypographyUnsafeStyle;
88
96
  }
89
97
 
90
98
  export type TextLevel = "text" | "textSupporting";
@@ -125,6 +133,7 @@ export function Text({
125
133
  italic = false,
126
134
  hideFromScreenReader = false,
127
135
  maxFontScaleSize,
136
+ UNSAFE_style,
128
137
  underline,
129
138
  selectable,
130
139
  }: TextProps): JSX.Element {
@@ -133,6 +142,7 @@ export function Text({
133
142
  return (
134
143
  <Typography
135
144
  color={variation}
145
+ UNSAFE_style={UNSAFE_style}
136
146
  fontFamily="base"
137
147
  fontStyle={italic ? "italic" : "regular"}
138
148
  fontWeight={getFontWeight({ level, emphasis })}
@@ -225,6 +225,20 @@ it("renders text that is inaccessible", () => {
225
225
  );
226
226
  });
227
227
 
228
+ it("applies custom UNSAFE_style to text", () => {
229
+ const customStyle = { color: "red", fontSize: 20 };
230
+ const typography = render(
231
+ <Typography UNSAFE_style={{ textStyle: customStyle }}>
232
+ Test Text
233
+ </Typography>,
234
+ );
235
+ const textElement = typography.getByText("Test Text");
236
+
237
+ expect(textElement.props.style).toEqual(
238
+ expect.arrayContaining([expect.objectContaining(customStyle)]),
239
+ );
240
+ });
241
+
228
242
  describe("underline", () => {
229
243
  it.each(["solid", "double", "dotted", "dashed"] as const)(
230
244
  "renders text with %s underline",
@@ -118,6 +118,8 @@ export interface TypographyProps<T extends FontFamily>
118
118
  * Have text styled with strike through
119
119
  */
120
120
  readonly strikeThrough?: boolean;
121
+
122
+ readonly UNSAFE_style?: TypographyUnsafeStyle;
121
123
  }
122
124
 
123
125
  const maxNumberOfLines = {
@@ -129,6 +131,10 @@ const maxNumberOfLines = {
129
131
  unlimited: undefined,
130
132
  };
131
133
 
134
+ export interface TypographyUnsafeStyle {
135
+ textStyle?: StyleProp<TextStyle>;
136
+ }
137
+
132
138
  export const Typography = React.memo(InternalTypography);
133
139
 
134
140
  // eslint-disable-next-line max-statements
@@ -152,6 +158,7 @@ function InternalTypography<T extends FontFamily = "base">({
152
158
  accessibilityRole = "text",
153
159
  strikeThrough = false,
154
160
  underline,
161
+ UNSAFE_style,
155
162
  selectable = true,
156
163
  }: TypographyProps<T>): JSX.Element {
157
164
  const sizeAndHeight = getSizeAndHeightStyle(size, lineHeight);
@@ -176,6 +183,10 @@ function InternalTypography<T extends FontFamily = "base">({
176
183
  style.push(underlineTextStyle, styles.underline);
177
184
  }
178
185
 
186
+ if (UNSAFE_style?.textStyle) {
187
+ style.push(UNSAFE_style.textStyle);
188
+ }
189
+
179
190
  const numberOfLinesForNativeText = maxNumberOfLines[maxLines];
180
191
 
181
192
  const text = getTransformedText(children, transform);