@oxyhq/bloom 0.17.0 → 0.18.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/lib/commonjs/admonition/index.js +24 -23
- package/lib/commonjs/admonition/index.js.map +1 -1
- package/lib/commonjs/combobox/Combobox.js +7 -7
- package/lib/commonjs/combobox/Combobox.js.map +1 -1
- package/lib/commonjs/context-menu/context.js +2 -2
- package/lib/commonjs/context-menu/context.js.map +1 -1
- package/lib/commonjs/context-menu/index.js +25 -25
- package/lib/commonjs/context-menu/index.js.map +1 -1
- package/lib/commonjs/context-menu/index.web.js +27 -27
- package/lib/commonjs/context-menu/index.web.js.map +1 -1
- package/lib/commonjs/dialog/SheetShell.js +1 -1
- package/lib/commonjs/index.js +134 -38
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +134 -38
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/input-group/InputGroup.js +1 -1
- package/lib/commonjs/menu/context.js +2 -2
- package/lib/commonjs/menu/context.js.map +1 -1
- package/lib/commonjs/menu/index.js +17 -17
- package/lib/commonjs/menu/index.js.map +1 -1
- package/lib/commonjs/menu/index.web.js +16 -16
- package/lib/commonjs/menu/index.web.js.map +1 -1
- package/lib/commonjs/popover/context.js +1 -1
- package/lib/commonjs/popover/context.js.map +1 -1
- package/lib/commonjs/popover/index.js +6 -6
- package/lib/commonjs/popover/index.js.map +1 -1
- package/lib/commonjs/popover/index.web.js +6 -6
- package/lib/commonjs/popover/index.web.js.map +1 -1
- package/lib/commonjs/search-input/index.js +4 -5
- package/lib/commonjs/search-input/index.js.map +1 -1
- package/lib/commonjs/segmented-control/index.js +18 -18
- package/lib/commonjs/segmented-control/index.js.map +1 -1
- package/lib/commonjs/select/common.js +2 -2
- package/lib/commonjs/select/common.js.map +1 -1
- package/lib/commonjs/select/index.js +34 -38
- package/lib/commonjs/select/index.js.map +1 -1
- package/lib/commonjs/select/index.web.js +31 -35
- package/lib/commonjs/select/index.web.js.map +1 -1
- package/lib/commonjs/tabs/Tabs.js +7 -7
- package/lib/commonjs/tabs/Tabs.js.map +1 -1
- package/lib/commonjs/tabs/index.js +6 -6
- package/lib/commonjs/text-field/index.js +14 -14
- package/lib/commonjs/text-field/index.js.map +1 -1
- package/lib/commonjs/theme/color-scope/style-builder.js +6 -6
- package/lib/commonjs/tooltip/index.js +11 -11
- package/lib/commonjs/tooltip/index.js.map +1 -1
- package/lib/commonjs/tooltip/index.web.js +11 -11
- package/lib/commonjs/tooltip/index.web.js.map +1 -1
- package/lib/module/admonition/index.js +18 -17
- package/lib/module/admonition/index.js.map +1 -1
- package/lib/module/combobox/Combobox.js +7 -7
- package/lib/module/combobox/Combobox.js.map +1 -1
- package/lib/module/context-menu/context.js +2 -2
- package/lib/module/context-menu/context.js.map +1 -1
- package/lib/module/context-menu/index.js +17 -17
- package/lib/module/context-menu/index.js.map +1 -1
- package/lib/module/context-menu/index.web.js +19 -19
- package/lib/module/context-menu/index.web.js.map +1 -1
- package/lib/module/dialog/SheetShell.js +1 -1
- package/lib/module/index.js +11 -11
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +11 -11
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/input-group/InputGroup.js +1 -1
- package/lib/module/menu/context.js +2 -2
- package/lib/module/menu/context.js.map +1 -1
- package/lib/module/menu/index.js +9 -9
- package/lib/module/menu/index.js.map +1 -1
- package/lib/module/menu/index.web.js +8 -8
- package/lib/module/menu/index.web.js.map +1 -1
- package/lib/module/popover/context.js +1 -1
- package/lib/module/popover/context.js.map +1 -1
- package/lib/module/popover/index.js +3 -3
- package/lib/module/popover/index.js.map +1 -1
- package/lib/module/popover/index.web.js +3 -3
- package/lib/module/popover/index.web.js.map +1 -1
- package/lib/module/search-input/index.js +4 -4
- package/lib/module/search-input/index.js.map +1 -1
- package/lib/module/segmented-control/index.js +15 -15
- package/lib/module/segmented-control/index.js.map +1 -1
- package/lib/module/select/common.js +1 -1
- package/lib/module/select/common.js.map +1 -1
- package/lib/module/select/index.js +25 -29
- package/lib/module/select/index.js.map +1 -1
- package/lib/module/select/index.web.js +22 -26
- package/lib/module/select/index.web.js.map +1 -1
- package/lib/module/tabs/Tabs.js +6 -6
- package/lib/module/tabs/Tabs.js.map +1 -1
- package/lib/module/tabs/index.js +1 -1
- package/lib/module/tabs/index.js.map +1 -1
- package/lib/module/text-field/index.js +8 -8
- package/lib/module/text-field/index.js.map +1 -1
- package/lib/module/theme/color-scope/style-builder.js +6 -6
- package/lib/module/tooltip/index.js +6 -6
- package/lib/module/tooltip/index.js.map +1 -1
- package/lib/module/tooltip/index.web.js +6 -6
- package/lib/module/tooltip/index.web.js.map +1 -1
- package/lib/typescript/commonjs/admonition/index.d.ts +14 -13
- package/lib/typescript/commonjs/admonition/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/combobox/Combobox.d.ts +7 -7
- package/lib/typescript/commonjs/combobox/Combobox.d.ts.map +1 -1
- package/lib/typescript/commonjs/context-menu/index.d.ts +9 -9
- package/lib/typescript/commonjs/context-menu/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/context-menu/index.web.d.ts +9 -9
- package/lib/typescript/commonjs/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/context-menu/types.d.ts +6 -6
- package/lib/typescript/commonjs/context-menu/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/SheetShell.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts +11 -11
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +11 -11
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/input-group/types.d.ts +1 -1
- package/lib/typescript/commonjs/menu/index.d.ts +9 -9
- package/lib/typescript/commonjs/menu/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/menu/index.web.d.ts +9 -9
- package/lib/typescript/commonjs/menu/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/menu/types.d.ts +5 -5
- package/lib/typescript/commonjs/menu/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/popover/index.d.ts +5 -5
- package/lib/typescript/commonjs/popover/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/popover/index.web.d.ts +5 -5
- package/lib/typescript/commonjs/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/popover/types.d.ts +1 -1
- package/lib/typescript/commonjs/popover/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/search-input/index.d.ts +3 -3
- package/lib/typescript/commonjs/search-input/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/segmented-control/index.d.ts +13 -13
- package/lib/typescript/commonjs/segmented-control/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/common.d.ts +1 -1
- package/lib/typescript/commonjs/select/common.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/index.d.ts +12 -12
- package/lib/typescript/commonjs/select/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/index.web.d.ts +12 -12
- package/lib/typescript/commonjs/select/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/types.d.ts +10 -10
- package/lib/typescript/commonjs/select/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/tabs/Tabs.d.ts +4 -4
- package/lib/typescript/commonjs/tabs/Tabs.d.ts.map +1 -1
- package/lib/typescript/commonjs/tabs/index.d.ts +2 -2
- package/lib/typescript/commonjs/tabs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/tabs/types.d.ts +3 -3
- package/lib/typescript/commonjs/tabs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/text-field/index.d.ts +8 -8
- package/lib/typescript/commonjs/text-field/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts +6 -6
- package/lib/typescript/commonjs/tooltip/index.d.ts +6 -6
- package/lib/typescript/commonjs/tooltip/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/tooltip/index.web.d.ts +6 -6
- package/lib/typescript/commonjs/tooltip/index.web.d.ts.map +1 -1
- package/lib/typescript/module/admonition/index.d.ts +14 -13
- package/lib/typescript/module/admonition/index.d.ts.map +1 -1
- package/lib/typescript/module/combobox/Combobox.d.ts +7 -7
- package/lib/typescript/module/combobox/Combobox.d.ts.map +1 -1
- package/lib/typescript/module/context-menu/index.d.ts +9 -9
- package/lib/typescript/module/context-menu/index.d.ts.map +1 -1
- package/lib/typescript/module/context-menu/index.web.d.ts +9 -9
- package/lib/typescript/module/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/module/context-menu/types.d.ts +6 -6
- package/lib/typescript/module/context-menu/types.d.ts.map +1 -1
- package/lib/typescript/module/dialog/SheetShell.d.ts +1 -1
- package/lib/typescript/module/index.d.ts +11 -11
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +11 -11
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/input-group/types.d.ts +1 -1
- package/lib/typescript/module/menu/index.d.ts +9 -9
- package/lib/typescript/module/menu/index.d.ts.map +1 -1
- package/lib/typescript/module/menu/index.web.d.ts +9 -9
- package/lib/typescript/module/menu/index.web.d.ts.map +1 -1
- package/lib/typescript/module/menu/types.d.ts +5 -5
- package/lib/typescript/module/menu/types.d.ts.map +1 -1
- package/lib/typescript/module/popover/index.d.ts +5 -5
- package/lib/typescript/module/popover/index.d.ts.map +1 -1
- package/lib/typescript/module/popover/index.web.d.ts +5 -5
- package/lib/typescript/module/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/module/popover/types.d.ts +1 -1
- package/lib/typescript/module/popover/types.d.ts.map +1 -1
- package/lib/typescript/module/search-input/index.d.ts +3 -3
- package/lib/typescript/module/search-input/index.d.ts.map +1 -1
- package/lib/typescript/module/segmented-control/index.d.ts +13 -13
- package/lib/typescript/module/segmented-control/index.d.ts.map +1 -1
- package/lib/typescript/module/select/common.d.ts +1 -1
- package/lib/typescript/module/select/common.d.ts.map +1 -1
- package/lib/typescript/module/select/index.d.ts +12 -12
- package/lib/typescript/module/select/index.d.ts.map +1 -1
- package/lib/typescript/module/select/index.web.d.ts +12 -12
- package/lib/typescript/module/select/index.web.d.ts.map +1 -1
- package/lib/typescript/module/select/types.d.ts +10 -10
- package/lib/typescript/module/select/types.d.ts.map +1 -1
- package/lib/typescript/module/tabs/Tabs.d.ts +4 -4
- package/lib/typescript/module/tabs/Tabs.d.ts.map +1 -1
- package/lib/typescript/module/tabs/index.d.ts +2 -2
- package/lib/typescript/module/tabs/index.d.ts.map +1 -1
- package/lib/typescript/module/tabs/types.d.ts +3 -3
- package/lib/typescript/module/tabs/types.d.ts.map +1 -1
- package/lib/typescript/module/text-field/index.d.ts +8 -8
- package/lib/typescript/module/text-field/index.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts +6 -6
- package/lib/typescript/module/tooltip/index.d.ts +6 -6
- package/lib/typescript/module/tooltip/index.d.ts.map +1 -1
- package/lib/typescript/module/tooltip/index.web.d.ts +6 -6
- package/lib/typescript/module/tooltip/index.web.d.ts.map +1 -1
- package/package.json +1 -7
- package/src/__tests__/public-api-contract.test.ts +19 -7
- package/src/__tests__/useNavigationTheme.test.tsx +227 -0
- package/src/admonition/index.tsx +22 -21
- package/src/combobox/Combobox.tsx +14 -14
- package/src/context-menu/ContextMenu.stories.tsx +26 -18
- package/src/context-menu/context.ts +2 -2
- package/src/context-menu/index.tsx +24 -24
- package/src/context-menu/index.web.tsx +26 -26
- package/src/context-menu/types.ts +6 -6
- package/src/dialog/SheetShell.tsx +1 -1
- package/src/field/Field.stories.tsx +3 -3
- package/src/index.ts +11 -11
- package/src/index.web.ts +11 -11
- package/src/input-group/InputGroup.stories.tsx +4 -4
- package/src/input-group/InputGroup.tsx +1 -1
- package/src/input-group/types.ts +1 -1
- package/src/menu/Menu.stories.tsx +35 -35
- package/src/menu/context.ts +2 -2
- package/src/menu/index.tsx +15 -15
- package/src/menu/index.web.tsx +14 -14
- package/src/menu/types.ts +5 -5
- package/src/popover/Popover.stories.tsx +13 -13
- package/src/popover/context.ts +1 -1
- package/src/popover/index.tsx +5 -5
- package/src/popover/index.web.tsx +5 -5
- package/src/popover/types.ts +1 -1
- package/src/search-input/index.tsx +7 -7
- package/src/segmented-control/index.tsx +15 -15
- package/src/select/Select.stories.tsx +32 -23
- package/src/select/common.ts +1 -1
- package/src/select/index.tsx +39 -43
- package/src/select/index.web.tsx +33 -37
- package/src/select/types.ts +10 -10
- package/src/tabs/Tabs.tsx +9 -9
- package/src/tabs/index.ts +2 -2
- package/src/tabs/types.ts +3 -3
- package/src/text-field/TextField.stories.tsx +5 -5
- package/src/text-field/index.tsx +12 -12
- package/src/theme/color-scope/style-builder.ts +6 -6
- package/src/tooltip/index.tsx +6 -6
- package/src/tooltip/index.web.tsx +6 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -880,12 +880,6 @@
|
|
|
880
880
|
"react-dom": {
|
|
881
881
|
"optional": true
|
|
882
882
|
},
|
|
883
|
-
"react-native-reanimated": {
|
|
884
|
-
"optional": true
|
|
885
|
-
},
|
|
886
|
-
"react-native-gesture-handler": {
|
|
887
|
-
"optional": true
|
|
888
|
-
},
|
|
889
883
|
"react-native-svg": {
|
|
890
884
|
"optional": true
|
|
891
885
|
},
|
|
@@ -3,12 +3,10 @@ import * as Bloom from '../index';
|
|
|
3
3
|
type AnyExports = Record<string, unknown>;
|
|
4
4
|
const api = Bloom as AnyExports;
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
// generic-colliding part names). Everything else is flat-with-prefix.
|
|
6
|
+
// Collections / generic-colliding part names that intentionally STAY namespaced.
|
|
8
7
|
const ALLOWED_NAMESPACES = ['Icons', 'Typography', 'Skeleton', 'Grid', 'Code', 'Fonts'] as const;
|
|
9
8
|
|
|
10
|
-
// Compound families converted to flat:
|
|
11
|
-
// root component (a function), no longer a namespace object.
|
|
9
|
+
// Compound families converted to flat: the top-level name is the root component.
|
|
12
10
|
const FLATTENED_ROOTS = [
|
|
13
11
|
'Tabs', 'Accordion', 'Select', 'Menu', 'ContextMenu', 'Popover',
|
|
14
12
|
'Tooltip', 'SegmentedControl', 'TextField', 'Admonition', 'PromptInput',
|
|
@@ -21,22 +19,36 @@ const REPRESENTATIVE_PARTS = [
|
|
|
21
19
|
'AdmonitionIcon', 'PromptInputTextarea',
|
|
22
20
|
] as const;
|
|
23
21
|
|
|
22
|
+
/**
|
|
23
|
+
* A flat React component export is either a plain function component or a wrapped
|
|
24
|
+
* component object (React.memo / forwardRef), which carries a `$$typeof` symbol.
|
|
25
|
+
* A `export * as X` namespace is a plain object WITHOUT `$$typeof`.
|
|
26
|
+
*/
|
|
27
|
+
function isReactComponent(value: unknown): boolean {
|
|
28
|
+
if (typeof value === 'function') return true;
|
|
29
|
+
if (typeof value === 'object' && value !== null) {
|
|
30
|
+
return typeof (value as { $$typeof?: unknown }).$$typeof === 'symbol';
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
24
35
|
describe('public API contract', () => {
|
|
25
|
-
it('keeps the six collection families as namespace objects', () => {
|
|
36
|
+
it('keeps the six collection families as namespace objects (not components)', () => {
|
|
26
37
|
for (const ns of ALLOWED_NAMESPACES) {
|
|
27
38
|
expect(typeof api[ns]).toBe('object');
|
|
39
|
+
expect(isReactComponent(api[ns])).toBe(false);
|
|
28
40
|
}
|
|
29
41
|
});
|
|
30
42
|
|
|
31
43
|
it('exposes each compound family root as a flat component (not a namespace)', () => {
|
|
32
44
|
for (const name of FLATTENED_ROOTS) {
|
|
33
|
-
expect(
|
|
45
|
+
expect(isReactComponent(api[name])).toBe(true);
|
|
34
46
|
}
|
|
35
47
|
});
|
|
36
48
|
|
|
37
49
|
it('exposes compound parts as flat prefixed components', () => {
|
|
38
50
|
for (const name of REPRESENTATIVE_PARTS) {
|
|
39
|
-
expect(
|
|
51
|
+
expect(isReactComponent(api[name])).toBe(true);
|
|
40
52
|
}
|
|
41
53
|
});
|
|
42
54
|
});
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import { Text, View } from 'react-native';
|
|
3
|
+
import { render } from '@testing-library/react-native';
|
|
4
|
+
|
|
5
|
+
let mockColorScheme: 'light' | 'dark' | null = 'light';
|
|
6
|
+
|
|
7
|
+
jest.mock('react-native', () => {
|
|
8
|
+
const actual = jest.requireActual('react-native');
|
|
9
|
+
return {
|
|
10
|
+
...actual,
|
|
11
|
+
useColorScheme: () => mockColorScheme,
|
|
12
|
+
Appearance: {
|
|
13
|
+
...actual.Appearance,
|
|
14
|
+
setColorScheme: jest.fn(),
|
|
15
|
+
getColorScheme: () => mockColorScheme,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
import { BloomThemeProvider } from '../theme/BloomThemeProvider';
|
|
21
|
+
import { useTheme } from '../theme/use-theme';
|
|
22
|
+
import { useNavigationTheme } from '../theme/use-navigation-theme';
|
|
23
|
+
import type { NavigationTheme } from '../theme/use-navigation-theme';
|
|
24
|
+
|
|
25
|
+
// Probe that captures both the navigation theme and the raw bloom theme
|
|
26
|
+
// into external refs so tests can inspect them after render.
|
|
27
|
+
function Probe({
|
|
28
|
+
navRef,
|
|
29
|
+
bloomRef,
|
|
30
|
+
}: {
|
|
31
|
+
navRef: React.MutableRefObject<NavigationTheme | null>;
|
|
32
|
+
bloomRef: React.MutableRefObject<ReturnType<typeof useTheme> | null>;
|
|
33
|
+
}) {
|
|
34
|
+
const nav = useNavigationTheme();
|
|
35
|
+
const bloom = useTheme();
|
|
36
|
+
navRef.current = nav;
|
|
37
|
+
bloomRef.current = bloom;
|
|
38
|
+
return (
|
|
39
|
+
<View>
|
|
40
|
+
<Text testID="dark">{String(nav.dark)}</Text>
|
|
41
|
+
<Text testID="primary">{nav.colors.primary}</Text>
|
|
42
|
+
<Text testID="notification">{nav.colors.notification}</Text>
|
|
43
|
+
</View>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Probe that captures the navigation theme reference twice across renders,
|
|
48
|
+
// storing it in an array so the test can assert referential stability.
|
|
49
|
+
function MemoProbe({
|
|
50
|
+
capturesRef,
|
|
51
|
+
triggerProp,
|
|
52
|
+
}: {
|
|
53
|
+
capturesRef: React.MutableRefObject<NavigationTheme[]>;
|
|
54
|
+
triggerProp: number;
|
|
55
|
+
}) {
|
|
56
|
+
void triggerProp; // read the prop so React re-renders when it changes
|
|
57
|
+
const nav = useNavigationTheme();
|
|
58
|
+
capturesRef.current.push(nav);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
mockColorScheme = 'light';
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('useNavigationTheme', () => {
|
|
67
|
+
describe('dark flag', () => {
|
|
68
|
+
it('returns dark=false under explicit light mode', () => {
|
|
69
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
70
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
71
|
+
render(
|
|
72
|
+
<BloomThemeProvider mode="light">
|
|
73
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
74
|
+
</BloomThemeProvider>,
|
|
75
|
+
);
|
|
76
|
+
expect(navRef.current?.dark).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('returns dark=true under explicit dark mode', () => {
|
|
80
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
81
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
82
|
+
render(
|
|
83
|
+
<BloomThemeProvider mode="dark">
|
|
84
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
85
|
+
</BloomThemeProvider>,
|
|
86
|
+
);
|
|
87
|
+
expect(navRef.current?.dark).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns dark=false when system mode resolves to light', () => {
|
|
91
|
+
mockColorScheme = 'light';
|
|
92
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
93
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
94
|
+
render(
|
|
95
|
+
<BloomThemeProvider mode="system">
|
|
96
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
97
|
+
</BloomThemeProvider>,
|
|
98
|
+
);
|
|
99
|
+
expect(navRef.current?.dark).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns dark=true when system mode resolves to dark', () => {
|
|
103
|
+
mockColorScheme = 'dark';
|
|
104
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
105
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
106
|
+
render(
|
|
107
|
+
<BloomThemeProvider mode="system">
|
|
108
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
109
|
+
</BloomThemeProvider>,
|
|
110
|
+
);
|
|
111
|
+
expect(navRef.current?.dark).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('color mapping', () => {
|
|
116
|
+
it('maps primary/background/card/text/border 1:1 from useTheme().colors', () => {
|
|
117
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
118
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
119
|
+
render(
|
|
120
|
+
<BloomThemeProvider mode="light" colorPreset="blue">
|
|
121
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
122
|
+
</BloomThemeProvider>,
|
|
123
|
+
);
|
|
124
|
+
const nav = navRef.current;
|
|
125
|
+
const bloom = bloomRef.current;
|
|
126
|
+
expect(nav).not.toBeNull();
|
|
127
|
+
expect(bloom).not.toBeNull();
|
|
128
|
+
expect(nav!.colors.primary).toBe(bloom!.colors.primary);
|
|
129
|
+
expect(nav!.colors.background).toBe(bloom!.colors.background);
|
|
130
|
+
expect(nav!.colors.card).toBe(bloom!.colors.card);
|
|
131
|
+
expect(nav!.colors.text).toBe(bloom!.colors.text);
|
|
132
|
+
expect(nav!.colors.border).toBe(bloom!.colors.border);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('maps notification to colors.error (not a direct key)', () => {
|
|
136
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
137
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
138
|
+
render(
|
|
139
|
+
<BloomThemeProvider mode="light" colorPreset="blue">
|
|
140
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
141
|
+
</BloomThemeProvider>,
|
|
142
|
+
);
|
|
143
|
+
expect(navRef.current!.colors.notification).toBe(
|
|
144
|
+
bloomRef.current!.colors.error,
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('colors differ between light and dark presets (sanity check)', () => {
|
|
149
|
+
const lightNavRef = { current: null as NavigationTheme | null };
|
|
150
|
+
const lightBloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
151
|
+
const { unmount } = render(
|
|
152
|
+
<BloomThemeProvider mode="light" colorPreset="teal">
|
|
153
|
+
<Probe navRef={lightNavRef} bloomRef={lightBloomRef} />
|
|
154
|
+
</BloomThemeProvider>,
|
|
155
|
+
);
|
|
156
|
+
const lightBg = lightNavRef.current!.colors.background;
|
|
157
|
+
unmount();
|
|
158
|
+
|
|
159
|
+
const darkNavRef = { current: null as NavigationTheme | null };
|
|
160
|
+
const darkBloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
161
|
+
render(
|
|
162
|
+
<BloomThemeProvider mode="dark" colorPreset="teal">
|
|
163
|
+
<Probe navRef={darkNavRef} bloomRef={darkBloomRef} />
|
|
164
|
+
</BloomThemeProvider>,
|
|
165
|
+
);
|
|
166
|
+
const darkBg = darkNavRef.current!.colors.background;
|
|
167
|
+
|
|
168
|
+
expect(lightBg).not.toBe(darkBg);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('fonts shape', () => {
|
|
173
|
+
it('has all four font variants with System fontFamily', () => {
|
|
174
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
175
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
176
|
+
render(
|
|
177
|
+
<BloomThemeProvider mode="light">
|
|
178
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
179
|
+
</BloomThemeProvider>,
|
|
180
|
+
);
|
|
181
|
+
const fonts = navRef.current!.fonts;
|
|
182
|
+
for (const key of ['regular', 'medium', 'bold', 'heavy'] as const) {
|
|
183
|
+
expect(fonts[key].fontFamily).toBe('System');
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('has correct fontWeight values for each variant', () => {
|
|
188
|
+
const navRef = { current: null as NavigationTheme | null };
|
|
189
|
+
const bloomRef = { current: null as ReturnType<typeof useTheme> | null };
|
|
190
|
+
render(
|
|
191
|
+
<BloomThemeProvider mode="light">
|
|
192
|
+
<Probe navRef={navRef} bloomRef={bloomRef} />
|
|
193
|
+
</BloomThemeProvider>,
|
|
194
|
+
);
|
|
195
|
+
const fonts = navRef.current!.fonts;
|
|
196
|
+
expect(fonts.regular.fontWeight).toBe('400');
|
|
197
|
+
expect(fonts.medium.fontWeight).toBe('500');
|
|
198
|
+
expect(fonts.bold.fontWeight).toBe('700');
|
|
199
|
+
expect(fonts.heavy.fontWeight).toBe('900');
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('memo stability', () => {
|
|
204
|
+
it('returns a referentially-equal object on re-render when theme is unchanged', () => {
|
|
205
|
+
const capturesRef: React.MutableRefObject<NavigationTheme[]> = {
|
|
206
|
+
current: [],
|
|
207
|
+
};
|
|
208
|
+
const { rerender } = render(
|
|
209
|
+
<BloomThemeProvider mode="light">
|
|
210
|
+
<MemoProbe capturesRef={capturesRef} triggerProp={0} />
|
|
211
|
+
</BloomThemeProvider>,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// Re-render without changing mode or colorPreset — memo should hold.
|
|
215
|
+
rerender(
|
|
216
|
+
<BloomThemeProvider mode="light">
|
|
217
|
+
<MemoProbe capturesRef={capturesRef} triggerProp={1} />
|
|
218
|
+
</BloomThemeProvider>,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
expect(capturesRef.current.length).toBeGreaterThanOrEqual(2);
|
|
222
|
+
const first = capturesRef.current[0];
|
|
223
|
+
const second = capturesRef.current[1];
|
|
224
|
+
expect(Object.is(first, second)).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
package/src/admonition/index.tsx
CHANGED
|
@@ -28,7 +28,7 @@ const ICON_MAP = {
|
|
|
28
28
|
apology: EmojiSadIcon,
|
|
29
29
|
} as const;
|
|
30
30
|
|
|
31
|
-
export function
|
|
31
|
+
export function AdmonitionIcon() {
|
|
32
32
|
const theme = useTheme();
|
|
33
33
|
const { type } = useContext(AdmonitionContext);
|
|
34
34
|
|
|
@@ -44,9 +44,9 @@ export function Icon() {
|
|
|
44
44
|
|
|
45
45
|
return <IconComponent fill={fillMap[type]} size="md" />;
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
AdmonitionIcon.displayName = 'AdmonitionIcon';
|
|
48
48
|
|
|
49
|
-
export function
|
|
49
|
+
export function AdmonitionContent({
|
|
50
50
|
children,
|
|
51
51
|
style,
|
|
52
52
|
}: {
|
|
@@ -59,9 +59,9 @@ export function Content({
|
|
|
59
59
|
</View>
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
AdmonitionContent.displayName = 'AdmonitionContent';
|
|
63
63
|
|
|
64
|
-
export function
|
|
64
|
+
export function AdmonitionText({
|
|
65
65
|
children,
|
|
66
66
|
style,
|
|
67
67
|
}: {
|
|
@@ -74,9 +74,9 @@ export function Text({
|
|
|
74
74
|
</BaseText>
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
AdmonitionText.displayName = 'AdmonitionText';
|
|
78
78
|
|
|
79
|
-
export function
|
|
79
|
+
export function AdmonitionButton({
|
|
80
80
|
children,
|
|
81
81
|
...props
|
|
82
82
|
}: Omit<ButtonProps, 'size' | 'variant'>) {
|
|
@@ -86,9 +86,9 @@ export function Button({
|
|
|
86
86
|
</BaseButton>
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
AdmonitionButton.displayName = 'AdmonitionButton';
|
|
90
90
|
|
|
91
|
-
export function
|
|
91
|
+
export function AdmonitionRow({
|
|
92
92
|
children,
|
|
93
93
|
style,
|
|
94
94
|
}: {
|
|
@@ -101,9 +101,9 @@ export function Row({
|
|
|
101
101
|
</View>
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
AdmonitionRow.displayName = 'AdmonitionRow';
|
|
105
105
|
|
|
106
|
-
export function
|
|
106
|
+
export function AdmonitionRoot({
|
|
107
107
|
children,
|
|
108
108
|
type = 'info',
|
|
109
109
|
style,
|
|
@@ -139,11 +139,12 @@ export function Outer({
|
|
|
139
139
|
</AdmonitionContext.Provider>
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
|
-
|
|
142
|
+
AdmonitionRoot.displayName = 'AdmonitionRoot';
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
145
|
* Simple all-in-one Admonition component for common use cases.
|
|
146
|
-
* For more control, compose with
|
|
146
|
+
* For more control, compose with AdmonitionRoot, AdmonitionRow, AdmonitionIcon,
|
|
147
|
+
* AdmonitionContent, AdmonitionText, and AdmonitionButton.
|
|
147
148
|
*/
|
|
148
149
|
export function Admonition({
|
|
149
150
|
children,
|
|
@@ -155,14 +156,14 @@ export function Admonition({
|
|
|
155
156
|
style?: StyleProp<ViewStyle>;
|
|
156
157
|
}) {
|
|
157
158
|
return (
|
|
158
|
-
<
|
|
159
|
-
<
|
|
160
|
-
<
|
|
161
|
-
<
|
|
162
|
-
<
|
|
163
|
-
</
|
|
164
|
-
</
|
|
165
|
-
</
|
|
159
|
+
<AdmonitionRoot type={type} style={style}>
|
|
160
|
+
<AdmonitionRow>
|
|
161
|
+
<AdmonitionIcon />
|
|
162
|
+
<AdmonitionContent>
|
|
163
|
+
<AdmonitionText>{children}</AdmonitionText>
|
|
164
|
+
</AdmonitionContent>
|
|
165
|
+
</AdmonitionRow>
|
|
166
|
+
</AdmonitionRoot>
|
|
166
167
|
);
|
|
167
168
|
}
|
|
168
169
|
Admonition.displayName = 'Admonition';
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { Check_Stroke2_Corner0_Rounded as CheckIcon } from '../icons/Check';
|
|
21
21
|
import type {
|
|
22
22
|
PopoverContentProps,
|
|
23
|
-
|
|
23
|
+
PopoverProps,
|
|
24
24
|
PopoverTriggerProps,
|
|
25
25
|
} from '../popover/types';
|
|
26
26
|
import type { usePopoverContext as usePopoverContextType } from '../popover/context';
|
|
@@ -29,13 +29,13 @@ import type { ComboboxOption, ComboboxProps } from './types';
|
|
|
29
29
|
/**
|
|
30
30
|
* Structural shape of the `Popover` module, satisfied by both the native and
|
|
31
31
|
* web platform forks. Defined explicitly (rather than `typeof import(...)`) so
|
|
32
|
-
* the slightly different render-return types between forks (web `
|
|
33
|
-
* return `null`) don't make the modules mutually unassignable.
|
|
32
|
+
* the slightly different render-return types between forks (web `PopoverContent`
|
|
33
|
+
* may return `null`) don't make the modules mutually unassignable.
|
|
34
34
|
*/
|
|
35
35
|
interface PopoverModule {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
Popover: (props: PopoverProps) => React.ReactElement;
|
|
37
|
+
PopoverTrigger: (props: PopoverTriggerProps) => React.ReactElement;
|
|
38
|
+
PopoverContent: (props: PopoverContentProps) => React.ReactElement | null;
|
|
39
39
|
usePopoverContext: typeof usePopoverContextType;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -59,7 +59,7 @@ function defaultFilter<T>(option: ComboboxOption<T>, query: string): boolean {
|
|
|
59
59
|
* read the overlay control from `Popover` context (via an inner component) so
|
|
60
60
|
* selecting an option reliably closes the overlay on every platform.
|
|
61
61
|
*/
|
|
62
|
-
export function createCombobox(
|
|
62
|
+
export function createCombobox(PopoverImpl: PopoverModule) {
|
|
63
63
|
function ComboboxInner<T>({
|
|
64
64
|
options,
|
|
65
65
|
value,
|
|
@@ -80,7 +80,7 @@ export function createCombobox(Popover: PopoverModule) {
|
|
|
80
80
|
> &
|
|
81
81
|
Pick<ComboboxProps<T>, 'label' | 'query' | 'onQueryChange' | 'disabled'>) {
|
|
82
82
|
const theme = useTheme();
|
|
83
|
-
const { control } =
|
|
83
|
+
const { control } = PopoverImpl.usePopoverContext();
|
|
84
84
|
const searchRef = useRef<TextInput>(null);
|
|
85
85
|
|
|
86
86
|
const [query, setQuery] = useControllableState<string>({
|
|
@@ -113,7 +113,7 @@ export function createCombobox(Popover: PopoverModule) {
|
|
|
113
113
|
|
|
114
114
|
return (
|
|
115
115
|
<>
|
|
116
|
-
<
|
|
116
|
+
<PopoverImpl.PopoverTrigger label={accessibleLabel}>
|
|
117
117
|
{({ props }) => (
|
|
118
118
|
<Pressable
|
|
119
119
|
{...props}
|
|
@@ -149,9 +149,9 @@ export function createCombobox(Popover: PopoverModule) {
|
|
|
149
149
|
<ChevronUpDownIcon size="xs" fill={theme.colors.textSecondary} />
|
|
150
150
|
</Pressable>
|
|
151
151
|
)}
|
|
152
|
-
</
|
|
152
|
+
</PopoverImpl.PopoverTrigger>
|
|
153
153
|
|
|
154
|
-
<
|
|
154
|
+
<PopoverImpl.PopoverContent label={accessibleLabel} placement="bottom-start" minWidth={240}>
|
|
155
155
|
<View style={styles.searchWrap}>
|
|
156
156
|
<SearchInput
|
|
157
157
|
ref={searchRef}
|
|
@@ -196,7 +196,7 @@ export function createCombobox(Popover: PopoverModule) {
|
|
|
196
196
|
})}
|
|
197
197
|
</ScrollView>
|
|
198
198
|
)}
|
|
199
|
-
</
|
|
199
|
+
</PopoverImpl.PopoverContent>
|
|
200
200
|
</>
|
|
201
201
|
);
|
|
202
202
|
}
|
|
@@ -217,7 +217,7 @@ export function createCombobox(Popover: PopoverModule) {
|
|
|
217
217
|
testID,
|
|
218
218
|
}: ComboboxProps<T>) {
|
|
219
219
|
return (
|
|
220
|
-
<Popover
|
|
220
|
+
<PopoverImpl.Popover>
|
|
221
221
|
<View style={style} testID={testID}>
|
|
222
222
|
<ComboboxInner<T>
|
|
223
223
|
options={options}
|
|
@@ -233,7 +233,7 @@ export function createCombobox(Popover: PopoverModule) {
|
|
|
233
233
|
maxListHeight={maxListHeight}
|
|
234
234
|
/>
|
|
235
235
|
</View>
|
|
236
|
-
</Popover
|
|
236
|
+
</PopoverImpl.Popover>
|
|
237
237
|
);
|
|
238
238
|
}
|
|
239
239
|
|
|
@@ -3,7 +3,15 @@ import { Pressable, Text, View } from 'react-native';
|
|
|
3
3
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
4
|
|
|
5
5
|
import { useTheme } from '../theme/use-theme';
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
ContextMenu,
|
|
8
|
+
ContextMenuContent,
|
|
9
|
+
ContextMenuDivider,
|
|
10
|
+
ContextMenuGroup,
|
|
11
|
+
ContextMenuItem,
|
|
12
|
+
ContextMenuItemText,
|
|
13
|
+
ContextMenuTrigger,
|
|
14
|
+
} from './index';
|
|
7
15
|
|
|
8
16
|
const meta: Meta = {
|
|
9
17
|
title: 'Components/ContextMenu',
|
|
@@ -16,8 +24,8 @@ type Story = StoryObj;
|
|
|
16
24
|
function TriggerSurface() {
|
|
17
25
|
const theme = useTheme();
|
|
18
26
|
return (
|
|
19
|
-
<ContextMenu
|
|
20
|
-
<
|
|
27
|
+
<ContextMenu>
|
|
28
|
+
<ContextMenuTrigger label="Long-press for actions">
|
|
21
29
|
{({ props }) => (
|
|
22
30
|
<Pressable
|
|
23
31
|
onPress={() => props.onPress?.()}
|
|
@@ -39,21 +47,21 @@ function TriggerSurface() {
|
|
|
39
47
|
</Text>
|
|
40
48
|
</Pressable>
|
|
41
49
|
)}
|
|
42
|
-
</
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
</
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
</
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
</
|
|
54
|
-
</
|
|
55
|
-
</
|
|
56
|
-
</ContextMenu
|
|
50
|
+
</ContextMenuTrigger>
|
|
51
|
+
<ContextMenuContent>
|
|
52
|
+
<ContextMenuGroup>
|
|
53
|
+
<ContextMenuItem label="Open" onPress={() => {}}>
|
|
54
|
+
<ContextMenuItemText>Open</ContextMenuItemText>
|
|
55
|
+
</ContextMenuItem>
|
|
56
|
+
<ContextMenuItem label="Rename" onPress={() => {}}>
|
|
57
|
+
<ContextMenuItemText>Rename</ContextMenuItemText>
|
|
58
|
+
</ContextMenuItem>
|
|
59
|
+
<ContextMenuItem label="Delete" onPress={() => {}}>
|
|
60
|
+
<ContextMenuItemText>Delete</ContextMenuItemText>
|
|
61
|
+
</ContextMenuItem>
|
|
62
|
+
</ContextMenuGroup>
|
|
63
|
+
</ContextMenuContent>
|
|
64
|
+
</ContextMenu>
|
|
57
65
|
);
|
|
58
66
|
}
|
|
59
67
|
|
|
@@ -21,7 +21,7 @@ export function useContextMenuContext(): ContextMenuContextValue {
|
|
|
21
21
|
const ctx = useContext(ContextMenuContext);
|
|
22
22
|
if (!ctx) {
|
|
23
23
|
throw new Error(
|
|
24
|
-
'ContextMenu components must be used within a ContextMenu
|
|
24
|
+
'ContextMenu components must be used within a <ContextMenu>',
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
27
|
return ctx;
|
|
@@ -38,7 +38,7 @@ export function useItemContext(): ItemContextValue {
|
|
|
38
38
|
const ctx = useContext(ItemCtx);
|
|
39
39
|
if (!ctx) {
|
|
40
40
|
throw new Error(
|
|
41
|
-
'
|
|
41
|
+
'ContextMenuItemText/ContextMenuItemIcon must be used within a <ContextMenuItem>',
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
return ctx;
|