@openmrs/esm-styleguide 8.0.1-pre.3648 → 8.0.1-pre.3663

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.
@@ -2,7 +2,9 @@
2
2
  import React from 'react';
3
3
  import { type WorkspaceGroupDefinition2 } from '@openmrs/esm-globals';
4
4
  export interface ActionMenuProps {
5
- workspaceGroup: WorkspaceGroupDefinition2;
5
+ workspaceGroup: WorkspaceGroupDefinition2 & {
6
+ moduleName: string;
7
+ };
6
8
  groupProps: Record<string, any> | null;
7
9
  }
8
10
  /**
@@ -75,6 +75,10 @@ type PromptReason = {
75
75
  explicit: boolean;
76
76
  windowName: string;
77
77
  workspaceName: string;
78
+ } | {
79
+ reason: 'CLOSE_OTHER_WINDOWS';
80
+ explicit: false;
81
+ windowNameToSpare: string;
78
82
  };
79
83
  /**
80
84
  * A user can perform actions that explicitly result in closing workspaces
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-styleguide",
3
- "version": "8.0.1-pre.3648",
3
+ "version": "8.0.1-pre.3663",
4
4
  "license": "MPL-2.0",
5
5
  "description": "The styleguide for OpenMRS SPA",
6
6
  "main": "dist/openmrs-esm-styleguide.js",
@@ -98,17 +98,17 @@
98
98
  "swr": "2.x"
99
99
  },
100
100
  "devDependencies": {
101
- "@openmrs/esm-api": "8.0.1-pre.3648",
102
- "@openmrs/esm-config": "8.0.1-pre.3648",
103
- "@openmrs/esm-emr-api": "8.0.1-pre.3648",
104
- "@openmrs/esm-error-handling": "8.0.1-pre.3648",
105
- "@openmrs/esm-extensions": "8.0.1-pre.3648",
106
- "@openmrs/esm-globals": "8.0.1-pre.3648",
107
- "@openmrs/esm-navigation": "8.0.1-pre.3648",
108
- "@openmrs/esm-react-utils": "8.0.1-pre.3648",
109
- "@openmrs/esm-state": "8.0.1-pre.3648",
110
- "@openmrs/esm-translations": "8.0.1-pre.3648",
111
- "@openmrs/esm-utils": "8.0.1-pre.3648",
101
+ "@openmrs/esm-api": "8.0.1-pre.3663",
102
+ "@openmrs/esm-config": "8.0.1-pre.3663",
103
+ "@openmrs/esm-emr-api": "8.0.1-pre.3663",
104
+ "@openmrs/esm-error-handling": "8.0.1-pre.3663",
105
+ "@openmrs/esm-extensions": "8.0.1-pre.3663",
106
+ "@openmrs/esm-globals": "8.0.1-pre.3663",
107
+ "@openmrs/esm-navigation": "8.0.1-pre.3663",
108
+ "@openmrs/esm-react-utils": "8.0.1-pre.3663",
109
+ "@openmrs/esm-state": "8.0.1-pre.3663",
110
+ "@openmrs/esm-translations": "8.0.1-pre.3663",
111
+ "@openmrs/esm-utils": "8.0.1-pre.3663",
112
112
  "@rspack/cli": "^1.3.11",
113
113
  "@rspack/core": "^1.3.11",
114
114
  "@types/geopattern": "^1.2.9",
@@ -3,7 +3,7 @@ import React, { useContext } from 'react';
3
3
  import classNames from 'classnames';
4
4
  import { Button, IconButton } from '@carbon/react';
5
5
  import { SingleSpaContext } from 'single-spa-react';
6
- import { useLayoutType } from '@openmrs/esm-react-utils';
6
+ import { ComponentContext, useLayoutType } from '@openmrs/esm-react-utils';
7
7
  import { type OpenedWindow } from '@openmrs/esm-extensions';
8
8
  import { launchWorkspace2, useWorkspace2Store } from '../workspace2';
9
9
  import styles from './action-menu-button2.module.scss';
@@ -72,10 +72,8 @@ export const ActionMenuButton2: React.FC<ActionMenuButtonProps2> = ({
72
72
  const layout = useLayoutType();
73
73
  const { openedWindows, restoreWindow, isMostRecentlyOpenedWindowHidden } = useWorkspace2Store();
74
74
 
75
- // name of the window that the button is associated with
76
- const { windowName } = useContext(SingleSpaContext);
77
-
78
- const openedWindowIndex = openedWindows.findIndex((w) => w.windowName === windowName);
75
+ const { extension } = useContext(ComponentContext);
76
+ const openedWindowIndex = openedWindows.findIndex((w) => w.windowName === extension?.extensionId);
79
77
  // can be undefined if the window is not opened
80
78
  const window: OpenedWindow | undefined = openedWindows[openedWindowIndex];
81
79
  const isWindowOpened = window != null;
@@ -4,15 +4,16 @@ import Parcel from 'single-spa-react/parcel';
4
4
  import { IconButton } from '@carbon/react';
5
5
  import { mountRootParcel } from 'single-spa';
6
6
  import { loadLifeCycles } from '@openmrs/esm-routes';
7
- import { isDesktop, useLayoutType } from '@openmrs/esm-react-utils';
7
+ import { ComponentContext, isDesktop, useLayoutType } from '@openmrs/esm-react-utils';
8
8
  import { type WorkspaceGroupDefinition2 } from '@openmrs/esm-globals';
9
9
  import { getCoreTranslation } from '@openmrs/esm-translations';
10
10
  import { closeWorkspaceGroup2, useWorkspace2Store } from '../workspace2';
11
11
  import { CloseIcon } from '../../icons';
12
12
  import styles from './action-menu2.module.scss';
13
+ import { ExtensionSlot } from '@openmrs/esm-framework';
13
14
 
14
15
  export interface ActionMenuProps {
15
- workspaceGroup: WorkspaceGroupDefinition2;
16
+ workspaceGroup: WorkspaceGroupDefinition2 & { moduleName: string };
16
17
  groupProps: Record<string, any> | null;
17
18
  }
18
19
 
@@ -48,15 +49,14 @@ export function ActionMenu({ workspaceGroup, groupProps }: ActionMenuProps) {
48
49
  <CloseIcon />
49
50
  </IconButton>
50
51
  )}
51
- {windowsWithIcons.map((window) => (
52
- <Parcel
53
- key={window.icon}
54
- config={() => loadLifeCycles(window.moduleName, window.icon)}
55
- mountParcel={mountRootParcel}
56
- windowName={window.name}
57
- groupProps={groupProps}
58
- />
59
- ))}
52
+ <ComponentContext.Provider
53
+ value={{
54
+ moduleName: workspaceGroup.moduleName,
55
+ featureName: workspaceGroup.name,
56
+ }}
57
+ >
58
+ <ExtensionSlot name={workspaceGroup.name} state={{ groupProps }} />
59
+ </ComponentContext.Provider>
60
60
  </div>
61
61
  {isClosable && !isDesktop(layout) && (
62
62
  <IconButton
@@ -115,7 +115,8 @@ export const Workspace2: React.FC<Workspace2Props> = ({ title, children, hasUnsa
115
115
  }
116
116
 
117
117
  const { icon, canMaximize } = windowDef;
118
- const canHide = !!icon;
118
+ const canCloseGroup = group.persistence === 'closable';
119
+ const canHide = !!icon && !canCloseGroup;
119
120
  const { maximized } = openedWindow;
120
121
  const width = windowDef?.width ?? 'narrow';
121
122
 
@@ -173,12 +174,14 @@ export const Workspace2: React.FC<Workspace2Props> = ({ title, children, hasUnsa
173
174
  <ArrowRightIcon />
174
175
  </HeaderGlobalAction>
175
176
  ) : (
176
- <HeaderGlobalAction
177
- aria-label={getCoreTranslation('close')}
178
- onClick={() => closeWorkspace({ closeWindow: true })}
179
- >
180
- <CloseIcon />
181
- </HeaderGlobalAction>
177
+ !canCloseGroup && (
178
+ <HeaderGlobalAction
179
+ aria-label={getCoreTranslation('close')}
180
+ onClick={() => closeWorkspace({ closeWindow: true })}
181
+ >
182
+ <CloseIcon />
183
+ </HeaderGlobalAction>
184
+ )
182
185
  )}
183
186
  </>
184
187
  ) : (
@@ -272,6 +272,33 @@ export async function launchWorkspace2<
272
272
  return false;
273
273
  }
274
274
  }
275
+ } else if (groupDef.persistence == 'closable') {
276
+ const okToCloseWorkspaces = await promptForClosingWorkspaces({
277
+ reason: 'CLOSE_OTHER_WINDOWS',
278
+ explicit: false,
279
+ windowNameToSpare: windowDef.name,
280
+ });
281
+ if (okToCloseWorkspaces) {
282
+ workspace2Store.setState({
283
+ ...storeState,
284
+ openedGroup: {
285
+ groupName: groupDef.name,
286
+ props: groupProps ?? storeState?.openedGroup?.props ?? null,
287
+ },
288
+ openedWindows: [
289
+ {
290
+ windowName: windowName,
291
+ openedWorkspaces: [newOpenedWorkspace(workspaceName, workspaceProps)], // root workspace at index 0
292
+ props: windowProps,
293
+ maximized: false,
294
+ },
295
+ ],
296
+ isMostRecentlyOpenedWindowHidden: false,
297
+ });
298
+ return true;
299
+ } else {
300
+ return false;
301
+ }
275
302
  } else {
276
303
  workspace2Store.setState({
277
304
  ...storeState,
@@ -316,7 +343,8 @@ function arePropsCompatible(a: Record<string, any> | null, b: Record<string, any
316
343
  type PromptReason =
317
344
  | { reason: 'CLOSE_WORKSPACE_GROUP'; explicit: boolean }
318
345
  | { reason: 'CLOSE_WINDOW'; explicit: boolean; windowName: string }
319
- | { reason: 'CLOSE_WORKSPACE'; explicit: boolean; windowName: string; workspaceName: string };
346
+ | { reason: 'CLOSE_WORKSPACE'; explicit: boolean; windowName: string; workspaceName: string }
347
+ | { reason: 'CLOSE_OTHER_WINDOWS'; explicit: false; windowNameToSpare: string };
320
348
 
321
349
  /**
322
350
  * A user can perform actions that explicitly result in closing workspaces
@@ -340,7 +368,7 @@ export function promptForClosingWorkspaces(promptReason: PromptReason): Promise<
340
368
  for (let i = openedWindow.openedWorkspaces.length - 1; i >= 0; i--) {
341
369
  const openedWorkspace = openedWindow.openedWorkspaces[i];
342
370
 
343
- if (openedWorkspace.hasUnsavedChanges || !promptReason.explicit) {
371
+ if (openedWorkspace.hasUnsavedChanges) {
344
372
  ret.push(openedWorkspace);
345
373
  }
346
374
  if (onlyUpToThisWorkspace && openedWorkspace.workspaceName === onlyUpToThisWorkspace) {
@@ -371,6 +399,12 @@ export function promptForClosingWorkspaces(promptReason: PromptReason): Promise<
371
399
  throw new Error(`Window ${promptReason.windowName} not found in opened windows.`);
372
400
  }
373
401
  affectedWorkspaces = getAffectedWorkspacesInWindow(openedWindow, promptReason.workspaceName);
402
+ break;
403
+ }
404
+ case 'CLOSE_OTHER_WINDOWS': {
405
+ const windowsToClose = openedWindows.filter((window) => window.windowName !== promptReason.windowNameToSpare);
406
+ affectedWorkspaces = windowsToClose.flatMap((w) => getAffectedWorkspacesInWindow(w));
407
+ break;
374
408
  }
375
409
  }
376
410