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. 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.tsx
import 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.ts
import { 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'
  });
}