зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1391688 - Show only messages that fit in the viewport on first console render; r=bgrins.
This allow us to have a faster first meaningful render for the user. The other messages get rendered after ConsoleOutput mounting. MozReview-Commit-ID: KIptXsLmTiA --HG-- extra : rebase_source : da4ff5b1d1027f4c1b83fe6bbf7cf81963e34558
This commit is contained in:
Родитель
7c358c7180
Коммит
b4450fc517
|
@ -11,10 +11,11 @@ const Services = require("Services");
|
|||
|
||||
const {
|
||||
FILTER_BAR_TOGGLE,
|
||||
INITIALIZE,
|
||||
PERSIST_TOGGLE,
|
||||
PREFS,
|
||||
TIMESTAMPS_TOGGLE,
|
||||
SELECT_NETWORK_MESSAGE_TAB,
|
||||
TIMESTAMPS_TOGGLE,
|
||||
} = require("devtools/client/webconsole/new-console-output/constants");
|
||||
|
||||
function filterBarToggle(show) {
|
||||
|
@ -51,9 +52,16 @@ function selectNetworkMessageTab(id) {
|
|||
};
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
return {
|
||||
type: INITIALIZE
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
filterBarToggle,
|
||||
initialize,
|
||||
persistToggle,
|
||||
timestampsToggle,
|
||||
selectNetworkMessageTab,
|
||||
timestampsToggle,
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ const {
|
|||
PropTypes
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const {initialize} = require("devtools/client/webconsole/new-console-output/actions/ui");
|
||||
|
||||
const {
|
||||
getAllMessagesById,
|
||||
|
@ -23,10 +24,14 @@ const MessageContainer = createFactory(require("devtools/client/webconsole/new-c
|
|||
const {
|
||||
MESSAGE_TYPE,
|
||||
} = require("devtools/client/webconsole/new-console-output/constants");
|
||||
const {
|
||||
getInitialMessageCountForViewport
|
||||
} = require("devtools/client/webconsole/new-console-output/utils/messages.js");
|
||||
|
||||
class ConsoleOutput extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
initialized: PropTypes.bool.isRequired,
|
||||
messages: PropTypes.object.isRequired,
|
||||
messagesUi: PropTypes.object.isRequired,
|
||||
serviceContainer: PropTypes.shape({
|
||||
|
@ -60,6 +65,11 @@ class ConsoleOutput extends Component {
|
|||
if (this.props.onFirstMeaningfulPaint) {
|
||||
this.props.onFirstMeaningfulPaint();
|
||||
}
|
||||
|
||||
// Dispatching on next tick so we don't block on action execution.
|
||||
setTimeout(() => {
|
||||
this.props.dispatch(initialize());
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -80,11 +90,18 @@ class ConsoleOutput extends Component {
|
|||
nextProps.messages.size - this.props.messages.size;
|
||||
|
||||
// We need to scroll to the bottom if:
|
||||
// - we are reacting to the "initialize" action,
|
||||
// and we are already scrolled to the bottom
|
||||
// - the number of messages displayed changed
|
||||
// and we are already scrolled to the bottom
|
||||
// - the number of messages in the store changed
|
||||
// and the new message is an evaluation result.
|
||||
this.shouldScrollBottom =
|
||||
(
|
||||
!this.props.initialized &&
|
||||
nextProps.initialized &&
|
||||
isScrolledToBottom(lastChild, outputNode)
|
||||
) ||
|
||||
(messagesDelta > 0 && nextProps.messages.last().type === MESSAGE_TYPE.RESULT) ||
|
||||
(visibleMessagesDelta > 0 && isScrolledToBottom(lastChild, outputNode));
|
||||
}
|
||||
|
@ -113,8 +130,17 @@ class ConsoleOutput extends Component {
|
|||
networkMessageActiveTabId,
|
||||
serviceContainer,
|
||||
timestampsVisible,
|
||||
initialized,
|
||||
} = this.props;
|
||||
|
||||
if (!initialized) {
|
||||
const numberMessagesFitViewport = getInitialMessageCountForViewport(window);
|
||||
if (numberMessagesFitViewport < visibleMessages.length) {
|
||||
visibleMessages = visibleMessages.slice(
|
||||
visibleMessages.length - numberMessagesFitViewport);
|
||||
}
|
||||
}
|
||||
|
||||
let messageNodes = visibleMessages.map((messageId) => MessageContainer({
|
||||
dispatch,
|
||||
key: messageId,
|
||||
|
@ -155,6 +181,7 @@ function isScrolledToBottom(outputNode, scrollNode) {
|
|||
|
||||
function mapStateToProps(state, props) {
|
||||
return {
|
||||
initialized: state.ui.initialized,
|
||||
messages: getAllMessagesById(state),
|
||||
visibleMessages: getVisibleMessages(state),
|
||||
messagesUi: getAllMessagesUiById(state),
|
||||
|
|
|
@ -7,23 +7,24 @@
|
|||
|
||||
const actionTypes = {
|
||||
BATCH_ACTIONS: "BATCH_ACTIONS",
|
||||
MESSAGE_ADD: "MESSAGE_ADD",
|
||||
MESSAGES_ADD: "MESSAGES_ADD",
|
||||
MESSAGES_CLEAR: "MESSAGES_CLEAR",
|
||||
MESSAGE_OPEN: "MESSAGE_OPEN",
|
||||
MESSAGE_CLOSE: "MESSAGE_CLOSE",
|
||||
NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE",
|
||||
NETWORK_UPDATE_REQUEST: "NETWORK_UPDATE_REQUEST",
|
||||
MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
|
||||
REMOVED_ACTORS_CLEAR: "REMOVED_ACTORS_CLEAR",
|
||||
TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
|
||||
FILTER_TOGGLE: "FILTER_TOGGLE",
|
||||
FILTER_TEXT_SET: "FILTER_TEXT_SET",
|
||||
FILTERS_CLEAR: "FILTERS_CLEAR",
|
||||
DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET",
|
||||
FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
|
||||
SELECT_NETWORK_MESSAGE_TAB: "SELECT_NETWORK_MESSAGE_TAB",
|
||||
FILTER_TEXT_SET: "FILTER_TEXT_SET",
|
||||
FILTER_TOGGLE: "FILTER_TOGGLE",
|
||||
FILTERS_CLEAR: "FILTERS_CLEAR",
|
||||
INITIALIZE: "INITIALIZE",
|
||||
MESSAGE_ADD: "MESSAGE_ADD",
|
||||
MESSAGE_CLOSE: "MESSAGE_CLOSE",
|
||||
MESSAGE_OPEN: "MESSAGE_OPEN",
|
||||
MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
|
||||
MESSAGES_ADD: "MESSAGES_ADD",
|
||||
MESSAGES_CLEAR: "MESSAGES_CLEAR",
|
||||
NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE",
|
||||
NETWORK_UPDATE_REQUEST: "NETWORK_UPDATE_REQUEST",
|
||||
PERSIST_TOGGLE: "PERSIST_TOGGLE",
|
||||
REMOVED_ACTORS_CLEAR: "REMOVED_ACTORS_CLEAR",
|
||||
SELECT_NETWORK_MESSAGE_TAB: "SELECT_NETWORK_MESSAGE_TAB",
|
||||
TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
|
||||
};
|
||||
|
||||
const prefs = {
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
|
||||
const {
|
||||
FILTER_BAR_TOGGLE,
|
||||
INITIALIZE,
|
||||
PERSIST_TOGGLE,
|
||||
TIMESTAMPS_TOGGLE,
|
||||
SELECT_NETWORK_MESSAGE_TAB,
|
||||
TIMESTAMPS_TOGGLE,
|
||||
} = require("devtools/client/webconsole/new-console-output/constants");
|
||||
const Immutable = require("devtools/client/shared/vendor/immutable");
|
||||
|
||||
|
@ -19,9 +20,10 @@ const {
|
|||
|
||||
const UiState = Immutable.Record({
|
||||
filterBarVisible: false,
|
||||
initialized: false,
|
||||
networkMessageActiveTabId: PANELS.HEADERS,
|
||||
persistLogs: false,
|
||||
timestampsVisible: true,
|
||||
networkMessageActiveTabId: PANELS.HEADERS,
|
||||
});
|
||||
|
||||
function ui(state = new UiState(), action) {
|
||||
|
@ -34,6 +36,8 @@ function ui(state = new UiState(), action) {
|
|||
return state.set("timestampsVisible", action.visible);
|
||||
case SELECT_NETWORK_MESSAGE_TAB:
|
||||
return state.set("networkMessageActiveTabId", action.id);
|
||||
case INITIALIZE:
|
||||
return state.set("initialized", true);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
createFactory,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
// Test utils.
|
||||
const expect = require("expect");
|
||||
const { render } = require("enzyme");
|
||||
|
||||
const ConsoleOutput = createFactory(require("devtools/client/webconsole/new-console-output/components/ConsoleOutput"));
|
||||
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
|
||||
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
|
||||
const {initialize} = require("devtools/client/webconsole/new-console-output/actions/ui");
|
||||
const {
|
||||
getInitialMessageCountForViewport
|
||||
} = require("devtools/client/webconsole/new-console-output/utils/messages.js");
|
||||
|
||||
const MESSAGES_NUMBER = 100;
|
||||
function getDefaultProps(initialized) {
|
||||
const store = setupStore(
|
||||
Array.from({length: MESSAGES_NUMBER})
|
||||
// Alternate message so we don't trigger the repeat mechanism.
|
||||
.map((_, i) => i % 2 ? "new Date(0)" : "console.log(NaN)")
|
||||
);
|
||||
|
||||
if (initialized) {
|
||||
store.dispatch(initialize());
|
||||
}
|
||||
|
||||
return {
|
||||
store,
|
||||
serviceContainer,
|
||||
};
|
||||
}
|
||||
|
||||
describe("ConsoleOutput component:", () => {
|
||||
it("Render only the last messages that fits the viewport when non-initialized", () => {
|
||||
const rendered = render(ConsoleOutput(getDefaultProps(false)));
|
||||
const messagesNumber = rendered.find(".message").length;
|
||||
expect(messagesNumber).toBe(getInitialMessageCountForViewport(window));
|
||||
});
|
||||
|
||||
it("Render every message when initialized", () => {
|
||||
const rendered = render(ConsoleOutput(getDefaultProps(true)));
|
||||
expect(rendered.find(".message").length).toBe(MESSAGES_NUMBER);
|
||||
});
|
||||
});
|
|
@ -355,9 +355,16 @@ function isGroupType(type) {
|
|||
].includes(type);
|
||||
}
|
||||
|
||||
exports.prepareMessage = prepareMessage;
|
||||
// Export for use in testing.
|
||||
exports.getRepeatId = getRepeatId;
|
||||
function getInitialMessageCountForViewport(win) {
|
||||
const minMessageHeight = 20;
|
||||
return Math.ceil(win.innerHeight / minMessageHeight);
|
||||
}
|
||||
|
||||
exports.l10n = l10n;
|
||||
exports.isGroupType = isGroupType;
|
||||
module.exports = {
|
||||
getInitialMessageCountForViewport,
|
||||
isGroupType,
|
||||
l10n,
|
||||
prepareMessage,
|
||||
// Export for use in testing.
|
||||
getRepeatId,
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче