@jobber/components-native 0.113.8 → 0.114.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.
- package/dist/docs/AtlantisThemeContext/AtlantisThemeContext.md +27 -10
- package/dist/package.json +2 -2
- package/dist/src/AtlantisThemeContext/AtlantisThemeContext.js +35 -12
- package/dist/src/AtlantisThemeContext/AtlantisThemeContext.test.js +103 -10
- package/dist/src/BottomSheet/BottomSheet.js +11 -7
- package/dist/src/BottomSheet/BottomSheet.test.js +16 -0
- package/dist/src/ContentOverlay/ContentOverlay.js +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/AtlantisThemeContext/AtlantisThemeContext.d.ts +1 -1
- package/dist/types/src/AtlantisThemeContext/index.d.ts +1 -1
- package/dist/types/src/AtlantisThemeContext/types.d.ts +19 -4
- package/package.json +2 -2
- package/src/AtlantisThemeContext/AtlantisThemeContext.guide.md +53 -0
- package/src/AtlantisThemeContext/AtlantisThemeContext.test.tsx +169 -10
- package/src/AtlantisThemeContext/AtlantisThemeContext.tsx +52 -11
- package/src/AtlantisThemeContext/docs/AtlantisThemeContextBasicExample.tsx +8 -2
- package/src/AtlantisThemeContext/docs/AtlantisThemeContextForceThemeForProviderExample.tsx +8 -4
- package/src/AtlantisThemeContext/index.ts +1 -0
- package/src/AtlantisThemeContext/types.ts +23 -4
- package/src/BottomSheet/BottomSheet.test.tsx +30 -0
- package/src/BottomSheet/BottomSheet.tsx +26 -12
- package/src/ContentOverlay/ContentOverlay.tsx +2 -2
|
@@ -5,16 +5,22 @@ tokens.
|
|
|
5
5
|
|
|
6
6
|
## Design & usage guidelines
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Web and mobile both expose Atlantis theme context, but the runtime ownership is
|
|
9
|
+
slightly different across platforms.
|
|
10
10
|
|
|
11
11
|
Each platform provides a `useAtlantisTheme` hook that you may use to access the
|
|
12
|
-
|
|
12
|
+
current theme information and design tokens in your components.
|
|
13
13
|
|
|
14
|
-
On mobile,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
On mobile, the hook returns:
|
|
15
|
+
|
|
16
|
+
* `theme`: the selected theme preference (`"system" | "light" | "dark"`)
|
|
17
|
+
* `effectiveTheme`: the resolved theme Atlantis uses for tokens
|
|
18
|
+
(`"light" | "dark"`)
|
|
19
|
+
* `tokens`
|
|
20
|
+
* `setTheme`
|
|
21
|
+
|
|
22
|
+
Typically there will only be a single provider at the root, and the consuming
|
|
23
|
+
app will control `theme` and persist that preference outside Atlantis.
|
|
18
24
|
|
|
19
25
|
On web, you'll need to import the `updateTheme` function and call it with the
|
|
20
26
|
new theme. This is a separate function because it synchronizes the theme update
|
|
@@ -86,7 +92,7 @@ function App() {
|
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
function ThemedComponent() {
|
|
89
|
-
const { theme, tokens, setTheme } = useAtlantisTheme();
|
|
95
|
+
const { theme, effectiveTheme, tokens, setTheme } = useAtlantisTheme();
|
|
90
96
|
return (
|
|
91
97
|
<Content>
|
|
92
98
|
<View
|
|
@@ -96,11 +102,16 @@ function ThemedComponent() {
|
|
|
96
102
|
}}
|
|
97
103
|
>
|
|
98
104
|
<Content>
|
|
99
|
-
<Text>The
|
|
105
|
+
<Text>The selected theme is: {theme}.</Text>
|
|
106
|
+
<Text>The effective theme is: {effectiveTheme}.</Text>
|
|
100
107
|
<Text>
|
|
101
108
|
The javascript tokens can be accessed via the tokens object.
|
|
102
109
|
</Text>
|
|
103
110
|
<Text>The theme can be changed using `setTheme`</Text>
|
|
111
|
+
<Button
|
|
112
|
+
onPress={() => setTheme("system")}
|
|
113
|
+
label="The theme can be changed to system"
|
|
114
|
+
/>
|
|
104
115
|
<Button
|
|
105
116
|
onPress={() => setTheme("light")}
|
|
106
117
|
label="The theme can be changed to light"
|
|
@@ -163,6 +174,10 @@ regardless of the main application theme. This can be done by setting the
|
|
|
163
174
|
`dangerouslyOverrideTheme` prop to `<themeToSet>` on the
|
|
164
175
|
`AtlantisThemeContextProvider`.
|
|
165
176
|
|
|
177
|
+
On mobile, forcing a theme changes the rendered tokens for that subtree, but it
|
|
178
|
+
does not replace the selected theme preference semantics from the nearest
|
|
179
|
+
non-forced provider.
|
|
180
|
+
|
|
166
181
|
Note: If you're using `buildThemedStyles`, any `useStyles` calls must be done
|
|
167
182
|
within a *child component* of the `AtlantisThemeContextProvider`, not in the
|
|
168
183
|
same component that renders the provider. Same goes for `useAtlantisTheme`.
|
|
@@ -293,4 +308,6 @@ Key features:
|
|
|
293
308
|
| Prop | Type | Required | Default | Description |
|
|
294
309
|
|------|------|----------|---------|-------------|
|
|
295
310
|
| `children` | `ReactNode` | Yes | — | The children to render. |
|
|
296
|
-
| `dangerouslyOverrideTheme` | `
|
|
311
|
+
| `dangerouslyOverrideTheme` | `EffectiveTheme` | No | — | Force the theme for this provider to always be the same as the provided theme. Useful for sections that should remain... |
|
|
312
|
+
| `onThemeChange` | `(theme: Theme) => void` | No | — | Called when children request a selected theme change through setTheme. |
|
|
313
|
+
| `theme` | `Theme` | No | — | Control the selected theme for this provider. |
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.114.0",
|
|
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": "
|
|
126
|
+
"gitHead": "d79c88a2241ded3f29e879cd3c0d5a1e2dceb568"
|
|
127
127
|
}
|
|
@@ -1,32 +1,55 @@
|
|
|
1
1
|
import { androidTokens, darkTokens, iosTokens } from "@jobber/design";
|
|
2
|
-
import React, { createContext, useContext, useState } from "react";
|
|
2
|
+
import React, { createContext, useCallback, useContext, useState } from "react";
|
|
3
3
|
import merge from "lodash/merge";
|
|
4
|
-
import { Platform } from "react-native";
|
|
4
|
+
import { Platform, useColorScheme } from "react-native";
|
|
5
5
|
const lightTokens = Platform.select({
|
|
6
6
|
ios: () => iosTokens,
|
|
7
7
|
android: () => androidTokens,
|
|
8
8
|
default: () => androidTokens,
|
|
9
9
|
})();
|
|
10
10
|
const completeDarkTokens = merge({}, lightTokens, darkTokens);
|
|
11
|
-
const
|
|
11
|
+
const defaultAtlantisThemeContextValue = {
|
|
12
12
|
theme: "light",
|
|
13
|
+
effectiveTheme: "light",
|
|
13
14
|
tokens: lightTokens,
|
|
14
15
|
setTheme: () => {
|
|
15
16
|
console.error("useAtlantisTheme accessed outside of AtlantisThemeContextProvider");
|
|
16
17
|
},
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
18
|
+
};
|
|
19
|
+
const AtlantisThemeContext = createContext(undefined);
|
|
20
|
+
export function AtlantisThemeContextProvider({ children, theme, onThemeChange, dangerouslyOverrideTheme, }) {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
const parentThemeContext = useContext(AtlantisThemeContext);
|
|
23
|
+
const [localTheme, setLocalTheme] = useState(undefined);
|
|
24
|
+
const colorScheme = useColorScheme();
|
|
25
|
+
const setTheme = useCallback((nextTheme) => {
|
|
26
|
+
onThemeChange === null || onThemeChange === void 0 ? void 0 : onThemeChange(nextTheme);
|
|
27
|
+
if (theme !== undefined)
|
|
28
|
+
return;
|
|
29
|
+
// A forced visual override should preserve the parent subtree's selected
|
|
30
|
+
// theme semantics while still rendering with the forced tokens.
|
|
31
|
+
if (dangerouslyOverrideTheme !== undefined && parentThemeContext) {
|
|
32
|
+
parentThemeContext.setTheme(nextTheme);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
setLocalTheme(nextTheme);
|
|
36
|
+
}, [dangerouslyOverrideTheme, onThemeChange, parentThemeContext, theme]);
|
|
37
|
+
const currentTheme = (_b = (_a = theme !== null && theme !== void 0 ? theme : localTheme) !== null && _a !== void 0 ? _a : parentThemeContext === null || parentThemeContext === void 0 ? void 0 : parentThemeContext.theme) !== null && _b !== void 0 ? _b : "light";
|
|
38
|
+
const effectiveTheme = dangerouslyOverrideTheme !== null && dangerouslyOverrideTheme !== void 0 ? dangerouslyOverrideTheme : resolveEffectiveTheme(currentTheme, colorScheme);
|
|
39
|
+
const currentTokens = effectiveTheme === "dark" ? completeDarkTokens : lightTokens;
|
|
24
40
|
return (React.createElement(AtlantisThemeContext.Provider, { value: {
|
|
25
41
|
theme: currentTheme,
|
|
42
|
+
effectiveTheme,
|
|
26
43
|
tokens: currentTokens,
|
|
27
|
-
setTheme
|
|
44
|
+
setTheme,
|
|
28
45
|
} }, children));
|
|
29
46
|
}
|
|
30
47
|
export function useAtlantisTheme() {
|
|
31
|
-
|
|
48
|
+
var _a;
|
|
49
|
+
return (_a = useContext(AtlantisThemeContext)) !== null && _a !== void 0 ? _a : defaultAtlantisThemeContextValue;
|
|
50
|
+
}
|
|
51
|
+
function resolveEffectiveTheme(theme, colorScheme) {
|
|
52
|
+
if (theme !== "system")
|
|
53
|
+
return theme;
|
|
54
|
+
return colorScheme === "dark" ? "dark" : "light";
|
|
32
55
|
}
|
|
@@ -11,22 +11,36 @@ import React from "react";
|
|
|
11
11
|
import { act, renderHook } from "@testing-library/react-native";
|
|
12
12
|
import { darkTokens, iosTokens } from "@jobber/design";
|
|
13
13
|
import merge from "lodash/merge";
|
|
14
|
+
import { useColorScheme } from "react-native";
|
|
14
15
|
import { AtlantisThemeContextProvider, useAtlantisTheme, } from "./AtlantisThemeContext";
|
|
16
|
+
jest.mock("react-native", () => {
|
|
17
|
+
const actual = jest.requireActual("react-native");
|
|
18
|
+
Object.defineProperty(actual, "useColorScheme", {
|
|
19
|
+
configurable: true,
|
|
20
|
+
value: jest.fn(),
|
|
21
|
+
});
|
|
22
|
+
return actual;
|
|
23
|
+
});
|
|
15
24
|
const expectedDarkTokens = merge({}, iosTokens, darkTokens);
|
|
16
25
|
const expectedLightTokens = iosTokens;
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
const mockUseColorScheme = useColorScheme;
|
|
27
|
+
function Wrapper({ children, theme, onThemeChange, dangerouslyOverrideTheme, }) {
|
|
28
|
+
return (React.createElement(AtlantisThemeContextProvider, { theme: theme, onThemeChange: onThemeChange, dangerouslyOverrideTheme: dangerouslyOverrideTheme }, children));
|
|
19
29
|
}
|
|
20
30
|
function WrapperWithOverride({ children, dangerouslyOverrideTheme, }) {
|
|
21
31
|
return (React.createElement(Wrapper, null,
|
|
22
32
|
React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: dangerouslyOverrideTheme }, children)));
|
|
23
33
|
}
|
|
24
34
|
describe("ThemeContext", () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
mockUseColorScheme.mockReturnValue("light");
|
|
37
|
+
});
|
|
25
38
|
it("defaults to the light theme", () => {
|
|
26
39
|
const { result } = renderHook(useAtlantisTheme, {
|
|
27
40
|
wrapper: (props) => (React.createElement(Wrapper, Object.assign({}, props))),
|
|
28
41
|
});
|
|
29
42
|
expect(result.current.theme).toBe("light");
|
|
43
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
30
44
|
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
31
45
|
});
|
|
32
46
|
it("updates the theme and tokens", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -35,30 +49,109 @@ describe("ThemeContext", () => {
|
|
|
35
49
|
});
|
|
36
50
|
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("dark"); }));
|
|
37
51
|
expect(result.current.theme).toBe("dark");
|
|
52
|
+
expect(result.current.effectiveTheme).toBe("dark");
|
|
38
53
|
expect(result.current.tokens).toEqual(expectedDarkTokens);
|
|
39
54
|
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("light"); }));
|
|
40
55
|
expect(result.current.theme).toBe("light");
|
|
56
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
57
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
58
|
+
}));
|
|
59
|
+
it("resolves the system theme from the OS color scheme", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
60
|
+
mockUseColorScheme.mockReturnValue("dark");
|
|
61
|
+
const { result, rerender } = renderHook(useAtlantisTheme, {
|
|
62
|
+
wrapper: (props) => (React.createElement(Wrapper, Object.assign({}, props))),
|
|
63
|
+
});
|
|
64
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("system"); }));
|
|
65
|
+
expect(result.current.theme).toBe("system");
|
|
66
|
+
expect(result.current.effectiveTheme).toBe("dark");
|
|
67
|
+
expect(result.current.tokens).toEqual(expectedDarkTokens);
|
|
68
|
+
mockUseColorScheme.mockReturnValue("light");
|
|
69
|
+
rerender({});
|
|
70
|
+
expect(result.current.theme).toBe("system");
|
|
71
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
72
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
73
|
+
}));
|
|
74
|
+
it("uses a controlled selected theme", () => {
|
|
75
|
+
mockUseColorScheme.mockReturnValue("dark");
|
|
76
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
77
|
+
wrapper: (props) => (React.createElement(Wrapper, Object.assign({}, props, { theme: "system" }))),
|
|
78
|
+
});
|
|
79
|
+
expect(result.current.theme).toBe("system");
|
|
80
|
+
expect(result.current.effectiveTheme).toBe("dark");
|
|
81
|
+
expect(result.current.tokens).toEqual(expectedDarkTokens);
|
|
82
|
+
});
|
|
83
|
+
it("reports selected theme changes from a controlled provider", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
84
|
+
const onThemeChange = jest.fn();
|
|
85
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
86
|
+
wrapper: (props) => (React.createElement(Wrapper, Object.assign({}, props, { theme: "light", onThemeChange: onThemeChange }))),
|
|
87
|
+
});
|
|
88
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("system"); }));
|
|
89
|
+
expect(onThemeChange).toHaveBeenCalledWith("system");
|
|
90
|
+
expect(result.current.theme).toBe("light");
|
|
91
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
92
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
93
|
+
}));
|
|
94
|
+
it("falls back to light when system color scheme is unavailable", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
95
|
+
mockUseColorScheme.mockReturnValue(null);
|
|
96
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
97
|
+
wrapper: (props) => (React.createElement(Wrapper, Object.assign({}, props))),
|
|
98
|
+
});
|
|
99
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("system"); }));
|
|
100
|
+
expect(result.current.theme).toBe("system");
|
|
101
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
41
102
|
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
42
103
|
}));
|
|
43
104
|
describe("when theme is forced for provider", () => {
|
|
44
|
-
it("
|
|
105
|
+
it("preserves the forced effective theme when the selected theme changes", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
106
|
const { result } = renderHook(useAtlantisTheme, {
|
|
46
107
|
wrapper: (props) => (React.createElement(WrapperWithOverride, Object.assign({}, props, { dangerouslyOverrideTheme: "light" }))),
|
|
47
108
|
});
|
|
48
|
-
// Update the
|
|
109
|
+
// Update the selected theme through the forced subtree.
|
|
49
110
|
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("dark"); }));
|
|
50
|
-
|
|
51
|
-
expect(result.current.
|
|
111
|
+
expect(result.current.theme).toBe("dark");
|
|
112
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
113
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
114
|
+
}));
|
|
115
|
+
it("preserves the parent selected theme while forcing the effective theme", () => {
|
|
116
|
+
mockUseColorScheme.mockReturnValue("dark");
|
|
117
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
118
|
+
wrapper: (props) => (React.createElement(Wrapper, { theme: "system" },
|
|
119
|
+
React.createElement(AtlantisThemeContextProvider, Object.assign({ dangerouslyOverrideTheme: "light" }, props)))),
|
|
120
|
+
});
|
|
121
|
+
expect(result.current.theme).toBe("system");
|
|
122
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
123
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
124
|
+
});
|
|
125
|
+
it("forwards selected theme changes through a forced subtree", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
126
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
127
|
+
wrapper: (props) => (React.createElement(Wrapper, null,
|
|
128
|
+
React.createElement(AtlantisThemeContextProvider, Object.assign({ dangerouslyOverrideTheme: "light" }, props)))),
|
|
129
|
+
});
|
|
130
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("dark"); }));
|
|
131
|
+
expect(result.current.theme).toBe("dark");
|
|
132
|
+
expect(result.current.effectiveTheme).toBe("light");
|
|
52
133
|
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
53
134
|
}));
|
|
54
135
|
it.each([
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
136
|
+
{
|
|
137
|
+
defaultTheme: "light",
|
|
138
|
+
colorScheme: "dark",
|
|
139
|
+
expectedEffectiveTheme: "light",
|
|
140
|
+
expectedTokens: expectedLightTokens,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
defaultTheme: "dark",
|
|
144
|
+
colorScheme: "light",
|
|
145
|
+
expectedEffectiveTheme: "dark",
|
|
146
|
+
expectedTokens: expectedDarkTokens,
|
|
147
|
+
},
|
|
148
|
+
])("provides the dangerouslyOverrideTheme $defaultTheme tokens", ({ defaultTheme, colorScheme, expectedEffectiveTheme, expectedTokens, }) => {
|
|
149
|
+
mockUseColorScheme.mockReturnValue(colorScheme);
|
|
58
150
|
const { result } = renderHook(useAtlantisTheme, {
|
|
59
151
|
wrapper: (props) => (React.createElement(WrapperWithOverride, Object.assign({}, props, { dangerouslyOverrideTheme: defaultTheme }))),
|
|
60
152
|
});
|
|
61
|
-
expect(result.current.theme).toBe(
|
|
153
|
+
expect(result.current.theme).toBe("light");
|
|
154
|
+
expect(result.current.effectiveTheme).toBe(expectedEffectiveTheme);
|
|
62
155
|
expect(result.current.tokens).toEqual(expectedTokens);
|
|
63
156
|
});
|
|
64
157
|
});
|
|
@@ -7,12 +7,15 @@ import { tokens } from "@jobber/design";
|
|
|
7
7
|
import { useStyles } from "./BottomSheet.style";
|
|
8
8
|
import { BottomSheetOption } from "./components/BottomSheetOption";
|
|
9
9
|
import { useBottomSheetBackHandler } from "./hooks/useBottomSheetBackHandler";
|
|
10
|
+
import { AtlantisThemeContextProvider, useAtlantisTheme, } from "../AtlantisThemeContext";
|
|
10
11
|
import { Divider } from "../Divider";
|
|
11
12
|
import { Heading } from "../Heading";
|
|
12
13
|
import { useIsScreenReaderEnabled } from "../hooks";
|
|
13
14
|
import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
|
|
15
|
+
// eslint-disable-next-line max-statements
|
|
14
16
|
export function BottomSheet({ children, showCancel, loading = false, heading, onOpen, onClose, ref, }) {
|
|
15
17
|
const styles = useStyles();
|
|
18
|
+
const { effectiveTheme } = useAtlantisTheme();
|
|
16
19
|
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
17
20
|
const cancellable = (showCancel && !loading) || isScreenReaderEnabled;
|
|
18
21
|
const { t } = useAtlantisI18n();
|
|
@@ -48,13 +51,14 @@ export function BottomSheet({ children, showCancel, loading = false, heading, on
|
|
|
48
51
|
dismissKeyboard();
|
|
49
52
|
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
50
53
|
}, keyboardBlurBehavior: "restore", handleStyle: styles.handle },
|
|
51
|
-
React.createElement(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: effectiveTheme },
|
|
55
|
+
React.createElement(BottomSheetView, { style: {
|
|
56
|
+
paddingBottom: insets.bottom + tokens["space-small"],
|
|
57
|
+
paddingTop: tokens["space-small"],
|
|
58
|
+
} },
|
|
59
|
+
heading && React.createElement(Header, { heading: heading, styles: styles }),
|
|
60
|
+
children,
|
|
61
|
+
cancellable && (React.createElement(Footer, { styles: styles, close: close, cancelLabel: t("cancel") }))))));
|
|
58
62
|
}
|
|
59
63
|
function Header({ heading, styles, }) {
|
|
60
64
|
return (React.createElement(View, { style: styles.header },
|
|
@@ -12,6 +12,7 @@ import { act, render, userEvent, waitFor } from "@testing-library/react-native";
|
|
|
12
12
|
import { AccessibilityInfo, View } from "react-native";
|
|
13
13
|
import { BottomSheet } from ".";
|
|
14
14
|
import { waitForUntestableRender } from "../utils/test/wait";
|
|
15
|
+
import { AtlantisThemeContextProvider, useAtlantisTheme, } from "../AtlantisThemeContext";
|
|
15
16
|
import { Text } from "../Text";
|
|
16
17
|
jest.unmock("../hooks/useIsScreenReaderEnabled");
|
|
17
18
|
const ref = createRef();
|
|
@@ -31,6 +32,17 @@ it("renders a BottomSheet", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
31
32
|
}));
|
|
32
33
|
expect(yield findByText("BottomSheet")).toBeDefined();
|
|
33
34
|
}));
|
|
35
|
+
it("preserves the effective Atlantis theme for children rendered through the portal", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
+
const { findByText } = render(React.createElement(AtlantisThemeContextProvider, { theme: "dark" },
|
|
37
|
+
React.createElement(BottomSheet, { ref: ref },
|
|
38
|
+
React.createElement(ThemeProbe, null))));
|
|
39
|
+
yield waitForUntestableRender("Wait for AccessibilityInfo.isScreenReaderEnabled to resolve");
|
|
40
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
+
var _a;
|
|
42
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.open();
|
|
43
|
+
}));
|
|
44
|
+
expect(yield findByText("theme:dark")).toBeDefined();
|
|
45
|
+
}));
|
|
34
46
|
it("renders a BottomSheet with a header", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
47
|
const header = "Hello this is header";
|
|
36
48
|
const { findByText } = setup({ heading: header });
|
|
@@ -103,3 +115,7 @@ describe("when there is a screen reader enabled", () => {
|
|
|
103
115
|
});
|
|
104
116
|
}));
|
|
105
117
|
});
|
|
118
|
+
function ThemeProbe() {
|
|
119
|
+
const { effectiveTheme } = useAtlantisTheme();
|
|
120
|
+
return React.createElement(Text, null, `theme:${effectiveTheme}`);
|
|
121
|
+
}
|
|
@@ -54,7 +54,7 @@ export function ContentOverlay({ children, title, accessibilityLabel, fullScreen
|
|
|
54
54
|
const [currentPosition, setCurrentPosition] = useState(-1);
|
|
55
55
|
const styles = useStyles();
|
|
56
56
|
const { t } = useAtlantisI18n();
|
|
57
|
-
const {
|
|
57
|
+
const { effectiveTheme, tokens } = useAtlantisTheme();
|
|
58
58
|
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
59
59
|
const behavior = computeContentOverlayBehavior({
|
|
60
60
|
fullScreen,
|
|
@@ -179,7 +179,7 @@ export function ContentOverlay({ children, title, accessibilityLabel, fullScreen
|
|
|
179
179
|
return (React.createElement(Backdrop, Object.assign({}, props, { pressBehavior: isCloseableOnOverlayTap ? "close" : "none" })));
|
|
180
180
|
}, [isCloseableOnOverlayTap]);
|
|
181
181
|
return (React.createElement(BottomSheetModal, { ref: bottomSheetModalRef, containerComponent: Platform.OS === "ios" ? Container : undefined, onChange: handleChange, style: sheetStyle, backgroundStyle: backgroundStyle, handleStyle: styles.handleWrapper, handleIndicatorStyle: handleIndicatorStyles, backdropComponent: backdropComponent, snapPoints: snapPoints, enablePanDownToClose: enablePanDownToClose !== null && enablePanDownToClose !== void 0 ? enablePanDownToClose : effectiveIsDraggable, enableContentPanningGesture: enableContentPanningGesture !== null && enableContentPanningGesture !== void 0 ? enableContentPanningGesture : effectiveIsDraggable, enableHandlePanningGesture: effectiveIsDraggable, enableDynamicSizing: !customSnapPoints && behavior.initialHeight === "contentHeight", keyboardBehavior: "interactive", keyboardBlurBehavior: "restore", topInset: topInset, onDismiss: () => onClose === null || onClose === void 0 ? void 0 : onClose() },
|
|
182
|
-
React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme:
|
|
182
|
+
React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: effectiveTheme },
|
|
183
183
|
React.createElement(ContentOverlayKeyboardContext.Provider, { value: scrollEnabled }, scrollEnabled ? (React.createElement(BottomSheetKeyboardAwareScrollView, { ref: scrollViewRef, contentContainerStyle: { paddingBottom: insets.bottom }, keyboardShouldPersistTaps: keyboardShouldPersistTaps ? "handled" : "never", showsVerticalScrollIndicator: false, onScroll: handleOnScroll, stickyHeaderIndices: [0], bottomOffset: KEYBOARD_TOP_PADDING_AUTO_SCROLL },
|
|
184
184
|
renderHeader(),
|
|
185
185
|
React.createElement(View, { testID: "ATL-Overlay-Children" }, children))) : (React.createElement(BottomSheetView, null,
|