@rancher/shell 3.0.5-rc.1 → 3.0.5-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/images/providers/sks.svg +1 -0
- package/assets/styles/base/_helpers.scss +4 -0
- package/assets/styles/base/_variables.scss +1 -0
- package/assets/translations/en-us.yaml +31 -15
- package/assets/translations/zh-hans.yaml +4 -3
- package/chart/monitoring/index.vue +3 -1
- package/components/ActionDropdownShell.vue +71 -0
- package/components/AppModal.vue +18 -4
- package/components/CommunityLinks.vue +3 -58
- package/components/CruResource.vue +6 -1
- package/components/ExplorerProjectsNamespaces.vue +12 -4
- package/components/GlobalRoleBindings.vue +5 -1
- package/components/GrowlManager.vue +1 -0
- package/components/LandingPagePreference.vue +2 -0
- package/components/LocaleSelector.vue +1 -1
- package/components/ModalManager.vue +55 -0
- package/components/PromptModal.vue +47 -8
- package/components/ResourceDetail/Masthead.vue +38 -12
- package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
- package/components/ResourceDetail/index.vue +47 -12
- package/components/ResourceTable.vue +54 -19
- package/components/SideNav.vue +5 -1
- package/components/SlideInPanelManager.vue +126 -0
- package/components/SortableTable/THead.vue +5 -2
- package/components/SortableTable/actions.js +1 -1
- package/components/SortableTable/index.vue +54 -40
- package/components/SortableTable/paging.js +16 -19
- package/components/SortableTable/selection.js +0 -11
- package/components/Wizard.vue +2 -2
- package/components/__tests__/ModalManager.spec.ts +176 -0
- package/components/__tests__/PromptModal.test.ts +148 -0
- package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
- package/components/auth/AuthBanner.vue +13 -11
- package/components/auth/Principal.vue +1 -0
- package/components/auth/login/ldap.vue +1 -1
- package/components/fleet/FleetResources.vue +21 -6
- package/components/form/ArrayList.vue +10 -6
- package/components/form/BannerSettings.vue +17 -2
- package/components/form/ColorInput.vue +35 -6
- package/components/form/EnvVars.vue +1 -0
- package/components/form/LabeledSelect.vue +18 -23
- package/components/form/MatchExpressions.vue +4 -1
- package/components/form/NameNsDescription.vue +5 -1
- package/components/form/NotificationSettings.vue +15 -1
- package/components/form/Password.vue +1 -0
- package/components/form/Probe.vue +1 -0
- package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +15 -34
- package/components/form/SSHKnownHosts/index.vue +14 -11
- package/components/form/Select.vue +1 -15
- package/components/form/ValueFromResource.vue +12 -12
- package/components/form/__tests__/ArrayList.test.ts +2 -2
- package/components/form/__tests__/ColorInput.test.ts +35 -0
- package/components/form/__tests__/LabeledSelect.test.ts +40 -0
- package/components/form/__tests__/SSHKnownHosts.test.ts +11 -2
- package/components/nav/Group.vue +12 -4
- package/components/nav/Header.vue +16 -43
- package/components/nav/NamespaceFilter.vue +134 -86
- package/components/nav/TopLevelMenu.vue +4 -5
- package/components/nav/WindowManager/ContainerLogs.vue +87 -61
- package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
- package/components/templates/default.vue +6 -3
- package/components/templates/home.vue +6 -0
- package/components/templates/plain.vue +6 -3
- package/composables/focusTrap.ts +12 -4
- package/config/store.js +4 -0
- package/config/uiplugins.js +5 -1
- package/core/types.ts +7 -6
- package/detail/catalog.cattle.io.app.vue +6 -1
- package/detail/fleet.cattle.io.bundle.vue +70 -6
- package/detail/fleet.cattle.io.gitrepo.vue +1 -1
- package/detail/namespace.vue +0 -3
- package/detail/node.vue +17 -13
- package/detail/provisioning.cattle.io.cluster.vue +72 -6
- package/dialog/AddCustomBadgeDialog.vue +0 -1
- package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
- package/dialog/AssignToDialog.vue +176 -0
- package/dialog/ChangePasswordDialog.vue +106 -0
- package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
- package/dialog/DisableAuthProviderDialog.vue +101 -0
- package/dialog/DrainNode.vue +1 -1
- package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
- package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +69 -57
- package/dialog/FeatureFlagListDialog.vue +288 -0
- package/dialog/ForceMachineRemoveDialog.vue +1 -1
- package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
- package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
- package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -62
- package/dialog/MoveNamespaceDialog.vue +157 -0
- package/dialog/ScalePoolDownDialog.vue +1 -1
- package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
- package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
- package/dialog/WechatDialog.vue +57 -0
- package/edit/auth/azuread.vue +1 -1
- package/edit/auth/github.vue +1 -1
- package/edit/auth/googleoauth.vue +1 -1
- package/edit/auth/ldap/index.vue +1 -1
- package/edit/auth/oidc.vue +1 -1
- package/edit/auth/saml.vue +1 -1
- package/edit/cloudcredential.vue +24 -10
- package/edit/management.cattle.io.user.vue +28 -3
- package/edit/namespace.vue +1 -4
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -10
- package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
- package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +24 -7
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +4 -1
- package/initialize/install-plugins.js +2 -1
- package/list/harvesterhci.io.management.cluster.vue +4 -1
- package/list/management.cattle.io.feature.vue +4 -288
- package/machine-config/azure.vue +16 -4
- package/mixins/vue-select-overrides.js +0 -4
- package/models/fleet.cattle.io.cluster.js +8 -2
- package/models/fleet.cattle.io.gitrepo.js +8 -34
- package/models/management.cattle.io.feature.js +7 -1
- package/models/namespace.js +7 -1
- package/package.json +1 -1
- package/pages/about.vue +13 -3
- package/pages/account/index.vue +12 -5
- package/pages/auth/login.vue +7 -4
- package/pages/auth/setup.vue +1 -0
- package/pages/auth/verify.vue +9 -7
- package/pages/c/_cluster/apps/charts/install.vue +26 -26
- package/pages/c/_cluster/auth/config/index.vue +10 -12
- package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
- package/pages/c/_cluster/explorer/index.vue +17 -15
- package/pages/c/_cluster/istio/index.vue +2 -2
- package/pages/c/_cluster/longhorn/index.vue +1 -1
- package/pages/c/_cluster/monitoring/index.vue +1 -1
- package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
- package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
- package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
- package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
- package/pages/c/_cluster/neuvector/index.vue +1 -1
- package/pages/c/_cluster/settings/banners.vue +4 -3
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
- package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
- package/pages/c/_cluster/uiplugins/index.vue +98 -55
- package/pages/diagnostic.vue +12 -9
- package/pages/fail-whale.vue +8 -5
- package/pages/prefs.vue +7 -6
- package/plugins/internal-api/index.ts +37 -0
- package/plugins/internal-api/shared/base-api.ts +13 -0
- package/plugins/internal-api/shell/shell.api.ts +108 -0
- package/plugins/steve/actions.js +0 -12
- package/public/index.html +1 -0
- package/rancher-components/Card/Card.vue +1 -1
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +47 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
- package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
- package/rancher-components/Form/Radio/RadioButton.vue +20 -4
- package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
- package/rancher-components/Form/Radio/RadioGroup.vue +75 -35
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +5 -0
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
- package/rancher-components/RcButton/RcButton.vue +2 -1
- package/rancher-components/RcButton/types.ts +1 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +17 -6
- package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
- package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
- package/rancher-components/RcDropdown/index.ts +2 -0
- package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
- package/scripts/extension/bundle +20 -0
- package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
- package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
- package/scripts/extension/helmpatch +44 -31
- package/scripts/extension/publish +12 -13
- package/scripts/typegen.sh +2 -4
- package/store/action-menu.js +26 -56
- package/store/index.js +5 -0
- package/store/modal.ts +71 -0
- package/store/slideInPanel.ts +47 -0
- package/store/type-map.js +8 -1
- package/store/type-map.utils.ts +4 -4
- package/types/global-vue.d.ts +5 -0
- package/types/internal-api/shell/growl.d.ts +25 -0
- package/types/internal-api/shell/modal.d.ts +77 -0
- package/types/internal-api/shell/slideIn.d.ts +15 -0
- package/types/resources/fleet.d.ts +0 -14
- package/types/shell/index.d.ts +35 -23
- package/types/vue-shim.d.ts +4 -1
- package/utils/__mocks__/tabbable.js +13 -0
- package/utils/__tests__/object.test.ts +38 -4
- package/utils/fleet.ts +15 -73
- package/utils/object.js +48 -5
- package/utils/validators/formRules/__tests__/index.test.ts +10 -1
- package/utils/validators/formRules/index.ts +27 -3
- package/components/AssignTo.vue +0 -199
- package/components/DisableAuthProviderModal.vue +0 -115
- package/components/MoveModal.vue +0 -167
- package/components/PromptChangePassword.vue +0 -123
- package/components/fleet/FleetBundleResources.vue +0 -86
- package/types/vue-shim.d +0 -20
|
@@ -21,7 +21,12 @@ export default ({
|
|
|
21
21
|
return [_EDIT, _VIEW].includes(value);
|
|
22
22
|
},
|
|
23
23
|
default: _EDIT,
|
|
24
|
-
}
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
hiddenAriaDescribedLabel: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: ''
|
|
29
|
+
},
|
|
25
30
|
},
|
|
26
31
|
|
|
27
32
|
});
|
|
@@ -29,12 +34,20 @@ export default ({
|
|
|
29
34
|
|
|
30
35
|
<template>
|
|
31
36
|
<div>
|
|
37
|
+
<p
|
|
38
|
+
v-if="hiddenAriaDescribedLabel"
|
|
39
|
+
id="hidden-label-notification-settings"
|
|
40
|
+
class="sr-only"
|
|
41
|
+
>
|
|
42
|
+
{{ hiddenAriaDescribedLabel }}
|
|
43
|
+
</p>
|
|
32
44
|
<div class="row mb-20">
|
|
33
45
|
<div class="col span-6">
|
|
34
46
|
<Checkbox
|
|
35
47
|
:mode="mode"
|
|
36
48
|
:value="value.showMessage === 'true'"
|
|
37
49
|
:label="t('notifications.loginError.showCheckboxLabel')"
|
|
50
|
+
aria-describedby="hidden-label-notification-settings"
|
|
38
51
|
@update:value="value.showMessage = $event.toString()"
|
|
39
52
|
/>
|
|
40
53
|
</div>
|
|
@@ -48,6 +61,7 @@ export default ({
|
|
|
48
61
|
:mode="mode"
|
|
49
62
|
:disabled="value.showMessage === 'false'"
|
|
50
63
|
:label="t('notifications.loginError.messageLabel')"
|
|
64
|
+
aria-describedby="hidden-label-notification-settings"
|
|
51
65
|
/>
|
|
52
66
|
</div>
|
|
53
67
|
</div>
|
|
@@ -137,6 +137,7 @@ export default {
|
|
|
137
137
|
tabindex="0"
|
|
138
138
|
class="hide-show"
|
|
139
139
|
role="button"
|
|
140
|
+
:aria-label="reveal ? t('action.ariaLabel.hidePass', { area: label }) : t('action.ariaLabel.showPass', { area: label })"
|
|
140
141
|
@click.prevent.stop="hideShowFn"
|
|
141
142
|
@keyup.space.prevent.stop="hideShowFn"
|
|
142
143
|
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mount, VueWrapper } from '@vue/test-utils';
|
|
2
2
|
import { _EDIT } from '@shell/config/query-params';
|
|
3
|
-
import KnownHostsEditDialog from '@shell/
|
|
3
|
+
import KnownHostsEditDialog from '@shell/dialog/KnownHostsEditDialog.vue';
|
|
4
4
|
import CodeMirror from '@shell/components/CodeMirror.vue';
|
|
5
5
|
import FileSelector from '@shell/components/form/FileSelector.vue';
|
|
6
6
|
|
|
@@ -14,17 +14,6 @@ const requiredSetup = () => {
|
|
|
14
14
|
return { global: { mocks: { $store: mockedStore() } } };
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
jest.mock('focus-trap', () => {
|
|
18
|
-
return {
|
|
19
|
-
createFocusTrap: jest.fn().mockImplementation(() => {
|
|
20
|
-
return {
|
|
21
|
-
activate: jest.fn(),
|
|
22
|
-
deactivate: jest.fn(),
|
|
23
|
-
};
|
|
24
|
-
}),
|
|
25
|
-
};
|
|
26
|
-
});
|
|
27
|
-
|
|
28
17
|
describe('component: KnownHostsEditDialog', () => {
|
|
29
18
|
beforeEach(() => {
|
|
30
19
|
document.body.innerHTML = '<div id="modals"></div>';
|
|
@@ -44,8 +33,6 @@ describe('component: KnownHostsEditDialog', () => {
|
|
|
44
33
|
});
|
|
45
34
|
|
|
46
35
|
it('should update text from CodeMirror', async() => {
|
|
47
|
-
await wrapper.setData({ showModal: true });
|
|
48
|
-
|
|
49
36
|
expect(wrapper.vm.text).toBe('line1\nline2\n');
|
|
50
37
|
|
|
51
38
|
const codeMirror = wrapper.getComponent(CodeMirror);
|
|
@@ -62,8 +49,6 @@ describe('component: KnownHostsEditDialog', () => {
|
|
|
62
49
|
});
|
|
63
50
|
|
|
64
51
|
it('should update text from FileSelector', async() => {
|
|
65
|
-
await wrapper.setData({ showModal: true });
|
|
66
|
-
|
|
67
52
|
expect(wrapper.vm.text).toBe('line1\nline2\n');
|
|
68
53
|
|
|
69
54
|
const fileSelector = wrapper.getComponent(FileSelector);
|
|
@@ -78,38 +63,34 @@ describe('component: KnownHostsEditDialog', () => {
|
|
|
78
63
|
});
|
|
79
64
|
|
|
80
65
|
it('should save changes and close dialog', async() => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
66
|
+
const closed = jest.spyOn(wrapper.vm, 'closed');
|
|
67
|
+
|
|
68
|
+
await wrapper.setData({ text: 'foo' });
|
|
85
69
|
|
|
86
70
|
expect(wrapper.vm.value).toBe('line1\nline2\n');
|
|
87
71
|
expect(wrapper.vm.text).toBe('foo');
|
|
88
72
|
|
|
89
73
|
await wrapper.vm.closeDialog(true);
|
|
90
74
|
|
|
91
|
-
expect(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
expect(dialog).toBeNull();
|
|
75
|
+
expect(closed).toHaveBeenCalledWith({
|
|
76
|
+
success: true,
|
|
77
|
+
value: 'foo'
|
|
78
|
+
});
|
|
96
79
|
});
|
|
97
80
|
|
|
98
81
|
it('should discard changes and close dialog', async() => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
});
|
|
82
|
+
const closed = jest.spyOn(wrapper.vm, 'closed');
|
|
83
|
+
|
|
84
|
+
await wrapper.setData({ text: 'foo' });
|
|
103
85
|
|
|
104
86
|
expect(wrapper.vm.value).toBe('line1\nline2\n');
|
|
105
87
|
expect(wrapper.vm.text).toBe('foo');
|
|
106
88
|
|
|
107
89
|
await wrapper.vm.closeDialog(false);
|
|
108
90
|
|
|
109
|
-
expect(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
expect(dialog).toBeNull();
|
|
91
|
+
expect(closed).toHaveBeenCalledWith({
|
|
92
|
+
success: false,
|
|
93
|
+
value: 'line1\nline2\n'
|
|
94
|
+
});
|
|
114
95
|
});
|
|
115
96
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { defineComponent } from 'vue';
|
|
3
|
-
import KnownHostsEditDialog from './KnownHostsEditDialog.vue';
|
|
4
3
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
5
4
|
|
|
6
5
|
export default defineComponent({
|
|
@@ -20,8 +19,6 @@ export default defineComponent({
|
|
|
20
19
|
},
|
|
21
20
|
},
|
|
22
21
|
|
|
23
|
-
components: { KnownHostsEditDialog },
|
|
24
|
-
|
|
25
22
|
computed: {
|
|
26
23
|
isViewMode() {
|
|
27
24
|
return this.mode === _VIEW;
|
|
@@ -40,7 +37,20 @@ export default defineComponent({
|
|
|
40
37
|
methods: {
|
|
41
38
|
openDialog() {
|
|
42
39
|
(this.$refs.button as HTMLInputElement)?.blur();
|
|
43
|
-
|
|
40
|
+
|
|
41
|
+
this.$store.dispatch('management/promptModal', {
|
|
42
|
+
component: 'KnownHostsEditDialog',
|
|
43
|
+
returnFocusSelector: '#known-ssh-hosts-trigger',
|
|
44
|
+
testId: 'sshKnownHostsDialog',
|
|
45
|
+
height: 'auto',
|
|
46
|
+
componentProps: {
|
|
47
|
+
mode: this.mode,
|
|
48
|
+
value: this.value,
|
|
49
|
+
closed: (res: any) => {
|
|
50
|
+
this.dialogClosed(res);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
44
54
|
},
|
|
45
55
|
|
|
46
56
|
dialogClosed(result: any) {
|
|
@@ -78,13 +88,6 @@ export default defineComponent({
|
|
|
78
88
|
:alt="t('secret.ssh.editKnownHosts.title')"
|
|
79
89
|
/>
|
|
80
90
|
</button>
|
|
81
|
-
|
|
82
|
-
<KnownHostsEditDialog
|
|
83
|
-
ref="editDialog"
|
|
84
|
-
:value="value"
|
|
85
|
-
:mode="mode"
|
|
86
|
-
@closed="dialogClosed"
|
|
87
|
-
/>
|
|
88
91
|
</template>
|
|
89
92
|
</div>
|
|
90
93
|
</template>
|
|
@@ -125,21 +125,6 @@ export default {
|
|
|
125
125
|
},
|
|
126
126
|
|
|
127
127
|
focusSearch() {
|
|
128
|
-
// we need this override as in a "closeOnSelect" type of component
|
|
129
|
-
// if we don't have this override, it would open again
|
|
130
|
-
if (this.overridesMixinPreventDoubleTriggerKeysOpen) {
|
|
131
|
-
this.$nextTick(() => {
|
|
132
|
-
const el = this.$refs['select'];
|
|
133
|
-
|
|
134
|
-
if ( el ) {
|
|
135
|
-
el.focus();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
this.overridesMixinPreventDoubleTriggerKeysOpen = false;
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
128
|
this.$refs['select-input'].open = true;
|
|
144
129
|
|
|
145
130
|
this.$nextTick(() => {
|
|
@@ -317,6 +302,7 @@ export default {
|
|
|
317
302
|
@open="onOpen"
|
|
318
303
|
@close="onClose"
|
|
319
304
|
@option:created="(e) => $emit('createdListItem', e)"
|
|
305
|
+
@keydown.enter.stop
|
|
320
306
|
>
|
|
321
307
|
<template
|
|
322
308
|
#option="option"
|
|
@@ -4,7 +4,7 @@ import { get } from '@shell/utils/object';
|
|
|
4
4
|
import { _VIEW } from '@shell/config/query-params';
|
|
5
5
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
6
6
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
7
|
-
import { ref,
|
|
7
|
+
import { ref, watch } from 'vue';
|
|
8
8
|
|
|
9
9
|
export default {
|
|
10
10
|
emits: ['update:value', 'remove'],
|
|
@@ -83,14 +83,14 @@ export default {
|
|
|
83
83
|
|
|
84
84
|
switch (type.value) {
|
|
85
85
|
case 'resourceFieldRef':
|
|
86
|
-
name.value =
|
|
87
|
-
refName.value =
|
|
86
|
+
name.value = props.value.name;
|
|
87
|
+
refName.value = props.value.valueFrom[type.value].containerName;
|
|
88
88
|
key.value = props.value.valueFrom[type.value].resource || '';
|
|
89
89
|
break;
|
|
90
90
|
case 'configMapKeyRef':
|
|
91
|
-
name.value =
|
|
91
|
+
name.value = props.value.name;
|
|
92
92
|
key.value = props.value.valueFrom[type.value].key || '';
|
|
93
|
-
refName.value =
|
|
93
|
+
refName.value = props.value.valueFrom[type.value].name;
|
|
94
94
|
referenced.value = props.allConfigMaps.filter((resource) => {
|
|
95
95
|
return resource.metadata.name === refName.value;
|
|
96
96
|
})[0];
|
|
@@ -100,13 +100,13 @@ export default {
|
|
|
100
100
|
break;
|
|
101
101
|
case 'secretRef':
|
|
102
102
|
case 'configMapRef':
|
|
103
|
-
name.value =
|
|
104
|
-
refName.value =
|
|
103
|
+
name.value = props.value.prefix;
|
|
104
|
+
refName.value = props.value[type.value].name;
|
|
105
105
|
break;
|
|
106
106
|
case 'secretKeyRef':
|
|
107
|
-
name.value =
|
|
107
|
+
name.value = props.value.name;
|
|
108
108
|
key.value = props.value.valueFrom[type.value].key || '';
|
|
109
|
-
refName.value =
|
|
109
|
+
refName.value = props.value.valueFrom[type.value].name;
|
|
110
110
|
referenced.value = props.allSecrets.filter((resource) => {
|
|
111
111
|
return resource.metadata.name === refName.value;
|
|
112
112
|
})[0];
|
|
@@ -116,11 +116,11 @@ export default {
|
|
|
116
116
|
break;
|
|
117
117
|
case 'fieldRef':
|
|
118
118
|
fieldPath.value = get(props.value.valueFrom, `${ type.value }.fieldPath`) || '';
|
|
119
|
-
name.value =
|
|
119
|
+
name.value = props.value.name;
|
|
120
120
|
break;
|
|
121
121
|
default:
|
|
122
|
-
name.value =
|
|
123
|
-
valStr.value =
|
|
122
|
+
name.value = props.value.name;
|
|
123
|
+
valStr.value = props.value.value;
|
|
124
124
|
break;
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -58,11 +58,11 @@ describe('the ArrayList', () => {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
jest.useFakeTimers();
|
|
61
|
-
await (wrapper.get('[data-testid="remove-item-1"]').element as HTMLElement).click();
|
|
61
|
+
await (wrapper.get('[data-testid="array-list-remove-item-1"]').element as HTMLElement).click();
|
|
62
62
|
jest.advanceTimersByTime(50);
|
|
63
63
|
jest.useRealTimers();
|
|
64
64
|
|
|
65
|
-
expect(wrapper.find('[data-testid="remove-item-2"]').exists()).toBe(false);
|
|
65
|
+
expect(wrapper.find('[data-testid="array-list-remove-item-2"]').exists()).toBe(false);
|
|
66
66
|
expect((wrapper.emitted('remove')![0][0] as any).row.value).toStrictEqual('string 1');
|
|
67
67
|
expect(wrapper.vm.rows).toStrictEqual([{ value: 'string 0' }, { value: 'string 2' }]);
|
|
68
68
|
expect(wrapper.emitted('update:value')![0][0]).toStrictEqual(['string 0', 'string 2']);
|
|
@@ -24,4 +24,39 @@ describe('colorInput.vue', () => {
|
|
|
24
24
|
expect(colorWrapper.classes()).not.toContain('disabled');
|
|
25
25
|
expect(Object.keys(colorInput.attributes())).not.toContain('disabled');
|
|
26
26
|
});
|
|
27
|
+
|
|
28
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', () => {
|
|
29
|
+
const label = 'some-label';
|
|
30
|
+
const describeById = 'some-id';
|
|
31
|
+
|
|
32
|
+
const wrapper = shallowMount(ColorInput, {
|
|
33
|
+
props: { label },
|
|
34
|
+
attrs: { 'aria-describedby': describeById }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const colorInput = wrapper.find('input');
|
|
38
|
+
const ariaDisabled = colorInput.attributes('aria-disabled');
|
|
39
|
+
const ariaLabel = colorInput.attributes('aria-label');
|
|
40
|
+
const ariaDescribedBy = colorInput.attributes('aria-describedby');
|
|
41
|
+
|
|
42
|
+
expect(ariaDisabled).toBe('false');
|
|
43
|
+
expect(ariaLabel).toBe(label);
|
|
44
|
+
expect(ariaDescribedBy).toBe(describeById);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('a11y: adding aria-label ($attrs) from parent should override label-based aria-label', () => {
|
|
48
|
+
const inputLabel = 'some-label';
|
|
49
|
+
const overrideLabel = 'some-override-label';
|
|
50
|
+
|
|
51
|
+
const wrapper = shallowMount(ColorInput, {
|
|
52
|
+
props: { label: inputLabel },
|
|
53
|
+
attrs: { 'aria-label': overrideLabel }
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const colorInput = wrapper.find('input');
|
|
57
|
+
const ariaLabel = colorInput.attributes('aria-label');
|
|
58
|
+
|
|
59
|
+
expect(ariaLabel).toBe(overrideLabel);
|
|
60
|
+
expect(ariaLabel).not.toBe(inputLabel);
|
|
61
|
+
});
|
|
27
62
|
});
|
|
@@ -214,4 +214,44 @@ describe('component: LabeledSelect', () => {
|
|
|
214
214
|
expect(wrapper.vm.$data.myValue).toStrictEqual(expectation);
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
|
+
|
|
218
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', async() => {
|
|
219
|
+
const label = 'Foo';
|
|
220
|
+
const value = 'foo';
|
|
221
|
+
const ariaDescribedById = 'some-described-by-id';
|
|
222
|
+
const itemLabel = 'some-label';
|
|
223
|
+
|
|
224
|
+
const wrapper = mount(LabeledSelect, {
|
|
225
|
+
props: {
|
|
226
|
+
value,
|
|
227
|
+
label: itemLabel,
|
|
228
|
+
options: [
|
|
229
|
+
{ label, value },
|
|
230
|
+
],
|
|
231
|
+
required: true
|
|
232
|
+
},
|
|
233
|
+
attrs: { 'aria-describedby': ariaDescribedById }
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const labeledSelectContainer = wrapper.find('.labeled-select');
|
|
237
|
+
const ariaExpanded = labeledSelectContainer.attributes('aria-expanded');
|
|
238
|
+
const ariaDescribedBy = labeledSelectContainer.attributes('aria-describedby');
|
|
239
|
+
const ariaRequired = labeledSelectContainer.attributes('aria-required');
|
|
240
|
+
const containerId = labeledSelectContainer.attributes('id');
|
|
241
|
+
const labelFor = wrapper.find('label').attributes('for');
|
|
242
|
+
|
|
243
|
+
const vSelectInput = wrapper.find('.v-select');
|
|
244
|
+
|
|
245
|
+
expect(ariaExpanded).toBe('false');
|
|
246
|
+
expect(ariaDescribedBy).toBe(ariaDescribedById);
|
|
247
|
+
expect(ariaRequired).toBe('true');
|
|
248
|
+
expect(containerId).toBe(wrapper.vm.labeledSelectLabelId);
|
|
249
|
+
expect(labelFor).toBe(wrapper.vm.labeledSelectLabelId);
|
|
250
|
+
|
|
251
|
+
// make sure it's hardcoded to a "neutral" value so that
|
|
252
|
+
// in the current architecture of the component
|
|
253
|
+
// screen readers won't pick up the default "Select option" aria-label
|
|
254
|
+
// from the library
|
|
255
|
+
expect(vSelectInput.attributes('aria-label')).toBe('-');
|
|
256
|
+
});
|
|
217
257
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
3
3
|
import SSHKnownHosts from '@shell/components/form/SSHKnownHosts/index.vue';
|
|
4
|
+
import { createStore } from 'vuex';
|
|
4
5
|
|
|
5
6
|
jest.mock('focus-trap', () => {
|
|
6
7
|
return {
|
|
@@ -53,18 +54,26 @@ describe('component: SSHKnownHosts', () => {
|
|
|
53
54
|
});
|
|
54
55
|
|
|
55
56
|
it('mode edit: should open edit dialog', async() => {
|
|
57
|
+
const actions = { 'management/promptModal': jest.fn() };
|
|
58
|
+
|
|
56
59
|
const wrapper = mount(SSHKnownHosts, {
|
|
57
60
|
props: {
|
|
58
61
|
mode: _EDIT,
|
|
59
62
|
value: '',
|
|
63
|
+
},
|
|
64
|
+
global: {
|
|
65
|
+
mocks: {
|
|
66
|
+
$store: createStore({ actions }),
|
|
67
|
+
$fetchState: {}
|
|
68
|
+
},
|
|
69
|
+
stubs: { transition: false }
|
|
60
70
|
}
|
|
61
71
|
});
|
|
62
72
|
|
|
63
73
|
const knownSshHostsOpenDialog = wrapper.find('[data-testid="input-known-ssh-hosts_open-dialog"]');
|
|
64
|
-
const editDialog = wrapper.vm.$refs['editDialog'] as any;
|
|
65
74
|
|
|
66
75
|
await knownSshHostsOpenDialog.trigger('click');
|
|
67
76
|
|
|
68
|
-
expect(
|
|
77
|
+
expect(actions['management/promptModal']).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ component: 'KnownHostsEditDialog' }));
|
|
69
78
|
});
|
|
70
79
|
});
|
package/components/nav/Group.vue
CHANGED
|
@@ -99,6 +99,11 @@ export default {
|
|
|
99
99
|
},
|
|
100
100
|
|
|
101
101
|
groupSelected() {
|
|
102
|
+
// Can not click on groups that are fixed open
|
|
103
|
+
if (this.fixedOpen) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
102
107
|
// Don't auto-select first group entry if we're already expanded and contain the currently-selected nav item
|
|
103
108
|
if (this.hasActiveRoute() && this.isExpanded) {
|
|
104
109
|
return;
|
|
@@ -219,14 +224,14 @@ export default {
|
|
|
219
224
|
<template>
|
|
220
225
|
<div
|
|
221
226
|
class="accordion"
|
|
222
|
-
:class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': isGroupActive}"
|
|
227
|
+
:class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': isGroupActive }"
|
|
223
228
|
>
|
|
224
229
|
<div
|
|
225
230
|
v-if="showHeader"
|
|
226
231
|
class="header"
|
|
227
|
-
:class="{'active': isOverview, 'noHover': !canCollapse}"
|
|
232
|
+
:class="{'active': isOverview, 'noHover': !canCollapse || fixedOpen}"
|
|
228
233
|
role="button"
|
|
229
|
-
tabindex="0"
|
|
234
|
+
:tabindex="fixedOpen ? -1 : 0"
|
|
230
235
|
:aria-label="group.labelDisplay || group.label || ''"
|
|
231
236
|
@click="groupSelected()"
|
|
232
237
|
@keyup.enter="groupSelected()"
|
|
@@ -274,7 +279,7 @@ export default {
|
|
|
274
279
|
v-if="child.divider"
|
|
275
280
|
:key="idx"
|
|
276
281
|
>
|
|
277
|
-
<hr>
|
|
282
|
+
<hr role="none">
|
|
278
283
|
</li>
|
|
279
284
|
<!-- <div v-else-if="child[childrenKey] && hideGroup(child[childrenKey])" :key="child.name">
|
|
280
285
|
HIDDEN
|
|
@@ -372,6 +377,9 @@ export default {
|
|
|
372
377
|
&:hover:not(.active) {
|
|
373
378
|
background-color: var(--nav-hover);
|
|
374
379
|
}
|
|
380
|
+
&:hover:not(.active).noHover {
|
|
381
|
+
background-color: inherit;
|
|
382
|
+
}
|
|
375
383
|
}
|
|
376
384
|
}
|
|
377
385
|
|
|
@@ -5,7 +5,6 @@ import { MANAGEMENT, NORMAN, STEVE } from '@shell/config/types';
|
|
|
5
5
|
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
6
6
|
import { ucFirst } from '@shell/utils/string';
|
|
7
7
|
import { isAlternate, isMac } from '@shell/utils/platform';
|
|
8
|
-
import Import from '@shell/components/Import';
|
|
9
8
|
import BrandImage from '@shell/components/BrandImage';
|
|
10
9
|
import { getProduct, getVendor } from '@shell/config/private-label';
|
|
11
10
|
import ClusterProviderIcon from '@shell/components/ClusterProviderIcon';
|
|
@@ -16,7 +15,6 @@ import NamespaceFilter from './NamespaceFilter';
|
|
|
16
15
|
import WorkspaceSwitcher from './WorkspaceSwitcher';
|
|
17
16
|
import TopLevelMenu from './TopLevelMenu';
|
|
18
17
|
|
|
19
|
-
import Jump from './Jump';
|
|
20
18
|
import { allHash } from '@shell/utils/promise';
|
|
21
19
|
import { ActionLocation, ExtensionPoint } from '@shell/core/types';
|
|
22
20
|
import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
|
|
@@ -36,9 +34,7 @@ export default {
|
|
|
36
34
|
components: {
|
|
37
35
|
NamespaceFilter,
|
|
38
36
|
WorkspaceSwitcher,
|
|
39
|
-
Import,
|
|
40
37
|
TopLevelMenu,
|
|
41
|
-
Jump,
|
|
42
38
|
BrandImage,
|
|
43
39
|
ClusterBadge,
|
|
44
40
|
ClusterProviderIcon,
|
|
@@ -79,9 +75,7 @@ export default {
|
|
|
79
75
|
LOGGED_OUT,
|
|
80
76
|
navHeaderRight: null,
|
|
81
77
|
extensionHeaderActions: getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, this.$route),
|
|
82
|
-
ctx: this
|
|
83
|
-
showImportModal: false,
|
|
84
|
-
showSearchModal: false
|
|
78
|
+
ctx: this
|
|
85
79
|
};
|
|
86
80
|
},
|
|
87
81
|
|
|
@@ -316,19 +310,24 @@ export default {
|
|
|
316
310
|
},
|
|
317
311
|
|
|
318
312
|
openImport() {
|
|
319
|
-
this.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
313
|
+
this.$store.dispatch('cluster/promptModal', {
|
|
314
|
+
component: 'ImportDialog',
|
|
315
|
+
modalWidth: '75%',
|
|
316
|
+
height: 'auto',
|
|
317
|
+
styles: 'max-height: 90vh;',
|
|
318
|
+
componentProps: { cluster: this.currentCluster }
|
|
319
|
+
});
|
|
324
320
|
},
|
|
325
321
|
|
|
326
322
|
openSearch() {
|
|
327
|
-
this.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
323
|
+
this.$store.dispatch('cluster/promptModal', {
|
|
324
|
+
component: 'SearchDialog',
|
|
325
|
+
testId: 'search-modal',
|
|
326
|
+
modalWidth: '50%',
|
|
327
|
+
height: 'auto',
|
|
328
|
+
styles: 'max-height: 90vh;',
|
|
329
|
+
returnFocusSelector: '#header-btn-search'
|
|
330
|
+
});
|
|
332
331
|
},
|
|
333
332
|
|
|
334
333
|
checkClusterName() {
|
|
@@ -555,20 +554,6 @@ export default {
|
|
|
555
554
|
>
|
|
556
555
|
<i class="icon icon-upload icon-lg" />
|
|
557
556
|
</button>
|
|
558
|
-
<app-modal
|
|
559
|
-
v-if="showImportModal"
|
|
560
|
-
class="import-modal"
|
|
561
|
-
name="importModal"
|
|
562
|
-
width="75%"
|
|
563
|
-
height="auto"
|
|
564
|
-
styles="max-height: 90vh;"
|
|
565
|
-
@close="closeImport"
|
|
566
|
-
>
|
|
567
|
-
<Import
|
|
568
|
-
:cluster="currentCluster"
|
|
569
|
-
@close="closeImport"
|
|
570
|
-
/>
|
|
571
|
-
</app-modal>
|
|
572
557
|
|
|
573
558
|
<button
|
|
574
559
|
v-if="showKubeShell"
|
|
@@ -641,18 +626,6 @@ export default {
|
|
|
641
626
|
>
|
|
642
627
|
<i class="icon icon-search icon-lg" />
|
|
643
628
|
</button>
|
|
644
|
-
<app-modal
|
|
645
|
-
v-if="showSearch && showSearchModal"
|
|
646
|
-
class="search-modal"
|
|
647
|
-
name="searchModal"
|
|
648
|
-
width="50%"
|
|
649
|
-
height="auto"
|
|
650
|
-
:trigger-focus-trap="true"
|
|
651
|
-
return-focus-selector="#header-btn-search"
|
|
652
|
-
@close="hideSearch()"
|
|
653
|
-
>
|
|
654
|
-
<Jump @closeSearch="hideSearch()" />
|
|
655
|
-
</app-modal>
|
|
656
629
|
</div>
|
|
657
630
|
|
|
658
631
|
<!-- Extension header actions -->
|