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

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 (63) hide show
  1. package/assets/styles/base/_basic.scss +2 -1
  2. package/assets/styles/global/_form.scss +2 -1
  3. package/assets/styles/themes/_dark.scss +1 -1
  4. package/assets/translations/en-us.yaml +35 -4
  5. package/assets/translations/zh-hans.yaml +2 -3
  6. package/components/AppModal.vue +50 -0
  7. package/components/ButtonGroup.vue +8 -1
  8. package/components/ButtonMultiAction.vue +5 -1
  9. package/components/Carousel.vue +54 -47
  10. package/components/CopyToClipboardText.vue +6 -1
  11. package/components/Dialog.vue +20 -1
  12. package/components/ExplorerProjectsNamespaces.vue +7 -0
  13. package/components/PromptChangePassword.vue +3 -0
  14. package/components/ResourceDetail/Masthead.vue +1 -1
  15. package/components/ResourceTable.vue +1 -14
  16. package/components/SelectIconGrid.vue +2 -0
  17. package/components/SortableTable/index.vue +32 -3
  18. package/components/Tabbed/index.vue +4 -7
  19. package/components/__tests__/Carousel.test.ts +56 -27
  20. package/components/form/LabeledSelect.vue +2 -1
  21. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +192 -0
  22. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +104 -0
  23. package/components/form/SSHKnownHosts/index.vue +101 -0
  24. package/components/form/Select.vue +2 -1
  25. package/components/form/SelectOrCreateAuthSecret.vue +43 -11
  26. package/components/form/__tests__/SSHKnownHosts.test.ts +59 -0
  27. package/components/nav/WindowManager/ContainerLogs.vue +13 -3
  28. package/components/nav/WindowManager/index.vue +14 -3
  29. package/composables/focusTrap.ts +68 -0
  30. package/config/home-links.js +1 -1
  31. package/detail/secret.vue +25 -0
  32. package/edit/fleet.cattle.io.gitrepo.vue +27 -22
  33. package/edit/provisioning.cattle.io.cluster/index.vue +18 -9
  34. package/edit/secret/index.vue +1 -1
  35. package/edit/secret/ssh.vue +21 -3
  36. package/list/management.cattle.io.setting.vue +5 -2
  37. package/list/provisioning.cattle.io.cluster.vue +1 -0
  38. package/mixins/auth-config.js +1 -1
  39. package/models/fleet.cattle.io.gitrepo.js +2 -2
  40. package/models/provisioning.cattle.io.cluster.js +2 -12
  41. package/models/secret.js +5 -0
  42. package/package.json +1 -1
  43. package/pages/account/index.vue +4 -0
  44. package/pages/c/_cluster/apps/charts/chart.vue +1 -0
  45. package/pages/c/_cluster/explorer/ConfigBadge.vue +5 -4
  46. package/pages/c/_cluster/explorer/tools/index.vue +14 -1
  47. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +3 -1
  48. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +3 -0
  49. package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +7 -1
  50. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +3 -1
  51. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +10 -7
  52. package/pages/c/_cluster/uiplugins/InstallDialog.vue +7 -0
  53. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +181 -106
  54. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +2 -0
  55. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +9 -1
  56. package/pages/c/_cluster/uiplugins/index.vue +52 -12
  57. package/rancher-components/Card/Card.vue +7 -21
  58. package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -0
  59. package/rancher-components/RcDropdown/RcDropdown.vue +11 -0
  60. package/rancher-components/RcDropdown/RcDropdownItem.vue +0 -12
  61. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +2 -3
  62. package/rancher-components/RcDropdown/useDropdownCollection.ts +1 -0
  63. package/rancher-components/RcDropdown/useDropdownContext.ts +28 -1
@@ -61,13 +61,13 @@ BODY {
61
61
  INPUT,
62
62
  SELECT,
63
63
  TEXTAREA,
64
- .labeled-input,
65
64
  .checkbox-custom {
66
65
  &:focus, &.focused {
67
66
  @include form-focus;
68
67
  }
69
68
  }
70
69
 
70
+ .labeled-input,
71
71
  .radio-custom,
72
72
  .labeled-select,
73
73
  .unlabeled-select {
@@ -76,6 +76,7 @@ TEXTAREA,
76
76
  }
77
77
  }
78
78
 
79
+ .labeled-input,
79
80
  .labeled-select,
80
81
  .unlabeled-select {
81
82
  &.focused {
@@ -27,7 +27,8 @@ TEXTAREA,
27
27
 
28
28
  @include input-status-color;
29
29
 
30
- &:focus:not(.unlabeled-select):not(.labeled-select), &.focused:not(.unlabeled-select):not(.labeled-select) {
30
+ &:focus:not(.labeled-input):not(.unlabeled-select):not(.labeled-select),
31
+ &.focused:not(.labeled-input):not(.unlabeled-select):not(.labeled-select) {
31
32
  @include form-focus;
32
33
  }
33
34
 
@@ -72,7 +72,7 @@
72
72
  --banner-text-color : #{$lightest};
73
73
 
74
74
  --nav-bg : #{$darkest};
75
- --nav-active : var(--primary-active-bg);
75
+ --nav-active : #333;
76
76
  --nav-border : #{$medium};
77
77
  --nav-hover : var(--primary);
78
78
  --nav-expander-hover : var(--primary-banner-bg);
@@ -16,6 +16,8 @@ generic:
16
16
  comingSoon: Coming Soon
17
17
  comma: ', '
18
18
  copy: Copy
19
+ copyToClipboard: Copy text to Clipboard
20
+ copiedToClipboard: Text copied to Clipboard
19
21
  create: Create
20
22
  created: Created
21
23
  customize: Customize
@@ -949,6 +951,7 @@ catalog:
949
951
  experimentalWarning: '{chartName} has been marked as experimental. Use caution when installing this helm chart as it might not function as expected.'
950
952
  deprecatedAndExperimentalWarning: '{chartName} has been marked as deprecated and experimental. Use caution when installing this helm chart as it might be removed in the future and might not function as expected.'
951
953
  charts:
954
+ iconAlt: Icon for {app} card/grid item
952
955
  refresh: refresh charts
953
956
  all: All
954
957
  categories:
@@ -1132,6 +1135,7 @@ catalog:
1132
1135
  label: Refresh Interval
1133
1136
  placeholder: 'default: {hours}'
1134
1137
  tools:
1138
+ iconAlt: Icon for {app} Cluster Tool
1135
1139
  header: Cluster Tools
1136
1140
  noTools: "No Cluster Tools found"
1137
1141
  action:
@@ -2440,6 +2444,7 @@ fleet:
2440
2444
  resources: Resources
2441
2445
  unready: Non-Ready
2442
2446
  auth:
2447
+ title: Authentication
2443
2448
  label: Authentication
2444
2449
  git: Git Authentication
2445
2450
  helm: Helm Authentication
@@ -2450,20 +2455,20 @@ fleet:
2450
2455
  label: Paths
2451
2456
  placeholder: e.g. /directory/in/your/repo
2452
2457
  addLabel: Add Path
2453
- empty: The root of the repo is used by default. To use one or more different directories, add them here.
2458
+ empty: The root of the repo is used by default. Multiple different directories can be provided.
2454
2459
  repo:
2460
+ title: Source
2455
2461
  label: Repository URL
2456
2462
  placeholder: e.g. https://github.com/rancher/fleet-examples.git or git@github.com:rancher/fleet-examples.git
2457
2463
  addRepo: Add Repository
2458
2464
  noRepos: No repositories have been added
2459
2465
  noWorkspaces: There are no workspaces. <br/> Please create a workspace before adding repositories
2460
- protocolBanner: Enter a valid HTTPS or SSH URL to a git repository.
2461
2466
  resources:
2462
2467
  label: 'Resource Handling'
2463
2468
  keepResources: Always Keep Resources
2464
- keepResourcesBanner: When enabled above, resources will be kept when deleting a GitRepo or Bundle - only Helm release secrets will be deleted.
2469
+ keepResourcesTooltip: When enabled, resources will be kept when deleting a GitRepo or Bundle - only Helm release secrets will be deleted.
2465
2470
  correctDrift: Enable Self-Healing
2466
- correctDriftBanner: When enabled, Fleet will ensure that the cluster resources are kept in sync with the git repository. All resource changes made on the cluster will be lost.
2471
+ correctDriftTooltip: When enabled, Fleet will ensure that the cluster resources are kept in sync with the git repository. All resource changes made on the cluster will be lost.
2467
2472
  add:
2468
2473
  steps:
2469
2474
  repoInfo:
@@ -4374,12 +4379,15 @@ inactivity:
4374
4379
 
4375
4380
  # Rancher Extensions
4376
4381
  plugins:
4382
+ altIcon: Icon for {extension} extension card
4377
4383
  incompatibleRancherVersion: "The latest version of this extension ({ version }) is not compatible with the current Rancher version ({ required })."
4378
4384
  incompatibleKubeVersion: "The latest version of this extension ({ version }) is not compatible with the current Kube version ({ required })."
4379
4385
  incompatibleUiExtensionsApiVersionMissing: 'The latest version of this extension ({ version }) is missing the mandatory annotation catalog.cattle.io/ui-extensions-version from Rancher 2.10 and onwards.'
4380
4386
  incompatibleUiExtensionsApiVersion: "The latest version of this extension ({ version }) is not compatible with the current Extensions API version ({ required })."
4381
4387
  incompatibleHost: 'The latest version of this extension ({ version }) has a host of "{ required }" which is not compatible with this application "{ mainHost }".'
4382
4388
  currentInstalledVersionBlockedByKubeVersion: "This version is not compatible with the current Kubernetes version ({ kubeVersion } Vs { kubeVersionToCheck })."
4389
+ closePluginPanel: Close plugin description panel
4390
+ viewVersionDetails: View extension {name} version {version} details/Readme
4383
4391
  labels:
4384
4392
  builtin: Built-in
4385
4393
  experimental: Experimental
@@ -4387,6 +4395,8 @@ plugins:
4387
4395
  image: Image
4388
4396
  installing: Installing ...
4389
4397
  uninstalling: Uninstalling ...
4398
+ menu: Extensions menu
4399
+ reloadRancher: Reload Rancher
4390
4400
  descriptions:
4391
4401
  experimental: This Extension is marked as experimental
4392
4402
  third-party: This Extension is provided by a Third-Party
@@ -4705,6 +4715,7 @@ projectMembers:
4705
4715
  workloadsView: View Workloads
4706
4716
 
4707
4717
  projectNamespaces:
4718
+ tableActionsLabel: More actions for project { resource }
4708
4719
  createNamespace: Create Namespace
4709
4720
  createProject: Create Project
4710
4721
  label: Projects/Namespaces
@@ -5139,10 +5150,22 @@ secret:
5139
5150
  username: Username
5140
5151
  ssh:
5141
5152
  keys: Keys
5153
+ keysAndHosts: Keys and Known Hosts
5142
5154
  public: Public Key
5143
5155
  publicPlaceholder: "Paste in your public key"
5144
5156
  private: Private Key
5145
5157
  privatePlaceholder: "Paste in your private key"
5158
+ knownHosts: Known Hosts
5159
+ knownHostsPlaceholder: "Known hosts metadata, one per line"
5160
+ editKnownHosts:
5161
+ title: SSH Known Hosts Configuration
5162
+ entries: |-
5163
+ {entries, plural,
5164
+ =0 {Empty}
5165
+ =1 {1 Entry}
5166
+ other {{entries} Entries}
5167
+ }
5168
+
5146
5169
  serviceAcct:
5147
5170
  ca: CA Certificate
5148
5171
  token: Token
@@ -5368,6 +5391,8 @@ setup:
5368
5391
  welcome: Welcome to {vendor}!
5369
5392
 
5370
5393
  sortableTable:
5394
+ tableActionsLabel: More actions - { resource }
5395
+ tableActionsImgAlt: More actions icon
5371
5396
  bulkActions:
5372
5397
  collapsed:
5373
5398
  label: Actions
@@ -6182,6 +6207,8 @@ wm:
6182
6207
  disconnected: Disconnected
6183
6208
  error: Error
6184
6209
  containerLogs:
6210
+ options: Additional container logs options
6211
+ expand: Expand additional container logs options
6185
6212
  clear: Clear
6186
6213
  containerName: "Container: {label}"
6187
6214
  download: Download
@@ -6208,6 +6235,9 @@ wm:
6208
6235
  timestamps: Show Timestamps
6209
6236
  wrap: Wrap Lines
6210
6237
  containerShell:
6238
+ resizeShellWindow: Resize Shell window
6239
+ tabIcon: Shell tab icon
6240
+ closeShellTab: Close Shell tab - {tab}
6211
6241
  clear: Clear
6212
6242
  containerName: "Container: {label}"
6213
6243
  failed: "Unable to open a shell to the container (none of the shell commands succeeded)\n\r"
@@ -7480,6 +7510,7 @@ advancedSettings:
7480
7510
  none: None
7481
7511
  modified: Modified
7482
7512
  edit:
7513
+ moreActions: More actions for setting - { setting }
7483
7514
  label: Edit Setting
7484
7515
  changeSetting: "Change Setting:"
7485
7516
  trueOption: "True"
@@ -2153,13 +2153,12 @@ fleet:
2153
2153
  addRepo: 添加仓库
2154
2154
  noRepos: 未添加任何仓库
2155
2155
  noWorkspaces: 没有工作空间。<br/>请在添加仓库之前创建一个工作空间
2156
- protocolBanner: 输入指向 git 仓库的有效 HTTPS 或 SSH URL。
2157
2156
  resources:
2158
2157
  label: '资源处理'
2159
2158
  keepResources: 永远保留资源
2160
- keepResourcesBanner: 启用时,删除 GitRepo 或 Bundle 后将保留资源,只会删除 Helm Release Secret。
2159
+ keepResourcesTooltip: 启用时,删除 GitRepo 或 Bundle 后将保留资源,只会删除 Helm Release Secret。
2161
2160
  correctDrift: 启用自我修复
2162
- correctDriftBanner: 启用后,Fleet 将确保集群资源与 Git 仓库保持同步。在 ecluster 上进行的所有资源更改都将丢失。
2161
+ correctDriftTooltip: 启用后,Fleet 将确保集群资源与 Git 仓库保持同步。在 ecluster 上进行的所有资源更改都将丢失。
2163
2162
  add:
2164
2163
  steps:
2165
2164
  repoInfo:
@@ -1,5 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { defineComponent } from 'vue';
3
+ import { DEFAULT_FOCUS_TRAP_OPTS, useBasicSetupFocusTrap, getFirstFocusableElement } from '@shell/composables/focusTrap';
4
+
5
+ export const DEFAULT_ITERABLE_NODE_SELECTOR = 'body;';
3
6
 
4
7
  export default defineComponent({
5
8
  name: 'AppModal',
@@ -56,6 +59,27 @@ export default defineComponent({
56
59
  name: {
57
60
  type: String,
58
61
  default: '',
62
+ },
63
+ /**
64
+ * trigger focus trap
65
+ */
66
+ triggerFocusTrap: {
67
+ type: Boolean,
68
+ default: false,
69
+ },
70
+ /**
71
+ * forcefully set return focus element based on this selector
72
+ */
73
+ returnFocusSelector: {
74
+ type: String,
75
+ default: '',
76
+ },
77
+ /**
78
+ * will return focus to the first iterable node of this container select
79
+ */
80
+ returnFocusFirstIterableNodeSelector: {
81
+ type: String,
82
+ default: DEFAULT_ITERABLE_NODE_SELECTOR,
59
83
  }
60
84
  },
61
85
  computed: {
@@ -85,6 +109,31 @@ export default defineComponent({
85
109
  };
86
110
  }
87
111
  },
112
+ setup(props) {
113
+ if (props.triggerFocusTrap) {
114
+ let opts:any = DEFAULT_FOCUS_TRAP_OPTS;
115
+
116
+ // if we have a "returnFocusFirstIterableNodeSelector" on top of "returnFocusSelector"
117
+ // then we will use "returnFocusFirstIterableNodeSelector" as a fallback of "returnFocusSelector"
118
+ if (props.returnFocusFirstIterableNodeSelector && props.returnFocusFirstIterableNodeSelector !== DEFAULT_ITERABLE_NODE_SELECTOR && props.returnFocusSelector) {
119
+ opts = {
120
+ ...DEFAULT_FOCUS_TRAP_OPTS,
121
+ setReturnFocus: () => {
122
+ return document.querySelector(props.returnFocusSelector) ? props.returnFocusSelector : getFirstFocusableElement(document.querySelector(props.returnFocusFirstIterableNodeSelector));
123
+ }
124
+ };
125
+ // otherwise, if we are sure of permanent existance of "returnFocusSelector"
126
+ // we just return to that element
127
+ } else if (props.returnFocusSelector) {
128
+ opts = {
129
+ ...DEFAULT_FOCUS_TRAP_OPTS,
130
+ setReturnFocus: props.returnFocusSelector
131
+ };
132
+ }
133
+
134
+ useBasicSetupFocusTrap('#modal-container-element', opts);
135
+ }
136
+ },
88
137
  mounted() {
89
138
  document.addEventListener('keydown', this.handleEscapeKey);
90
139
  },
@@ -134,6 +183,7 @@ export default defineComponent({
134
183
  >
135
184
  <div
136
185
  v-bind="$attrs"
186
+ id="modal-container-element"
137
187
  ref="modalRef"
138
188
  :class="customClass"
139
189
  class="modal-container"
@@ -64,6 +64,12 @@ export default {
64
64
  methods: {
65
65
  change(value) {
66
66
  this.$emit('update:value', value);
67
+ },
68
+ actionDescription(opt) {
69
+ const tooltip = opt.tooltipKey ? this.t(opt.tooltipKey) : opt.tooltip;
70
+ const label = opt.labelKey ? this.t(opt.labelKey) : opt.label;
71
+
72
+ return tooltip || label || '';
67
73
  }
68
74
  }
69
75
  };
@@ -83,7 +89,7 @@ export default {
83
89
  :class="opt.class"
84
90
  :disabled="disabled || opt.disabled"
85
91
  role="button"
86
- :aria-label="opt.labelKey ? t(opt.labelKey) : opt.label"
92
+ :aria-label="actionDescription(opt)"
87
93
  @click="change(opt.value)"
88
94
  >
89
95
  <slot
@@ -94,6 +100,7 @@ export default {
94
100
  <i
95
101
  v-if="opt.icon"
96
102
  :class="{icon: true, [opt.icon]: true, [`icon-${iconSize}`]: !!iconSize }"
103
+ :alt="actionDescription(opt)"
97
104
  />
98
105
  <t
99
106
  v-if="opt.labelKey"
@@ -22,10 +22,14 @@ const buttonClass = computed(() => {
22
22
  <button
23
23
  type="button"
24
24
  class="btn btn-sm role-multi-action actions"
25
+ role="button"
25
26
  :class="buttonClass"
26
27
  @click="(e: Event) => $emit('click', e)"
27
28
  >
28
- <i class="icon icon-actions" />
29
+ <i
30
+ class="icon icon-actions"
31
+ :alt="t('sortableTable.tableActionsImgAlt')"
32
+ />
29
33
  </button>
30
34
  </template>
31
35
 
@@ -43,24 +43,29 @@ export default {
43
43
  activeItemId: 0,
44
44
  autoScroll: true,
45
45
  autoScrollSlideInterval: null,
46
+ isTransitionning: false, // prevents showing empty spaces caused by aggressive clicking
47
+ shouldDisableTransition: false // smoothes the move from the first/last slides to the previous/next slide
46
48
  };
47
49
  },
48
50
 
49
51
  computed: {
50
52
  ...mapGetters(['clusterId']),
51
53
  trackStyle() {
52
- let sliderItem = ( this.activeItemId + 1) * 100 / (this.slider.length + 2);
53
- const width = 60 * (this.slider.length + 2);
54
-
55
54
  if (this.slider.length === 1) {
56
- sliderItem = 0;
55
+ return `
56
+ width: 100%;
57
+ left: 0%;
58
+ `;
57
59
  }
58
60
 
59
- return `transform: translateX(-${ sliderItem }%); width: ${ width }%`;
60
- },
61
+ const width = 60 * (this.slider.length + 2);
62
+ const left = -(40 + this.activeItemId * 60);
61
63
 
62
- test() {
63
- return 'test';
64
+ return `
65
+ width: ${ width }%;
66
+ left: ${ left }%;
67
+ transition: ${ this.shouldDisableTransition ? 'none' : '700ms ease-in-out' };
68
+ `;
64
69
  }
65
70
  },
66
71
 
@@ -77,10 +82,14 @@ export default {
77
82
  },
78
83
 
79
84
  nextPrev(direction) {
80
- this.autoScroll = false;
81
- const slideTrack = document.getElementById('slide-track');
85
+ if (this.isTransitionning) {
86
+ return;
87
+ }
82
88
 
83
- slideTrack.style.transition = `transform 450ms ease-in-out`;
89
+ this.isTransitionning = true;
90
+ this.autoScroll = false;
91
+ this.shouldDisableTransition = false;
92
+ const slideTrack = this.$refs.slider;
84
93
 
85
94
  direction !== 'prev' ? (this.activeItemId++) : (this.activeItemId--);
86
95
 
@@ -88,21 +97,27 @@ export default {
88
97
  },
89
98
 
90
99
  slideTransition() {
91
- const slideTrack = document.getElementById('slide-track');
92
100
  const slidesArray = this.slider.length + 2;
93
101
 
94
102
  if (this.activeItemId === -1) {
95
- slideTrack.style.transition = 'none';
103
+ this.shouldDisableTransition = true;
96
104
  this.activeItemId = this.slider.length - 1;
97
105
  }
106
+
98
107
  if (this.activeItemId === slidesArray - 2) {
99
- slideTrack.style.transition = 'none';
108
+ this.shouldDisableTransition = true;
100
109
  this.activeItemId = 0;
101
110
  }
111
+
112
+ this.isTransitionning = false;
102
113
  },
103
114
 
104
115
  autoScrollSlide() {
105
- if (this.activeItemId < (this.slider.length + 1) && this.autoScroll ) {
116
+ if (!this.autoScroll) {
117
+ return;
118
+ }
119
+
120
+ if (this.activeItemId < (this.slider.length + 1)) {
106
121
  this.activeItemId++;
107
122
  }
108
123
 
@@ -120,23 +135,21 @@ export default {
120
135
  },
121
136
 
122
137
  mounted() {
123
- const slideTrack = document.getElementById('slide-track');
138
+ const slideTrack = this.$refs.slider;
124
139
 
125
- if (this.slider.length === 1) {
126
- slideTrack.style = 'transform:translateX(0%); width:100%; left:0';
127
- } else {
128
- const node = document.getElementById('slide0');
140
+ if (this.slider.length > 1) {
141
+ const firstSlide = this.$refs['slide0']?.[0];
129
142
 
130
- if (node) {
131
- const clone = node.cloneNode(true);
143
+ if (firstSlide) {
144
+ const clone = firstSlide.cloneNode(true);
132
145
 
133
146
  slideTrack.appendChild(clone);
134
147
  }
135
148
 
136
- const nodeLast = document.getElementById(`slide${ this.slider.length - 1 }`);
149
+ const lastSlide = this.$refs[`slide${ this.slider.length - 1 }`]?.[0];
137
150
 
138
- if (nodeLast) {
139
- const cloneLast = nodeLast.cloneNode(true);
151
+ if (lastSlide) {
152
+ const cloneLast = lastSlide.cloneNode(true);
140
153
 
141
154
  slideTrack.insertBefore(cloneLast, slideTrack.children[0]);
142
155
  }
@@ -159,7 +172,7 @@ export default {
159
172
  <template>
160
173
  <div
161
174
  class="slider"
162
- :class="{'disable': sliders.length === 1}"
175
+ :class="{'disabled': sliders.length === 1}"
163
176
  >
164
177
  <div
165
178
  id="slide-track"
@@ -170,8 +183,8 @@ export default {
170
183
  <component
171
184
  :is="asLink ? 'a' : 'div'"
172
185
  v-for="(slide, i) in sliders"
173
- :id="`slide` + i"
174
- ref="slide"
186
+ :id="`slide${i}`"
187
+ :ref="`slide${i}`"
175
188
  :key="get(slide, keyField)"
176
189
  class="slide"
177
190
  :class="{'singleSlide': sliders.length === 1}"
@@ -187,7 +200,7 @@ export default {
187
200
  <div class="slide-content-right">
188
201
  <BadgeState
189
202
  :label="slide.repoName"
190
- color="slider-badge mb-20"
203
+ color="slide-badge mb-20"
191
204
  />
192
205
  <h1>{{ slide.chartNameDisplay }}</h1>
193
206
  <p>{{ slide.chartDescription }}</p>
@@ -196,12 +209,11 @@ export default {
196
209
  </component>
197
210
  </div>
198
211
  <div
199
- ref="prev"
200
212
  role="button"
201
213
  class="prev"
202
214
  :aria-label="t('carousel.previous')"
203
215
  :aria-disabled="sliders.length === 1"
204
- :class="{'disable': sliders.length === 1}"
216
+ :class="{'disabled': sliders.length === 1}"
205
217
  tabindex="0"
206
218
  @click="nextPrev('prev')"
207
219
  @keyup.enter.space="nextPrev('prev')"
@@ -211,12 +223,11 @@ export default {
211
223
  />
212
224
  </div>
213
225
  <div
214
- ref="next"
215
226
  role="button"
216
227
  class="next"
217
228
  :aria-label="t('carousel.next')"
218
229
  :aria-disabled="sliders.length === 1"
219
- :class="{'disable': sliders.length === 1}"
230
+ :class="{'disabled': sliders.length === 1}"
220
231
  tabindex="0"
221
232
  @click="nextPrev('next')"
222
233
  @keyup.enter.space="nextPrev('next')"
@@ -226,8 +237,8 @@ export default {
226
237
  />
227
238
  </div>
228
239
  <div
240
+ v-if="sliders.length > 1"
229
241
  class="controls"
230
- :class="{'disable': sliders.length === 1}"
231
242
  >
232
243
  <div
233
244
  v-for="(slide, i) in slider"
@@ -252,23 +263,21 @@ export default {
252
263
  place-items: center;
253
264
  overflow: hidden;
254
265
  margin-bottom: 30px;
255
- // min-width: 700px;
266
+ height: 245px;
256
267
 
257
- &.disable::before,
258
- &.disable::after {
268
+ &.disabled::before,
269
+ &.disabled::after {
259
270
  display: none;
260
271
  }
261
272
  }
262
273
 
263
274
  .slide-track {
264
275
  display: flex;
265
- animation: scrolls 10s ;
266
- position: relative;
267
- transition: 1s ease-in-out;
268
- left: 21%;
276
+ position: absolute;
277
+ top: 0;
269
278
  }
270
279
 
271
- .slider-badge {
280
+ .slide-badge {
272
281
  background: var(--app-partner-accent);
273
282
  color: var(--body-bg);
274
283
  }
@@ -333,7 +342,7 @@ export default {
333
342
  .slider::before {
334
343
  left: 0;
335
344
  top: 0;
336
- &.disable {
345
+ &.disabled {
337
346
  display: none;
338
347
  }
339
348
  }
@@ -344,15 +353,13 @@ export default {
344
353
  }
345
354
 
346
355
  .controls {
356
+ position: absolute;
357
+ bottom: 0;
347
358
  width: 100%;
348
359
  display: flex;
349
360
  justify-content: center;
350
361
  margin-top: 10px;
351
362
 
352
- &.disable {
353
- display: none;
354
- }
355
-
356
363
  .control-item {
357
364
  width: 10px;
358
365
  height: 10px;
@@ -48,13 +48,18 @@ export default {
48
48
  <a
49
49
  v-if="text"
50
50
  class="copy-to-clipboard-text"
51
+ role="button"
52
+ :aria-label="t('generic.copyToClipboard')"
51
53
  :class="{ 'copied': copied, 'plain': plain}"
52
54
  href="#"
53
55
  @click="clicked"
56
+ @keyup.space="clicked"
54
57
  >
55
- {{ text }} <i
58
+ {{ text }}
59
+ <i
56
60
  class="icon"
57
61
  :class="{ 'icon-copy': !copied, 'icon-checkmark': copied}"
62
+ :alt="!copied ? t('generic.copyToClipboard') : t('generic.copiedToClipboard')"
58
63
  />
59
64
  </a>
60
65
  </template>
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import AsyncButton from '@shell/components/AsyncButton';
3
- import AppModal from '@shell/components/AppModal.vue';
3
+ import AppModal, { DEFAULT_ITERABLE_NODE_SELECTOR } from '@shell/components/AppModal.vue';
4
4
 
5
5
  export default {
6
6
  emits: ['okay', 'closed'],
@@ -21,6 +21,22 @@ export default {
21
21
  mode: {
22
22
  type: String,
23
23
  default: '',
24
+ },
25
+
26
+ /**
27
+ * forcefully set return focus element based on this selector
28
+ */
29
+ returnFocusSelector: {
30
+ type: String,
31
+ default: '',
32
+ },
33
+
34
+ /**
35
+ * will return focus to the first iterable node of this container select
36
+ */
37
+ returnFocusFirstIterableNodeSelector: {
38
+ type: String,
39
+ default: DEFAULT_ITERABLE_NODE_SELECTOR,
24
40
  }
25
41
  },
26
42
 
@@ -60,6 +76,9 @@ export default {
60
76
  :name="name"
61
77
  height="auto"
62
78
  :scrollable="true"
79
+ :trigger-focus-trap="true"
80
+ :return-focus-selector="returnFocusSelector"
81
+ :return-focus-first-iterable-node-selector="returnFocusFirstIterableNodeSelector"
63
82
  @close="closeDialog(false)"
64
83
  @before-open="beforeOpen"
65
84
  >
@@ -368,6 +368,12 @@ export default {
368
368
  return project?.description;
369
369
  },
370
370
 
371
+ projectResource(group) {
372
+ const row = group.rows[0];
373
+
374
+ return row.nameDisplay || row.id || '';
375
+ },
376
+
371
377
  clearSelection() {
372
378
  this.$refs.table.clearSelection();
373
379
  },
@@ -462,6 +468,7 @@ export default {
462
468
  <ButtonMultiAction
463
469
  class="project-action mr-10"
464
470
  :borderless="true"
471
+ :aria-label="t('projectNamespaces.tableActionsLabel', { resource: projectResource(group.group) })"
465
472
  :invisible="!showProjectActionButton(group.group)"
466
473
  @click="showProjectAction($event, group.group)"
467
474
  />
@@ -43,6 +43,7 @@ export default {
43
43
  name="password-modal"
44
44
  :width="500"
45
45
  :height="465"
46
+ :trigger-focus-trap="true"
46
47
  @close="show(false)"
47
48
  >
48
49
  <Card
@@ -68,6 +69,8 @@ export default {
68
69
  <!-- type reset is required by lastpass -->
69
70
  <button
70
71
  class="btn role-secondary"
72
+ role="button"
73
+ :aria-label="t('changePassword.cancel')"
71
74
  type="reset"
72
75
  @click="show(false)"
73
76
  >
@@ -609,7 +609,7 @@ export default {
609
609
  .masthead-resource-title {
610
610
  padding: 0 8px;
611
611
  text-overflow: ellipsis;
612
- overflow-x: hidden;
612
+ overflow: hidden;
613
613
  white-space: nowrap;
614
614
  }
615
615