Skip to content

Commit

Permalink
fix: User Preferences Issues (#1207)
Browse files Browse the repository at this point in the history
* fix: study list translations

* Don't render until translations are ready

* Try to wait for translations to load

* Use null to render "nothing"

* Try toggling useSuspense off up a layer

* logging

* Remove useSuspense false flags

* DO NOT OPEN A PR WITH IT.
Experimental changes only.
Try to solve issues with useTranslation hooks
 #Please enter the commit message for your changes. Lines starting

* Remove unecessary changes

* feat: 🎸 useMedia hook to not use one more prop for upd state vl

* docs: Add license scan report and status (#1161)

Signed-off-by: fossabot <badges@fossa.io>

* fix: 🐛 Fix for JS breaking on header (#1164)

* feat: 🎸 Code review and refact

Revised code based on PRs(variable alias,...). Changed hook for
useMedia. Now, it provides two hooks: one to get displayMediaSize and
other to get Entity(component, objects...) based on displayMediaSize.
Implemented a similar solution for state manager to store
mediaQueryList(s) and displaySize for app.

* feat: 🎸 Fine tunning on mediaQuery value, fixed issue about it

* fix: 🐛 Fixed issue and refactoring

Fixed js exception and also refactoring userPreferences components to
functional component

* fix: 🐛 Code review. Localstorage fix minor bugs

* fix: 🐛 Ensure hotkey lower case always

* fix: translation switcher

* chore(release): publish [skip ci]

 - @ohif/extension-vtk@0.53.6
 - @ohif/ui@0.62.1
 - @ohif/viewer@2.8.2

* Add new modal service

* Change serviceManager prop to servicesManager

* CR Update: fix casing and add required proptypes to providers

* CR Update: Improve ohifmodal proptypes

* CR Update: Fix typo in extensionmanager

* CR Update: add default props to service and check service in provider

* Refactor modal provider to better use its own state

* ci: don't build our master branch (#1177)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* Use modal instead of modal context

* Ci/promotable builds (#1179)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* ci: try building with QUICK_BUILD flag

* Change modal children order

* Ci/promotable builds (#1180)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* ci: try building with QUICK_BUILD flag

* Try using ~/repo prefix in command

* Ci/promotable builds (#1181)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* ci: try building with QUICK_BUILD flag

* Try using ~/repo prefix in command

* ci: make sure netlify command is available

* Ci/promotable builds (#1182)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* ci: try building with QUICK_BUILD flag

* Try using ~/repo prefix in command

* ci: make sure netlify command is available

* ci: use sudo for global command

* Fix OHIFModal proptypes

* Ci/promotable builds (#1183)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* ci: try building with QUICK_BUILD flag

* Try using ~/repo prefix in command

* ci: make sure netlify command is available

* ci: use sudo for global command

* Inline personal access token w/ env var

* Ci/promotable builds (#1184)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* ci: try building with QUICK_BUILD flag

* Try using ~/repo prefix in command

* ci: make sure netlify command is available

* ci: use sudo for global command

* Inline personal access token w/ env var

* ci: workaround for sudo limitations

* ci: restore release workflow (#1185)

* ci: don't build our master branch

* Add netlify-cli as a dev dependency

* ci: trying a sheltered merge flow for promotions

* ci: try building with QUICK_BUILD flag

* Try using ~/repo prefix in command

* ci: make sure netlify command is available

* ci: use sudo for global command

* Inline personal access token w/ env var

* ci: workaround for sudo limitations

* ci: restore release workflow

* Pass services to each module, improve tests

* Add servicesManager test and registerServices method

* Fix key warning of snackbar elements

* Remove netlify-cli; we';ll install this on CI server

* Update staging and prod netlify site IDs

* Clean up NPM_PUBLISH step

* Clean up DOCS_PUBLISH step

* Clean up Deploy workflow

* Custom executor to override cypress config

* Spacing

* Use an existing docker hub image

* Switch to npx instead of digging into npm bin location

* Remove e2e test before prod deploy

* Add workflow images

* docs: continous integrationn

* Add default props to modal

* chore(release): publish [skip ci]

 - @ohif/extension-cornerstone@1.5.1
 - @ohif/extension-vtk@0.53.7
 - @ohif/core@1.9.1
 - @ohif/ui@0.62.2
 - @ohif/viewer@2.8.3

* chore(release): publish [skip ci]

 - @ohif/extension-vtk@0.53.8
 - @ohif/core@1.10.0
 - @ohif/ui@0.62.3
 - @ohif/viewer@2.8.4

* ci: Redirect site traffic to index.html if file is not resolved

* ci: fix typo

* fix: 🐛 Code review. Remove 'global state' for displaySize

* fix: 🐛 Code review. Ref back to useMedia and pass value down

Code review. Ref back to useMedia and pass value down (components)
instead of creating a specialized hook to tied any component on it

* fix: translation switcher

* fix

* cleanup

* add missed translation

* fix: 🐛 Code review

* fix: 🐛 Fix unit tests

* fix: 🐛 Fix unit test

* fix: 🐛 Code merge solve conflicts. Missing files changes

* Merge from master. Missing files changes

* feat: 🎸 Code review. Fix issues with DatePicker

Fixed at least the minimum issue with datePicker and update some content
on every translation changed

* fix: 🐛 Code review. Style fix on modal

* fixes in general preferences after merge

* fix

* export default i18n language

* translation fixes

* increase snackbar zindex

* initialize language

* WIP propagate props<>state fixes

* fix state propagation

* remove hotkey error when reset default

* fix datePicker

* i18n updates

* fix i18n strings

* fix reset default -- set right input value

* fix default language

* fix e2e for user preferences

* small fix datePicker props

* remove i18n from window -- debugging purposes only

* lint fixes
  • Loading branch information
rodrigoea authored and dannyrb committed Nov 28, 2019
1 parent fd1a9e2 commit 1df21a9
Show file tree
Hide file tree
Showing 31 changed files with 1,329 additions and 734 deletions.
69 changes: 58 additions & 11 deletions platform/core/src/classes/HotkeysManager.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import cloneDeep from 'lodash.clonedeep';
import hotkeys from './hotkeys';
import log from './../log.js';

Expand Down Expand Up @@ -44,20 +43,68 @@ export class HotkeysManager {
}

/**
* Registers a list of hotkeydefinitions. Optionally, sets the
* default hotkey bindings for all provided definitions. These
* values are used in `this.restoreDefaultBindings`.
* Registers a list of hotkeydefinitions.
*
* @param {HotkeyDefinition[]} hotkeyDefinitions
* @param {Boolean} [isDefaultDefinitions]
* @param {HotkeyDefinition[] | Object} hotkeyDefinitions Contains hotkeys definitions
*/
setHotkeys(hotkeyDefinitions, isDefaultDefinitions = false) {
const definitions = cloneDeep(hotkeyDefinitions);
setHotkeys(hotkeyDefinitions) {
const definitions = Array.isArray(hotkeyDefinitions)
? [...hotkeyDefinitions]
: this._parseToArrayLike(hotkeyDefinitions);

definitions.forEach(definition => this.registerHotkeys(definition));
}

if (isDefaultDefinitions) {
this.hotkeyDefaults = definitions;
}
/**
* Set default hotkey bindings. These
* values are used in `this.restoreDefaultBindings`.
*
* @param {HotkeyDefinition[] | Object} hotkeyDefinitions Contains hotkeys definitions
*/
setDefaultHotKeys(hotkeyDefinitions) {
const definitions = Array.isArray(hotkeyDefinitions)
? [...hotkeyDefinitions]
: this._parseToArrayLike(hotkeyDefinitions);

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,
};
}

/**
Expand Down
10 changes: 6 additions & 4 deletions platform/core/src/classes/HotkeysManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
});

describe('setDefaultHotKeys()', () => {
it('it sets default hotkeys', () => {
const hotkeyDefinitions = [{ commandName: 'dance', keys: '+' }];
const isDefaultDefinitions = true;

hotkeysManager.setHotkeys(hotkeyDefinitions, isDefaultDefinitions);
hotkeysManager.setDefaultHotKeys(hotkeyDefinitions);

expect(hotkeysManager.hotkeyDefaults).toEqual(hotkeyDefinitions);
});
Expand Down
5 changes: 4 additions & 1 deletion platform/core/src/redux/reducers/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 4 additions & 2 deletions platform/i18n/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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;
3 changes: 3 additions & 0 deletions platform/i18n/src/locales/ar/UserPreferencesModal.json
Original file line number Diff line number Diff line change
@@ -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."
}
7 changes: 7 additions & 0 deletions platform/i18n/src/locales/ar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import UserPreferencesModal from "./UserPreferencesModal.json";

export default {
'ar': {
UserPreferencesModal,
}
};
3 changes: 3 additions & 0 deletions platform/i18n/src/locales/en-US/UserPreferencesModal.json
Original file line number Diff line number Diff line change
@@ -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. <br /> You must <strong>Save</strong> to perform this action.",
"Save": "$t(Buttons:Save)",
"SaveMessage": "Preferences saved",
"User Preferences": "User Preferences"
}
2 changes: 2 additions & 0 deletions platform/i18n/src/locales/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ar from './ar/';
import en_US from './en-US/';
import es from './es/';
import ja_JP from './ja-JP/';
Expand All @@ -7,6 +8,7 @@ import vi from './vi/';
import zh from './zh/';

export default {
...ar,
...en_US,
...es,
...ja_JP,
Expand Down
2 changes: 2 additions & 0 deletions platform/i18n/src/locales/pt-BR/UserPreferencesModal.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"Cancel": "Cancelar",
"Reset to Defaults": "Restaurar Default",
"ResetDefaultMessage": "Preferências resetadas com sucesso. <br /> Você deve <strong>Salvar</strong> para que essa ação seja realizada.",
"Save": "Salvar",
"SaveMessage": "Preferências salvas",
"User Preferences": "Preferências do Usuário"
}
47 changes: 15 additions & 32 deletions platform/ui/src/components/languageSwitcher/LanguageSwitcher.js
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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 (
<select
name="language-select"
id="language-select"
className="language-select"
value={currentLanguage}
value={parseLanguage(language)}
onChange={onChange}
>
{languages.map(language => (
<option key={language.value} value={language.value}>
{language.label}
{languages.map(lng => (
<option key={lng.value} value={lng.value}>
{lng.label}
</option>
))}
</select>
);
};

LanguageSwitcher.propTypes = {
language: PropTypes.string.isRequired,
onLanguageChange: PropTypes.func.isRequired,
};

export default withTranslation('UserPreferencesModal')(LanguageSwitcher);
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
6 changes: 3 additions & 3 deletions platform/ui/src/components/studyList/StudyList.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -187,7 +187,6 @@ function StudyList(props) {
studyDate={study.studyDate}
studyDescription={study.studyDescription || ''}
studyInstanceUid={study.studyInstanceUid}
t={t}
displaySize={displaySize}
/>
))}
Expand Down Expand Up @@ -239,10 +238,11 @@ function TableRow(props) {
studyDescription,
studyInstanceUid,
onClick: handleClick,
t,
displaySize,
} = props;

const { t } = useTranslation('StudyList');

const largeRowTemplate = (
<tr
onClick={() => handleClick(studyInstanceUid)}
Expand Down
9 changes: 4 additions & 5 deletions platform/ui/src/components/studyList/TableSearchFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,14 @@ function TableSearchFilter(props) {
// https://github.com/airbnb/react-dates
<CustomDateRangePicker
// Required
startDate={getDateEntry(studyDateTo, defaultStartDate)}
startDate={getDateEntry(studyDateFrom, defaultStartDate)}
startDateId="start-date"
endDate={getDateEntry(studyDateFrom, defaultEndDate)}
endDate={getDateEntry(studyDateTo, defaultEndDate)}
endDateId="end-date"
autoFocus={false}
// TODO: We need a dynamic way to determine which fields values to update
onDatesChange={({ startDate, endDate, preset = false }) => {
onValueChange('studyDateTo', startDate);
onValueChange('studyDateFrom', endDate);
onValueChange('studyDateFrom', startDate);
onValueChange('studyDateTo', endDate);
}}
focusedInput={focusedInput}
onFocusChange={updatedVal => setFocusedInput(updatedVal)}
Expand Down

0 comments on commit 1df21a9

Please sign in to comment.