@rancher/shell 3.0.4 → 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/_basic.scss +6 -0
- package/assets/styles/base/_helpers.scss +4 -0
- package/assets/styles/base/_variables.scss +1 -0
- package/assets/styles/global/_button.scss +1 -0
- package/assets/translations/en-us.yaml +65 -15
- package/assets/translations/zh-hans.yaml +4 -3
- package/chart/monitoring/index.vue +3 -1
- package/cloud-credential/aws.vue +2 -0
- package/components/ActionDropdownShell.vue +71 -0
- package/components/AppModal.vue +18 -4
- package/components/AsyncButton.vue +24 -7
- package/components/BannerGraphic.vue +1 -0
- package/components/CommunityLinks.vue +4 -59
- package/components/CopyToClipboardText.vue +2 -1
- package/components/CruResource.vue +6 -1
- package/components/DetailText.vue +5 -0
- package/components/ExplorerMembers.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +68 -18
- package/components/GlobalRoleBindings.vue +5 -1
- package/components/GrowlManager.vue +1 -0
- package/components/LandingPagePreference.vue +7 -3
- package/components/LocaleSelector.vue +39 -95
- package/components/ModalManager.vue +55 -0
- package/components/ModalWithCard.vue +1 -0
- package/components/PromptModal.vue +47 -8
- package/components/PromptRemove.vue +1 -0
- package/components/PromptRestore.vue +1 -0
- package/components/ResourceCancelModal.vue +1 -0
- 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 +64 -51
- package/components/SortableTable/paging.js +16 -19
- package/components/SortableTable/selection.js +0 -11
- package/components/Wizard.vue +2 -2
- package/components/__tests__/AsyncButton.test.ts +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/__tests__/RoleDetailEdit.test.ts +3 -2
- package/components/auth/login/ldap.vue +1 -1
- package/components/fleet/FleetResources.vue +21 -6
- package/components/form/ArrayList.vue +76 -60
- package/components/form/BannerSettings.vue +17 -2
- package/components/form/ColorInput.vue +35 -6
- package/components/form/Command.vue +6 -15
- package/components/form/EnvVars.vue +16 -8
- package/components/form/HealthCheck.vue +3 -3
- package/components/form/HookOption.vue +11 -16
- package/components/form/LabeledSelect.vue +18 -22
- package/components/form/LifecycleHooks.vue +3 -3
- package/components/form/MatchExpressions.vue +14 -8
- package/components/form/NameNsDescription.vue +128 -104
- package/components/form/Networking.vue +20 -12
- package/components/form/NodeAffinity.vue +31 -23
- package/components/form/NodeScheduling.vue +13 -3
- package/components/form/NotificationSettings.vue +15 -1
- package/components/form/Password.vue +1 -0
- package/components/form/PodAffinity.vue +43 -43
- package/components/form/Probe.vue +68 -66
- package/components/form/ResourceQuota/Project.vue +5 -1
- package/components/form/ResourceSelector.vue +7 -9
- package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +16 -24
- package/components/form/SSHKnownHosts/index.vue +30 -13
- package/components/form/Security.vue +54 -56
- package/components/form/Select.vue +32 -21
- package/components/form/ShellInput.vue +5 -1
- package/components/form/Tolerations.vue +5 -1
- package/components/form/ValueFromResource.vue +134 -121
- package/components/form/WorkloadPorts.vue +18 -18
- package/components/form/__tests__/ArrayList.test.ts +5 -2
- package/components/form/__tests__/ColorInput.test.ts +35 -0
- package/components/form/__tests__/LabeledSelect.test.ts +40 -0
- package/components/form/__tests__/MatchExpressions.test.ts +12 -12
- package/components/form/__tests__/NameNsDescription.test.ts +115 -14
- package/components/form/__tests__/Probe.test.ts +12 -8
- package/components/form/__tests__/SSHKnownHosts.test.ts +22 -2
- package/components/form/__tests__/Select.test.ts +37 -0
- package/components/formatter/InternalExternalIP.vue +2 -0
- package/components/formatter/SecretData.vue +20 -7
- package/components/nav/Group.vue +27 -5
- package/components/nav/Header.vue +17 -43
- package/components/nav/NamespaceFilter.vue +134 -86
- package/components/nav/TopLevelMenu.vue +4 -5
- package/components/nav/Type.vue +12 -1
- package/components/nav/WindowManager/ContainerLogs.vue +87 -61
- package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
- package/components/templates/blank.vue +4 -1
- package/components/templates/default.vue +8 -3
- package/components/templates/home.vue +10 -1
- package/components/templates/plain.vue +10 -4
- package/composables/focusTrap.ts +12 -4
- package/composables/useRuntimeFlag.ts +29 -0
- package/config/router/routes.js +20 -13
- package/config/store.js +4 -0
- package/config/uiplugins.js +5 -1
- package/core/types.ts +12 -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 +1 -1
- package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
- package/{components/AssignTo.vue → dialog/AssignToDialog.vue} +71 -80
- package/dialog/ChangePasswordDialog.vue +106 -0
- package/dialog/DeactivateDriverDialog.vue +1 -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 +5 -2
- 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 -59
- 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/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
- package/edit/auth/__tests__/oidc.test.ts +152 -109
- package/edit/auth/azuread.vue +2 -1
- package/edit/auth/github.vue +1 -1
- package/edit/auth/googleoauth.vue +5 -1
- package/edit/auth/ldap/index.vue +1 -1
- package/edit/auth/oidc.vue +38 -5
- package/edit/auth/saml.vue +1 -1
- package/edit/cloudcredential.vue +24 -9
- package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
- package/edit/management.cattle.io.user.vue +28 -3
- package/edit/namespace.vue +1 -4
- package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
- package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
- package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -9
- 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 +49 -41
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +33 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
- package/edit/token.vue +2 -0
- package/edit/workload/index.vue +1 -0
- package/edit/workload/mixins/workload.js +0 -2
- package/initialize/install-plugins.js +2 -1
- package/list/harvesterhci.io.management.cluster.vue +4 -1
- package/list/management.cattle.io.feature.vue +4 -287
- package/list/provisioning.cattle.io.cluster.vue +20 -12
- package/machine-config/azure.vue +16 -4
- package/mixins/vue-select-overrides.js +0 -4
- package/models/__tests__/namespace.test.ts +25 -1
- package/models/cloudcredential.js +5 -0
- package/models/fleet.cattle.io.cluster.js +8 -2
- package/models/fleet.cattle.io.gitrepo.js +8 -34
- package/models/kontainerdriver.js +6 -3
- package/models/management.cattle.io.feature.js +7 -1
- package/models/management.cattle.io.node.js +3 -3
- package/models/namespace.js +11 -6
- package/models/nodedriver.js +6 -3
- package/models/workload.js +4 -1
- package/package.json +3 -3
- package/pages/about.vue +13 -3
- package/pages/account/index.vue +16 -6
- package/pages/auth/login.vue +18 -7
- package/pages/auth/logout.vue +4 -1
- package/pages/auth/setup.vue +2 -0
- package/pages/auth/verify.vue +13 -8
- package/pages/c/_cluster/apps/charts/chart.vue +1 -1
- 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 +59 -11
- package/pages/fail-whale.vue +14 -8
- package/pages/home.vue +24 -18
- package/pages/prefs.vue +7 -6
- package/pages/support/index.vue +4 -1
- 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 +52 -10
- 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 +18 -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/RcDropdownTrigger.vue +10 -0
- package/rancher-components/RcDropdown/index.ts +2 -0
- package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
- 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 -12
- package/scripts/typegen.sh +2 -4
- package/server/har-file.js +25 -3
- package/store/action-menu.js +26 -56
- package/store/features.js +2 -1
- package/store/index.js +5 -0
- package/store/modal.ts +71 -0
- package/store/slideInPanel.ts +47 -0
- package/store/type-map.js +12 -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 +43 -24
- 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/cluster.js +35 -0
- 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/utils/validators/machine-pool.ts +20 -0
- package/components/DisableAuthProviderModal.vue +0 -114
- package/components/MoveModal.vue +0 -166
- package/components/PromptChangePassword.vue +0 -123
- package/components/fleet/FleetBundleResources.vue +0 -86
- package/components/formatter/ExtensionCache.vue +0 -74
- package/components/formatter/Port.vue +0 -24
- package/components/formatter/SecretType.vue +0 -41
- package/types/vue-shim.d +0 -20
|
@@ -13,12 +13,15 @@ export default {
|
|
|
13
13
|
components: { AppModal },
|
|
14
14
|
|
|
15
15
|
data() {
|
|
16
|
-
return {
|
|
16
|
+
return {
|
|
17
|
+
opened: false,
|
|
18
|
+
backgroundClosing: null,
|
|
19
|
+
componentRendered: false
|
|
20
|
+
};
|
|
17
21
|
},
|
|
18
22
|
|
|
19
23
|
computed: {
|
|
20
24
|
...mapState('action-menu', ['showModal', 'modalData']),
|
|
21
|
-
|
|
22
25
|
resources() {
|
|
23
26
|
let resources = this.modalData?.resources;
|
|
24
27
|
|
|
@@ -28,11 +31,28 @@ export default {
|
|
|
28
31
|
|
|
29
32
|
return resources || [];
|
|
30
33
|
},
|
|
31
|
-
|
|
34
|
+
testId() {
|
|
35
|
+
return this.modalData?.testId || 'prompt-modal-generic-testid';
|
|
36
|
+
},
|
|
37
|
+
returnFocusSelector() {
|
|
38
|
+
return this.modalData?.returnFocusSelector || undefined;
|
|
39
|
+
},
|
|
40
|
+
returnFocusFirstIterableNodeSelector() {
|
|
41
|
+
return this.modalData?.returnFocusFirstIterableNodeSelector || undefined;
|
|
42
|
+
},
|
|
32
43
|
modalWidth() {
|
|
33
44
|
// property set from workload.js to overwrite modal default width of 600px, with fallback value as well
|
|
34
45
|
return this.modalData?.modalWidth || '600px';
|
|
35
46
|
},
|
|
47
|
+
customClass() {
|
|
48
|
+
return this.modalData?.customClass || undefined;
|
|
49
|
+
},
|
|
50
|
+
styles() {
|
|
51
|
+
return this.modalData?.styles || undefined;
|
|
52
|
+
},
|
|
53
|
+
height() {
|
|
54
|
+
return this.modalData?.height || undefined;
|
|
55
|
+
},
|
|
36
56
|
component() {
|
|
37
57
|
// Looks for a dialog component by looking up in plugins and @shell/dialog/${name}.
|
|
38
58
|
return this.$store.getters['type-map/importDialog'](this.modalData?.component);
|
|
@@ -48,27 +68,36 @@ export default {
|
|
|
48
68
|
},
|
|
49
69
|
closeOnClickOutside() {
|
|
50
70
|
return this.modalData?.closeOnClickOutside;
|
|
71
|
+
},
|
|
72
|
+
modalName() {
|
|
73
|
+
return this.modalData?.modalName;
|
|
51
74
|
}
|
|
52
75
|
},
|
|
53
76
|
|
|
54
77
|
watch: {
|
|
55
78
|
showModal(show) {
|
|
56
79
|
this.opened = show;
|
|
57
|
-
}
|
|
80
|
+
}
|
|
58
81
|
},
|
|
59
82
|
|
|
60
83
|
methods: {
|
|
61
|
-
|
|
84
|
+
onSlotComponentMounted() {
|
|
85
|
+
// variable for the watcher based focus-trap
|
|
86
|
+
// so that we know when the component is rendered
|
|
87
|
+
this.componentRendered = true;
|
|
88
|
+
},
|
|
89
|
+
close(data) {
|
|
62
90
|
if (!this.opened) {
|
|
63
91
|
return;
|
|
64
92
|
}
|
|
65
93
|
|
|
66
94
|
this.errors = [];
|
|
67
|
-
this.$store.commit('action-menu/togglePromptModal');
|
|
95
|
+
this.$store.commit('action-menu/togglePromptModal', data);
|
|
68
96
|
if (this.backgroundClosing) {
|
|
69
97
|
this.backgroundClosing();
|
|
70
98
|
}
|
|
71
99
|
|
|
100
|
+
this.componentRendered = false;
|
|
72
101
|
this.opened = false;
|
|
73
102
|
},
|
|
74
103
|
|
|
@@ -83,16 +112,26 @@ export default {
|
|
|
83
112
|
<template>
|
|
84
113
|
<app-modal
|
|
85
114
|
v-if="opened && component"
|
|
115
|
+
:name="modalName"
|
|
86
116
|
:click-to-close="closeOnClickOutside"
|
|
87
117
|
:width="modalWidth"
|
|
88
|
-
|
|
118
|
+
:data-testid="testId"
|
|
119
|
+
:custom-class="customClass"
|
|
120
|
+
:styles="styles"
|
|
121
|
+
:height="height"
|
|
122
|
+
:trigger-focus-trap="true"
|
|
123
|
+
:return-focus-selector="returnFocusSelector"
|
|
124
|
+
:return-focus-first-iterable-node-selector="returnFocusFirstIterableNodeSelector"
|
|
125
|
+
:focus-trap-watcher-based-variable="componentRendered"
|
|
126
|
+
@close="close"
|
|
89
127
|
>
|
|
90
128
|
<component
|
|
91
129
|
v-bind="modalData.componentProps || {}"
|
|
92
130
|
:is="component"
|
|
93
131
|
:resources="resources"
|
|
94
132
|
:register-background-closing="registerBackgroundClosing"
|
|
95
|
-
@
|
|
133
|
+
@vue:mounted="onSlotComponentMounted"
|
|
134
|
+
@close="close"
|
|
96
135
|
/>
|
|
97
136
|
</app-modal>
|
|
98
137
|
</template>
|
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
import { ExtensionPoint, PanelLocation } from '@shell/core/types';
|
|
14
14
|
import ExtensionPanel from '@shell/components/ExtensionPanel';
|
|
15
15
|
import TabTitle from '@shell/components/TabTitle';
|
|
16
|
+
import ActionMenu from '@shell/components/ActionMenuShell.vue';
|
|
17
|
+
import { useRuntimeFlag } from '@shell/composables/useRuntimeFlag';
|
|
18
|
+
import { useStore } from 'vuex';
|
|
16
19
|
|
|
17
20
|
// i18n-uses resourceDetail.header.*
|
|
18
21
|
|
|
@@ -26,7 +29,12 @@ export default {
|
|
|
26
29
|
name: 'MastheadResourceDetail',
|
|
27
30
|
|
|
28
31
|
components: {
|
|
29
|
-
BadgeState,
|
|
32
|
+
BadgeState,
|
|
33
|
+
Banner,
|
|
34
|
+
ButtonGroup,
|
|
35
|
+
ExtensionPanel,
|
|
36
|
+
TabTitle,
|
|
37
|
+
ActionMenu,
|
|
30
38
|
},
|
|
31
39
|
props: {
|
|
32
40
|
value: {
|
|
@@ -92,6 +100,13 @@ export default {
|
|
|
92
100
|
}
|
|
93
101
|
},
|
|
94
102
|
|
|
103
|
+
setup() {
|
|
104
|
+
const store = useStore();
|
|
105
|
+
const { featureDropdownMenu } = useRuntimeFlag(store);
|
|
106
|
+
|
|
107
|
+
return { featureDropdownMenu };
|
|
108
|
+
},
|
|
109
|
+
|
|
95
110
|
data() {
|
|
96
111
|
return {
|
|
97
112
|
DETAIL_VIEW: _DETAIL,
|
|
@@ -561,17 +576,28 @@ export default {
|
|
|
561
576
|
class="mr-10"
|
|
562
577
|
/>
|
|
563
578
|
|
|
564
|
-
<
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
>
|
|
573
|
-
|
|
574
|
-
|
|
579
|
+
<template v-if="featureDropdownMenu">
|
|
580
|
+
<ActionMenu
|
|
581
|
+
v-if="isView"
|
|
582
|
+
button-role="multiAction"
|
|
583
|
+
button-size="compact"
|
|
584
|
+
:resource="value"
|
|
585
|
+
data-testid="masthead-action-menu"
|
|
586
|
+
/>
|
|
587
|
+
</template>
|
|
588
|
+
<template v-else>
|
|
589
|
+
<button
|
|
590
|
+
v-if="isView"
|
|
591
|
+
ref="actions"
|
|
592
|
+
data-testid="masthead-action-menu"
|
|
593
|
+
aria-haspopup="true"
|
|
594
|
+
type="button"
|
|
595
|
+
class="btn role-multi-action actions"
|
|
596
|
+
@click="showActions"
|
|
597
|
+
>
|
|
598
|
+
<i class="icon icon-actions" />
|
|
599
|
+
</button>
|
|
600
|
+
</template>
|
|
575
601
|
</div>
|
|
576
602
|
</div>
|
|
577
603
|
</slot>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mount, RouterLinkStub } from '@vue/test-utils';
|
|
2
2
|
import { _VIEW } from '@shell/config/query-params';
|
|
3
3
|
import Masthead from '@shell/components/ResourceDetail/Masthead.vue';
|
|
4
|
+
import { createStore } from 'vuex';
|
|
4
5
|
|
|
5
6
|
const mockedStore = () => {
|
|
6
7
|
return {
|
|
@@ -17,12 +18,15 @@ const mockedStore = () => {
|
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
const requiredSetup = () => {
|
|
21
|
+
const store = createStore({ getters: { 'management/byId': () => jest.fn() } });
|
|
22
|
+
|
|
20
23
|
return {
|
|
21
24
|
stubs: {
|
|
22
25
|
'router-link': RouterLinkStub,
|
|
23
26
|
LiveDate: true
|
|
24
27
|
},
|
|
25
|
-
|
|
28
|
+
provide: { store },
|
|
29
|
+
mocks: { $store: mockedStore() }
|
|
26
30
|
};
|
|
27
31
|
};
|
|
28
32
|
|
|
@@ -384,18 +384,7 @@ export default {
|
|
|
384
384
|
},
|
|
385
385
|
|
|
386
386
|
created() {
|
|
387
|
-
|
|
388
|
-
const id = this.$route.params.id;
|
|
389
|
-
const resource = this.resourceOverride || this.$route.params.resource;
|
|
390
|
-
const options = this.$store.getters[`type-map/optionsFor`](resource);
|
|
391
|
-
|
|
392
|
-
const detailResource = options.resourceDetail || options.resource || resource;
|
|
393
|
-
const editResource = options.resourceEdit || options.resource || resource;
|
|
394
|
-
|
|
395
|
-
// FIXME: These aren't right... signature is (rawType, subType).. not (rawType, resourceId)
|
|
396
|
-
// Remove id? How does subtype get in (cluster/node)
|
|
397
|
-
this.detailComponent = this.$store.getters['type-map/importDetail'](detailResource, id);
|
|
398
|
-
this.editComponent = this.$store.getters['type-map/importEdit'](editResource, id);
|
|
387
|
+
this.configureResource();
|
|
399
388
|
},
|
|
400
389
|
|
|
401
390
|
methods: {
|
|
@@ -414,6 +403,51 @@ export default {
|
|
|
414
403
|
closeError(index) {
|
|
415
404
|
this.errors = this.errors.filter((_, i) => i !== index);
|
|
416
405
|
},
|
|
406
|
+
/**
|
|
407
|
+
* Initializes the resource components based on the provided user and
|
|
408
|
+
* resource override.
|
|
409
|
+
*
|
|
410
|
+
* Configures the detail and edit components for a resource based on the
|
|
411
|
+
* user's ID and the specified resource.
|
|
412
|
+
*
|
|
413
|
+
* @param {Object} user - The user object containing user-specific
|
|
414
|
+
* information.
|
|
415
|
+
* @param {string|null} resourceOverride - An optional resource override
|
|
416
|
+
* string. If not provided, the method will use the default resource from
|
|
417
|
+
* the route parameters or the instance's resourceOverride property.
|
|
418
|
+
*/
|
|
419
|
+
configureResource(userId = '', resourceOverride = null) {
|
|
420
|
+
const id = userId || this.$route.params.id;
|
|
421
|
+
const resource = resourceOverride || this.resourceOverride || this.$route.params.resource;
|
|
422
|
+
const options = this.$store.getters[`type-map/optionsFor`](resource);
|
|
423
|
+
|
|
424
|
+
const detailResource = options.resourceDetail || options.resource || resource;
|
|
425
|
+
const editResource = options.resourceEdit || options.resource || resource;
|
|
426
|
+
|
|
427
|
+
// FIXME: These aren't right... signature is (rawType, subType).. not (rawType, resourceId)
|
|
428
|
+
// Remove id? How does subtype get in (cluster/node)
|
|
429
|
+
this.detailComponent = this.$store.getters['type-map/importDetail'](detailResource, id);
|
|
430
|
+
this.editComponent = this.$store.getters['type-map/importEdit'](editResource, id);
|
|
431
|
+
},
|
|
432
|
+
/**
|
|
433
|
+
* Sets the mode and initializes the resource components.
|
|
434
|
+
*
|
|
435
|
+
* This method sets the mode of the component and configures the resource
|
|
436
|
+
* components based on the provided user and resource.
|
|
437
|
+
*
|
|
438
|
+
* @param {Object} payload - An object containing the mode, user, and
|
|
439
|
+
* resource properties.
|
|
440
|
+
* @param {string} payload.mode - The mode to set.
|
|
441
|
+
* @param {Object} payload.user - The user object containing user-specific
|
|
442
|
+
* information.
|
|
443
|
+
* @param {string} payload.resource - The resource string to use for
|
|
444
|
+
* initialization.
|
|
445
|
+
*/
|
|
446
|
+
setMode({ mode, userId, resource }) {
|
|
447
|
+
this.mode = mode;
|
|
448
|
+
this.value.id = userId;
|
|
449
|
+
this.configureResource(userId, resource);
|
|
450
|
+
}
|
|
417
451
|
}
|
|
418
452
|
};
|
|
419
453
|
</script>
|
|
@@ -491,6 +525,7 @@ export default {
|
|
|
491
525
|
:real-mode="realMode"
|
|
492
526
|
:class="{'flex-content': flexContent}"
|
|
493
527
|
@update:value="$emit('input', $event)"
|
|
528
|
+
@update:mode="setMode"
|
|
494
529
|
@set-subtype="setSubtype"
|
|
495
530
|
/>
|
|
496
531
|
|
|
@@ -77,11 +77,6 @@ export default {
|
|
|
77
77
|
default: null,
|
|
78
78
|
},
|
|
79
79
|
|
|
80
|
-
groupBy: {
|
|
81
|
-
type: String,
|
|
82
|
-
default: null
|
|
83
|
-
},
|
|
84
|
-
|
|
85
80
|
namespaced: {
|
|
86
81
|
type: Boolean,
|
|
87
82
|
default: null, // Automatic from schema
|
|
@@ -117,11 +112,35 @@ export default {
|
|
|
117
112
|
default: true,
|
|
118
113
|
},
|
|
119
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Field to group rows by, row[groupBy] must be something that can be a map key
|
|
117
|
+
*/
|
|
118
|
+
groupBy: {
|
|
119
|
+
type: String,
|
|
120
|
+
default: null
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Override any product based group options
|
|
125
|
+
*/
|
|
126
|
+
groupOptions: {
|
|
127
|
+
type: Array,
|
|
128
|
+
default: null
|
|
129
|
+
},
|
|
130
|
+
|
|
120
131
|
groupable: {
|
|
121
132
|
type: Boolean,
|
|
122
133
|
default: null, // Null: auto based on namespaced and type custom groupings
|
|
123
134
|
},
|
|
124
135
|
|
|
136
|
+
/**
|
|
137
|
+
* If the current preference for group isn't applicable, or not set, use this instead
|
|
138
|
+
*/
|
|
139
|
+
groupDefault: {
|
|
140
|
+
type: String,
|
|
141
|
+
default: DEFAULT_GROUP,
|
|
142
|
+
},
|
|
143
|
+
|
|
125
144
|
groupTooltip: {
|
|
126
145
|
type: String,
|
|
127
146
|
default: 'resourceTable.groupBy.namespace',
|
|
@@ -189,10 +208,6 @@ export default {
|
|
|
189
208
|
default: null, // Default comes from the user preference
|
|
190
209
|
},
|
|
191
210
|
|
|
192
|
-
hideGroupingControls: {
|
|
193
|
-
type: Boolean,
|
|
194
|
-
default: false
|
|
195
|
-
}
|
|
196
211
|
},
|
|
197
212
|
|
|
198
213
|
data() {
|
|
@@ -328,8 +343,18 @@ export default {
|
|
|
328
343
|
// If we are grouping by a custom group, it may specify that we hide a specific column
|
|
329
344
|
const custom = this._listGroupMapped?.[this.group];
|
|
330
345
|
|
|
346
|
+
let hideColumn;
|
|
347
|
+
|
|
331
348
|
if (custom?.hideColumn) {
|
|
332
|
-
|
|
349
|
+
hideColumn = custom.hideColumn;
|
|
350
|
+
} else {
|
|
351
|
+
const componentCustom = this.groupOptions?.find((go) => go.value === this.group);
|
|
352
|
+
|
|
353
|
+
hideColumn = componentCustom?.hideColumn;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (hideColumn) {
|
|
357
|
+
const idx = headers.findIndex((header) => header.name === hideColumn);
|
|
333
358
|
|
|
334
359
|
if ( idx >= 0 ) {
|
|
335
360
|
headers.splice(idx, 1);
|
|
@@ -388,17 +413,17 @@ export default {
|
|
|
388
413
|
group: {
|
|
389
414
|
get() {
|
|
390
415
|
// Check group is valid
|
|
391
|
-
const exists = this.
|
|
416
|
+
const exists = this._groupOptions.find((g) => g.value === this._group);
|
|
392
417
|
|
|
393
418
|
if (!exists) {
|
|
394
419
|
// Attempt to find the default option in available options...
|
|
395
420
|
// if not use the first value in the options collection...
|
|
396
421
|
// and if not that just fall back to the default
|
|
397
|
-
if (this.
|
|
398
|
-
return
|
|
422
|
+
if (this._groupOptions.find((g) => g.value === this.groupDefault)) {
|
|
423
|
+
return this.groupDefault;
|
|
399
424
|
}
|
|
400
425
|
|
|
401
|
-
return this.
|
|
426
|
+
return this._groupOptions[0]?.value || this.groupDefault || DEFAULT_GROUP;
|
|
402
427
|
}
|
|
403
428
|
|
|
404
429
|
return this._group;
|
|
@@ -413,7 +438,7 @@ export default {
|
|
|
413
438
|
const namespaceGroupable = this.$store.getters['isMultipleNamespaces'] && this.isNamespaced;
|
|
414
439
|
const customGroupable = !!this.options?.listGroups?.length;
|
|
415
440
|
|
|
416
|
-
return namespaceGroupable || customGroupable;
|
|
441
|
+
return namespaceGroupable || customGroupable || this.groupOptions?.length;
|
|
417
442
|
}
|
|
418
443
|
|
|
419
444
|
return this.groupable || false;
|
|
@@ -442,10 +467,20 @@ export default {
|
|
|
442
467
|
return custom.field;
|
|
443
468
|
}
|
|
444
469
|
|
|
470
|
+
const componentCustom = this.groupOptions?.find((go) => go.value === this.group);
|
|
471
|
+
|
|
472
|
+
if (componentCustom?.field) {
|
|
473
|
+
return componentCustom.field;
|
|
474
|
+
}
|
|
475
|
+
|
|
445
476
|
return null;
|
|
446
477
|
},
|
|
447
478
|
|
|
448
|
-
|
|
479
|
+
_groupOptions() {
|
|
480
|
+
if (this.groupOptions) {
|
|
481
|
+
return this.groupOptions;
|
|
482
|
+
}
|
|
483
|
+
|
|
449
484
|
// Ignore the defaults below, we have an override set of groups
|
|
450
485
|
// REPLACE (instead of SUPPLEMENT) defaults with listGroups (given listGroupsWillOverride is true)
|
|
451
486
|
if (this.options?.listGroupsWillOverride && !!this.options?.listGroups?.length) {
|
|
@@ -569,7 +604,7 @@ export default {
|
|
|
569
604
|
:alt-loading="altLoading"
|
|
570
605
|
:group-by="computedGroupBy"
|
|
571
606
|
:group="group"
|
|
572
|
-
:group-options="
|
|
607
|
+
:group-options="_groupOptions"
|
|
573
608
|
:search="search"
|
|
574
609
|
:paging="true"
|
|
575
610
|
:paging-params="parsedPagingParams"
|
|
@@ -596,14 +631,14 @@ export default {
|
|
|
596
631
|
@enter="handleEnterKeyPress"
|
|
597
632
|
>
|
|
598
633
|
<template
|
|
599
|
-
v-if="
|
|
634
|
+
v-if="showGrouping && _groupOptions.length > 1"
|
|
600
635
|
#header-middle
|
|
601
636
|
>
|
|
602
637
|
<slot name="more-header-middle" />
|
|
603
638
|
|
|
604
639
|
<ButtonGroup
|
|
605
640
|
v-model:value="group"
|
|
606
|
-
:options="
|
|
641
|
+
:options="_groupOptions"
|
|
607
642
|
/>
|
|
608
643
|
</template>
|
|
609
644
|
|
package/components/SideNav.vue
CHANGED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import { useStore } from 'vuex';
|
|
4
|
+
|
|
5
|
+
const HEADER_HEIGHT = 55;
|
|
6
|
+
|
|
7
|
+
const store = useStore();
|
|
8
|
+
const isOpen = computed(() => store.getters['slideInPanel/isOpen']);
|
|
9
|
+
const currentComponent = computed(() => store.getters['slideInPanel/component']);
|
|
10
|
+
const currentProps = computed(() => store.getters['slideInPanel/componentProps']);
|
|
11
|
+
|
|
12
|
+
const panelTop = computed(() => {
|
|
13
|
+
const banner = document.getElementById('banner-header');
|
|
14
|
+
let height = HEADER_HEIGHT;
|
|
15
|
+
|
|
16
|
+
if (banner) {
|
|
17
|
+
height += banner.clientHeight;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return `${ height }px`;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const panelHeight = computed(() => `calc(100vh - ${ panelTop?.value })`);
|
|
24
|
+
const panelWidth = computed(() => currentProps?.value?.width || '33%');
|
|
25
|
+
const panelRight = computed(() => (isOpen?.value ? '0' : `-${ panelWidth?.value }`));
|
|
26
|
+
|
|
27
|
+
const panelTitle = computed(() => currentProps?.value?.title || 'Details');
|
|
28
|
+
|
|
29
|
+
function closePanel() {
|
|
30
|
+
store.commit('slideInPanel/close');
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<Teleport to="#slides">
|
|
36
|
+
<div id="slide-in-panel-manager">
|
|
37
|
+
<div
|
|
38
|
+
v-show="isOpen"
|
|
39
|
+
data-testid="slide-in-glass"
|
|
40
|
+
class="slide-in-glass"
|
|
41
|
+
:class="{ 'slide-in-glass-open': isOpen }"
|
|
42
|
+
@click="closePanel"
|
|
43
|
+
/>
|
|
44
|
+
<div
|
|
45
|
+
class="slide-in"
|
|
46
|
+
:class="{ 'slide-in-open': isOpen }"
|
|
47
|
+
:style="{ width: panelWidth, right: panelRight, top: panelTop, height: panelHeight }"
|
|
48
|
+
>
|
|
49
|
+
<div class="header">
|
|
50
|
+
<div class="title">
|
|
51
|
+
{{ panelTitle }}
|
|
52
|
+
</div>
|
|
53
|
+
<i
|
|
54
|
+
class="icon icon-close"
|
|
55
|
+
data-testid="slide-in-close"
|
|
56
|
+
:trigger-focus-trap="true"
|
|
57
|
+
tabindex="0"
|
|
58
|
+
@click="closePanel"
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="main-panel">
|
|
62
|
+
<component
|
|
63
|
+
:is="currentComponent"
|
|
64
|
+
v-if="isOpen || currentComponent"
|
|
65
|
+
v-bind="currentProps"
|
|
66
|
+
data-testid="slide-in-panel-component"
|
|
67
|
+
class="dynamic-panel-content"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</Teleport>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<style lang="scss" scoped>
|
|
76
|
+
.slide-in-glass {
|
|
77
|
+
display: none;
|
|
78
|
+
position: fixed;
|
|
79
|
+
top: 0;
|
|
80
|
+
left: 0;
|
|
81
|
+
height: 100vh;
|
|
82
|
+
width: 100vw;
|
|
83
|
+
}
|
|
84
|
+
.slide-in-glass-open {
|
|
85
|
+
background-color: var(--body-bg);
|
|
86
|
+
display: block;
|
|
87
|
+
opacity: 0.5;
|
|
88
|
+
z-index: 1000;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.slide-in {
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
position: fixed;
|
|
95
|
+
top: 0;
|
|
96
|
+
z-index: 2000;
|
|
97
|
+
transition: right 0.5s ease;
|
|
98
|
+
border-left: 1px solid var(--border);
|
|
99
|
+
background-color: var(--body-bg);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.slide-in-open {
|
|
103
|
+
right: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.header {
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
padding: 4px;
|
|
110
|
+
border-bottom: 1px solid var(--border);
|
|
111
|
+
|
|
112
|
+
.title {
|
|
113
|
+
flex: 1;
|
|
114
|
+
font-weight: bold;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.icon-close {
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.main-panel {
|
|
123
|
+
padding: 10px;
|
|
124
|
+
overflow: auto;
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
@@ -259,10 +259,13 @@ export default {
|
|
|
259
259
|
v-clean-tooltip="tooltip(col)"
|
|
260
260
|
class="content"
|
|
261
261
|
>
|
|
262
|
-
<span
|
|
262
|
+
<span
|
|
263
|
+
v-clean-html="labelFor(col)"
|
|
264
|
+
class="text-no-break"
|
|
265
|
+
/>
|
|
263
266
|
<span
|
|
264
267
|
v-if="col.subLabel"
|
|
265
|
-
class="text-muted"
|
|
268
|
+
class="text-muted text-no-break"
|
|
266
269
|
>
|
|
267
270
|
{{ col.subLabel }}
|
|
268
271
|
</span>
|