Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5fcb148
Keep autoscroll enabled when list shrinks on switch to live mode
isaachilly Sep 24, 2026
ef4e85f
Add test to catch disabling of autoscroll on switch to live mode
isaachilly Sep 24, 2026
cb25f0d
Merge branch 'dev' into bug/ILG/OGUI-1924/Fix-incorrectly-called-disa…
isaachilly Sep 25, 2026
976c684
Clarify scroll handling
isaachilly Sep 25, 2026
0bed3c9
Add logs table autoscroll regression tests
isaachilly Sep 25, 2026
4e0f3f6
Fix commented out tests
isaachilly Sep 25, 2026
70c9dc2
Stabilize logs table autoscroll tests
isaachilly Sep 25, 2026
5e96120
Fix failing test due to context menu overlay swallowing clicks
isaachilly Sep 25, 2026
5c32013
Keep test setup consistent and testing through UI as much as possible
isaachilly Sep 25, 2026
1b0658a
Testing what CI likes or not
isaachilly Sep 25, 2026
5a5ced5
Refactor autoscroll assertions in logs table tests for clarity and ef…
isaachilly Sep 25, 2026
11f05c5
Make scroll check clearer
isaachilly Sep 27, 2026
04f6cbe
Fix JSDoc comment
isaachilly Sep 27, 2026
50c8c9b
Refactor autoscroll tests
isaachilly Sep 27, 2026
eb921bd
Updating comments
isaachilly Sep 28, 2026
7cd80a4
Don't use onTableScroll for resizes
isaachilly Sep 28, 2026
29acaeb
Refine log table scroll viewport updates
isaachilly Sep 28, 2026
aaad543
Derive UI state from model
isaachilly Sep 29, 2026
060078b
Merge branch 'improv/ILG/OGUI-1926/Clean-up-logic-for-query-buttons' …
isaachilly Sep 29, 2026
8847913
Improve autoscroll behavior and update status bar display in live mode
isaachilly Sep 30, 2026
0b54150
Notify on websocket authentication success
isaachilly Sep 30, 2026
2932ce7
Merge branch 'improv/ILG/OGUI-1926/Clean-up-logic-for-query-buttons' …
isaachilly Sep 30, 2026
19b70fc
Merge branch 'improv/ILG/OGUI-1926/Clean-up-logic-for-query-buttons' …
isaachilly Sep 30, 2026
ea3cc2b
Improve live autoscroll reactivation UX
isaachilly Oct 1, 2026
1caead1
Update autoscroll UI tests
isaachilly Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions InfoLogger/public/log/Log.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,11 +575,12 @@ export default class Log extends Observable {
}

/**
* Enable or disable auto-scroll for live mode, a checkbox is used to control it
* Enable auto-scroll and focus the log table
*/
toggleAutoScroll() {
this.autoScrollLive = !this.autoScrollLive;
enableAutoScrollLive() {
this.autoScrollLive = true;
this.dom.table.focus();
this.dom.table.scrollTo(0, this.dom.table.scrollHeight);
this.notify();
}

Expand Down
2 changes: 1 addition & 1 deletion InfoLogger/public/log/commandLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const LIVE_BUTTON_TYPES_BY_MODE = {
export const commandLogs = (model) => [
userActionsDropdown(model),
h('', interactionModesGroupButton(model)),
h('', h('button.btn', { onclick: () => model.log.empty(), style: 'font-weight: bold' }, 'Clear')),
h('', h('button.btn', { id: 'clear-button', onclick: () => model.log.empty(), style: 'font-weight: bold' }, 'Clear')),
h('.btn-group', [
h('button.btn', {
disabled: !model.log.list.length,
Expand Down
23 changes: 18 additions & 5 deletions InfoLogger/public/log/statusBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/

import { h } from '/js/src/index.js';
import { MODE } from '../constants/mode.const.js';

/**
* Bottom bar, showing status of the log's list and its details,
Expand Down Expand Up @@ -96,11 +97,23 @@ const applicationMessage = (model) => model.log.list.length > model.log.applicat
* @returns {vnode} - the view of the application options
*/
const applicationOptions = (model) => [
h('label.d-inline', { title: 'Scroll down in live mode on new log incoming' }, h('input', {
type: 'checkbox',
checked: model.log.autoScrollLive,
onchange: () => model.log.toggleAutoScroll(),
}), ' Autoscroll'),
model.log.activeMode === MODE.LIVE.RUNNING ? h(
'label.d-inline',
{
title: model.log.autoScrollLive
? 'Autoscroll is active'
: 'Click here / Scroll down to the bottom of the screen to reactivate',
},
h(
'span',
{
id: 'status-bar-auto-scroll',
className: model.log.autoScrollLive ? 'success' : 'warning actionable-icon',
onclick: model.log.autoScrollLive ? undefined : () => model.log.enableAutoScrollLive(),
},
model.log.autoScrollLive ? 'Autoscroll Active' : '⚠️ Autoscroll Inactive',
),
) : null,
h('span.mh1'),
h('label.d-inline', { title: 'Show details of selected log' }, h('input', {
type: 'checkbox',
Expand Down
140 changes: 125 additions & 15 deletions InfoLogger/public/log/tableLogsContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,30 +203,130 @@ const tableContainerHooks = (model) => ({
* @param {vnode} vnode - the vnode of the element
*/
oncreate(vnode) {
const container = vnode.dom;

/**
* THis handler allow to notify model of element scrolling change (.tableLogsContent)
* Update the viewport size and scroll position in the model
*/
const onTableScroll = () => {
const container = vnode.dom;
const updateViewport = () => {
const { height } = container.getBoundingClientRect();
const scrollTop = Math.max(container.scrollTop, 0); // cancel negative position due to Safari bounce scrolling
if (container.scrollTop < model.log.scrollTop) {
model.log.autoScrollLive = false; // stop auto-scrolling if user scrolls up
}
model.log.setScrollTop(scrollTop, height);
};

// call the function when scrolling is updated
vnode.dom.addEventListener('scroll', onTableScroll);
model.log.dom.table = vnode.dom;
/**
* Whether the viewport is at the bottom of the table, <=1 tolerates fractional heights (zoom / HiDPI)
* @returns {boolean} true if at the bottom
*/
const isAtBottom = () => container.scrollHeight - Math.max(container.scrollTop, 0) - container.clientHeight <= 1;

/**
* Record the direction of a user scroll; scrolling up disables auto-scroll straight away.
* Scroll events alone cannot tell a user scroll from a layout change (zoom, new logs, table emptied),
* so the user's intent is taken from their input instead.
* @param {boolean} isUp - true if the user scrolls towards older logs
*/
const onUserScroll = (isUp) => {
container.isUserScrollingDown = !isUp;
if (isUp && model.log.autoScrollLive && container.scrollTop > 0) {
model.log.autoScrollLive = false;
model.notify();
}
};

/**
* Resume auto-scroll when the user scrolls to the bottom in live mode.
* Called from the input itself rather than waiting for the scroll to land, as a smooth scroll
* animation targets the bottom at the time of the input and falls short if logs arrive meanwhile.
*/
const resumeAutoScroll = () => {
if (model.log.isLiveModeRunning() && !model.log.autoScrollLive) {
model.log.autoScrollLive = true;
model.notify();
}
};

/**
* Mouse wheel and touchpad scrolling
* @param {WheelEvent} e - wheel event
*/
const onWheel = (e) => {
// ctrl/cmd + wheel is zoom, handled by Model
if (e.ctrlKey || e.metaKey) {
return;
}
onUserScroll(e.deltaY < 0);
const maxScrollTop = container.scrollHeight - container.clientHeight;
if (e.deltaY > 0 && container.scrollTop + e.deltaY >= maxScrollTop - 1) {
resumeAutoScroll();
}
};

/**
* Keyboard scrolling, arrow keys are handled by Model to move the selected log
* @param {KeyboardEvent} e - keyboard event
*/
const onKeyDown = (e) => {
if (e.target.tagName.toLowerCase() === 'input') {
return;
}
if (e.key === 'PageUp' || e.key === 'Home') {
onUserScroll(true);
} else if (e.key === 'End') {
onUserScroll(false);
resumeAutoScroll();
} else if (e.key === 'PageDown') {
onUserScroll(false);
}
};

/**
* A pointer pressed on the container itself rather than a row is on the scrollbar
* @param {PointerEvent} e - pointer event
*/
const onPointerDown = (e) => {
container.isDraggingScrollbar = e.target === container;
};

/**
* End of a scrollbar drag
*/
const onPointerUp = () => {
container.isDraggingScrollbar = false;
};

/**
* Re-enable auto-scroll when the user reaches the bottom, then update the viewport.
* Disabling is done by the user input handlers above.
*/
const onTableScroll = () => {
// programmatic scroll is set when jumping to a selected log (e.g. the error navigation buttons)
if (container.isProgrammaticScroll) {
container.isProgrammaticScroll = false;
} else if (model.log.isLiveModeRunning()) {
if (container.isDraggingScrollbar) {
model.log.autoScrollLive = isAtBottom();
} else if (container.isUserScrollingDown && isAtBottom()) {
model.log.autoScrollLive = true;
}
}
updateViewport();
};

container.addEventListener('scroll', onTableScroll);
container.addEventListener('wheel', onWheel, { passive: true });
container.addEventListener('pointerdown', onPointerDown);
window.addEventListener('pointerup', onPointerUp);
window.addEventListener('keydown', onKeyDown);
model.log.dom.table = container;
// setup window size listener - view needs redraw for smart scrolling
window.addEventListener('resize', onTableScroll);
window.addEventListener('resize', updateViewport);

// remember this function for later (destroy)
vnode.dom.onTableScroll = onTableScroll;
// remember these functions for later (destroy)
Object.assign(container, { onTableScroll, onWheel, onPointerDown, onPointerUp, onKeyDown, updateViewport });

// call the function once on next frame when we know sizes
onTableScroll();
updateViewport();
},

/**
Expand All @@ -242,8 +342,13 @@ const tableContainerHooks = (model) => ({
* @param {vnode} vnode - the vnode of the element
*/
ondestroy(vnode) {
vnode.dom.removeEventListener('scroll', vnode.dom.onTableScroll);
window.removeEventListener('resize', vnode.dom.onTableScroll);
const container = vnode.dom;
container.removeEventListener('scroll', container.onTableScroll);
container.removeEventListener('wheel', container.onWheel);
container.removeEventListener('pointerdown', container.onPointerDown);
window.removeEventListener('pointerup', container.onPointerUp);
window.removeEventListener('keydown', container.onKeyDown);
window.removeEventListener('resize', container.updateViewport);
},
});

Expand Down Expand Up @@ -281,7 +386,12 @@ const autoscrollManager = (model, vnode) => {
const index = model.log.list.indexOf(model.log.item);
const positionRow = model.log.rowHeight * index;
const halfView = model.log.scrollHeight / 2;
const previousScrollTop = vnode.dom.scrollTop;
vnode.dom.scrollTo(0, positionRow - halfView);
// scrollTo updates scrollTop synchronously; the event fires later and only if the position moved
if (vnode.dom.scrollTop !== previousScrollTop) {
vnode.dom.isProgrammaticScroll = true;
}
}

// Save the fact that we changed `item`
Expand Down
1 change: 1 addition & 0 deletions InfoLogger/test/mocha-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ describe('InfoLogger', function () {
require('./public/status-bar-mocha');
require('./public/zoom.mocha');
require('./public/log-context-menu-mocha');
require('./public/logs-table-mocha');

after(async () => {
await browser.close();
Expand Down
4 changes: 4 additions & 0 deletions InfoLogger/test/public/live-mode-mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ describe('Live Mode test-suite', async () => {
({ helpers: { baseUrl }, page } = test);
});

after(async () => {
await page.evaluate(() => model.log.liveStop('Query'));
});

it('should successfully go to homepage with predefined filters', async () => {
await page.goto(baseUrl, { waitUntil: 'networkidle0' });
const location = await page.evaluate(() => window.location);
Expand Down
4 changes: 4 additions & 0 deletions InfoLogger/test/public/log-context-menu-mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ describe('Cell Context Menu', async () => {
});
});

after(async () => {
await page.evaluate(() => model.log.contextMenu.hide());
});

describe('Menu visibility', async () => {
it('should show context menu on right-click', async () => {
await page.evaluate(() => {
Expand Down
Loading
Loading