зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1641554 - Fix scroll to bottom issue when expanding object in console. r=bomsy.
The issue was that we were having a ResizeObserver only on the console output node. When the output only has a few node, its height is impacted when an element is expanded. We fix this by observing the output parent node, which contains both the input and the output, which prevents the issue. In editor mode though, we still need to observe only the output element, as the editor is on the right side. So when the console changes from editor mode to inline, or the other way around, we change the observed element. A test case is added to make sure the issue is fixed. Sadly, this also means we have to remove a test case (typing a multiline expression in input mode won't keep the output scroll to the bottom), but it's a tradeoff I'm willing to make as the issue isn't as annoying as the one we're fixing here. Differential Revision: https://phabricator.services.mozilla.com/D79961
This commit is contained in:
Родитель
e3d69a5d6b
Коммит
e7f3831299
|
@ -319,12 +319,13 @@ class App extends Component {
|
|||
}
|
||||
|
||||
renderConsoleOutput() {
|
||||
const { onFirstMeaningfulPaint, serviceContainer } = this.props;
|
||||
const { onFirstMeaningfulPaint, serviceContainer, editorMode } = this.props;
|
||||
|
||||
return ConsoleOutput({
|
||||
key: "console-output",
|
||||
serviceContainer,
|
||||
onFirstMeaningfulPaint,
|
||||
editorMode,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ class ConsoleOutput extends Component {
|
|||
visibleMessages: PropTypes.array.isRequired,
|
||||
networkMessageActiveTabId: PropTypes.string.isRequired,
|
||||
onFirstMeaningfulPaint: PropTypes.func.isRequired,
|
||||
editorMode: PropTypes.bool.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -69,7 +70,7 @@ class ConsoleOutput extends Component {
|
|||
this.onContextMenu = this.onContextMenu.bind(this);
|
||||
this.maybeScrollToBottom = this.maybeScrollToBottom.bind(this);
|
||||
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
this.resizeObserver = new ResizeObserver(entries => {
|
||||
// If we don't have the outputNode reference, or if the outputNode isn't connected
|
||||
// anymore, we disconnect the resize observer (componentWillUnmount is never called
|
||||
// on this component, so we have to do it here).
|
||||
|
@ -100,7 +101,7 @@ class ConsoleOutput extends Component {
|
|||
{ root: this.outputNode, threshold: [0.5] }
|
||||
);
|
||||
|
||||
this.resizeObserver.observe(this.outputNode);
|
||||
this.resizeObserver.observe(this.getElementToObserve());
|
||||
|
||||
const { serviceContainer, onFirstMeaningfulPaint, dispatch } = this.props;
|
||||
serviceContainer.attachRefToWebConsoleUI("outputScroller", this.outputNode);
|
||||
|
@ -119,6 +120,10 @@ class ConsoleOutput extends Component {
|
|||
}
|
||||
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
if (nextProps.editorMode !== this.props.editorMode) {
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
const { outputNode } = this;
|
||||
if (!outputNode?.lastChild) {
|
||||
// Force a scroll to bottom when messages are added to an empty console.
|
||||
|
@ -166,11 +171,15 @@ class ConsoleOutput extends Component {
|
|||
(this.scrolledToBottom && visibleMessagesDelta > 0 && !isOpeningGroup);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
componentDidUpdate(prevProps) {
|
||||
this.maybeScrollToBottom();
|
||||
if (this?.outputNode?.lastChild) {
|
||||
this.lastMessageIntersectionObserver.observe(this.outputNode.lastChild);
|
||||
}
|
||||
|
||||
if (prevProps.editorMode !== this.props.editorMode) {
|
||||
this.resizeObserver.observe(this.getElementToObserve());
|
||||
}
|
||||
}
|
||||
|
||||
maybeScrollToBottom() {
|
||||
|
@ -187,6 +196,15 @@ class ConsoleOutput extends Component {
|
|||
this.scrolledToBottom = true;
|
||||
}
|
||||
|
||||
getElementToObserve() {
|
||||
// In inline mode, we need to observe the output node parent, which contains both the
|
||||
// output and the input, so we don't trigger the resizeObserver callback when only the
|
||||
// output size changes (e.g. when a network request is expanded).
|
||||
return this.props.editorMode
|
||||
? this.outputNode
|
||||
: this.outputNode?.parentNode;
|
||||
}
|
||||
|
||||
onContextMenu(e) {
|
||||
this.props.serviceContainer.openContextMenu(e);
|
||||
e.stopPropagation();
|
||||
|
|
|
@ -235,26 +235,6 @@ add_task(async function() {
|
|||
"The console is scrolled to the bottom after a repeated message"
|
||||
);
|
||||
|
||||
info(
|
||||
"Check that typing a multiline expression in the input keep the output scrolled to bottom"
|
||||
);
|
||||
await setInputValue(hud, "hello\nworld\n!\n");
|
||||
// Wait until the output is scrolled to the bottom.
|
||||
await waitFor(
|
||||
() => isScrolledToBottom(outputContainer),
|
||||
"Output does not scroll to the bottom after typing a multiline expression"
|
||||
);
|
||||
ok(
|
||||
true,
|
||||
"The console is scrolled to the bottom after typing a multiline expression in the input"
|
||||
);
|
||||
|
||||
await setInputValue(hud, "");
|
||||
ok(
|
||||
isScrolledToBottom(outputContainer),
|
||||
"The console is scrolled to the bottom after clearing the input"
|
||||
);
|
||||
|
||||
info(
|
||||
"Check that switching between editor and inline mode keep the output scrolled to bottom"
|
||||
);
|
||||
|
@ -280,6 +260,35 @@ add_task(async function() {
|
|||
true,
|
||||
"The console is scrolled to the bottom after switching back to inline mode"
|
||||
);
|
||||
|
||||
info(
|
||||
"Check that expanding a large object does not scroll the output to the bottom"
|
||||
);
|
||||
// Clear the output so we only have the object
|
||||
await clearOutput(hud);
|
||||
// Evaluate an object with a hundred properties
|
||||
const result = await executeAndWaitForMessage(
|
||||
hud,
|
||||
`Array.from({length: 100}, (_, i) => i)
|
||||
.reduce(
|
||||
(acc, item) => {acc["item-" + item] = item; return acc;},
|
||||
{}
|
||||
)`,
|
||||
"Object",
|
||||
".message.result"
|
||||
);
|
||||
// Expand the object
|
||||
result.node.querySelector(".arrow").click();
|
||||
// Wait until we have 102 nodes (the root node, 100 properties + <prototype>)
|
||||
await waitFor(() => result.node.querySelectorAll(".node").length === 102);
|
||||
// wait for a bit to give time to the resize observer callback to be triggered
|
||||
await wait(500);
|
||||
ok(hasVerticalOverflow(outputContainer), "The output does overflow");
|
||||
is(
|
||||
isScrolledToBottom(outputContainer),
|
||||
false,
|
||||
"The output was not scrolled to the bottom"
|
||||
);
|
||||
});
|
||||
|
||||
function hasVerticalOverflow(container) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче