Bug 1421663 - Allow changing of custom viewport size in RDM with arrow keys. r=jryans
MozReview-Commit-ID: AQwqkt9EPn3
--- a/devtools/client/responsive.html/components/ViewportDimension.js
+++ b/devtools/client/responsive.html/components/ViewportDimension.js
@@ -2,20 +2,49 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const { isKeyIn } = require("../utils/key");
const Constants = require("../constants");
const Types = require("../types");
+/**
+ * Get the increment/decrement step to use for the provided key event.
+ */
+function getIncrement(event) {
+ const defaultIncrement = 1;
+ const largeIncrement = 100;
+ const mediumIncrement = 10;
+
+ let increment = 0;
+ let key = event.keyCode;
+
+ if (isKeyIn(key, "UP", "PAGE_UP")) {
+ increment = 1 * defaultIncrement;
+ } else if (isKeyIn(key, "DOWN", "PAGE_DOWN")) {
+ increment = -1 * defaultIncrement;
+ }
+
+ if (event.shiftKey) {
+ if (isKeyIn(key, "PAGE_UP", "PAGE_DOWN")) {
+ increment *= largeIncrement;
+ } else {
+ increment *= mediumIncrement;
+ }
+ }
+
+ return increment;
+}
+
class ViewportDimension extends Component {
static get propTypes() {
return {
viewport: PropTypes.shape(Types.viewport).isRequired,
onChangeSize: PropTypes.func.isRequired,
onRemoveDeviceAssociation: PropTypes.func.isRequired,
};
}
@@ -30,16 +59,17 @@ class ViewportDimension extends Componen
isEditing: false,
isInvalid: false,
};
this.validateInput = this.validateInput.bind(this);
this.onInputBlur = this.onInputBlur.bind(this);
this.onInputChange = this.onInputChange.bind(this);
this.onInputFocus = this.onInputFocus.bind(this);
+ this.onInputKeyDown = this.onInputKeyDown.bind(this);
this.onInputKeyUp = this.onInputKeyUp.bind(this);
this.onInputSubmit = this.onInputSubmit.bind(this);
}
componentWillReceiveProps(nextProps) {
let { width, height } = nextProps.viewport;
this.setState({
@@ -70,38 +100,48 @@ class ViewportDimension extends Componen
}
this.setState({
isEditing: false,
inInvalid: false,
});
}
- onInputChange({ target }) {
+ onInputChange({ target }, callback) {
if (target.value.length > 4) {
return;
}
if (this.refs.widthInput == target) {
- this.setState({ width: target.value });
+ this.setState({ width: target.value }, callback);
this.validateInput(target.value);
}
if (this.refs.heightInput == target) {
- this.setState({ height: target.value });
+ this.setState({ height: target.value }, callback);
this.validateInput(target.value);
}
}
onInputFocus() {
this.setState({
isEditing: true,
});
}
+ onInputKeyDown(event) {
+ let { target } = event;
+ let increment = getIncrement(event);
+ if (!increment) {
+ return;
+ }
+ target.value = parseInt(target.value, 10) + increment;
+ this.onInputChange(event, this.onInputSubmit);
+ }
+
onInputKeyUp({ target, keyCode }) {
// On Enter, submit the input
if (keyCode == 13) {
this.onInputSubmit();
}
// On Esc, blur the target
if (keyCode == 27) {
@@ -155,29 +195,31 @@ class ViewportDimension extends Componen
dom.input({
ref: "widthInput",
className: inputClass,
size: 4,
value: this.state.width,
onBlur: this.onInputBlur,
onChange: this.onInputChange,
onFocus: this.onInputFocus,
+ onKeyDown: this.onInputKeyDown,
onKeyUp: this.onInputKeyUp,
}),
dom.span({
className: "viewport-dimension-separator",
}, "×"),
dom.input({
ref: "heightInput",
className: inputClass,
size: 4,
value: this.state.height,
onBlur: this.onInputBlur,
onChange: this.onInputChange,
onFocus: this.onInputFocus,
+ onKeyDown: this.onInputKeyDown,
onKeyUp: this.onInputKeyUp,
})
)
);
}
}
module.exports = ViewportDimension;
--- a/devtools/client/responsive.html/test/browser/browser_device_width.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_width.js
@@ -13,28 +13,68 @@ addRDMTask(TEST_URL, function* ({ ui, ma
yield doInitialChecks(ui);
info("Changing the RDM size");
yield setViewportSize(ui, manager, 90, 500);
info("Checking for screen props");
yield checkScreenProps(ui);
+ info("Changing the RDM size using input keys");
+ yield setViewportSizeWithInputKeys(ui);
+
info("Setting docShell.deviceSizeIsPageSize to false");
yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
docShell.deviceSizeIsPageSize = false;
});
info("Checking for screen props once again.");
yield checkScreenProps2(ui);
});
+function* setViewportSizeWithInputKeys(ui) {
+ let width = 320, height = 500;
+ let resized = waitForViewportResizeTo(ui, width, height);
+ ui.setViewportSize({ width, height });
+ yield resized;
+
+ let dimensions = ui.toolWindow.document.querySelectorAll(".viewport-dimension-input");
+
+ // Increase width value to 420 by using the Up arrow key
+ resized = waitForViewportResizeTo(ui, 420, height);
+ dimensions[0].focus();
+ for (let i = 1; i <= 100; i++) {
+ EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
+ }
+ yield resized;
+
+ // Resetting width value back to 320 using `Shift + Down` arrow
+ resized = waitForViewportResizeTo(ui, width, height);
+ dimensions[0].focus();
+ for (let i = 1; i <= 10; i++) {
+ EventUtils.synthesizeKey("KEY_ArrowDown", { shiftKey: true, code: "ArrowDown" });
+ }
+ yield resized;
+
+ // Increase height value to 600 by using `PageUp + Shift` key
+ resized = waitForViewportResizeTo(ui, width, 600);
+ dimensions[1].focus();
+ EventUtils.synthesizeKey("VK_PAGE_UP", { shiftKey: true });
+ yield resized;
+
+ // Resetting height value back to 500 by using `PageDown + Shift` key
+ resized = waitForViewportResizeTo(ui, width, height);
+ dimensions[1].focus();
+ EventUtils.synthesizeKey("VK_PAGE_DOWN", { shiftKey: true });
+ yield resized;
+}
+
function* doInitialChecks(ui) {
let { innerWidth, matchesMedia } = yield grabContentInfo(ui);
is(innerWidth, 110, "initial width should be 110px");
ok(!matchesMedia, "media query shouldn't match.");
}
function* checkScreenProps(ui) {
let { matchesMedia, screen } = yield grabContentInfo(ui);
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/utils/key.js
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { KeyCodes } = require("devtools/client/shared/keycodes");
+
+/**
+ * Helper to check if the provided key matches one of the expected keys.
+ * Keys will be prefixed with DOM_VK_ and should match a key in KeyCodes.
+ *
+ * @param {String} key
+ * the key to check (can be a keyCode).
+ * @param {...String} keys
+ * list of possible keys allowed.
+ * @return {Boolean} true if the key matches one of the keys.
+ */
+function isKeyIn(key, ...keys) {
+ return keys.some(expectedKey => {
+ return key === KeyCodes["DOM_VK_" + expectedKey];
+ });
+}
+
+exports.isKeyIn = isKeyIn;
--- a/devtools/client/responsive.html/utils/moz.build
+++ b/devtools/client/responsive.html/utils/moz.build
@@ -2,12 +2,13 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'css.js',
'e10s.js',
+ 'key.js',
'l10n.js',
'message.js',
'window.js',
)