Skip to content

Commit

Permalink
(feat) Allow useConfig() to load configuration from other modules (#751)
Browse files Browse the repository at this point in the history
  • Loading branch information
ibacher authored Aug 18, 2023
1 parent c836b87 commit dabecc7
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 12 deletions.
12 changes: 9 additions & 3 deletions packages/framework/esm-framework/docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -1715,23 +1715,29 @@ ___

### useConfig

**useConfig**<`T`\>(): `T`
**useConfig**<`T`\>(`options?`): `T`

Use this React Hook to obtain your module's configuration.

#### Type parameters

| Name | Type |
| :------ | :------ |
| `T` | `Omit`<[`ConfigObject`](interfaces/ConfigObject.md), ``"Display conditions"`` \| ``"Translation overrides"``\> |
| `T` | `Record`<`string`, `any`\> |

#### Parameters

| Name | Type | Description |
| :------ | :------ | :------ |
| `options?` | `UseConfigOptions` | Additional options that can be passed to useConfig() |

#### Returns

`T`

#### Defined in

[packages/framework/esm-react-utils/src/useConfig.ts:163](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-react-utils/src/useConfig.ts#L163)
[packages/framework/esm-react-utils/src/useConfig.ts:171](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-react-utils/src/useConfig.ts#L171)

___

Expand Down
42 changes: 42 additions & 0 deletions packages/framework/esm-react-utils/src/useConfig.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ function RenderConfig(props) {
return <button>{config[props.configKey]}</button>;
}

function RenderExternalConfig(props) {
const config = useConfig({ externalModuleName: props.externalModuleName });

return <button>{config[props.configKey]}</button>;
}

function clearConfig() {
mockConfigInternalStore.resetMock();
}
Expand Down Expand Up @@ -297,4 +303,40 @@ describe(`useConfig in an extension`, () => {
expect(screen.findByText("Yet another thing")).toBeTruthy()
);
});

it("can optionally load an external module's configuration", async () => {
defineConfigSchema("first-module", {
thing: {
_default: "first thing",
},
});

defineConfigSchema("second-module", {
thing: {
_default: "second thing",
},
});

render(
<React.Suspense fallback={<div>Suspense!</div>}>
<ComponentContext.Provider
value={{
moduleName: "first-module",
extension: {
extensionSlotName: "fooSlot",
extensionSlotModuleName: "slot-mod",
extensionId: "fooExt#id1",
},
}}
>
<RenderExternalConfig
externalModuleName="second-module"
configKey="thing"
/>
</ComponentContext.Provider>
</React.Suspense>
);

await waitFor(() => expect(screen.findByText("second thing")).toBeTruthy());
});
});
26 changes: 17 additions & 9 deletions packages/framework/esm-react-utils/src/useConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,24 @@ function useNormalConfig(moduleName: string) {
return state;
}

interface UseConfigOptions {
/** An external module to load the configuration from. This option should only be used if
absolutely necessary as it can end up making frontend modules coupled to one another. */
externalModuleName?: string;
}

/**
* Use this React Hook to obtain your module's configuration.
*
* @param options Additional options that can be passed to useConfig()
*/
export function useConfig<
T = Omit<ConfigObject, "Display conditions" | "Translation overrides">
>() {
export function useConfig<T = Record<string, any>>(options?: UseConfigOptions) {
// This hook uses the config of the MF defining the component.
// If the component is used in an extension slot then the slot
// may override (part of) its configuration.
const { moduleName, extension } = useContext(ComponentContext);
const { moduleName: contextModuleName, extension } =
useContext(ComponentContext);
const moduleName = options?.externalModuleName ?? contextModuleName;

if (!moduleName && !extension) {
throw Error(errorMessage);
Expand All @@ -175,11 +183,11 @@ export function useConfig<
const normalConfig = useNormalConfig(moduleName);
const extensionConfig = useExtensionConfig(extension);
const config = useMemo(
() => ({
...normalConfig,
...extensionConfig,
}),
[normalConfig, extensionConfig]
() =>
options?.externalModuleName && moduleName === options.externalModuleName
? { ...normalConfig }
: { ...normalConfig, ...extensionConfig },
[moduleName, options?.externalModuleName, normalConfig, extensionConfig]
);

return config as T;
Expand Down

0 comments on commit dabecc7

Please sign in to comment.