From c7cbbf85b098d146a4de03fe1b1af9b2d4b138d3 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 23 Oct 2026 10:55:04 +0200 Subject: [PATCH 1/3] [O2B-1638] Adapt the filters button, the filters panel and the header of the overview pages to phones and tablets On tablets and phones the filters panel is displayed on the whole width of the screen and, on phones, each filter label is displayed above its input. On phones the header of the overview pages wraps on several lines instead of squeezing and clipping its buttons. The filters buttons, the panel and the header use a smaller font. They are shared by every overview page using the filters panel component or the header-container class. --- lib/public/app.css | 62 +++++++++++++++++++ .../Filters/common/filtersPanelPopover.js | 11 ++-- test/public/logs/overview.test.js | 41 ++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/lib/public/app.css b/lib/public/app.css index 4fe92017c9..e05e3776d3 100644 --- a/lib/public/app.css +++ b/lib/public/app.css @@ -992,6 +992,68 @@ label { } } +/* The filters buttons and panel, and the header of the overview pages containing them, use a smaller font, as the pagination does */ +.filters-toolbar, +.filters-panel, +.header-container { + font-size: 0.875rem; +} + +.filters-panel { + width: var(--ui-component-large); +} + +/* + * Filters panel on tablets and phones: the panel is displayed below the navbar on the whole width of the screen + * (the popover position computed in javascript is overridden) and each filter label is displayed above its input + */ +@media screen and (max-width: 1023px) { + .popover.filters-popover { + position: fixed !important; + top: 56px !important; + left: 8px !important; + right: 8px !important; + width: auto !important; + max-width: none !important; + max-height: 75vh; + } + + .filters-panel { + width: 100%; + box-sizing: border-box; + } +} + +@media screen and (max-width: 767px) { + /* The filters button, the active filters indicator and the other buttons and quick filters wrap on several lines instead of being squeezed */ + .header-container { + max-height: none; + flex-wrap: wrap; + row-gap: 8px; + } + + .header-container > .flex-row { + flex-wrap: wrap; + row-gap: 8px; + } + + .header-container .active-filters-indicator, + .header-container label { + white-space: nowrap; + } + + .filters-panel .filter-row { + flex-direction: column; + align-items: stretch; + row-gap: 4px; + } + + .filters-panel .filter-label, + .filters-panel .filter-input { + width: 100%; + } +} + /* xl */ @media screen and (min-width: 1920px) { .columns-3-xl { diff --git a/lib/public/components/Filters/common/filtersPanelPopover.js b/lib/public/components/Filters/common/filtersPanelPopover.js index 404841bbe0..c9cb0d4ea1 100644 --- a/lib/public/components/Filters/common/filtersPanelPopover.js +++ b/lib/public/components/Filters/common/filtersPanelPopover.js @@ -90,12 +90,12 @@ export const filtersSection = (filteringModel, filtersConfiguration, { profile: .map(([columnKey, { name, filterTooltip, filter }]) => name ? [ - h(`.flex-row.items-baseline.${columnKey}-filter`, [ - h('.w-30.f5.flex-row.items-center.g2', [ + h(`.flex-row.items-baseline.filter-row.${columnKey}-filter`, [ + h('.w-30.f5.flex-row.items-center.g2.filter-label', [ name, filterTooltip ? tooltip(info(), filterTooltip) : null, ]), - h('.w-70', typeof filter === 'function' ? filter(filteringModel) : filter), + h('.w-70.filter-input', typeof filter === 'function' ? filter(filteringModel) : filter), ]), ] : typeof filter === 'function' ? filter(filteringModel) : filter), @@ -114,7 +114,7 @@ const filtersToggleContent = ( filteringModel, filtersConfiguration, configuration = {}, -) => h('.w-l.flex-column.p3.g3', [ +) => h('.filters-panel.flex-column.p3.g3', [ filtersToggleContentHeader(filteringModel), filtersSection(filteringModel, filtersConfiguration, configuration), ]); @@ -134,6 +134,7 @@ const filtersPanelButton = (filteringModel, filtersConfiguration, configuration) { ...PopoverTriggerPreConfiguration.click, anchor: PopoverAnchors.RIGHT_START, + popoverClass: ['filters-popover'], }, ); @@ -209,7 +210,7 @@ export const filtersPanelPopover = (filteringModel, filtersConfiguration, config const hasActiveFilters = filteringModel.isAnyFilterActive(); return h( - '.flex-row.items-center.btn-group', + '.flex-row.items-center.btn-group.filters-toolbar', [ filtersPanelButton(filteringModel, filtersConfiguration, configuration), DropdownComponent( diff --git a/test/public/logs/overview.test.js b/test/public/logs/overview.test.js index a735e21dbc..bc61769581 100644 --- a/test/public/logs/overview.test.js +++ b/test/public/logs/overview.test.js @@ -205,6 +205,47 @@ module.exports = () => { } }); + it('should display the filters panel on the whole width of small screens', async () => { + const getFiltersPanelLayout = () => page.$eval('.filters-popover', (popover) => { + const { left, right } = popover.getBoundingClientRect(); + const filterRow = popover.querySelector('.filter-row'); + return { + fitsInViewport: left >= 0 && right <= document.documentElement.clientWidth, + labelIsAboveInput: window.getComputedStyle(filterRow).flexDirection === 'column', + }; + }); + + try { + await page.setViewport({ width: 375, height: 812, deviceScaleFactor: 1 }); + await goToPage(page, 'log-overview'); + await pressElement(page, '#openFilterToggle'); + await page.waitForSelector('.filters-popover .filter-row'); + expect(await getFiltersPanelLayout()).to.eql({ fitsInViewport: true, labelIsAboveInput: true }); + + await page.setViewport({ width: 800, height: 1024, deviceScaleFactor: 1 }); + expect(await getFiltersPanelLayout()).to.eql({ fitsInViewport: true, labelIsAboveInput: false }); + } finally { + await page.setViewport({ width: 1400, height: 940, deviceScaleFactor: 1 }); + } + }); + + it('should let the header of the page wrap instead of being squeezed on small screens', async () => { + try { + await page.setViewport({ width: 375, height: 812, deviceScaleFactor: 1 }); + await goToPage(page, 'log-overview'); + await page.waitForSelector('#main-action-bar'); + + const { overflows, maxHeight, flexWrap } = await page.$eval('#main-action-bar', (header) => ({ + overflows: header.scrollWidth > header.clientWidth, + maxHeight: window.getComputedStyle(header).maxHeight, + flexWrap: window.getComputedStyle(header).flexWrap, + })); + expect({ overflows, maxHeight, flexWrap }).to.eql({ overflows: false, maxHeight: 'none', flexWrap: 'wrap' }); + } finally { + await page.setViewport({ width: 1400, height: 940, deviceScaleFactor: 1 }); + } + }); + it('can sort by columns in ascending and descending manners', async () => { // Close the filter panel await waitForFirstRowToHaveId(page, 'row119'); From a8eaa712bc198e40d5acbbf18295704edfc2601f Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 23 Oct 2026 11:06:56 +0200 Subject: [PATCH 2/3] [O2B-1638] Apply the header rules to any row containing the filters button and keep tables from widening the page on phones and tablets Seven of the thirteen pages using the filters panel do not use the header-container class for their header, so the header rules are also applied to the row directly containing the filters button group, whatever its classes, and to the flex rows nested in it. A table wider than the screen now scrolls inside its own box instead of widening the layout viewport, in which the fixed filters panel is positioned and was displayed partly out of the screen. --- lib/public/app.css | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/public/app.css b/lib/public/app.css index e05e3776d3..9fe5a6a7f1 100644 --- a/lib/public/app.css +++ b/lib/public/app.css @@ -992,10 +992,14 @@ label { } } -/* The filters buttons and panel, and the header of the overview pages containing them, use a smaller font, as the pagination does */ +/* + * The filters buttons and panel, and the header of the overview pages containing them, use a smaller font, as the pagination does. + * The header is the row directly containing the filters buttons, or the header-container used by most of the overview pages + */ .filters-toolbar, .filters-panel, -.header-container { +.header-container, +.flex-row:has(> .filters-toolbar) { font-size: 0.875rem; } @@ -1008,6 +1012,17 @@ label { * (the popover position computed in javascript is overridden) and each filter label is displayed above its input */ @media screen and (max-width: 1023px) { + /* + * A table wider than the screen scrolls inside its own box instead of widening the whole page: otherwise the layout viewport of the + * phone gets wider than the screen, and the filters panel, positioned against it, is partly displayed out of the screen. + * Tables having their own scroll wrapper (the logs one) are left untouched + */ + table.table:not(.logs-table) { + display: block; + max-width: 100%; + overflow-x: auto; + } + .popover.filters-popover { position: fixed !important; top: 56px !important; @@ -1026,19 +1041,23 @@ label { @media screen and (max-width: 767px) { /* The filters button, the active filters indicator and the other buttons and quick filters wrap on several lines instead of being squeezed */ - .header-container { + .header-container, + .flex-row:has(> .filters-toolbar) { max-height: none; flex-wrap: wrap; row-gap: 8px; } - .header-container > .flex-row { + .header-container .flex-row, + .flex-row:has(> .filters-toolbar) .flex-row { flex-wrap: wrap; row-gap: 8px; } .header-container .active-filters-indicator, - .header-container label { + .header-container label, + .flex-row:has(> .filters-toolbar) .active-filters-indicator, + .flex-row:has(> .filters-toolbar) label { white-space: nowrap; } From 8e514622092b356bffe0e1d39d8704d0e3fd9d2d Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 23 Oct 2026 11:30:12 +0200 Subject: [PATCH 3/3] [O2B-1638] Do not click on a link of the previously opened popover in the environments overview test In the test displaying the dropdown links, the popover of the second environment was opened with a real mouse click while the popover of the first one was still opened. Depending on the layout, the click landed on one of its external links, opening a new tab: the page of the test then stopped rendering, and the next click of the suite (opening the filters panel) hung until the protocol timeout. The second popover is now opened with a javascript click, as the first one. --- test/public/envs/overview.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/public/envs/overview.test.js b/test/public/envs/overview.test.js index fe5671206e..fccf684d4e 100644 --- a/test/public/envs/overview.test.js +++ b/test/public/envs/overview.test.js @@ -220,7 +220,9 @@ module.exports = () => { // Not running env envId = 'EIDO13i3D'; - await pressElement(page, `tr[id='row${envId}'] .popover-trigger`); + // The popover of the previous environment is still opened: with a real mouse click, depending on the layout, the click could land on + // one of its links (opening an external page in a new tab, which stops the page from rendering and hangs the next tests) + await pressElement(page, `tr[id='row${envId}'] .popover-trigger`, true); popover = await getPopoverSelector(await page.waitForSelector(`tr[id='row${envId}'] .popover-trigger`)); await expectLink(page, `${popover} :nth-child(1 of .external-link)`, {