diff --git a/platform/core/src/classes/HotkeysManager.js b/platform/core/src/classes/HotkeysManager.js
index 280c045ce..781f79251 100644
--- a/platform/core/src/classes/HotkeysManager.js
+++ b/platform/core/src/classes/HotkeysManager.js
@@ -1,4 +1,3 @@
-import cloneDeep from 'lodash.clonedeep';
import hotkeys from './hotkeys';
import log from './../log.js';
@@ -44,20 +43,68 @@ export class HotkeysManager {
}
/**
- * Registers a list of hotkeydefinitions. Optionally, sets the
- * default hotkey bindings for all provided definitions. These
+ * Registers a list of hotkeydefinitions.
+ *
+ * @param {HotkeyDefinition[] | Object} hotkeyDefinitions Contains hotkeys definitions
+ */
+ setHotkeys(hotkeyDefinitions) {
+ const definitions = Array.isArray(hotkeyDefinitions)
+ ? [...hotkeyDefinitions]
+ : this._parseToArrayLike(hotkeyDefinitions);
+
+ definitions.forEach(definition => this.registerHotkeys(definition));
+ }
+
+ /**
+ * Set default hotkey bindings. These
* values are used in `this.restoreDefaultBindings`.
*
- * @param {HotkeyDefinition[]} hotkeyDefinitions
- * @param {Boolean} [isDefaultDefinitions]
+ * @param {HotkeyDefinition[] | Object} hotkeyDefinitions Contains hotkeys definitions
*/
- setHotkeys(hotkeyDefinitions, isDefaultDefinitions = false) {
- const definitions = cloneDeep(hotkeyDefinitions);
- definitions.forEach(definition => this.registerHotkeys(definition));
+ setDefaultHotKeys(hotkeyDefinitions) {
+ const definitions = Array.isArray(hotkeyDefinitions)
+ ? [...hotkeyDefinitions]
+ : this._parseToArrayLike(hotkeyDefinitions);
- if (isDefaultDefinitions) {
- this.hotkeyDefaults = definitions;
- }
+ this.hotkeyDefaults = definitions;
+ }
+
+ /**
+ * It parses given object containing hotkeyDefinition to array like.
+ * Each property of given object will be mapped to an object of an array. And its property name will be the value of a property named as commandName
+ *
+ * @param {HotkeyDefinition[] | Object} hotkeyDefinitions Contains hotkeys definitions
+ * @returns {HotkeyDefinition[]}
+ */
+ _parseToArrayLike(hotkeyDefinitionsObj) {
+ const copy = { ...hotkeyDefinitionsObj };
+ return Object.entries(copy).map(entryValue =>
+ this._parseToHotKeyObj(entryValue[0], entryValue[1])
+ );
+ }
+
+ /**
+ * Return HotkeyDefinition object like based on given property name and property value
+ * @param {string} propertyName property name of hotkey definition object
+ * @param {object} propertyValue property value of hotkey definition object
+ *
+ * @example
+ *
+ * const hotKeyObj = {hotKeyDefA: {keys:[],....}}
+ *
+ * const parsed = _parseToHotKeyObj(Object.keys(hotKeyDefA)[0], hotKeyObj[hotKeyDefA]);
+ * {
+ * commandName: hotKeyDefA,
+ * keys: [],
+ * ....
+ * }
+ *
+ */
+ _parseToHotKeyObj(propertyName, propertyValue) {
+ return {
+ commandName: propertyName,
+ ...propertyValue,
+ };
}
/**
diff --git a/platform/core/src/classes/HotkeysManager.test.js b/platform/core/src/classes/HotkeysManager.test.js
index e02c1f9a0..c980c5b1a 100644
--- a/platform/core/src/classes/HotkeysManager.test.js
+++ b/platform/core/src/classes/HotkeysManager.test.js
@@ -94,18 +94,20 @@ describe('HotkeysManager', () => {
expect(firstCallArgs).toEqual(hotkeyDefinitions[0]);
expect(secondCallArgs).toEqual(hotkeyDefinitions[1]);
});
- it('does not set this.hotkeyDefaults by default', () => {
+ it('does not set this.hotkeyDefaults when calling setHotKeys', () => {
const hotkeyDefinitions = [{ commandName: 'dance', keys: '+' }];
hotkeysManager.setHotkeys(hotkeyDefinitions);
expect(hotkeysManager.hotkeyDefaults).toEqual([]);
});
- it('sets this.hotkeyDefaults when isDefaultDefinitions is true', () => {
- const hotkeyDefinitions = [{ commandName: 'dance', keys: '+' }];
- const isDefaultDefinitions = true;
+ });
- hotkeysManager.setHotkeys(hotkeyDefinitions, isDefaultDefinitions);
+ describe('setDefaultHotKeys()', () => {
+ it('it sets default hotkeys', () => {
+ const hotkeyDefinitions = [{ commandName: 'dance', keys: '+' }];
+
+ hotkeysManager.setDefaultHotKeys(hotkeyDefinitions);
expect(hotkeysManager.hotkeyDefaults).toEqual(hotkeyDefinitions);
});
diff --git a/platform/core/src/redux/reducers/preferences.js b/platform/core/src/redux/reducers/preferences.js
index 443996adc..c04ea2603 100644
--- a/platform/core/src/redux/reducers/preferences.js
+++ b/platform/core/src/redux/reducers/preferences.js
@@ -11,12 +11,15 @@ const defaultState = {
// order, description, window (int), level (int)
// 0: { description: 'Soft tissue', window: '', level: '' },
},
+ generalPreferences: {
+ // language: 'en-US'
+ },
};
const preferences = (state, action) => {
switch (action.type) {
case 'SET_USER_PREFERENCES': {
- const newState = action.state ? action.state : cloneDeep(defaultState);
+ const newState = action.state || cloneDeep(defaultState);
return Object.assign({}, state, newState);
}
diff --git a/platform/i18n/src/index.js b/platform/i18n/src/index.js
index 1862714c6..f8b34fc9f 100644
--- a/platform/i18n/src/index.js
+++ b/platform/i18n/src/index.js
@@ -42,6 +42,7 @@ const locizeOptions = {
const envUseLocize = !!config.USE_LOCIZE;
const envApiKeyAvailable = !!config.LOCIZE_API_KEY;
+const DEFAULT_LANGUAGE = 'en-US';
function initI18n(
detection = detectionOptions,
@@ -74,7 +75,7 @@ function initI18n(
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
- fallbackLng: 'en-US',
+ fallbackLng: DEFAULT_LANGUAGE,
saveMissing: apiKeyAvailable,
debug: debugMode,
keySeparator: false,
@@ -111,7 +112,7 @@ function initI18n(
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
- fallbackLng: 'en-US',
+ fallbackLng: DEFAULT_LANGUAGE,
resources: locales,
debug: debugMode,
keySeparator: false,
@@ -136,5 +137,6 @@ customDebug(`version ${pkg.version} loaded.`, 'info');
i18n.initializing = initI18n();
i18n.initI18n = initI18n;
i18n.addLocales = addLocales;
+i18n.defaultLanguage = DEFAULT_LANGUAGE;
export default i18n;
diff --git a/platform/i18n/src/locales/ar/UserPreferencesModal.json b/platform/i18n/src/locales/ar/UserPreferencesModal.json
new file mode 100644
index 000000000..06992c508
--- /dev/null
+++ b/platform/i18n/src/locales/ar/UserPreferencesModal.json
@@ -0,0 +1,3 @@
+{
+ "No hotkeys found": "Nenhuma tecla de atalho está configurada para este aplicativo. As teclas de atalho podem ser configuradas no arquivo app-config.js do aplicativo."
+}
\ No newline at end of file
diff --git a/platform/i18n/src/locales/ar/index.js b/platform/i18n/src/locales/ar/index.js
new file mode 100644
index 000000000..37525f490
--- /dev/null
+++ b/platform/i18n/src/locales/ar/index.js
@@ -0,0 +1,7 @@
+import UserPreferencesModal from "./UserPreferencesModal.json";
+
+export default {
+ 'ar': {
+ UserPreferencesModal,
+ }
+};
\ No newline at end of file
diff --git a/platform/i18n/src/locales/en-US/UserPreferencesModal.json b/platform/i18n/src/locales/en-US/UserPreferencesModal.json
index 1b1b5f1d8..52b0dc167 100644
--- a/platform/i18n/src/locales/en-US/UserPreferencesModal.json
+++ b/platform/i18n/src/locales/en-US/UserPreferencesModal.json
@@ -1,6 +1,9 @@
{
"Cancel": "$t(Buttons:Cancel)",
+ "No hotkeys found": "No hotkeys are configured for this application. Hotkeys can be configured in the application's app-config.js file.",
"Reset to Defaults": "$t(Buttons:Reset to Defaults)",
+ "ResetDefaultMessage": "Preferences successfully reset to default. You must Save to perform this action.",
"Save": "$t(Buttons:Save)",
+ "SaveMessage": "Preferences saved",
"User Preferences": "User Preferences"
}
\ No newline at end of file
diff --git a/platform/i18n/src/locales/index.js b/platform/i18n/src/locales/index.js
index 2f7a0f0c6..02ef9d331 100644
--- a/platform/i18n/src/locales/index.js
+++ b/platform/i18n/src/locales/index.js
@@ -1,3 +1,4 @@
+import ar from './ar/';
import en_US from './en-US/';
import es from './es/';
import ja_JP from './ja-JP/';
@@ -7,6 +8,7 @@ import vi from './vi/';
import zh from './zh/';
export default {
+ ...ar,
...en_US,
...es,
...ja_JP,
diff --git a/platform/i18n/src/locales/pt-BR/UserPreferencesModal.json b/platform/i18n/src/locales/pt-BR/UserPreferencesModal.json
index 156a13dfd..b500f9f82 100644
--- a/platform/i18n/src/locales/pt-BR/UserPreferencesModal.json
+++ b/platform/i18n/src/locales/pt-BR/UserPreferencesModal.json
@@ -1,6 +1,8 @@
{
"Cancel": "Cancelar",
"Reset to Defaults": "Restaurar Default",
+ "ResetDefaultMessage": "Preferências resetadas com sucesso. Você deve Salvar para que essa ação seja realizada.",
"Save": "Salvar",
+ "SaveMessage": "Preferências salvas",
"User Preferences": "Preferências do Usuário"
}
\ No newline at end of file
diff --git a/platform/ui/src/components/languageSwitcher/LanguageSwitcher.js b/platform/ui/src/components/languageSwitcher/LanguageSwitcher.js
index f531b6e62..da22eb80b 100644
--- a/platform/ui/src/components/languageSwitcher/LanguageSwitcher.js
+++ b/platform/ui/src/components/languageSwitcher/LanguageSwitcher.js
@@ -1,14 +1,12 @@
-import React, { useState, useEffect } from 'react';
-import i18n from '@ohif/i18n';
+import React from 'react';
+import PropTypes from 'prop-types';
import './LanguageSwitcher.styl';
import { withTranslation } from '../../contextProviders';
-const LanguageSwitcher = () => {
- const getCurrentLanguage = (language = i18n.language) =>
- language.split('-')[0];
+const LanguageSwitcher = ({ language, onLanguageChange }) => {
+ const parseLanguage = lang => lang.split('-')[0];
- const [currentLanguage, setCurrentLanguage] = useState(getCurrentLanguage());
const languages = [
// TODO: list of available languages should come from i18n.options.resources
{
@@ -21,46 +19,31 @@ const LanguageSwitcher = () => {
},
];
- const onChange = () => {
+ const onChange = event => {
const { value } = event.target;
- const language = getCurrentLanguage(value);
- setCurrentLanguage(language);
-
- i18n.init({
- fallbackLng: language,
- lng: language,
- });
+ onLanguageChange(parseLanguage(value));
};
- useEffect(() => {
- let mounted = true;
-
- i18n.on('languageChanged', () => {
- if (mounted) {
- setCurrentLanguage(getCurrentLanguage());
- }
- });
-
- return () => {
- mounted = false;
- };
- }, []);
-
return (
);
};
+LanguageSwitcher.propTypes = {
+ language: PropTypes.string.isRequired,
+ onLanguageChange: PropTypes.func.isRequired,
+};
+
export default withTranslation('UserPreferencesModal')(LanguageSwitcher);
diff --git a/platform/ui/src/components/studyList/CustomDateRangePicker.js b/platform/ui/src/components/studyList/CustomDateRangePicker.js
index 17b5d47df..dc82d5f32 100644
--- a/platform/ui/src/components/studyList/CustomDateRangePicker.js
+++ b/platform/ui/src/components/studyList/CustomDateRangePicker.js
@@ -131,7 +131,6 @@ CustomDateRangePicker.propTypes = {
end: PropTypes.required,
})
),
- autoFocus: PropTypes.bool.isRequired,
onDatesChange: PropTypes.func.isRequired,
startDate: PropTypes.instanceOf(Date),
endDate: PropTypes.instanceOf(Date),
diff --git a/platform/ui/src/components/studyList/StudyList.js b/platform/ui/src/components/studyList/StudyList.js
index 4565c9823..9e6c56fad 100644
--- a/platform/ui/src/components/studyList/StudyList.js
+++ b/platform/ui/src/components/studyList/StudyList.js
@@ -81,7 +81,7 @@ function StudyList(props) {
const mediumTableMeta = [
{
- displayText: `${t('Patient')} / ${t('MRN')}`,
+ displayText: `${t('PatientName')} / ${t('MRN')}`,
fieldName: 'patientNameOrId',
inputType: 'text',
size: 250,
@@ -187,7 +187,6 @@ function StudyList(props) {
studyDate={study.studyDate}
studyDescription={study.studyDescription || ''}
studyInstanceUid={study.studyInstanceUid}
- t={t}
displaySize={displaySize}
/>
))}
@@ -239,10 +238,11 @@ function TableRow(props) {
studyDescription,
studyInstanceUid,
onClick: handleClick,
- t,
displaySize,
} = props;
+ const { t } = useTranslation('StudyList');
+
const largeRowTemplate = (
handleClick(studyInstanceUid)}
diff --git a/platform/ui/src/components/studyList/TableSearchFilter.js b/platform/ui/src/components/studyList/TableSearchFilter.js
index 396738db9..ca9874e5a 100644
--- a/platform/ui/src/components/studyList/TableSearchFilter.js
+++ b/platform/ui/src/components/studyList/TableSearchFilter.js
@@ -103,15 +103,14 @@ function TableSearchFilter(props) {
// https://github.com/airbnb/react-dates
{
- onValueChange('studyDateTo', startDate);
- onValueChange('studyDateFrom', endDate);
+ onValueChange('studyDateFrom', startDate);
+ onValueChange('studyDateTo', endDate);
}}
focusedInput={focusedInput}
onFocusChange={updatedVal => setFocusedInput(updatedVal)}
diff --git a/platform/ui/src/components/userPreferencesForm/GeneralPreferences.js b/platform/ui/src/components/userPreferencesForm/GeneralPreferences.js
index 51d68344e..42ad1e7ec 100644
--- a/platform/ui/src/components/userPreferencesForm/GeneralPreferences.js
+++ b/platform/ui/src/components/userPreferencesForm/GeneralPreferences.js
@@ -1,17 +1,57 @@
-import React, { Component } from 'react';
+import React from 'react';
+import PropTypes from 'prop-types';
import LanguageSwitcher from '../languageSwitcher';
+import i18n from '@ohif/i18n';
-export class GeneralPreferences extends Component {
- render() {
- return (
-
-
-
-
-
+/**
+ * General Preferences tab
+ */
+
+/**
+ * General Preferences tab
+ * It renders the General Preferences content
+ *
+ * It stores current state and whenever it changes, component messages parent of new value (through function callback)
+ * @param {object} props component props
+ * @param {string} props.name Tab`s name
+ * @param {object} props.generalPreferences Data for initial state
+ * @param {function} props.onTabStateChanged Callback function to communicate parent in case its states changes
+ * @param {function} props.onTabErrorChanged Callback Function in case any error on tab
+ */
+function GeneralPreferences({
+ generalPreferences,
+ name,
+ onTabStateChanged,
+ onTabErrorChanged,
+}) {
+ const { language = i18n.language } = generalPreferences;
+
+ const onLanguageChange = language => {
+ onTabStateChanged(name, {
+ generalPreferences: { ...generalPreferences, language },
+ });
+ };
+
+ return (
+