@rancher/shell 3.0.1 → 3.0.2-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/assets/styles/base/_basic.scss +17 -5
  2. package/assets/styles/base/_mixins.scss +2 -1
  3. package/assets/styles/global/_button.scss +10 -0
  4. package/assets/styles/global/_form.scss +2 -2
  5. package/assets/translations/en-us.yaml +33 -5
  6. package/assets/translations/zh-hans.yaml +1 -1
  7. package/components/ActionMenu.vue +8 -0
  8. package/components/AsyncButton.vue +9 -3
  9. package/components/BannerGraphic.vue +10 -0
  10. package/components/ButtonGroup.vue +2 -0
  11. package/components/ButtonMultiAction.vue +6 -0
  12. package/components/ClusterIconMenu.vue +1 -1
  13. package/components/CodeMirror.vue +28 -1
  14. package/components/CommunityLinks.vue +13 -0
  15. package/components/CruResource.vue +6 -0
  16. package/components/GrowlManager.vue +14 -4
  17. package/components/LocaleSelector.vue +49 -5
  18. package/components/PaginatedResourceTable.vue +4 -3
  19. package/components/ResourceDetail/Masthead.vue +11 -4
  20. package/components/ResourceList/index.vue +5 -3
  21. package/components/ResourceTable.vue +1 -1
  22. package/components/SortableTable/THead.vue +19 -4
  23. package/components/SortableTable/index.vue +13 -9
  24. package/components/SortableTable/selection.js +19 -5
  25. package/components/YamlEditor.vue +2 -1
  26. package/components/auth/SelectPrincipal.vue +1 -1
  27. package/components/fleet/FleetBundles.vue +2 -1
  28. package/components/form/LabeledSelect.vue +20 -7
  29. package/components/form/NodeScheduling.vue +5 -1
  30. package/components/form/Password.vue +23 -13
  31. package/components/form/ResourceLabeledSelect.vue +1 -1
  32. package/components/form/Select.vue +28 -6
  33. package/components/form/SelectOrCreateAuthSecret.vue +39 -11
  34. package/components/form/__tests__/NodeScheduling.test.ts +44 -0
  35. package/components/formatter/Endpoints.vue +1 -1
  36. package/components/formatter/LiveExpiryDate.vue +5 -1
  37. package/components/formatter/ServiceTargets.vue +1 -1
  38. package/components/formatter/ServiceType.vue +19 -17
  39. package/components/nav/Pinned.vue +6 -1
  40. package/components/nav/TopLevelMenu.helper.ts +17 -1
  41. package/components/nav/TopLevelMenu.vue +154 -19
  42. package/config/pagination-table-headers.js +9 -1
  43. package/config/product/apps.js +63 -30
  44. package/config/product/explorer.js +182 -17
  45. package/config/product/settings.js +9 -1
  46. package/config/router/routes.js +0 -1
  47. package/config/settings.ts +20 -2
  48. package/config/table-headers.js +23 -15
  49. package/config/types.js +2 -1
  50. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +12 -3
  51. package/edit/__tests__/management.cattle.io.setting.test.ts +37 -18
  52. package/edit/fleet.cattle.io.gitrepo.vue +40 -33
  53. package/edit/management.cattle.io.setting.vue +2 -0
  54. package/edit/provisioning.cattle.io.cluster/rke2.vue +13 -2
  55. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +10 -2
  56. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +8 -2
  57. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +6 -3
  58. package/edit/workload/mixins/workload.js +15 -7
  59. package/list/catalog.cattle.io.app.vue +4 -11
  60. package/list/catalog.cattle.io.clusterrepo.vue +59 -25
  61. package/list/fleet.cattle.io.bundle.vue +2 -2
  62. package/list/management.cattle.io.feature.vue +12 -5
  63. package/list/management.cattle.io.setting.vue +30 -19
  64. package/list/namespace.vue +4 -1
  65. package/list/networking.k8s.io.ingress.vue +14 -11
  66. package/list/node.vue +65 -63
  67. package/list/persistentvolume.vue +55 -20
  68. package/list/persistentvolumeclaim.vue +3 -15
  69. package/list/service.vue +16 -21
  70. package/list/workload.vue +35 -49
  71. package/mixins/resource-fetch.js +8 -1
  72. package/mixins/vue-select-overrides.js +10 -16
  73. package/models/management.cattle.io.cluster.js +6 -1
  74. package/models/persistentvolume.js +1 -3
  75. package/models/storage.k8s.io.storageclass.js +4 -0
  76. package/package.json +29 -29
  77. package/pages/c/_cluster/explorer/EventsTable.vue +58 -16
  78. package/pages/c/_cluster/explorer/index.vue +3 -16
  79. package/pages/c/_cluster/settings/performance.vue +49 -23
  80. package/pages/home.vue +24 -3
  81. package/pages/support/index.vue +1 -1
  82. package/plugins/floating-vue.js +1 -1
  83. package/plugins/steve/steve-pagination-utils.ts +85 -15
  84. package/rancher-components/Banner/Banner.vue +12 -0
  85. package/rancher-components/Form/Checkbox/Checkbox.vue +27 -5
  86. package/rancher-components/Form/Radio/RadioButton.vue +0 -6
  87. package/rancher-components/Form/Radio/RadioGroup.vue +5 -1
  88. package/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml +2 -2
  89. package/scripts/test-plugins-build.sh +21 -6
  90. package/scripts/typegen.sh +1 -0
  91. package/store/index.js +16 -0
  92. package/store/type-map.utils.ts +14 -1
  93. package/types/shell/index.d.ts +467 -418
  94. package/types/store/vuex.d.ts +1 -1
  95. package/types/vue-shim.d.ts +2 -8
  96. package/utils/cluster.js +2 -2
  97. package/utils/string.js +6 -0
  98. package/vue.config.js +3 -4
@@ -27,7 +27,7 @@ export default {
27
27
  const nodeWithExternal = nodes.find((node) => !!node.externalIp) || {};
28
28
  const externalIp = nodeWithExternal.externalIp;
29
29
 
30
- if ( this.value && this.value.length ) {
30
+ if ( this.value?.length ) {
31
31
  let out ;
32
32
 
33
33
  try {
@@ -12,6 +12,10 @@ export default {
12
12
  type: Object,
13
13
  default: () => {}
14
14
  },
15
+ missingKey: {
16
+ type: String,
17
+ default: 'generic.never',
18
+ }
15
19
  },
16
20
 
17
21
  data() {
@@ -77,7 +81,7 @@ export default {
77
81
  />
78
82
  <span
79
83
  v-else
80
- v-t="'generic.never'"
84
+ v-t="missingKey"
81
85
  class="text-muted"
82
86
  />
83
87
  </template>
@@ -95,7 +95,7 @@ export default {
95
95
  },
96
96
  },
97
97
  };
98
- </script>>
98
+ </script>
99
99
 
100
100
  <template>
101
101
  <div>
@@ -17,28 +17,30 @@ export default {
17
17
  },
18
18
  },
19
19
  data() {
20
- const { row } = this;
21
- let cloned = this.value.toLowerCase();
20
+ const cloned = this.getLabel(this.value.toLowerCase());
21
+ const headless = this.value === 'ClusterIP' && this.row?.spec?.clusterIP === 'None' ? this.getLabel('headless') : undefined;
22
22
 
23
- if (this.value === 'ClusterIP' && row?.spec?.clusterIP === 'None') {
24
- cloned = 'headless';
25
- }
23
+ return { translated: cloned, headless };
24
+ },
26
25
 
27
- const match = DEFAULT_SERVICE_TYPES.find((s) => s.id.toLowerCase() === cloned);
28
- const translationLabel = match?.label;
29
- let translated;
26
+ methods: {
27
+ getLabel(type) {
28
+ const match = DEFAULT_SERVICE_TYPES.find((s) => s.id.toLowerCase() === type);
29
+ const translationLabel = match?.label;
30
+ let translated;
30
31
 
31
- if (translationLabel && this.$store.getters['i18n/exists'](translationLabel)) {
32
- translated = this.$store.getters['i18n/t'](translationLabel);
33
- } else {
34
- translated = this.value;
35
- }
32
+ if (translationLabel && this.$store.getters['i18n/exists'](translationLabel)) {
33
+ translated = this.$store.getters['i18n/t'](translationLabel);
34
+ } else {
35
+ translated = this.value;
36
+ }
36
37
 
37
- return { translated };
38
- },
38
+ return translated;
39
+ }
40
+ }
39
41
  };
40
- </script>>
42
+ </script>
41
43
 
42
44
  <template>
43
- <span>{{ translated }}</span>
45
+ <span>{{ translated }}{{ headless ? ` (${headless})` : '' }}</span>
44
46
  </template>
@@ -5,6 +5,10 @@ export default {
5
5
  cluster: {
6
6
  type: Object,
7
7
  required: true,
8
+ },
9
+ tabOrder: {
10
+ type: Number,
11
+ default: null,
8
12
  }
9
13
  },
10
14
 
@@ -28,11 +32,12 @@ export default {
28
32
 
29
33
  <template>
30
34
  <i
31
- :tabindex="0"
35
+ :tabindex="tabOrder"
32
36
  :aria-checked="!!pinned"
33
37
  class="pin icon"
34
38
  :class="{'icon-pin-outlined': !pinned, 'icon-pin': pinned}"
35
39
  aria-role="button"
40
+ :aria-label="`${t('nav.ariaLabel.pinCluster')} ${ cluster.label }`"
36
41
  @click.stop.prevent="toggle"
37
42
  @keydown.enter.prevent="toggle"
38
43
  @keydown.space.prevent="toggle"
@@ -13,6 +13,7 @@ interface TopLevelMenuCluster {
13
13
  ready: boolean
14
14
  providerNavLogo: string,
15
15
  badge: string,
16
+ iconColor: string,
16
17
  isLocal: boolean,
17
18
  pinned: boolean,
18
19
  description: string,
@@ -128,6 +129,12 @@ export abstract class BaseTopLevelMenuHelper {
128
129
  this.$store = $store;
129
130
 
130
131
  this.hasProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
132
+
133
+ // Reduce flicker when component is recreated on a different layout
134
+ const { clustersPinned = [], clustersOthers = [] } = this.$store.getters['sideNavCache'] || {};
135
+
136
+ this.clustersPinned.push(...clustersPinned);
137
+ this.clustersOthers.push(...clustersOthers);
131
138
  }
132
139
 
133
140
  protected convertToCluster(mgmtCluster: MgmtCluster, provCluster: ProvCluster): TopLevelMenuCluster {
@@ -137,6 +144,7 @@ export abstract class BaseTopLevelMenuHelper {
137
144
  ready: mgmtCluster.isReady, // && !provCluster?.hasError,
138
145
  providerNavLogo: mgmtCluster.providerMenuLogo,
139
146
  badge: mgmtCluster.badge,
147
+ iconColor: mgmtCluster.iconColor,
140
148
  isLocal: mgmtCluster.isLocal,
141
149
  pinned: mgmtCluster.pinned,
142
150
  description: provCluster?.description || mgmtCluster.description,
@@ -145,6 +153,10 @@ export abstract class BaseTopLevelMenuHelper {
145
153
  clusterRoute: { name: 'c-cluster-explorer', params: { cluster: mgmtCluster.id } }
146
154
  };
147
155
  }
156
+
157
+ protected cacheClusters() {
158
+ this.$store.dispatch('setSideNavCache', { clustersPinned: this.clustersPinned, clustersOthers: this.clustersOthers });
159
+ }
148
160
  }
149
161
 
150
162
  /**
@@ -258,6 +270,8 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
258
270
 
259
271
  this.clustersPinned.push(..._clustersPinned);
260
272
  this.clustersOthers.push(..._clustersNotPinned);
273
+
274
+ this.cacheClusters();
261
275
  }
262
276
 
263
277
  private constructParams({
@@ -378,7 +392,7 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
378
392
  }
379
393
 
380
394
  /**
381
- * Helper designed to supply non-pagainted results for the top level menu cluster resources
395
+ * Helper designed to supply non-paginated results for the top level menu cluster resources
382
396
  */
383
397
  export class TopLevelMenuHelperLegacy extends BaseTopLevelMenuHelper implements TopLevelMenuHelper {
384
398
  constructor({ $store }: {
@@ -401,6 +415,8 @@ export class TopLevelMenuHelperLegacy extends BaseTopLevelMenuHelper implements
401
415
 
402
416
  this.clustersPinned.push(..._clustersPinned);
403
417
  this.clustersOthers.push(..._clustersNotPinned);
418
+
419
+ this.cacheClusters();
404
420
  }
405
421
 
406
422
  /**
@@ -42,6 +42,17 @@ export default {
42
42
  const provClusters = !canPagination && hasProvCluster ? this.$store.getters[`management/all`](CAPI.RANCHER_CLUSTER) : [];
43
43
  const mgmtClusters = !canPagination ? this.$store.getters[`management/all`](MANAGEMENT.CLUSTER) : [];
44
44
 
45
+ if (!canPagination) {
46
+ // Reduce the impact of the initial load, but only if we're not making a request
47
+ const args = {
48
+ pinnedIds: this.pinnedIds,
49
+ searchTerm: this.search,
50
+ unPinnedMax: this.maxClustersToShow
51
+ };
52
+
53
+ helper.update(args);
54
+ }
55
+
45
56
  return {
46
57
  shown: false,
47
58
  displayVersion,
@@ -54,8 +65,9 @@ export default {
54
65
 
55
66
  canPagination,
56
67
  helper,
57
- debouncedHelperUpdateSlow: debounce((...args) => this.helper.update(...args), 750),
58
- debouncedHelperUpdateQuick: debounce((...args) => this.helper.update(...args), 200),
68
+ debouncedHelperUpdateSlow: debounce((...args) => this.helper.update(...args), 1000),
69
+ debouncedHelperUpdateMedium: debounce((...args) => this.helper.update(...args), 750),
70
+ debouncedHelperUpdateQuick: debounce((...args) => this.helper.update(...args), 200),
59
71
  provClusters,
60
72
  mgmtClusters,
61
73
  };
@@ -264,6 +276,14 @@ export default {
264
276
  this.shown = false;
265
277
  },
266
278
 
279
+ // Before SSP world all of these changes were kicked off given Vue change detection to properties in a computed method.
280
+ // Changes could come from two scenarios
281
+ // 1. Changes made by the user (pin / search). Could be tens per second
282
+ // 2. Changes made by rancher to clusters (state, label, etc change). Could be hundreds a second
283
+ // They can be restricted to help the churn caused from above
284
+ // 1. When SSP enabled reduce http spam
285
+ // 2. When SSP is disabled (legacy) reduce fn churn (this was a known performance customer issue)
286
+
267
287
  pinnedIds: {
268
288
  immediate: true,
269
289
  handler(neu, old) {
@@ -271,27 +291,29 @@ export default {
271
291
  return;
272
292
  }
273
293
 
294
+ // Low throughput (user click). Changes should be shown quickly
274
295
  this.updateClusters(neu, 'quick');
275
296
  }
276
297
  },
277
298
 
278
299
  search() {
279
- this.updateClusters(this.pinnedIds, 'slow');
300
+ // Medium throughput. Changes should be shown quickly, unless we want to reduce http spam in SSP world
301
+ this.updateClusters(this.pinnedIds, this.canPagination ? 'medium' : 'quick');
280
302
  },
281
303
 
282
304
  provClusters: {
283
- handler() {
284
- // Shouldn't get here if SSP
285
- this.updateClusters(this.pinnedIds, 'slow');
305
+ handler(neu, old) {
306
+ // Potentially incredibly high throughput. Changes should be at least limited (slow if state change, quick if added/removed). Shouldn't get here if SSP
307
+ this.updateClusters(this.pinnedIds, neu?.length === old?.length ? 'slow' : 'quick');
286
308
  },
287
309
  deep: true,
288
310
  immediate: true,
289
311
  },
290
312
 
291
313
  mgmtClusters: {
292
- handler() {
293
- // Shouldn't get here if SSP
294
- this.updateClusters(this.pinnedIds, 'slow');
314
+ handler(neu, old) {
315
+ // Potentially incredibly high throughput. Changes should be at least limited (slow if state change, quick if added/removed). Shouldn't get here if SSP
316
+ this.updateClusters(this.pinnedIds, neu?.length === old?.length ? 'slow' : 'quick');
295
317
  },
296
318
  deep: true,
297
319
  immediate: true,
@@ -424,7 +446,7 @@ export default {
424
446
  };
425
447
  },
426
448
 
427
- updateClusters(pinnedIds, speed = 'slow' | 'quick') {
449
+ updateClusters(pinnedIds, speed = 'slow' | 'medium' | 'quick') {
428
450
  const args = {
429
451
  pinnedIds,
430
452
  searchTerm: this.search,
@@ -435,6 +457,9 @@ export default {
435
457
  case 'slow':
436
458
  this.debouncedHelperUpdateSlow(args);
437
459
  break;
460
+ case 'medium':
461
+ this.debouncedHelperUpdateMedium(args);
462
+ break;
438
463
  case 'quick':
439
464
  this.debouncedHelperUpdateQuick(args);
440
465
  break;
@@ -460,6 +485,8 @@ export default {
460
485
  :class="{'menu-open': shown, 'menu-close':!shown}"
461
486
  :style="sideMenuStyle"
462
487
  tabindex="-1"
488
+ role="navigation"
489
+ :aria-label="t('nav.ariaLabel.topLevelMenu')"
463
490
  >
464
491
  <!-- Logo and name -->
465
492
  <div class="title">
@@ -479,6 +506,7 @@ export default {
479
506
  height="24"
480
507
  viewBox="0 0 24 24"
481
508
  width="24"
509
+ :alt="t('nav.alt.mainMenuIcon')"
482
510
  ><path
483
511
  d="M0 0h24v24H0z"
484
512
  fill="none"
@@ -487,6 +515,7 @@ export default {
487
515
  <div class="side-menu-logo">
488
516
  <BrandImage
489
517
  data-testid="side-menu__brand-img"
518
+ :alt="t('nav.alt.mainMenuRancherLogo')"
490
519
  file-name="rancher-logo.svg"
491
520
  />
492
521
  </div>
@@ -500,9 +529,12 @@ export default {
500
529
  <router-link
501
530
  class="option cluster selector home"
502
531
  :to="{ name: 'home' }"
532
+ role="link"
533
+ :aria-label="t('nav.ariaLabel.homePage')"
503
534
  >
504
535
  <svg
505
536
  v-clean-tooltip="getTooltipConfig(t('nav.home'))"
537
+ class="top-menu-icon"
506
538
  xmlns="http://www.w3.org/2000/svg"
507
539
  height="24"
508
540
  viewBox="0 0 24 24"
@@ -537,6 +569,8 @@ export default {
537
569
  ref="clusterFilter"
538
570
  v-model="clusterFilter"
539
571
  :placeholder="t('nav.search.placeholder')"
572
+ :tabindex="!shown ? -1 : 0"
573
+ :aria-label="t('nav.search.ariaLabel')"
540
574
  >
541
575
  <i
542
576
  class="magnifier icon icon-search"
@@ -558,10 +592,11 @@ export default {
558
592
  <a
559
593
  v-if="isRancherInHarvester"
560
594
  class="option"
595
+ tabindex="0"
561
596
  @click="goToHarvesterCluster()"
562
597
  >
563
598
  <i
564
- class="icon icon-dashboard"
599
+ class="icon icon-dashboard app-icon"
565
600
  />
566
601
  <div>
567
602
  {{ t('nav.harvesterDashboard') }}
@@ -577,8 +612,11 @@ export default {
577
612
  class="option"
578
613
  :to="a.to"
579
614
  :class="{'active-menu-link': a.isMenuActive }"
615
+ role="link"
616
+ :aria-label="`${t('nav.ariaLabel.harvesterCluster')} ${ a.label }`"
580
617
  >
581
618
  <IconOrSvg
619
+ class="app-icon"
582
620
  :icon="a.icon"
583
621
  :src="a.svg"
584
622
  />
@@ -612,6 +650,8 @@ export default {
612
650
  class="cluster selector option"
613
651
  :class="{'active-menu-link': c.isMenuActive }"
614
652
  :to="c.clusterRoute"
653
+ role="button"
654
+ :aria-label="`${t('nav.ariaLabel.cluster')} ${ c.label }`"
615
655
  @click.prevent="clusterMenuClick($event, c)"
616
656
  @shortkey="handleKeyComboClick"
617
657
  >
@@ -635,6 +675,7 @@ export default {
635
675
  </div>
636
676
  <Pinned
637
677
  :cluster="c"
678
+ :tab-order="shown ? 0 : -1"
638
679
  />
639
680
  </button>
640
681
  <span
@@ -661,6 +702,7 @@ export default {
661
702
  </div>
662
703
  <Pinned
663
704
  :cluster="c"
705
+ :tab-order="shown ? 0 : -1"
664
706
  />
665
707
  </span>
666
708
  </div>
@@ -687,6 +729,8 @@ export default {
687
729
  class="cluster selector option"
688
730
  :class="{'active-menu-link': c.isMenuActive }"
689
731
  :to="c.clusterRoute"
732
+ role="button"
733
+ :aria-label="`${t('nav.ariaLabel.cluster')} ${ c.label }`"
690
734
  @click="clusterMenuClick($event, c)"
691
735
  @shortkey="handleKeyComboClick"
692
736
  >
@@ -700,7 +744,6 @@ export default {
700
744
  v-clean-tooltip="getTooltipConfig(c)"
701
745
  class="cluster-name"
702
746
  >
703
- <!-- HERE LOCAL CLUSTER! -->
704
747
  <p>{{ c.label }}</p>
705
748
  <p
706
749
  v-if="c.description"
@@ -711,6 +754,7 @@ export default {
711
754
  </div>
712
755
  <Pinned
713
756
  :class="{'showPin': c.pinned}"
757
+ :tab-order="shown ? 0 : -1"
714
758
  :cluster="c"
715
759
  />
716
760
  </button>
@@ -738,6 +782,7 @@ export default {
738
782
  </div>
739
783
  <Pinned
740
784
  :class="{'showPin': c.pinned}"
785
+ :tab-order="shown ? 0 : -1"
741
786
  :cluster="c"
742
787
  />
743
788
  </span>
@@ -763,6 +808,8 @@ export default {
763
808
  product: 'manager',
764
809
  resource: 'provisioning.cattle.io.cluster'
765
810
  } }"
811
+ role="link"
812
+ :aria-label="t('nav.ariaLabel.seeAll')"
766
813
  >
767
814
  <span>
768
815
  {{ shown ? t('nav.seeAllClusters') : t('nav.seeAllClustersCollapsed') }}
@@ -771,6 +818,7 @@ export default {
771
818
  </router-link>
772
819
  </template>
773
820
 
821
+ <!-- MULTI CLUSTER APPS -->
774
822
  <div class="category">
775
823
  <template v-if="multiClusterApps.length">
776
824
  <div
@@ -790,9 +838,12 @@ export default {
790
838
  class="option"
791
839
  :class="{'active-menu-link': a.isMenuActive }"
792
840
  :to="a.to"
841
+ role="link"
842
+ :aria-label="`${t('nav.ariaLabel.multiClusterApps')} ${ a.label }`"
793
843
  >
794
844
  <IconOrSvg
795
845
  v-clean-tooltip="getTooltipConfig(a.label)"
846
+ class="app-icon"
796
847
  :icon="a.icon"
797
848
  :src="a.svg"
798
849
  />
@@ -801,7 +852,7 @@ export default {
801
852
  </div>
802
853
  </template>
803
854
 
804
- <!-- App menu -->
855
+ <!-- Configuration apps menu -->
805
856
  <template v-if="configurationApps.length">
806
857
  <div
807
858
  class="category-title"
@@ -820,9 +871,12 @@ export default {
820
871
  class="option"
821
872
  :class="{'active-menu-link': a.isMenuActive }"
822
873
  :to="a.to"
874
+ role="link"
875
+ :aria-label="`${t('nav.ariaLabel.configurationApps')} ${ a.label }`"
823
876
  >
824
877
  <IconOrSvg
825
878
  v-clean-tooltip="getTooltipConfig(a.label)"
879
+ class="app-icon"
826
880
  :icon="a.icon"
827
881
  :src="a.svg"
828
882
  />
@@ -844,6 +898,8 @@ export default {
844
898
  >
845
899
  <router-link
846
900
  :to="{name: 'support'}"
901
+ role="link"
902
+ :aria-label="t('nav.ariaLabel.support')"
847
903
  >
848
904
  {{ t('nav.support', {hasSupport}) }}
849
905
  </router-link>
@@ -855,6 +911,8 @@ export default {
855
911
  >
856
912
  <router-link
857
913
  :to="{ name: 'about' }"
914
+ role="link"
915
+ :aria-label="t('nav.ariaLabel.about')"
858
916
  >
859
917
  {{ aboutText }}
860
918
  </router-link>
@@ -958,13 +1016,27 @@ export default {
958
1016
  overflow: hidden;
959
1017
  transition: width 250ms;
960
1018
 
961
- &:focus {
1019
+ &:focus, &:focus-visible {
1020
+ outline: 0;
1021
+ }
1022
+
1023
+ .option:focus-visible {
962
1024
  outline: 0;
963
1025
  }
964
1026
 
965
- &.menu-open {
1027
+ &.menu-open {
966
1028
  width: 300px;
967
1029
  box-shadow: 3px 1px 3px var(--shadow);
1030
+
1031
+ // because of accessibility, we force pin action to be visible on menu open
1032
+ .pin {
1033
+ display: block !important;
1034
+
1035
+ &:focus-visible {
1036
+ @include focus-outline;
1037
+ outline-offset: 4px;
1038
+ }
1039
+ }
968
1040
  }
969
1041
 
970
1042
  .title {
@@ -1074,10 +1146,6 @@ export default {
1074
1146
  &:focus {
1075
1147
  outline: 0;
1076
1148
  box-shadow: none;
1077
-
1078
- > div {
1079
- text-decoration: underline;
1080
- }
1081
1149
  }
1082
1150
 
1083
1151
  > i, > img {
@@ -1095,7 +1163,22 @@ export default {
1095
1163
  fill: var(--link);
1096
1164
  }
1097
1165
 
1166
+ .top-menu-icon {
1167
+ outline-offset: 4px;
1168
+ }
1169
+
1098
1170
  &.router-link-active, &.active-menu-link {
1171
+ &:focus-visible {
1172
+ .top-menu-icon, .app-icon {
1173
+ @include focus-outline;
1174
+ }
1175
+ }
1176
+
1177
+ &:focus-visible .rancher-provider-icon {
1178
+ @include focus-outline;
1179
+ outline-offset: -4px;
1180
+ }
1181
+
1099
1182
  background: var(--primary-hover-bg);
1100
1183
  color: var(--primary-hover-text);
1101
1184
 
@@ -1112,6 +1195,12 @@ export default {
1112
1195
  }
1113
1196
  }
1114
1197
 
1198
+ &:focus-visible {
1199
+ .top-menu-icon, .rancher-provider-icon, .app-icon {
1200
+ @include focus-outline;
1201
+ }
1202
+ }
1203
+
1115
1204
  &:hover {
1116
1205
  color: var(--primary-hover-text);
1117
1206
  background: var(--primary-hover-bg);
@@ -1188,10 +1277,19 @@ export default {
1188
1277
  margin-right: 16px;
1189
1278
  margin-top: 10px;
1190
1279
 
1280
+ &:focus-visible {
1281
+ outline: none;
1282
+ }
1283
+
1191
1284
  span {
1192
1285
  display: flex;
1193
1286
  align-items: center;
1194
1287
  }
1288
+
1289
+ &:focus-visible span {
1290
+ @include focus-outline;
1291
+ outline-offset: 4px;
1292
+ }
1195
1293
  }
1196
1294
 
1197
1295
  .clusters {
@@ -1314,6 +1412,33 @@ export default {
1314
1412
  }
1315
1413
  }
1316
1414
 
1415
+ &.menu-open {
1416
+ .option {
1417
+ &.router-link-active, &.active-menu-link {
1418
+ &:focus-visible {
1419
+ @include focus-outline;
1420
+ border-radius: 0;
1421
+ outline-offset: -4px;
1422
+
1423
+ .top-menu-icon, .app-icon, .rancher-provider-icon {
1424
+ outline: none;
1425
+ border-radius: 0;
1426
+ }
1427
+ }
1428
+ }
1429
+
1430
+ &:focus-visible {
1431
+ @include focus-outline;
1432
+ outline-offset: -4px;
1433
+
1434
+ .top-menu-icon, .app-icon, .rancher-provider-icon {
1435
+ outline: none;
1436
+ border-radius: 0;
1437
+ }
1438
+ }
1439
+ }
1440
+ }
1441
+
1317
1442
  &.menu-close {
1318
1443
  .side-menu-logo {
1319
1444
  opacity: 0;
@@ -1387,8 +1512,18 @@ export default {
1387
1512
  text-align: center;
1388
1513
  }
1389
1514
 
1515
+ .support a:focus-visible {
1516
+ @include focus-outline;
1517
+ outline-offset: 4px;
1518
+ }
1519
+
1390
1520
  .version {
1391
1521
  cursor: pointer;
1522
+
1523
+ a:focus-visible {
1524
+ @include focus-outline;
1525
+ outline-offset: 4px;
1526
+ }
1392
1527
  }
1393
1528
  }
1394
1529
  }
@@ -1,4 +1,6 @@
1
- import { STATE, NAME as NAME_COL, NAMESPACE as NAMESPACE_COL, AGE } from '@shell/config/table-headers';
1
+ import {
2
+ STATE, NAME as NAME_COL, NAMESPACE as NAMESPACE_COL, AGE, OBJECT
3
+ } from '@shell/config/table-headers';
2
4
 
3
5
  // This file contains table headers
4
6
  // These table headers are used for server side pagination
@@ -44,6 +46,12 @@ export const STEVE_NAMESPACE_COL = {
44
46
  search: 'metadata.namespace',
45
47
  };
46
48
 
49
+ export const STEVE_EVENT_OBJECT = {
50
+ ...OBJECT,
51
+ sort: 'involvedObject.kind',
52
+ search: 'involvedObject.kind',
53
+ };
54
+
47
55
  export const STEVE_LIST_GROUPS = [{
48
56
  tooltipKey: 'resourceTable.groupBy.none',
49
57
  icon: 'icon-list-flat',