@jobber/components-native 0.102.2 → 0.103.1-JOB-fix-me-b57d779.1
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/docs/ActivityIndicator/ActivityIndicator.md +48 -8
- package/dist/package.json +16 -3
- package/dist/src/Icon/Icon.js +3 -0
- package/dist/src/Icon/Icon.test.js +37 -0
- package/dist/src/primitives/index.js +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/primitives/index.d.ts +4 -0
- package/package.json +16 -3
- package/src/Icon/Icon.test.tsx +58 -0
- package/src/Icon/Icon.tsx +3 -0
- package/src/primitives/index.ts +4 -0
|
@@ -1,16 +1,56 @@
|
|
|
1
1
|
# Activity Indicator
|
|
2
2
|
|
|
3
|
-
ActivityIndicator is used to
|
|
4
|
-
|
|
3
|
+
`ActivityIndicator` is used to communicate an **indeterminate** activity the
|
|
4
|
+
user cannot directly control or measure — loading, fetching, or waiting on an
|
|
5
|
+
external system.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
Use `ActivityIndicator` when the loading time or fraction of progress is
|
|
8
|
+
unknown. If you know the fraction of progress (for example, "3 of 4 files
|
|
9
|
+
uploaded"), use `ProgressBar` instead. A `ProgressIndicator` counterpart that
|
|
10
|
+
will subsume `ProgressBar` under the new naming is forthcoming.
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
[Spinner](/components/Spinner).
|
|
12
|
+
## Design & usage guidelines
|
|
10
13
|
|
|
11
|
-
ActivityIndicator
|
|
12
|
-
|
|
13
|
-
`
|
|
14
|
+
The default `ActivityIndicator` size is `base` (32px) and can be used in most
|
|
15
|
+
cases. The `small` size (16px) should be used on individual elements of an
|
|
16
|
+
interface (e.g. inside a `Button` or `Card`) or when sitting next to short
|
|
17
|
+
inline text.
|
|
18
|
+
|
|
19
|
+
Layout is the consumer's responsibility — `ActivityIndicator` does not expose
|
|
20
|
+
an `inline` prop. Place it inside your own flex / inline-block container, or
|
|
21
|
+
pass a `className` / `style`, to control surrounding layout.
|
|
22
|
+
|
|
23
|
+
## Accessibility
|
|
24
|
+
|
|
25
|
+
`ActivityIndicator` announces itself politely to assistive technology:
|
|
26
|
+
|
|
27
|
+
* `role="status"` marks the element as a polite live region, appropriate for
|
|
28
|
+
a non-urgent loading state.
|
|
29
|
+
* `aria-label` defaults to the literal English string `"Loading"`. Pass a
|
|
30
|
+
custom `ariaLabel` to localize or to describe the specific activity
|
|
31
|
+
(e.g. `ariaLabel="Uploading file"`).
|
|
32
|
+
|
|
33
|
+
The indicator does not participate in the keyboard tab order.
|
|
34
|
+
|
|
35
|
+
### Reduced motion
|
|
36
|
+
|
|
37
|
+
When the user's operating system reports
|
|
38
|
+
[`prefers-reduced-motion: reduce`](https://developer.mozilla.org/docs/Web/CSS/@media/prefers-reduced-motion),
|
|
39
|
+
the indicator hides its rocking and rotating layers, repaints a single
|
|
40
|
+
static ring in the icon foreground colour, and gently pulses its opacity so
|
|
41
|
+
the indicator still reads as "busy" without rotational motion.
|
|
42
|
+
|
|
43
|
+
## Relationship to Spinner
|
|
44
|
+
|
|
45
|
+
The legacy [Spinner](/components/Spinner) component remains available and
|
|
46
|
+
unchanged. New code should prefer `ActivityIndicator`. A follow-up change is
|
|
47
|
+
expected to deprecate `Spinner` in favour of `ActivityIndicator`.
|
|
48
|
+
|
|
49
|
+
## Mobile
|
|
50
|
+
|
|
51
|
+
On mobile (`@jobber/components-native`), `ActivityIndicator` uses the
|
|
52
|
+
[ActivityIndicator](https://reactnative.dev/docs/activityIndicator) core
|
|
53
|
+
component from React Native.
|
|
14
54
|
|
|
15
55
|
## Mockup
|
|
16
56
|
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.103.1-JOB-fix-me-b57d779.1+b57d779a",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -15,6 +15,19 @@
|
|
|
15
15
|
"main": "dist/src/index.js",
|
|
16
16
|
"module": "dist/src/index.js",
|
|
17
17
|
"types": "dist/types/src/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/types/src/index.d.ts",
|
|
21
|
+
"default": "./dist/src/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./primitives": {
|
|
24
|
+
"types": "./dist/types/src/primitives/index.d.ts",
|
|
25
|
+
"default": "./dist/src/primitives/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./meta.json": "./dist/src/utils/meta/meta.json",
|
|
28
|
+
"./dist/src/utils/meta/meta.json": "./dist/src/utils/meta/meta.json",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
18
31
|
"files": [
|
|
19
32
|
"dist",
|
|
20
33
|
"dist/docs/**/*",
|
|
@@ -58,7 +71,7 @@
|
|
|
58
71
|
"devDependencies": {
|
|
59
72
|
"@babel/runtime": "^7.29.2",
|
|
60
73
|
"@gorhom/bottom-sheet": "^5.2.8",
|
|
61
|
-
"@jobber/design": "0.101.
|
|
74
|
+
"@jobber/design": "0.101.1",
|
|
62
75
|
"@jobber/hooks": "2.20.1",
|
|
63
76
|
"@react-native-community/datetimepicker": "^8.4.5",
|
|
64
77
|
"@react-native/babel-preset": "^0.82.1",
|
|
@@ -109,5 +122,5 @@
|
|
|
109
122
|
"react-native-screens": ">=4.18.0",
|
|
110
123
|
"react-native-svg": ">=12.0.0"
|
|
111
124
|
},
|
|
112
|
-
"gitHead": "
|
|
125
|
+
"gitHead": "b57d779ace9b72a8aaa258ee26ee1ebd3cdc4cbf"
|
|
113
126
|
}
|
package/dist/src/Icon/Icon.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import Svg, { Path } from "react-native-svg";
|
|
3
3
|
import { getIcon } from "@jobber/design";
|
|
4
|
+
import { useAtlantisTheme } from "../AtlantisThemeContext";
|
|
4
5
|
export function Icon({ name, color, size = "base", customColor, testID, }) {
|
|
6
|
+
const { tokens } = useAtlantisTheme();
|
|
5
7
|
const { svgStyle, paths, viewBox } = getIcon({
|
|
6
8
|
name,
|
|
7
9
|
color,
|
|
8
10
|
size,
|
|
9
11
|
platform: "mobile",
|
|
10
12
|
format: "js",
|
|
13
|
+
tokens,
|
|
11
14
|
});
|
|
12
15
|
const icon = paths.map((path) => {
|
|
13
16
|
return React.createElement(Path, { key: path, d: path, fill: customColor || svgStyle.fill });
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { Path } from "react-native-svg";
|
|
4
|
+
import { darkTokens } from "@jobber/design";
|
|
3
5
|
import { Icon } from ".";
|
|
6
|
+
import { AtlantisThemeContextProvider } from "../AtlantisThemeContext";
|
|
4
7
|
it("renders home icon", () => {
|
|
5
8
|
const tree = render(React.createElement(Icon, { name: "home" })).toJSON();
|
|
6
9
|
expect(tree).toMatchSnapshot();
|
|
@@ -38,3 +41,37 @@ it("applies name prop as testID when testID prop is not provided", () => {
|
|
|
38
41
|
const { getByTestId } = render(React.createElement(Icon, { name: "home" }));
|
|
39
42
|
expect(getByTestId("home")).toBeDefined();
|
|
40
43
|
});
|
|
44
|
+
describe("theme-aware fill", () => {
|
|
45
|
+
function getPathFills(svg) {
|
|
46
|
+
return svg.UNSAFE_root.findAllByType(Path).map(p => p.props.fill);
|
|
47
|
+
}
|
|
48
|
+
it("resolves the icon color to the dark-theme token under a dark theme", () => {
|
|
49
|
+
const expected = darkTokens["color-icon"];
|
|
50
|
+
const svg = render(React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
51
|
+
React.createElement(Icon, { name: "home", color: "icon" })));
|
|
52
|
+
expect(getPathFills(svg)[0]).toBe(expected);
|
|
53
|
+
expect(getPathFills(svg)[0]).not.toBe("hsl(198, 35%, 21%)");
|
|
54
|
+
});
|
|
55
|
+
it("resolves an icon's semantic default fill via live tokens when no color prop is given", () => {
|
|
56
|
+
// Regression check: semantic fills (e.g. quote -> color-quote) must
|
|
57
|
+
// resolve through their own token, not fall back to color-icon.
|
|
58
|
+
const expectedQuote = darkTokens["color-quote"];
|
|
59
|
+
const fallbackIcon = darkTokens["color-icon"];
|
|
60
|
+
const svg = render(React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
61
|
+
React.createElement(Icon, { name: "quote" })));
|
|
62
|
+
expect(getPathFills(svg)[0]).toBe(expectedQuote);
|
|
63
|
+
expect(getPathFills(svg)[0]).not.toBe(fallbackIcon);
|
|
64
|
+
});
|
|
65
|
+
it("defaults to the color-icon token when no color prop and no semantic default exist", () => {
|
|
66
|
+
const expected = darkTokens["color-icon"];
|
|
67
|
+
const svg = render(React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
68
|
+
React.createElement(Icon, { name: "home" })));
|
|
69
|
+
expect(getPathFills(svg)[0]).toBe(expected);
|
|
70
|
+
expect(getPathFills(svg)[0]).not.toBe("hsl(198, 35%, 21%)");
|
|
71
|
+
});
|
|
72
|
+
it("customColor still wins over theme tokens", () => {
|
|
73
|
+
const svg = render(React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
74
|
+
React.createElement(Icon, { name: "home", color: "icon", customColor: "#f33323" })));
|
|
75
|
+
expect(getPathFills(svg)[0]).toBe("#f33323");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|