зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 8c7e3a9b7b6b (bug 1613885) for dt failures in browser_net_content-type.js on a CLOSED TREE
--HG-- rename : devtools/client/netmonitor/src/components/previews/HtmlPreview.js => devtools/client/netmonitor/src/components/HtmlPreview.js rename : devtools/client/netmonitor/src/components/request-details/ResponsePanel.js => devtools/client/netmonitor/src/components/ResponsePanel.js
This commit is contained in:
Родитель
ad219d5402
Коммит
51881bc9e7
|
@ -12,6 +12,9 @@
|
|||
|
||||
.network-monitor .panel-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.network-monitor .panel-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
|
@ -80,7 +83,6 @@
|
|||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Text inputs in tab panels */
|
||||
|
||||
.network-monitor .textbox-input {
|
||||
|
@ -180,7 +182,7 @@
|
|||
/* If there is a source editor shows up in the last row of TreeView,
|
||||
* it should occupy the available vertical space.
|
||||
*/
|
||||
.network-monitor .accordion .editor-row-container,
|
||||
.network-monitor .tree-container .treeTable .editor-row-container,
|
||||
.network-monitor .tree-container .treeTable tr:last-child td[colspan="2"] {
|
||||
display: block;
|
||||
height: 100%;
|
||||
|
@ -188,7 +190,7 @@
|
|||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.network-monitor .accordion .responseTextContainer {
|
||||
.network-monitor .tree-container .treeTable .editor-row-container .responseTextContainer {
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -377,33 +379,6 @@
|
|||
}
|
||||
|
||||
/* Response tabpanel */
|
||||
/* Supports the single expand mode used for the accordion in the response panel */
|
||||
.network-monitor #response-panel .panel-container {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.network-monitor #response-panel .accordion {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.network-monitor #response-panel .accordion-item .accordion-content {
|
||||
position: absolute;
|
||||
top: calc(var(--theme-toolbar-height) + 1px);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.network-monitor #response-panel .accordion-item {
|
||||
position: relative;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.network-monitor #response-panel .accordion-item.accordion-open {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.network-monitor .response-image-box {
|
||||
display: flex;
|
||||
|
|
|
@ -45,7 +45,7 @@ loader.lazyGetter(this, "SourceEditor", function() {
|
|||
});
|
||||
loader.lazyGetter(this, "HTMLPreview", function() {
|
||||
return createFactory(
|
||||
require("devtools/client/netmonitor/src/components/previews/HtmlPreview")
|
||||
require("devtools/client/netmonitor/src/components/HtmlPreview")
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -3,53 +3,43 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
Component,
|
||||
createFactory,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const {
|
||||
connect,
|
||||
} = require("devtools/client/shared/redux/visibility-handler-connect");
|
||||
const Services = require("Services");
|
||||
const { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
|
||||
const {
|
||||
decodeUnicodeBase64,
|
||||
fetchNetworkUpdatePacket,
|
||||
formDataURI,
|
||||
getUrlBaseName,
|
||||
isJSON,
|
||||
} = require("devtools/client/netmonitor/src/utils/request-utils");
|
||||
const {
|
||||
Filters,
|
||||
} = require("devtools/client/netmonitor/src/utils/filter-predicates");
|
||||
const {
|
||||
FILTER_SEARCH_DELAY,
|
||||
} = require("devtools/client/netmonitor/src/constants");
|
||||
setTargetSearchResult,
|
||||
} = require("devtools/client/netmonitor/src/actions/search");
|
||||
|
||||
// Components
|
||||
const PropertiesView = createFactory(
|
||||
require("devtools/client/netmonitor/src/components/request-details/PropertiesView")
|
||||
);
|
||||
const ImagePreview = createFactory(
|
||||
require("devtools/client/netmonitor/src/components/previews/ImagePreview")
|
||||
);
|
||||
const SourcePreview = createFactory(
|
||||
require("devtools/client/netmonitor/src/components/previews/SourcePreview")
|
||||
);
|
||||
const HtmlPreview = createFactory(
|
||||
require("devtools/client/netmonitor/src/components/previews/HtmlPreview")
|
||||
);
|
||||
const Accordion = createFactory(
|
||||
require("devtools/client/shared/components/Accordion")
|
||||
);
|
||||
const SearchBox = createFactory(
|
||||
require("devtools/client/shared/components/SearchBox")
|
||||
require("devtools/client/netmonitor/src/components/PropertiesView")
|
||||
);
|
||||
|
||||
loader.lazyGetter(this, "MODE", function() {
|
||||
return require("devtools/client/shared/components/reps/reps").MODE;
|
||||
});
|
||||
|
||||
const { div } = dom;
|
||||
const { div, img } = dom;
|
||||
const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
|
||||
const JSON_FILTER_TEXT = L10N.getStr("jsonFilterText");
|
||||
const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
|
||||
const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
|
||||
const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
|
||||
const RESPONSE_PAYLOAD = L10N.getStr("responsePayload");
|
||||
const RESPONSE_PREVIEW = L10N.getStr("responsePreview");
|
||||
const RESPONSE_EMPTY_TEXT = L10N.getStr("responseEmptyText");
|
||||
|
@ -67,6 +57,7 @@ class ResponsePanel extends Component {
|
|||
request: PropTypes.object.isRequired,
|
||||
openLink: PropTypes.func,
|
||||
targetSearchResult: PropTypes.object,
|
||||
resetTargetSearchResult: PropTypes.func,
|
||||
connector: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
@ -75,9 +66,13 @@ class ResponsePanel extends Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
filterText: "",
|
||||
currentOpen: undefined,
|
||||
imageDimensions: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
};
|
||||
|
||||
this.updateImageDimensions = this.updateImageDimensions.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -109,6 +104,15 @@ class ResponsePanel extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
updateImageDimensions({ target }) {
|
||||
this.setState({
|
||||
imageDimensions: {
|
||||
width: target.naturalWidth,
|
||||
height: target.naturalHeight,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle json, which we tentatively identify by checking the
|
||||
* MIME type for "json" after any word boundary. This works
|
||||
|
@ -173,9 +177,8 @@ class ResponsePanel extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { request, targetSearchResult } = this.props;
|
||||
const { openLink, request, targetSearchResult } = this.props;
|
||||
const { responseContent, url } = request;
|
||||
const { filterText } = this.state;
|
||||
|
||||
if (
|
||||
!responseContent ||
|
||||
|
@ -188,7 +191,31 @@ class ResponsePanel extends Component {
|
|||
let { encoding, mimeType, text } = responseContent.content;
|
||||
|
||||
if (mimeType.includes("image/")) {
|
||||
return ImagePreview({ encoding, mimeType, text, url });
|
||||
const { width, height } = this.state.imageDimensions;
|
||||
|
||||
return div(
|
||||
{ className: "panel-container response-image-box devtools-monospace" },
|
||||
img({
|
||||
className: "response-image",
|
||||
src: formDataURI(mimeType, encoding, text),
|
||||
onLoad: this.updateImageDimensions,
|
||||
}),
|
||||
div(
|
||||
{ className: "response-summary" },
|
||||
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_NAME),
|
||||
div({ className: "tabpanel-summary-value" }, getUrlBaseName(url))
|
||||
),
|
||||
div(
|
||||
{ className: "response-summary" },
|
||||
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_DIMENSIONS),
|
||||
div({ className: "tabpanel-summary-value" }, `${width} × ${height}`)
|
||||
),
|
||||
div(
|
||||
{ className: "response-summary" },
|
||||
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_MIMETYPE),
|
||||
div({ className: "tabpanel-summary-value" }, mimeType)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Decode response if it's coming from JSONView.
|
||||
|
@ -199,120 +226,41 @@ class ResponsePanel extends Component {
|
|||
// Display Properties View
|
||||
const { json, jsonpCallback, error } =
|
||||
this.handleJSONResponse(mimeType, text) || {};
|
||||
|
||||
const items = [];
|
||||
const object = {};
|
||||
let sectionName;
|
||||
|
||||
const onToggle = (open, item) => {
|
||||
this.setState({ currentOpen: open ? item : null });
|
||||
};
|
||||
|
||||
if (json) {
|
||||
if (jsonpCallback) {
|
||||
sectionName = L10N.getFormatStr("jsonpScopeName", jsonpCallback);
|
||||
} else {
|
||||
sectionName = JSON_SCOPE_NAME;
|
||||
}
|
||||
|
||||
items.push({
|
||||
component: PropertiesView,
|
||||
componentProps: {
|
||||
object: json,
|
||||
useQuotes: true,
|
||||
filterText,
|
||||
targetSearchResult,
|
||||
mode: MODE.LONG,
|
||||
},
|
||||
header: sectionName,
|
||||
id: "jsonpScopeName",
|
||||
opened: !!targetSearchResult,
|
||||
shouldOpen: item => {
|
||||
const { currentOpen } = this.state;
|
||||
if (typeof currentOpen == "undefined" && item.id === items[0].id) {
|
||||
// if this the first and panel just displayed, open this item
|
||||
// by default;
|
||||
return true;
|
||||
} else if (!currentOpen) {
|
||||
if (!targetSearchResult) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Open the item is toggled open or there is a serch result to show
|
||||
if (item.id == currentOpen.id || targetSearchResult) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onToggle,
|
||||
});
|
||||
object[sectionName] = json;
|
||||
}
|
||||
|
||||
// Display HTML
|
||||
// Display HTML under Properties View
|
||||
if (Filters.html(this.props.request)) {
|
||||
items.push({
|
||||
component: HtmlPreview,
|
||||
componentProps: { responseContent },
|
||||
header: RESPONSE_PREVIEW,
|
||||
id: "responsePreview",
|
||||
opened: false,
|
||||
shouldOpen: item => {
|
||||
const { currentOpen } = this.state;
|
||||
if (typeof currentOpen == "undefined" && item.id === items[0].id) {
|
||||
// if this the first and panel just displayed, open this item
|
||||
// by default;
|
||||
if (targetSearchResult) {
|
||||
// collapse when we do a search
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (!currentOpen) {
|
||||
return false;
|
||||
}
|
||||
// close this if there is a search result since
|
||||
// it does not apply search
|
||||
if (targetSearchResult) {
|
||||
return false;
|
||||
}
|
||||
if (item.id == currentOpen.id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onToggle,
|
||||
});
|
||||
object[RESPONSE_PREVIEW] = {
|
||||
HTML_PREVIEW: { responseContent },
|
||||
};
|
||||
}
|
||||
|
||||
items.push({
|
||||
component: SourcePreview,
|
||||
componentProps: {
|
||||
let scrollToLine;
|
||||
let expandedNodes;
|
||||
|
||||
if (targetSearchResult && targetSearchResult.line) {
|
||||
scrollToLine = targetSearchResult.line;
|
||||
expandedNodes = new Set(["/" + RESPONSE_PAYLOAD]);
|
||||
}
|
||||
|
||||
// Others like text/html, text/plain, application/javascript
|
||||
object[RESPONSE_PAYLOAD] = {
|
||||
EDITOR_CONFIG: {
|
||||
text,
|
||||
mode: json ? "application/json" : mimeType.replace(/;.+/, ""),
|
||||
targetSearchResult,
|
||||
limit: Services.prefs.getIntPref(
|
||||
"devtools.netmonitor.response.ui.limit"
|
||||
),
|
||||
scrollToLine,
|
||||
},
|
||||
header: RESPONSE_PAYLOAD,
|
||||
id: "paramsPostPayload",
|
||||
opened: !!targetSearchResult,
|
||||
shouldOpen: item => {
|
||||
const { currentOpen } = this.state;
|
||||
if (typeof currentOpen == "undefined" && item.id === items[0].id) {
|
||||
return true;
|
||||
} else if (!currentOpen) {
|
||||
if (targetSearchResult) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (item.id == currentOpen.id || targetSearchResult) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onToggle,
|
||||
});
|
||||
};
|
||||
|
||||
const classList = ["panel-container"];
|
||||
if (Filters.html(this.props.request)) {
|
||||
|
@ -322,19 +270,22 @@ class ResponsePanel extends Component {
|
|||
return div(
|
||||
{ className: classList.join(" ") },
|
||||
error && div({ className: "response-error-header", title: error }, error),
|
||||
json &&
|
||||
div(
|
||||
{ className: "devtools-toolbar devtools-input-toolbar" },
|
||||
SearchBox({
|
||||
delay: FILTER_SEARCH_DELAY,
|
||||
type: "filter",
|
||||
onChange: filter => this.setState({ filterText: filter }),
|
||||
placeholder: JSON_FILTER_TEXT,
|
||||
})
|
||||
),
|
||||
Accordion({ items })
|
||||
PropertiesView({
|
||||
object,
|
||||
expandedNodes,
|
||||
useQuotes: true,
|
||||
filterPlaceHolder: JSON_FILTER_TEXT,
|
||||
sectionNames: Object.keys(object),
|
||||
openLink,
|
||||
targetSearchResult,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResponsePanel;
|
||||
module.exports = connect(
|
||||
null,
|
||||
dispatch => ({
|
||||
resetTargetSearchResult: () => dispatch(setTargetSearchResult(null)),
|
||||
})
|
||||
)(ResponsePanel);
|
|
@ -36,7 +36,7 @@ const CachePanel = createFactory(
|
|||
require("devtools/client/netmonitor/src/components/request-details/CachePanel")
|
||||
);
|
||||
const ResponsePanel = createFactory(
|
||||
require("devtools/client/netmonitor/src/components/request-details/ResponsePanel")
|
||||
require("devtools/client/netmonitor/src/components/ResponsePanel")
|
||||
);
|
||||
const SecurityPanel = createFactory(
|
||||
require("devtools/client/netmonitor/src/components/request-details/SecurityPanel")
|
||||
|
|
|
@ -16,10 +16,12 @@ DevToolsModules(
|
|||
'CustomRequestPanel.js',
|
||||
'DropHarHandler.js',
|
||||
'HeadersPanel.js',
|
||||
'HtmlPreview.js',
|
||||
'JSONPreview.js',
|
||||
'MonitorPanel.js',
|
||||
'NetworkActionBar.js',
|
||||
'PropertiesView.js',
|
||||
'ResponsePanel.js',
|
||||
'SecurityState.js',
|
||||
'SourceEditor.js',
|
||||
'StatisticsPanel.js',
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
/* 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 { Component } = require("devtools/client/shared/vendor/react");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
|
||||
|
||||
const {
|
||||
div,
|
||||
img,
|
||||
} = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
|
||||
const {
|
||||
formDataURI,
|
||||
getUrlBaseName,
|
||||
} = require("devtools/client/netmonitor/src/utils/request-utils");
|
||||
|
||||
const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
|
||||
const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
|
||||
const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
|
||||
|
||||
class ImagePreview extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
mimeType: PropTypes.string,
|
||||
encoding: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
url: PropTypes.string,
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
dimensions: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
};
|
||||
|
||||
this.updateDimensions = this.updateDimensions.bind(this);
|
||||
}
|
||||
|
||||
updateDimensions({ target }) {
|
||||
this.setState({
|
||||
dimensions: {
|
||||
width: target.naturalWidth,
|
||||
height: target.naturalHeight,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { mimeType, encoding, text, url } = this.props;
|
||||
const { width, height } = this.state.dimensions;
|
||||
|
||||
return div(
|
||||
{ className: "panel-container response-image-box devtools-monospace" },
|
||||
img({
|
||||
className: "response-image",
|
||||
src: formDataURI(mimeType, encoding, text),
|
||||
onLoad: this.updateDimensions,
|
||||
}),
|
||||
div(
|
||||
{ className: "response-summary" },
|
||||
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_NAME),
|
||||
div({ className: "tabpanel-summary-value" }, getUrlBaseName(url))
|
||||
),
|
||||
div(
|
||||
{ className: "response-summary" },
|
||||
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_DIMENSIONS),
|
||||
div({ className: "tabpanel-summary-value" }, `${width} × ${height}`)
|
||||
),
|
||||
div(
|
||||
{ className: "response-summary" },
|
||||
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_MIMETYPE),
|
||||
div({ className: "tabpanel-summary-value" }, mimeType)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ImagePreview;
|
|
@ -3,6 +3,7 @@
|
|||
* 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 {
|
||||
|
@ -13,7 +14,8 @@ const Editor = require("devtools/client/shared/sourceeditor/editor");
|
|||
const {
|
||||
setTargetSearchResult,
|
||||
} = require("devtools/client/netmonitor/src/actions/search");
|
||||
const { div, pre } = dom;
|
||||
const { div } = dom;
|
||||
|
||||
/**
|
||||
* CodeMirror editor as a React component
|
||||
*/
|
||||
|
@ -25,58 +27,16 @@ class SourcePreview extends Component {
|
|||
// Source editor content
|
||||
text: PropTypes.string,
|
||||
// Search result text to select
|
||||
targetSearchResult: PropTypes.object,
|
||||
targetSearchResult: PropTypes.string,
|
||||
// Reset target search result that has been used for navigation in this panel.
|
||||
// This is done to avoid second navigation the next time.
|
||||
resetTargetSearchResult: PropTypes.func,
|
||||
// Limit for determine how to render large content
|
||||
limit: PropTypes.number,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { mode, text } = this.props;
|
||||
if (!this.isOverSizeLimit(text)) {
|
||||
this.loadEditor(mode, text);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (
|
||||
nextProps.mode !== this.props.mode ||
|
||||
nextProps.text !== this.props.text ||
|
||||
nextProps.targetSearchResult !== this.props.targetSearchResult
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { mode, targetSearchResult, text } = this.props;
|
||||
|
||||
if (prevProps.text !== text) {
|
||||
// if the text size is over the limit we have switched to
|
||||
// rendering with a PRE, lets clean up any previous editor object.
|
||||
if (this.isOverSizeLimit(text)) {
|
||||
this.unloadEditor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.editor || this.editor.isDestroyed()) {
|
||||
// When updating from a PRE to an editor
|
||||
this.loadEditor(mode, text);
|
||||
} else {
|
||||
// When updating from editor to editor
|
||||
this.updateEditor(mode, text);
|
||||
}
|
||||
} else if (prevProps.targetSearchResult !== targetSearchResult) {
|
||||
this.findSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unloadEditor();
|
||||
}
|
||||
|
||||
loadEditor(mode, text) {
|
||||
this.editor = new Editor({
|
||||
lineNumbers: true,
|
||||
lineWrapping: false,
|
||||
|
@ -102,33 +62,49 @@ class SourcePreview extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
updateEditor(mode, text) {
|
||||
// Reset the existed 'mode' attribute in order to make setText() process faster
|
||||
// to prevent drawing unnecessary syntax highlight.
|
||||
this.editor.setMode(null);
|
||||
this.editor.setText(text);
|
||||
|
||||
if (this.editorSetModeTimeout) {
|
||||
clearTimeout(this.editorSetModeTimeout);
|
||||
}
|
||||
|
||||
// CodeMirror's setMode() (syntax highlight) is the performance bottleneck when
|
||||
// processing large content, so we enable it asynchronously within the setTimeout
|
||||
// to avoid UI blocking. (rendering source code -> drawing syntax highlight)
|
||||
this.editorSetModeTimeout = setTimeout(() => {
|
||||
this.editorSetModeTimeout = null;
|
||||
this.editor.setMode(mode);
|
||||
this.findSearchResult();
|
||||
});
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (
|
||||
nextProps.mode !== this.props.mode ||
|
||||
nextProps.text !== this.props.text ||
|
||||
nextProps.targetSearchResult !== this.props.targetSearchResult
|
||||
);
|
||||
}
|
||||
|
||||
unloadEditor() {
|
||||
componentDidUpdate(prevProps) {
|
||||
const { mode, targetSearchResult, text } = this.props;
|
||||
|
||||
// Bail out if the editor has been destroyed in the meantime.
|
||||
if (this.editor.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevProps.text !== text) {
|
||||
// Reset the existed 'mode' attribute in order to make setText() process faster
|
||||
// to prevent drawing unnecessary syntax highlight.
|
||||
this.editor.setMode(null);
|
||||
this.editor.setText(text);
|
||||
|
||||
if (this.editorSetModeTimeout) {
|
||||
clearTimeout(this.editorSetModeTimeout);
|
||||
}
|
||||
|
||||
// CodeMirror's setMode() (syntax highlight) is the performance bottleneck when
|
||||
// processing large content, so we enable it asynchronously within the setTimeout
|
||||
// to avoid UI blocking. (rendering source code -> drawing syntax highlight)
|
||||
this.editorSetModeTimeout = setTimeout(() => {
|
||||
this.editorSetModeTimeout = null;
|
||||
this.editor.setMode(mode);
|
||||
this.findSearchResult();
|
||||
});
|
||||
} else if (prevProps.targetSearchResult !== targetSearchResult) {
|
||||
this.findSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.editorTimeout);
|
||||
clearTimeout(this.editorSetModeTimeout);
|
||||
if (this.editor) {
|
||||
this.editor.destroy();
|
||||
this.editor = null;
|
||||
}
|
||||
this.editor.destroy();
|
||||
}
|
||||
|
||||
findSearchResult() {
|
||||
|
@ -138,71 +114,17 @@ class SourcePreview extends Component {
|
|||
const { line } = targetSearchResult;
|
||||
// scroll the editor to center the line
|
||||
// with the target search result
|
||||
if (this.editor) {
|
||||
this.editor.setCursor({ line: line - 1 }, "center");
|
||||
}
|
||||
this.editor.setCursor({ line: line - 1 }, "center");
|
||||
}
|
||||
|
||||
resetTargetSearchResult();
|
||||
}
|
||||
|
||||
// Scroll to specified line if the user clicks on search results.
|
||||
scrollToLine(element) {
|
||||
const { targetSearchResult, resetTargetSearchResult } = this.props;
|
||||
|
||||
// The following code is responsible for scrolling given line
|
||||
// to visible view-port.
|
||||
// It gets the <div> child element representing the target
|
||||
// line (by index) and uses `scrollIntoView` API to make sure
|
||||
// it's visible to the user.
|
||||
if (element && targetSearchResult && targetSearchResult.line) {
|
||||
const child = element.children[targetSearchResult.line - 1];
|
||||
if (child) {
|
||||
const range = document.createRange();
|
||||
range.selectNode(child);
|
||||
document.getSelection().addRange(range);
|
||||
child.scrollIntoView({ block: "center" });
|
||||
}
|
||||
resetTargetSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
isOverSizeLimit(text) {
|
||||
const { limit } = this.props;
|
||||
return text && text.length > limit;
|
||||
}
|
||||
|
||||
renderPre(text) {
|
||||
return div(
|
||||
{ className: "responseTextContainer" },
|
||||
pre(
|
||||
{ ref: element => this.scrollToLine(element) },
|
||||
text.split(/\r\n|\r|\n/).map((line, index) => {
|
||||
return div({ key: index }, line);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderEditor() {
|
||||
return div(
|
||||
{ className: "editor-row-container" },
|
||||
div({
|
||||
ref: "editorElement",
|
||||
className: "source-editor-mount devtools-monospace",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { text } = this.props;
|
||||
// To prevent performance issues, switch from editor to pre()
|
||||
// if response size is greater than specified limit.
|
||||
const isOverSize = this.isOverSizeLimit(text);
|
||||
return div(
|
||||
{ key: "EDITOR_CONFIG", className: "editor-row-container" },
|
||||
isOverSize ? this.renderPre(text) : this.renderEditor()
|
||||
);
|
||||
return div({
|
||||
ref: "editorElement",
|
||||
className: "source-editor-mount devtools-monospace",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,5 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'HtmlPreview.js',
|
||||
'ImagePreview.js',
|
||||
'SourcePreview.js',
|
||||
'SourcePreview.js',
|
||||
)
|
||||
|
|
|
@ -198,12 +198,13 @@ class ParamsPanel extends Component {
|
|||
}
|
||||
|
||||
// Request payload section
|
||||
|
||||
const limit = Services.prefs.getIntPref(
|
||||
"devtools.netmonitor.requestBodyLimit"
|
||||
);
|
||||
|
||||
// Check if the request post data has been truncated from the backend,
|
||||
// in which case no parse should be attempted.
|
||||
// Check if the request post data has been truncated, in which case no parse should
|
||||
// be attempted.
|
||||
if (postData && limit <= postData.length) {
|
||||
error = REQUEST_TRUNCATED;
|
||||
}
|
||||
|
@ -235,10 +236,6 @@ class ParamsPanel extends Component {
|
|||
text: postData,
|
||||
mode: mimeType.replace(/;.+/, ""),
|
||||
targetSearchResult,
|
||||
// Using the response limit to improve perf when rendering
|
||||
limit: Services.prefs.getIntPref(
|
||||
"devtools.netmonitor.response.ui.limit"
|
||||
),
|
||||
},
|
||||
header: PARAMS_POST_PAYLOAD,
|
||||
id: "paramsPostPayload",
|
||||
|
|
|
@ -50,7 +50,7 @@ const { div } = dom;
|
|||
class PropertiesView extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
object: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
object: PropTypes.object,
|
||||
provider: PropTypes.object,
|
||||
enableInput: PropTypes.bool,
|
||||
expandableStrings: PropTypes.bool,
|
||||
|
@ -59,7 +59,6 @@ class PropertiesView extends Component {
|
|||
cropLimit: PropTypes.number,
|
||||
targetSearchResult: PropTypes.object,
|
||||
resetTargetSearchResult: PropTypes.func,
|
||||
mode: PropTypes.symbol,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -170,7 +169,7 @@ class PropertiesView extends Component {
|
|||
// FIXME: A workaround for the issue in StringRep
|
||||
// Force StringRep to crop the text every time
|
||||
member: Object.assign({}, member, { open: false }),
|
||||
mode: this.props.mode || MODE.TINY,
|
||||
mode: MODE.TINY,
|
||||
cropLimit: this.props.cropLimit,
|
||||
noGrip: true,
|
||||
})
|
||||
|
|
|
@ -8,9 +8,7 @@ DevToolsModules(
|
|||
'NetworkDetailsBar.js',
|
||||
'ParamsPanel.js',
|
||||
'PropertiesView.js',
|
||||
'ResponsePanel.js',
|
||||
'SecurityPanel.js',
|
||||
'StackTracePanel.js',
|
||||
'TimingsPanel.js'
|
||||
|
||||
)
|
||||
|
|
|
@ -13,13 +13,10 @@ add_task(async function() {
|
|||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
const { getDisplayedRequests, getSortedRequests } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
let wait = waitForNetworkEvents(monitor, 1);
|
||||
tab.linkedBrowser.reload();
|
||||
await wait;
|
||||
|
@ -49,21 +46,13 @@ add_task(async function() {
|
|||
document.querySelectorAll(".request-list-item")[0]
|
||||
);
|
||||
await wait;
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
await wait;
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
const header = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(header, monitor);
|
||||
await wait;
|
||||
|
||||
// CodeMirror will only load lines currently in view to the DOM. getValue()
|
||||
// retrieves all lines pending render after a user begins scrolling.
|
||||
const text = document.querySelector(".CodeMirror").CodeMirror.getValue();
|
||||
|
|
|
@ -20,58 +20,22 @@ add_task(async function() {
|
|||
// Execute requests.
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
const waitForPropsView = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
);
|
||||
|
||||
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
|
||||
await Promise.all([wait, waitForPropsView]);
|
||||
await wait;
|
||||
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
|
||||
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
`"This is a base 64 string."`,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
|
||||
// Open the response payload section, it should hide the json section
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
const header = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(header, monitor);
|
||||
await wait;
|
||||
|
||||
is(
|
||||
tabpanel.querySelector(".response-error-header") === null,
|
||||
true,
|
||||
"The response error header doesn't have the intended visibility."
|
||||
);
|
||||
const jsonView =
|
||||
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
|
||||
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
|
||||
is(
|
||||
jsonView.textContent === L10N.getStr("jsonScopeName"),
|
||||
true,
|
||||
|
@ -89,15 +53,38 @@ add_task(async function() {
|
|||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".accordion-item").length,
|
||||
tabpanel.querySelectorAll(".tree-section").length,
|
||||
2,
|
||||
"There should be 2 tree sections displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".empty-notice").length,
|
||||
0,
|
||||
"The empty notice should not be displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeLabelCell .treeLabel"
|
||||
);
|
||||
const values = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeValueCell .objectBox"
|
||||
);
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
`"This is a base 64 string."`,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
|
|
@ -22,35 +22,20 @@ add_task(async function() {
|
|||
|
||||
const onResponsePanelReady = waitForDOM(
|
||||
document,
|
||||
"#response-panel .accordion-item",
|
||||
2
|
||||
"#response-panel .CodeMirror-code"
|
||||
);
|
||||
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
|
||||
await onResponsePanelReady;
|
||||
|
||||
const codeMirrorReady = waitForDOM(
|
||||
document,
|
||||
"#response-panel .CodeMirror-code"
|
||||
);
|
||||
|
||||
const header = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(header, monitor);
|
||||
|
||||
await codeMirrorReady;
|
||||
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
is(
|
||||
tabpanel.querySelectorAll(".accordion-item").length,
|
||||
tabpanel.querySelectorAll(".tree-section").length,
|
||||
2,
|
||||
"There should be 2 accordion items displayed in this tabpanel."
|
||||
"There should be 2 tree sections displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".empty-notice").length,
|
||||
|
@ -74,8 +59,7 @@ add_task(async function() {
|
|||
"The response image box doesn't have the intended visibility."
|
||||
);
|
||||
|
||||
const jsonView =
|
||||
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
|
||||
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
|
||||
is(
|
||||
jsonView.textContent === L10N.getStr("jsonScopeName"),
|
||||
true,
|
||||
|
|
|
@ -53,45 +53,76 @@ add_task(async function() {
|
|||
}
|
||||
);
|
||||
|
||||
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
const waitForPropsView = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
);
|
||||
|
||||
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
|
||||
await Promise.all([wait, waitForPropsView]);
|
||||
|
||||
testJsonAccordionInResposeTab();
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
const payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
await wait;
|
||||
|
||||
testResponseTab();
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
function testJsonAccordionInResposeTab() {
|
||||
function testResponseTab() {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow").length,
|
||||
tabpanel.querySelector(".response-error-header") === null,
|
||||
true,
|
||||
"The response error header doesn't have the intended visibility."
|
||||
);
|
||||
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
|
||||
is(
|
||||
jsonView.textContent === L10N.getStr("jsonScopeName"),
|
||||
true,
|
||||
"The response json view has the intended visibility."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelector(".editor-row-container").clientHeight !== 0,
|
||||
true,
|
||||
"The source editor container has visible height."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelector(".CodeMirror-code") === null,
|
||||
false,
|
||||
"The response editor has the intended visibility."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelector(".response-image-box") === null,
|
||||
true,
|
||||
"The response image box doesn't have the intended visibility."
|
||||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".tree-section").length,
|
||||
2,
|
||||
"There should be 2 tree sections displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
|
||||
2047,
|
||||
"There should be 2047 json properties displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".empty-notice").length,
|
||||
0,
|
||||
"The empty notice should not be displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
|
||||
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
|
||||
is(
|
||||
tabpanel.querySelector(".tree-section .treeLabel").textContent,
|
||||
L10N.getStr("jsonScopeName"),
|
||||
"The json view section doesn't have the correct title."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeLabelCell .treeLabel"
|
||||
);
|
||||
const values = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeValueCell .objectBox"
|
||||
);
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
|
@ -115,54 +146,4 @@ add_task(async function() {
|
|||
"The second json property value was incorrect."
|
||||
);
|
||||
}
|
||||
|
||||
function testResponseTab() {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
|
||||
is(
|
||||
tabpanel.querySelector(".response-error-header") === null,
|
||||
true,
|
||||
"The response error header doesn't have the intended visibility."
|
||||
);
|
||||
const jsonView =
|
||||
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
|
||||
is(
|
||||
jsonView.textContent === L10N.getStr("jsonScopeName"),
|
||||
true,
|
||||
"The response json view has the intended visibility."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelector(".source-editor-mount").clientHeight !== 0,
|
||||
true,
|
||||
"The source editor container has visible height."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelector(".CodeMirror-code") === null,
|
||||
false,
|
||||
"The response editor has the intended visibility."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelector(".response-image-box") === null,
|
||||
true,
|
||||
"The response image box doesn't have the intended visibility."
|
||||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".accordion-item").length,
|
||||
2,
|
||||
"There should be 2 accordion items displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".empty-notice").length,
|
||||
0,
|
||||
"The empty notice should not be displayed in this tabpanel."
|
||||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelector(".accordion-item .accordion-header-label")
|
||||
.textContent,
|
||||
L10N.getStr("jsonScopeName"),
|
||||
"The json view section doesn't have the correct title."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,26 +22,22 @@ add_task(async function() {
|
|||
|
||||
const onResponsePanelReady = waitForDOM(
|
||||
document,
|
||||
"#response-panel .accordion-item",
|
||||
2
|
||||
"#response-panel .CodeMirror-code"
|
||||
);
|
||||
|
||||
const onPropsViewReady = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
);
|
||||
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
await Promise.all([onResponsePanelReady, onPropsViewReady]);
|
||||
await onResponsePanelReady;
|
||||
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
|
||||
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
|
||||
const labels = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeLabelCell .treeLabel"
|
||||
);
|
||||
const values = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeValueCell .objectBox"
|
||||
);
|
||||
|
||||
is(labels[0].textContent, "obj", "The first json property name is correct.");
|
||||
is(
|
||||
|
|
|
@ -22,30 +22,25 @@ add_task(async function() {
|
|||
|
||||
const onResponsePanelReady = waitForDOM(
|
||||
document,
|
||||
"#response-panel .accordion-item",
|
||||
2
|
||||
);
|
||||
|
||||
const onPropsViewReady = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
"#response-panel .CodeMirror-code"
|
||||
);
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
await Promise.all([onResponsePanelReady, onPropsViewReady]);
|
||||
await onResponsePanelReady;
|
||||
|
||||
checkResponsePanelDisplaysJSON();
|
||||
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
is(
|
||||
tabpanel.querySelectorAll(".accordion-item").length,
|
||||
tabpanel.querySelectorAll(".tree-section").length,
|
||||
2,
|
||||
"There should be 2 accordion items displayed in this tabpanel."
|
||||
"There should be 2 tree sections displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow").length,
|
||||
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
|
@ -55,8 +50,12 @@ add_task(async function() {
|
|||
"The empty notice should not be displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
|
||||
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
|
||||
const labels = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeLabelCell .treeLabel"
|
||||
);
|
||||
const values = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeValueCell .objectBox"
|
||||
);
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
|
@ -69,20 +68,6 @@ add_task(async function() {
|
|||
"The first json property value was incorrect."
|
||||
);
|
||||
|
||||
const onCodeMirrorReady = waitForDOM(
|
||||
document,
|
||||
"#response-panel .CodeMirror-code"
|
||||
);
|
||||
|
||||
const payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
|
||||
await onCodeMirrorReady;
|
||||
|
||||
checkResponsePanelDisplaysJSON();
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
/**
|
||||
|
@ -96,8 +81,7 @@ add_task(async function() {
|
|||
true,
|
||||
"The response error header doesn't have the intended visibility."
|
||||
);
|
||||
const jsonView =
|
||||
panel.querySelector(".accordion-item .accordion-header-label") || {};
|
||||
const jsonView = panel.querySelector(".tree-section .treeLabel") || {};
|
||||
is(
|
||||
jsonView.textContent === L10N.getStr("jsonScopeName"),
|
||||
true,
|
||||
|
|
|
@ -45,56 +45,18 @@ add_task(async function() {
|
|||
}
|
||||
);
|
||||
|
||||
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
const waitForPropsView = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
);
|
||||
|
||||
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
await Promise.all([wait, waitForPropsView]);
|
||||
|
||||
testJsonSectionInResponseTab();
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
const payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
await wait;
|
||||
|
||||
testResponseTab();
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
function testJsonSectionInResponseTab() {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
|
||||
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
`"Hello oddly-named JSON!"`,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
}
|
||||
|
||||
function testResponseTab() {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
|
||||
|
@ -103,8 +65,7 @@ add_task(async function() {
|
|||
true,
|
||||
"The response error header doesn't have the intended visibility."
|
||||
);
|
||||
const jsonView =
|
||||
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
|
||||
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
|
||||
is(
|
||||
jsonView.textContent === L10N.getStr("jsonScopeName"),
|
||||
true,
|
||||
|
@ -122,15 +83,37 @@ add_task(async function() {
|
|||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".accordion-item").length,
|
||||
tabpanel.querySelectorAll(".tree-section").length,
|
||||
2,
|
||||
"There should be 2 accordion items displayed in this tabpanel."
|
||||
"There should be 2 tree sections displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".empty-notice").length,
|
||||
0,
|
||||
"The empty notice should not be displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeLabelCell .treeLabel"
|
||||
);
|
||||
const values = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeValueCell .objectBox"
|
||||
);
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
`"Hello oddly-named JSON!"`,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -46,56 +46,18 @@ add_task(async function() {
|
|||
}
|
||||
);
|
||||
|
||||
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
const waitForPropsView = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
);
|
||||
|
||||
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
await Promise.all([wait, waitForPropsView]);
|
||||
|
||||
testJsonSectionInResponseTab();
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
const payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
await wait;
|
||||
|
||||
testResponseTab();
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
function testJsonSectionInResponseTab() {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
|
||||
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
`"Hello third-party JSON!"`,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
}
|
||||
|
||||
function testResponseTab() {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
|
||||
|
@ -104,8 +66,7 @@ add_task(async function() {
|
|||
true,
|
||||
"The response error header doesn't have the intended visibility."
|
||||
);
|
||||
const jsonView =
|
||||
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
|
||||
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
|
||||
is(
|
||||
jsonView.textContent === L10N.getStr("jsonScopeName"),
|
||||
true,
|
||||
|
@ -123,14 +84,37 @@ add_task(async function() {
|
|||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".accordion-item").length,
|
||||
tabpanel.querySelectorAll(".tree-section").length,
|
||||
2,
|
||||
"There should be 2 accordion items displayed in this tabpanel."
|
||||
"There should be 2 tree sections displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".empty-notice").length,
|
||||
0,
|
||||
"The empty notice should not be displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeLabelCell .treeLabel"
|
||||
);
|
||||
const values = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeValueCell .objectBox"
|
||||
);
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
`"Hello third-party JSON!"`,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -65,90 +65,30 @@ add_task(async function() {
|
|||
);
|
||||
|
||||
info("Testing first request");
|
||||
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
let waitForPropsView = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
);
|
||||
|
||||
let wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
document.querySelector("#response-tab")
|
||||
);
|
||||
await Promise.all([wait, waitForPropsView]);
|
||||
|
||||
testJsonSectionInResponseTab(`"Hello JSONP!"`);
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
let payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
await wait;
|
||||
|
||||
testResponseTab("$_0123Fun");
|
||||
testResponseTab("$_0123Fun", `"Hello JSONP!"`);
|
||||
|
||||
info("Testing second request");
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "mousedown" },
|
||||
document.querySelectorAll(".request-list-item")[1]
|
||||
);
|
||||
|
||||
await wait;
|
||||
|
||||
waitForPropsView = waitForDOM(
|
||||
document,
|
||||
"#response-panel .properties-view",
|
||||
1
|
||||
);
|
||||
payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:first-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
|
||||
await waitForPropsView;
|
||||
|
||||
testJsonSectionInResponseTab(`"Hello weird JSONP!"`);
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
await wait;
|
||||
|
||||
testResponseTab("$_4567Sad");
|
||||
testResponseTab("$_4567Sad", `"Hello weird JSONP!"`);
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
function testJsonSectionInResponseTab(greeting) {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
|
||||
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
greeting,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
}
|
||||
|
||||
function testResponseTab(func) {
|
||||
function testResponseTab(func, greeting) {
|
||||
const tabpanel = document.querySelector("#response-panel");
|
||||
|
||||
is(
|
||||
|
@ -157,8 +97,7 @@ add_task(async function() {
|
|||
"The response error header doesn't have the intended visibility."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelector(".accordion-item .accordion-header-label")
|
||||
.textContent,
|
||||
tabpanel.querySelector(".tree-section .treeLabel").textContent,
|
||||
L10N.getFormatStr("jsonpScopeName", func),
|
||||
"The response json view has the intened visibility and correct title."
|
||||
);
|
||||
|
@ -174,14 +113,37 @@ add_task(async function() {
|
|||
);
|
||||
|
||||
is(
|
||||
tabpanel.querySelectorAll(".accordion-item").length,
|
||||
tabpanel.querySelectorAll(".tree-section").length,
|
||||
2,
|
||||
"There should be 2 accordion items displayed in this tabpanel."
|
||||
"There should be 2 tree sections displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
|
||||
1,
|
||||
"There should be 1 json properties displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
tabpanel.querySelectorAll(".empty-notice").length,
|
||||
0,
|
||||
"The empty notice should not be displayed in this tabpanel."
|
||||
);
|
||||
|
||||
const labels = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeLabelCell .treeLabel"
|
||||
);
|
||||
const values = tabpanel.querySelectorAll(
|
||||
"tr:not(.tree-section) .treeValueCell .objectBox"
|
||||
);
|
||||
|
||||
is(
|
||||
labels[0].textContent,
|
||||
"greeting",
|
||||
"The first json property name was incorrect."
|
||||
);
|
||||
is(
|
||||
values[0].textContent,
|
||||
greeting,
|
||||
"The first json property value was incorrect."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -52,7 +52,7 @@ add_task(async function() {
|
|||
}
|
||||
);
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
|
@ -60,13 +60,6 @@ add_task(async function() {
|
|||
);
|
||||
await wait;
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
|
||||
const payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
await wait;
|
||||
|
||||
ok(
|
||||
getCodeMirrorValue(monitor).match(/^<p>/),
|
||||
"The text shown in the source editor is incorrect."
|
||||
|
|
|
@ -33,7 +33,7 @@ add_task(async function() {
|
|||
|
||||
const responsePanel = document.querySelector("#response-panel");
|
||||
|
||||
const objectRow = responsePanel.querySelectorAll(".objectRow")[0];
|
||||
const objectRow = responsePanel.querySelectorAll(".objectRow")[1];
|
||||
const stringRow = responsePanel.querySelectorAll(".stringRow")[0];
|
||||
|
||||
/* Test for copy an object */
|
||||
|
@ -46,7 +46,9 @@ add_task(async function() {
|
|||
EventUtils.sendMouseEvent({ type: "contextmenu" }, objectRow);
|
||||
await waitForClipboardPromise(function setup() {
|
||||
getContextMenuItem(monitor, "properties-view-context-menu-copyall").click();
|
||||
}, `{"obj":{"type":"string"}}`);
|
||||
}, `{"JSON":{"obj":{"type":"string"}},` +
|
||||
`"Response Payload":{"EDITOR_CONFIG":{"text":` +
|
||||
`"{\\"obj\\": {\\"type\\": \\"string\\" }}","mode":"application/json"}}}`);
|
||||
|
||||
/* Test for copy a single row */
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, stringRow);
|
||||
|
@ -93,7 +95,9 @@ add_task(async function() {
|
|||
|
||||
const cookiesPanel = document.querySelector("#cookies-panel");
|
||||
|
||||
const objectRows = cookiesPanel.querySelectorAll(".objectRow");
|
||||
const objectRows = cookiesPanel.querySelectorAll(
|
||||
".objectRow:not(.tree-section)"
|
||||
);
|
||||
const stringRows = cookiesPanel.querySelectorAll(".stringRow");
|
||||
|
||||
const expectedResponseCookies = [
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests if large response contents are displayed using <pre>
|
||||
* Tests if large response contents are displayed usin <pre>
|
||||
* and not using syntax color highlighting (e.g. using CodeMirror)
|
||||
*/
|
||||
const HTML_LONG_URL = CONTENT_TYPE_SJS + "?fmt=html-long";
|
||||
|
@ -32,7 +32,7 @@ add_task(async function() {
|
|||
});
|
||||
await wait;
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
|
||||
wait = waitForDOM(document, "#response-panel .responseTextContainer");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" },
|
||||
|
@ -40,13 +40,6 @@ add_task(async function() {
|
|||
);
|
||||
await wait;
|
||||
|
||||
wait = waitForDOM(document, "#response-panel .responseTextContainer");
|
||||
const payloadHeader = document.querySelector(
|
||||
"#response-panel .accordion-item:last-child .accordion-header"
|
||||
);
|
||||
clickElement(payloadHeader, monitor);
|
||||
await wait;
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
// This test uses a lot of memory, so force a GC to help fragmentation.
|
||||
|
|
|
@ -299,16 +299,16 @@ add_task(async function() {
|
|||
|
||||
async function testResponseTab() {
|
||||
const tabpanel = await selectTab(PANELS.RESPONSE, 3);
|
||||
await waitForDOM(document, ".accordion .source-editor-mount");
|
||||
await waitForDOM(document, ".treeTable tbody");
|
||||
|
||||
const responseAccordion = tabpanel.querySelector(".accordion");
|
||||
const responseTable = tabpanel.querySelector(".treeTable tbody");
|
||||
is(
|
||||
responseAccordion.querySelectorAll(".accordion-item").length,
|
||||
responseTable.querySelectorAll(".tree-section").length,
|
||||
1,
|
||||
"There should be 1 response scope displayed in this tabpanel."
|
||||
);
|
||||
is(
|
||||
responseAccordion.querySelectorAll(".source-editor-mount").length,
|
||||
responseTable.querySelectorAll(".editor-row-container").length,
|
||||
1,
|
||||
"The response payload tab should be open initially."
|
||||
);
|
||||
|
|
|
@ -35,7 +35,7 @@ add_task(async function() {
|
|||
);
|
||||
const waitSourceEditor = waitForDOM(
|
||||
document,
|
||||
"#params-panel .responseTextContainer"
|
||||
"#params-panel .CodeMirror-code"
|
||||
);
|
||||
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
|
|
|
@ -1196,16 +1196,3 @@ async function toggleBlockedUrl(element, monitor, store, action = "block") {
|
|||
await onRequestComplete;
|
||||
info(`Selected request is now ${action}ed`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and click an element
|
||||
*
|
||||
* @param {Element} element
|
||||
* Target element to be clicked
|
||||
* @param {Object} monitor
|
||||
* The netmonitor instance used for retrieving the window.
|
||||
*/
|
||||
|
||||
function clickElement(element, monitor) {
|
||||
EventUtils.synthesizeMouseAtCenter(element, {}, monitor.panelWin);
|
||||
}
|
||||
|
|
|
@ -25,22 +25,40 @@ class Accordion extends Component {
|
|||
PropTypes.shape({
|
||||
buttons: PropTypes.arrayOf(PropTypes.object),
|
||||
className: PropTypes.string,
|
||||
component: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
|
||||
component: PropTypes.object,
|
||||
componentProps: PropTypes.object,
|
||||
contentClassName: PropTypes.string,
|
||||
header: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
onToggle: PropTypes.func,
|
||||
// Determines the initial open state of the accordion item
|
||||
opened: PropTypes.bool.isRequired,
|
||||
// Enables dynamically changing the open state of the accordion
|
||||
// on update.
|
||||
shouldOpen: PropTypes.func,
|
||||
})
|
||||
).isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add initial data to the state.opened map, and inject new data
|
||||
* when receiving updated props.
|
||||
*/
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
const newItems = props.items.filter(
|
||||
({ id }) => typeof state.opened[id] !== "boolean"
|
||||
);
|
||||
|
||||
if (newItems.length) {
|
||||
const everOpened = { ...state.everOpened };
|
||||
const opened = { ...state.opened };
|
||||
for (const item of newItems) {
|
||||
everOpened[item.id] = item.opened;
|
||||
opened[item.id] = item.opened;
|
||||
}
|
||||
return { everOpened, opened };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
@ -50,115 +68,56 @@ class Accordion extends Component {
|
|||
|
||||
this.onHeaderClick = this.onHeaderClick.bind(this);
|
||||
this.onHeaderKeyDown = this.onHeaderKeyDown.bind(this);
|
||||
this.setInitialState = this.setInitialState.bind(this);
|
||||
this.updateCurrentState = this.updateCurrentState.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setInitialState();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.items !== this.props.items) {
|
||||
this.updateCurrentState();
|
||||
}
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
/**
|
||||
* Add initial data to the `state.opened` map.
|
||||
* This happens only on initial mount of the accordion.
|
||||
*/
|
||||
const newItems = this.props.items.filter(
|
||||
({ id }) => typeof this.state.opened[id] !== "boolean"
|
||||
);
|
||||
|
||||
if (newItems.length) {
|
||||
const everOpened = { ...this.state.everOpened };
|
||||
const opened = { ...this.state.opened };
|
||||
for (const item of newItems) {
|
||||
everOpened[item.id] = item.opened;
|
||||
opened[item.id] = item.opened;
|
||||
}
|
||||
|
||||
this.setState({ everOpened, opened });
|
||||
}
|
||||
}
|
||||
|
||||
updateCurrentState() {
|
||||
/**
|
||||
* Updates the `state.opened` map based on the
|
||||
* new items that have been added and those that
|
||||
* `item.shouldOpen()` has changed. This happens
|
||||
* on each update.
|
||||
*/
|
||||
const updatedItems = this.props.items.filter(item => {
|
||||
const notExist = typeof this.state.opened[item.id] !== "boolean";
|
||||
if (typeof item.shouldOpen == "function") {
|
||||
return notExist || this.state.opened[item.id] !== item.shouldOpen(item);
|
||||
}
|
||||
return notExist;
|
||||
});
|
||||
|
||||
if (updatedItems.length) {
|
||||
const everOpened = { ...this.state.everOpened };
|
||||
const opened = { ...this.state.opened };
|
||||
for (const item of updatedItems) {
|
||||
let itemOpen = item.opened;
|
||||
if (typeof item.shouldOpen == "function") {
|
||||
itemOpen = item.shouldOpen(item);
|
||||
}
|
||||
everOpened[item.id] = itemOpen;
|
||||
opened[item.id] = itemOpen;
|
||||
}
|
||||
this.setState({ everOpened, opened });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event Click event.
|
||||
* @param {Object} item The item to be collapsed/expanded.
|
||||
*/
|
||||
onHeaderClick(event, item) {
|
||||
onHeaderClick(event) {
|
||||
event.preventDefault();
|
||||
// In the Browser Toolbox's Inspector/Layout view, handleHeaderClick is
|
||||
// called twice unless we call stopPropagation, making the accordion item
|
||||
// open-and-close or close-and-open
|
||||
event.stopPropagation();
|
||||
this.toggleItem(item);
|
||||
this.toggleItem(event.currentTarget.parentElement.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event Keyboard event.
|
||||
* @param {Object} item The item to be collapsed/expanded.
|
||||
*/
|
||||
onHeaderKeyDown(event, item) {
|
||||
onHeaderKeyDown(event) {
|
||||
if (event.key === " " || event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
this.toggleItem(item);
|
||||
this.toggleItem(event.currentTarget.parentElement.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand or collapse an accordion list item.
|
||||
* @param {Object} item The item to be collapsed or expanded.
|
||||
* @param {String} id Id of the item to be collapsed or expanded.
|
||||
*/
|
||||
toggleItem(item) {
|
||||
const opened = !this.state.opened[item.id];
|
||||
toggleItem(id) {
|
||||
const item = this.props.items.find(x => x.id === id);
|
||||
const opened = !this.state.opened[id];
|
||||
// We could have no item if props just changed
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
everOpened: {
|
||||
...this.state.everOpened,
|
||||
[item.id]: true,
|
||||
[id]: true,
|
||||
},
|
||||
opened: {
|
||||
...this.state.opened,
|
||||
[item.id]: opened,
|
||||
[id]: opened,
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof item.onToggle === "function") {
|
||||
item.onToggle(opened, item);
|
||||
item.onToggle(opened);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,15 +131,14 @@ class Accordion extends Component {
|
|||
header,
|
||||
id,
|
||||
} = item;
|
||||
|
||||
const headerId = `${id}-header`;
|
||||
const opened = this.state.opened[id];
|
||||
let itemContent;
|
||||
|
||||
// Only render content if the accordion item is open or has been opened once before.
|
||||
// This saves us rendering complex components when users are keeping
|
||||
// Only render content if the accordion item is open or has been opened once
|
||||
// before. This saves us rendering complex components when users are keeping
|
||||
// them closed (e.g. in Inspector/Layout) or may not open them at all.
|
||||
if (this.state.everOpened && this.state.everOpened[id]) {
|
||||
if (this.state.everOpened[id]) {
|
||||
if (typeof component === "function") {
|
||||
itemContent = createElement(component, componentProps);
|
||||
} else if (typeof component === "object") {
|
||||
|
@ -192,9 +150,7 @@ class Accordion extends Component {
|
|||
{
|
||||
key: id,
|
||||
id,
|
||||
className: `accordion-item ${
|
||||
opened ? "accordion-open" : ""
|
||||
} ${className} `.trim(),
|
||||
className: `accordion-item ${className}`.trim(),
|
||||
"aria-labelledby": headerId,
|
||||
},
|
||||
h2(
|
||||
|
@ -206,8 +162,8 @@ class Accordion extends Component {
|
|||
// If the header contains buttons, make sure the heading name only
|
||||
// contains the "header" text and not the button text
|
||||
"aria-label": header,
|
||||
onKeyDown: event => this.onHeaderKeyDown(event, item),
|
||||
onClick: event => this.onHeaderClick(event, item),
|
||||
onKeyDown: this.onHeaderKeyDown,
|
||||
onClick: this.onHeaderClick,
|
||||
},
|
||||
span({
|
||||
className: `theme-twisty${opened ? " open" : ""}`,
|
||||
|
|
|
@ -27,8 +27,8 @@ window._snapshots = {
|
|||
tabIndex: 0,
|
||||
"aria-expanded": false,
|
||||
"aria-label": "Test Accordion Item 1",
|
||||
onKeyDown: "event => this.onHeaderKeyDown(event, item)",
|
||||
onClick: "event => this.onHeaderClick(event, item)",
|
||||
onKeyDown: "function() {\n [native code]\n}",
|
||||
onClick: "function() {\n [native code]\n}",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
@ -73,8 +73,8 @@ window._snapshots = {
|
|||
tabIndex: 0,
|
||||
"aria-expanded": false,
|
||||
"aria-label": "Test Accordion Item 2",
|
||||
onKeyDown: "event => this.onHeaderKeyDown(event, item)",
|
||||
onClick: "event => this.onHeaderClick(event, item)",
|
||||
onKeyDown: "function() {\n [native code]\n}",
|
||||
onClick: "function() {\n [native code]\n}",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
@ -121,7 +121,7 @@ window._snapshots = {
|
|||
type: "li",
|
||||
props: {
|
||||
id: "accordion-item-3",
|
||||
className: "accordion-item accordion-open",
|
||||
className: "accordion-item",
|
||||
"aria-labelledby": "accordion-item-3-header",
|
||||
},
|
||||
children: [
|
||||
|
@ -133,8 +133,8 @@ window._snapshots = {
|
|||
tabIndex: 0,
|
||||
"aria-expanded": true,
|
||||
"aria-label": "Test Accordion Item 3",
|
||||
onKeyDown: "event => this.onHeaderKeyDown(event, item)",
|
||||
onClick: "event => this.onHeaderClick(event, item)",
|
||||
onKeyDown: "function() {\n [native code]\n}",
|
||||
onClick: "function() {\n [native code]\n}",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
|
|
@ -61,7 +61,6 @@ window.onload = async function() {
|
|||
},
|
||||
];
|
||||
|
||||
// Accordion basic render
|
||||
const accordion = React.createElement(Accordion, { items: testItems });
|
||||
|
||||
matchSnapshot("Accordion basic render.", accordion);
|
||||
|
@ -86,7 +85,7 @@ window.onload = async function() {
|
|||
"accordion-item-1": true,
|
||||
"accordion-item-2": false,
|
||||
"accordion-item-3": true,
|
||||
},
|
||||
}
|
||||
},
|
||||
"State updated correctly"
|
||||
);
|
||||
|
@ -105,7 +104,7 @@ window.onload = async function() {
|
|||
"accordion-item-1": false,
|
||||
"accordion-item-2": false,
|
||||
"accordion-item-3": true,
|
||||
},
|
||||
}
|
||||
},
|
||||
"State updated correctly"
|
||||
);
|
||||
|
@ -124,7 +123,7 @@ window.onload = async function() {
|
|||
"accordion-item-1": false,
|
||||
"accordion-item-2": true,
|
||||
"accordion-item-3": true,
|
||||
},
|
||||
}
|
||||
},
|
||||
"State updated correctly"
|
||||
);
|
||||
|
|
Загрузка…
Ссылка в новой задаче