From 73e2d60823f21fb91b39a9995972f24a88f1fdc7 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 15:54:15 +0100 Subject: [PATCH 01/11] feat: add search bar to the table header of the object tree page --- .../public/object/objectTreePage.js | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index f53b09d38..b0351584e 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -46,9 +46,15 @@ export default (model) => { const objectsLoaded = object.list; const objectsToDisplay = objectsLoaded.filter((qcObject) => qcObject.name.toLowerCase().includes(searchInput.toLowerCase())); - return virtualTable(model, 'main', objectsToDisplay); + return [ + tableSearchInput(model.object), + virtualTable(model, 'main', objectsToDisplay), + ]; } - return tableShow(model); + return [ + tableSearchInput(model.object), + tableShow(model), + ]; }, Failure: () => null, // Notification is displayed })), @@ -167,7 +173,17 @@ const tableShow = (model) => h('table.table.table-sm.text-no-select', [ h('thead', [h('tr', [h('th', 'Name')])]), h('tbody', [treeRows(model)]), - ]); + ]) + +const tableSearchInput = (qcObject) => + h('input.form-control.form-inline.m2', { + id: 'searchObjectTree', + placeholder: 'Search', + type: 'text', + value: qcObject.searchInput, + disabled: qcObject.queryingObjects ? true : false, + oninput: (e) => qcObject.search(e.target.value), + }) /** * Shows a list of lines of objects From c4b9b58866fecabb533c5b0b39cde5a09592c8d0 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 17:53:22 +0100 Subject: [PATCH 02/11] feat: add the collapse all button to the table header --- .../public/object/objectTreeHeader.js | 17 +---------- .../public/object/objectTreePage.js | 29 ++++++++++++++++--- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/QualityControl/public/object/objectTreeHeader.js b/QualityControl/public/object/objectTreeHeader.js index fe53637d8..e9787ef1b 100644 --- a/QualityControl/public/object/objectTreeHeader.js +++ b/QualityControl/public/object/objectTreeHeader.js @@ -13,7 +13,7 @@ */ import { h } from '/js/src/index.js'; -import { iconCollapseUp, iconArrowBottom, iconArrowTop } from '/js/src/icons.js'; +import { iconArrowBottom, iconArrowTop } from '/js/src/icons.js'; import { filterPanelToggleButton } from '../common/filters/filterViews.js'; /** @@ -56,21 +56,6 @@ export default function objectTreeHeader(qcObject, filterModel) { ]), ]), ' ', - h('button.btn', { - title: 'Close whole tree', - onclick: () => qcObject.tree.closeAll(), - disabled: Boolean(qcObject.searchInput), - }, iconCollapseUp()), - ' ', - h('input.form-control.form-inline.mh1.w-33', { - id: 'searchObjectTree', - placeholder: 'Search', - type: 'text', - value: qcObject.searchInput, - disabled: qcObject.queryingObjects ? true : false, - oninput: (e) => qcObject.search(e.target.value), - }), - ' ', ]), }; } diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index b0351584e..d053de268 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -12,7 +12,15 @@ * or submit itself to any jurisdiction. */ -import { h, iconBarChart, iconCaretRight, iconResizeBoth, iconCaretBottom, iconCircleX } from '/js/src/index.js'; +import { + h, + iconCollapseUp, + iconBarChart, + iconCaretRight, + iconResizeBoth, + iconCaretBottom, + iconCircleX +} from '/js/src/index.js'; import { spinner } from '../common/spinner.js'; import { draw } from '../common/object/draw.js'; import timestampSelectForm from './../common/timestampSelectForm.js'; @@ -47,12 +55,12 @@ export default (model) => { const objectsToDisplay = objectsLoaded.filter((qcObject) => qcObject.name.toLowerCase().includes(searchInput.toLowerCase())); return [ - tableSearchInput(model.object), + tableHeader(model.object), virtualTable(model, 'main', objectsToDisplay), ]; } return [ - tableSearchInput(model.object), + tableHeader(model.object), tableShow(model), ]; }, @@ -175,8 +183,21 @@ const tableShow = (model) => h('tbody', [treeRows(model)]), ]) +const tableHeader = (qcObject) => + h('.flex-row.w-100', [ + tableSearchInput(qcObject), + tableCollapseAll(qcObject), + ]) + +const tableCollapseAll = (qcObject) => + h('button.btn.m2', { + title: 'Close whole tree', + onclick: () => qcObject.tree.closeAll(), + disabled: Boolean(qcObject.searchInput), + }, iconCollapseUp()) + const tableSearchInput = (qcObject) => - h('input.form-control.form-inline.m2', { + h('input.form-control.form-inline.m2.flex-grow', { id: 'searchObjectTree', placeholder: 'Search', type: 'text', From 1b8eeabd88d2b15bacd971cfd119ae1a18675d15 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 19:38:05 +0100 Subject: [PATCH 03/11] feat: add reusable component for adding table sort buttons --- QualityControl/public/app.css | 17 +++++ .../public/common/enums/columnSort.enum.js | 24 +++++++ QualityControl/public/common/sortButton.js | 72 +++++++++++++++++++ QualityControl/public/object/QCObject.js | 13 +--- .../public/object/objectTreeHeader.js | 16 ----- .../public/object/objectTreePage.js | 18 ++++- 6 files changed, 130 insertions(+), 30 deletions(-) create mode 100644 QualityControl/public/common/enums/columnSort.enum.js create mode 100644 QualityControl/public/common/sortButton.js diff --git a/QualityControl/public/app.css b/QualityControl/public/app.css index 956128933..fe8b491dd 100644 --- a/QualityControl/public/app.css +++ b/QualityControl/public/app.css @@ -187,3 +187,20 @@ .whitespace-nowrap { white-space: nowrap; } + +.sort-button { + .hover-icon { + display: none; + opacity: 0.6; + } + + &:hover { + .current-icon { + display: none; + } + + .hover-icon { + display: inline-block; + } + } +} diff --git a/QualityControl/public/common/enums/columnSort.enum.js b/QualityControl/public/common/enums/columnSort.enum.js new file mode 100644 index 000000000..ba58e6619 --- /dev/null +++ b/QualityControl/public/common/enums/columnSort.enum.js @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +/** + * Enumeration for sort directions + * @enum {number} + * @readonly + */ +export const SortDirectionsEnum = Object.freeze({ + NONE: 0, + ASC: 1, + DESC: -1, +}); diff --git a/QualityControl/public/common/sortButton.js b/QualityControl/public/common/sortButton.js new file mode 100644 index 000000000..936d411fd --- /dev/null +++ b/QualityControl/public/common/sortButton.js @@ -0,0 +1,72 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { SortDirectionsEnum } from '../common/enums/columnSort.enum.js'; +import { h, iconCircleX, iconArrowBottom, iconArrowTop } from '/js/src/index.js'; + +/** + * Get the icon for the sort direction. + * @param {SortDirectionsEnum} direction - direction of the sort. + * @returns {vnode} the correct icon related to the direction. + */ +const getSortIcon = (direction) => { + if (direction === SortDirectionsEnum.ASC) { + return iconArrowTop(); + } + if (direction === SortDirectionsEnum.DESC) { + return iconArrowBottom(); + } + return iconCircleX(); +}; + +/** + * @callback SortClickCallback + * @param {string} label - The label of the column being sorted. + * @param {number} order - The next sort direction in the cycle. + * @param {vnode} icon - The VNode for the icon representing the next sort state. + * @returns {void} + */ + +/** + * Renders a sortable table header button that cycles through sort states. + * Displays the current sort icon and a preview icon of the next state on hover. + * @param {object} props - The component properties. + * @param {number} props.order - The current sort direction value from SortDirectionsEnum. + * @param {object|undefined} props.icon - The VNode/element for the current active sort icon. + * @param {string} props.label - The display text for the column header. + * @param {SortClickCallback} props.onclick - Callback triggered on click. + * @param {Array} [props.sortOptions] - Array of SortDirectionsEnum values defining the + * order of the sort cycle. Defaults to all enum values. + * @returns {object} A HyperScript VNode representing the sortable button. + */ +export const sortableTableHead = ({ + order, + icon, + label, + onclick, + sortOptions = [...Object.values(SortDirectionsEnum)] +}) => { + const currentIndex = sortOptions.indexOf(order); + const nextIndex = (currentIndex + 1) % sortOptions.length; + const nextSortOrder = sortOptions[nextIndex]; + const hoverIcon = getSortIcon(nextSortOrder); + + return h('button.btn.sort-button', { onclick: () => onclick(label, nextSortOrder, hoverIcon) }, [ + label, + h('span.icon-container', [ + h('span.current-icon', [order != SortDirectionsEnum.NONE ? icon : undefined]), + h('span.hover-icon', [getSortIcon(nextSortOrder)]) + ]) + ]); +} diff --git a/QualityControl/public/object/QCObject.js b/QualityControl/public/object/QCObject.js index 840215994..83f69e2a9 100644 --- a/QualityControl/public/object/QCObject.js +++ b/QualityControl/public/object/QCObject.js @@ -47,7 +47,6 @@ export default class QCObject extends BaseViewModel { title: 'Name', order: 1, icon: iconArrowTop(), - open: false, }; this.tree = new ObjectTree('database'); @@ -115,15 +114,6 @@ export default class QCObject extends BaseViewModel { this.notify(); } - /** - * Toggle the display of the sort by dropdown - * @returns {undefined} - */ - toggleSortDropdown() { - this.sortBy.open = !this.sortBy.open; - this.notify(); - } - /** * Computes the final list of objects to be seen by user depending on search input from user * If any of those changes, this method should be called to update the outputs. @@ -189,7 +179,7 @@ export default class QCObject extends BaseViewModel { this._computeFilters(); - this.sortBy = { field, title, order, icon, open: false }; + this.sortBy = { field, title, order, icon }; this.notify(); } @@ -253,7 +243,6 @@ export default class QCObject extends BaseViewModel { title: 'Name', order: 1, icon: iconArrowTop(), - open: false, }; this._computeFilters(); diff --git a/QualityControl/public/object/objectTreeHeader.js b/QualityControl/public/object/objectTreeHeader.js index e9787ef1b..742040c56 100644 --- a/QualityControl/public/object/objectTreeHeader.js +++ b/QualityControl/public/object/objectTreeHeader.js @@ -13,7 +13,6 @@ */ import { h } from '/js/src/index.js'; -import { iconArrowBottom, iconArrowTop } from '/js/src/icons.js'; import { filterPanelToggleButton } from '../common/filters/filterViews.js'; /** @@ -41,21 +40,6 @@ export default function objectTreeHeader(qcObject, filterModel) { rightCol: h('.w-25.flex-row.items-center.g2.justify-end', [ filterModel.isRunModeActivated ? null : filterPanelToggleButton(filterModel), - ' ', - h('.dropdown', { - id: 'sortTreeButton', title: 'Sort by', class: qcObject.sortBy.open ? 'dropdown-open' : '', - }, [ - h('button.btn', { - title: 'Sort by', - onclick: () => qcObject.toggleSortDropdown(), - }, [qcObject.sortBy.title, ' ', qcObject.sortBy.icon]), - h('.dropdown-menu.text-left', [ - sortMenuItem(qcObject, 'Name', 'Sort by name ASC', iconArrowTop(), 'name', 1), - sortMenuItem(qcObject, 'Name', 'Sort by name DESC', iconArrowBottom(), 'name', -1), - - ]), - ]), - ' ', ]), }; } diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index d053de268..0bd9311c6 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -19,7 +19,7 @@ import { iconCaretRight, iconResizeBoth, iconCaretBottom, - iconCircleX + iconCircleX, } from '/js/src/index.js'; import { spinner } from '../common/spinner.js'; import { draw } from '../common/object/draw.js'; @@ -28,6 +28,8 @@ import virtualTable from './virtualTable.js'; import { defaultRowAttributes, qcObjectInfoPanel } from '../common/object/objectInfoCard.js'; import { downloadButton } from '../common/downloadButton.js'; import { resizableDivider } from '../common/resizableDivider.js'; +import { SortDirectionsEnum } from '../common/enums/columnSort.enum.js'; +import { sortableTableHead } from '../common/sortButton.js'; /** * Shows a page to explore though a tree of objects with a preview on the right if clicked @@ -179,7 +181,19 @@ const statusBarRight = (model) => model.object.selected */ const tableShow = (model) => h('table.table.table-sm.text-no-select', [ - h('thead', [h('tr', [h('th', 'Name')])]), + h('thead', [ + h('tr', [ + h('th', sortableTableHead({ + order: model.object.sortBy.order, + icon: model.object.sortBy.icon, + label: 'Name', + sortOptions: [SortDirectionsEnum.ASC, SortDirectionsEnum.DESC], + onclick: (label, order, icon) => { + model.object.sortTree(label, 'name', order, icon) + } + })) + ]) + ]), h('tbody', [treeRows(model)]), ]) From 4273674da21d6296bdd0d0df23087ee59824d811 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 19:41:32 +0100 Subject: [PATCH 04/11] feat: add more margin on the sort direction icon --- QualityControl/public/common/sortButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QualityControl/public/common/sortButton.js b/QualityControl/public/common/sortButton.js index 936d411fd..f7363c6e4 100644 --- a/QualityControl/public/common/sortButton.js +++ b/QualityControl/public/common/sortButton.js @@ -64,7 +64,7 @@ export const sortableTableHead = ({ return h('button.btn.sort-button', { onclick: () => onclick(label, nextSortOrder, hoverIcon) }, [ label, - h('span.icon-container', [ + h('span.icon-container.mh1', [ h('span.current-icon', [order != SortDirectionsEnum.NONE ? icon : undefined]), h('span.hover-icon', [getSortIcon(nextSortOrder)]) ]) From fc4452f2c365aeeee451963258ee95df019a7e9b Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 19:46:39 +0100 Subject: [PATCH 05/11] feat: add sortable column to virtual table --- QualityControl/public/object/virtualTable.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/QualityControl/public/object/virtualTable.js b/QualityControl/public/object/virtualTable.js index 29ebda4d4..ddb5326f1 100644 --- a/QualityControl/public/object/virtualTable.js +++ b/QualityControl/public/object/virtualTable.js @@ -12,6 +12,8 @@ * or submit itself to any jurisdiction. */ +import { SortDirectionsEnum } from '../common/enums/columnSort.enum.js'; +import { sortableTableHead } from '../common/sortButton.js'; import { h, iconBarChart } from '/js/src/index.js'; let ROW_HEIGHT = 33.6; @@ -102,7 +104,15 @@ const objectFullRow = (model, item, location) => const tableHeader = () => h('table.table.table-sm.text-no-select', { style: 'margin-bottom:0', - }, h('thead', [h('tr', [h('th', 'Name')])])); + }, h('thead', [h('tr', [h('th', sortableTableHead({ + order: model.object.sortBy.order, + icon: model.object.sortBy.icon, + label: 'Name', + sortOptions: [SortDirectionsEnum.ASC, SortDirectionsEnum.DESC], + onclick: (label, order, icon) => { + model.object.sortTree(label, 'name', order, icon) + } + }))])])); /** * Set styles of the floating table and its position inside the big div .tableLogsContentPlaceholder From 4b06d78b300726c8c71f4e3428c1ab77fcddfebb Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 19:49:56 +0100 Subject: [PATCH 06/11] style: fix linting errors --- QualityControl/public/common/sortButton.js | 8 +++---- .../public/object/objectTreeHeader.js | 21 ++++-------------- .../public/object/objectTreePage.js | 16 +++++++------- QualityControl/public/object/virtualTable.js | 22 +++++++++++-------- 4 files changed, 29 insertions(+), 38 deletions(-) diff --git a/QualityControl/public/common/sortButton.js b/QualityControl/public/common/sortButton.js index f7363c6e4..76c24b633 100644 --- a/QualityControl/public/common/sortButton.js +++ b/QualityControl/public/common/sortButton.js @@ -55,7 +55,7 @@ export const sortableTableHead = ({ icon, label, onclick, - sortOptions = [...Object.values(SortDirectionsEnum)] + sortOptions = [...Object.values(SortDirectionsEnum)], }) => { const currentIndex = sortOptions.indexOf(order); const nextIndex = (currentIndex + 1) % sortOptions.length; @@ -66,7 +66,7 @@ export const sortableTableHead = ({ label, h('span.icon-container.mh1', [ h('span.current-icon', [order != SortDirectionsEnum.NONE ? icon : undefined]), - h('span.hover-icon', [getSortIcon(nextSortOrder)]) - ]) + h('span.hover-icon', [getSortIcon(nextSortOrder)]), + ]), ]); -} +}; diff --git a/QualityControl/public/object/objectTreeHeader.js b/QualityControl/public/object/objectTreeHeader.js index 742040c56..00b8b0447 100644 --- a/QualityControl/public/object/objectTreeHeader.js +++ b/QualityControl/public/object/objectTreeHeader.js @@ -38,22 +38,9 @@ export default function objectTreeHeader(qcObject, filterModel) { qcObject.objectsRemote.isSuccess() && h('span', `(${howMany})`), ]), - rightCol: h('.w-25.flex-row.items-center.g2.justify-end', [ - filterModel.isRunModeActivated ? null : filterPanelToggleButton(filterModel), - ]), + rightCol: h( + '.w-25.flex-row.items-center.g2.justify-end', + [filterModel.isRunModeActivated ? null : filterPanelToggleButton(filterModel)], + ), }; } - -/** - * Create a menu-item for sort-by dropdown - * @param {QcObject} qcObject - Model that manages the QCObject state. - * @param {string} shortTitle - title that gets displayed to the user - * @param {string} title - title that gets displayed to the user on hover - * @param {Icon} icon - svg icon to be used - * @param {string} field - field by which sorting should happen - * @param {number} order - {-1/1}/{DESC/ASC} - * @returns {vnode} - virtual node element - */ -const sortMenuItem = (qcObject, shortTitle, title, icon, field, order) => h('a.menu-item', { - title: title, style: 'white-space: nowrap;', onclick: () => qcObject.sortTree(shortTitle, field, order, icon), -}, [shortTitle, ' ', icon]); diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index 0bd9311c6..4ebc4425f 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -189,26 +189,26 @@ const tableShow = (model) => label: 'Name', sortOptions: [SortDirectionsEnum.ASC, SortDirectionsEnum.DESC], onclick: (label, order, icon) => { - model.object.sortTree(label, 'name', order, icon) - } - })) - ]) + model.object.sortTree(label, 'name', order, icon); + }, + })), + ]), ]), h('tbody', [treeRows(model)]), - ]) + ]); const tableHeader = (qcObject) => h('.flex-row.w-100', [ tableSearchInput(qcObject), tableCollapseAll(qcObject), - ]) + ]); const tableCollapseAll = (qcObject) => h('button.btn.m2', { title: 'Close whole tree', onclick: () => qcObject.tree.closeAll(), disabled: Boolean(qcObject.searchInput), - }, iconCollapseUp()) + }, iconCollapseUp()); const tableSearchInput = (qcObject) => h('input.form-control.form-inline.m2.flex-grow', { @@ -218,7 +218,7 @@ const tableSearchInput = (qcObject) => value: qcObject.searchInput, disabled: qcObject.queryingObjects ? true : false, oninput: (e) => qcObject.search(e.target.value), - }) + }); /** * Shows a list of lines of objects diff --git a/QualityControl/public/object/virtualTable.js b/QualityControl/public/object/virtualTable.js index ddb5326f1..c5e472f95 100644 --- a/QualityControl/public/object/virtualTable.js +++ b/QualityControl/public/object/virtualTable.js @@ -104,15 +104,19 @@ const objectFullRow = (model, item, location) => const tableHeader = () => h('table.table.table-sm.text-no-select', { style: 'margin-bottom:0', - }, h('thead', [h('tr', [h('th', sortableTableHead({ - order: model.object.sortBy.order, - icon: model.object.sortBy.icon, - label: 'Name', - sortOptions: [SortDirectionsEnum.ASC, SortDirectionsEnum.DESC], - onclick: (label, order, icon) => { - model.object.sortTree(label, 'name', order, icon) - } - }))])])); + }, h('thead', [ + h('tr', [ + h('th', sortableTableHead({ + order: model.object.sortBy.order, + icon: model.object.sortBy.icon, + label: 'Name', + sortOptions: [SortDirectionsEnum.ASC, SortDirectionsEnum.DESC], + onclick: (label, order, icon) => { + model.object.sortTree(label, 'name', order, icon); + }, + })), + ]), + ])); /** * Set styles of the floating table and its position inside the big div .tableLogsContentPlaceholder From 5078fd919fa67ecfef06c6ad6f81c90ad3a63565 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 19:58:02 +0100 Subject: [PATCH 07/11] feat: add hover title text to sort button --- QualityControl/public/common/sortButton.js | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/QualityControl/public/common/sortButton.js b/QualityControl/public/common/sortButton.js index 76c24b633..2690bf3bf 100644 --- a/QualityControl/public/common/sortButton.js +++ b/QualityControl/public/common/sortButton.js @@ -62,11 +62,20 @@ export const sortableTableHead = ({ const nextSortOrder = sortOptions[nextIndex]; const hoverIcon = getSortIcon(nextSortOrder); - return h('button.btn.sort-button', { onclick: () => onclick(label, nextSortOrder, hoverIcon) }, [ - label, - h('span.icon-container.mh1', [ - h('span.current-icon', [order != SortDirectionsEnum.NONE ? icon : undefined]), - h('span.hover-icon', [getSortIcon(nextSortOrder)]), - ]), - ]); + const directionLabel = Object.keys(SortDirectionsEnum).find((key) => SortDirectionsEnum[key] === nextSortOrder); + + return h( + 'button.btn.sort-button', + { + onclick: () => onclick(label, nextSortOrder, hoverIcon), + title: `Sort by ${directionLabel}` + }, + [ + label, + h('span.icon-container.mh1', [ + h('span.current-icon', [order != SortDirectionsEnum.NONE ? icon : undefined]), + h('span.hover-icon', [getSortIcon(nextSortOrder)]), + ]), + ] + ); }; From 8c9450f4a00196389935cf5a55364cb6d1de456c Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 20:15:21 +0100 Subject: [PATCH 08/11] test: fix object tree tests --- .../test/public/pages/object-tree.test.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/QualityControl/test/public/pages/object-tree.test.js b/QualityControl/test/public/pages/object-tree.test.js index 45788051d..4c4931b60 100644 --- a/QualityControl/test/public/pages/object-tree.test.js +++ b/QualityControl/test/public/pages/object-tree.test.js @@ -55,8 +55,8 @@ export const objectTreePageTests = async (url, page, timeout = 5000, testParent) }); await testParent.test('should have a button to sort by (default "Name" ASC)', async () => { - const sortByButtonTitle = await page.evaluate((path) => document.querySelector(path).title, '#sortTreeButton'); - strictEqual(sortByButtonTitle, 'Sort by'); + const sortByButtonTitle = await page.evaluate((path) => document.querySelector(path).title, '.btn.sort-button'); + strictEqual(sortByButtonTitle, 'Sort by DESC'); }); await testParent.test('should have first element in tree as "qc/test/object/1"', async () => { @@ -229,9 +229,7 @@ export const objectTreePageTests = async (url, page, timeout = 5000, testParent) ); await testParent.test('should sort list of histograms by name in descending order', async () => { - await page.locator('#sortTreeButton').click(); - const sortingByNameOptionPath = '#sortTreeButton > div > a:nth-child(2)'; - await page.locator(sortingByNameOptionPath).click(); + await page.locator('.btn.sort-button').click(); const sorted = await page.evaluate(() => ({ list: window.model.object.currentList, @@ -244,9 +242,8 @@ export const objectTreePageTests = async (url, page, timeout = 5000, testParent) }); await testParent.test('should sort list of histograms by name in ascending order', async () => { - await page.locator('#sortTreeButton').click(); - const sortingByNameOptionPath = '#sortTreeButton > div > a:nth-child(1)'; - await page.locator(sortingByNameOptionPath).click(); + await page.locator('.btn.sort-button').click(); + const sorted = await page.evaluate(() => ({ list: window.model.object.currentList, sort: window.model.object.sortBy, From 9c4bf1ac4be2de728f5cf304773c4ff2efb04505 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 21:03:02 +0100 Subject: [PATCH 09/11] fix: dom node errors when rerendering --- QualityControl/public/object/objectTreePage.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index 4ebc4425f..132895603 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -56,15 +56,15 @@ export default (model) => { const objectsLoaded = object.list; const objectsToDisplay = objectsLoaded.filter((qcObject) => qcObject.name.toLowerCase().includes(searchInput.toLowerCase())); - return [ + return h('', [ tableHeader(model.object), virtualTable(model, 'main', objectsToDisplay), - ]; + ]); } - return [ + return h('', [ tableHeader(model.object), tableShow(model), - ]; + ]); }, Failure: () => null, // Notification is displayed })), From b43ad11550c45c00be9269753134d6b8f5946bca Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 21:03:16 +0100 Subject: [PATCH 10/11] test: fix failing tests due to changes --- QualityControl/test/public/pages/object-tree.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QualityControl/test/public/pages/object-tree.test.js b/QualityControl/test/public/pages/object-tree.test.js index 4c4931b60..62b75bdee 100644 --- a/QualityControl/test/public/pages/object-tree.test.js +++ b/QualityControl/test/public/pages/object-tree.test.js @@ -34,7 +34,7 @@ export const objectTreePageTests = async (url, page, timeout = 5000, testParent) }); await testParent.test('should have a tree as a table', { timeout }, async () => { - const tableRowPath = 'section > div > div > div > table > tbody > tr'; + const tableRowPath = 'section > div > div > div > div > table > tbody > tr'; await page.waitForSelector(tableRowPath, { timeout: 1000 }); const rowsCount = await page.evaluate( (tableRowPath) => document.querySelectorAll(tableRowPath).length, @@ -44,12 +44,12 @@ export const objectTreePageTests = async (url, page, timeout = 5000, testParent) }); await testParent.test('should not preserve state if refreshed not in run mode', { timeout }, async () => { - const tbodyPath = 'section > div > div > div > table > tbody'; + const tbodyPath = 'section > div > div > div > div > table > tbody'; await page.locator(`${tbodyPath} > tr:nth-child(2)`).click(); await page.reload({ waitUntil: 'networkidle0' }); const rowCount = await page.evaluate(() => - document.querySelectorAll('section > div > div > div > table > tbody > tr').length); + document.querySelectorAll('section > div > div > div > div > table > tbody > tr').length); strictEqual(rowCount, 2); }); From ee02932777965ff9351f2ea57f9700085b592367 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 21:07:47 +0100 Subject: [PATCH 11/11] style: fix linting errors --- QualityControl/public/common/sortButton.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QualityControl/public/common/sortButton.js b/QualityControl/public/common/sortButton.js index 2690bf3bf..97e3197d4 100644 --- a/QualityControl/public/common/sortButton.js +++ b/QualityControl/public/common/sortButton.js @@ -68,7 +68,7 @@ export const sortableTableHead = ({ 'button.btn.sort-button', { onclick: () => onclick(label, nextSortOrder, hoverIcon), - title: `Sort by ${directionLabel}` + title: `Sort by ${directionLabel}`, }, [ label, @@ -76,6 +76,6 @@ export const sortableTableHead = ({ h('span.current-icon', [order != SortDirectionsEnum.NONE ? icon : undefined]), h('span.hover-icon', [getSortIcon(nextSortOrder)]), ]), - ] + ], ); };