From 33876135cad14f7c8517f3c3f3520b86c25bcb91 Mon Sep 17 00:00:00 2001 From: Richard Cox <18697775+richard-cox@users.noreply.github.com> Date: Fri, 1 Nov 2024 01:16:04 +0100 Subject: [PATCH] Ensure lists update when switching between workspaces with the same number of rows (#12449) --- shell/components/ResourceTable.vue | 27 ++++++++++++++++++++++- shell/components/SortableTable/index.vue | 18 ++++++++++++++- shell/components/SortableTable/sorting.js | 14 +++++++++--- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/shell/components/ResourceTable.vue b/shell/components/ResourceTable.vue index 1925461b58f..10f1fe22cf5 100644 --- a/shell/components/ResourceTable.vue +++ b/shell/components/ResourceTable.vue @@ -204,7 +204,31 @@ export default { // Confirm which store we're in, if schema isn't available we're probably showing a list with different types const inStore = this.schema?.id ? this.$store.getters['currentStore'](this.schema.id) : undefined; - return { inStore }; + return { + inStore, + /** + * Override the sortGenerationFn given changes in the rows we pass through to sortable table + * + * Primary purpose is to directly connect an iteration of `rows` with a sortGeneration string. This avoids + * reactivity issues where `rows` hasn't yet changed but something like workspaces has (stale values stored against fresh key) + */ + sortGeneration: undefined + }; + }, + + watch: { + filteredRows: { + handler() { + // This is only prevalent in fleet world and the workspace switcher + // - it's singular (a --> b --> c) instead of namespace switchers additive (a --> a+b --> a) + // - this means it's much more likely to switch between resource sets containing the same mount of rows + // + if (this.currentProduct.showWorkspaceSwitcher) { + this.sortGeneration = this.safeSortGenerationFn(this.schema, this.$store); + } + }, + immediate: true + } }, computed: { @@ -556,6 +580,7 @@ export default { :adv-filter-hide-labels-as-cols="advFilterHideLabelsAsCols" :adv-filter-prevent-filtering-labels="advFilterPreventFilteringLabels" :key-field="keyField" + :sortGeneration="sortGeneration" :sort-generation-fn="safeSortGenerationFn" :use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering" :force-update-live-and-delayed="forceUpdateLiveAndDelayed" diff --git a/shell/components/SortableTable/index.vue b/shell/components/SortableTable/index.vue index 81ecbe41f0b..5bf3f6dfccd 100644 --- a/shell/components/SortableTable/index.vue +++ b/shell/components/SortableTable/index.vue @@ -37,7 +37,7 @@ import ButtonMultiAction from '@shell/components/ButtonMultiAction.vue'; // --> filtering.js filteredRows // --> paging.js pageRows // --> grouping.js groupedRows -// --> index.vue displayedRows +// --> index.vue displayRows export default { name: 'SortableTable', @@ -77,11 +77,13 @@ export default { type: Array, required: true }, + rows: { // The array of objects to show type: Array, required: true }, + keyField: { // Field that is unique for each row. type: String, @@ -285,11 +287,25 @@ export default { default: true }, + /** + * Provide a unique key that will provide a new value given changes to the environment that + * should kick off an update to table rows (for instance resource list generation or change of namespace) + * + * This does not have to update given internal facets like sort order or direction + */ sortGenerationFn: { type: Function, default: null, }, + /** + * Can be used in place of sortGenerationFn + */ + sortGeneration: { + type: String, + default: null + }, + /** * The list will always be sorted by these regardless of what the user has selected */ diff --git a/shell/components/SortableTable/sorting.js b/shell/components/SortableTable/sorting.js index 7f509d97ac8..e6dec39d60e 100644 --- a/shell/components/SortableTable/sorting.js +++ b/shell/components/SortableTable/sorting.js @@ -39,9 +39,17 @@ export default { let key; - if ( this.sortGenerationFn ) { - key = `${ this.sortGenerationFn.apply(this) }/${ this.rows.length }/${ this.descending }/${ this.sortFields.join(',') }`; - + // Why is sortGeneration needed when we have sortGenerationFn? + // 1. sortGenerationFn is called when this fn is kicked off and returns latest and greatest string (given things like namespace) + // 2. it can be kicked off with stale rows... which is then stored against latest string + // 3. when updates rows comes through... sortGenerationFn returns same string + // 4. we therefor think nothing has changed and return old, stale rows + // This is avoided by outside storage of sortGeneration against rows + // (it would be nice to have that hash on the rows object itself, but it gets messy) + const sortGenerationKey = this.sortGeneration || this.sortGenerationFn?.apply(this); + + if ( sortGenerationKey) { + key = `${ sortGenerationKey }/${ this.rows.length }/${ this.descending }/${ this.sortFields.join(',') }`; if ( this.cacheKey === key ) { return this.cachedRows; }