@dxos/react-ui 0.1.28 → 0.1.29-next.b1a9bc6

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.
Files changed (52) hide show
  1. package/dist/lib/browser/index.mjs +162 -18
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/composites/Shell/Shell.d.ts +1 -1
  5. package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -1
  6. package/dist/types/src/composites/Shell/Shell.stories.d.ts +10 -0
  7. package/dist/types/src/composites/Shell/Shell.stories.d.ts.map +1 -0
  8. package/dist/types/src/composites/Shell/ShellContext.d.ts +22 -0
  9. package/dist/types/src/composites/Shell/ShellContext.d.ts.map +1 -0
  10. package/dist/types/src/composites/Shell/index.d.ts +1 -0
  11. package/dist/types/src/composites/Shell/index.d.ts.map +1 -1
  12. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -1
  13. package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts +1 -1
  14. package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts.map +1 -1
  15. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts +1 -2
  16. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts.map +1 -1
  17. package/dist/types/src/playwright/invitations-manager.d.ts +33 -0
  18. package/dist/types/src/playwright/invitations-manager.d.ts.map +1 -0
  19. package/dist/types/src/playwright/invitations.spec.d.ts +2 -0
  20. package/dist/types/src/playwright/invitations.spec.d.ts.map +1 -0
  21. package/dist/types/src/playwright/playwright.config.d.ts +3 -0
  22. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
  23. package/dist/types/src/stories/Invitations.stories.d.ts +14 -0
  24. package/dist/types/src/stories/Invitations.stories.d.ts.map +1 -0
  25. package/dist/types/src/translations/locales/en-US.d.ts +5 -0
  26. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  27. package/package.json +14 -12
  28. package/src/components/InvitationList/InvitationList.stories.tsx +2 -2
  29. package/src/components/SpaceList/SpaceListItem.tsx +1 -1
  30. package/src/composites/JoinDialog/JoinDialog.stories.tsx +2 -2
  31. package/src/composites/Shell/Shell.stories.tsx +76 -0
  32. package/src/composites/Shell/Shell.tsx +17 -3
  33. package/src/composites/Shell/ShellContext.tsx +175 -0
  34. package/src/composites/Shell/index.ts +1 -0
  35. package/src/panels/DevicesPanel/DevicesPanel.tsx +2 -2
  36. package/src/panels/JoinPanel/JoinHeading.tsx +4 -3
  37. package/src/panels/JoinPanel/JoinPanel.tsx +1 -1
  38. package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +2 -2
  39. package/src/panels/JoinPanel/view-states/InvitationInput.tsx +1 -1
  40. package/src/panels/SpacePanel/SpacePanel.stories.tsx +2 -40
  41. package/src/panels/SpacePanel/SpacePanel.tsx +5 -5
  42. package/src/playwright/invitations-manager.ts +210 -0
  43. package/src/playwright/invitations.spec.ts +363 -0
  44. package/src/playwright/playwright.config.ts +7 -0
  45. package/src/stories/Invitations.stories.tsx +165 -0
  46. package/src/translations/locales/en-US.ts +6 -1
  47. package/dist/types/src/testing/ClientProvider.d.ts +0 -3
  48. package/dist/types/src/testing/ClientProvider.d.ts.map +0 -1
  49. package/dist/types/src/testing/index.d.ts +0 -2
  50. package/dist/types/src/testing/index.d.ts.map +0 -1
  51. package/src/testing/ClientProvider.tsx +0 -18
  52. 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.createClientServicesHost() }));
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.createClientServicesHost() }));
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
+ };
@@ -3,3 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './Shell';
6
+ export * from './ShellContext';
@@ -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-space-invitation'
61
+ data-testid='devices-panel.create-invitation'
62
62
  >
63
- <span>{t('create space invitation label')}</span>
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, withReactorRef } from '@dxos/react-client';
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
- export const JoinHeading = withReactorRef(
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 (thure): Validate if this is sufficiently synchronous for iOS to move focus. It might not be!
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 { StoryContext, StoryFn } from '@storybook/react';
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: [SpaceDecorator]
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?: 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-space-invitation'
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;