@dxos/plugin-testing 0.8.4-main.d05673bc65 → 0.8.4-main.fcc0d83b33
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/lib/browser/harness.mjs +35 -0
- package/dist/lib/browser/harness.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +21 -166
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/harness.mjs +37 -0
- package/dist/lib/node-esm/harness.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +21 -165
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/StorybookPlugin.d.ts +1 -1
- package/dist/types/src/StorybookPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +15 -2
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/{state/state.d.ts → state.d.ts} +1 -1
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/components/Layout/Layout.d.ts.map +1 -1
- package/dist/types/src/core.d.ts +1 -7
- package/dist/types/src/core.d.ts.map +1 -1
- package/dist/types/src/harness.d.ts +20 -0
- package/dist/types/src/harness.d.ts.map +1 -0
- package/dist/types/src/operations/add-toast.d.ts +5 -0
- package/dist/types/src/operations/add-toast.d.ts.map +1 -0
- package/dist/types/src/operations/close.d.ts +5 -0
- package/dist/types/src/operations/close.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/open.d.ts +5 -0
- package/dist/types/src/operations/open.d.ts.map +1 -0
- package/dist/types/src/operations/scroll-into-view.d.ts +5 -0
- package/dist/types/src/operations/scroll-into-view.d.ts.map +1 -0
- package/dist/types/src/operations/set-layout-mode.d.ts +5 -0
- package/dist/types/src/operations/set-layout-mode.d.ts.map +1 -0
- package/dist/types/src/operations/switch-workspace.d.ts +5 -0
- package/dist/types/src/operations/switch-workspace.d.ts.map +1 -0
- package/dist/types/src/operations/update-complementary.d.ts +5 -0
- package/dist/types/src/operations/update-complementary.d.ts.map +1 -0
- package/dist/types/src/operations/update-dialog.d.ts +5 -0
- package/dist/types/src/operations/update-dialog.d.ts.map +1 -0
- package/dist/types/src/operations/update-popover.d.ts +5 -0
- package/dist/types/src/operations/update-popover.d.ts.map +1 -0
- package/dist/types/src/operations/update-sidebar.d.ts +5 -0
- package/dist/types/src/operations/update-sidebar.d.ts.map +1 -0
- package/dist/types/src/operations/update-state.d.ts +5 -0
- package/dist/types/src/operations/update-state.d.ts.map +1 -0
- package/dist/types/src/types/capabilities.d.ts +2 -0
- package/dist/types/src/types/capabilities.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +30 -18
- package/src/StorybookPlugin.ts +7 -7
- package/src/capabilities/index.ts +8 -2
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/{state/state.tsx → state.tsx} +2 -1
- package/src/components/Layout/Layout.tsx +133 -73
- package/src/core.ts +0 -5
- package/src/harness.ts +52 -0
- package/src/operations/add-toast.ts +22 -0
- package/src/operations/close.ts +14 -0
- package/src/operations/index.ts +18 -0
- package/src/operations/open.ts +18 -0
- package/src/operations/scroll-into-view.ts +14 -0
- package/src/operations/set-layout-mode.ts +14 -0
- package/src/operations/switch-workspace.ts +20 -0
- package/src/operations/update-complementary.ts +27 -0
- package/src/operations/update-dialog.ts +27 -0
- package/src/operations/update-popover.ts +37 -0
- package/src/operations/update-sidebar.ts +26 -0
- package/src/operations/update-state.ts +17 -0
- package/src/types/capabilities.ts +4 -1
- package/dist/lib/browser/chunk-NWOKPVNQ.mjs +0 -21
- package/dist/lib/browser/chunk-NWOKPVNQ.mjs.map +0 -7
- package/dist/lib/browser/operation-resolver-SR4GZ7Q5.mjs +0 -112
- package/dist/lib/browser/operation-resolver-SR4GZ7Q5.mjs.map +0 -7
- package/dist/lib/browser/state-TCYYH5JN.mjs +0 -43
- package/dist/lib/browser/state-TCYYH5JN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NWN7D2LS.mjs +0 -23
- package/dist/lib/node-esm/chunk-NWN7D2LS.mjs.map +0 -7
- package/dist/lib/node-esm/operation-resolver-A6XLCTWX.mjs +0 -113
- package/dist/lib/node-esm/operation-resolver-A6XLCTWX.mjs.map +0 -7
- package/dist/lib/node-esm/state-ZJEK6SP7.mjs +0 -44
- package/dist/lib/node-esm/state-ZJEK6SP7.mjs.map +0 -7
- package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
- package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/state/index.d.ts +0 -14
- package/dist/types/src/capabilities/state/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
- package/src/capabilities/operation-resolver/index.ts +0 -7
- package/src/capabilities/operation-resolver/operation-resolver.ts +0 -99
- package/src/capabilities/state/index.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-testing",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.fcc0d83b33",
|
|
4
4
|
"description": "Plugin testing utils",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -12,11 +12,24 @@
|
|
|
12
12
|
"author": "DXOS.org",
|
|
13
13
|
"sideEffects": true,
|
|
14
14
|
"type": "module",
|
|
15
|
+
"imports": {
|
|
16
|
+
"#capabilities": "./src/capabilities/index.ts",
|
|
17
|
+
"#components": "./src/components/index.ts",
|
|
18
|
+
"#meta": "./src/meta.ts",
|
|
19
|
+
"#operations": "./src/operations/index.ts",
|
|
20
|
+
"#types": "./src/types/index.ts"
|
|
21
|
+
},
|
|
15
22
|
"exports": {
|
|
16
23
|
".": {
|
|
17
24
|
"source": "./src/index.ts",
|
|
18
25
|
"types": "./dist/types/src/index.d.ts",
|
|
19
26
|
"browser": "./dist/lib/browser/index.mjs"
|
|
27
|
+
},
|
|
28
|
+
"./harness": {
|
|
29
|
+
"source": "./src/harness.ts",
|
|
30
|
+
"types": "./dist/types/src/harness.d.ts",
|
|
31
|
+
"browser": "./dist/lib/browser/harness.mjs",
|
|
32
|
+
"node": "./dist/lib/node-esm/harness.mjs"
|
|
20
33
|
}
|
|
21
34
|
},
|
|
22
35
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -27,33 +40,32 @@
|
|
|
27
40
|
"dependencies": {
|
|
28
41
|
"@effect-atom/atom": "^0.5.1",
|
|
29
42
|
"@effect-atom/atom-react": "^0.5.0",
|
|
30
|
-
"@dxos/app-framework": "0.8.4-main.
|
|
31
|
-
"@dxos/app-toolkit": "0.8.4-main.
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/plugin-
|
|
34
|
-
"@dxos/plugin-
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/util": "0.8.4-main.d05673bc65"
|
|
43
|
+
"@dxos/app-framework": "0.8.4-main.fcc0d83b33",
|
|
44
|
+
"@dxos/app-toolkit": "0.8.4-main.fcc0d83b33",
|
|
45
|
+
"@dxos/plugin-attention": "0.8.4-main.fcc0d83b33",
|
|
46
|
+
"@dxos/plugin-settings": "0.8.4-main.fcc0d83b33",
|
|
47
|
+
"@dxos/plugin-graph": "0.8.4-main.fcc0d83b33",
|
|
48
|
+
"@dxos/compute": "0.8.4-main.fcc0d83b33",
|
|
49
|
+
"@dxos/react-ui-mosaic": "0.8.4-main.fcc0d83b33",
|
|
50
|
+
"@dxos/plugin-theme": "0.8.4-main.fcc0d83b33",
|
|
51
|
+
"@dxos/util": "0.8.4-main.fcc0d83b33"
|
|
40
52
|
},
|
|
41
53
|
"devDependencies": {
|
|
42
54
|
"@types/react": "~19.2.7",
|
|
43
55
|
"@types/react-dom": "~19.2.3",
|
|
44
|
-
"effect": "3.
|
|
56
|
+
"effect": "3.20.0",
|
|
45
57
|
"react": "~19.2.3",
|
|
46
58
|
"react-dom": "~19.2.3",
|
|
47
|
-
"vite": "^
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/ui
|
|
59
|
+
"vite": "^8.0.10",
|
|
60
|
+
"@dxos/ui-theme": "0.8.4-main.fcc0d83b33",
|
|
61
|
+
"@dxos/react-ui": "0.8.4-main.fcc0d83b33"
|
|
50
62
|
},
|
|
51
63
|
"peerDependencies": {
|
|
52
|
-
"effect": "3.
|
|
64
|
+
"effect": "3.20.0",
|
|
53
65
|
"react": "~19.2.3",
|
|
54
66
|
"react-dom": "~19.2.3",
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/ui
|
|
67
|
+
"@dxos/ui-theme": "0.8.4-main.fcc0d83b33",
|
|
68
|
+
"@dxos/react-ui": "0.8.4-main.fcc0d83b33"
|
|
57
69
|
},
|
|
58
70
|
"publishConfig": {
|
|
59
71
|
"access": "public"
|
package/src/StorybookPlugin.ts
CHANGED
|
@@ -7,18 +7,18 @@ import * as Effect from 'effect/Effect';
|
|
|
7
7
|
import { ActivationEvents, Capabilities, Capability, Plugin } from '@dxos/app-framework';
|
|
8
8
|
import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { Layout } from '
|
|
12
|
-
import { meta } from '
|
|
13
|
-
import { type LayoutStateProps } from '
|
|
10
|
+
import { OperationHandler, State } from '#capabilities';
|
|
11
|
+
import { Layout } from '#components';
|
|
12
|
+
import { meta } from '#meta';
|
|
13
|
+
import { type LayoutStateProps } from '#types';
|
|
14
14
|
|
|
15
15
|
export type StorybookPluginOptions = {
|
|
16
16
|
initialState?: Partial<LayoutStateProps>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export const StorybookPlugin = Plugin.define<StorybookPluginOptions>(meta).pipe(
|
|
20
|
-
AppPlugin.
|
|
21
|
-
activate:
|
|
20
|
+
AppPlugin.addOperationHandlerModule({
|
|
21
|
+
activate: OperationHandler,
|
|
22
22
|
}),
|
|
23
23
|
AppPlugin.addReactContextModule({
|
|
24
24
|
activate: () =>
|
|
@@ -32,7 +32,7 @@ export const StorybookPlugin = Plugin.define<StorybookPluginOptions>(meta).pipe(
|
|
|
32
32
|
Plugin.addModule(({ initialState }) => ({
|
|
33
33
|
id: Capability.getModuleTag(State),
|
|
34
34
|
activatesOn: ActivationEvents.Startup,
|
|
35
|
-
|
|
35
|
+
firesAfterActivation: [AppActivationEvents.LayoutReady],
|
|
36
36
|
activate: () => State({ initialState }),
|
|
37
37
|
})),
|
|
38
38
|
Plugin.make,
|
|
@@ -2,5 +2,11 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { Capability } from '@dxos/app-framework';
|
|
6
|
+
import { OperationHandlerSet } from '@dxos/compute';
|
|
7
|
+
|
|
8
|
+
export const OperationHandler = Capability.lazy<OperationHandlerSet.OperationHandlerSet>(
|
|
9
|
+
'OperationHandler',
|
|
10
|
+
() => import('./operation-handler'),
|
|
11
|
+
);
|
|
12
|
+
export const State = Capability.lazy('State', () => import('./state'));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
+
import type { OperationHandlerSet } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
import { TestingOperationHandlerSet } from '#operations';
|
|
11
|
+
|
|
12
|
+
export default Capability.makeModule<OperationHandlerSet.OperationHandlerSet>(
|
|
13
|
+
Effect.fnUntraced(function* () {
|
|
14
|
+
return Capability.contributes(Capabilities.OperationHandler, TestingOperationHandlerSet);
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
@@ -8,12 +8,13 @@ import * as Effect from 'effect/Effect';
|
|
|
8
8
|
import { Capability } from '@dxos/app-framework';
|
|
9
9
|
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
10
10
|
|
|
11
|
-
import { LayoutState, type LayoutStateProps } from '
|
|
11
|
+
import { LayoutState, type LayoutStateProps } from '#types';
|
|
12
12
|
|
|
13
13
|
const defaultState: LayoutStateProps = {
|
|
14
14
|
sidebarState: 'closed',
|
|
15
15
|
complementarySidebarState: 'closed',
|
|
16
16
|
dialogOpen: false,
|
|
17
|
+
toasts: [],
|
|
17
18
|
workspace: 'default',
|
|
18
19
|
};
|
|
19
20
|
|
|
@@ -6,12 +6,17 @@ import { RegistryContext, useAtomValue } from '@effect-atom/atom-react';
|
|
|
6
6
|
import React, { type PropsWithChildren, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { Surface, useCapability } from '@dxos/app-framework/ui';
|
|
9
|
+
import { type LayoutOperation } from '@dxos/app-toolkit';
|
|
10
|
+
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
9
11
|
import {
|
|
10
12
|
AlertDialog,
|
|
13
|
+
Button,
|
|
11
14
|
Dialog,
|
|
15
|
+
Icon,
|
|
12
16
|
Main,
|
|
13
17
|
Popover,
|
|
14
18
|
type PopoverContentInteractOutsideEvent,
|
|
19
|
+
Toast,
|
|
15
20
|
toLocalizedString,
|
|
16
21
|
useTranslation,
|
|
17
22
|
} from '@dxos/react-ui';
|
|
@@ -19,12 +24,49 @@ import { Card } from '@dxos/react-ui';
|
|
|
19
24
|
import { Mosaic } from '@dxos/react-ui-mosaic';
|
|
20
25
|
import { descriptionMessage, mx } from '@dxos/ui-theme';
|
|
21
26
|
|
|
22
|
-
import { meta } from '
|
|
23
|
-
import { LayoutState, type LayoutStateProps } from '
|
|
27
|
+
import { meta } from '#meta';
|
|
28
|
+
import { LayoutState, type LayoutStateProps } from '#types';
|
|
24
29
|
|
|
25
30
|
const debounce_delay = 100;
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
const StoryToast = ({ toast, onDismiss }: { toast: LayoutOperation.Toast; onDismiss: (id: string) => void }) => {
|
|
33
|
+
const { t } = useTranslation(meta.id);
|
|
34
|
+
return (
|
|
35
|
+
<Toast.Root
|
|
36
|
+
data-testid={toast.id}
|
|
37
|
+
defaultOpen
|
|
38
|
+
duration={toast.duration}
|
|
39
|
+
onOpenChange={(open) => {
|
|
40
|
+
if (!open) {
|
|
41
|
+
onDismiss(toast.id);
|
|
42
|
+
}
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
<Toast.Body>
|
|
46
|
+
<Toast.Title classNames='items-center'>
|
|
47
|
+
{toast.icon && <Icon icon={toast.icon} classNames='inline mr-1' />}
|
|
48
|
+
{toast.title && <span>{toLocalizedString(toast.title, t)}</span>}
|
|
49
|
+
</Toast.Title>
|
|
50
|
+
{toast.description && <Toast.Description>{toLocalizedString(toast.description, t)}</Toast.Description>}
|
|
51
|
+
</Toast.Body>
|
|
52
|
+
<Toast.Actions>
|
|
53
|
+
{toast.onAction && toast.actionAlt && toast.actionLabel && (
|
|
54
|
+
<Toast.Action altText={toLocalizedString(toast.actionAlt, t)} asChild>
|
|
55
|
+
<Button variant='primary' onClick={() => toast.onAction?.()}>
|
|
56
|
+
{toLocalizedString(toast.actionLabel, t)}
|
|
57
|
+
</Button>
|
|
58
|
+
</Toast.Action>
|
|
59
|
+
)}
|
|
60
|
+
{toast.closeLabel && (
|
|
61
|
+
<Toast.Close asChild>
|
|
62
|
+
<Button>{toLocalizedString(toast.closeLabel, t)}</Button>
|
|
63
|
+
</Toast.Close>
|
|
64
|
+
)}
|
|
65
|
+
</Toast.Actions>
|
|
66
|
+
</Toast.Root>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
28
70
|
export const Layout = ({ children }: PropsWithChildren<{}>) => {
|
|
29
71
|
const { t } = useTranslation(meta.id);
|
|
30
72
|
const trigger = useRef<HTMLButtonElement | null>(null);
|
|
@@ -81,81 +123,99 @@ export const Layout = ({ children }: PropsWithChildren<{}>) => {
|
|
|
81
123
|
[handleClose],
|
|
82
124
|
);
|
|
83
125
|
|
|
126
|
+
const handleDismissToast = useCallback(
|
|
127
|
+
(id: string) => {
|
|
128
|
+
updateState({ toasts: layout.toasts.filter((toast) => toast.id !== id) });
|
|
129
|
+
},
|
|
130
|
+
[updateState, layout.toasts],
|
|
131
|
+
);
|
|
132
|
+
|
|
84
133
|
const DialogRoot = layout.dialogType === 'alert' ? AlertDialog.Root : Dialog.Root;
|
|
85
134
|
const DialogOverlay = layout.dialogType === 'alert' ? AlertDialog.Overlay : Dialog.Overlay;
|
|
86
135
|
|
|
87
136
|
return (
|
|
88
|
-
<
|
|
89
|
-
<
|
|
90
|
-
<
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
{layout.dialogBlockAlign === 'end' ? (
|
|
106
|
-
<Surface.Surface
|
|
107
|
-
role='dialog'
|
|
108
|
-
data={layout.dialogContent}
|
|
109
|
-
limit={1}
|
|
110
|
-
fallback={ErrorFallback}
|
|
111
|
-
placeholder={<div />}
|
|
112
|
-
/>
|
|
113
|
-
) : (
|
|
114
|
-
<DialogOverlay
|
|
115
|
-
blockAlign={layout.dialogBlockAlign}
|
|
116
|
-
classNames={layout.dialogOverlayClasses}
|
|
117
|
-
style={layout.dialogOverlayStyle}
|
|
118
|
-
>
|
|
119
|
-
<Surface.Surface role='dialog' data={layout.dialogContent} limit={1} fallback={ErrorFallback} />
|
|
120
|
-
</DialogOverlay>
|
|
121
|
-
)}
|
|
122
|
-
</DialogRoot>
|
|
123
|
-
|
|
124
|
-
<Popover.VirtualTrigger key={iter} virtualRef={trigger} />
|
|
125
|
-
<Popover.Portal>
|
|
126
|
-
<Popover.Content
|
|
127
|
-
side={layout.popoverSide}
|
|
128
|
-
onInteractOutside={handleInteractOutside}
|
|
129
|
-
onEscapeKeyDown={handleInteractOutside}
|
|
130
|
-
sticky='always'
|
|
131
|
-
hideWhenDetached
|
|
137
|
+
<Toast.Provider>
|
|
138
|
+
<div role='none' className='fixed inset-0 flex overflow-hidden'>
|
|
139
|
+
<Mosaic.Root>
|
|
140
|
+
<Popover.Root open={open}>
|
|
141
|
+
<Main.Root
|
|
142
|
+
navigationSidebarState={layout.sidebarState}
|
|
143
|
+
complementarySidebarState={layout.complementarySidebarState}
|
|
144
|
+
onNavigationSidebarStateChange={(next) => updateState({ sidebarState: next })}
|
|
145
|
+
onComplementarySidebarStateChange={(next) => updateState({ complementarySidebarState: next })}
|
|
146
|
+
>
|
|
147
|
+
{children}
|
|
148
|
+
</Main.Root>
|
|
149
|
+
|
|
150
|
+
<DialogRoot
|
|
151
|
+
modal={layout.dialogBlockAlign !== 'end'}
|
|
152
|
+
open={layout.dialogOpen}
|
|
153
|
+
onOpenChange={(nextOpen) => updateState({ dialogOpen: nextOpen })}
|
|
132
154
|
>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
155
|
+
{layout.dialogBlockAlign === 'end' ? (
|
|
156
|
+
<Surface.Surface
|
|
157
|
+
type={AppSurface.Dialog}
|
|
158
|
+
data={layout.dialogContent}
|
|
159
|
+
limit={1}
|
|
160
|
+
fallback={ErrorFallback}
|
|
161
|
+
placeholder={<div />}
|
|
162
|
+
/>
|
|
163
|
+
) : (
|
|
164
|
+
<DialogOverlay
|
|
165
|
+
blockAlign={layout.dialogBlockAlign}
|
|
166
|
+
classNames={layout.dialogOverlayClasses}
|
|
167
|
+
style={layout.dialogOverlayStyle}
|
|
168
|
+
>
|
|
169
|
+
<Surface.Surface
|
|
170
|
+
type={AppSurface.Dialog}
|
|
171
|
+
data={layout.dialogContent}
|
|
172
|
+
limit={1}
|
|
173
|
+
fallback={ErrorFallback}
|
|
174
|
+
/>
|
|
175
|
+
</DialogOverlay>
|
|
176
|
+
)}
|
|
177
|
+
</DialogRoot>
|
|
178
|
+
|
|
179
|
+
<Popover.VirtualTrigger key={iter} virtualRef={trigger} />
|
|
180
|
+
<Popover.Portal>
|
|
181
|
+
<Popover.Content
|
|
182
|
+
side={layout.popoverSide}
|
|
183
|
+
onInteractOutside={handleInteractOutside}
|
|
184
|
+
onEscapeKeyDown={handleInteractOutside}
|
|
185
|
+
sticky='always'
|
|
186
|
+
hideWhenDetached
|
|
187
|
+
>
|
|
188
|
+
<Popover.Viewport>
|
|
189
|
+
{layout.popoverKind === 'card' && (
|
|
190
|
+
<Card.Root>
|
|
191
|
+
<Card.Toolbar>
|
|
192
|
+
{/* TODO(wittjosiah): Cleaner way to handle no drag handle in toolbar? */}
|
|
142
193
|
<span />
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
194
|
+
{layout.popoverTitle ? (
|
|
195
|
+
<Card.Title>{toLocalizedString(layout.popoverTitle, t)}</Card.Title>
|
|
196
|
+
) : (
|
|
197
|
+
<span />
|
|
198
|
+
)}
|
|
199
|
+
<Card.CloseIconButton onClick={handleClose} />
|
|
200
|
+
</Card.Toolbar>
|
|
201
|
+
<Surface.Surface type={AppSurface.Card} data={layout.popoverContent} limit={1} />
|
|
202
|
+
</Card.Root>
|
|
203
|
+
)}
|
|
204
|
+
{layout.popoverKind === 'base' && (
|
|
205
|
+
<Surface.Surface type={AppSurface.Popover} data={layout.popoverContent} limit={1} />
|
|
206
|
+
)}
|
|
207
|
+
</Popover.Viewport>
|
|
208
|
+
<Popover.Arrow />
|
|
209
|
+
</Popover.Content>
|
|
210
|
+
</Popover.Portal>
|
|
211
|
+
</Popover.Root>
|
|
212
|
+
</Mosaic.Root>
|
|
213
|
+
{layout.toasts.map((toast) => (
|
|
214
|
+
<StoryToast key={toast.id} toast={toast} onDismiss={handleDismissToast} />
|
|
215
|
+
))}
|
|
216
|
+
<Toast.Viewport />
|
|
217
|
+
</div>
|
|
218
|
+
</Toast.Provider>
|
|
159
219
|
);
|
|
160
220
|
};
|
|
161
221
|
|
|
@@ -169,7 +229,7 @@ export const ErrorFallback = ({ error }: { error?: Error }) => {
|
|
|
169
229
|
className={mx('overflow-auto p-8 dx-attention-surface grid place-items-center')}
|
|
170
230
|
>
|
|
171
231
|
<p className={mx(descriptionMessage, 'break-words rounded-md p-8', errorString.length < 256 && 'text-lg')}>
|
|
172
|
-
{error ? errorString : t('error
|
|
232
|
+
{error ? errorString : t('error-fallback.message')}
|
|
173
233
|
</p>
|
|
174
234
|
</div>
|
|
175
235
|
);
|
package/src/core.ts
CHANGED
|
@@ -4,16 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { OperationPlugin, type Plugin, RuntimePlugin } from '@dxos/app-framework';
|
|
6
6
|
import { AttentionPlugin } from '@dxos/plugin-attention';
|
|
7
|
-
import { ClientPlugin } from '@dxos/plugin-client';
|
|
8
7
|
import { GraphPlugin } from '@dxos/plugin-graph';
|
|
9
8
|
import { SettingsPlugin } from '@dxos/plugin-settings';
|
|
10
9
|
import { ThemePlugin } from '@dxos/plugin-theme';
|
|
11
10
|
import { defaultTx } from '@dxos/ui-theme';
|
|
12
11
|
|
|
13
|
-
// TODO(burdon): Remove this.
|
|
14
|
-
// Re-export common framework plugins.
|
|
15
|
-
export { AttentionPlugin, ClientPlugin, GraphPlugin, OperationPlugin, RuntimePlugin, SettingsPlugin, ThemePlugin };
|
|
16
|
-
|
|
17
12
|
/**
|
|
18
13
|
* Core plugins for testing/storybook environments.
|
|
19
14
|
* NOTE: Does not include SpacePlugin to avoid circular dependencies.
|
package/src/harness.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { OperationPlugin, type Plugin, RuntimePlugin } from '@dxos/app-framework';
|
|
6
|
+
import { createTestApp, type TestAppOptions, type TestHarness } from '@dxos/app-framework/testing';
|
|
7
|
+
import { AttentionPlugin } from '@dxos/plugin-attention';
|
|
8
|
+
import { GraphPlugin } from '@dxos/plugin-graph';
|
|
9
|
+
import { SettingsPlugin } from '@dxos/plugin-settings';
|
|
10
|
+
|
|
11
|
+
export type ComposerTestAppOptions = Omit<TestAppOptions, 'plugins'> & {
|
|
12
|
+
/** Plugins to register in addition to the Composer core plugins. */
|
|
13
|
+
plugins?: Plugin.Plugin[];
|
|
14
|
+
/**
|
|
15
|
+
* Whether to include `ThemePlugin` in the core plugin set.
|
|
16
|
+
* Defaults to `false` — `ThemePlugin` requires a browser DOM and breaks Node-only tests.
|
|
17
|
+
* Set to `true` (with jsdom/happy-dom) when rendering React surfaces.
|
|
18
|
+
*/
|
|
19
|
+
theme?: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Headless core plugins for the test harness — the subset of `corePlugins()`
|
|
24
|
+
* that can be activated without a browser DOM.
|
|
25
|
+
*/
|
|
26
|
+
const headlessCorePlugins = (): Plugin.Plugin[] => [
|
|
27
|
+
AttentionPlugin(),
|
|
28
|
+
GraphPlugin(),
|
|
29
|
+
OperationPlugin(),
|
|
30
|
+
RuntimePlugin(),
|
|
31
|
+
SettingsPlugin(),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates a TestHarness pre-loaded with the Composer core plugins
|
|
36
|
+
* (Attention, Graph, Operation, Runtime, Settings, optionally Theme).
|
|
37
|
+
*
|
|
38
|
+
* For a ClientPlugin-backed harness, pass `ClientPlugin({ ... })` via `plugins`.
|
|
39
|
+
*/
|
|
40
|
+
export const createComposerTestApp = async (opts: ComposerTestAppOptions = {}): Promise<TestHarness> => {
|
|
41
|
+
const { plugins = [], theme = false, ...rest } = opts;
|
|
42
|
+
const core = headlessCorePlugins();
|
|
43
|
+
if (theme) {
|
|
44
|
+
const { ThemePlugin } = await import('@dxos/plugin-theme');
|
|
45
|
+
const { defaultTx } = await import('@dxos/ui-theme');
|
|
46
|
+
core.push(ThemePlugin({ tx: defaultTx }));
|
|
47
|
+
}
|
|
48
|
+
return createTestApp({
|
|
49
|
+
...rest,
|
|
50
|
+
plugins: [...core, ...plugins],
|
|
51
|
+
});
|
|
52
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
import { updateState } from './update-state';
|
|
11
|
+
|
|
12
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.AddToast> = LayoutOperation.AddToast.pipe(
|
|
13
|
+
Operation.withHandler(
|
|
14
|
+
Effect.fnUntraced(function* (input) {
|
|
15
|
+
yield* updateState((state) => ({
|
|
16
|
+
toasts: [...state.toasts, input as LayoutOperation.Toast],
|
|
17
|
+
}));
|
|
18
|
+
}),
|
|
19
|
+
),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export default handler;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.Close> = LayoutOperation.Close.pipe(
|
|
11
|
+
Operation.withHandler(() => Effect.void),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export default handler;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { OperationHandlerSet } from '@dxos/compute';
|
|
6
|
+
|
|
7
|
+
export const TestingOperationHandlerSet = OperationHandlerSet.lazy(
|
|
8
|
+
() => import('./add-toast'),
|
|
9
|
+
() => import('./close'),
|
|
10
|
+
() => import('./open'),
|
|
11
|
+
() => import('./scroll-into-view'),
|
|
12
|
+
() => import('./set-layout-mode'),
|
|
13
|
+
() => import('./switch-workspace'),
|
|
14
|
+
() => import('./update-complementary'),
|
|
15
|
+
() => import('./update-dialog'),
|
|
16
|
+
() => import('./update-popover'),
|
|
17
|
+
() => import('./update-sidebar'),
|
|
18
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.Open> = LayoutOperation.Open.pipe(
|
|
11
|
+
Operation.withHandler(
|
|
12
|
+
Effect.fnUntraced(function* (input) {
|
|
13
|
+
return input.subject;
|
|
14
|
+
}),
|
|
15
|
+
),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export default handler;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.ScrollIntoView> = LayoutOperation.ScrollIntoView.pipe(
|
|
11
|
+
Operation.withHandler(() => Effect.void),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export default handler;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.SetLayoutMode> = LayoutOperation.SetLayoutMode.pipe(
|
|
11
|
+
Operation.withHandler(() => Effect.void),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export default handler;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
import { updateState } from './update-state';
|
|
11
|
+
|
|
12
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.SwitchWorkspace> = LayoutOperation.SwitchWorkspace.pipe(
|
|
13
|
+
Operation.withHandler(
|
|
14
|
+
Effect.fnUntraced(function* ({ subject }) {
|
|
15
|
+
yield* updateState(() => ({ workspace: subject }));
|
|
16
|
+
}),
|
|
17
|
+
),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default handler;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
import { updateState } from './update-state';
|
|
11
|
+
|
|
12
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.UpdateComplementary> =
|
|
13
|
+
LayoutOperation.UpdateComplementary.pipe(
|
|
14
|
+
Operation.withHandler(
|
|
15
|
+
Effect.fnUntraced(function* ({ state }) {
|
|
16
|
+
yield* updateState((layout) => {
|
|
17
|
+
const next = state ?? layout.complementarySidebarState;
|
|
18
|
+
if (next !== layout.complementarySidebarState) {
|
|
19
|
+
return { complementarySidebarState: next };
|
|
20
|
+
}
|
|
21
|
+
return {};
|
|
22
|
+
});
|
|
23
|
+
}),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export default handler;
|