dashboard-shell-shell 1.0.112 → 1.0.113

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 (161) hide show
  1. package/.DS_Store +0 -0
  2. package/assets/icons/demo.css +539 -0
  3. package/assets/icons/demo_index.html +1131 -0
  4. package/assets/icons/iconfont.css +200 -0
  5. package/assets/icons/iconfont.js +1 -0
  6. package/assets/icons/iconfont.json +296 -0
  7. package/assets/icons/iconfont.ttf +0 -0
  8. package/assets/icons/iconfont.woff +0 -0
  9. package/assets/icons/iconfont.woff2 +0 -0
  10. package/assets/images/API.svg +3 -0
  11. package/assets/images/login/password.svg +20 -0
  12. package/assets/images/login/user.svg +6 -0
  13. package/assets/images/login-bg.png +0 -0
  14. package/assets/images/login-left.png +0 -0
  15. package/assets/images/login-logo.svg +19 -0
  16. package/assets/images/logo.png +0 -0
  17. package/assets/images/pl/harvester.png +0 -0
  18. package/assets/images/promp-yellow.svg +5 -0
  19. package/assets/images/user.png +0 -0
  20. package/assets/styles/all.scss +63 -0
  21. package/assets/styles/app.scss +2 -0
  22. package/assets/styles/base/_basic.scss +8 -2
  23. package/assets/styles/base/_helpers.scss +4 -0
  24. package/assets/styles/base/_typography.scss +2 -1
  25. package/assets/styles/base/_variables.scss +10 -2
  26. package/assets/styles/global/_button.scss +37 -25
  27. package/assets/styles/global/_columns.scss +3 -1
  28. package/assets/styles/global/_form.scss +45 -13
  29. package/assets/styles/global/_labeled-input.scss +50 -25
  30. package/assets/styles/global/_layout.scss +9 -3
  31. package/assets/styles/global/_select.scss +20 -13
  32. package/assets/styles/global/_table.scss +1 -1
  33. package/assets/styles/global/_tooltip.scss +47 -6
  34. package/assets/styles/themes/_dark.scss +1 -0
  35. package/assets/styles/themes/_light.scss +59 -46
  36. package/assets/styles/themes/_suse.scss +1 -0
  37. package/assets/styles/vendor/vue-select.scss +18 -7
  38. package/assets/translations/en-us.yaml +93 -12
  39. package/assets/translations/zh-hans.yaml +278 -141
  40. package/components/ActionDropdown.vue +1 -1
  41. package/components/ActionDropdownShell.vue +71 -0
  42. package/components/ActionMenu.vue +2 -2
  43. package/components/ActionMenuShell.vue +1 -0
  44. package/components/AppModal.vue +78 -6
  45. package/components/AssignTo.vue +25 -11
  46. package/components/AsyncButton.vue +24 -7
  47. package/components/BannerGraphic.vue +1 -0
  48. package/components/ButtonDropdown.vue +26 -4
  49. package/components/ButtonGroup.vue +4 -0
  50. package/components/ButtonMultiAction.vue +1 -0
  51. package/components/CommunityLinks.vue +3 -3
  52. package/components/ConsumptionGauge.vue +24 -5
  53. package/components/CopyToClipboardText.vue +2 -1
  54. package/components/CruResource.vue +12 -7
  55. package/components/CruResourceFooter.vue +2 -2
  56. package/components/DashboardOptions.vue +21 -15
  57. package/components/DetailText.vue +5 -0
  58. package/components/DisableAuthProviderModal.vue +1 -0
  59. package/components/DotState.vue +84 -0
  60. package/components/ExplorerMembers.vue +1 -1
  61. package/components/ExplorerProjectsNamespaces.vue +56 -14
  62. package/components/FixedBanner.vue +19 -12
  63. package/components/GlobalRoleBindings.vue +5 -1
  64. package/components/GrafanaDashboard.vue +4 -4
  65. package/components/GrowlManager.vue +4 -1
  66. package/components/HardwareResourceGauge.vue +39 -3
  67. package/components/InfoBox.vue +3 -3
  68. package/components/InputOrDisplay.vue +28 -2
  69. package/components/LabelValue.vue +16 -1
  70. package/components/LandingPagePreference.vue +5 -3
  71. package/components/LocaleSelector.vue +39 -93
  72. package/components/ModalManager.vue +55 -0
  73. package/components/ModalWithCard.vue +2 -0
  74. package/components/MoveModal.vue +1 -0
  75. package/components/PromptChangePassword.vue +1 -1
  76. package/components/PromptModal.vue +15 -2
  77. package/components/PromptRemove.vue +28 -8
  78. package/components/PromptRestore.vue +1 -0
  79. package/components/ResourceCancelModal.vue +1 -0
  80. package/components/ResourceDetail/Masthead.vue +188 -43
  81. package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
  82. package/components/ResourceDetail/index.vue +49 -14
  83. package/components/ResourceList/Masthead.vue +80 -18
  84. package/components/ResourceTable.vue +60 -19
  85. package/components/SideNav.vue +32 -12
  86. package/components/SlideInPanelManager.vue +126 -0
  87. package/components/SortableTable/THead.vue +34 -5
  88. package/components/SortableTable/actions.js +1 -1
  89. package/components/SortableTable/index.vue +649 -142
  90. package/components/SortableTable/paging.js +36 -28
  91. package/components/SortableTable/selection.js +0 -11
  92. package/components/StatusBadge.vue +77 -0
  93. package/components/Tabbed/Tab.vue +3 -3
  94. package/components/Tabbed/index.vue +44 -26
  95. package/components/Wizard.vue +2 -2
  96. package/components/__tests__/AsyncButton.test.ts +2 -2
  97. package/components/__tests__/FixedBanner.test.ts +3 -3
  98. package/components/__tests__/ModalManager.spec.ts +176 -0
  99. package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
  100. package/components/auth/Principal.vue +10 -3
  101. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  102. package/components/form/ArrayList.vue +123 -85
  103. package/components/form/ArrayListGrouped.vue +10 -2
  104. package/components/form/Command.vue +6 -15
  105. package/components/form/EnvVars.vue +16 -8
  106. package/components/form/Footer.vue +8 -5
  107. package/components/form/HealthCheck.vue +3 -3
  108. package/components/form/HookOption.vue +11 -16
  109. package/components/form/KeyValue.vue +16 -7
  110. package/components/form/LabeledSelect.vue +59 -76
  111. package/components/form/LifecycleHooks.vue +3 -3
  112. package/components/form/MatchExpressions.vue +35 -12
  113. package/components/form/NameNsDescription.vue +147 -115
  114. package/components/form/Networking.vue +20 -12
  115. package/components/form/NodeAffinity.vue +31 -23
  116. package/components/form/NodeScheduling.vue +13 -3
  117. package/components/form/Password.vue +11 -5
  118. package/components/form/PodAffinity.vue +43 -44
  119. package/components/form/Probe.vue +68 -66
  120. package/components/form/ResourceQuota/Project.vue +5 -1
  121. package/components/form/ResourceSelector.vue +7 -9
  122. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +6 -3
  123. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +12 -1
  124. package/components/form/SSHKnownHosts/index.vue +16 -2
  125. package/components/form/Security.vue +54 -56
  126. package/components/form/Select.vue +41 -7
  127. package/components/form/ShellInput.vue +5 -1
  128. package/components/form/Tolerations.vue +5 -1
  129. package/components/form/UnitInput.vue +2 -2
  130. package/components/form/ValueFromResource.vue +134 -121
  131. package/components/form/WorkloadPorts.vue +18 -18
  132. package/components/form/__tests__/ArrayList.test.ts +5 -2
  133. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  134. package/components/form/__tests__/NameNsDescription.test.ts +115 -14
  135. package/components/form/__tests__/Probe.test.ts +12 -8
  136. package/components/form/__tests__/SSHKnownHosts.test.ts +11 -0
  137. package/components/form/__tests__/Select.test.ts +37 -0
  138. package/components/form/__tests__/UnitInput.test.ts +4 -5
  139. package/components/formatter/BadgeStateFormatter.vue +8 -5
  140. package/components/formatter/InternalExternalIP.vue +2 -0
  141. package/components/formatter/SecretData.vue +20 -7
  142. package/components/nav/Favorite.vue +5 -1
  143. package/components/nav/Group.vue +60 -27
  144. package/components/nav/Header.vue +39 -13
  145. package/components/nav/Jump.vue +7 -0
  146. package/components/nav/NamespaceFilter.vue +14 -8
  147. package/components/nav/Pinned.vue +1 -1
  148. package/components/nav/TopLevelMenu.vue +5 -17
  149. package/components/nav/Type.vue +32 -35
  150. package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
  151. package/components/templates/blank.vue +4 -1
  152. package/components/templates/default.vue +8 -0
  153. package/components/templates/home.vue +10 -1
  154. package/components/templates/plain.vue +10 -1
  155. package/package.json +1 -1
  156. package/store/type-map.js +29 -2
  157. package/utils/error.js +30 -8
  158. package/utils/errorTranslate.json +916 -0
  159. package/components/formatter/ExtensionCache.vue +0 -74
  160. package/components/formatter/Port.vue +0 -24
  161. package/components/formatter/SecretType.vue +0 -41
@@ -921,6 +921,7 @@ export default {
921
921
  display: flex;
922
922
  > i {
923
923
  padding-right: 5px;
924
+ font-size: 10px;
924
925
  }
925
926
  }
926
927
 
@@ -932,6 +933,8 @@ export default {
932
933
 
933
934
  .ns-filter-input {
934
935
  height: 24px;
936
+ border: 1px solid #d7d7d7;
937
+ padding: 0px 5px;
935
938
  }
936
939
 
937
940
  .ns-filter-clear {
@@ -946,7 +949,8 @@ export default {
946
949
 
947
950
  .ns-dropdown-menu {
948
951
  background-color: var(--header-bg);
949
- border: 1px solid var(--primary-border);
952
+ /* border: 1px solid var(--primary-border); */
953
+ border: 1px solid #d7d7d7;;
950
954
  border-bottom-left-radius: var(--border-radius);
951
955
  border-bottom-right-radius: var(--border-radius);
952
956
  color: var(--header-btn-text);
@@ -966,10 +970,10 @@ export default {
966
970
  }
967
971
 
968
972
  .ns-divider {
969
- border-top: 1px solid var(--border);
973
+ /* border-top: 1px solid var(--border); */
970
974
  cursor: default;
971
- margin-top: 10px;
972
- padding-bottom: 10px;
975
+ /* margin-top: 10px; */
976
+ /* padding-bottom: 10px; */
973
977
  }
974
978
 
975
979
  .ns-option {
@@ -1009,7 +1013,7 @@ export default {
1009
1013
  &.ns-selected:not(:hover) {
1010
1014
  .ns-item {
1011
1015
  > * {
1012
- color: var(--primary);
1016
+ /* color: var(--primary); */
1013
1017
  }
1014
1018
  }
1015
1019
 
@@ -1062,7 +1066,7 @@ export default {
1062
1066
  border-radius: var(--border-radius);
1063
1067
  color: var(--header-btn-text);
1064
1068
  cursor: pointer;
1065
- height: 40px;
1069
+ height: 32px;
1066
1070
  padding: 0 10px;
1067
1071
  position: relative;
1068
1072
  z-index: z-index('dropdownOverlay');
@@ -1070,7 +1074,8 @@ export default {
1070
1074
  &.ns-open {
1071
1075
  border-bottom-left-radius: 0;
1072
1076
  border-bottom-right-radius: 0;
1073
- border-color: var(--primary-border);
1077
+ /* border-color: var(--primary-border); */
1078
+ border-color: #d7d7d7;
1074
1079
  }
1075
1080
 
1076
1081
  > .ns-values {
@@ -1109,12 +1114,13 @@ export default {
1109
1114
  border-radius: 5px;
1110
1115
  color: var(--tag-text);
1111
1116
  display: flex;
1112
- line-height: 20px;
1117
+ /* line-height: 20px; */
1113
1118
  padding: 2px 5px;
1114
1119
  white-space: nowrap;
1115
1120
 
1116
1121
  > i {
1117
1122
  margin-left: 5px;
1123
+ font-size: 8px;
1118
1124
 
1119
1125
  &:hover {
1120
1126
  color: var(--primary);
@@ -36,7 +36,7 @@ export default {
36
36
  :aria-checked="!!pinned"
37
37
  class="pin icon"
38
38
  :class="{'icon-pin-outlined': !pinned, 'icon-pin': pinned}"
39
- aria-role="button"
39
+ role="button"
40
40
  :aria-label="t('nav.ariaLabel.pinCluster', { cluster: cluster.label })"
41
41
  @click.stop.prevent="toggle"
42
42
  @keydown.enter.prevent="toggle"
@@ -14,7 +14,6 @@ import { SETTING } from '@shell/config/settings';
14
14
  import { getProductFromRoute } from '@shell/utils/router';
15
15
  import { isRancherPrime } from '@shell/config/version';
16
16
  import Pinned from '@shell/components/nav/Pinned';
17
- import { getGlobalBannerFontSizes } from '@shell/utils/banners';
18
17
  import { TopLevelMenuHelperPagination, TopLevelMenuHelperLegacy } from '@shell/components/nav/TopLevelMenu.helper';
19
18
  import { debounce } from 'lodash';
20
19
  import { sameContents } from '@shell/utils/array';
@@ -82,15 +81,6 @@ export default {
82
81
  return this.$store.getters['prefs/get'](PINNED_CLUSTERS);
83
82
  },
84
83
 
85
- sideMenuStyle() {
86
- const globalBannerSettings = getGlobalBannerFontSizes(this.$store);
87
-
88
- return {
89
- marginBottom: globalBannerSettings?.footerFont,
90
- marginTop: globalBannerSettings?.headerFont
91
- };
92
- },
93
-
94
84
  showClusterSearch() {
95
85
  return this.allClustersCount > this.maxClustersToShow;
96
86
  },
@@ -440,8 +430,7 @@ export default {
440
430
 
441
431
  return {
442
432
  content,
443
- placement: 'right',
444
- popperOptions: { modifiers: { preventOverflow: { enabled: false }, hide: { enabled: false } } },
433
+ placement: 'right',
445
434
  popperClass
446
435
  };
447
436
  },
@@ -483,7 +472,6 @@ export default {
483
472
  data-testid="side-menu"
484
473
  class="side-menu"
485
474
  :class="{'menu-open': shown, 'menu-close':!shown}"
486
- :style="sideMenuStyle"
487
475
  tabindex="-1"
488
476
  role="navigation"
489
477
  :aria-label="t('nav.ariaLabel.topLevelMenu')"
@@ -710,7 +698,7 @@ export default {
710
698
  v-if="clustersFiltered.length > 0"
711
699
  class="category-title"
712
700
  >
713
- <hr>
701
+ <hr role="none">
714
702
  </div>
715
703
  </div>
716
704
 
@@ -824,7 +812,7 @@ export default {
824
812
  <div
825
813
  class="category-title"
826
814
  >
827
- <hr>
815
+ <hr role="none">
828
816
  <span>
829
817
  {{ t('nav.categories.multiCluster') }}
830
818
  </span>
@@ -857,7 +845,7 @@ export default {
857
845
  <div
858
846
  class="category-title"
859
847
  >
860
- <hr>
848
+ <hr role="none">
861
849
  <span>
862
850
  {{ t('nav.categories.configuration') }}
863
851
  </span>
@@ -1002,7 +990,7 @@ export default {
1002
990
  }
1003
991
  }
1004
992
 
1005
- position: fixed;
993
+ position: absolute;
1006
994
  top: 0;
1007
995
  left: 0px;
1008
996
  bottom: 0;
@@ -58,7 +58,18 @@ export default {
58
58
 
59
59
  isActive() {
60
60
  const typeFullPath = this.$router.resolve(this.type.route)?.fullPath.toLowerCase();
61
- const pageFullPath = this.$route.fullPath?.toLowerCase();
61
+ const pageFullPath = this.$route.fullPath?.toLowerCase().split('#')[0]; // Ignore the shebang when comparing routes
62
+ const routeMetaNav = this.$route.meta?.nav;
63
+
64
+ // If the route explicitly declares the nav path that should be highlighted, then use that
65
+ if (routeMetaNav) {
66
+ const cluster = this.$route.params?.cluster;
67
+ const navPath = routeMetaNav.replace(':cluster', cluster);
68
+
69
+ if (navPath === typeFullPath) {
70
+ return true;
71
+ }
72
+ }
62
73
 
63
74
  if ( !this.type.exact) {
64
75
  const typeSplit = typeFullPath.split('/');
@@ -122,6 +133,7 @@ export default {
122
133
  :aria-label="type.labelKey ? t(type.labelKey) : (type.labelDisplay || type.label)"
123
134
  :href="href"
124
135
  class="type-link"
136
+ :aria-current="isActive ? 'page' : undefined"
125
137
  @click="selectType(); navigate($event);"
126
138
  @mouseenter="setNear(true)"
127
139
  @mouseleave="setNear(false)"
@@ -136,7 +148,7 @@ export default {
136
148
  class="label"
137
149
  :class="{'no-icon': !type.icon}"
138
150
  />
139
- <span
151
+ <!-- <span
140
152
  v-if="showFavorite || namespaceIcon || showCount"
141
153
  class="count"
142
154
  >
@@ -154,7 +166,7 @@ export default {
154
166
  v-if="showCount"
155
167
  data-testid="type-count"
156
168
  >{{ count }}</span>
157
- </span>
169
+ </span> -->
158
170
  </a>
159
171
  </li>
160
172
  </router-link>
@@ -199,6 +211,7 @@ export default {
199
211
  .label {
200
212
  align-items: center;
201
213
  grid-area: label;
214
+ display: flex;
202
215
  overflow: hidden;
203
216
  text-overflow: ellipsis;
204
217
 
@@ -206,13 +219,13 @@ export default {
206
219
  padding-left: 3px;
207
220
  }
208
221
 
209
- :deep() .highlight {
222
+ ::v-deep .highlight {
210
223
  background: var(--diff-ins-bg);
211
224
  color: var(--body-text);
212
225
  padding: 2px;
213
226
  }
214
227
 
215
- :deep() .icon {
228
+ ::v-deep .icon {
216
229
  position: relative;
217
230
  color: var(--muted);
218
231
  }
@@ -225,20 +238,24 @@ export default {
225
238
  grid-column-gap: 5px;
226
239
  font-size: 14px;
227
240
  line-height: 24px;
228
- padding: 7.5px 7px 7.5px 10px;
241
+ /* padding: 7.5px 7px 7.5px 10px; */
242
+ padding: 0px 16px 0px 40px;
243
+ height: 50px;
229
244
  margin: 0 0 0 -3px;
230
245
  overflow: hidden;
231
246
  text-overflow: ellipsis;
232
247
  white-space: nowrap;
233
248
  color: var(--body-text);
234
- height: 33px;
235
249
 
236
250
  &:hover {
237
- background: var(--nav-hover);
251
+ background: var(--nav-active);
252
+ color: var(--nav-hover-color);
253
+ /* background: var(--nav-hover); */
238
254
  text-decoration: none;
239
255
 
240
- :deep() .icon {
241
- color: var(--body-text);
256
+ ::v-deep .icon {
257
+ /* color: var(--body-text); */
258
+ color: var(--nav-hover-color);
242
259
  }
243
260
  }
244
261
  }
@@ -247,44 +264,24 @@ export default {
247
264
  grid-area: favorite;
248
265
  font-size: 12px;
249
266
  position: relative;
250
- vertical-align: middle;
251
- margin-right: 4px;
252
267
  }
253
268
 
254
269
  .count {
270
+ grid-area: count;
255
271
  font-size: 12px;
272
+ text-align: right;
256
273
  justify-items: center;
257
274
  padding-right: 4px;
258
- display: flex;
259
- align-items: center;
260
- }
261
-
262
- &.nav-type.nav-link {
263
- a .label {
264
- display: flex;
265
- }
266
275
  }
267
276
 
268
277
  &.nav-type:not(.depth-0) {
269
- A {
270
- padding-left: 16px;
271
- }
272
-
273
- :deep() .label I {
274
- padding-right: 2px;
275
- }
276
- }
277
-
278
- &.nav-type:is(.depth-1) {
279
278
  A {
280
279
  font-size: 13px;
281
- padding-left: 23px;
280
+ padding: 5.5px 7px 5.5px 10px;
282
281
  }
283
- }
284
282
 
285
- &.nav-type:not(.depth-0):not(.depth-1) {
286
- A {
287
- padding-left: 14px;
283
+ ::v-deep .label I {
284
+ padding-right: 2px;
288
285
  }
289
286
  }
290
287
  }
@@ -1,5 +1,4 @@
1
1
  import TopLevelMenu from '@shell/components/nav/TopLevelMenu.vue';
2
- import { SETTING } from '@shell/config/settings';
3
2
  import { mount, Wrapper } from '@vue/test-utils';
4
3
  import { CAPI, COUNT, MANAGEMENT } from '@shell/config/types';
5
4
  import { PINNED_CLUSTERS } from '@shell/store/prefs';
@@ -429,45 +428,6 @@ describe('topLevelMenu', () => {
429
428
  expect(description4.text()).toStrictEqual('some-description4');
430
429
  });
431
430
 
432
- it('should not "crash" the component if the structure of banner settings is in an old format', async() => {
433
- const wrapper: Wrapper<InstanceType<typeof TopLevelMenu>> = mount(TopLevelMenu, {
434
- global: {
435
- mocks: {
436
- $route: {},
437
- $store: {
438
- ...generateStore(
439
- [{ name: 'whatever' }],
440
- [
441
- // object based on https://github.com/rancher/dashboard/issues/10140#issuecomment-1883252402
442
- {
443
- id: SETTING.BANNERS,
444
- value: JSON.stringify({
445
- banner: {
446
- color: '#78c9cf',
447
- background: '#27292e',
448
- text: 'Hello World!'
449
- },
450
- showHeader: 'true',
451
- showFooter: 'true'
452
- })
453
- }
454
- ]
455
- ),
456
- }
457
- },
458
-
459
- stubs: ['BrandImage', 'router-link'],
460
- },
461
- });
462
-
463
- await waitForIt();
464
-
465
- expect(wrapper.vm.sideMenuStyle).toStrictEqual({
466
- marginBottom: '2em',
467
- marginTop: '2em'
468
- });
469
- });
470
-
471
431
  describe('searching a term', () => {
472
432
  describe('should displays a no results message if have clusters but', () => {
473
433
  it('given no matching clusters', async() => {
@@ -9,7 +9,10 @@ export default {
9
9
  </script>
10
10
 
11
11
  <template>
12
- <main class="main-layout">
12
+ <main
13
+ class="main-layout"
14
+ :aria-label="t('layouts.blank')"
15
+ >
13
16
  <router-view :key="$route.path" />
14
17
 
15
18
  <Inactivity />
@@ -7,6 +7,8 @@ import {
7
7
  } from '@shell/store/prefs';
8
8
  import ActionMenu from '@shell/components/ActionMenu';
9
9
  import GrowlManager from '@shell/components/GrowlManager';
10
+ import ModalManager from '@shell/components/ModalManager';
11
+ import SlideInPanelManager from '@shell/components/SlideInPanelManager';
10
12
  import WindowManager from '@shell/components/nav/WindowManager';
11
13
  import PromptRemove from '@shell/components/PromptRemove';
12
14
  import PromptRestore from '@shell/components/PromptRestore';
@@ -39,6 +41,8 @@ export default {
39
41
  Header,
40
42
  ActionMenu,
41
43
  GrowlManager,
44
+ ModalManager,
45
+ SlideInPanelManager,
42
46
  WindowManager,
43
47
  FixedBanner,
44
48
  AwsComplianceBanner,
@@ -198,6 +202,7 @@ export default {
198
202
  <main
199
203
  v-if="clusterAndRouteReady"
200
204
  class="main-layout"
205
+ :aria-label="t('layouts.default')"
201
206
  >
202
207
  <router-view
203
208
  :key="$route.path"
@@ -208,6 +213,7 @@ export default {
208
213
  <PromptRestore />
209
214
  <AssignTo />
210
215
  <PromptModal />
216
+ <ModalManager />
211
217
  <button
212
218
  v-if="noLocaleShortcut"
213
219
  v-shortkey.once="['shift','l']"
@@ -235,6 +241,7 @@ export default {
235
241
  <main
236
242
  v-else-if="unmatchedRoute"
237
243
  class="main-layout"
244
+ :aria-label="t('layouts.default')"
238
245
  >
239
246
  <router-view
240
247
  :key="$route.path"
@@ -257,6 +264,7 @@ export default {
257
264
  </div>
258
265
  <FixedBanner :footer="true" />
259
266
  <GrowlManager />
267
+ <SlideInPanelManager />
260
268
  <Inactivity />
261
269
  <DraggableZone ref="draggableZone" />
262
270
  </div>
@@ -3,6 +3,8 @@ import Header from '@shell/components/nav/Header';
3
3
  import Brand from '@shell/mixins/brand';
4
4
  import FixedBanner from '@shell/components/FixedBanner';
5
5
  import GrowlManager from '@shell/components/GrowlManager';
6
+ import ModalManager from '@shell/components/ModalManager';
7
+ import SlideInPanelManager from '@shell/components/SlideInPanelManager';
6
8
  import { mapPref, THEME_SHORTCUT } from '@shell/store/prefs';
7
9
  import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
8
10
  import AzureWarning from '@shell/components/auth/AzureWarning';
@@ -17,6 +19,8 @@ export default {
17
19
  Header,
18
20
  FixedBanner,
19
21
  GrowlManager,
22
+ ModalManager,
23
+ SlideInPanelManager,
20
24
  AzureWarning,
21
25
  AwsComplianceBanner,
22
26
  Inactivity,
@@ -58,6 +62,7 @@ export default {
58
62
  <AwsComplianceBanner />
59
63
  <AzureWarning />
60
64
  <PromptModal />
65
+ <ModalManager />
61
66
  <div
62
67
  class="dashboard-content"
63
68
  :class="{'dashboard-padding-left': showTopLevelMenu}"
@@ -67,7 +72,10 @@ export default {
67
72
  :simple="true"
68
73
  />
69
74
 
70
- <main class="main-layout">
75
+ <main
76
+ class="main-layout"
77
+ :aria-label="t('layouts.home')"
78
+ >
71
79
  <router-view
72
80
  :key="$route.path"
73
81
  class="outlet"
@@ -76,6 +84,7 @@ export default {
76
84
  </div>
77
85
  <FixedBanner :footer="true" />
78
86
  <GrowlManager />
87
+ <SlideInPanelManager />
79
88
  <button
80
89
  v-if="themeShortcut"
81
90
  v-shortkey.once="['shift','t']"
@@ -8,6 +8,8 @@ import IndentedPanel from '@shell/components/IndentedPanel';
8
8
  import Brand from '@shell/mixins/brand';
9
9
  import FixedBanner from '@shell/components/FixedBanner';
10
10
  import GrowlManager from '@shell/components/GrowlManager';
11
+ import ModalManager from '@shell/components/ModalManager';
12
+ import SlideInPanelManager from '@shell/components/SlideInPanelManager';
11
13
  import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
12
14
  import AzureWarning from '@shell/components/auth/AzureWarning';
13
15
  import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
@@ -26,6 +28,8 @@ export default {
26
28
  PromptModal,
27
29
  FixedBanner,
28
30
  GrowlManager,
31
+ ModalManager,
32
+ SlideInPanelManager,
29
33
  AwsComplianceBanner,
30
34
  AzureWarning,
31
35
  Inactivity
@@ -68,7 +72,10 @@ export default {
68
72
  :class="{'dashboard-padding-left': showTopLevelMenu}"
69
73
  >
70
74
  <Header :simple="true" />
71
- <main class="main-layout">
75
+ <main
76
+ class="main-layout"
77
+ :aria-label="t('layouts.plain')"
78
+ >
72
79
  <IndentedPanel class="pt-20">
73
80
  <router-view
74
81
  :key="$route.path"
@@ -78,6 +85,7 @@ export default {
78
85
  <ActionMenu />
79
86
  <PromptRemove />
80
87
  <PromptModal />
88
+ <ModalManager />
81
89
  <AssignTo />
82
90
  <button
83
91
  v-if="themeShortcut"
@@ -96,6 +104,7 @@ export default {
96
104
 
97
105
  <FixedBanner :footer="true" />
98
106
  <GrowlManager />
107
+ <SlideInPanelManager />
99
108
  <Inactivity />
100
109
  </div>
101
110
  </template>
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "license": "Apache-2.0",
6
6
  "author": "SUSE",
7
7
  "private": false,
8
- "version": "1.0.00000000112",
8
+ "version": "1.0.00000000113",
9
9
  "engines": {
10
10
  "node": ">=20.0.0"
11
11
  },
package/store/type-map.js CHANGED
@@ -107,6 +107,7 @@
107
107
  // graphConfig: undefined -- Use this to pass along the graph configuration
108
108
  // notFilterNamespace: undefined -- Define namespaces that do not need to be filtered
109
109
  // localOnly: False -- Hide this type from the nav/search bar on downstream clusters
110
+ // custom: any - Custom options for a given type
110
111
  // }
111
112
  // )
112
113
  // ignoreGroup(group): Never show group or any types in it
@@ -508,6 +509,12 @@ export const getters = {
508
509
  return state.basicTypes?.[product]?.[schemaId];
509
510
  };
510
511
  },
512
+
513
+ groupsForVirTypes(state) {
514
+ return (product, name) => {
515
+ return state.virtualTypes?.[product].find(item => item.name === name)?.icon;
516
+ };
517
+ },
511
518
 
512
519
  optionsFor(state, getters, rootState, rootGetters) {
513
520
  const def = {
@@ -524,6 +531,7 @@ export const getters = {
524
531
  depaginate: false,
525
532
  customRoute: undefined,
526
533
  resourceEditMasthead: true,
534
+ custom: {},
527
535
  };
528
536
 
529
537
  return (schemaOrType, pagination) => {
@@ -657,7 +665,18 @@ export const getters = {
657
665
 
658
666
  const label = typeObj.labelKey ? rootGetters['i18n/t'](typeObj.labelKey) || typeObj.label : typeObj.label;
659
667
 
660
- const labelDisplay = highlightLabel(label, count, typeObj.schema);
668
+ const virtual = !!typeObj.virtual;
669
+ let icon = typeObj.icon;
670
+
671
+ if ( (!virtual || typeObj.isSpoofed ) && !icon ) {
672
+ if ( namespaced ) {
673
+ icon = 'folder';
674
+ } else {
675
+ icon = 'globe';
676
+ }
677
+ }
678
+
679
+ const labelDisplay = highlightLabel(label,icon, count, typeObj.schema);
661
680
 
662
681
  if ( !labelDisplay ) {
663
682
  // Search happens in highlight and returns null if not found
@@ -765,7 +784,7 @@ export const getters = {
765
784
  return group;
766
785
  }
767
786
 
768
- function highlightLabel(original, count, schema) {
787
+ function highlightLabel(original, icon, count, schema) {
769
788
  let label = escapeHtml(original);
770
789
 
771
790
  if ( searchRegex ) {
@@ -789,6 +808,12 @@ export const getters = {
789
808
  }
790
809
  }
791
810
 
811
+ if ( icon ) {
812
+ console.log(icon)
813
+ label = `<i class="icon icon-fw icon-${ icon }"></i>${ label }`;
814
+ console.log(label)
815
+ }
816
+
792
817
  return label;
793
818
  }
794
819
  };
@@ -1729,6 +1754,8 @@ export const mutations = {
1729
1754
  let obj = { ...options, match };
1730
1755
 
1731
1756
  if ( idx >= 0 ) {
1757
+ // Merge the custom data object - multiple configures will update existing rather than overwrite
1758
+ obj.custom = Object.assign(state.typeOptions[idx].custom || {}, obj.custom || {});
1732
1759
  obj = Object.assign(state.typeOptions[idx], obj);
1733
1760
  state.typeOptions.splice(idx, 1, obj);
1734
1761
  } else {
package/utils/error.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { isArray } from '@shell/utils/array';
2
2
 
3
+ import translations from './errorTranslate.json';
4
+
3
5
  export class ClusterNotFoundError extends Error {
4
6
  static NAME = 'ClusterNotFoundError'
5
7
 
@@ -48,7 +50,7 @@ export class ApiError extends Error {
48
50
 
49
51
  export function stringify(err) {
50
52
  let str;
51
-
53
+ console.log('00043', err);
52
54
  if ( typeof err === 'string' ) {
53
55
  str = err;
54
56
  } else if ( err && typeof err === 'object' ) {
@@ -60,7 +62,7 @@ export function stringify(err) {
60
62
  const data = JSON.parse(str).data;
61
63
 
62
64
  if (data) {
63
- return data;
65
+ return translateError(data);
64
66
  }
65
67
  } catch {}
66
68
  }
@@ -84,24 +86,30 @@ export function stringify(err) {
84
86
  str = JSON.stringify(err);
85
87
  }
86
88
 
87
- return str;
89
+ return translateError(str);
88
90
  }
89
91
 
90
92
  export function exceptionToErrorsArray(err) {
93
+ console.log('00055', err);
91
94
  if ( err?.response?.data ) {
92
95
  const body = err.response.data;
93
96
 
94
97
  if ( body && body.message ) {
95
- return [body.message];
98
+ return [translateError(body.message)];
96
99
  } else {
97
- return [err];
100
+ return [translateError(err)];
98
101
  }
99
102
  } else if (err.status && err.message) {
100
- return [err.message];
103
+ return [translateError(err.message)];
101
104
  } else if ( isArray(err) ) {
102
- return err;
105
+ let arr = []
106
+ for (let index = 0; index < err.length; index++) {
107
+ arr[index] = translateError(err[index])
108
+ }
109
+
110
+ return [...new Set(arr)];
103
111
  } else {
104
- return [err];
112
+ return [translateError(err)];
105
113
  }
106
114
  }
107
115
 
@@ -129,3 +137,17 @@ export const normalizeError = (err) => {
129
137
  statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
130
138
  };
131
139
  };
140
+ export function translateError(error) {
141
+ const originError = error;
142
+ error = error.toLowerCase();
143
+ console.log('00044', error);
144
+
145
+ // Apply translations from JSON
146
+ for (const translation of translations.translations) {
147
+ if (error.includes(translation.pattern)) {
148
+ error = error.replace(new RegExp(translation.pattern, 'g'), translation.replacement);
149
+ }
150
+ }
151
+
152
+ return error;
153
+ }