feat: Add functionality to resize image based on percentage (#94)
* feat: add image resizing with percents
This commit is contained in:
@@ -50,7 +50,7 @@ export const AltTextControls = ({
|
||||
onChange={hooks.onCheckboxChange(setIsDecorative)}
|
||||
>
|
||||
<Form.Label>
|
||||
<FormattedMessage {...messages.decorativeCheckboxLabel} />
|
||||
<FormattedMessage {...messages.decorativeAltTextCheckboxLabel} />
|
||||
</Form.Label>
|
||||
</Form.Checkbox>
|
||||
</Form.Group>
|
||||
|
||||
@@ -22,10 +22,10 @@ describe('AltTextControls', () => {
|
||||
props.validation = { show: true };
|
||||
});
|
||||
describe('render', () => {
|
||||
test('snapshot: isDecorative=true errorProps.showSubmissionError=true', () => {
|
||||
test('snapshot: isDecorative=true errorProps.showAltTextSubmissionError=true', () => {
|
||||
expect(shallow(<AltTextControls {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: isDecorative=true errorProps.showSubmissionError=false', () => {
|
||||
test('snapshot: isDecorative=true errorProps.showAltTextSubmissionError=false', () => {
|
||||
props.validation.show = false;
|
||||
expect(shallow(<AltTextControls {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -42,18 +42,14 @@ export const DimensionControls = ({
|
||||
<div className="mt-4.5">
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
type="number"
|
||||
value={value.width}
|
||||
min={1}
|
||||
onChange={hooks.onInputChange(setWidth)}
|
||||
onBlur={updateDimensions}
|
||||
floatingLabel={intl.formatMessage(messages.widthFloatingLabel)}
|
||||
/>
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
type="number"
|
||||
value={value.height}
|
||||
min={1}
|
||||
onChange={hooks.onInputChange(setHeight)}
|
||||
onBlur={updateDimensions}
|
||||
floatingLabel={intl.formatMessage(messages.heightFloatingLabel)}
|
||||
@@ -80,8 +76,8 @@ DimensionControls.defaultProps = {
|
||||
};
|
||||
DimensionControls.propTypes = ({
|
||||
value: PropTypes.shape({
|
||||
height: PropTypes.number,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.string,
|
||||
width: PropTypes.string,
|
||||
}),
|
||||
setHeight: PropTypes.func.isRequired,
|
||||
setWidth: PropTypes.func.isRequired,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AltTextControls render snapshot: isDecorative=true errorProps.showSubmissionError=false 1`] = `
|
||||
exports[`AltTextControls render snapshot: isDecorative=true errorProps.showAltTextSubmissionError=false 1`] = `
|
||||
<Form.Group
|
||||
className="mt-4.5"
|
||||
>
|
||||
@@ -39,14 +39,14 @@ exports[`AltTextControls render snapshot: isDecorative=true errorProps.showSubmi
|
||||
<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"
|
||||
id="authoring.texteditor.imagesettingsmodal.decorativeAltTextCheckboxLabel"
|
||||
/>
|
||||
</Form.Label>
|
||||
</Form.Checkbox>
|
||||
</Form.Group>
|
||||
`;
|
||||
|
||||
exports[`AltTextControls render snapshot: isDecorative=true errorProps.showSubmissionError=true 1`] = `
|
||||
exports[`AltTextControls render snapshot: isDecorative=true errorProps.showAltTextSubmissionError=true 1`] = `
|
||||
<Form.Group
|
||||
className="mt-4.5"
|
||||
>
|
||||
@@ -94,7 +94,7 @@ exports[`AltTextControls render snapshot: isDecorative=true errorProps.showSubmi
|
||||
<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"
|
||||
id="authoring.texteditor.imagesettingsmodal.decorativeAltTextCheckboxLabel"
|
||||
/>
|
||||
</Form.Label>
|
||||
</Form.Checkbox>
|
||||
|
||||
@@ -19,27 +19,23 @@ exports[`DimensionControls render snapshot 1`] = `
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
floatingLabel="Width"
|
||||
min={1}
|
||||
onBlur={[MockFunction props.updateDimensions]}
|
||||
onChange={
|
||||
Object {
|
||||
"hooks.onInputChange": [MockFunction props.setWidth],
|
||||
}
|
||||
}
|
||||
type="number"
|
||||
value={20}
|
||||
/>
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
floatingLabel="Height"
|
||||
min={1}
|
||||
onBlur={[MockFunction props.updateDimensions]}
|
||||
onChange={
|
||||
Object {
|
||||
"hooks.onInputChange": [MockFunction props.setHeight],
|
||||
}
|
||||
}
|
||||
type="number"
|
||||
value={40}
|
||||
/>
|
||||
<IconButton
|
||||
@@ -70,27 +66,23 @@ exports[`DimensionControls render unlocked dimensions 1`] = `
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
floatingLabel="Width"
|
||||
min={1}
|
||||
onBlur={[MockFunction props.updateDimensions]}
|
||||
onChange={
|
||||
Object {
|
||||
"hooks.onInputChange": [MockFunction props.setWidth],
|
||||
}
|
||||
}
|
||||
type="number"
|
||||
value={20}
|
||||
/>
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
floatingLabel="Height"
|
||||
min={1}
|
||||
onBlur={[MockFunction props.updateDimensions]}
|
||||
onChange={
|
||||
Object {
|
||||
"hooks.onInputChange": [MockFunction props.setHeight],
|
||||
}
|
||||
}
|
||||
type="number"
|
||||
value={40}
|
||||
/>
|
||||
<IconButton
|
||||
|
||||
@@ -7,8 +7,8 @@ import * as module from './hooks';
|
||||
export const state = {
|
||||
altText: (val) => React.useState(val),
|
||||
dimensions: (val) => React.useState(val),
|
||||
showDismissibleError: (val) => React.useState(val),
|
||||
showSubmissionError: (val) => React.useState(val),
|
||||
showAltTextDismissibleError: (val) => React.useState(val),
|
||||
showAltTextSubmissionError: (val) => React.useState(val),
|
||||
isDecorative: (val) => React.useState(val),
|
||||
isLocked: (val) => React.useState(val),
|
||||
local: (val) => React.useState(val),
|
||||
@@ -135,6 +135,7 @@ export const dimensionLockHooks = () => {
|
||||
export const dimensionHooks = () => {
|
||||
const [dimensions, setDimensions] = module.state.dimensions(null);
|
||||
const [local, setLocal] = module.state.local(null);
|
||||
|
||||
const setAll = ({ height, width }) => {
|
||||
setDimensions({ height, width });
|
||||
setLocal({ height, width });
|
||||
@@ -157,8 +158,22 @@ export const dimensionHooks = () => {
|
||||
lock,
|
||||
unlock,
|
||||
value: local,
|
||||
setHeight: (height) => setLocal({ ...local, height: parseInt(height, 10) }),
|
||||
setWidth: (width) => setLocal({ ...local, width: parseInt(width, 10) }),
|
||||
setHeight: (height) => {
|
||||
if (height.match(/[0-9]+[%]{1}/)) {
|
||||
const heightPercent = height.match(/[0-9]+[%]{1}/)[0];
|
||||
setLocal({ ...local, height: heightPercent });
|
||||
} else if (height.match(/[0-9]/)) {
|
||||
setLocal({ ...local, height: parseInt(height, 10) });
|
||||
}
|
||||
},
|
||||
setWidth: (width) => {
|
||||
if (width.match(/[0-9]+[%]{1}/)) {
|
||||
const widthPercent = width.match(/[0-9]+[%]{1}/)[0];
|
||||
setLocal({ ...local, width: widthPercent });
|
||||
} else if (width.match(/[0-9]/)) {
|
||||
setLocal({ ...local, width: parseInt(width, 10) });
|
||||
}
|
||||
},
|
||||
updateDimensions: () => setAll(module.getValidDimensions({
|
||||
dimensions,
|
||||
local,
|
||||
@@ -189,13 +204,13 @@ export const dimensionHooks = () => {
|
||||
export const altTextHooks = (savedText) => {
|
||||
const [value, setValue] = module.state.altText(savedText || '');
|
||||
const [isDecorative, setIsDecorative] = module.state.isDecorative(false);
|
||||
const [showDismissibleError, setShowDismissibleError] = module.state.showDismissibleError(false);
|
||||
const [showSubmissionError, setShowSubmissionError] = module.state.showSubmissionError(false);
|
||||
const [showAltTextDismissibleError, setShowAltTextDismissibleError] = module.state.showAltTextDismissibleError(false);
|
||||
const [showAltTextSubmissionError, setShowAltTextSubmissionError] = module.state.showAltTextSubmissionError(false);
|
||||
|
||||
const validateAltText = (newVal, newDecorative) => {
|
||||
if (showSubmissionError) {
|
||||
if (showAltTextSubmissionError) {
|
||||
if (newVal || newDecorative) {
|
||||
setShowSubmissionError(false);
|
||||
setShowAltTextSubmissionError(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -212,14 +227,14 @@ export const altTextHooks = (savedText) => {
|
||||
validateAltText(null, decorative);
|
||||
},
|
||||
error: {
|
||||
show: showDismissibleError,
|
||||
set: () => setShowDismissibleError(true),
|
||||
dismiss: () => setShowDismissibleError(false),
|
||||
show: showAltTextDismissibleError,
|
||||
set: () => setShowAltTextDismissibleError(true),
|
||||
dismiss: () => setShowAltTextDismissibleError(false),
|
||||
},
|
||||
validation: {
|
||||
show: showSubmissionError,
|
||||
set: () => setShowSubmissionError(true),
|
||||
dismiss: () => setShowSubmissionError(false),
|
||||
show: showAltTextSubmissionError,
|
||||
set: () => setShowAltTextSubmissionError(true),
|
||||
dismiss: () => setShowAltTextSubmissionError(false),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -263,7 +278,7 @@ export const checkFormValidation = ({
|
||||
* onSave({ altText, dimensions, isDecorative, saveToEditor })
|
||||
* Handle saving the image context to the text editor
|
||||
* @param {string} altText - image alt text
|
||||
* @param {object} dimension - image dimensions ({ width, height })
|
||||
* @param {object} dimensions - image dimensions ({ width, height })
|
||||
* @param {bool} isDecorative - is the image decorative?
|
||||
* @param {func} saveToEditor - save method for submitting image settings.
|
||||
*/
|
||||
|
||||
@@ -34,8 +34,8 @@ describe('state values', () => {
|
||||
};
|
||||
test('provides altText state value', () => testStateMethod(state.keys.altText));
|
||||
test('provides dimensions state value', () => testStateMethod(state.keys.dimensions));
|
||||
test('provides showDismissibleError state value', () => testStateMethod(state.keys.showDismissibleError));
|
||||
test('provides showSubmissionError state value', () => testStateMethod(state.keys.showSubmissionError));
|
||||
test('provides showAltTextDismissibleError state value', () => testStateMethod(state.keys.showAltTextDismissibleError));
|
||||
test('provides showAltTextSubmissionError state value', () => testStateMethod(state.keys.showAltTextSubmissionError));
|
||||
test('provides isDecorative state value', () => testStateMethod(state.keys.isDecorative));
|
||||
test('provides isLocked state value', () => testStateMethod(state.keys.isLocked));
|
||||
test('provides local state value', () => testStateMethod(state.keys.local));
|
||||
@@ -244,8 +244,8 @@ describe('ImageSettingsModal hooks', () => {
|
||||
describe('altTextHooks', () => {
|
||||
const value = 'myVAL';
|
||||
const isDecorative = true;
|
||||
const showDismissibleError = 'dismiSSiBLE';
|
||||
const showSubmissionError = 'subMISsion';
|
||||
const showAltTextDismissibleError = 'dismiSSiBLE';
|
||||
const showAltTextSubmissionError = 'subMISsion';
|
||||
beforeEach(() => {
|
||||
state.mock();
|
||||
hook = hooks.altTextHooks();
|
||||
@@ -275,33 +275,33 @@ describe('ImageSettingsModal hooks', () => {
|
||||
describe('error', () => {
|
||||
test('show is initialized to false and returns properly', () => {
|
||||
expect(hook.error.show).toEqual(false);
|
||||
state.mockVal(state.keys.showDismissibleError, showDismissibleError);
|
||||
state.mockVal(state.keys.showAltTextDismissibleError, showAltTextDismissibleError);
|
||||
hook = hooks.altTextHooks();
|
||||
expect(hook.error.show).toEqual(showDismissibleError);
|
||||
expect(hook.error.show).toEqual(showAltTextDismissibleError);
|
||||
});
|
||||
test('set sets showDismissibleError to true', () => {
|
||||
test('set sets showAltTextDismissibleError to true', () => {
|
||||
hook.error.set();
|
||||
expect(state.setState.showDismissibleError).toHaveBeenCalledWith(true);
|
||||
expect(state.setState.showAltTextDismissibleError).toHaveBeenCalledWith(true);
|
||||
});
|
||||
test('dismiss sets showDismissibleError to false', () => {
|
||||
test('dismiss sets showAltTextDismissibleError to false', () => {
|
||||
hook.error.dismiss();
|
||||
expect(state.setState.showDismissibleError).toHaveBeenCalledWith(false);
|
||||
expect(state.setState.showAltTextDismissibleError).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
describe('validation', () => {
|
||||
test('show is initialized to false and returns properly', () => {
|
||||
expect(hook.validation.show).toEqual(false);
|
||||
state.mockVal(state.keys.showSubmissionError, showSubmissionError);
|
||||
state.mockVal(state.keys.showAltTextSubmissionError, showAltTextSubmissionError);
|
||||
hook = hooks.altTextHooks();
|
||||
expect(hook.validation.show).toEqual(showSubmissionError);
|
||||
expect(hook.validation.show).toEqual(showAltTextSubmissionError);
|
||||
});
|
||||
test('set sets showSubmissionError to true', () => {
|
||||
test('set sets showAltTextSubmissionError to true', () => {
|
||||
hook.validation.set();
|
||||
expect(state.setState.showSubmissionError).toHaveBeenCalledWith(true);
|
||||
expect(state.setState.showAltTextSubmissionError).toHaveBeenCalledWith(true);
|
||||
});
|
||||
test('dismiss sets showSubmissionError to false', () => {
|
||||
test('dismiss sets showAltTextSubmissionError to false', () => {
|
||||
hook.validation.dismiss();
|
||||
expect(state.setState.showSubmissionError).toHaveBeenCalledWith(false);
|
||||
expect(state.setState.showAltTextSubmissionError).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -376,7 +376,7 @@ describe('ImageSettingsModal hooks', () => {
|
||||
isDecorative: props.isDecorative,
|
||||
});
|
||||
});
|
||||
it('calls dismissError and sets showSubmissionError to false when checkFormValidation is true', () => {
|
||||
it('calls dismissError and sets showAltTextSubmissionError to false when checkFormValidation is true', () => {
|
||||
jest.spyOn(hooks, hookKeys.checkFormValidation).mockReturnValueOnce(true);
|
||||
hooks.onSaveClick({ ...props })();
|
||||
expect(props.altText.error.dismiss).toHaveBeenCalled();
|
||||
|
||||
@@ -42,6 +42,11 @@ export const messages = {
|
||||
defaultMessage: 'lock dimensions',
|
||||
description: 'Label for button when locking dimensions.',
|
||||
},
|
||||
decorativeDimensionCheckboxLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.decorativeDimensionCheckboxLabel',
|
||||
defaultMessage: "Use percentages for the image's width and height",
|
||||
description: 'Checkbox label for whether or not an image uses percentages for width and height.',
|
||||
},
|
||||
|
||||
// AltTextControls
|
||||
accessibilityLabel: {
|
||||
@@ -54,8 +59,8 @@ export const messages = {
|
||||
defaultMessage: 'Alt Text',
|
||||
description: 'Floating label title for alt text input.',
|
||||
},
|
||||
decorativeCheckboxLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.decorativeCheckboxLabel',
|
||||
decorativeAltTextCheckboxLabel: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.decorativeAltTextCheckboxLabel',
|
||||
defaultMessage: 'This image is decorative (no alt text required).',
|
||||
description: 'Checkbox label for whether or not an image is decorative.',
|
||||
},
|
||||
@@ -71,6 +76,16 @@ export const messages = {
|
||||
defaultMessage: 'Enter alt text',
|
||||
description: 'Message feedback for user below the alt text field.',
|
||||
},
|
||||
dimensionError: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.error.dimensionError',
|
||||
defaultMessage: 'Dimension values must be less than or equal to 100.',
|
||||
description: 'Message presented to user when user attempts to save unaccepted dimension configuration.',
|
||||
},
|
||||
dimensionLocalFeedback: {
|
||||
id: 'authoring.texteditor.imagesettingsmodal.error.dimensionFeedback',
|
||||
defaultMessage: 'Enter a value less than or equal to 100.',
|
||||
description: 'Message feedback for user below the dimension fields.',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
|
||||
Reference in New Issue
Block a user