@dxos/shell 0.6.14-staging.8758a12 → 0.6.14-staging.9b873ce

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 (53) hide show
  1. package/dist/bundle/shell.mjs +4344 -4251
  2. package/dist/bundle/shell.mjs.map +1 -1
  3. package/dist/bundle/shell.umd.js +45 -45
  4. package/dist/bundle/shell.umd.js.map +1 -1
  5. package/dist/bundle/style.css +1 -1
  6. package/dist/lib/browser/meta.json +1 -1
  7. package/dist/lib/browser/react.mjs +40 -46
  8. package/dist/lib/browser/react.mjs.map +3 -3
  9. package/dist/lib/browser/testing/index.mjs +2 -0
  10. package/dist/lib/browser/testing/index.mjs.map +2 -2
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/react.cjs +23 -29
  13. package/dist/lib/node/react.cjs.map +3 -3
  14. package/dist/lib/node/testing/index.cjs +2 -0
  15. package/dist/lib/node/testing/index.cjs.map +2 -2
  16. package/dist/lib/node-esm/meta.json +1 -1
  17. package/dist/lib/node-esm/react.mjs +40 -46
  18. package/dist/lib/node-esm/react.mjs.map +3 -3
  19. package/dist/lib/node-esm/testing/index.mjs +2 -0
  20. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  21. package/dist/types/src/components/DeviceList/DeviceListItem.d.ts.map +1 -1
  22. package/dist/types/src/components/DeviceList/DeviceListItem.js +4 -10
  23. package/dist/types/src/components/DeviceList/DeviceListItem.js.map +1 -1
  24. package/dist/types/src/components/Panel/Action.d.ts +2 -0
  25. package/dist/types/src/components/Panel/Action.d.ts.map +1 -1
  26. package/dist/types/src/components/Panel/Action.js +16 -14
  27. package/dist/types/src/components/Panel/Action.js.map +1 -1
  28. package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.d.ts.map +1 -1
  29. package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js +2 -2
  30. package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js.map +1 -1
  31. package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts.map +1 -1
  32. package/dist/types/src/panels/SpacePanel/SpacePanel.js +2 -2
  33. package/dist/types/src/panels/SpacePanel/SpacePanel.js.map +1 -1
  34. package/dist/types/src/panels/SpacePanel/SpacePanelProps.d.ts +1 -0
  35. package/dist/types/src/panels/SpacePanel/SpacePanelProps.d.ts.map +1 -1
  36. package/dist/types/src/panels/SpacePanel/steps/SpaceManager.d.ts.map +1 -1
  37. package/dist/types/src/panels/SpacePanel/steps/SpaceManager.js +5 -3
  38. package/dist/types/src/panels/SpacePanel/steps/SpaceManager.js.map +1 -1
  39. package/dist/types/src/playwright/invitations-manager.d.ts.map +1 -1
  40. package/dist/types/src/playwright/invitations-manager.js +9 -1
  41. package/dist/types/src/playwright/invitations-manager.js.map +1 -1
  42. package/dist/types/src/testing/shell-manager.d.ts.map +1 -1
  43. package/dist/types/src/testing/shell-manager.js +2 -0
  44. package/dist/types/src/testing/shell-manager.js.map +1 -1
  45. package/package.json +25 -25
  46. package/src/components/DeviceList/DeviceListItem.tsx +38 -42
  47. package/src/components/Panel/Action.tsx +40 -31
  48. package/src/panels/IdentityPanel/steps/IdentityActionChooser.tsx +4 -2
  49. package/src/panels/SpacePanel/SpacePanel.tsx +2 -1
  50. package/src/panels/SpacePanel/SpacePanelProps.ts +1 -0
  51. package/src/panels/SpacePanel/steps/SpaceManager.tsx +5 -3
  52. package/src/playwright/invitations-manager.ts +8 -1
  53. package/src/testing/shell-manager.ts +2 -0
@@ -29,7 +29,6 @@ import {
29
29
  useTranslation,
30
30
  DropdownMenu,
31
31
  Button,
32
- Tooltip,
33
32
  } from '@dxos/react-ui';
34
33
  import { getSize } from '@dxos/react-ui-theme';
35
34
  import { keyToFallback } from '@dxos/util';
@@ -58,7 +57,7 @@ export const DeviceListItem = forwardRef<
58
57
  onClickJoinExisting,
59
58
  classNames,
60
59
  connectionState,
61
- onAgentDestroy,
60
+ onAgentDestroy: _,
62
61
  ...props
63
62
  },
64
63
  forwardedRef,
@@ -111,7 +110,8 @@ export const DeviceListItem = forwardRef<
111
110
  </Avatar.Frame>
112
111
  <Avatar.Label classNames='flex-1 text-sm truncate'>{displayName}</Avatar.Label>
113
112
  {isCurrent && <Tag color='primary'>{t('current device tag label')}</Tag>}
114
- {device.profile?.type === DeviceType.AGENT_MANAGED ? (
113
+ {/* TODO(wittjosiah): EDGE agents cannot current be turned off. */}
114
+ {/* {device.profile?.type === DeviceType.AGENT_MANAGED && (
115
115
  <Tooltip.Root>
116
116
  <Tooltip.Trigger asChild>
117
117
  <Button
@@ -130,46 +130,42 @@ export const DeviceListItem = forwardRef<
130
130
  </Tooltip.Content>
131
131
  </Tooltip.Portal>
132
132
  </Tooltip.Root>
133
- ) : (
134
- device.kind === DeviceKind.CURRENT && (
135
- <DropdownMenu.Root>
136
- <DropdownMenu.Trigger asChild>
137
- <Button
138
- variant='ghost'
139
- classNames='pli-0 is-[--rail-action] bs-[--rail-action]'
140
- data-testid={`device-list-item${isCurrent ? '-current' : ''}.options`}
141
- >
142
- <span className='sr-only'>{t('more options label')}</span>
143
- <DotsThree className={getSize(5)} />
144
- </Button>
145
- </DropdownMenu.Trigger>
146
- <DropdownMenu.Content>
147
- <DropdownMenu.Viewport>
148
- {/* <DropdownMenu.Item disabled onClick={onClickEdit}> */}
149
- {/* <PencilSimpleLine className={getSize(5)} /> */}
150
- {/* {t('edit device label')} */}
151
- {/* </DropdownMenu.Item> */}
152
- <DropdownMenu.Item
153
- data-testid='device-list-item-current.join-existing'
154
- onClick={onClickJoinExisting}
155
- >
156
- <ShareFat className={getSize(5)} />
157
- {t('choose join new identity label')}
158
- </DropdownMenu.Item>
159
- {onClickRecover && (
160
- <DropdownMenu.Item data-testid='device-list-item-current.recover' onClick={onClickRecover}>
161
- <FirstAidKit className={getSize(5)} />
162
- {t('choose recover identity label')}
163
- </DropdownMenu.Item>
164
- )}
165
- <DropdownMenu.Item data-testid='device-list-item-current.reset' onClick={onClickReset}>
166
- <Power className={getSize(5)} />
167
- {t('reset device label')}
133
+ )} */}
134
+ {device.kind === DeviceKind.CURRENT && (
135
+ <DropdownMenu.Root>
136
+ <DropdownMenu.Trigger asChild>
137
+ <Button
138
+ variant='ghost'
139
+ classNames='pli-0 is-[--rail-action] bs-[--rail-action]'
140
+ data-testid={`device-list-item${isCurrent ? '-current' : ''}.options`}
141
+ >
142
+ <span className='sr-only'>{t('more options label')}</span>
143
+ <DotsThree className={getSize(5)} />
144
+ </Button>
145
+ </DropdownMenu.Trigger>
146
+ <DropdownMenu.Content>
147
+ <DropdownMenu.Viewport>
148
+ {/* <DropdownMenu.Item disabled onClick={onClickEdit}> */}
149
+ {/* <PencilSimpleLine className={getSize(5)} /> */}
150
+ {/* {t('edit device label')} */}
151
+ {/* </DropdownMenu.Item> */}
152
+ <DropdownMenu.Item data-testid='device-list-item-current.join-existing' onClick={onClickJoinExisting}>
153
+ <ShareFat className={getSize(5)} />
154
+ {t('choose join new identity label')}
155
+ </DropdownMenu.Item>
156
+ {onClickRecover && (
157
+ <DropdownMenu.Item data-testid='device-list-item-current.recover' onClick={onClickRecover}>
158
+ <FirstAidKit className={getSize(5)} />
159
+ {t('choose recover identity label')}
168
160
  </DropdownMenu.Item>
169
- </DropdownMenu.Viewport>
170
- </DropdownMenu.Content>
171
- </DropdownMenu.Root>
172
- )
161
+ )}
162
+ <DropdownMenu.Item data-testid='device-list-item-current.reset' onClick={onClickReset}>
163
+ <Power className={getSize(5)} />
164
+ {t('reset device label')}
165
+ </DropdownMenu.Item>
166
+ </DropdownMenu.Viewport>
167
+ </DropdownMenu.Content>
168
+ </DropdownMenu.Root>
173
169
  )}
174
170
  </Avatar.Root>
175
171
  </ListItem.Root>
@@ -17,6 +17,7 @@ export type ActionMenuItem = {
17
17
  label: string;
18
18
  description: string;
19
19
  icon: FC<IconProps>;
20
+ testId?: string;
20
21
  } & Pick<ButtonProps, 'onClick'>;
21
22
 
22
23
  export type BifurcatedActionProps = {
@@ -24,6 +25,7 @@ export type BifurcatedActionProps = {
24
25
  activeAction?: string;
25
26
  onChangeActiveAction?: Dispatch<SetStateAction<string>>;
26
27
  defaultActiveAction?: string;
28
+ 'data-testid'?: string;
27
29
  } & Omit<LargeButtonProps, 'children' | 'onClick'>;
28
30
 
29
31
  const defaultActions = {
@@ -44,9 +46,12 @@ export const BifurcatedAction = forwardRef<HTMLButtonElement, BifurcatedActionPr
44
46
  activeAction: propsActiveAction,
45
47
  onChangeActiveAction,
46
48
  defaultActiveAction,
49
+ 'data-testid': testId,
47
50
  ...rest
48
51
  } = props;
49
52
 
53
+ const dropdownTestId = testId && `${testId}.more`;
54
+
50
55
  const [activeActionKey = Object.keys(actions)[0], setActiveAction] = useControllableState({
51
56
  prop: propsActiveAction,
52
57
  onChange: onChangeActiveAction,
@@ -64,6 +69,7 @@ export const BifurcatedAction = forwardRef<HTMLButtonElement, BifurcatedActionPr
64
69
  classNames={['bs-11 flex-1 min-is-0 flex gap-2 rounded-ie-none', classNames]}
65
70
  ref={forwardedRef}
66
71
  variant={variant}
72
+ data-testid={testId}
67
73
  onClick={activeAction.onClick}
68
74
  >
69
75
  {activeAction.icon && <activeAction.icon className={getSize(5)} />}
@@ -71,42 +77,45 @@ export const BifurcatedAction = forwardRef<HTMLButtonElement, BifurcatedActionPr
71
77
  </Button>
72
78
  <DropdownMenu.Root>
73
79
  <DropdownMenu.Trigger asChild>
74
- <Button classNames={['bs-11 flex-none rounded-is-none', classNames]}>
80
+ <Button classNames={['bs-11 flex-none rounded-is-none', classNames]} data-testid={dropdownTestId}>
75
81
  <span className='sr-only'>{t('invite options label')}</span>
76
82
  <CaretDown className={getSize(4)} />
77
83
  </Button>
78
84
  </DropdownMenu.Trigger>
79
85
  {/* TODO(thure): Putting `DropdownMenu.Portal` here breaks highlighting and focus. Why? */}
80
- <DropdownMenu.Content>
81
- <DropdownMenu.Viewport>
82
- {Object.entries(actions).map(([id, action]) => {
83
- return (
84
- <DropdownMenu.CheckboxItem
85
- key={id}
86
- aria-labelledby={`${id}__label`}
87
- aria-describedby={`${id}__description`}
88
- checked={activeActionKey === id}
89
- onCheckedChange={(checked) => checked && setActiveAction(id)}
90
- classNames='gap-2'
91
- >
92
- {action.icon && <action.icon className={getSize(5)} />}
93
- <div role='none' className='flex-1 min-is-0 space-b-1'>
94
- <p id={`${id}__label`}>{action.label}</p>
95
- {action.description && (
96
- <p id={`${id}__description`} className={descriptionText}>
97
- {action.description}
98
- </p>
99
- )}
100
- </div>
101
- <DropdownMenu.ItemIndicator asChild>
102
- <Check weight='bold' className={getSize(4)} />
103
- </DropdownMenu.ItemIndicator>
104
- </DropdownMenu.CheckboxItem>
105
- );
106
- })}
107
- </DropdownMenu.Viewport>
108
- <DropdownMenu.Arrow />
109
- </DropdownMenu.Content>
86
+ <DropdownMenu.Portal>
87
+ <DropdownMenu.Content>
88
+ <DropdownMenu.Viewport>
89
+ {Object.entries(actions).map(([id, action]) => {
90
+ return (
91
+ <DropdownMenu.CheckboxItem
92
+ key={id}
93
+ aria-labelledby={`${id}__label`}
94
+ aria-describedby={`${id}__description`}
95
+ checked={activeActionKey === id}
96
+ onCheckedChange={(checked) => checked && setActiveAction(id)}
97
+ classNames='gap-2'
98
+ data-testid={action.testId}
99
+ >
100
+ {action.icon && <action.icon className={getSize(5)} />}
101
+ <div role='none' className='flex-1 min-is-0 space-b-1'>
102
+ <p id={`${id}__label`}>{action.label}</p>
103
+ {action.description && (
104
+ <p id={`${id}__description`} className={descriptionText}>
105
+ {action.description}
106
+ </p>
107
+ )}
108
+ </div>
109
+ <DropdownMenu.ItemIndicator asChild>
110
+ <Check weight='bold' className={getSize(4)} />
111
+ </DropdownMenu.ItemIndicator>
112
+ </DropdownMenu.CheckboxItem>
113
+ );
114
+ })}
115
+ </DropdownMenu.Viewport>
116
+ <DropdownMenu.Arrow />
117
+ </DropdownMenu.Content>
118
+ </DropdownMenu.Portal>
110
119
  </DropdownMenu.Root>
111
120
  </div>
112
121
  );
@@ -59,7 +59,9 @@ export const IdentityActionChooserImpl = ({
59
59
  onClickReset,
60
60
  ...agentProps
61
61
  }: IdentityActionChooserImplProps) => {
62
- const managedDeviceAvailable = devices.find((device) => device.profile?.type === DeviceType.AGENT_MANAGED);
62
+ const edgeDeviceAvailable = devices.find(
63
+ (device) => device.profile?.type === DeviceType.AGENT_MANAGED && device.profile?.os?.toUpperCase() === 'EDGE',
64
+ );
63
65
  return (
64
66
  <div role='none' className='bs-40 grow overflow-y-auto overflow-x-hidden'>
65
67
  <DeviceList
@@ -71,7 +73,7 @@ export const IdentityActionChooserImpl = ({
71
73
  onClickReset={onClickReset}
72
74
  onAgentDestroy={agentProps.onAgentDestroy!}
73
75
  />
74
- {!managedDeviceAvailable && agentHostingEnabled && <AgentConfig {...(agentProps as AgentFormProps)} />}
76
+ {!edgeDeviceAvailable && agentHostingEnabled && <AgentConfig {...(agentProps as AgentFormProps)} />}
75
77
  </div>
76
78
  );
77
79
  };
@@ -39,6 +39,7 @@ export const SpacePanelImpl = (props: SpacePanelImplProps) => {
39
39
  titleId,
40
40
  activeView,
41
41
  space,
42
+ hideHeading,
42
43
  target,
43
44
  SpaceManager: SpaceManagerComponent = SpaceManager,
44
45
  InvitationManager: InvitationManagerComponent = InvitationManager,
@@ -47,7 +48,7 @@ export const SpacePanelImpl = (props: SpacePanelImplProps) => {
47
48
 
48
49
  return (
49
50
  <>
50
- <SpacePanelHeading {...rest} {...{ titleId, space }} />
51
+ {!hideHeading && <SpacePanelHeading {...rest} {...{ titleId, space }} />}
51
52
  <Viewport.Root activeView={activeView}>
52
53
  <Viewport.Views>
53
54
  <Viewport.View id='space manager' classNames={stepStyles}>
@@ -16,6 +16,7 @@ export type SpacePanelImplProps = {
16
16
  titleId: string;
17
17
  activeView: string;
18
18
  send: (event: SingleOrArray<Event<any>>) => void;
19
+ hideHeading?: boolean;
19
20
  target?: string;
20
21
  createInvitationUrl: (invitationCode: string) => string;
21
22
  space: ErsatzSpace | Space;
@@ -56,6 +56,7 @@ export const SpaceManager = (props: SpaceManagerProps) => {
56
56
  label: t('invite one label'),
57
57
  description: t('invite one description'),
58
58
  icon: UserPlus,
59
+ testId: 'spaces-panel.invite-one',
59
60
  onClick: useCallback(() => {
60
61
  const invitation = space.share?.({
61
62
  type: Invitation.Type.INTERACTIVE,
@@ -68,12 +69,13 @@ export const SpaceManager = (props: SpaceManagerProps) => {
68
69
  handleInvitationEvent(invitation, subscription),
69
70
  );
70
71
  }
71
- }, [space]),
72
+ }, [space, target]),
72
73
  },
73
74
  inviteMany: {
74
75
  label: t('invite many label'),
75
76
  description: t('invite many description'),
76
77
  icon: UsersThree,
78
+ testId: 'spaces-panel.invite-many',
77
79
  onClick: useCallback(() => {
78
80
  const invitation = space.share?.({
79
81
  type: Invitation.Type.DELEGATED,
@@ -86,7 +88,7 @@ export const SpaceManager = (props: SpaceManagerProps) => {
86
88
  handleInvitationEvent(invitation, subscription),
87
89
  );
88
90
  }
89
- }, [space]),
91
+ }, [space, target]),
90
92
  },
91
93
  };
92
94
 
@@ -120,7 +122,7 @@ export const SpaceManagerImpl = (props: SpaceManagerImplProps) => {
120
122
  },
121
123
  } as Record<string, ActionMenuItem>);
122
124
 
123
- const [activeAction, setInternalActiveAction] = useState(localStorage.getItem(activeActionKey) ?? 'inviteOne');
125
+ const [activeAction, setInternalActiveAction] = useState(localStorage.getItem(activeActionKey) ?? 'inviteMany');
124
126
 
125
127
  const setActiveAction = (nextAction: string) => {
126
128
  setInternalActiveAction(nextAction);
@@ -129,7 +129,14 @@ export class InvitationsManager extends ScopedShellManager {
129
129
 
130
130
  if (!options) {
131
131
  const peer = this.peer(id);
132
- await peer.getByTestId(`${type}s-panel.create-invitation`).click();
132
+ if (type === 'space') {
133
+ await peer.getByTestId('spaces-panel.create-invitation.more').click();
134
+ // NOTE: This is a popper portal.
135
+ await this.page.getByTestId('spaces-panel.invite-one').click();
136
+ await peer.getByTestId('spaces-panel.create-invitation').click();
137
+ } else {
138
+ await peer.getByTestId(`${type}s-panel.create-invitation`).click();
139
+ }
133
140
  return this._invitationCode.wait();
134
141
  }
135
142
 
@@ -94,6 +94,8 @@ export class ShellManager extends ScopedShellManager {
94
94
  async createSpaceInvitation(): Promise<string> {
95
95
  this._invitationCode = new Trigger<string>();
96
96
  this._authCode = new Trigger<string>();
97
+ await this.shell.getByTestId('spaces-panel.create-invitation.more').click();
98
+ await this.shell.getByTestId('spaces-panel.invite-one').click();
97
99
  await this.shell.getByTestId('spaces-panel.create-invitation').click();
98
100
  return await this._invitationCode.wait();
99
101
  }