@rancher/shell 3.0.2-rc.5 → 3.0.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.
- package/assets/images/providers/nutanix.svg +12 -1
- package/assets/styles/base/_basic.scss +2 -1
- package/assets/styles/base/_helpers.scss +4 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/global/_labeled-input.scss +5 -13
- package/assets/styles/global/_layout.scss +4 -1
- package/assets/styles/global/_select.scss +5 -0
- package/assets/styles/themes/_dark.scss +1 -3
- package/assets/styles/themes/_light.scss +5 -1
- package/assets/translations/en-us.yaml +130 -23
- package/assets/translations/zh-hans.yaml +0 -3
- package/cloud-credential/azure.vue +1 -1
- package/components/ActionMenuShell.vue +105 -0
- package/components/AppModal.vue +2 -2
- package/components/AsyncButton.vue +2 -0
- package/components/ButtonGroup.vue +9 -2
- package/components/ClusterBadge.vue +1 -0
- package/components/ClusterIconMenu.vue +3 -0
- package/components/ClusterProviderIcon.vue +14 -1
- package/components/CodeMirror.vue +96 -5
- package/components/Collapse.vue +16 -3
- package/components/CruResource.vue +9 -0
- package/components/CruResourceFooter.vue +1 -1
- package/components/ExplorerMembers.vue +2 -1
- package/components/FixedBanner.vue +19 -12
- package/components/Import.vue +14 -1
- package/components/LandingPagePreference.vue +4 -2
- package/components/PodSecurityAdmission.vue +8 -6
- package/components/PromptChangePassword.vue +1 -0
- package/components/PromptRemove.vue +23 -21
- package/components/ResourceDetail/Masthead.vue +30 -11
- package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
- package/components/ResourceDetail/index.vue +6 -0
- package/components/ResourceTable.vue +6 -1
- package/components/ResourceYaml.vue +1 -0
- package/components/Setting.vue +115 -0
- package/components/SortableTable/THead.vue +2 -0
- package/components/SortableTable/index.vue +7 -12
- package/components/StatusBadge.vue +71 -0
- package/components/Tabbed/index.vue +16 -15
- package/components/Wizard.vue +108 -104
- package/components/YamlEditor.vue +12 -2
- package/components/__tests__/Collapse.test.ts +2 -2
- package/components/__tests__/FixedBanner.test.ts +3 -3
- package/components/auth/Principal.vue +29 -17
- package/components/auth/__tests__/Principal.test.ts +40 -0
- package/components/auth/login/ldap.vue +7 -0
- package/components/fleet/FleetBundles.vue +1 -1
- package/components/fleet/FleetRepos.vue +1 -1
- package/components/fleet/FleetResources.vue +0 -2
- package/components/fleet/FleetSummary.vue +60 -65
- package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
- package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
- package/components/form/ArrayList.vue +6 -2
- package/components/form/ColorInput.vue +1 -0
- package/components/form/KeyValue.vue +11 -12
- package/components/form/LabeledSelect.vue +15 -3
- package/components/form/Labels.vue +8 -1
- package/components/form/Members/MembershipEditor.vue +230 -222
- package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
- package/components/form/Password.vue +3 -0
- package/components/form/ProjectMemberEditor.vue +6 -3
- package/components/form/ResourceTabs/index.vue +15 -13
- package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
- package/components/form/SchedulingCustomization.vue +85 -0
- package/components/form/Select.vue +3 -2
- package/components/form/SelectOrCreateAuthSecret.vue +2 -1
- package/components/form/UnitInput.vue +3 -4
- package/components/form/__tests__/ArrayList.test.ts +9 -6
- package/components/form/__tests__/LabeledSelect.test.ts +37 -0
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
- package/components/form/__tests__/UnitInput.test.ts +4 -5
- package/components/formatter/LiveDate.vue +3 -1
- package/components/formatter/ServiceType.vue +12 -4
- package/components/formatter/WorkloadHealthScale.vue +2 -1
- package/components/nav/Header.vue +35 -2
- package/components/nav/HeaderPageActionMenu.vue +11 -40
- package/components/nav/Jump.vue +8 -2
- package/components/nav/NamespaceFilter.vue +5 -4
- package/components/nav/Pinned.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +5 -5
- package/components/nav/TopLevelMenu.vue +1 -12
- package/components/nav/WindowManager/ContainerLogs.vue +96 -58
- package/components/nav/WindowManager/ContainerShell.vue +99 -18
- package/components/nav/WindowManager/index.vue +74 -6
- package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
- package/components/templates/default.vue +2 -47
- package/config/features.js +1 -0
- package/config/labels-annotations.js +11 -1
- package/config/router/navigation-guards/index.js +2 -1
- package/config/router/navigation-guards/record-last-route.js +24 -0
- package/config/settings.ts +66 -98
- package/config/version.js +1 -1
- package/core/types-provisioning.ts +7 -0
- package/detail/fleet.cattle.io.bundle.vue +7 -0
- package/detail/fleet.cattle.io.cluster.vue +0 -3
- package/detail/fleet.cattle.io.gitrepo.vue +8 -15
- package/detail/provisioning.cattle.io.cluster.vue +8 -2
- package/dialog/DeactivateDriverDialog.vue +5 -5
- package/dialog/GitRepoForceUpdateDialog.vue +132 -0
- package/directives/strip-html-aria-label.js +19 -0
- package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
- package/edit/auth/__tests__/oidc.test.ts +60 -12
- package/edit/auth/ldap/__tests__/config.test.ts +40 -0
- package/edit/auth/ldap/config.vue +67 -89
- package/edit/auth/oidc.vue +16 -2
- package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
- package/edit/cis.cattle.io.clusterscan.vue +13 -1
- package/edit/fleet.cattle.io.gitrepo.vue +198 -72
- package/edit/logging-flow/Match.vue +0 -21
- package/edit/management.cattle.io.project.vue +1 -1
- package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
- package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +0 -2
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
- package/edit/provisioning.cattle.io.cluster/index.vue +28 -30
- package/edit/provisioning.cattle.io.cluster/rke2.vue +64 -13
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
- package/edit/resources.cattle.io.backup.vue +150 -15
- package/edit/secret/__tests__/ssh.test.ts +79 -0
- package/edit/secret/ssh.vue +7 -1
- package/edit/service.vue +0 -3
- package/edit/workload/Job.vue +8 -8
- package/edit/workload/__tests__/Job.test.ts +0 -1
- package/edit/workload/index.vue +3 -1
- package/initialize/install-directives.js +2 -0
- package/initialize/install-plugins.js +6 -1
- package/list/catalog.cattle.io.app.vue +21 -4
- package/list/fleet.cattle.io.bundle.vue +1 -1
- package/list/management.cattle.io.setting.vue +34 -132
- package/list/provisioning.cattle.io.cluster.vue +11 -3
- package/machine-config/vmwarevsphere.vue +15 -8
- package/mixins/__tests__/auth-config.test.ts +74 -0
- package/mixins/__tests__/chart.test.ts +5 -4
- package/mixins/__tests__/create-edit-view.test.ts +38 -0
- package/mixins/auth-config.js +8 -0
- package/mixins/chart.js +2 -2
- package/mixins/create-edit-view/impl.js +4 -1
- package/mixins/vue-select-overrides.js +10 -0
- package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
- package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
- package/models/__tests__/secret.test.ts +56 -13
- package/models/catalog.cattle.io.app.js +112 -37
- package/models/cluster.js +11 -0
- package/models/fleet.cattle.io.bundle.js +40 -2
- package/models/fleet.cattle.io.gitrepo.js +169 -109
- package/models/management.cattle.io.fleetworkspace.js +4 -0
- package/models/management.cattle.io.kontainerdriver.js +7 -0
- package/models/nodedriver.js +4 -1
- package/models/provisioning.cattle.io.cluster.js +24 -0
- package/models/secret.js +1 -1
- package/package.json +5 -5
- package/pages/auth/login.vue +5 -11
- package/pages/auth/verify.vue +11 -1
- package/pages/c/_cluster/apps/charts/index.vue +6 -4
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
- package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
- package/pages/c/_cluster/explorer/index.vue +33 -35
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/fleet/index.vue +0 -5
- package/pages/c/_cluster/legacy/project/index.vue +1 -1
- package/pages/c/_cluster/settings/performance.vue +52 -53
- package/pages/c/_cluster/uiplugins/index.vue +19 -22
- package/pages/home.vue +17 -12
- package/pages/prefs.vue +5 -1
- package/plugins/shortkey.js +10 -1
- package/plugins/steve/steve-pagination-utils.ts +58 -8
- package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
- package/promptRemove/management.cattle.io.globalrole.vue +1 -1
- package/promptRemove/management.cattle.io.project.vue +2 -8
- package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
- package/promptRemove/mixin/roleDeletionCheck.js +1 -7
- package/promptRemove/pod.vue +7 -28
- package/rancher-components/Card/Card.vue +9 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
- package/rancher-components/Form/Radio/RadioButton.vue +18 -3
- package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
- package/rancher-components/RcButton/RcButton.test.ts +97 -0
- package/rancher-components/RcButton/RcButton.vue +14 -9
- package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
- package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -2
- package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
- package/rancher-components/RcDropdown/index.ts +1 -0
- package/rancher-components/RcDropdown/types.ts +27 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
- package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
- package/scripts/typegen.sh +1 -0
- package/store/__tests__/auth.test.ts +120 -0
- package/store/action-menu.js +13 -3
- package/store/auth.js +14 -9
- package/store/aws.js +9 -2
- package/store/catalog.js +14 -7
- package/store/features.js +1 -0
- package/store/prefs.js +9 -28
- package/store/type-map.utils.ts +4 -0
- package/types/resources/settings.d.ts +27 -20
- package/types/shell/index.d.ts +18 -12
- package/utils/__tests__/array.test.ts +13 -1
- package/utils/__tests__/string.test.ts +80 -1
- package/utils/array.ts +13 -0
- package/utils/auth.js +4 -0
- package/utils/banners.js +0 -45
- package/utils/cluster.js +1 -1
- package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
- package/utils/object.js +0 -3
- package/utils/pagination-utils.ts +15 -2
- package/utils/string.js +31 -7
- package/utils/validators/formRules/__tests__/index.test.ts +27 -0
- package/utils/validators/formRules/index.ts +16 -0
- package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import { useStore } from 'vuex';
|
|
4
|
+
import { useRoute } from 'vue-router';
|
|
5
|
+
|
|
6
|
+
import { isAlternate } from '@shell/utils/platform';
|
|
7
|
+
import { RcDropdownMenu } from '@components/RcDropdown';
|
|
8
|
+
import { ButtonRoleProps, ButtonSizeProps } from '@components/RcButton/types';
|
|
9
|
+
import { DropdownOption } from '@components/RcDropdown/types';
|
|
10
|
+
|
|
11
|
+
const store = useStore();
|
|
12
|
+
|
|
13
|
+
type RcDropdownMenuComponentProps = {
|
|
14
|
+
buttonRole?: keyof ButtonRoleProps;
|
|
15
|
+
buttonSize?: keyof ButtonSizeProps;
|
|
16
|
+
buttonAriaLabel?: string;
|
|
17
|
+
dropdownAriaLabel?: string;
|
|
18
|
+
dataTestid?: string;
|
|
19
|
+
resource: Object;
|
|
20
|
+
customActions?: DropdownOption[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const props = defineProps <RcDropdownMenuComponentProps>();
|
|
24
|
+
|
|
25
|
+
const openChanged = (event: boolean) => {
|
|
26
|
+
if (event) {
|
|
27
|
+
store.dispatch('action-menu/setResource', props.resource);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const emit = defineEmits<{(event: string, payload: any): void}>();
|
|
32
|
+
const route = useRoute();
|
|
33
|
+
|
|
34
|
+
const execute = (action: any, event: MouseEvent, args?: any) => {
|
|
35
|
+
if (action.disabled) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// this will come from extensions...
|
|
40
|
+
if (action.invoke) {
|
|
41
|
+
const fn = action.invoke;
|
|
42
|
+
|
|
43
|
+
if (fn && action.enabled) {
|
|
44
|
+
const resources = store.getters['action-menu/resources'];
|
|
45
|
+
const opts = {
|
|
46
|
+
event,
|
|
47
|
+
action,
|
|
48
|
+
isAlt: isAlternate(event)
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (resources.length === 1) {
|
|
52
|
+
fn.apply(this, [opts, resources]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} else if (props.customActions) {
|
|
56
|
+
// If the state of this component is controlled
|
|
57
|
+
// by props instead of Vuex, we assume you wouldn't want
|
|
58
|
+
// the mutation to have a dependency on Vuex either.
|
|
59
|
+
// So in that case we use events to execute actions instead.
|
|
60
|
+
// If an action list item is clicked, this
|
|
61
|
+
// component emits that event, then we assume the parent
|
|
62
|
+
// component will execute the action.
|
|
63
|
+
emit(
|
|
64
|
+
action.action,
|
|
65
|
+
{
|
|
66
|
+
action,
|
|
67
|
+
event,
|
|
68
|
+
...args,
|
|
69
|
+
route,
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
// If the state of this component is controlled
|
|
74
|
+
// by Vuex, mutate the store when an action is clicked.
|
|
75
|
+
const opts = { alt: isAlternate(event) };
|
|
76
|
+
|
|
77
|
+
store.dispatch('action-menu/execute', {
|
|
78
|
+
action, args, opts
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const options = computed(() => store.getters['action-menu/optionsArray']);
|
|
84
|
+
|
|
85
|
+
const menuOptions = () => {
|
|
86
|
+
if (props.customActions && props.customActions.length > 0) {
|
|
87
|
+
return props.customActions;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return options.value;
|
|
91
|
+
};
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<template>
|
|
95
|
+
<rc-dropdown-menu
|
|
96
|
+
:button-role="buttonRole || 'link'"
|
|
97
|
+
:button-size="buttonSize || 'small'"
|
|
98
|
+
:button-aria-label="buttonAriaLabel"
|
|
99
|
+
:dropdown-aria-label="dropdownAriaLabel"
|
|
100
|
+
:options="menuOptions()"
|
|
101
|
+
:data-testid="dataTestid"
|
|
102
|
+
@update:open="openChanged"
|
|
103
|
+
@select="(e: MouseEvent, option: object) => execute(option, e)"
|
|
104
|
+
/>
|
|
105
|
+
</template>
|
package/components/AppModal.vue
CHANGED
|
@@ -107,7 +107,7 @@ export default defineComponent({
|
|
|
107
107
|
width: this.modalWidth,
|
|
108
108
|
...this.stylesPropToObj,
|
|
109
109
|
};
|
|
110
|
-
}
|
|
110
|
+
},
|
|
111
111
|
},
|
|
112
112
|
setup(props) {
|
|
113
113
|
if (props.triggerFocusTrap) {
|
|
@@ -208,7 +208,7 @@ export default defineComponent({
|
|
|
208
208
|
display: flex;
|
|
209
209
|
justify-content: center;
|
|
210
210
|
align-items: center;
|
|
211
|
-
z-index:
|
|
211
|
+
z-index: z-index('modalOverlay');
|
|
212
212
|
|
|
213
213
|
.modal-container {
|
|
214
214
|
background-color: var(--modal-bg);
|
|
@@ -279,10 +279,12 @@ export default defineComponent({
|
|
|
279
279
|
<template>
|
|
280
280
|
<button
|
|
281
281
|
ref="btn"
|
|
282
|
+
role="button"
|
|
282
283
|
:class="classes"
|
|
283
284
|
:name="name"
|
|
284
285
|
:type="type"
|
|
285
286
|
:disabled="isDisabled"
|
|
287
|
+
:aria-disabled="isDisabled"
|
|
286
288
|
:tab-index="tabIndex"
|
|
287
289
|
:data-testid="componentTestid + '-async-button'"
|
|
288
290
|
@click="clicked"
|
|
@@ -70,6 +70,13 @@ export default {
|
|
|
70
70
|
const label = opt.labelKey ? this.t(opt.labelKey) : opt.label;
|
|
71
71
|
|
|
72
72
|
return tooltip || label || '';
|
|
73
|
+
},
|
|
74
|
+
actionAriaLabel(opt) {
|
|
75
|
+
const ariaLabel = opt.ariaLabel;
|
|
76
|
+
const label = opt.labelKey ? this.t(opt.labelKey) : opt.label;
|
|
77
|
+
const tooltip = opt.tooltipKey ? this.t(opt.tooltipKey) : opt.tooltip;
|
|
78
|
+
|
|
79
|
+
return ariaLabel || tooltip || label || undefined;
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
};
|
|
@@ -89,7 +96,7 @@ export default {
|
|
|
89
96
|
:class="opt.class"
|
|
90
97
|
:disabled="disabled || opt.disabled"
|
|
91
98
|
role="button"
|
|
92
|
-
:aria-label="
|
|
99
|
+
:aria-label="actionAriaLabel(opt)"
|
|
93
100
|
@click="change(opt.value)"
|
|
94
101
|
>
|
|
95
102
|
<slot
|
|
@@ -100,7 +107,7 @@ export default {
|
|
|
100
107
|
<i
|
|
101
108
|
v-if="opt.icon"
|
|
102
109
|
:class="{icon: true, [opt.icon]: true, [`icon-${iconSize}`]: !!iconSize }"
|
|
103
|
-
:alt="
|
|
110
|
+
:alt="actionAriaLabel(opt)"
|
|
104
111
|
/>
|
|
105
112
|
<t
|
|
106
113
|
v-if="opt.labelKey"
|
|
@@ -23,6 +23,7 @@ export default {
|
|
|
23
23
|
:style="{ backgroundColor: cluster.badge.color, color: cluster.badge.textColor }"
|
|
24
24
|
class="cluster-badge"
|
|
25
25
|
:class="{'cluster-badge-border': showBorders}"
|
|
26
|
+
:aria-label="t('clusterBadge.clusterComment', { text: cluster.badge?.text || '' })"
|
|
26
27
|
>
|
|
27
28
|
{{ cluster.badge.text }}
|
|
28
29
|
</div>
|
|
@@ -76,6 +76,7 @@ export default {
|
|
|
76
76
|
style="enable-background:new 0 0 100 100;"
|
|
77
77
|
xml:space="preserve"
|
|
78
78
|
>
|
|
79
|
+
<title>{{ t('nav.ariaLabel.localClusterIcon') }}</title>
|
|
79
80
|
<g>
|
|
80
81
|
<g>
|
|
81
82
|
<path
|
|
@@ -106,10 +107,12 @@ export default {
|
|
|
106
107
|
<i
|
|
107
108
|
v-if="!routeCombo && cluster.pinned"
|
|
108
109
|
class="icon icon-pin cluster-pin-icon"
|
|
110
|
+
:alt="t('nav.ariaLabel.pinCluster', { cluster: cluster.nameDisplay })"
|
|
109
111
|
/>
|
|
110
112
|
<i
|
|
111
113
|
v-else-if="routeCombo"
|
|
112
114
|
class="icon icon-keyboard_tab key-combo-icon"
|
|
115
|
+
:alt="t('nav.ariaLabel.clusterIconKeyCombo')"
|
|
113
116
|
/>
|
|
114
117
|
</div>
|
|
115
118
|
</template>
|
|
@@ -37,7 +37,19 @@ export default {
|
|
|
37
37
|
{{ cluster.badge.iconText }}
|
|
38
38
|
</div>
|
|
39
39
|
<!-- eslint-disable -->
|
|
40
|
-
<svg
|
|
40
|
+
<svg
|
|
41
|
+
v-else-if="cluster.isLocal && !cluster.isHarvester"
|
|
42
|
+
class="cluster-local-logo"
|
|
43
|
+
version="1.1"
|
|
44
|
+
id="Layer_1"
|
|
45
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
46
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
47
|
+
x="0px"
|
|
48
|
+
y="0px"
|
|
49
|
+
viewBox="0 0 100 100"
|
|
50
|
+
style="enable-background:new 0 0 100 100;"
|
|
51
|
+
xml:space="preserve">
|
|
52
|
+
<title>{{ t('nav.ariaLabel.clusterProvIcon', { cluster: 'local' }) }}</title>
|
|
41
53
|
<g>
|
|
42
54
|
<g>
|
|
43
55
|
<path class="rancher-icon-fill" d="M26.0862026,44.4953918H8.6165142c-5.5818157,0-9.3979139-4.6252708-8.4802637-10.1311035l2.858391-17.210701
|
|
@@ -60,6 +72,7 @@ export default {
|
|
|
60
72
|
v-else-if="cluster.providerNavLogo"
|
|
61
73
|
class="cluster-os-logo"
|
|
62
74
|
:src="cluster.providerNavLogo"
|
|
75
|
+
:alt="t('nav.ariaLabel.clusterProvIcon', { cluster: cluster.nameDisplay })"
|
|
63
76
|
>
|
|
64
77
|
</div>
|
|
65
78
|
</template>
|
|
@@ -36,10 +36,13 @@ export default {
|
|
|
36
36
|
|
|
37
37
|
data() {
|
|
38
38
|
return {
|
|
39
|
-
codeMirrorRef:
|
|
40
|
-
loaded:
|
|
41
|
-
removeKeyMapBox:
|
|
42
|
-
hasLintErrors:
|
|
39
|
+
codeMirrorRef: null,
|
|
40
|
+
loaded: false,
|
|
41
|
+
removeKeyMapBox: false,
|
|
42
|
+
hasLintErrors: false,
|
|
43
|
+
currFocusedElem: undefined,
|
|
44
|
+
isCodeMirrorFocused: false,
|
|
45
|
+
codeMirrorContainerRef: undefined
|
|
43
46
|
};
|
|
44
47
|
},
|
|
45
48
|
|
|
@@ -83,6 +86,10 @@ export default {
|
|
|
83
86
|
out.lint = { onUpdateLinting: this.handleLintErrors };
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
// fixes https://github.com/rancher/dashboard/issues/13653
|
|
90
|
+
// we can't use the inert HTML prop on the parent because it disables all interaction
|
|
91
|
+
out.readOnly = this.isDisabled ? 'nocursor' : false;
|
|
92
|
+
|
|
86
93
|
return out;
|
|
87
94
|
},
|
|
88
95
|
|
|
@@ -98,6 +105,14 @@ export default {
|
|
|
98
105
|
|
|
99
106
|
isNonDefaultKeyMap() {
|
|
100
107
|
return this.combinedOptions?.keyMap !== 'sublime';
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
isCodeMirrorContainerFocused() {
|
|
111
|
+
return this.currFocusedElem === this.codeMirrorContainerRef;
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
codeMirrorContainerTabIndex() {
|
|
115
|
+
return this.isCodeMirrorFocused ? 0 : -1;
|
|
101
116
|
}
|
|
102
117
|
},
|
|
103
118
|
|
|
@@ -111,13 +126,64 @@ export default {
|
|
|
111
126
|
}
|
|
112
127
|
},
|
|
113
128
|
|
|
129
|
+
async mounted() {
|
|
130
|
+
const el = this.$refs.codeMirrorContainer;
|
|
131
|
+
|
|
132
|
+
el.addEventListener('keydown', this.handleKeyPress);
|
|
133
|
+
this.codeMirrorContainerRef = this.$refs.codeMirrorContainer;
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
beforeUnmount() {
|
|
137
|
+
const el = this.$refs.codeMirrorContainer;
|
|
138
|
+
|
|
139
|
+
el.removeEventListener('keydown', this.handleKeyPress);
|
|
140
|
+
},
|
|
141
|
+
|
|
114
142
|
watch: {
|
|
115
143
|
hasLintErrors(neu) {
|
|
116
144
|
this.$emit('validationChanged', !neu);
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
isCodeMirrorContainerFocused: {
|
|
148
|
+
handler(neu) {
|
|
149
|
+
const codeMirrorEl = this.codeMirrorRef?.getInputField();
|
|
150
|
+
|
|
151
|
+
if (codeMirrorEl) {
|
|
152
|
+
codeMirrorEl.tabIndex = neu ? -1 : 0;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
immediate: true
|
|
117
156
|
}
|
|
118
157
|
},
|
|
119
158
|
|
|
120
159
|
methods: {
|
|
160
|
+
focusChanged(ev, isBlurred = false) {
|
|
161
|
+
if (isBlurred) {
|
|
162
|
+
this.currFocusedElem = undefined;
|
|
163
|
+
} else {
|
|
164
|
+
this.currFocusedElem = ev.target;
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
handleKeyPress(ev) {
|
|
169
|
+
// allows pressing escape in the editor, useful for modal editing with vim
|
|
170
|
+
if (this.isCodeMirrorFocused && ev.code === 'Escape') {
|
|
171
|
+
ev.preventDefault();
|
|
172
|
+
ev.stopPropagation();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// make focus leave the editor for it's parent container so that we can tab
|
|
176
|
+
const didPressEscapeSequence = ev.shiftKey && ev.code === 'Escape';
|
|
177
|
+
|
|
178
|
+
if (this.isCodeMirrorFocused && didPressEscapeSequence) {
|
|
179
|
+
this.$refs?.codeMirrorContainer?.focus();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// if parent container is focused and we press a trigger, focus goes to the editor inside
|
|
183
|
+
if (this.isCodeMirrorContainerFocused && (ev.code === 'Enter' || ev.code === 'Space')) {
|
|
184
|
+
this.codeMirrorRef.focus();
|
|
185
|
+
}
|
|
186
|
+
},
|
|
121
187
|
/**
|
|
122
188
|
* Codemirror yaml linting uses js-yaml parse
|
|
123
189
|
* it does not distinguish between warnings and errors so we will treat all yaml lint messages as errors
|
|
@@ -161,10 +227,12 @@ export default {
|
|
|
161
227
|
},
|
|
162
228
|
|
|
163
229
|
onFocus() {
|
|
230
|
+
this.isCodeMirrorFocused = true;
|
|
164
231
|
this.$emit('onFocus', true);
|
|
165
232
|
},
|
|
166
233
|
|
|
167
234
|
onBlur() {
|
|
235
|
+
this.isCodeMirrorFocused = false;
|
|
168
236
|
this.$emit('onFocus', false);
|
|
169
237
|
},
|
|
170
238
|
|
|
@@ -183,8 +251,12 @@ export default {
|
|
|
183
251
|
|
|
184
252
|
<template>
|
|
185
253
|
<div
|
|
186
|
-
|
|
254
|
+
ref="codeMirrorContainer"
|
|
255
|
+
:tabindex="codeMirrorContainerTabIndex"
|
|
256
|
+
class="code-mirror code-mirror-container"
|
|
187
257
|
:class="{['as-text-area']: asTextArea}"
|
|
258
|
+
@focusin="focusChanged"
|
|
259
|
+
@blur="focusChanged($event, true)"
|
|
188
260
|
>
|
|
189
261
|
<div v-if="loaded">
|
|
190
262
|
<div
|
|
@@ -204,6 +276,7 @@ export default {
|
|
|
204
276
|
</div>
|
|
205
277
|
</div>
|
|
206
278
|
<Codemirror
|
|
279
|
+
id="code-mirror-el"
|
|
207
280
|
ref="codeMirrorRef"
|
|
208
281
|
:value="value"
|
|
209
282
|
:options="combinedOptions"
|
|
@@ -215,6 +288,12 @@ export default {
|
|
|
215
288
|
@focus="onFocus"
|
|
216
289
|
@blur="onBlur"
|
|
217
290
|
/>
|
|
291
|
+
<span
|
|
292
|
+
v-show="isCodeMirrorFocused"
|
|
293
|
+
class="escape-text"
|
|
294
|
+
role="alert"
|
|
295
|
+
:aria-describedby="t('wm.containerShell.escapeText')"
|
|
296
|
+
>{{ t('codeMirror.escapeText') }}</span>
|
|
218
297
|
</div>
|
|
219
298
|
<div v-else>
|
|
220
299
|
Loading...
|
|
@@ -226,6 +305,10 @@ export default {
|
|
|
226
305
|
$code-mirror-animation-time: 0.1s;
|
|
227
306
|
|
|
228
307
|
.code-mirror {
|
|
308
|
+
&.code-mirror-container:focus-visible {
|
|
309
|
+
@include focus-outline;
|
|
310
|
+
}
|
|
311
|
+
|
|
229
312
|
&.as-text-area .codemirror-container{
|
|
230
313
|
min-height: 40px;
|
|
231
314
|
position: relative;
|
|
@@ -321,6 +404,14 @@ export default {
|
|
|
321
404
|
|
|
322
405
|
.code-mirror {
|
|
323
406
|
position: relative;
|
|
407
|
+
margin-bottom: 20px;
|
|
408
|
+
|
|
409
|
+
.escape-text {
|
|
410
|
+
font-size: 12px;
|
|
411
|
+
position: absolute;
|
|
412
|
+
bottom: -20px;
|
|
413
|
+
left: 0;
|
|
414
|
+
}
|
|
324
415
|
|
|
325
416
|
.codemirror-container {
|
|
326
417
|
z-index: 0;
|
package/components/Collapse.vue
CHANGED
|
@@ -13,7 +13,12 @@ export default {
|
|
|
13
13
|
title: {
|
|
14
14
|
type: String,
|
|
15
15
|
default: ''
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
isDisabled: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: false
|
|
21
|
+
},
|
|
17
22
|
},
|
|
18
23
|
|
|
19
24
|
methods: {
|
|
@@ -25,10 +30,15 @@ export default {
|
|
|
25
30
|
</script>
|
|
26
31
|
|
|
27
32
|
<template>
|
|
28
|
-
<div
|
|
33
|
+
<div
|
|
34
|
+
class="collapse"
|
|
35
|
+
:class="{ 'disabled': isDisabled }"
|
|
36
|
+
>
|
|
29
37
|
<slot name="title">
|
|
30
38
|
<div
|
|
31
39
|
class="advanced text-link"
|
|
40
|
+
:class="{ 'disabled': isDisabled }"
|
|
41
|
+
:disabled="isDisabled"
|
|
32
42
|
data-testid="collapse-div"
|
|
33
43
|
@click="showAdvanced"
|
|
34
44
|
>
|
|
@@ -59,11 +69,14 @@ export default {
|
|
|
59
69
|
<style lang="scss" scoped>
|
|
60
70
|
.advanced {
|
|
61
71
|
user-select: none;
|
|
62
|
-
padding: 0 5px;
|
|
63
72
|
cursor: pointer;
|
|
64
73
|
line-height: 40px;
|
|
65
74
|
font-size: 15px;
|
|
66
75
|
font-weight: 500;
|
|
76
|
+
|
|
77
|
+
.disabled {
|
|
78
|
+
cursor: not-allowed;
|
|
79
|
+
}
|
|
67
80
|
}
|
|
68
81
|
.content {
|
|
69
82
|
background: var(--nav-active);
|
|
@@ -490,7 +490,12 @@ export default {
|
|
|
490
490
|
class="subtype-banner"
|
|
491
491
|
:class="{ selected: subtype.id === _selectedSubtype }"
|
|
492
492
|
:data-testid="`subtype-banner-item-${subtype.id}`"
|
|
493
|
+
tabindex="0"
|
|
494
|
+
:aria-disabled="false"
|
|
495
|
+
:aria-label="subtype.description ? `${subtype.label} - ${subtype.description}` : subtype.label"
|
|
496
|
+
role="link"
|
|
493
497
|
@click="selectType(subtype.id, $event)"
|
|
498
|
+
@keyup.enter.space="selectType(subtype.id, $event)"
|
|
494
499
|
>
|
|
495
500
|
<slot name="subtype-content">
|
|
496
501
|
<div class="subtype-container">
|
|
@@ -825,6 +830,10 @@ export default {
|
|
|
825
830
|
.round-image {
|
|
826
831
|
background-color: var(--primary);
|
|
827
832
|
}
|
|
833
|
+
|
|
834
|
+
&:focus-visible {
|
|
835
|
+
@include focus-outline;
|
|
836
|
+
}
|
|
828
837
|
}
|
|
829
838
|
}
|
|
830
839
|
|
|
@@ -355,7 +355,8 @@ export default {
|
|
|
355
355
|
<button
|
|
356
356
|
v-if="canEditProjectMembers"
|
|
357
357
|
type="button"
|
|
358
|
-
class="
|
|
358
|
+
class="btn btn-sm role-secondary mr-10 right"
|
|
359
|
+
:data-testid="`add-project-member-${getProjectLabel(group).replace(' ', '').toLowerCase()}`"
|
|
359
360
|
@click="addProjectMember(group)"
|
|
360
361
|
>
|
|
361
362
|
{{ t('members.createActionLabel') }}
|
|
@@ -37,7 +37,7 @@ export default {
|
|
|
37
37
|
handleLineBreaksConsentText(banner) {
|
|
38
38
|
if (banner.text?.length) {
|
|
39
39
|
// split text by newline char
|
|
40
|
-
const textArray = banner.text.split(
|
|
40
|
+
const textArray = banner.text.split('\n').filter((element) => element);
|
|
41
41
|
|
|
42
42
|
if (textArray.length > 1) {
|
|
43
43
|
textArray.forEach((str, i) => {
|
|
@@ -130,11 +130,11 @@ export default {
|
|
|
130
130
|
|
|
131
131
|
if (isEmpty(banner)) {
|
|
132
132
|
if (showHeader && this.header) {
|
|
133
|
-
bannerContent = bannerHeader || {};
|
|
133
|
+
bannerContent = this.handleLineBreaksConsentText(bannerHeader) || {};
|
|
134
134
|
} else if (showConsent && this.consent) {
|
|
135
135
|
bannerContent = this.handleLineBreaksConsentText(bannerConsent) || {};
|
|
136
136
|
} else if (showFooter && this.footer) {
|
|
137
|
-
bannerContent = bannerFooter || {};
|
|
137
|
+
bannerContent = this.handleLineBreaksConsentText(bannerFooter) || {};
|
|
138
138
|
} else {
|
|
139
139
|
bannerContent = {};
|
|
140
140
|
}
|
|
@@ -168,16 +168,20 @@ export default {
|
|
|
168
168
|
>
|
|
169
169
|
<!-- text as array to support line breaks programmatically rather than just exposing HTML -->
|
|
170
170
|
<div v-if="isTextAnArray">
|
|
171
|
-
<
|
|
171
|
+
<div
|
|
172
172
|
v-for="(text, index) in banner.text"
|
|
173
173
|
:key="index"
|
|
174
|
+
class="array-row"
|
|
174
175
|
>
|
|
175
176
|
{{ text }}
|
|
176
|
-
</
|
|
177
|
+
</div>
|
|
177
178
|
</div>
|
|
178
|
-
<
|
|
179
|
+
<div
|
|
180
|
+
v-else
|
|
181
|
+
class="single-row"
|
|
182
|
+
>
|
|
179
183
|
{{ banner.text }}
|
|
180
|
-
</
|
|
184
|
+
</div>
|
|
181
185
|
</div>
|
|
182
186
|
<div v-else-if="showDialog">
|
|
183
187
|
<div class="banner-dialog-glass" />
|
|
@@ -192,16 +196,20 @@ export default {
|
|
|
192
196
|
>
|
|
193
197
|
<!-- text as array to support line breaks programmatically rather than just exposing HTML -->
|
|
194
198
|
<div v-if="isTextAnArray">
|
|
195
|
-
<
|
|
199
|
+
<div
|
|
196
200
|
v-for="(text, index) in banner.text"
|
|
197
201
|
:key="index"
|
|
202
|
+
class="array-row"
|
|
198
203
|
>
|
|
199
204
|
{{ text }}
|
|
200
|
-
</
|
|
205
|
+
</div>
|
|
201
206
|
</div>
|
|
202
|
-
<
|
|
207
|
+
<div
|
|
208
|
+
v-else
|
|
209
|
+
class="single-row"
|
|
210
|
+
>
|
|
203
211
|
{{ banner.text }}
|
|
204
|
-
</
|
|
212
|
+
</div>
|
|
205
213
|
</div>
|
|
206
214
|
<button
|
|
207
215
|
class="btn role-primary"
|
|
@@ -223,7 +231,6 @@ export default {
|
|
|
223
231
|
padding: 0 20px;
|
|
224
232
|
|
|
225
233
|
&.banner-consent {
|
|
226
|
-
position: absolute;
|
|
227
234
|
height: unset;
|
|
228
235
|
min-height: 2em;
|
|
229
236
|
overflow: hidden;
|
package/components/Import.vue
CHANGED
|
@@ -14,7 +14,7 @@ import { NAMESPACE } from '@shell/config/types';
|
|
|
14
14
|
import { NAME as NAME_COL, TYPE, NAMESPACE as NAMESPACE_COL, AGE } from '@shell/config/table-headers';
|
|
15
15
|
|
|
16
16
|
export default {
|
|
17
|
-
emits: ['close'],
|
|
17
|
+
emits: ['close', 'onReadyYamlEditor'],
|
|
18
18
|
|
|
19
19
|
components: {
|
|
20
20
|
AsyncButton,
|
|
@@ -111,6 +111,10 @@ export default {
|
|
|
111
111
|
this.close();
|
|
112
112
|
}
|
|
113
113
|
},
|
|
114
|
+
|
|
115
|
+
onReadyYamlEditor(arg) {
|
|
116
|
+
this.$emit('onReadyYamlEditor', arg);
|
|
117
|
+
}
|
|
114
118
|
},
|
|
115
119
|
};
|
|
116
120
|
</script>
|
|
@@ -121,6 +125,7 @@ export default {
|
|
|
121
125
|
v-else
|
|
122
126
|
:show-highlight-border="false"
|
|
123
127
|
data-testid="import-yaml"
|
|
128
|
+
:trigger-focus-trap="true"
|
|
124
129
|
>
|
|
125
130
|
<template #title>
|
|
126
131
|
<div style="display: block; width: 100%;">
|
|
@@ -134,6 +139,8 @@ export default {
|
|
|
134
139
|
<div class="row">
|
|
135
140
|
<div class="col span-6">
|
|
136
141
|
<FileSelector
|
|
142
|
+
role="button"
|
|
143
|
+
:aria-label="t('generic.readFromFileArea', { area: t('import.title') })"
|
|
137
144
|
class="btn role-secondary pull-left"
|
|
138
145
|
:label="t('generic.readFromFile')"
|
|
139
146
|
@selected="onFileSelected"
|
|
@@ -174,6 +181,7 @@ export default {
|
|
|
174
181
|
ref="yamleditor"
|
|
175
182
|
v-model:value="currentYaml"
|
|
176
183
|
class="yaml-editor"
|
|
184
|
+
@onReady="onReadyYamlEditor"
|
|
177
185
|
/>
|
|
178
186
|
<Banner
|
|
179
187
|
v-for="(err, i) in errors"
|
|
@@ -189,6 +197,8 @@ export default {
|
|
|
189
197
|
style="width: 100%"
|
|
190
198
|
>
|
|
191
199
|
<button
|
|
200
|
+
:aria-label="t('generic.close')"
|
|
201
|
+
role="button"
|
|
192
202
|
type="button"
|
|
193
203
|
class="btn role-primary"
|
|
194
204
|
data-testid="import-yaml-close"
|
|
@@ -203,6 +213,8 @@ export default {
|
|
|
203
213
|
style="width: 100%"
|
|
204
214
|
>
|
|
205
215
|
<button
|
|
216
|
+
:aria-label="t('generic.cancel')"
|
|
217
|
+
role="button"
|
|
206
218
|
type="button"
|
|
207
219
|
class="btn role-secondary mr-10"
|
|
208
220
|
data-testid="import-yaml-cancel"
|
|
@@ -215,6 +227,7 @@ export default {
|
|
|
215
227
|
mode="import"
|
|
216
228
|
:disabled="!currentYaml.length"
|
|
217
229
|
data-testid="import-yaml-import-action"
|
|
230
|
+
:aria-label="t('import.title')"
|
|
218
231
|
@click="importYaml"
|
|
219
232
|
/>
|
|
220
233
|
</div>
|
|
@@ -94,7 +94,7 @@ export default {
|
|
|
94
94
|
if (neu) {
|
|
95
95
|
this.afterLoginRoute = neu;
|
|
96
96
|
} else {
|
|
97
|
-
this.afterLoginRoute = this.routeFromDropdown?.value;
|
|
97
|
+
this.afterLoginRoute = this.routeFromDropdown?.value || this.routeDropdownOptions[0]?.value;
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
100
|
}
|
|
@@ -120,10 +120,12 @@ export default {
|
|
|
120
120
|
:val="false"
|
|
121
121
|
:value="afterLoginRoute=== 'home' || afterLoginRoute === 'last-visited'"
|
|
122
122
|
:v-bind="$attrs"
|
|
123
|
-
|
|
123
|
+
:prevent-focus-on-radio-groups="true"
|
|
124
|
+
@update:value="updateLoginRoute(null)"
|
|
124
125
|
/>
|
|
125
126
|
<Select
|
|
126
127
|
v-model:value="routeFromDropdown"
|
|
128
|
+
:aria-label="t('landing.landingPrefs.ariaLabelTakeMeToCluster')"
|
|
127
129
|
:searchable="true"
|
|
128
130
|
:disabled="afterLoginRoute === 'home' || afterLoginRoute === 'last-visited'"
|
|
129
131
|
:clearable="false"
|