@jobber/components-native 0.115.1 → 0.115.2

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.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.115.1",
3
+ "version": "0.115.2",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -123,5 +123,5 @@
123
123
  "react-native-screens": ">=4.18.0",
124
124
  "react-native-svg": ">=12.0.0"
125
125
  },
126
- "gitHead": "df5ca5cd691c56be40fad67fae7ea644724656cc"
126
+ "gitHead": "b608e2db83fa37ba3ceaa2d8cd521a806853701c"
127
127
  }
@@ -1,16 +1,31 @@
1
1
  import React, { useMemo } from "react";
2
2
  import { Pressable, View } from "react-native";
3
+ import { allColors } from "@jobber/design";
3
4
  import { useStyles } from "./Chip.style";
4
5
  import { Icon } from "../Icon";
5
6
  import { Typography } from "../Typography";
6
7
  import { useAtlantisTheme } from "../AtlantisThemeContext";
7
8
  export function Chip({ icon, label, onPress, isDismissible, isActive, inactiveBackgroundColor = "background", accessibilityLabel, accessibilityRole = "radio", accent, }) {
8
9
  const styles = useStyles();
9
- const { tokens } = useAtlantisTheme();
10
- const defaultAccentColor = tokens["color-surface--reverse"];
11
- const { chipStyle, iconCustomColor, dismissColor } = useMemo(() => {
12
- const accentColor = accent ? tokens[`color-${accent}`] : defaultAccentColor;
13
- const iconColor = isActive ? tokens["color-surface"] : accentColor;
10
+ const { tokens, effectiveTheme } = useAtlantisTheme();
11
+ const { chipStyle, iconCustomColor, labelStyle, dismissColor } = useMemo(() => {
12
+ const accentColor = accent
13
+ ? tokens[`color-${accent}`]
14
+ : tokens["color-surface--reverse"];
15
+ let backgroundColor = accentColor;
16
+ let iconColor = isActive ? tokens["color-surface"] : accentColor;
17
+ let selectedLabelStyle;
18
+ if (isActive && accent) {
19
+ const selected = getSelectedAccentColors(accent, {
20
+ tokens,
21
+ effectiveTheme,
22
+ });
23
+ backgroundColor = selected.background;
24
+ iconColor = selected.foreground;
25
+ selectedLabelStyle = {
26
+ textStyle: { color: selected.foreground },
27
+ };
28
+ }
14
29
  const chip = [
15
30
  styles.container,
16
31
  {
@@ -18,7 +33,7 @@ export function Chip({ icon, label, onPress, isDismissible, isActive, inactiveBa
18
33
  ? tokens["color-surface"]
19
34
  : tokens["color-interactive--background"],
20
35
  },
21
- isActive && { backgroundColor: accentColor },
36
+ isActive && { backgroundColor },
22
37
  getBorderStyle(inactiveBackgroundColor, tokens),
23
38
  ];
24
39
  const dismiss = (isActive || inactiveBackgroundColor === "surface") &&
@@ -26,9 +41,17 @@ export function Chip({ icon, label, onPress, isDismissible, isActive, inactiveBa
26
41
  return {
27
42
  chipStyle: chip,
28
43
  iconCustomColor: iconColor,
44
+ labelStyle: selectedLabelStyle,
29
45
  dismissColor: dismiss,
30
46
  };
31
- }, [accent, isActive, inactiveBackgroundColor, getBorderStyle, styles]);
47
+ }, [
48
+ accent,
49
+ isActive,
50
+ inactiveBackgroundColor,
51
+ styles,
52
+ tokens,
53
+ effectiveTheme,
54
+ ]);
32
55
  const accessibilityState = useMemo(() => {
33
56
  const checkableRoles = ["radio", "switch", "togglebutton", "checkbox"];
34
57
  if (checkableRoles.includes(accessibilityRole)) {
@@ -40,10 +63,24 @@ export function Chip({ icon, label, onPress, isDismissible, isActive, inactiveBa
40
63
  icon && (React.createElement(View, { style: styles.iconLeft },
41
64
  React.createElement(Icon, { name: icon, size: "base", customColor: iconCustomColor }))),
42
65
  label && (React.createElement(View, { style: styles.chipText },
43
- React.createElement(Typography, { color: "base", fontWeight: "medium", maxFontScaleSize: tokens["typography--fontSize-large"], maxLines: "single", reverseTheme: isActive }, label))),
66
+ React.createElement(Typography, { color: "base", fontWeight: "medium", maxFontScaleSize: tokens["typography--fontSize-large"], maxLines: "single", reverseTheme: isActive && !accent, UNSAFE_style: labelStyle }, label))),
44
67
  isDismissible && (React.createElement(View, { style: [styles.dismissIcon, dismissColor] },
45
68
  React.createElement(Icon, { name: "cross", size: "small" })))));
46
69
  }
70
+ // Object labels and leading icons share the selected foreground.
71
+ function getSelectedAccentColors(accent, { tokens, effectiveTheme, }) {
72
+ const colors = {
73
+ background: allColors[`color-${accent}`],
74
+ foreground: allColors["color-text--reverse"],
75
+ };
76
+ if (accent === "client") {
77
+ colors.background = tokens["color-client"];
78
+ if (effectiveTheme === "dark") {
79
+ colors.foreground = allColors["color-text"];
80
+ }
81
+ }
82
+ return colors;
83
+ }
47
84
  function getBorderStyle(inactiveBackgroundColor, tokens) {
48
85
  let borderColor = "transparent";
49
86
  if (inactiveBackgroundColor === "surface") {
@@ -1,6 +1,9 @@
1
1
  import React from "react";
2
+ import { allColors, darkTokens } from "@jobber/design";
2
3
  import { fireEvent, render } from "@testing-library/react-native";
4
+ import { Path } from "react-native-svg";
3
5
  import { Chip } from "./Chip";
6
+ import { AtlantisThemeContextProvider } from "../AtlantisThemeContext";
4
7
  import { tokens } from "../utils/design";
5
8
  it("renders an active Chip", () => {
6
9
  const { getByText, getByLabelText } = render(React.createElement(Chip, { label: "Foo", onPress: jest.fn(), accessibilityLabel: "Foo chip", isActive: true }));
@@ -67,3 +70,53 @@ describe("accent", () => {
67
70
  });
68
71
  });
69
72
  });
73
+ describe("selection color transitions", () => {
74
+ function example(theme, props = {}) {
75
+ return (React.createElement(AtlantisThemeContextProvider, { theme: theme },
76
+ React.createElement(Chip, Object.assign({ label: "Object", icon: "request", isActive: true }, props))));
77
+ }
78
+ function expectColors(view, background, text, icon = text) {
79
+ expect(view.getByTestId("chipTest")).toHaveStyle({
80
+ backgroundColor: background,
81
+ });
82
+ expect(view.getByText("Object")).toHaveStyle({ color: text });
83
+ const paths = view.UNSAFE_root.findAllByType(Path);
84
+ expect(paths.length).toBeGreaterThan(0);
85
+ for (const path of paths) {
86
+ expect(path.props.fill).toBe(icon);
87
+ }
88
+ }
89
+ it.each(["request", "quote", "job", "invoice"])("keeps the selected %s fill and white content when the theme changes", accent => {
90
+ expect.hasAssertions();
91
+ const props = { accent, icon: accent };
92
+ const view = render(example("light", props));
93
+ for (const theme of ["light", "dark", "light"]) {
94
+ view.rerender(example(theme, props));
95
+ expectColors(view, allColors[`color-${accent}`], allColors["color-text--reverse"]);
96
+ }
97
+ });
98
+ it("changes the selected client fill and foreground together with the theme", () => {
99
+ expect.hasAssertions();
100
+ const props = { accent: "client", icon: "clients" };
101
+ const view = render(example("light", props));
102
+ expectColors(view, allColors["color-client"], allColors["color-text--reverse"]);
103
+ view.rerender(example("dark", props));
104
+ expectColors(view, darkTokens["color-client"], allColors["color-text"]);
105
+ view.rerender(example("light", props));
106
+ expectColors(view, allColors["color-client"], allColors["color-text--reverse"]);
107
+ });
108
+ it.each(["request", "client"])("restores the theme's normal colors when %s is deselected", accent => {
109
+ const view = render(example("dark", { accent }));
110
+ view.rerender(example("dark", { accent, isActive: false }));
111
+ expectColors(view, darkTokens["color-interactive--background"], darkTokens["color-text"], darkTokens[`color-${accent}`]);
112
+ expect(view.getByTestId("chipTest").props.accessibilityState.checked).toBe(false);
113
+ });
114
+ it("preserves default selected colors when an accent is removed and the theme changes", () => {
115
+ expect.hasAssertions();
116
+ const view = render(example("dark", { accent: "request" }));
117
+ view.rerender(example("dark"));
118
+ expectColors(view, darkTokens["color-surface--reverse"], darkTokens["color-text--reverse"], darkTokens["color-surface"]);
119
+ view.rerender(example("light"));
120
+ expectColors(view, allColors["color-surface--reverse"], allColors["color-text--reverse"], allColors["color-surface"]);
121
+ });
122
+ });