@rancher/shell 3.0.2-rc.2 → 3.0.2-rc.3

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 (141) hide show
  1. package/assets/styles/base/_basic.scss +5 -7
  2. package/assets/styles/global/_button.scss +10 -0
  3. package/assets/styles/global/_tooltip.scss +2 -2
  4. package/assets/styles/themes/_dark.scss +14 -2
  5. package/assets/styles/themes/_light.scss +7 -2
  6. package/assets/styles/vendor/vue-select.scss +4 -0
  7. package/assets/translations/en-us.yaml +44 -5
  8. package/components/BannerGraphic.vue +0 -42
  9. package/components/ButtonMultiAction.vue +1 -1
  10. package/components/Carousel.vue +36 -29
  11. package/components/CommunityLinks.vue +6 -1
  12. package/components/GrowlManager.vue +9 -2
  13. package/components/LocaleSelector.vue +8 -1
  14. package/components/PaginatedResourceTable.vue +4 -7
  15. package/components/ProgressBarMulti.vue +14 -0
  16. package/components/Questions/Reference.vue +57 -28
  17. package/components/SelectIconGrid.vue +12 -1
  18. package/components/SideNav.vue +12 -38
  19. package/components/SortableTable/index.vue +1 -0
  20. package/components/Tabbed/index.vue +12 -1
  21. package/components/YamlEditor.vue +1 -0
  22. package/components/auth/Principal.vue +5 -3
  23. package/components/fleet/FleetClusters.vue +82 -1
  24. package/components/fleet/FleetRepos.vue +13 -30
  25. package/components/fleet/ForceDirectedTreeChart/index.vue +2 -2
  26. package/components/form/ChangePassword.vue +2 -0
  27. package/components/form/ColorInput.vue +24 -1
  28. package/components/form/FileSelector.vue +2 -0
  29. package/components/form/KeyValue.vue +230 -160
  30. package/components/form/LabeledSelect.vue +1 -1
  31. package/components/form/PlusMinus.vue +14 -2
  32. package/components/form/ResourceLabeledSelect.vue +13 -53
  33. package/components/form/ResourceSelector.vue +1 -0
  34. package/components/form/ResourceTabs/index.vue +79 -36
  35. package/components/form/SecretSelector.vue +2 -2
  36. package/components/form/__tests__/KeyValue.test.ts +1 -1
  37. package/components/formatter/FleetClusterSummaryGraph.vue +2 -2
  38. package/components/formatter/FleetSummaryGraph.vue +6 -7
  39. package/components/formatter/WorkloadHealthScale.vue +7 -0
  40. package/components/nav/Group.vue +30 -4
  41. package/components/nav/Header.vue +82 -114
  42. package/components/nav/HeaderPageActionMenu.vue +27 -131
  43. package/components/nav/NamespaceFilter.vue +1 -1
  44. package/components/nav/Type.vue +15 -0
  45. package/config/home-links.js +21 -13
  46. package/config/labels-annotations.js +2 -0
  47. package/config/page-actions.js +1 -0
  48. package/config/pagination-table-headers.js +15 -1
  49. package/config/product/explorer.js +7 -17
  50. package/config/table-headers.js +6 -0
  51. package/config/version.js +5 -1
  52. package/core/plugin.ts +41 -1
  53. package/core/plugins.js +125 -72
  54. package/core/types-provisioning.ts +91 -2
  55. package/core/types.ts +55 -0
  56. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +12 -3
  57. package/detail/catalog.cattle.io.app.vue +1 -1
  58. package/detail/fleet.cattle.io.cluster.vue +3 -3
  59. package/detail/namespace.vue +13 -19
  60. package/detail/networking.k8s.io.ingress.vue +13 -53
  61. package/detail/provisioning.cattle.io.cluster.vue +12 -1
  62. package/detail/workload/index.vue +3 -3
  63. package/dialog/AddCustomBadgeDialog.vue +5 -1
  64. package/edit/auth/ldap/__tests__/config.test.ts +18 -0
  65. package/edit/auth/ldap/config.vue +24 -0
  66. package/edit/auth/saml.vue +8 -6
  67. package/edit/fleet.cattle.io.gitrepo.vue +7 -1
  68. package/edit/logging-flow/index.vue +4 -19
  69. package/edit/networking.k8s.io.ingress/index.vue +18 -65
  70. package/edit/networking.k8s.io.networkpolicy/index.vue +4 -5
  71. package/edit/provisioning.cattle.io.cluster/index.vue +13 -1
  72. package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -115
  73. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +2 -2
  74. package/edit/provisioning.cattle.io.cluster/tabs/networking/ACE.vue +14 -28
  75. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +25 -12
  76. package/edit/service.vue +1 -2
  77. package/list/networking.k8s.io.ingress.vue +1 -1
  78. package/list/node.vue +15 -8
  79. package/list/persistentvolume.vue +12 -4
  80. package/list/service.vue +1 -1
  81. package/list/workload.vue +4 -0
  82. package/mixins/chart.js +4 -1
  83. package/models/catalog.cattle.io.app.js +3 -1
  84. package/models/catalog.cattle.io.clusterrepo.js +56 -7
  85. package/models/fleet.cattle.io.bundle.js +0 -11
  86. package/models/fleet.cattle.io.cluster.js +17 -1
  87. package/models/fleet.cattle.io.gitrepo.js +86 -50
  88. package/models/provisioning.cattle.io.cluster.js +47 -2
  89. package/models/service.js +1 -0
  90. package/models/workload.js +19 -1
  91. package/package.json +5 -4
  92. package/pages/c/_cluster/apps/charts/index.vue +4 -0
  93. package/pages/c/_cluster/explorer/ConfigBadge.vue +8 -7
  94. package/pages/c/_cluster/explorer/index.vue +13 -6
  95. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +3 -3
  96. package/pages/c/_cluster/fleet/index.vue +75 -89
  97. package/pages/c/_cluster/settings/links.vue +2 -2
  98. package/pages/diagnostic.vue +17 -15
  99. package/pages/home.vue +32 -6
  100. package/plugins/clean-html.js +50 -0
  101. package/plugins/dashboard-store/resource-class.js +4 -0
  102. package/plugins/plugin.js +54 -49
  103. package/plugins/steve/mutations.js +1 -1
  104. package/plugins/steve/steve-class.js +8 -0
  105. package/plugins/steve/steve-pagination-utils.ts +3 -1
  106. package/rancher-components/Accordion/Accordion.vue +4 -4
  107. package/rancher-components/BadgeState/BadgeState.vue +7 -0
  108. package/rancher-components/Card/Card.vue +27 -1
  109. package/rancher-components/Form/Checkbox/Checkbox.vue +9 -2
  110. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  111. package/rancher-components/Form/LabeledInput/LabeledInput.vue +18 -1
  112. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +39 -2
  113. package/rancher-components/RcButton/RcButton.vue +90 -0
  114. package/rancher-components/RcButton/index.ts +2 -0
  115. package/rancher-components/RcButton/types.ts +17 -0
  116. package/rancher-components/RcDropdown/RcDropdown.vue +111 -0
  117. package/rancher-components/RcDropdown/RcDropdownItem.vue +127 -0
  118. package/rancher-components/RcDropdown/RcDropdownSeparator.vue +6 -0
  119. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +43 -0
  120. package/rancher-components/RcDropdown/index.ts +4 -0
  121. package/rancher-components/RcDropdown/types.ts +22 -0
  122. package/rancher-components/RcDropdown/useDropdownCollection.ts +45 -0
  123. package/rancher-components/RcDropdown/useDropdownContext.ts +83 -0
  124. package/scripts/test-plugins-build.sh +2 -0
  125. package/scripts/typegen.sh +2 -0
  126. package/store/catalog.js +1 -1
  127. package/tsconfig.json +2 -1
  128. package/types/components/paginatedResourceTable.ts +25 -0
  129. package/types/components/resourceLabeledSelect.ts +48 -0
  130. package/types/resources/fleet.d.ts +17 -0
  131. package/types/shell/index.d.ts +61 -0
  132. package/utils/auth.js +5 -1
  133. package/utils/cluster.js +106 -0
  134. package/utils/fleet.ts +35 -3
  135. package/utils/ingress.ts +64 -0
  136. package/utils/uiplugins.ts +56 -44
  137. package/utils/validators/cron-schedule.js +7 -2
  138. package/utils/validators/formRules/__tests__/index.test.ts +53 -17
  139. package/utils/validators/formRules/index.ts +20 -5
  140. package/vue.config.js +1 -1
  141. package/components/RelatedWorkloadsTable.vue +0 -50
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  import { LabeledInput } from '@components/Form/LabeledInput';
3
- import LabeledSelect from '@shell/components/form/LabeledSelect';
4
- import { filterBy } from '@shell/utils/array';
3
+ import ResourceLabeledSelect from '@shell/components/form/ResourceLabeledSelect.vue';
4
+ import { PaginationParamFilter } from '@shell/types/store/pagination.types';
5
+
5
6
  import { PVC, STORAGE_CLASS } from '@shell/config/types';
6
7
  import Question from './Question';
7
8
 
@@ -14,7 +15,7 @@ const LEGACY_MAP = {
14
15
  export default {
15
16
  emits: ['update:value'],
16
17
 
17
- components: { LabeledInput, LabeledSelect },
18
+ components: { LabeledInput, ResourceLabeledSelect },
18
19
  mixins: [Question],
19
20
 
20
21
  props: {
@@ -29,12 +30,6 @@ export default {
29
30
  },
30
31
  },
31
32
 
32
- async fetch() {
33
- if ( this.typeSchema ) {
34
- this.all = await this.$store.dispatch(`${ this.inStore }/findAll`, { type: this.typeName });
35
- }
36
- },
37
-
38
33
  data() {
39
34
  const t = this.question.type;
40
35
 
@@ -57,29 +52,61 @@ export default {
57
52
  return {
58
53
  typeName,
59
54
  typeSchema,
60
- all: [],
55
+ all: [],
56
+ allResourceSettings: {
57
+ updateResources: (all) => {
58
+ // Filter to only include required namespaced resources
59
+ const resources = this.isNamespaced ? all.filter((r) => r.metadata.namespace === this.targetNamespace) : all;
60
+
61
+ return this.mapResourcesToOptions(resources);
62
+ }
63
+ },
64
+ paginateResourceSetting: {
65
+ updateResources: (resources) => {
66
+ return this.mapResourcesToOptions(resources);
67
+ },
68
+ /**
69
+ * of type PaginateTypeOverridesFn
70
+ * @param [LabelSelectPaginationFunctionOptions] opts
71
+ * @returns LabelSelectPaginationFunctionOptions
72
+ */
73
+ requestSettings: (opts) => {
74
+ // Filter to only include required namespaced resources
75
+ const filters = this.isNamespaced ? [
76
+ PaginationParamFilter.createSingleField({ field: 'metadata.namespace', value: this.targetNamespace }),
77
+ ] : [];
78
+
79
+ return {
80
+ ...opts,
81
+ filters,
82
+ groupByNamespace: false,
83
+ classify: true,
84
+ };
85
+ }
86
+ },
61
87
  };
62
88
  },
63
89
 
90
+ methods: {
91
+ mapResourcesToOptions(resources) {
92
+ return resources.map((r) => {
93
+ if (r.id) {
94
+ return {
95
+ label: r.nameDisplay || r.metadata.name,
96
+ value: r.metadata.name
97
+ };
98
+ } else {
99
+ return r;
100
+ }
101
+ });
102
+ },
103
+
104
+ },
105
+
64
106
  computed: {
65
107
  isNamespaced() {
66
108
  return !!this.typeSchema?.attributes?.namespaced;
67
109
  },
68
-
69
- options() {
70
- let out = this.all;
71
-
72
- if ( this.isNamespaced ) {
73
- out = filterBy(this.all, 'metadata.namespace', this.targetNamespace);
74
- }
75
-
76
- return out.map((x) => {
77
- return {
78
- label: x.nameDisplay || x.metadata.name,
79
- value: x.metadata.name
80
- };
81
- });
82
- }
83
110
  },
84
111
  };
85
112
  </script>
@@ -90,15 +117,17 @@ export default {
90
117
  class="row"
91
118
  >
92
119
  <div class="col span-6">
93
- <LabeledSelect
94
- :mode="mode"
95
- :options="options"
120
+ <ResourceLabeledSelect
121
+ :resource-type="typeName"
122
+ :in-store="inStore"
96
123
  :disabled="$fetchState.pending || disabled"
97
124
  :label="displayLabel"
98
125
  :placeholder="question.description"
99
126
  :required="question.required"
100
127
  :value="value"
101
128
  :tooltip="displayTooltip"
129
+ :paginated-resource-settings="paginateResourceSetting"
130
+ :all-resources-settings="allResourceSettings"
102
131
  @update:value="!$fetchState.pending && $emit('update:value', $event)"
103
132
  />
104
133
  </div>
@@ -104,6 +104,10 @@ export default {
104
104
  :is="asLink ? 'a' : 'div'"
105
105
  v-for="(r, idx) in rows"
106
106
  :key="get(r, keyField)"
107
+ :role="asLink ? 'link' : null"
108
+ :aria-disabled="asLink && get(r, disabledField) === true ? true : null"
109
+ :aria-label="get(r, nameField)"
110
+ :tabindex="get(r, disabledField) === true ? -1 : 0"
107
111
  :href="asLink ? get(r, linkField) : null"
108
112
  :target="get(r, targetField)"
109
113
  :rel="rel"
@@ -111,9 +115,12 @@ export default {
111
115
  :data-testid="componentTestid + '-' + get(r, nameField)"
112
116
  :class="{
113
117
  'has-description': !!get(r, descriptionField),
114
- 'has-side-label': !!get(r, sideLabelField), [colorFor(r, idx)]: true, disabled: get(r, disabledField) === true
118
+ 'has-side-label': !!get(r, sideLabelField),
119
+ [colorFor(r, idx)]: true,
120
+ disabled: get(r, disabledField) === true
115
121
  }"
116
122
  @click="select(r, idx)"
123
+ @keyup.enter.space="select(r, idx)"
117
124
  >
118
125
  <div
119
126
  class="side-label"
@@ -212,6 +219,10 @@ export default {
212
219
  text-decoration: none !important;
213
220
  color: $color;
214
221
 
222
+ &:focus-visible {
223
+ @include focus-outline;
224
+ }
225
+
215
226
  &:hover:not(.disabled) {
216
227
  box-shadow: 0 0 30px var(--shadow);
217
228
  transition: box-shadow 0.1s ease-in-out;
@@ -19,10 +19,11 @@ import { NAME as EXPLORER } from '@shell/config/product/explorer';
19
19
  import { TYPE_MODES } from '@shell/store/type-map';
20
20
  import { NAME as NAVLINKS } from '@shell/config/product/navlinks';
21
21
  import Group from '@shell/components/nav/Group';
22
+ import LocaleSelector from '@shell/components/LocaleSelector';
22
23
 
23
24
  export default {
24
25
  name: 'SideNav',
25
- components: { Group },
26
+ components: { Group, LocaleSelector },
26
27
  data() {
27
28
  return {
28
29
  groups: [],
@@ -112,9 +113,7 @@ export default {
112
113
  computed: {
113
114
  ...mapState(['managementReady', 'clusterReady']),
114
115
  ...mapGetters(['isStandaloneHarvester', 'productId', 'clusterId', 'currentProduct', 'rootProduct', 'isSingleProduct', 'namespaceMode', 'isExplorer', 'isVirtualCluster']),
115
- ...mapGetters({
116
- locale: 'i18n/selectedLocaleLabel', availableLocales: 'i18n/availableLocales', hasMultipleLocales: 'i18n/hasMultipleLocales'
117
- }),
116
+ ...mapGetters({ locale: 'i18n/selectedLocaleLabel', hasMultipleLocales: 'i18n/hasMultipleLocales' }),
118
117
  ...mapGetters('type-map', ['activeProducts']),
119
118
 
120
119
  favoriteTypes: mapPref(FAVORITE_TYPES),
@@ -360,10 +359,6 @@ export default {
360
359
  });
361
360
  },
362
361
 
363
- switchLocale(locale) {
364
- this.$store.dispatch('i18n/switchTo', locale);
365
- },
366
-
367
362
  syncNav() {
368
363
  const refs = this.$refs.groups;
369
364
 
@@ -427,6 +422,8 @@ export default {
427
422
  <router-link
428
423
  :to="supportLink"
429
424
  class="pull-right"
425
+ role="link"
426
+ :aria-label="t('nav.support', {hasSupport: true})"
430
427
  >
431
428
  {{ t('nav.support', {hasSupport: true}) }}
432
429
  </router-link>
@@ -439,36 +436,11 @@ export default {
439
436
  </span>
440
437
 
441
438
  <!-- locale selector -->
442
- <span v-if="isSingleProduct && hasMultipleLocales && !isStandaloneHarvester">
443
- <v-dropdown
444
- popperClass="localeSelector"
445
- placement="top"
446
- :triggers="['click']"
447
- >
448
- <a
449
- data-testid="locale-selector"
450
- class="locale-chooser"
451
- >
452
- {{ locale }}
453
- </a>
454
-
455
- <template #popper>
456
- <ul
457
- class="list-unstyled dropdown"
458
- style="margin: -1px;"
459
- >
460
- <li
461
- v-for="(label, name) in availableLocales"
462
- :key="name"
463
- class="hand"
464
- @click="switchLocale(name)"
465
- >
466
- {{ label }}
467
- </li>
468
- </ul>
469
- </template>
470
- </v-dropdown>
471
- </span>
439
+ <LocaleSelector
440
+ v-if="isSingleProduct && hasMultipleLocales && !isStandaloneHarvester"
441
+ mode="login"
442
+ :show-icon="false"
443
+ />
472
444
  </div>
473
445
  <!-- SideNav footer alternative -->
474
446
  <div
@@ -478,6 +450,8 @@ export default {
478
450
  <router-link
479
451
  v-if="singleProductAbout"
480
452
  :to="singleProductAbout"
453
+ role="link"
454
+ :aria-label="t('nav.ariaLabel.productAboutPage')"
481
455
  >
482
456
  {{ displayVersion }}
483
457
  </router-link>
@@ -1213,6 +1213,7 @@ export default {
1213
1213
  v-model="eventualSearchQuery"
1214
1214
  type="search"
1215
1215
  class="input-sm search-box"
1216
+ :aria-label="t('sortableTable.searchLabel')"
1216
1217
  :placeholder="t('sortableTable.search')"
1217
1218
  >
1218
1219
  <slot name="header-button" />
@@ -258,7 +258,6 @@ export default {
258
258
  role="tablist"
259
259
  class="tabs"
260
260
  :class="{'clearfix':!sideTabs, 'vertical': sideTabs, 'horizontal': !sideTabs}"
261
- tabindex="0"
262
261
  data-testid="tabbed-block"
263
262
  @keydown.right.prevent="selectNext(1)"
264
263
  @keydown.left.prevent="selectNext(-1)"
@@ -277,8 +276,12 @@ export default {
277
276
  :data-testid="`btn-${tab.name}`"
278
277
  :aria-controls="'#' + tab.name"
279
278
  :aria-selected="tab.active"
279
+ :aria-label="tab.labelDisplay"
280
280
  role="tab"
281
+ tabindex="0"
281
282
  @click.prevent="select(tab.name, $event)"
283
+ @keyup.enter="select(tab.name, $event)"
284
+ @keyup.space="select(tab.name, $event)"
282
285
  >
283
286
  <span>{{ tab.labelDisplay }}</span>
284
287
  <span
@@ -403,6 +406,14 @@ export default {
403
406
  text-decoration: underline;
404
407
  }
405
408
  }
409
+
410
+ &:focus-visible {
411
+ @include focus-outline;
412
+
413
+ span {
414
+ text-decoration: underline;
415
+ }
416
+ }
406
417
  }
407
418
 
408
419
  .conditions-alert-icon {
@@ -126,6 +126,7 @@ export default {
126
126
  cm.indentSelection('subtract');
127
127
  }
128
128
  },
129
+ screenReaderLabel: this.t('import.editor.label'),
129
130
  // @TODO find a better way to display the outline
130
131
  // foldOptions: {
131
132
  // widget: (from, to) => {
@@ -85,9 +85,11 @@ export default {
85
85
  class="name"
86
86
  >
87
87
  <table>
88
- <tr><td>{{ t('principal.name') }}: </td><td>{{ principal.name || principal.loginName }}</td></tr>
89
- <tr><td>{{ t('principal.loginName') }}: </td><td>{{ principal.loginName }}</td></tr>
90
- <tr><td>{{ t('principal.type') }}: </td><td>{{ principal.displayType }}</td></tr>
88
+ <tbody>
89
+ <tr><td>{{ t('principal.name') }}: </td><td>{{ principal.name || principal.loginName }}</td></tr>
90
+ <tr><td>{{ t('principal.loginName') }}: </td><td>{{ principal.loginName }}</td></tr>
91
+ <tr><td>{{ t('principal.type') }}: </td><td>{{ principal.displayType }}</td></tr>
92
+ </tbody>
91
93
  </table>
92
94
  </div>
93
95
  <template v-else>
@@ -1,10 +1,11 @@
1
1
  <script>
2
2
  import ResourceTable from '@shell/components/ResourceTable';
3
+ import Tag from '@shell/components/Tag.vue';
3
4
  import { STATE, NAME, AGE, FLEET_SUMMARY } from '@shell/config/table-headers';
4
5
  import { FLEET, MANAGEMENT } from '@shell/config/types';
5
6
 
6
7
  export default {
7
- components: { ResourceTable },
8
+ components: { ResourceTable, Tag },
8
9
 
9
10
  props: {
10
11
  rows: {
@@ -75,6 +76,12 @@ export default {
75
76
  pluralLabel: this.$store.getters['type-map/labelFor'](schema, 99),
76
77
  };
77
78
  },
79
+ },
80
+
81
+ methods: {
82
+ toggleCustomLabels(row) {
83
+ row['displayCustomLabels'] = !row.displayCustomLabels;
84
+ }
78
85
  }
79
86
  };
80
87
  </script>
@@ -85,6 +92,7 @@ export default {
85
92
  :schema="schema"
86
93
  :headers="headers"
87
94
  :rows="rows"
95
+ :sub-rows="true"
88
96
  :loading="loading"
89
97
  :use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
90
98
  key-field="_key"
@@ -123,5 +131,78 @@ export default {
123
131
  :class="{'text-error': !row.bundleInfo.total}"
124
132
  >{{ row.bundleInfo.total }}</span>
125
133
  </template>
134
+
135
+ <template #sub-row="{fullColspan, row, onRowMouseEnter, onRowMouseLeave}">
136
+ <tr
137
+ class="labels-row sub-row"
138
+ @mouseenter="onRowMouseEnter"
139
+ @mouseleave="onRowMouseLeave"
140
+ >
141
+ <template v-if="row.customLabels.length">
142
+ <td>&nbsp;</td>
143
+ <td>&nbsp;</td>
144
+ <td :colspan="fullColspan-2">
145
+ <span
146
+ v-if="row.customLabels.length"
147
+ class="mt-5"
148
+ > {{ t('fleet.cluster.labels') }}:
149
+ <span
150
+ v-for="(label, i) in row.customLabels"
151
+ :key="i"
152
+ class="mt-5 labels"
153
+ >
154
+ <Tag
155
+ v-if="i < 7"
156
+ class="mr-5 label"
157
+ >
158
+ {{ label }}
159
+ </Tag>
160
+ <Tag
161
+ v-else-if="i > 6 && row.displayCustomLabels"
162
+ class="mr-5 label"
163
+ >
164
+ {{ label }}
165
+ </Tag>
166
+ </span>
167
+ <a
168
+ v-if="row.customLabels.length > 7"
169
+ href="#"
170
+ @click.prevent="toggleCustomLabels(row)"
171
+ >
172
+ {{ t(`fleet.cluster.${row.displayCustomLabels? 'hideLabels' : 'showLabels'}`) }}
173
+ </a>
174
+ </span>
175
+ </td>
176
+ </template>
177
+ <td
178
+ v-else
179
+ :colspan="fullColspan"
180
+ >
181
+ &nbsp;
182
+ </td>
183
+ </tr>
184
+ </template>
126
185
  </ResourceTable>
127
186
  </template>
187
+
188
+ <style lang='scss' scoped>
189
+ .labels-row {
190
+ td {
191
+ padding-top:0;
192
+ .tag {
193
+ margin-right: 5px;
194
+ display: inline-block;
195
+ margin-top: 2px;
196
+ }
197
+ }
198
+ }
199
+ .labels {
200
+ display: inline;
201
+ flex-wrap: wrap;
202
+
203
+ .label {
204
+ display: inline-block;
205
+ margin-top: 2px;
206
+ }
207
+ }
208
+ </style>
@@ -6,17 +6,15 @@ import FleetIntro from '@shell/components/fleet/FleetIntro';
6
6
 
7
7
  import {
8
8
  AGE,
9
- STATE,
10
- NAME,
11
- FLEET_SUMMARY,
12
9
  FLEET_REPO,
13
- FLEET_REPO_TARGET,
14
- FLEET_REPO_CLUSTERS_READY,
15
10
  FLEET_REPO_CLUSTER_SUMMARY,
16
- FLEET_REPO_PER_CLUSTER_STATE
17
-
11
+ FLEET_REPO_CLUSTERS_READY,
12
+ FLEET_REPO_PER_CLUSTER_STATE,
13
+ FLEET_REPO_TARGET,
14
+ FLEET_SUMMARY,
15
+ NAME,
16
+ STATE,
18
17
  } from '@shell/config/table-headers';
19
- import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
20
18
 
21
19
  // i18n-ignore repoDisplay
22
20
  export default {
@@ -77,31 +75,18 @@ export default {
77
75
 
78
76
  headers() {
79
77
  // Cluster summary is only shown in the cluster view
80
- const fleetClusterSummary = {
78
+ const summary = this.isClusterView ? [{
81
79
  ...FLEET_REPO_CLUSTER_SUMMARY,
82
- formatterOpts: {
83
- // Fleet uses labels to identify clusters
84
- clusterLabel: this.clusterId
85
- },
86
- };
80
+ formatterOpts: { clusterId: this.clusterId },
81
+ }] : [FLEET_REPO_CLUSTERS_READY, FLEET_SUMMARY];
87
82
 
88
83
  // if hasPerClusterState then use the repo state
89
- const fleetPerClusterState = {
84
+ const state = this.isClusterView ? {
90
85
  ...FLEET_REPO_PER_CLUSTER_STATE,
91
- value: (row) => {
92
- const statePerCluster = row.clusterResourceStatus?.find((c) => {
93
- return c.clusterLabel === this.clusterId;
94
- });
95
-
96
- return statePerCluster ? statePerCluster?.status?.displayStatus : STATES_ENUM.ACTIVE;
97
- },
98
- };
86
+ value: (repo) => repo.clusterState(this.clusterId),
87
+ } : STATE;
99
88
 
100
- const summary = this.isClusterView ? [fleetClusterSummary] : [FLEET_REPO_CLUSTERS_READY, FLEET_SUMMARY];
101
-
102
- const state = this.isClusterView ? fleetPerClusterState : STATE;
103
-
104
- const out = [
89
+ return [
105
90
  state,
106
91
  NAME,
107
92
  FLEET_REPO,
@@ -109,8 +94,6 @@ export default {
109
94
  ...summary,
110
95
  AGE
111
96
  ];
112
-
113
- return out;
114
97
  },
115
98
  },
116
99
  methods: {
@@ -386,10 +386,10 @@ export default {
386
386
  <router-link
387
387
  :to="item.valueObj.detailLocation"
388
388
  >
389
- {{ item.valueObj.id }}
389
+ {{ item.valueObj.label }}
390
390
  </router-link>
391
391
  </span>
392
- <span v-else>{{ item.valueObj.id }}</span>
392
+ <span v-else>{{ item.valueObj.label }}</span>
393
393
  </td>
394
394
  <!-- state-badge template -->
395
395
  <td
@@ -407,6 +407,8 @@ export default {
407
407
  <Banner
408
408
  v-for="(err, i) in errorMessages"
409
409
  :key="i"
410
+ role="alert"
411
+ :aria-label="err"
410
412
  color="error"
411
413
  :label="err"
412
414
  class="mb-0"
@@ -73,7 +73,21 @@ export default {
73
73
  mounted() {
74
74
  // Ensures that if the default value is used, the model is updated with it
75
75
  this.$emit('update:value', this.inputValue);
76
- }
76
+ },
77
+
78
+ methods: {
79
+ handleKeyup(ev) {
80
+ if (this.isDisabled) {
81
+ return '';
82
+ }
83
+
84
+ return this.$refs.input.click(ev);
85
+ }
86
+ },
87
+
88
+ // according to https://www.w3.org/TR/html-aria/
89
+ // input type="color" has no applicable role
90
+ // and only aria-label and aria-disabled
77
91
  };
78
92
  </script>
79
93
 
@@ -82,6 +96,8 @@ export default {
82
96
  class="color-input"
83
97
  :class="{[mode]:mode, disabled: isDisabled}"
84
98
  :data-testid="componentTestid + '-color-input'"
99
+ :tabindex="isDisabled ? -1 : 0"
100
+ @keyup.enter.space.stop="handleKeyup($event)"
85
101
  >
86
102
  <label class="text-label"><t
87
103
  v-if="labelKey"
@@ -99,8 +115,11 @@ export default {
99
115
  >
100
116
  <input
101
117
  ref="input"
118
+ :aria-disabled="isDisabled ? 'true' : 'false'"
119
+ :aria-label="t('generic.colorPicker')"
102
120
  type="color"
103
121
  :disabled="isDisabled"
122
+ tabindex="-1"
104
123
  :value="inputValue"
105
124
  @input="$emit('update:value', $event.target.value)"
106
125
  >
@@ -116,6 +135,10 @@ export default {
116
135
  border-radius: var(--border-radius);
117
136
  padding: 10px;
118
137
 
138
+ &:focus-visible {
139
+ @include focus-outline;
140
+ }
141
+
119
142
  &.disabled, &.disabled .selected, &[disabled], &[disabled]:hover {
120
143
  color: var(--input-disabled-text);
121
144
  background-color: var(--input-disabled-bg);
@@ -148,7 +148,9 @@ export default {
148
148
  <button
149
149
  v-if="!isView"
150
150
  :disabled="disabled"
151
+ :aria-label="label"
151
152
  type="button"
153
+ role="button"
152
154
  class="file-selector btn"
153
155
  data-testid="file-selector__uploader-button"
154
156
  @click="selectFile"