diff --git a/InfoLogger/public/log/Log.js b/InfoLogger/public/log/Log.js index 03608b52c..98f334a32 100644 --- a/InfoLogger/public/log/Log.js +++ b/InfoLogger/public/log/Log.js @@ -575,11 +575,11 @@ export default class Log extends Observable { } /** - * Enable or disable auto-scroll for live mode, a checkbox is used to control it + * Set auto-scroll for live mode and notify the UI + * @param {boolean} enabled - whether auto-scroll should be enabled */ - toggleAutoScroll() { - this.autoScrollLive = !this.autoScrollLive; - this.dom.table.focus(); + setAutoScrollLive(enabled) { + this.autoScrollLive = enabled; this.notify(); } diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index 3d99f4198..d72286c29 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -12,11 +12,14 @@ * or submit itself to any jurisdiction. */ -import { h } from '/js/src/index.js'; +import { + h, + iconWarning, +} from '/js/src/index.js'; /** * Bottom bar, showing status of the log's list and its details, - * some application messages and some basic options like auto-scroll checkbox. + * some application messages and some basic options like auto-scroll label/button. * @param {Model} model - root model of the application * @returns {vnode} - the view of the bottom bar */ @@ -95,21 +98,35 @@ const applicationMessage = (model) => model.log.list.length > model.log.applicat * @param {Model} model - root model of the application * @returns {vnode} - the view of the application options */ -const applicationOptions = (model) => [ - model.log.isLiveModeRunning() && [ - h('label.d-inline', { title: 'Scroll down in live mode on new log incoming' }, h('input', { +const applicationOptions = (model) => { + const { log } = model; + const autoScrollEnabled = log.autoScrollLive; + return [ + log.isLiveModeRunning() && [ + h( + `button.btn.btn-sm${autoScrollEnabled ? '.btn-success' : '.btn-warning'}`, + { + id: 'status-bar-auto-scroll', + title: autoScrollEnabled ? 'Click here to disable autoscroll' : 'Click here to re-enable autoscroll', + onclick: (e) => { + e.currentTarget.blur(); + log.setAutoScrollLive(!autoScrollEnabled); + }, + }, + [ + !autoScrollEnabled && iconWarning(), + h('span.d-inline', autoScrollEnabled ? 'Autoscroll Enabled' : 'Autoscroll Disabled'), + ], + ), + h('span.mh1'), + ], + h('label.d-inline', { title: 'Show details of selected log' }, h('input', { type: 'checkbox', - checked: model.log.autoScrollLive, - onchange: () => model.log.toggleAutoScroll(), - }), ' Autoscroll'), - h('span.mh1'), - ], - h('label.d-inline', { title: 'Show details of selected log' }, h('input', { - type: 'checkbox', - checked: model.inspectorEnabled, - onchange: () => model.toggleInspector(), - }), ' Inspector'), -]; + checked: model.inspectorEnabled, + onchange: () => model.toggleInspector(), + }), ' Inspector'), + ]; +}; /** * Status for query mode, showing how many logs are really in DB and how much time it took to load it diff --git a/InfoLogger/public/log/tableLogsContent.js b/InfoLogger/public/log/tableLogsContent.js index 2250078ca..13c822940 100644 --- a/InfoLogger/public/log/tableLogsContent.js +++ b/InfoLogger/public/log/tableLogsContent.js @@ -211,7 +211,7 @@ const tableContainerHooks = (model) => ({ 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.setAutoScrollLive(false); // stop auto-scrolling if user scrolls up } model.log.setScrollTop(scrollTop, height); }; @@ -270,6 +270,9 @@ const autoscrollManager = (model, vnode) => { return; } + // forget the last scrolled log so re-enabling autoscroll jumps to bottom straight away on a redraw + delete vnode.dom.dataset.lastLogId; + // Autoscroll to selected item if (model.log.item) { // Scroll only if we did not previously, save last try in DOM dataset diff --git a/InfoLogger/test/public/status-bar-mocha.js b/InfoLogger/test/public/status-bar-mocha.js index 4080ae337..bc11f6756 100644 --- a/InfoLogger/test/public/status-bar-mocha.js +++ b/InfoLogger/test/public/status-bar-mocha.js @@ -35,7 +35,7 @@ async function getSeverityCounts(page) { } describe('Status Bar test-suite', async () => { - const AUTOSCROLL_SELECTOR = '#status-bar-application-options label[title*="Scroll down"] input'; + const AUTOSCROLL_SELECTOR = '#status-bar-auto-scroll'; const INSPECTOR_SELECTOR = '#status-bar-application-options label[title*="Show details"] input'; const SQL_QUERY = 'SELECT * FROM `messages` WHERE `timestamp`>=? AND `severity` IN (?) ' + 'ORDER BY `TIMESTAMP` LIMIT 100000'; @@ -357,16 +357,39 @@ describe('Status Bar test-suite', async () => { await page.waitForSelector(AUTOSCROLL_SELECTOR, { timeout: 2000 }); }); - it('should toggle autoscroll when its checkbox is clicked', async () => { - await page.waitForSelector(AUTOSCROLL_SELECTOR, { timeout: 2000 }); - const checkedBefore = await page.$eval(AUTOSCROLL_SELECTOR, (el) => el.checked); + it('should show correct label when autoscroll is enabled', async () => { + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Enabled'); + }); + + it('should show correct label when autoscroll is disabled', async () => { + await page.evaluate(() => { + model.log.setAutoScrollLive(false); + }); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); + }); + + it('should disable auto-scroll when clicked whilst auto-scroll is enabled', async () => { + await page.evaluate(() => { + model.log.setAutoScrollLive(true); + }); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Enabled'); + await page.click(AUTOSCROLL_SELECTOR); - const checkedAfter = await page.$eval(AUTOSCROLL_SELECTOR, (el) => el.checked); - const autoScrollModelValue = await page.evaluate(() => model.log.autoScrollLive); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); + assert.strictEqual(await page.evaluate(() => model.log.autoScrollLive), false); + }); + + it('should enable auto-scroll when clicked whilst auto-scroll is disabled', async () => { + await page.evaluate(() => { + model.log.setAutoScrollLive(false); + }); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); + + await page.click(AUTOSCROLL_SELECTOR); - assert.strictEqual(checkedAfter, !checkedBefore); - assert.strictEqual(autoScrollModelValue, checkedAfter); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Enabled'); + assert.strictEqual(await page.evaluate(() => model.log.autoScrollLive), true); }); }); });