From 8ea76d397e362ab196f6d019d7a2f13a5c6be507 Mon Sep 17 00:00:00 2001 From: Alejandro Mariscal Romero <87366244+mariscalromeroalejandro@users.noreply.github.com> Date: Wed, 20 Nov 2024 17:11:32 +0100 Subject: [PATCH] [OGUI-1568] Filter issues filtering objects on edit mode layout show (#2665) * fixes virtual table reliance on model. Now acts as a reusable component which recieves objects that are to be displayed. * adapts objectTree and objectTreeSidebar usage of virtual table to pass array of objects to display * Adds tests to validate the fix and ensure functionality. --------- Co-authored-by: George Raduta --- QualityControl/public/layout/Layout.js | 4 +-- .../layout/view/panels/objectTreeSidebar.js | 26 ++++++++++----- .../public/object/objectTreePage.js | 26 +++++++++------ QualityControl/public/object/virtualTable.js | 33 ++++++++++--------- .../test/public/pages/layout-show.test.js | 32 +++++++++++++++--- 5 files changed, 81 insertions(+), 40 deletions(-) diff --git a/QualityControl/public/layout/Layout.js b/QualityControl/public/layout/Layout.js index bc8f7b382..857439db8 100644 --- a/QualityControl/public/layout/Layout.js +++ b/QualityControl/public/layout/Layout.js @@ -451,7 +451,6 @@ export default class Layout extends Observable { */ edit() { this.model.services.object.listObjects(); - if (!this.item) { throw new Error('An item should be loaded before editing it'); } @@ -460,7 +459,7 @@ export default class Layout extends Observable { this.editOriginalClone = JSON.parse(JSON.stringify(this.item)); this.editingTabObject = null; window.dispatchEvent(new Event('resize')); - + this.model.object.searchInput = ''; this.notify(); } @@ -486,6 +485,7 @@ export default class Layout extends Observable { this.editingTabObject = null; this.item = this.editOriginalClone; this.selectTab(this._tabIndex); + this.model.object.searchInput = ''; this.notify(); } diff --git a/QualityControl/public/layout/view/panels/objectTreeSidebar.js b/QualityControl/public/layout/view/panels/objectTreeSidebar.js index 40667fc23..90d1abf80 100644 --- a/QualityControl/public/layout/view/panels/objectTreeSidebar.js +++ b/QualityControl/public/layout/view/panels/objectTreeSidebar.js @@ -29,14 +29,24 @@ export default (model) => model.services.object.list.match({ NotAsked: () => null, Loading: () => h('.flex-column.items-center', [spinner(2), h('.f6', 'Loading Objects')]), - Success: () => [ - searchForm(model), - h( - '.scroll-y', - model.object.searchInput.trim() !== '' ? virtualTable(model, 'side') : treeTable(model), - ), - objectPreview(model), - ], + Success: (objects) => { + let objectsToDisplay = []; + const { searchInput = '' } = model.object; + if (searchInput.trim() !== '') { + objectsToDisplay = objects.filter((qcObject) => + qcObject.path.toLowerCase().includes(searchInput.toLowerCase())); + } + return [ + searchForm(model), + h( + '.scroll-y', + searchInput.trim() !== '' + ? virtualTable(model, 'side', objectsToDisplay) + : treeTable(model), + ), + objectPreview(model), + ]; + }, Failure: (error) => h('.f6.danger.flex-column.text-center', [ h('', 'Unable to list objects due to:'), h('', error.message), diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index 599469290..6a71500b0 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -31,16 +31,22 @@ export default (model) => h('.h-100.flex-column', { key: model.router.params.pag style: { width: model.object.selected ? '50%' : '100%', }, - }, model.object.searchInput.trim() !== '' ? - virtualTable(model, 'main') - : - model.object.objectsRemote.match({ - NotAsked: () => null, - Loading: () => - h('.absolute-fill.flex-column.items-center.justify-center.f5', [spinner(5), h('', 'Loading Objects')]), - Success: () => tableShow(model), - Failure: () => null, // Notification is displayed - })), + }, model.object.objectsRemote.match({ + NotAsked: () => null, + Loading: () => + h('.absolute-fill.flex-column.items-center.justify-center.f5', [spinner(5), h('', 'Loading Objects')]), + Success: () => { + const searchInput = model.object?.searchInput?.trim() ?? ''; + if (searchInput !== '') { + const objectsLoaded = model.object.list; + const objectsToDisplay = objectsLoaded.filter((qcObject) => + qcObject.path.toLowerCase().includes(searchInput.toLowerCase())); + return virtualTable(model, 'main', objectsToDisplay); + } + return tableShow(model); + }, + Failure: () => null, // Notification is displayed + })), h('.animate-width.scroll-y', { style: { width: model.object.selected ? '50%' : 0, diff --git a/QualityControl/public/object/virtualTable.js b/QualityControl/public/object/virtualTable.js index b7b4bef8a..29ebda4d4 100644 --- a/QualityControl/public/object/virtualTable.js +++ b/QualityControl/public/object/virtualTable.js @@ -21,9 +21,10 @@ let FONT = ''; * A table which only displays the rows visible to the user * @param {Model} model - root model of the application * @param {string} location - location of where the virtual table is used: main(default) / side + * @param {Array<{path: string, name: string, createdAt: number}>} objects - list of objects to display * @returns {vnode} - virtual node element */ -export default function virtualTable(model, location = 'main') { +export default function virtualTable(model, location = 'main', objects = []) { ROW_HEIGHT = location === 'side' ? 29.4 : 33.6; FONT = location === 'side' ? '.f6' : ''; return h('.flex-grow.flex-column', { @@ -34,20 +35,22 @@ export default function virtualTable(model, location = 'main') { tableContainerHooks(model), h( '', - maximumTableSizeStyling(model.object.searchResult.length), - h( - `table.table-logs-content.text-no-select.table.table-sm${FONT}`, - scrollStyling(model), - [ - h( - 'tbody', - [ - listLogsInViewportOnly(model, model.object.searchResult).map((item) => - objectFullRow(model, item, location)), - ], - ), - ], - ), + maximumTableSizeStyling(objects.length === 0 ? 1 : objects.length), + objects.length === 0 + ? h('p.text-center', 'No objects found for this search') + : h( + `table.table-logs-content.text-no-select.table.table-sm${FONT}`, + scrollStyling(model), + [ + h( + 'tbody', + [ + listLogsInViewportOnly(model, objects) + .map((item) => objectFullRow(model, item, location)), + ], + ), + ], + ), ), ), ]); diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 9c6c1a9cb..bb8d42a8e 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -209,11 +209,33 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => await testParent.test( 'should have filtered results on input search filled', { timeout }, - async (t) => { - t.skip('Skip test execution for now'); - // TODO: review - //await page.type('nav > div > div > div:nth-child(6) > input', '1'); - //await page.waitForFunction('document.querySelectorAll(\'nav table tbody tr\').length === 1', { timeout: 5000 }); + async () => { + const inputs = await page.$$('nav input'); + await inputs[3].type('1'); + await delay(50); + const { count, firstResult, secondResult } = await page.evaluate(() => { + const rows = document.querySelectorAll('nav table tbody tr'); + return { + count: rows.length, + firstResult: rows[0].firstElementChild.textContent, + secondResult: rows[1].firstElementChild.textContent, + }; + }); + strictEqual(count, 2); + strictEqual(firstResult, ' qc/test/object/1'); + strictEqual(secondResult, ' qc/test/object/11'); + }, + ); + + await testParent.test( + 'should have no results if query does not match any objects', + { timeout }, + async () => { + const inputs = await page.$$('nav input'); + await inputs[3].type('123'); + await delay(50); + const text = await page.evaluate(() => document.querySelector('nav p.text-center').textContent); + strictEqual(text, 'No objects found for this search'); }, );