Creating Workspaces
To create a Workspace, you need to create the component that implements it, and then register that component with the Workspace system.
The Workspace Component
A Workspace is implemented as a React Component. All Workspaces receive the props defined and documented in the interface DefaultWorkspaceProps
including patientUuid
and the functions closeWorkspace
and promptBeforeClosing
.
Your Workspace's prop type should extend (or simply be) DefaultWorkspaceProps
. This will ensure that you have the correct typings for the props that the Workspace system passes along.
The promptBeforeClosing
prop should be used to inform the Workspace system whether there are unsaved changes in the form. By default, it will be assumed that any open Workspace has unsaved changes. This results in the very annoying behavior that if someone opens your Workspace and then immediately tries to click away, they will be prompted about unsaved changes (this is safer than the alternative, which is by default to never prompt). It is your job as a Workspace creator to ensure that this doesn't happen. You do that using promptBeforeClosing
.
Workspaces may also receive custom props. These will be passed along by the launch function call.
Example Workspace
We define a very minimalistic Workspace supporting the gastrodynamics department at our hospital. The Workspace file uses the .workspace.tsx
extension to make it easy to identify.
// gastrodynamics.workspace.tsximport React, { useEffect, useState } from 'react';import { useTranslation } from 'react-i18next';import { Button, ButtonSet, Form, TextInput } from '@carbon/react';import { type DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';import { postGastrodynamicsForm } from './gastrodynamics.resource';import styles from './gastrodynamics-form.scss'; export function GastrodynamicsForm({ patientUuid, closeWorkspace, promptBeforeClosing }: DefaultWorkspaceProps) { const { t } = useTranslation(); const [textValue, setTextValue] = useState(''); useEffect(() => { promptBeforeClosing(() => textValue.length > 0); }, [textValue]) const handleSubmit = (e) => { e.preventDefault(); postGastrodynamicsForm(patientUuid, textValue, new AbortController()); }; return ( <Form className={styles.form}> <TextInput id="howsThePlumbing" labelText={t('howsThePlumbing', "How's the plumbing?")} onChange={(event) => setTextValue(event.target.value)} value={textValue} /> <ButtonSet> <Button className={styles.button} kind="secondary" onClick={closeWorkspace}> {t('discard', 'Discard')} </Button> <Button onClick={handleSubmit} className={styles.button} kind="primary"> {t('submit', 'Submit')} </Button> </ButtonSet> </Form> );};
Registering a Workspace
Registering lets the Workspace system know about the new Workspace. Once the Workspace is registered, it may be launched.
Our new Workspace can be registered in the index.ts
file, using the registerWorkspace
function from @openmrs/esm-patient-common-lib
. The parameters to registerWorkspace
are documented in the WorkspaceRegistration
interface.
Note that while the parameters to registerWorkspace
allow some flexibility of behavior, you should generally strive to make Workspaces conformant to one of the designed variants(opens in a new tab).
Example Registration
Here's an example, which would go in the index file of the app. We are assuming that the app is called @myorg/esm-gastrodynamics-app
.
// index.tsimport { registerWorkspace } from '@openmrs/esm-patient-common-lib'; export function startupApp() { registerWorkspace({ name: 'gastrodynamics-form', title: translateFrom('@myorg/esm-gastrodynamics-app', 'gastrodynamicsTitle', 'Gastrodynamics'), load: getAsyncLifecycle(() => import('./gastrodynamics.workspace'), options), type: 'other-form', canHide: false, canMaximize: true, width: 'narrow', preferredWindowSize: 'maximized' });}