Workspace v2 Migration Guide
This guide documents the major changes in workspace v2, and how to migrate from the old workspace system.
Summary of changes:
Defining workspaces, workspace windows, and workspace groups, and how they interact together.
Support of (React-like) props at each level (workspace / window / group)
Support for child workspaces
Proper handling of Unsaved changes in workspaces
Removal of
<WorkspaceContainer>
Details:
Workspaces often work and share data together, and the new system requires those relationships to be clearly defined.
A workspace is a "page" that gets rendered in a workspace window. A workspace must belong to a workspace window.
A workspace window is the UI element that actually renders a workspace. (It's the thing that slides from the right in desktop mode.) When a workspace window is opened, it can only render one workspace at any given time. It is possible, however, for multiple workspaces to belong to the same workspace window. A workspace window must belong to a workspace group. Note that multiple windows in the same workspace group can be opened at the same time.
Examples:
"Add patient to queue" feature in the service queues app involves 2 workspaces: the patient search workspace, and the subsequent workspace to add a selected patient to a queue. This is an example of a workspace launching another (child) workspace. Both of them are rendered in the same window.
The order basket in patient chart. The main order basket workspace, the drug order form workspace , and the lab order form workspace all belong to the same Order Basket workspace window. (Note that there need not be parent-child workspace relationships for 2 workspaces to belong to the same window. For example, the drug order form can be opened standalone when we modify an existing drug order.)
Note that a workspace window can never be launched directly (same with the old system.) Instead, when we do
launchWorkspace, not only do we launch the specified workspace, but the
A workspace window can optionally have an action menu icon associated with it. For example, the Order Basket window has a shopping cart icon.
A workspace and its workspace window are related, but they should not be conflated. This is true even for windows with just one workspace.
A workspace group is a set of workspace windows. When a workspace group is opened, and at least one of its workspace window has an icon, the action menu (right nav in desktop mode) is rendered. Only one workspace group can be opened at any given time.
Examples:
In the patient chart, the "Patient Chart" workspace group contains many workspace windows, 4 of which have icons: The order basket window, the visit note window, the clinical forms window, and the patient list window. Note that it has many other windows that do not have icons. (The start visit window, the vitals windows,
Each level of grouping (of workspaces into windows, and windows into groups) has a notion of context. For example, in the patient chart:
the "Patient Chart" workspace group has a context that is the patient + their visit. (It would be jarring if the order basket window and clinical form window are both opened, but operating on a different patient, or operating on the same patient but different visits.)
Each workspace window also has its own context. For example, the Clinical Forms window, the order basket window and the visit note window can all be opened for viewing / editing different encounters from the past (provided that the encounters are from the same visit.) In this case, the context of each window is the encounter. If we attempt to open any of those workspaces to view another encounter, its context changes.
The props that we pass in at the workspace / window / groups level define the context at that level. If we attempt to switch context at any level, all affected opened workspace MUST first close. The system prompts the user, using a modal, for discarding unsaved changes before proceeding.
Migration
The new workspace system is (unfortunately) backwards-incompatible change. To migrate:
In
routes.json, theworkspace2,workspaceWindow2andworkspaceGroup2sections should be used to declare workspaces / windows / group. Follow the schema defined here. Of note:Previously,
routes.jsonhas aworkspacessection and aworkspaceGroupssection . Instead, there are now 3 sections:workspaces2,workspaceWindows2andworkspaceGroups2. Workspaces belong to windows, and windows belong to groups. The hierarchy must be explicit and intentional.The
typefield no longer exists when defining aworkspace. It served two purpose:Workspaces with the same
typewere grouped into the same window. Instead, aworkspacenow has thewindowfield to specify the window it belongs to.The
typefield was also used to match the corresponding<ActionMenuButton>of the same type, to associate the icon with the window. Instead, aworkspaceWindownow has an optionaliconfield to specify the icon to display in the workspace action menu.
The
canHidefield no longer exists when defining aworkspace. Instead, aworkspaceWindowcan be hidden (minimized) if and only if it has anicondefined.
Existing calls to
launchWorkspaceandlaunchWorkspaceGroupneeds to be switched tolaunchWorkspace2andlaunchWorkspaceGroup2. ThelaunchWorkspacefunction takes in not only workspace props, but also (optionally) the window props and group props. ThelaunchWorkspaceGroup2function takes in group props (same as before). As part of the migration, developers need to re-consider what the right props (context) should be at each level.The workspace action menu is only rendered when
launchWorkspaceGroup2is called, provided that the group has at least one window with an icon. Similarly,closeWorkspaceGroup2removes the action menu.closeWorkspace2is no longer a global function. Instead, it is a prop passed in to workspace components. See typeWorkspace2DefinitionProps
Each Workspace component should be wrapped in a
<Workspace2>at the top level:The workspace component should be of type
React.FC<Workspace2DefinitionProps<X,Y,Z>>, whereX, Y, Zare workspace props type, workspace window props type, and workspace group props type respectively.The
<Workspace2>component takes in the following props:the workspace title (Previously, the workspace title was defined as an i18n key in the
workspacedefinition inroutes.json)hasUnsavedChanges, to denote whether the workspace has unsaved changes (Previously, this was done by callingpromptBeforeClosing(() => isDirty))
The new system has proper support for child workspaces, which can be launched using the
launchChildWorkspacefunction (available in typeWorkspace2DefinitionProps)components that render
<ActionMenuButton>should be switched to render<ActionMenuButton2>instead (with different props). Note that the component still need to be export in the module'sindex.tsfile, but need not be declared as an extension inroutes.json.
Note that the names of these types / components / functions with the “2"at the end should be temporary while we are transitioning between the old system and the new.
Migration Examples
Migration in the Dispensing App. This is a relatively simple migration, as the 3 workspaces defined in the app act standalone and do not share data or interact with other workspace in any way.
Migration of “Add patient to queue” workspace window in the Service Queues App. This is a more complex, but representative, migration example. Note that the “Add patient to queue” workspace window contains 3 workspaces (The patient search workspace, the actual create queue entry workspace, and the start visit workspace.) All three workspace (from 3 different ESMs) need to be migrated to the new system.