23564
package-lock.json
generated
23564
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,11 +18,11 @@ import 'tinymce/plugins/autoresize';
|
||||
import 'tinymce/plugins/image';
|
||||
import 'tinymce/plugins/imagetools';
|
||||
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Spinner,
|
||||
Toast,
|
||||
} from '@edx/paragon';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { actions, selectors } from '../../data/redux';
|
||||
import { RequestKeys } from '../../data/constants/requests';
|
||||
@@ -43,6 +43,8 @@ export const TextEditor = ({
|
||||
blockFailed,
|
||||
blockFinished,
|
||||
initializeEditor,
|
||||
// inject
|
||||
intl,
|
||||
}) => {
|
||||
const { isOpen, openModal, closeModal } = modalToggle();
|
||||
|
||||
@@ -66,7 +68,7 @@ export const TextEditor = ({
|
||||
{(!blockFinished)
|
||||
? (
|
||||
<div className="text-center p-6">
|
||||
<Spinner animation="border" className="m-3" screenreadertext="loading" />
|
||||
<Spinner animation="border" className="m-3" screenreadertext={intl.formatMessage(messages.spinnerScreenReaderText)} />
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
@@ -101,6 +103,8 @@ TextEditor.propTypes = {
|
||||
blockFailed: PropTypes.bool.isRequired,
|
||||
blockFinished: PropTypes.bool.isRequired,
|
||||
initializeEditor: PropTypes.func.isRequired,
|
||||
// inject
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
@@ -113,4 +117,4 @@ export const mapDispatchToProps = {
|
||||
initializeEditor: actions.app.initializeEditor,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TextEditor);
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TextEditor));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { formatMessage } from '../../../testUtils';
|
||||
import { TextEditor, mapStateToProps, mapDispatchToProps } from './TextEditor';
|
||||
import { actions, selectors } from '../../data/redux';
|
||||
import { RequestKeys } from '../../data/constants/requests';
|
||||
@@ -68,6 +70,8 @@ describe('TextEditor', () => {
|
||||
blockFailed: false,
|
||||
blockFinished: true,
|
||||
initializeEditor: jest.fn().mockName('args.intializeEditor'),
|
||||
// inject
|
||||
intl: { formatMessage },
|
||||
};
|
||||
describe('snapshots', () => {
|
||||
modalToggle.mockReturnValue({
|
||||
|
||||
@@ -5,6 +5,9 @@ import {
|
||||
ActionRow,
|
||||
ModalDialog,
|
||||
} from '@edx/paragon';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
export const BaseModal = ({
|
||||
isOpen,
|
||||
@@ -15,7 +18,6 @@ export const BaseModal = ({
|
||||
footerAction,
|
||||
}) => (
|
||||
<ModalDialog
|
||||
title="My dialog"
|
||||
isOpen={isOpen}
|
||||
onClose={close}
|
||||
size="lg"
|
||||
@@ -37,7 +39,7 @@ export const BaseModal = ({
|
||||
{footerAction}
|
||||
<ActionRow.Spacer />
|
||||
<ModalDialog.CloseButton variant="tertiary" onClick={close}>
|
||||
Cancel
|
||||
<FormattedMessage {...messages.cancelButtonLabel} />
|
||||
</ModalDialog.CloseButton>
|
||||
{confirmAction}
|
||||
</ActionRow>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Form } from '@edx/paragon';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import * as hooks from './hooks';
|
||||
import messages from './messages';
|
||||
|
||||
/**
|
||||
* Wrapper for alt-text input and isDecorative checkbox control
|
||||
@@ -16,16 +19,20 @@ export const AltTextControls = ({
|
||||
setIsDecorative,
|
||||
setValue,
|
||||
value,
|
||||
// inject
|
||||
intl,
|
||||
}) => (
|
||||
<Form.Group className="mt-4.5">
|
||||
<Form.Label as="h4">Accessibility</Form.Label>
|
||||
<Form.Label as="h4">
|
||||
<FormattedMessage {...messages.accessibilityLabel} />
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
className="mt-4.5"
|
||||
type="input"
|
||||
value={value}
|
||||
disabled={isDecorative}
|
||||
onChange={hooks.onInputChange(setValue)}
|
||||
floatingLabel="Alt Text"
|
||||
floatingLabel={intl.formatMessage(messages.altTextFloatingLabel)}
|
||||
/>
|
||||
<Form.Checkbox
|
||||
className="mt-4.5 decorative-control-label"
|
||||
@@ -33,7 +40,7 @@ export const AltTextControls = ({
|
||||
onChange={hooks.onCheckboxChange(setIsDecorative)}
|
||||
>
|
||||
<Form.Label>
|
||||
This image is decorative (no alt text required).
|
||||
<FormattedMessage {...messages.decorativeCheckboxLabel} />
|
||||
</Form.Label>
|
||||
</Form.Checkbox>
|
||||
</Form.Group>
|
||||
@@ -43,6 +50,8 @@ AltTextControls.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
setValue: PropTypes.func.isRequired,
|
||||
setIsDecorative: PropTypes.func.isRequired,
|
||||
// inject
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default AltTextControls;
|
||||
export default injectIntl(AltTextControls);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import AltTextControls from './AltTextControls';
|
||||
|
||||
import { formatMessage } from '../../../../../testUtils';
|
||||
import { AltTextControls } from './AltTextControls';
|
||||
|
||||
jest.mock('./hooks', () => ({
|
||||
onInputChange: (handler) => ({ 'hooks.onInputChange': handler }),
|
||||
@@ -11,6 +13,8 @@ describe('AltTextControls', () => {
|
||||
const props = {
|
||||
isDecorative: true,
|
||||
value: 'props.value',
|
||||
// inject
|
||||
intl: { formatMessage },
|
||||
};
|
||||
beforeEach(() => {
|
||||
props.setValue = jest.fn().mockName('props.setValue');
|
||||
|
||||
@@ -9,8 +9,10 @@ import {
|
||||
Locked,
|
||||
Unlocked,
|
||||
} from '@edx/paragon/icons';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import hooks from './hooks';
|
||||
import messages from './messages';
|
||||
|
||||
/**
|
||||
* Wrapper for image dimension inputs and the lock checkbox.
|
||||
@@ -30,9 +32,13 @@ export const DimensionControls = ({
|
||||
unlock,
|
||||
updateDimensions,
|
||||
value,
|
||||
// inject
|
||||
intl,
|
||||
}) => ((value !== null) && (
|
||||
<Form.Group>
|
||||
<Form.Label as="h4">Image Dimensions</Form.Label>
|
||||
<Form.Label as="h4">
|
||||
<FormattedMessage {...messages.imageDimensionsLabel} />
|
||||
</Form.Label>
|
||||
<div className="mt-4.5">
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
@@ -41,7 +47,7 @@ export const DimensionControls = ({
|
||||
min={1}
|
||||
onChange={hooks.onInputChange(setWidth)}
|
||||
onBlur={updateDimensions}
|
||||
floatingLabel="Width"
|
||||
floatingLabel={intl.formatMessage(messages.widthFloatingLabel)}
|
||||
/>
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
@@ -50,11 +56,15 @@ export const DimensionControls = ({
|
||||
min={1}
|
||||
onChange={hooks.onInputChange(setHeight)}
|
||||
onBlur={updateDimensions}
|
||||
floatingLabel="Height"
|
||||
floatingLabel={intl.formatMessage(messages.heightFloatingLabel)}
|
||||
/>
|
||||
<IconButton
|
||||
className="d-inline-block"
|
||||
alt={isLocked ? 'unlock dimensions' : 'lock dimensions'}
|
||||
alt={
|
||||
isLocked
|
||||
? intl.formatMessage(messages.unlockDimensionsLabel)
|
||||
: intl.formatMessage(messages.lockDimensionsLabel)
|
||||
}
|
||||
iconAs={Icon}
|
||||
src={isLocked ? Locked : Unlocked}
|
||||
onClick={isLocked ? unlock : lock}
|
||||
@@ -79,6 +89,8 @@ DimensionControls.propTypes = ({
|
||||
lock: PropTypes.func.isRequired,
|
||||
unlock: PropTypes.func.isRequired,
|
||||
updateDimensions: PropTypes.func.isRequired,
|
||||
// inject
|
||||
intl: intlShape.isRequired,
|
||||
});
|
||||
|
||||
export default DimensionControls;
|
||||
export default injectIntl(DimensionControls);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import DimensionControls from './DimensionControls';
|
||||
|
||||
import { formatMessage } from '../../../../../testUtils';
|
||||
import { DimensionControls } from './DimensionControls';
|
||||
|
||||
jest.mock('./hooks', () => ({
|
||||
onInputChange: (handler) => ({ 'hooks.onInputChange': handler }),
|
||||
@@ -12,6 +14,8 @@ describe('DimensionControls', () => {
|
||||
locked: { 'props.locked': 'lockedValue' },
|
||||
isLocked: true,
|
||||
value: { width: 20, height: 40 },
|
||||
// inject
|
||||
intl: { formatMessage },
|
||||
};
|
||||
beforeEach(() => {
|
||||
props.setWidth = jest.fn().mockName('props.setWidth');
|
||||
|
||||
@@ -7,7 +7,11 @@ exports[`AltTextControls render snapshot: isDecorative=true 1`] = `
|
||||
<Form.Label
|
||||
as="h4"
|
||||
>
|
||||
Accessibility
|
||||
<FormattedMessage
|
||||
defaultMessage="Accessibility"
|
||||
description="Label title for accessibility section."
|
||||
id="authoring.texteditor.imagesettingsmodal.accessibilityLabel"
|
||||
/>
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
className="mt-4.5"
|
||||
@@ -31,7 +35,11 @@ exports[`AltTextControls render snapshot: isDecorative=true 1`] = `
|
||||
}
|
||||
>
|
||||
<Form.Label>
|
||||
This image is decorative (no alt text required).
|
||||
<FormattedMessage
|
||||
defaultMessage="This image is decorative (no alt text required)."
|
||||
description="Checkbox label for whether or not an image is decorative."
|
||||
id="authoring.texteditor.imagesettingsmodal.decorativeCheckboxLabel"
|
||||
/>
|
||||
</Form.Label>
|
||||
</Form.Checkbox>
|
||||
</Form.Group>
|
||||
|
||||
@@ -7,7 +7,11 @@ exports[`DimensionControls render snapshot 1`] = `
|
||||
<Form.Label
|
||||
as="h4"
|
||||
>
|
||||
Image Dimensions
|
||||
<FormattedMessage
|
||||
defaultMessage="Image Dimensions"
|
||||
description="Label title for the image dimensions section."
|
||||
id="authoring.texteditor.imagesettingsmodal.imageDimensionsLabel"
|
||||
/>
|
||||
</Form.Label>
|
||||
<div
|
||||
className="mt-4.5"
|
||||
@@ -54,7 +58,11 @@ exports[`DimensionControls render unlocked dimensions 1`] = `
|
||||
<Form.Label
|
||||
as="h4"
|
||||
>
|
||||
Image Dimensions
|
||||
<FormattedMessage
|
||||
defaultMessage="Image Dimensions"
|
||||
description="Label title for the image dimensions section."
|
||||
id="authoring.texteditor.imagesettingsmodal.imageDimensionsLabel"
|
||||
/>
|
||||
</Form.Label>
|
||||
<div
|
||||
className="mt-4.5"
|
||||
|
||||
@@ -28,7 +28,11 @@ exports[`ImageSettingsModal render snapshot 1`] = `
|
||||
}
|
||||
variant="primary"
|
||||
>
|
||||
Save
|
||||
<FormattedMessage
|
||||
defaultMessage="Save"
|
||||
description="Label for save button."
|
||||
id="authoring.texteditor.imagesettingsmodal.saveButtonLabel"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
footerAction={null}
|
||||
@@ -40,7 +44,11 @@ exports[`ImageSettingsModal render snapshot 1`] = `
|
||||
size="inline"
|
||||
variant="link"
|
||||
>
|
||||
Replace image
|
||||
<FormattedMessage
|
||||
defaultMessage="Replace image"
|
||||
description="Label for replace image button."
|
||||
id="authoring.texteditor.imagesettingsmodal.replaceImageButtonLabel"
|
||||
/>
|
||||
</Button>
|
||||
<br />
|
||||
<div
|
||||
|
||||
@@ -2,12 +2,14 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Image } from '@edx/paragon';
|
||||
import { ArrowBackIos } from '@edx/paragon/icons';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import BaseModal from '../BaseModal';
|
||||
|
||||
import AltTextControls from './AltTextControls';
|
||||
import DimensionControls from './DimensionControls';
|
||||
import hooks from './hooks';
|
||||
import messages from './messages';
|
||||
import './index.scss';
|
||||
|
||||
/**
|
||||
@@ -26,6 +28,8 @@ export const ImageSettingsModal = ({
|
||||
selection,
|
||||
saveToEditor,
|
||||
returnToSelection,
|
||||
// inject
|
||||
intl,
|
||||
}) => {
|
||||
const dimensions = hooks.dimensions();
|
||||
const altText = hooks.altText();
|
||||
@@ -37,7 +41,7 @@ export const ImageSettingsModal = ({
|
||||
});
|
||||
return (
|
||||
<BaseModal
|
||||
title="Image Settings"
|
||||
title={intl.formatMessage(messages.titleLabel)}
|
||||
close={close}
|
||||
isOpen={isOpen}
|
||||
confirmAction={(
|
||||
@@ -46,7 +50,7 @@ export const ImageSettingsModal = ({
|
||||
onClick={onSaveClick}
|
||||
disabled={hooks.isSaveDisabled(altText)}
|
||||
>
|
||||
Save
|
||||
<FormattedMessage {...messages.saveButtonLabel} />
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
@@ -56,7 +60,7 @@ export const ImageSettingsModal = ({
|
||||
size="inline"
|
||||
iconBefore={ArrowBackIos}
|
||||
>
|
||||
Replace image
|
||||
<FormattedMessage {...messages.replaceImageButtonLabel} />
|
||||
</Button>
|
||||
<br />
|
||||
<div className="d-flex flex-row m-2 img-settings-form-container">
|
||||
@@ -87,5 +91,7 @@ ImageSettingsModal.propTypes = {
|
||||
}).isRequired,
|
||||
saveToEditor: PropTypes.func.isRequired,
|
||||
returnToSelection: PropTypes.func.isRequired,
|
||||
// inject
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
export default ImageSettingsModal;
|
||||
export default injectIntl(ImageSettingsModal);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import ImageSettingsModal from '.';
|
||||
|
||||
import { formatMessage } from '../../../../../testUtils';
|
||||
import { ImageSettingsModal } from '.';
|
||||
|
||||
jest.mock('./AltTextControls', () => 'AltTextControls');
|
||||
jest.mock('./DimensionControls', () => 'DimensionControls');
|
||||
@@ -24,6 +26,8 @@ describe('ImageSettingsModal', () => {
|
||||
const props = {
|
||||
isOpen: false,
|
||||
selection: { selected: 'image data' },
|
||||
// inject
|
||||
intl: { formatMessage },
|
||||
};
|
||||
beforeEach(() => {
|
||||
props.close = jest.fn().mockName('props.close');
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
export const messages = {
|
||||
// index
|
||||
titleLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.titleLabel',
|
||||
defaultMessage: 'Image Settings',
|
||||
description: 'Label title for image settings modal.',
|
||||
},
|
||||
saveButtonLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.saveButtonLabel',
|
||||
defaultMessage: 'Save',
|
||||
description: 'Label for save button.',
|
||||
},
|
||||
replaceImageButtonLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.replaceImageButtonLabel',
|
||||
defaultMessage: 'Replace image',
|
||||
description: 'Label for replace image button.',
|
||||
},
|
||||
|
||||
// DimensionControls
|
||||
imageDimensionsLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.imageDimensionsLabel',
|
||||
defaultMessage: 'Image Dimensions',
|
||||
description: 'Label title for the image dimensions section.',
|
||||
},
|
||||
widthFloatingLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.widthFloatingLabel',
|
||||
defaultMessage: 'Width',
|
||||
description: 'Floating label for width input.',
|
||||
},
|
||||
heightFloatingLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.heightFloatingLabel',
|
||||
defaultMessage: 'Height',
|
||||
description: 'Floating label for height input.',
|
||||
},
|
||||
unlockDimensionsLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.unlockDimensionsLabel',
|
||||
defaultMessage: 'unlock dimensions',
|
||||
description: 'Label for button when unlocking dimensions.',
|
||||
},
|
||||
lockDimensionsLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.lockDimensionsLabel',
|
||||
defaultMessage: 'lock dimensions',
|
||||
description: 'Label for button when locking dimensions.',
|
||||
},
|
||||
|
||||
// AltTextControls
|
||||
accessibilityLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.accessibilityLabel',
|
||||
defaultMessage: 'Accessibility',
|
||||
description: 'Label title for accessibility section.',
|
||||
},
|
||||
altTextFloatingLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.altTextFloatingLabel',
|
||||
defaultMessage: 'Alt Text',
|
||||
description: 'Floating label title for alt text input.',
|
||||
},
|
||||
decorativeCheckboxLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.decorativeCheckboxLabel',
|
||||
defaultMessage: 'This image is decorative (no alt text required).',
|
||||
description: 'Checkbox label for whether or not an image is decorative.',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
@@ -8,7 +8,6 @@ exports[`BaseModal ImageUploadModal template component snapshot 1`] = `
|
||||
isOpen={true}
|
||||
onClose={[MockFunction props.close]}
|
||||
size="lg"
|
||||
title="My dialog"
|
||||
variant="default"
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
@@ -27,7 +26,11 @@ exports[`BaseModal ImageUploadModal template component snapshot 1`] = `
|
||||
onClick={[MockFunction props.close]}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
description="Label for cancel button."
|
||||
id="authoring.texteditor.baseModal.cancelButtonLabel"
|
||||
/>
|
||||
</ModalDialog.CloseButton>
|
||||
props.confirmAction node
|
||||
</ActionRow>
|
||||
|
||||
9
src/editors/containers/TextEditor/components/messages.js
Normal file
9
src/editors/containers/TextEditor/components/messages.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export const messages = {
|
||||
cancelButtonLabel: {
|
||||
id: 'authoring.texteditor.baseModal.cancelButtonLabel',
|
||||
defaultMessage: 'Cancel',
|
||||
description: 'Label for cancel button.',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
@@ -4,6 +4,11 @@ export const messages = {
|
||||
defaultMessage: 'Error: Could Not Load Text Content',
|
||||
description: 'Error Message Dispayed When HTML content fails to Load',
|
||||
},
|
||||
spinnerScreenReaderText: {
|
||||
id: 'authoring.texteditor.spinnerScreenReaderText',
|
||||
defaultMessage: 'loading',
|
||||
description: 'Loading message for spinner screenreader text.',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
|
||||
Reference in New Issue
Block a user