I18n example (#33)
* feat: i18n pt 1 * fix: prevent float calculations when updating dimensions
This commit is contained in:
@@ -28,7 +28,11 @@ exports[`EditorFooter snapshots Save Failed, error message raised 1`] = `
|
||||
}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
description="Label for cancel button"
|
||||
id="authoring.editorfooter.cancelButton.label"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Save Changes and Return to Learning Context"
|
||||
@@ -72,7 +76,11 @@ exports[`EditorFooter snapshots not intialized, Spinner appears and button is di
|
||||
}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
description="Label for cancel button"
|
||||
id="authoring.editorfooter.cancelButton.label"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Save Changes and Return to Learning Context"
|
||||
@@ -115,7 +123,11 @@ exports[`EditorFooter snapshots renders as expected with default behavior 1`] =
|
||||
}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
description="Label for cancel button"
|
||||
id="authoring.editorfooter.cancelButton.label"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Save Changes and Return to Learning Context"
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
ModalDialog,
|
||||
Toast,
|
||||
} from '@edx/paragon';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { nullMethod, saveBlock, navigateCallback } from '../../hooks';
|
||||
|
||||
import { RequestKeys } from '../../data/constants/requests';
|
||||
import { selectors, thunkActions } from '../../data/redux';
|
||||
|
||||
import messages from '../messages';
|
||||
import messages from './messages';
|
||||
import * as module from '.';
|
||||
|
||||
export const handleSaveClicked = (props) => () => saveBlock(props);
|
||||
@@ -23,6 +23,8 @@ export const handleCancelClicked = ({ returnUrl }) => navigateCallback(returnUrl
|
||||
|
||||
export const EditorFooter = ({
|
||||
editorRef,
|
||||
// injected
|
||||
intl,
|
||||
// redux
|
||||
isInitialized,
|
||||
returnUrl,
|
||||
@@ -38,14 +40,14 @@ export const EditorFooter = ({
|
||||
<ActionRow>
|
||||
<ActionRow.Spacer />
|
||||
<Button
|
||||
aria-label="Discard Changes and Return to Learning Context"
|
||||
aria-label={intl.formatMessage(messages.cancelButtonAriaLabel)}
|
||||
variant="tertiary"
|
||||
onClick={module.handleCancelClicked({ returnUrl })}
|
||||
>
|
||||
Cancel
|
||||
<FormattedMessage {...messages.cancelButtonLabel} />
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Save Changes and Return to Learning Context"
|
||||
aria-label={intl.formatMessage(messages.saveButtonAriaLabel)}
|
||||
onClick={module.handleSaveClicked({
|
||||
editorRef,
|
||||
returnUrl,
|
||||
@@ -70,6 +72,8 @@ EditorFooter.propTypes = {
|
||||
PropTypes.func,
|
||||
PropTypes.shape({ current: PropTypes.any }),
|
||||
]),
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
isInitialized: PropTypes.bool.isRequired,
|
||||
returnUrl: PropTypes.string,
|
||||
@@ -88,4 +92,4 @@ export const mapDispatchToProps = {
|
||||
saveBlockContent: thunkActions.app.saveBlock,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EditorFooter);
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(EditorFooter));
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import * as module from './index';
|
||||
|
||||
import { formatMessage } from '../../../testUtils';
|
||||
import { selectors, thunkActions } from '../../data/redux';
|
||||
import { RequestKeys } from '../../data/constants/requests';
|
||||
import { saveBlock, navigateCallback } from '../../hooks';
|
||||
import * as module from './index';
|
||||
|
||||
jest.mock('../../data/redux', () => ({
|
||||
thunkActions: {
|
||||
@@ -39,6 +41,7 @@ jest.mock('../../hooks', () => ({
|
||||
|
||||
describe('EditorFooter', () => {
|
||||
const props = {
|
||||
intl: { formatMessage },
|
||||
editorRef: jest.fn().mockName('args.editorRef'),
|
||||
isInitialized: true,
|
||||
returnUrl: 'hocuspocus.ca',
|
||||
|
||||
29
src/editors/components/EditorFooter/messages.js
Normal file
29
src/editors/components/EditorFooter/messages.js
Normal file
@@ -0,0 +1,29 @@
|
||||
export const messages = {
|
||||
contentSaveFailed: {
|
||||
id: 'authoring.editorfooter.save.error',
|
||||
defaultMessage: 'Error: Content save failed. Try again later.',
|
||||
description: 'Error message displayed when content fails to save.',
|
||||
},
|
||||
cancelButtonAriaLabel: {
|
||||
id: 'authoring.editorfooter.cancelButton.ariaLabel',
|
||||
defaultMessage: 'Discard Changes and Return to Learning Context',
|
||||
description: 'Aria label for cancel button',
|
||||
},
|
||||
cancelButtonLabel: {
|
||||
id: 'authoring.editorfooter.cancelButton.label',
|
||||
defaultMessage: 'Cancel',
|
||||
description: 'Label for cancel button',
|
||||
},
|
||||
saveButtonAriaLabel: {
|
||||
id: 'authoring.editorfooter.savebutton.ariaLabel',
|
||||
defaultMessage: 'Save Changes and Return to Learning Context',
|
||||
description: 'Aria label for save button',
|
||||
},
|
||||
addToCourse: {
|
||||
id: 'authoring.editorfooter.savebutton.label',
|
||||
defaultMessage: 'Save',
|
||||
description: 'Label for Save button',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
@@ -4,20 +4,21 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { Icon, IconButton } from '@edx/paragon';
|
||||
import { Edit } from '@edx/paragon/icons';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { actions, selectors } from '../../data/redux';
|
||||
import { localTitleHooks } from './hooks';
|
||||
import messages from '../messages';
|
||||
import messages from './messages';
|
||||
import EditableHeader from './EditableHeader';
|
||||
|
||||
export const HeaderTitle = ({
|
||||
editorRef,
|
||||
intl,
|
||||
isInitialized,
|
||||
setBlockTitle,
|
||||
typeHeader,
|
||||
}) => {
|
||||
if (!isInitialized) { return <FormattedMessage {...messages.loading} />; }
|
||||
if (!isInitialized) { return intl.formatMessage(messages.loading); }
|
||||
const {
|
||||
inputRef,
|
||||
isEditing,
|
||||
@@ -51,8 +52,7 @@ export const HeaderTitle = ({
|
||||
{localTitle}
|
||||
</div>
|
||||
<IconButton
|
||||
alt="Edit"
|
||||
aria-label="Edit Title"
|
||||
alt={intl.formatMessage(messages.editTitleLabel)}
|
||||
className="mr-2"
|
||||
iconAs={Icon}
|
||||
onClick={startEditing}
|
||||
@@ -70,6 +70,8 @@ HeaderTitle.propTypes = {
|
||||
PropTypes.func,
|
||||
PropTypes.shape({ current: PropTypes.any }),
|
||||
]),
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
isInitialized: PropTypes.bool.isRequired,
|
||||
setBlockTitle: PropTypes.func.isRequired,
|
||||
@@ -85,4 +87,4 @@ export const mapDispatchToProps = {
|
||||
setBlockTitle: actions.app.setBlockTitle,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(HeaderTitle);
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(HeaderTitle));
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import * as module from './HeaderTitle';
|
||||
import { formatMessage } from '../../../testUtils';
|
||||
import { actions, selectors } from '../../data/redux';
|
||||
import { localTitleHooks } from './hooks';
|
||||
import * as module from './HeaderTitle';
|
||||
|
||||
jest.mock('../../data/redux', () => ({
|
||||
actions: {
|
||||
@@ -25,6 +26,7 @@ jest.mock('./hooks', () => ({
|
||||
|
||||
describe('HeaderTitle', () => {
|
||||
const props = {
|
||||
intl: { formatMessage },
|
||||
editorRef: jest.fn().mockName('args.editorRef'),
|
||||
isInitialized: false,
|
||||
setBlockTitle: jest.fn().mockName('args.setBlockTitle'),
|
||||
|
||||
@@ -25,8 +25,7 @@ exports[`HeaderTitle snapshots initialized 1`] = `
|
||||
TeST LocALtitLE
|
||||
</div>
|
||||
<IconButton
|
||||
alt="Edit"
|
||||
aria-label="Edit Title"
|
||||
alt="Edit Title"
|
||||
className="mr-2"
|
||||
iconAs="Icon"
|
||||
onClick={[MockFunction localTitleHooks.startEditing]}
|
||||
@@ -36,10 +35,4 @@ exports[`HeaderTitle snapshots initialized 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`HeaderTitle snapshots not initialized 1`] = `
|
||||
<FormattedMessage
|
||||
defaultMessage="Loading..."
|
||||
description="Message displayed while loading content"
|
||||
id="authoring.texteditor.title.loading"
|
||||
/>
|
||||
`;
|
||||
exports[`HeaderTitle snapshots not initialized 1`] = `"Loading..."`;
|
||||
|
||||
@@ -11,8 +11,7 @@ exports[` 1`] = `
|
||||
</ModalDialog.Title>
|
||||
<ActionRow.Spacer />
|
||||
<IconButton
|
||||
alt="Close"
|
||||
aria-label="Cancel Changes and Return to Learning Context"
|
||||
alt="Cancel Changes and Return to Learning Context"
|
||||
className="mr-2"
|
||||
iconAs="Icon"
|
||||
src={[MockFunction icons.Close]}
|
||||
|
||||
@@ -9,23 +9,24 @@ import {
|
||||
ModalDialog,
|
||||
} from '@edx/paragon';
|
||||
import { Close } from '@edx/paragon/icons';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { selectors } from '../../data/redux';
|
||||
import * as appHooks from '../../hooks';
|
||||
|
||||
import HeaderTitle from './HeaderTitle';
|
||||
import messages from './messages';
|
||||
|
||||
export const EditorHeader = ({ returnUrl }) => (
|
||||
export const EditorHeader = ({ intl, returnUrl }) => (
|
||||
<div className="editor-header">
|
||||
<ModalDialog.Header>
|
||||
<ActionRow>
|
||||
<ModalDialog.Title><HeaderTitle /></ModalDialog.Title>
|
||||
<ActionRow.Spacer />
|
||||
<IconButton
|
||||
aria-label="Cancel Changes and Return to Learning Context"
|
||||
alt={intl.formatMessage(messages.cancelChangesLabel)}
|
||||
src={Close}
|
||||
iconAs={Icon}
|
||||
alt="Close"
|
||||
onClick={appHooks.navigateCallback(returnUrl)}
|
||||
variant="light"
|
||||
className="mr-2"
|
||||
@@ -35,10 +36,13 @@ export const EditorHeader = ({ returnUrl }) => (
|
||||
</div>
|
||||
);
|
||||
EditorHeader.propTypes = {
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
returnUrl: PropTypes.string.isRequired,
|
||||
};
|
||||
export const mapStateToProps = (state) => ({
|
||||
returnUrl: selectors.app.returnUrl(state),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(EditorHeader);
|
||||
export default injectIntl(connect(mapStateToProps)(EditorHeader));
|
||||
|
||||
@@ -2,9 +2,11 @@ import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { IconButton } from '@edx/paragon';
|
||||
import * as module from './index';
|
||||
|
||||
import { formatMessage } from '../../../testUtils';
|
||||
import { selectors } from '../../data/redux';
|
||||
import * as appHooks from '../../hooks';
|
||||
import * as module from './index';
|
||||
|
||||
jest.mock('.', () => ({
|
||||
__esModule: true, // Use it when dealing with esModules
|
||||
@@ -28,6 +30,7 @@ jest.mock('./HeaderTitle', () => 'HeaderTitle');
|
||||
|
||||
describe('Editor Header index', () => {
|
||||
const props = {
|
||||
intl: { formatMessage },
|
||||
returnUrl: 'TeST-ReTurNurL',
|
||||
};
|
||||
const { EditorHeader } = module;
|
||||
|
||||
19
src/editors/components/EditorHeader/messages.js
Normal file
19
src/editors/components/EditorHeader/messages.js
Normal file
@@ -0,0 +1,19 @@
|
||||
export const messages = {
|
||||
loading: {
|
||||
id: 'authoring.texteditor.title.loading',
|
||||
description: 'Message displayed while loading content',
|
||||
defaultMessage: 'Loading...',
|
||||
},
|
||||
cancelChangesLabel: {
|
||||
id: 'authoring.texteditor.header.cancelChangesLabel',
|
||||
defaultMessage: 'Cancel Changes and Return to Learning Context',
|
||||
description: 'Aria label title for icon button to return to learning context',
|
||||
},
|
||||
editTitleLabel: {
|
||||
id: 'authoring.texteditor.header.editTitleLabel',
|
||||
defaultMessage: 'Edit Title',
|
||||
description: 'Aria label title for icon button to edit the xblock title',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
@@ -1,19 +0,0 @@
|
||||
export const messages = {
|
||||
contentSaveFailed: {
|
||||
id: 'authoring.editorfooter.save.error',
|
||||
defaultMessage: 'Error: Content save failed. Try again later.',
|
||||
description: 'Error message displayed when content fails to save.',
|
||||
},
|
||||
addToCourse: {
|
||||
id: 'authoring.editorfooter.savebutton.label',
|
||||
defaultMessage: 'Save',
|
||||
description: 'Label for Save button',
|
||||
},
|
||||
loading: {
|
||||
id: 'authoring.texteditor.title.loading',
|
||||
description: 'Message displayed while loading content',
|
||||
defaultMessage: 'Loading...',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
@@ -59,8 +59,8 @@ export const getValidDimensions = ({
|
||||
// if closest valid iteration is current iteration, move one iteration in the change direction
|
||||
if (iter === (dimensions[keys.changed] / minInc[keys.changed])) { iter += direction; }
|
||||
|
||||
out[keys.changed] = iter * minInc[keys.changed];
|
||||
out[keys.other] = out[keys.changed] * (locked[keys.other] / locked[keys.changed]);
|
||||
out[keys.changed] = Math.round(iter * minInc[keys.changed]);
|
||||
out[keys.other] = Math.round(out[keys.changed] * (locked[keys.other] / locked[keys.changed]));
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user