feat: Add functionality to resize image based on percentage (#94)

* feat: add image resizing with percents
This commit is contained in:
Kristin Aoki
2022-08-05 12:13:57 -04:00
committed by GitHub
parent 3821378dc1
commit 9b23731acc
8 changed files with 73 additions and 55 deletions

View File

@@ -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>

View File

@@ -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();
});

View File

@@ -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,

View File

@@ -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>

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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();

View File

@@ -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;