Docs
Workspaces
Creating workspaces

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. For patient workspaces, all Workspaces receive the props defined and documented in the interface DefaultPatientWorkspaceProps which includes patient, patientUuid, and the functions closeWorkspace, promptBeforeClosing, and closeWorkspaceWithSavedChanges.

Your Workspace's prop type should extend DefaultPatientWorkspaceProps from @openmrs/esm-patient-common-lib for patient workspaces. 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.tsx
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, ButtonSet, Form, TextInput } from '@carbon/react';
import { type DefaultPatientWorkspaceProps } from '@openmrs/esm-patient-common-lib';
import { postGastrodynamicsForm } from './gastrodynamics.resource';
import styles from './gastrodynamics-form.module.scss';
 
export function GastrodynamicsForm({ patientUuid, closeWorkspace, promptBeforeClosing }: DefaultPatientWorkspaceProps) {
  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. Workspaces are registered in the routes.json file and exported in the index.ts file. Once the Workspace is registered, it may be launched.

Note that while the parameters 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 of registering a workspace. First, add the workspace definition to routes.json:

// routes.json
{
  "workspaces": [
    {
      "name": "gastrodynamics-form",
      "title": "Gastrodynamics",
      "component": "gastrodynamicsForm",
      "type": "other-form",
      "canHide": false,
      "canMaximize": true,
      "width": "narrow",
      "preferredWindowSize": "maximized"
    }
  ]
}

Then export the workspace component in your index.ts file:

// index.ts
import { getAsyncLifecycle } from '@openmrs/esm-framework';
 
const moduleName = '@myorg/esm-gastrodynamics-app';
 
const options = {
  featureName: 'gastrodynamics',
  moduleName,
};
 
export const gastrodynamicsForm = getAsyncLifecycle(
  () => import('./gastrodynamics.workspace'),
  options
);