Workspaces
Workspaces are side panels that slide in from the right side of the screen, typically used for forms and detailed views in the patient chart. They provide a consistent way to display contextual information and forms without navigating away from the current page.
-
Workspace forms should extend
DefaultPatientWorkspacePropsfrom@openmrs/esm-patient-common-libto ensure consistent props and behavior:import { type DefaultPatientWorkspaceProps } from '@openmrs/esm-patient-common-lib'; interface MyWorkspaceProps extends DefaultPatientWorkspaceProps { // Add workspace-specific props here itemId?: string; } const MyWorkspace: React.FC<MyWorkspaceProps> = ({ closeWorkspace, closeWorkspaceWithSavedChanges, patientUuid, promptBeforeClosing, itemId, }) => { // Workspace implementation }; -
Always implement
promptBeforeClosingto warn users when they try to close a workspace with unsaved changes. This should be called in auseEffectthat watches the form's dirty state:const { formState: { isDirty }, } = useForm(...); useEffect(() => { promptBeforeClosing(() => isDirty); }, [isDirty, promptBeforeClosing]);For non-form workspaces, you can use any condition that indicates unsaved changes:
useEffect(() => { promptBeforeClosing(() => hasUnsavedChanges); }, [hasUnsavedChanges, promptBeforeClosing]); -
Use
closeWorkspaceWithSavedChangeswhen data is successfully saved, andcloseWorkspacewhen the user cancels or when closing without saving:const handleSave = async () => { try { await saveData(); mutate(); // Update SWR cache closeWorkspaceWithSavedChanges(); // Indicates successful save showSnackbar({ title: t('saved', 'Saved successfully') }); } catch (error) { showSnackbar({ kind: 'error', title: t('errorSaving', 'Error saving'), subtitle: error?.message, }); } }; // Cancel button <Button kind="secondary" onClick={() => closeWorkspace()}> {t('cancel', 'Cancel')} </Button> -
Workspace components should be registered using
getAsyncLifecyclefor code splitting, orgetSyncLifecycleif they need to be available immediately:// Async - loaded on demand export const myWorkspace = getAsyncLifecycle( () => import('./my-workspace.workspace'), options ); // Sync - included in main bundle export const myWorkspace = getSyncLifecycle( MyWorkspaceComponent, options ); -
Workspace files should use the
.workspace.tsxsuffix to clearly indicate their purpose and enable proper translation key extraction. -
You can use
ExtensionSlotcomponents within workspaces to allow other modules to extend the workspace with additional functionality:import { ExtensionSlot } from '@openmrs/esm-framework'; <ExtensionSlot name="my-workspace-header-slot" state={{ patientUuid, formData }} /> -
Workspace forms should handle both create and edit modes when appropriate. Use conditional logic based on whether an ID or existing data is provided:
const isEditMode = Boolean(itemId); const existingItem = isEditMode ? data?.find(item => item.id === itemId) : null; const defaultValues = isEditMode && existingItem ? { name: existingItem.name, ...existingItem } : { name: '', ...emptyDefaults };