diff --git a/src/components/cylc/Drawer.vue b/src/components/cylc/Drawer.vue index 29a08f18c..e758a08c5 100644 --- a/src/components/cylc/Drawer.vue +++ b/src/components/cylc/Drawer.vue @@ -38,7 +38,7 @@ along with this program. If not, see . Dashboard - + Workflows diff --git a/src/components/cylc/Header.vue b/src/components/cylc/Header.vue index acddfa1ae..6f343136a 100644 --- a/src/components/cylc/Header.vue +++ b/src/components/cylc/Header.vue @@ -18,24 +18,7 @@ along with this program. If not, see . - diff --git a/src/components/cylc/TaskFilterSelect.vue b/src/components/cylc/TaskFilterSelect.vue index ec71e0457..fa5e2673d 100644 --- a/src/components/cylc/TaskFilterSelect.vue +++ b/src/components/cylc/TaskFilterSelect.vue @@ -3,14 +3,18 @@ :items="items" clearable multiple - v-model="localValue" + v-model="model" > diff --git a/src/components/cylc/analysis/TimeSeries.vue b/src/components/cylc/analysis/TimeSeries.vue index c5469cbe7..7d9073645 100644 --- a/src/components/cylc/analysis/TimeSeries.vue +++ b/src/components/cylc/analysis/TimeSeries.vue @@ -33,14 +33,14 @@ along with this program. If not, see . ref="selectTasks" > diff --git a/src/components/cylc/common/sort.js b/src/components/cylc/common/sort.js index 06d46252a..dc98c600d 100644 --- a/src/components/cylc/common/sort.js +++ b/src/components/cylc/common/sort.js @@ -70,27 +70,26 @@ export const DEFAULT_COMPARATOR = (left, right) => { * @param {Object[]} array - list of string values, or of objects with string values * @param {Object} value - a value to be inserted in the list, or an object wrapping the value (see iteratee) * @param {SortedIndexByIteratee=} iteratee - an optional function used to return the value of the element of the list} - * @param {SortedIndexByComparator=} comparator - function used to compare the newValue with otherValues in the list + * @param {{comparator: SortedIndexByComparator, reverse: boolean}=} options - an optional object with a comparator function and a reverse flag * @return {number} - sorted index */ -export function sortedIndexBy (array, value, iteratee, options = {}) { - // comparator, reverse = false) { +export function sortedIndexBy (array, value, iteratee = (x) => x, options = {}) { if (array.length === 0) { return 0 } - // If given a function, use it. Otherwise, simply use identity function. - const iterateeFunction = iteratee || ((value) => value) // If given a function, use it. Otherwise, simply use locale sort with numeric enabled - const comparatorFunction = options.comparator || ((leftObject, leftValue, rightObject, rightValue) => DEFAULT_COMPARATOR(leftValue, rightValue)) + const comparator = options.comparator || ( + (leftObject, leftValue, rightObject, rightValue) => DEFAULT_COMPARATOR(leftValue, rightValue) + ) let low = 0 let high = array.length - const newValue = iterateeFunction(value) + const newValue = iteratee(value) while (low < high) { const mid = Math.floor((low + high) / 2) - const midValue = iterateeFunction(array[mid]) - let higher = comparatorFunction(value, newValue, array[mid], midValue) + const midValue = iteratee(array[mid]) + let higher = comparator(value, newValue, array[mid], midValue) if (options.reverse) { higher = higher * -1 } diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js index 19b147213..3b4f8410e 100644 --- a/src/plugins/vuetify.js +++ b/src/plugins/vuetify.js @@ -22,6 +22,7 @@ import { VCombobox } from 'vuetify/components/VCombobox' import { VSelect } from 'vuetify/components/VSelect' import { VTextarea } from 'vuetify/components/VTextarea' import { VTextField } from 'vuetify/components/VTextField' +import { VCardActions } from 'vuetify/components/VCard' import colors from 'vuetify/util/colors' import { mdiClose } from '@mdi/js' import { useReducedAnimation } from '@/composables/localStorage' @@ -75,11 +76,20 @@ export const vuetifyOptions = { mdi } }, + aliases: { + VSelectActions: VCardActions, + }, defaults: { VTooltip: { activator: 'parent', location: 'bottom', }, + VList: { + slim: true, + }, + VSelectActions: { + class: 'mt-n2' + }, ...inputDefaults }, } diff --git a/src/store/workflows.module.js b/src/store/workflows.module.js index 7fcd95797..6f22a4cf1 100644 --- a/src/store/workflows.module.js +++ b/src/store/workflows.module.js @@ -124,7 +124,7 @@ function getIndex (state, id) { return state.cylcTree.$index[id] } -/* Add a child node under a parent Node */ +/** Add a child node under a parent Node */ function addChild (parentNode, childNode) { // determine which list to add this node to // console.log(`$t ++ ${childNode.id}`) @@ -144,20 +144,12 @@ function addChild (parentNode, childNode) { } // insert the child preserving sort order - let comparator - if (['cycle', 'family'].includes(parentNode.type)) { - // sort by type, then name - // (this makes families sort before tasks in the graph) - comparator = (n) => `${n.type}-${n.name}` - } else { - // sort by name - comparator = (n) => n.name - } + const iteratee = (n) => `${n.type}-${n.name}` // (this makes families sort before tasks in the tree) const reverse = ['cycle', 'job'].includes(childNode.type) const index = sortedIndexBy( parentNode[key], childNode, - comparator, + iteratee, { reverse } ) parentNode[key].splice(index, 0, childNode) diff --git a/tests/e2e/specs/tree.cy.js b/tests/e2e/specs/tree.cy.js index 71838d275..2c458b068 100644 --- a/tests/e2e/specs/tree.cy.js +++ b/tests/e2e/specs/tree.cy.js @@ -266,18 +266,14 @@ describe('Tree view', () => { it('Provides a select all functionality', () => { cy.visit('/#/tree/one') - cy - .get('[data-cy="filter task state"]') + cy.get('[data-cy="filter task state"]') .get('.v-list-item--active') .should('have.length', 0) - cy - .get('[data-cy="filter task state"]') + cy.get('[data-cy="filter task state"]') .click() - .get('.v-list-item') - .contains('Select All') - .click({ force: true }) - cy - .get('[data-cy="filter task state"]') + .get('[data-cy=task-filter-select-all]') + .click() + cy.get('[data-cy="filter task state"]') .get('.v-list-item--active') .should('have.length', 8) }) diff --git a/tests/unit/components/cylc/common/sort.spec.js b/tests/unit/components/cylc/common/sort.spec.js index 9a367bfd4..b895aa9a8 100644 --- a/tests/unit/components/cylc/common/sort.spec.js +++ b/tests/unit/components/cylc/common/sort.spec.js @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import { DEFAULT_COMPARATOR } from '@/components/cylc/common/sort' +import { DEFAULT_COMPARATOR, sortedIndexBy } from '@/components/cylc/common/sort' describe('DEFAULT_COMPARATOR', () => { it.each([ @@ -31,3 +31,47 @@ describe('DEFAULT_COMPARATOR', () => { expect(DEFAULT_COMPARATOR(a, b)).toEqual(expected) }) }) + +describe('sortedIndexBy', () => { + it.each([ + [['a', 'c', 'e'], 'a', 0], + [['a', 'c', 'e'], 'b', 1], + [['a', 'c', 'e'], 'c', 1], + [['a', 'c', 'e'], 'd', 2], + [['a', 'c', 'e'], 'x', 3], + [[], 'x', 0], + ])('sortedIndexBy(%o, %o) -> %i', (arr, value, expected) => { + expect(sortedIndexBy(arr, value)).toEqual(expected) + }) + + it('uses the iteratee function to determine the value for comparison', () => { + const arr = [{ key: 'a' }, { key: 'c' }, { key: 'e' }] + const value = { key: 'b' } + const iteratee = (x) => x.key + expect(sortedIndexBy(arr, value, iteratee)).toEqual(1) + }) + + it.each([ + [[8], 0], + [[8, 8], 0], + [[8, 8, 8], 3], + ])('%#) uses a custom comparator if provided', (value, expected) => { + const arr = [[8, 8], [8, 8], [8, 8]] + const comparator = (leftObj, leftVal, rightObj, rightVal) => ( + leftObj.length - rightObj.length + ) + expect(sortedIndexBy(arr, value, (x) => x, { comparator })).toEqual(expected) + }) + + it('reverses the comparison if reverse option is true', () => { + const arr = ['e', 'c', 'a'] + const value = 'd' + expect(sortedIndexBy(arr, value, (x) => x, { reverse: true })).toEqual(1) + }) + + it('handles numeric collation correctly by default', () => { + const arr = ['1', '2'] + const value = '10' + expect(sortedIndexBy(arr, value)).toEqual(2) + }) +})