Skip to content

Commit

Permalink
(feat) O3-2882 Create core translations library (#933)
Browse files Browse the repository at this point in the history
* (feat) O3-2882 Create core translations library

* Update mock

* yarn

* Unrelated: make prettier quieter

* Remove @openmrs/esm-app-shell namespace

* Fix tests. However, translateFrom (and therefore getCoreTranslations is not working; it always returns the fallback

* fixup

* Get everything working

* Fix fallback and add documentation

* Transition all uses of `translateFrom` to use core translations

* Ian's fix to use --list-different instead of grep for prettier

* Decentralize mocks

* Update yarn.lock

* Try switching apps to workspace:* dependencies

* Fixup

* Try switching apps to workspace:* dependencies
  • Loading branch information
brandones committed Mar 19, 2024
1 parent 8435603 commit 1306721
Show file tree
Hide file tree
Showing 70 changed files with 1,428 additions and 504 deletions.
7 changes: 7 additions & 0 deletions .tx/config
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ type = KEYVALUEJSON
replace_edited_strings = false
keep_translations = false

[o:openmrs:p:openmrs-esm-core:r:esm-translations]
file_filter = packages/framework/esm-translations/translations/<lang>.json
source_file = packages/framework/esm-translations/translations/en.json
source_lang = en
type = KEYVALUEJSON
replace_edited_strings = false
keep_translations = false
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The following common libraries have been developed. They may also be used indepe
- [@openmrs/esm-react-utils](packages/framework/esm-react-utils): utilities for React components
- [@openmrs/esm-state](packages/framework/esm-state): brings in state management
- [@openmrs/esm-styleguide](packages/framework/esm-styleguide): styling and UI capabilities
- [@openmrs/esm-translations](packages/apps/esm-translations): common translations and utilities
- [@openmrs/esm-utils](packages/framework/esm-utils): general utility and helper functions

All libraries are aggregated in the `@openmrs/esm-framework` package:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"setup": "yarn install && turbo run build",
"start": "openmrs develop",
"verify": "turbo run lint && turbo run test && turbo run typescript",
"prettier": "prettier --config prettier.config.js --write \"packages/**/*.{ts,tsx,css,scss}\" \"e2e/**/*.ts\"",
"prettier": "prettier --config prettier.config.js --write \"packages/**/*.{ts,tsx,css,scss}\" \"e2e/**/*.ts\" --list-different",
"postinstall": "husky install",
"test": "cross-env TZ=UTC jest --config jest.config.json --verbose false --passWithNoTests",
"test-watch": "cross-env TZ=UTC jest --watch --config jest.config.json",
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/esm-devtools-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
"rxjs": "6.x"
},
"devDependencies": {
"@openmrs/esm-framework": "^5.5.0",
"@openmrs/webpack-config": "^5.5.0",
"@openmrs/esm-framework": "workspace:*",
"@openmrs/webpack-config": "workspace:*",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-i18next": "^11.18.6",
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/esm-implementer-tools-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
"swr": "2.x"
},
"devDependencies": {
"@openmrs/esm-framework": "^5.5.0",
"@openmrs/webpack-config": "^5.5.0",
"@openmrs/esm-framework": "workspace:*",
"@openmrs/webpack-config": "workspace:*",
"ace-builds": "^1.4.14",
"react": "^18.1.0",
"react-ace": "^9.5.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/esm-login-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
"swr": "2.x"
},
"devDependencies": {
"@openmrs/esm-framework": "^5.5.0",
"@openmrs/webpack-config": "^5.5.0",
"@openmrs/esm-framework": "workspace:*",
"@openmrs/webpack-config": "workspace:*",
"jest": "^29.7.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import {
RadioButtonGroup,
RadioButtonSkeleton,
} from '@carbon/react';
import { navigate, setSessionLocation, useConfig, useConnectivity, useSession } from '@openmrs/esm-framework';
import {
getCoreTranslation,
navigate,
setSessionLocation,
useConfig,
useConnectivity,
useSession,
} from '@openmrs/esm-framework';
import type { LoginReferrer } from '../login/login.component';
import { useLoginLocations } from '../login.resource';
import styles from './location-picker.scss';
Expand Down Expand Up @@ -246,7 +253,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ hideWelcomeMessage, cur
{isSubmitting ? (
<InlineLoading className={styles.loader} description={t('submitting', 'Submitting')} />
) : (
<span>{t('confirm', 'Confirm')}</span>
<span>{getCoreTranslation('confirm')}</span>
)}
</Button>
</div>
Expand Down
3 changes: 2 additions & 1 deletion packages/apps/esm-login-app/src/login/login.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
refetchCurrentUser,
useConnectivity,
navigate as openmrsNavigate,
getCoreTranslation,
} from '@openmrs/esm-framework';
import { type ConfigSchema } from '../config-schema';
import styles from './login.scss';
Expand Down Expand Up @@ -156,7 +157,7 @@ const Login: React.FC = () => {
<InlineNotification
kind="error"
subtitle={t(errorMessage)}
title={t('error', 'Error')}
title={getCoreTranslation('error')}
onClick={() => setErrorMessage('')}
/>
</div>
Expand Down
2 changes: 0 additions & 2 deletions packages/apps/esm-login-app/translations/en.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"back": "Back",
"change": "Change",
"confirm": "Confirm",
"continue": "Continue",
"error": "Error",
"errorLoadingLoginLocations": "Error loading login locations",
"invalidCredentials": "Invalid username or password",
"loading": "Loading",
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/esm-offline-tools-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
"swr": "2.x"
},
"devDependencies": {
"@openmrs/esm-framework": "^5.5.0",
"@openmrs/webpack-config": "^5.5.0",
"@openmrs/esm-framework": "workspace:*",
"@openmrs/webpack-config": "workspace:*",
"@types/lodash-es": "^4.17.5",
"jest": "^29.7.0",
"react": "^18.1.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/esm-primary-navigation-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
"single-spa": "5.x"
},
"devDependencies": {
"@openmrs/esm-framework": "^5.5.0",
"@openmrs/webpack-config": "^5.5.0",
"@openmrs/esm-framework": "workspace:*",
"@openmrs/webpack-config": "workspace:*",
"jest": "^29.7.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
Expand Down
27 changes: 27 additions & 0 deletions packages/framework/esm-api/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { of } from 'rxjs';
import { type SessionStore } from '@openmrs/esm-api';
import { createGlobalStore } from '@openmrs/esm-state/mock';

export const setSessionLocation = jest.fn(() => Promise.resolve());
export const openmrsFetch = jest.fn((url?: string) => new Promise(() => {}));
export const openmrsObservableFetch = jest.fn(() => of({ data: { entry: [] } }));
export function getCurrentUser() {
return of({ authenticated: false });
}
export const mockSessionStore = createGlobalStore<SessionStore>('mock-session-store', {
loaded: false,
session: null,
});
export const getSessionStore = jest.fn(() => mockSessionStore);
export const setCurrentVisit = jest.fn();
export const newWorkspaceItem = jest.fn();
export const restBaseURL = '/ws/rest/v1';
export const fhirBaseUrl = '/ws/fhir2/R4';
export const attachmentUrl = '/ws/rest/v1/attachment';
export const getAttachmentByUuid = jest.fn();
export const getAttachments = jest.fn();
export const createAttachment = jest.fn();
export const deleteAttachmentPermanently = jest.fn();
export const clearCurrentUser = jest.fn();
export const refetchCurrentUser = jest.fn();
export const setUserLanguage = jest.fn();
2 changes: 1 addition & 1 deletion packages/framework/esm-config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = {
moduleNameMapper: {
'@openmrs/esm-context': '<rootDir>/__mocks__/openmrs-esm-context.mock.tsx',
'@openmrs/esm-globals': '<rootDir>/__mocks__/openmrs-esm-globals.mock.tsx',
'@openmrs/esm-state': '<rootDir>/__mocks__/openmrs-esm-state.mock.tsx',
'@openmrs/esm-state': '@openmrs/esm-state/mock',
},
testEnvironment: 'jsdom',
testEnvironmentOptions: {
Expand Down
36 changes: 36 additions & 0 deletions packages/framework/esm-config/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createGlobalStore } from '@openmrs/esm-state/mock';
import * as utils from '@openmrs/esm-utils';

export { validators, validator } from './src/index';

export const configInternalStore = createGlobalStore('config-internal', {});

export const implementerToolsConfigStore = createGlobalStore('implementer-tools-config', {});

export const temporaryConfigStore = createGlobalStore('temporary-config', {});

export enum Type {
Array = 'Array',
Boolean = 'Boolean',
ConceptUuid = 'ConceptUuid',
Number = 'Number',
Object = 'Object',
String = 'String',
UUID = 'UUID',
}

export let configSchema = {};

export const getConfig = jest
.fn()
.mockImplementation(() => Promise.resolve(utils.getDefaultsFromConfigSchema(configSchema)));

export function defineConfigSchema(moduleName, schema) {
configSchema = schema;
}

export function defineExtensionConfigSchema(extensionName, schema) {
configSchema = schema;
}

export const clearConfigErrors = jest.fn();
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import unset from 'lodash/unset';
import * as Config from './module-config';
import type { MockedStore } from '../../__mocks__/openmrs-esm-state.mock';
import { mockStores } from '../../__mocks__/openmrs-esm-state.mock';
import type { MockedStore } from '@openmrs/esm-state/mock';
import { mockStores } from '@openmrs/esm-state/mock';
import { validator } from '../validators/validator';
import { validators, isUrl } from '../validators/validators';
import type { ConfigExtensionStore, ConfigInternalStore, ImplementerToolsConfigStore } from './state';
Expand Down
42 changes: 33 additions & 9 deletions packages/framework/esm-config/src/module-config/module-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,22 @@ export function registerModuleWithConfigSystem(moduleName: string) {
});
}

/**
* This allows the config system to support translation overrides for namespaces that
* do not correspond to modules.
*
* This should only be used in esm-app-shell.
*
* @internal
* @param namespace
*/
export function registerTranslationNamespace(namespace: string) {
const state = configInternalStore.getState();
configInternalStore.setState({
schemas: { ...state.schemas, [namespace]: translationOverridesSchema },
});
}

/**
* This defines a configuration schema for an extension. When a schema is defined
* for an extension, that extension will receive the configuration corresponding
Expand Down Expand Up @@ -783,16 +799,10 @@ function getExtensionNameFromId(extensionId: string) {
}

/**
* The implicitConfigSchema is implicitly included in every configuration schema
* The translation overrides schema is used in the implicit schema given to every module;
* plus any additional translation namespaces (at time of writing, this is just 'core').
*/
const implicitConfigSchema: ConfigSchema = {
'Display conditions': {
privileges: {
_description: 'The privilege(s) the user must have to use this extension',
_type: Type.Array,
_default: [],
},
},
const translationOverridesSchema: ConfigSchema = {
'Translation overrides': {
_description:
'Per-language overrides for frontend translations should be keyed by language code and each language dictionary contains the translation key and the display value',
Expand All @@ -811,3 +821,17 @@ const implicitConfigSchema: ConfigSchema = {
],
},
};

/**
* The implicitConfigSchema is implicitly included in every configuration schema
*/
const implicitConfigSchema: ConfigSchema = {
'Display conditions': {
privileges: {
_description: 'The privilege(s) the user must have to use this extension',
_type: Type.Array,
_default: [],
},
},
...translationOverridesSchema,
};
15 changes: 15 additions & 0 deletions packages/framework/esm-extensions/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getGlobalStore } from '@openmrs/esm-state/mock';

export const attach = jest.fn();
export const detach = jest.fn();
export const detachAll = jest.fn();

export const switchTo = jest.fn();

export const getExtensionStore = () => getGlobalStore('extensions', { slots: {} });

export const getExtensionInternalStore = () =>
getGlobalStore('extensions-internal', {
slots: {},
extensions: {},
});

0 comments on commit 1306721

Please sign in to comment.