@dxos/react-ui 0.1.28 → 0.1.29-next.1bc50d4
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/index.mjs +162 -18
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/composites/Shell/Shell.d.ts +1 -1
- package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -1
- package/dist/types/src/composites/Shell/Shell.stories.d.ts +10 -0
- package/dist/types/src/composites/Shell/Shell.stories.d.ts.map +1 -0
- package/dist/types/src/composites/Shell/ShellContext.d.ts +22 -0
- package/dist/types/src/composites/Shell/ShellContext.d.ts.map +1 -0
- package/dist/types/src/composites/Shell/index.d.ts +1 -0
- package/dist/types/src/composites/Shell/index.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -1
- package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts +1 -1
- package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts.map +1 -1
- package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts +1 -2
- package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts.map +1 -1
- package/dist/types/src/playwright/invitations-manager.d.ts +33 -0
- package/dist/types/src/playwright/invitations-manager.d.ts.map +1 -0
- package/dist/types/src/playwright/invitations.spec.d.ts +2 -0
- package/dist/types/src/playwright/invitations.spec.d.ts.map +1 -0
- package/dist/types/src/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
- package/dist/types/src/stories/Invitations.stories.d.ts +14 -0
- package/dist/types/src/stories/Invitations.stories.d.ts.map +1 -0
- package/dist/types/src/translations/locales/en-US.d.ts +5 -0
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/package.json +14 -12
- package/src/components/InvitationList/InvitationList.stories.tsx +2 -2
- package/src/components/SpaceList/SpaceListItem.tsx +1 -1
- package/src/composites/JoinDialog/JoinDialog.stories.tsx +2 -2
- package/src/composites/Shell/Shell.stories.tsx +76 -0
- package/src/composites/Shell/Shell.tsx +17 -3
- package/src/composites/Shell/ShellContext.tsx +175 -0
- package/src/composites/Shell/index.ts +1 -0
- package/src/panels/DevicesPanel/DevicesPanel.tsx +2 -2
- package/src/panels/JoinPanel/JoinHeading.tsx +4 -3
- package/src/panels/JoinPanel/JoinPanel.tsx +1 -1
- package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +2 -2
- package/src/panels/JoinPanel/view-states/InvitationInput.tsx +1 -1
- package/src/panels/SpacePanel/SpacePanel.stories.tsx +2 -40
- package/src/panels/SpacePanel/SpacePanel.tsx +5 -5
- package/src/playwright/invitations-manager.ts +210 -0
- package/src/playwright/invitations.spec.ts +363 -0
- package/src/playwright/playwright.config.ts +7 -0
- package/src/stories/Invitations.stories.tsx +165 -0
- package/src/translations/locales/en-US.ts +6 -1
- package/dist/types/src/testing/ClientProvider.d.ts +0 -3
- package/dist/types/src/testing/ClientProvider.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/src/testing/ClientProvider.tsx +0 -18
- package/src/testing/index.ts +0 -5
|
@@ -8,7 +8,7 @@ import { StoryFn } from '@storybook/react';
|
|
|
8
8
|
import React, { useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { CancellableInvitationObservable, Client, Invitation } from '@dxos/client';
|
|
11
|
-
import { TestBuilder } from '@dxos/client/testing';
|
|
11
|
+
import { TestBuilder } from '@dxos/client-services/testing';
|
|
12
12
|
import { log } from '@dxos/log';
|
|
13
13
|
import { useAsyncEffect } from '@dxos/react-async';
|
|
14
14
|
import { ClientProvider } from '@dxos/react-client';
|
|
@@ -26,7 +26,7 @@ export const Default = {
|
|
|
26
26
|
(Story: StoryFn) => {
|
|
27
27
|
const testBuilder = new TestBuilder();
|
|
28
28
|
const clients = useMemo(() => {
|
|
29
|
-
return [...Array(1)].map(() => new Client({ services: testBuilder.
|
|
29
|
+
return [...Array(1)].map(() => new Client({ services: testBuilder.createLocal() }));
|
|
30
30
|
}, []);
|
|
31
31
|
|
|
32
32
|
const [invitations, setInvitations] = useState<CancellableInvitationObservable[]>([]);
|
|
@@ -12,7 +12,7 @@ export const SpaceListItem = forwardRef(
|
|
|
12
12
|
({ space, onClick }: { space: Space; onClick?: () => void }, ref: ForwardedRef<HTMLLIElement>) => {
|
|
13
13
|
return (
|
|
14
14
|
<li
|
|
15
|
-
className={mx('flex gap-2 items-center', onClick && 'cursor-pointer')}
|
|
15
|
+
className={mx('flex gap-2 items-center mbe-2', onClick && 'cursor-pointer')}
|
|
16
16
|
onClick={() => onClick?.()}
|
|
17
17
|
ref={ref}
|
|
18
18
|
data-testid='space-list-item'
|
|
@@ -8,7 +8,7 @@ import { StoryContext, StoryFn } from '@storybook/react';
|
|
|
8
8
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { Client, Invitation, InvitationEncoder } from '@dxos/client';
|
|
11
|
-
import { TestBuilder } from '@dxos/client/testing';
|
|
11
|
+
import { TestBuilder } from '@dxos/client-services/testing';
|
|
12
12
|
import { log } from '@dxos/log';
|
|
13
13
|
import { useAsyncEffect } from '@dxos/react-async';
|
|
14
14
|
import { ClientProvider } from '@dxos/react-client';
|
|
@@ -24,7 +24,7 @@ const JoinClientDecorator = (Story: StoryFn, { args }: StoryContext) => {
|
|
|
24
24
|
const n = 2;
|
|
25
25
|
const testBuilder = new TestBuilder();
|
|
26
26
|
const clients = useMemo(() => {
|
|
27
|
-
return [...Array(n)].map(() => new Client({ services: testBuilder.
|
|
27
|
+
return [...Array(n)].map(() => new Client({ services: testBuilder.createLocal() }));
|
|
28
28
|
}, []);
|
|
29
29
|
|
|
30
30
|
const [invitationCode, setInvitationCode] = useState<string>();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
import { faker } from '@faker-js/faker';
|
|
7
|
+
import { Intersect, Laptop, PlusCircle } from 'phosphor-react';
|
|
8
|
+
import React, { useState } from 'react';
|
|
9
|
+
|
|
10
|
+
import { PublicKey, ShellLayout } from '@dxos/client';
|
|
11
|
+
import { useClient, useSpace, useSpaces } from '@dxos/react-client';
|
|
12
|
+
import { ClientDecorator } from '@dxos/react-client/testing';
|
|
13
|
+
import { Button, ButtonGroup, getSize, Group, Tooltip } from '@dxos/react-components';
|
|
14
|
+
|
|
15
|
+
import { SpaceListItem } from '../../components';
|
|
16
|
+
import { ShellProvider, useShell } from './ShellContext';
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
component: ShellProvider
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const ShellControls = () => {
|
|
23
|
+
const client = useClient();
|
|
24
|
+
const spaces = useSpaces();
|
|
25
|
+
const shell = useShell();
|
|
26
|
+
|
|
27
|
+
const controls = (
|
|
28
|
+
<ButtonGroup className='mbe-4'>
|
|
29
|
+
<Tooltip content='Devices'>
|
|
30
|
+
<Button onClick={() => shell.setLayout(ShellLayout.DEVICE_INVITATIONS)}>
|
|
31
|
+
<Laptop weight='fill' className={getSize(6)} />
|
|
32
|
+
</Button>
|
|
33
|
+
</Tooltip>
|
|
34
|
+
<Tooltip content='Create Space'>
|
|
35
|
+
<Button onClick={() => client.echo.createSpace({ name: faker.animal.bird() })}>
|
|
36
|
+
<PlusCircle weight='fill' className={getSize(6)} />
|
|
37
|
+
</Button>
|
|
38
|
+
</Tooltip>
|
|
39
|
+
<Tooltip content='Join Space'>
|
|
40
|
+
<Button onClick={() => shell.setLayout(ShellLayout.JOIN_SPACE)}>
|
|
41
|
+
<Intersect weight='fill' className={getSize(6)} />
|
|
42
|
+
</Button>
|
|
43
|
+
</Tooltip>
|
|
44
|
+
</ButtonGroup>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const header = <div className='flex flex-row-reverse'>{controls}</div>;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Group label={{ children: header }} className='flex flex-col w-96'>
|
|
51
|
+
<ul>
|
|
52
|
+
{spaces.map((space) => (
|
|
53
|
+
<SpaceListItem
|
|
54
|
+
key={space.key.toHex()}
|
|
55
|
+
space={space}
|
|
56
|
+
onClick={() => shell.setLayout(ShellLayout.SPACE_INVITATIONS, { spaceKey: space.key })}
|
|
57
|
+
/>
|
|
58
|
+
))}
|
|
59
|
+
</ul>
|
|
60
|
+
</Group>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const Default = {
|
|
65
|
+
render: () => {
|
|
66
|
+
const [spaceKey, setSpaceKey] = useState<PublicKey>();
|
|
67
|
+
const space = useSpace(spaceKey);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<ShellProvider space={space} onJoinedSpace={setSpaceKey}>
|
|
71
|
+
<ShellControls />
|
|
72
|
+
</ShellProvider>
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
decorators: [ClientDecorator()]
|
|
76
|
+
};
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useEffect, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { ShellRuntime } from '@dxos/client';
|
|
7
|
+
import { ShellRuntime } from '@dxos/client-services';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
8
9
|
import { LayoutRequest, ShellDisplay, ShellLayout } from '@dxos/protocols/proto/dxos/iframe';
|
|
9
10
|
import { useClient, useSpace, useSpaces } from '@dxos/react-client';
|
|
10
11
|
|
|
@@ -27,6 +28,19 @@ export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: stri
|
|
|
27
28
|
return runtime.layoutUpdate.on((request) => setLayout(request));
|
|
28
29
|
}, [runtime]);
|
|
29
30
|
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (layout === ShellLayout.SPACE_INVITATIONS && !space) {
|
|
33
|
+
log.warn('No space found for shell space invitations.');
|
|
34
|
+
|
|
35
|
+
const timeout = setTimeout(async () => {
|
|
36
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE });
|
|
37
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return () => clearTimeout(timeout);
|
|
41
|
+
}
|
|
42
|
+
}, [runtime, layout, space]);
|
|
43
|
+
|
|
30
44
|
switch (layout) {
|
|
31
45
|
case ShellLayout.INITIALIZE_IDENTITY:
|
|
32
46
|
return (
|
|
@@ -54,7 +68,7 @@ export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: stri
|
|
|
54
68
|
);
|
|
55
69
|
|
|
56
70
|
case ShellLayout.SPACE_INVITATIONS:
|
|
57
|
-
return (
|
|
71
|
+
return space ? (
|
|
58
72
|
<SpaceDialog
|
|
59
73
|
space={space}
|
|
60
74
|
createInvitationUrl={(invitationCode) => `${origin}?spaceInvitationCode=${invitationCode}`}
|
|
@@ -63,7 +77,7 @@ export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: stri
|
|
|
63
77
|
runtime.setLayout(ShellLayout.DEFAULT);
|
|
64
78
|
}}
|
|
65
79
|
/>
|
|
66
|
-
);
|
|
80
|
+
) : null;
|
|
67
81
|
|
|
68
82
|
case ShellLayout.JOIN_SPACE:
|
|
69
83
|
return (
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, {
|
|
6
|
+
Context,
|
|
7
|
+
createContext,
|
|
8
|
+
PropsWithChildren,
|
|
9
|
+
useCallback,
|
|
10
|
+
useContext,
|
|
11
|
+
useEffect,
|
|
12
|
+
useMemo,
|
|
13
|
+
useState
|
|
14
|
+
} from 'react';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
IFrameClientServicesProxy,
|
|
18
|
+
type PublicKey,
|
|
19
|
+
type ShellController,
|
|
20
|
+
ShellDisplay,
|
|
21
|
+
ShellLayout,
|
|
22
|
+
type Space
|
|
23
|
+
} from '@dxos/client';
|
|
24
|
+
import { MemoryShellRuntime } from '@dxos/client-services';
|
|
25
|
+
import { useClient, useIdentity } from '@dxos/react-client';
|
|
26
|
+
import { mx } from '@dxos/react-components';
|
|
27
|
+
|
|
28
|
+
import { Shell } from './Shell';
|
|
29
|
+
|
|
30
|
+
export type ShellContextProps = {
|
|
31
|
+
runtime?: MemoryShellRuntime;
|
|
32
|
+
setDisplay?: (display: ShellDisplay) => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const ShellContext: Context<ShellContextProps> = createContext<ShellContextProps>({});
|
|
36
|
+
|
|
37
|
+
export const useShell = (): {
|
|
38
|
+
setLayout: ShellController['setLayout'];
|
|
39
|
+
} => {
|
|
40
|
+
const client = useClient();
|
|
41
|
+
const { runtime, setDisplay } = useContext(ShellContext);
|
|
42
|
+
|
|
43
|
+
const setLayout: ShellController['setLayout'] = async (layout, options) => {
|
|
44
|
+
if (runtime) {
|
|
45
|
+
if (layout === ShellLayout.DEFAULT) {
|
|
46
|
+
setDisplay?.(ShellDisplay.NONE);
|
|
47
|
+
} else {
|
|
48
|
+
setDisplay?.(ShellDisplay.FULLSCREEN);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
runtime.setLayout(layout, options);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (client.services instanceof IFrameClientServicesProxy) {
|
|
55
|
+
await client.services.setLayout(layout, options);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
setLayout
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export type ShellProviderProps = PropsWithChildren<{
|
|
64
|
+
space?: Space;
|
|
65
|
+
haloInvitationCode?: string | null;
|
|
66
|
+
spaceInvitationCode?: string | null;
|
|
67
|
+
onJoinedSpace?: (spaceKey?: PublicKey) => void;
|
|
68
|
+
}>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Renders the DXOS shell and provides a way to set the layout of the shell from the rest of the app.
|
|
72
|
+
*/
|
|
73
|
+
export const ShellProvider = ({
|
|
74
|
+
space,
|
|
75
|
+
haloInvitationCode,
|
|
76
|
+
spaceInvitationCode,
|
|
77
|
+
onJoinedSpace,
|
|
78
|
+
children
|
|
79
|
+
}: ShellProviderProps) => {
|
|
80
|
+
//
|
|
81
|
+
// IFrame Shell
|
|
82
|
+
//
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (client.services instanceof IFrameClientServicesProxy && onJoinedSpace) {
|
|
86
|
+
return client.services.joinedSpace.on(onJoinedSpace);
|
|
87
|
+
}
|
|
88
|
+
}, []);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (client.services instanceof IFrameClientServicesProxy) {
|
|
92
|
+
client.services.setSpaceProvider(() => space?.key);
|
|
93
|
+
}
|
|
94
|
+
}, [space]);
|
|
95
|
+
|
|
96
|
+
//
|
|
97
|
+
// Component Shell
|
|
98
|
+
//
|
|
99
|
+
|
|
100
|
+
const client = useClient();
|
|
101
|
+
const identity = useIdentity();
|
|
102
|
+
const [display, setDisplay] = useState(
|
|
103
|
+
!identity || spaceInvitationCode || haloInvitationCode ? ShellDisplay.FULLSCREEN : ShellDisplay.NONE
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const shellRuntime = useMemo(() => {
|
|
107
|
+
if (client.config.get('runtime.app.env.DX_VAULT') === 'true') {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (spaceInvitationCode) {
|
|
112
|
+
return new MemoryShellRuntime({
|
|
113
|
+
layout: ShellLayout.JOIN_SPACE,
|
|
114
|
+
invitationCode: spaceInvitationCode
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return new MemoryShellRuntime({
|
|
119
|
+
layout: identity ? ShellLayout.DEFAULT : ShellLayout.INITIALIZE_IDENTITY,
|
|
120
|
+
invitationCode: haloInvitationCode ?? undefined
|
|
121
|
+
});
|
|
122
|
+
}, [client, identity, spaceInvitationCode, haloInvitationCode]);
|
|
123
|
+
|
|
124
|
+
const handleKeyDown = useCallback(
|
|
125
|
+
(event: KeyboardEvent) => {
|
|
126
|
+
if (!space || !shellRuntime) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const modifier = event.ctrlKey || event.metaKey;
|
|
131
|
+
if (event.key === '>' && event.shiftKey && modifier) {
|
|
132
|
+
shellRuntime.setLayout(ShellLayout.DEVICE_INVITATIONS);
|
|
133
|
+
setDisplay(ShellDisplay.FULLSCREEN);
|
|
134
|
+
} else if (event.key === '.' && modifier) {
|
|
135
|
+
shellRuntime.setLayout(ShellLayout.SPACE_INVITATIONS, { spaceKey: space.key });
|
|
136
|
+
setDisplay(ShellDisplay.FULLSCREEN);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
[space, shellRuntime]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!shellRuntime) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
148
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
149
|
+
}, [shellRuntime, handleKeyDown]);
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (!shellRuntime) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return shellRuntime.contextUpdate.on(({ display, spaceKey }) => {
|
|
157
|
+
setDisplay(display);
|
|
158
|
+
onJoinedSpace?.(spaceKey);
|
|
159
|
+
});
|
|
160
|
+
}, [shellRuntime]);
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<>
|
|
164
|
+
{shellRuntime && (
|
|
165
|
+
<div className={mx(display === ShellDisplay.NONE ? 'hidden' : '')}>
|
|
166
|
+
<Shell runtime={shellRuntime} origin={window.location.origin} />
|
|
167
|
+
</div>
|
|
168
|
+
)}
|
|
169
|
+
|
|
170
|
+
<ShellContext.Provider value={{ runtime: shellRuntime, setDisplay }}>
|
|
171
|
+
{identity ? children : undefined}
|
|
172
|
+
</ShellContext.Provider>
|
|
173
|
+
</>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
@@ -58,9 +58,9 @@ const DeviceListView = ({ createInvitationUrl, titleId, onDone, doneActionParent
|
|
|
58
58
|
<Button
|
|
59
59
|
className='is-full flex gap-2 mbs-2'
|
|
60
60
|
onClick={() => client.halo.createInvitation()}
|
|
61
|
-
data-testid='create-
|
|
61
|
+
data-testid='devices-panel.create-invitation'
|
|
62
62
|
>
|
|
63
|
-
<span>{t('create
|
|
63
|
+
<span>{t('create device invitation label')}</span>
|
|
64
64
|
<UserPlus className={getSize(4)} weight='bold' />
|
|
65
65
|
</Button>
|
|
66
66
|
<PanelSeparator />
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
import { ProhibitInset } from 'phosphor-react';
|
|
5
|
-
import React, { cloneElement, ForwardedRef } from 'react';
|
|
5
|
+
import React, { cloneElement, ForwardedRef, forwardRef } from 'react';
|
|
6
6
|
|
|
7
7
|
import { AuthenticatingInvitationObservable } from '@dxos/client';
|
|
8
|
-
import { useSpace
|
|
8
|
+
import { useSpace } from '@dxos/react-client';
|
|
9
9
|
import { Avatar, Button, getSize, Heading, mx, Trans, useId, useTranslation } from '@dxos/react-components';
|
|
10
10
|
|
|
11
11
|
import { subduedSurface } from '../../styles';
|
|
@@ -20,7 +20,8 @@ export interface JoinSpaceHeadingProps {
|
|
|
20
20
|
preventExit?: boolean;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// TODO(wittjosiah): Accesses the space properties directly which will trigger ECHO warnings without observer.
|
|
24
|
+
export const JoinHeading = forwardRef(
|
|
24
25
|
(
|
|
25
26
|
{ mode, titleId, invitation, onExit, exitActionParent, preventExit }: JoinSpaceHeadingProps,
|
|
26
27
|
ref: ForwardedRef<HTMLDivElement>
|
|
@@ -151,7 +151,7 @@ export const JoinPanel = ({
|
|
|
151
151
|
}, [unredeemedHaloInvitationCode]);
|
|
152
152
|
|
|
153
153
|
useEffect(() => {
|
|
154
|
-
// TODO
|
|
154
|
+
// TODO(thure): Validate if this is sufficiently synchronous for iOS to move focus. It might not be!
|
|
155
155
|
const attrValue =
|
|
156
156
|
joinState.activeView === 'identity input'
|
|
157
157
|
? `${joinState.activeView}; ${joinState.additionMethod}`
|
|
@@ -50,14 +50,14 @@ const PureInvitationAuthenticatorContent = ({
|
|
|
50
50
|
disabled,
|
|
51
51
|
inputMode: 'numeric',
|
|
52
52
|
pattern: '\\d*',
|
|
53
|
-
'data-autofocus': `${invitationType} invitation acceptor; invitation authenticator
|
|
53
|
+
'data-autofocus': `${invitationType} invitation acceptor; invitation authenticator`,
|
|
54
|
+
'data-testid': `${invitationType}-auth-code-input`
|
|
54
55
|
} as ComponentPropsWithoutRef<'input'>
|
|
55
56
|
}}
|
|
56
57
|
{...(failed && {
|
|
57
58
|
validationValence: 'error',
|
|
58
59
|
validationMessage: t('failed to authenticate message')
|
|
59
60
|
})}
|
|
60
|
-
data-testid='auth-code-input'
|
|
61
61
|
/>
|
|
62
62
|
<div role='none' className='grow' />
|
|
63
63
|
<div className='flex gap-2'>
|
|
@@ -43,10 +43,10 @@ export const InvitationInput = ({ invitationType, ...viewStateProps }: Invitatio
|
|
|
43
43
|
label: { className: 'sr-only' },
|
|
44
44
|
input: {
|
|
45
45
|
'data-autofocus': `${invitationType} invitation acceptor; invitation input`,
|
|
46
|
+
'data-testid': `${invitationType}-invitation-input`,
|
|
46
47
|
onKeyUp: ({ key }) => key === 'Enter' && handleNext()
|
|
47
48
|
} as ComponentPropsWithoutRef<'input'>
|
|
48
49
|
}}
|
|
49
|
-
data-testid='invitation-input'
|
|
50
50
|
/>
|
|
51
51
|
<div role='none' className='grow' />
|
|
52
52
|
<div className='flex gap-2'>
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import React, { useEffect, useState } from 'react';
|
|
7
|
-
|
|
8
|
-
import { Client, Space } from '@dxos/client';
|
|
9
|
-
import { TestBuilder } from '@dxos/client/testing';
|
|
10
|
-
import { ClientContext } from '@dxos/react-client';
|
|
11
|
-
import { Loading } from '@dxos/react-components';
|
|
5
|
+
import { ClientSpaceDecorator } from '@dxos/react-client/testing';
|
|
12
6
|
|
|
13
7
|
import { SpacePanel } from './SpacePanel';
|
|
14
8
|
|
|
@@ -16,38 +10,6 @@ export default {
|
|
|
16
10
|
component: SpacePanel
|
|
17
11
|
};
|
|
18
12
|
|
|
19
|
-
const SpaceDecorator = (Story: StoryFn, { args }: StoryContext) => {
|
|
20
|
-
const testBuilder = new TestBuilder();
|
|
21
|
-
const [client, setClient] = useState<Client>();
|
|
22
|
-
const [space, setSpace] = useState<Space>();
|
|
23
|
-
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
const timeout = setTimeout(async () => {
|
|
26
|
-
const client = new Client({ services: testBuilder.createClientServicesHost() });
|
|
27
|
-
await client.initialize();
|
|
28
|
-
setClient(client);
|
|
29
|
-
|
|
30
|
-
await client.halo.createIdentity();
|
|
31
|
-
const space = await client.echo.createSpace();
|
|
32
|
-
space.properties.name = 'Q3 2022 Planning';
|
|
33
|
-
|
|
34
|
-
setSpace(space);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return () => clearTimeout(timeout);
|
|
38
|
-
}, []);
|
|
39
|
-
|
|
40
|
-
if (!client || !space) {
|
|
41
|
-
return <Loading label='Setting things up…' />;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<ClientContext.Provider value={{ client }}>
|
|
46
|
-
<Story args={{ space, ...args }} />
|
|
47
|
-
</ClientContext.Provider>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
13
|
export const Default = {
|
|
52
|
-
decorators: [
|
|
14
|
+
decorators: [ClientSpaceDecorator()]
|
|
53
15
|
};
|
|
@@ -6,7 +6,7 @@ import { UserPlus } from 'phosphor-react';
|
|
|
6
6
|
import React, { cloneElement, useReducer } from 'react';
|
|
7
7
|
|
|
8
8
|
import { Space } from '@dxos/client';
|
|
9
|
-
import { useSpaceInvitations } from '@dxos/react-client';
|
|
9
|
+
import { useSpaceInvitations, observer } from '@dxos/react-client';
|
|
10
10
|
import { Button, DensityProvider, getSize, mx, useTranslation } from '@dxos/react-components';
|
|
11
11
|
|
|
12
12
|
import { InvitationList, PanelSeparator, SpaceMemberListContainer } from '../../components';
|
|
@@ -14,7 +14,7 @@ import { defaultSurface, subduedSurface } from '../../styles';
|
|
|
14
14
|
|
|
15
15
|
export type SpacePanelProps = {
|
|
16
16
|
titleId?: string;
|
|
17
|
-
space
|
|
17
|
+
space: Space;
|
|
18
18
|
createInvitationUrl: (invitationCode: string) => string;
|
|
19
19
|
doneActionParent?: Parameters<typeof cloneElement>[0];
|
|
20
20
|
onDone?: () => void;
|
|
@@ -22,7 +22,7 @@ export type SpacePanelProps = {
|
|
|
22
22
|
|
|
23
23
|
export type SpaceView = 'current space';
|
|
24
24
|
|
|
25
|
-
const CurrentSpaceView = ({ space, createInvitationUrl, titleId }: SpacePanelProps) => {
|
|
25
|
+
const CurrentSpaceView = observer(({ space, createInvitationUrl, titleId }: SpacePanelProps) => {
|
|
26
26
|
const { t } = useTranslation('os');
|
|
27
27
|
const invitations = useSpaceInvitations(space?.key);
|
|
28
28
|
const name = space?.properties.name;
|
|
@@ -48,7 +48,7 @@ const CurrentSpaceView = ({ space, createInvitationUrl, titleId }: SpacePanelPro
|
|
|
48
48
|
<Button
|
|
49
49
|
className='is-full flex gap-2 mbs-2'
|
|
50
50
|
onClick={() => space?.createInvitation()}
|
|
51
|
-
data-testid='create-
|
|
51
|
+
data-testid='spaces-panel.create-invitation'
|
|
52
52
|
>
|
|
53
53
|
<span>{t('create space invitation label')}</span>
|
|
54
54
|
<UserPlus className={getSize(4)} weight='bold' />
|
|
@@ -58,7 +58,7 @@ const CurrentSpaceView = ({ space, createInvitationUrl, titleId }: SpacePanelPro
|
|
|
58
58
|
</div>
|
|
59
59
|
</div>
|
|
60
60
|
);
|
|
61
|
-
};
|
|
61
|
+
});
|
|
62
62
|
|
|
63
63
|
interface SpacePanelState {
|
|
64
64
|
activeView: SpaceView;
|