From fb3604160e9e9a558599bf212a05b00a78615a49 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Tue, 20 Oct 2026 12:43:45 +0200 Subject: [PATCH] [O2B-1633] Fix shadow of tables inside scrollable wrappers not being displayed Tables displayed with horizontal and/or vertical scroll (for example on the runs per data pass and runs per simulation pass pages) are wrapped in `overflow: auto` containers, which clip the table box-shadow. Add a `table-shadow-room` class to the first wrapper of scrollable tables, leaving 6px on the sides and at the bottom (not at the top, to keep the sticky header in place) so that the shadow is visible. Add UI tests on the runs per data pass and runs per simulation pass pages. --- lib/public/app.css | 8 +++++++ lib/public/components/common/table/table.js | 1 + .../runsPerDataPass.overview.test.js | 24 +++++++++++++++++++ .../runsPerSimulationPass.overview.test.js | 24 +++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/lib/public/app.css b/lib/public/app.css index d2a524dfa2..abd8f54853 100644 --- a/lib/public/app.css +++ b/lib/public/app.css @@ -208,6 +208,14 @@ th.text-center, td.text-center { } +/* + * Scrollable wrappers clip everything overflowing their box, including the table shadow: + * leave some room on the sides and at the bottom (not at the top to not break the sticky header) to display it + */ +.table-shadow-room { + padding: 0 6px 6px; +} + .sticky-table-wrapper { flex: 1 1 auto; overflow-y: auto; diff --git a/lib/public/components/common/table/table.js b/lib/public/components/common/table/table.js index c481ec451e..206e26f5cc 100644 --- a/lib/public/components/common/table/table.js +++ b/lib/public/components/common/table/table.js @@ -156,6 +156,7 @@ export const table = ( optionalTableClassesExpression += tableClasses; const firstLevelWrapperClasses = + (scrollEnabled ? '.table-shadow-room' : '') + (verticalScrollEnabled ? '.sticky-table-wrapper' : '') + (singleWrapper && scrollEnabled ? '.scroll-auto' : ''); diff --git a/test/public/runsPerPeriodOrPass/runsPerDataPass.overview.test.js b/test/public/runsPerPeriodOrPass/runsPerDataPass.overview.test.js index 75d4a42e8a..43318e971b 100644 --- a/test/public/runsPerPeriodOrPass/runsPerDataPass.overview.test.js +++ b/test/public/runsPerPeriodOrPass/runsPerDataPass.overview.test.js @@ -97,6 +97,30 @@ module.exports = () => { expect(viewTitleElements).to.have.all.ordered.members(['Physics Runs', 'LHC22b_apass1']); }); + it('leaves enough room around the table to display its shadow', async () => { + await goToPage(page, 'runs-per-data-pass', { queryParameters: { dataPassId: 1 } }); + await page.waitForSelector('.sticky-table-wrapper table tbody tr'); + + // The scrollable wrappers clip what overflows them: the table shadow is visible only if there is some room around the table + const { shadow, spaceLeft, paddingRight, spaceBottom } = await page.evaluate(() => { + const table = document.querySelector('.sticky-table-wrapper table'); + const wrapper = document.querySelector('.sticky-table-wrapper'); + const tableBox = table.getBoundingClientRect(); + const wrapperBox = wrapper.getBoundingClientRect(); + return { + shadow: getComputedStyle(table).boxShadow, + spaceLeft: tableBox.left - wrapperBox.left, + // The table may overflow horizontally (scroll), so its right side can only be checked through the wrapper padding + paddingRight: parseFloat(getComputedStyle(wrapper).paddingRight), + spaceBottom: wrapperBox.bottom - tableBox.bottom, + }; + }); + expect(shadow).to.not.equal('none'); + expect(spaceLeft).to.be.at.least(5); + expect(paddingRight).to.be.at.least(5); + expect(spaceBottom).to.be.at.least(5); + }); + it('shows correct datatypes in respective columns', async () => { const commonColumnsValidators = { runNumber: (number) => !isNaN(number), diff --git a/test/public/runsPerPeriodOrPass/runsPerSimulationPass.overview.test.js b/test/public/runsPerPeriodOrPass/runsPerSimulationPass.overview.test.js index 58effa87e7..078ed2e792 100644 --- a/test/public/runsPerPeriodOrPass/runsPerSimulationPass.overview.test.js +++ b/test/public/runsPerPeriodOrPass/runsPerSimulationPass.overview.test.js @@ -94,6 +94,30 @@ module.exports = () => { expect(await headerBreadcrumbs[1].evaluate((element) => element.innerText)).to.be.equal('LHC23k6b'); }); + it('leaves enough room around the table to display its shadow', async () => { + await goToPage(page, 'runs-per-simulation-pass', { queryParameters: { simulationPassId: 2 } }); + await page.waitForSelector('.sticky-table-wrapper table tbody tr'); + + // The scrollable wrappers clip what overflows them: the table shadow is visible only if there is some room around the table + const { shadow, spaceLeft, paddingRight, spaceBottom } = await page.evaluate(() => { + const table = document.querySelector('.sticky-table-wrapper table'); + const wrapper = document.querySelector('.sticky-table-wrapper'); + const tableBox = table.getBoundingClientRect(); + const wrapperBox = wrapper.getBoundingClientRect(); + return { + shadow: getComputedStyle(table).boxShadow, + spaceLeft: tableBox.left - wrapperBox.left, + // The table may overflow horizontally (scroll), so its right side can only be checked through the wrapper padding + paddingRight: parseFloat(getComputedStyle(wrapper).paddingRight), + spaceBottom: wrapperBox.bottom - tableBox.bottom, + }; + }); + expect(shadow).to.not.equal('none'); + expect(spaceLeft).to.be.at.least(5); + expect(paddingRight).to.be.at.least(5); + expect(spaceBottom).to.be.at.least(5); + }); + it('shows correct datatypes in respective columns', async () => { const tableDataValidators = { runNumber: (number) => !isNaN(number),