@rancher/shell 3.0.8-rc.8 → 3.0.8
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/apis/impl/apis.ts +61 -0
- package/apis/index.ts +40 -0
- package/apis/intf/modal.ts +90 -0
- package/apis/intf/shell.ts +36 -0
- package/apis/intf/slide-in.ts +98 -0
- package/apis/intf/system.ts +41 -0
- package/apis/shell/__tests__/modal.test.ts +80 -0
- package/apis/shell/__tests__/notifications.test.ts +71 -0
- package/apis/shell/__tests__/slide-in.test.ts +54 -0
- package/apis/shell/__tests__/system.test.ts +129 -0
- package/apis/shell/index.ts +38 -0
- package/apis/shell/modal.ts +41 -0
- package/apis/shell/notifications.ts +65 -0
- package/apis/shell/slide-in.ts +33 -0
- package/apis/shell/system.ts +65 -0
- package/apis/vue-shim.d.ts +11 -0
- package/assets/brand/suse/dark/rancher-logo.svg +1 -64
- package/assets/styles/global/_tooltip.scss +6 -1
- package/assets/translations/en-us.yaml +14 -1
- package/components/ActionMenuShell.vue +3 -1
- package/components/BackLink.vue +8 -0
- package/components/BannerGraphic.vue +1 -5
- package/components/BrandImage.vue +17 -6
- package/components/Cron/CronExpressionEditor.vue +1 -1
- package/components/Cron/CronExpressionEditorModal.vue +1 -1
- package/components/CruResource.vue +8 -1
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +1 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +50 -1
- package/components/Drawer/ResourceDetailDrawer/composables.ts +19 -0
- package/components/Drawer/ResourceDetailDrawer/index.vue +4 -1
- package/components/Drawer/ResourceDetailDrawer/types.ts +2 -1
- package/components/LocaleSelector.vue +2 -2
- package/components/ModalManager.vue +11 -1
- package/components/Questions/__tests__/Yaml.test.ts +1 -1
- package/components/Questions/__tests__/index.test.ts +159 -0
- package/components/RelatedResources.vue +5 -0
- package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
- package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
- package/components/Resource/Detail/Metadata/index.vue +3 -3
- package/components/Resource/Detail/ResourcePopover/index.vue +5 -1
- package/components/Resource/Detail/composables.ts +2 -2
- package/components/ResourceDetail/Masthead/latest.vue +23 -21
- package/components/ResourceDetail/index.vue +3 -0
- package/components/ResourceTable.vue +54 -21
- package/components/SlideInPanelManager.vue +16 -11
- package/components/SortableTable/THead.vue +2 -1
- package/components/SortableTable/index.vue +20 -2
- package/components/Tabbed/__tests__/index.test.ts +86 -0
- package/components/Tabbed/index.vue +37 -2
- package/components/__tests__/NamespaceFilter.test.ts +49 -0
- package/components/auth/SelectPrincipal.vue +28 -6
- package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
- package/components/auth/login/ldap.vue +3 -3
- package/components/fleet/FleetSecretSelector.vue +1 -1
- package/components/form/KeyValue.vue +1 -1
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/NodeScheduling.vue +2 -2
- package/components/form/ResourceTabs/composable.ts +2 -2
- package/components/form/ResourceTabs/index.vue +0 -2
- package/components/form/__tests__/NameNsDescription.test.ts +42 -0
- package/components/formatter/InternalExternalIP.vue +4 -1
- package/components/formatter/LinkName.vue +5 -0
- package/components/formatter/__tests__/InternalExternalIP.test.ts +1 -1
- package/components/nav/Group.vue +25 -7
- package/components/nav/Header.vue +1 -1
- package/components/nav/NamespaceFilter.vue +1 -0
- package/components/nav/Type.vue +17 -6
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +1 -1
- package/components/nav/__tests__/Type.test.ts +59 -0
- package/components/templates/standalone.vue +1 -1
- package/composables/cruResource.ts +27 -0
- package/composables/focusTrap.ts +3 -1
- package/composables/resourceDetail.ts +15 -0
- package/composables/useI18n.ts +10 -1
- package/composables/useLabeledFormElement.ts +3 -4
- package/config/__test__/uiplugins.test.ts +309 -0
- package/config/labels-annotations.js +1 -0
- package/config/product/explorer.js +3 -1
- package/config/product/fleet.js +1 -1
- package/config/router/navigation-guards/clusters.js +3 -3
- package/config/router/navigation-guards/products.js +1 -1
- package/config/router/routes.js +7 -7
- package/config/types.js +7 -0
- package/config/uiplugins.js +46 -2
- package/core/__tests__/extension-manager-impl.test.js +437 -0
- package/core/extension-manager-impl.js +21 -25
- package/core/plugin-helpers.ts +2 -2
- package/core/plugin.ts +9 -1
- package/core/plugins-loader.js +2 -2
- package/core/types-provisioning.ts +5 -1
- package/core/types.ts +35 -0
- package/detail/provisioning.cattle.io.cluster.vue +9 -6
- package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
- package/dialog/MoveNamespaceDialog.vue +20 -4
- package/dialog/RollbackWorkloadDialog.vue +2 -5
- package/dialog/SearchDialog.vue +1 -0
- package/dialog/__tests__/MoveNamespaceDialog.test.ts +249 -0
- package/directives/__tests__/clean-tooltip.test.ts +298 -0
- package/directives/clean-tooltip.ts +234 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -2
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +100 -3
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
- package/edit/configmap.vue +1 -0
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.helmop.vue +11 -6
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
- package/edit/logging-flow/index.vue +1 -0
- package/edit/logging.banzaicloud.io.output/index.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +1 -1
- package/edit/management.cattle.io.project.vue +1 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
- package/edit/monitoring.coreos.com.route.vue +1 -1
- package/edit/namespace.vue +1 -0
- package/edit/networking.istio.io.destinationrule/index.vue +1 -0
- package/edit/networking.k8s.io.ingress/index.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
- package/edit/node.vue +1 -0
- package/edit/persistentvolume/index.vue +27 -22
- package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
- package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
- package/edit/persistentvolume/plugins/azureFile.vue +15 -14
- package/edit/persistentvolume/plugins/cephfs.vue +15 -14
- package/edit/persistentvolume/plugins/cinder.vue +15 -14
- package/edit/persistentvolume/plugins/csi.vue +18 -16
- package/edit/persistentvolume/plugins/fc.vue +13 -14
- package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
- package/edit/persistentvolume/plugins/flocker.vue +1 -3
- package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
- package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
- package/edit/persistentvolume/plugins/hostPath.vue +40 -39
- package/edit/persistentvolume/plugins/iscsi.vue +13 -14
- package/edit/persistentvolume/plugins/local.vue +1 -3
- package/edit/persistentvolume/plugins/longhorn.vue +23 -22
- package/edit/persistentvolume/plugins/nfs.vue +15 -14
- package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
- package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
- package/edit/persistentvolume/plugins/quobyte.vue +15 -14
- package/edit/persistentvolume/plugins/rbd.vue +15 -14
- package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
- package/edit/persistentvolume/plugins/storageos.vue +15 -14
- package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
- package/edit/provisioning.cattle.io.cluster/index.vue +5 -5
- package/edit/provisioning.cattle.io.cluster/rke2.vue +9 -8
- package/edit/resources.cattle.io.restore.vue +1 -1
- package/edit/secret/index.vue +1 -1
- package/edit/service.vue +1 -0
- package/edit/serviceaccount.vue +1 -0
- package/edit/storage.k8s.io.storageclass/index.vue +1 -0
- package/edit/workload/Job.vue +2 -2
- package/edit/workload/index.vue +2 -1
- package/edit/workload/mixins/workload.js +1 -1
- package/initialize/App.vue +4 -4
- package/initialize/install-plugins.js +19 -5
- package/machine-config/azure.vue +1 -1
- package/machine-config/components/GCEImage.vue +1 -1
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/brand.js +1 -7
- package/mixins/create-edit-view/index.js +5 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +128 -5
- package/models/chart.js +70 -74
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/provisioning.cattle.io.cluster.js +31 -11
- package/package.json +11 -10
- package/pages/auth/login.vue +4 -6
- package/pages/auth/setup.vue +1 -1
- package/pages/auth/verify.vue +3 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
- package/pages/c/_cluster/apps/charts/chart.vue +33 -15
- package/pages/c/_cluster/apps/charts/index.vue +122 -24
- package/pages/c/_cluster/apps/charts/install.vue +33 -0
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
- package/pages/c/_cluster/explorer/index.vue +8 -6
- package/pages/c/_cluster/fleet/index.vue +4 -7
- package/pages/c/_cluster/manager/hostedprovider/index.vue +12 -6
- package/pages/c/_cluster/settings/brand.vue +1 -1
- package/pages/c/_cluster/settings/index.vue +5 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
- package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
- package/pages/c/_cluster/uiplugins/index.vue +126 -184
- package/pkg/auto-import.js +3 -3
- package/pkg/dynamic-importer.lib.js +1 -1
- package/pkg/import.js +1 -1
- package/plugins/__tests__/mutations.tests.ts +179 -0
- package/plugins/dashboard-client-init.js +3 -0
- package/plugins/dashboard-store/getters.js +19 -2
- package/plugins/dashboard-store/model-loader.js +1 -1
- package/plugins/dashboard-store/mutations.js +23 -2
- package/plugins/dashboard-store/resource-class.js +11 -5
- package/plugins/i18n.js +8 -0
- package/plugins/plugin.js +2 -2
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +506 -0
- package/plugins/steve/steve-class.js +1 -1
- package/plugins/steve/steve-pagination-utils.ts +131 -47
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -42
- package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
- package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
- package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
- package/rancher-components/Pill/types.ts +0 -1
- package/rancher-components/RcDropdown/useDropdownContext.ts +2 -4
- package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
- package/rancher-components/RcIcon/RcIcon.vue +46 -0
- package/rancher-components/RcIcon/index.ts +1 -0
- package/rancher-components/RcIcon/types.ts +160 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +1 -1
- package/rancher-components/utils/status.test.ts +67 -0
- package/rancher-components/utils/status.ts +77 -0
- package/scripts/publish-shell.sh +25 -0
- package/scripts/typegen.sh +1 -0
- package/store/__tests__/catalog.test.ts +1 -1
- package/store/__tests__/type-map.test.ts +164 -2
- package/store/action-menu.js +8 -0
- package/store/auth.js +25 -13
- package/store/catalog.js +6 -0
- package/store/i18n.js +3 -3
- package/store/index.js +8 -6
- package/store/notifications.ts +2 -0
- package/store/prefs.js +6 -7
- package/store/type-map.js +17 -7
- package/store/wm.ts +4 -4
- package/types/internal-api/shell/modal.d.ts +6 -6
- package/types/notifications/index.ts +126 -15
- package/types/rancher/index.d.ts +9 -0
- package/types/shell/index.d.ts +54 -3
- package/types/store/__tests__/pagination.types.spec.ts +137 -0
- package/types/store/pagination.types.ts +157 -9
- package/types/vue-shim.d.ts +5 -4
- package/utils/__tests__/provider.test.ts +98 -0
- package/utils/__tests__/router.test.js +238 -0
- package/utils/__tests__/selector-typed.test.ts +263 -0
- package/utils/cluster.js +4 -1
- package/utils/color.js +1 -1
- package/utils/dynamic-content/__tests__/info.test.ts +6 -0
- package/utils/dynamic-content/info.ts +43 -0
- package/utils/favicon.js +4 -4
- package/utils/fleet.ts +8 -1
- package/utils/pagination-utils.ts +2 -2
- package/utils/pagination-wrapper.ts +1 -1
- package/utils/provider.ts +14 -0
- package/utils/router.js +50 -0
- package/utils/selector-typed.ts +6 -2
- package/utils/unit-tests/pagination-utils.spec.ts +8 -8
- package/vue.config.js +3 -3
- package/composables/useExtensionManager.ts +0 -17
- package/core/plugins.js +0 -38
- package/directives/clean-tooltip.js +0 -32
- package/plugins/internal-api/index.ts +0 -37
- package/plugins/internal-api/shared/base-api.ts +0 -13
- package/plugins/internal-api/shell/shell.api.ts +0 -108
- package/plugins/nuxt-client-init.js +0 -3
- package/types/internal-api/shell/growl.d.ts +0 -25
- package/types/internal-api/shell/slideIn.d.ts +0 -15
|
@@ -39,10 +39,10 @@ export const useTabCountWatcher = () => {
|
|
|
39
39
|
|
|
40
40
|
export const useTabCountUpdater = () => {
|
|
41
41
|
const tabKey = randomStr();
|
|
42
|
-
const updateCount = inject<UpdateCountFn>(UPDATE_COUNT_PROVIDER_KEY);
|
|
42
|
+
const updateCount = inject<UpdateCountFn>(UPDATE_COUNT_PROVIDER_KEY, () => { });
|
|
43
43
|
|
|
44
44
|
const updateTabCount = (count: number | undefined) => {
|
|
45
|
-
updateCount
|
|
45
|
+
updateCount(tabKey, count);
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
const clearTabCount = () => updateTabCount(undefined);
|
|
@@ -101,7 +101,6 @@ export default {
|
|
|
101
101
|
return {
|
|
102
102
|
eventSchema,
|
|
103
103
|
EVENT,
|
|
104
|
-
selectedTab: this.defaultTab,
|
|
105
104
|
inStore,
|
|
106
105
|
showConditions: false,
|
|
107
106
|
paginationHeaders
|
|
@@ -245,7 +244,6 @@ export default {
|
|
|
245
244
|
:default-tab="defaultTab"
|
|
246
245
|
:resource="value"
|
|
247
246
|
:use-hash="useHash"
|
|
248
|
-
@changed="tabChange"
|
|
249
247
|
>
|
|
250
248
|
<slot />
|
|
251
249
|
<Tab
|
|
@@ -87,6 +87,48 @@ describe('component: NameNsDescription', () => {
|
|
|
87
87
|
expect(wrapper.emitted().isNamespaceNew?.[0][0]).toBe(true);
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
+
it('should set the namespace using the namespaceKey prop', () => {
|
|
91
|
+
const namespaceName = 'custom-namespace';
|
|
92
|
+
const store = createStore({
|
|
93
|
+
getters: {
|
|
94
|
+
allowedNamespaces: () => () => ({ [namespaceName]: true }),
|
|
95
|
+
currentStore: () => () => 'cluster',
|
|
96
|
+
'cluster/schemaFor': () => jest.fn()
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const wrapper = mount(NameNsDescription, {
|
|
101
|
+
props: {
|
|
102
|
+
value: {
|
|
103
|
+
setAnnotation: jest.fn(),
|
|
104
|
+
metadata: {},
|
|
105
|
+
value: { metadata: { namespace: namespaceName } }
|
|
106
|
+
},
|
|
107
|
+
mode: 'create',
|
|
108
|
+
namespaceKey: 'value.metadata.namespace',
|
|
109
|
+
},
|
|
110
|
+
global: {
|
|
111
|
+
provide: { store },
|
|
112
|
+
mocks: {
|
|
113
|
+
$store: {
|
|
114
|
+
dispatch: jest.fn(),
|
|
115
|
+
getters: {
|
|
116
|
+
namespaces: jest.fn(),
|
|
117
|
+
'customizations/getPreviewCluster': {
|
|
118
|
+
ready: true,
|
|
119
|
+
isLocal: false,
|
|
120
|
+
badge: {},
|
|
121
|
+
},
|
|
122
|
+
'i18n/t': jest.fn(),
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect((wrapper.vm as any).namespace).toBe(namespaceName);
|
|
130
|
+
});
|
|
131
|
+
|
|
90
132
|
it('renders the name input with the expected value', () => {
|
|
91
133
|
const namespaceName = 'test';
|
|
92
134
|
const store = createStore({
|
|
@@ -108,10 +108,13 @@ export default {
|
|
|
108
108
|
>
|
|
109
109
|
<template #default>
|
|
110
110
|
<RcStatusBadge
|
|
111
|
-
v-clean-tooltip="tooltipContent"
|
|
111
|
+
v-clean-tooltip="{content: tooltipContent, triggers: ['hover', 'focus']}"
|
|
112
|
+
:aria-label="t('generic.plusMore', {n: remainingIpCount})"
|
|
113
|
+
tabindex="0"
|
|
112
114
|
status="info"
|
|
113
115
|
data-testid="plus-more"
|
|
114
116
|
@click.stop
|
|
117
|
+
@keyup.enter.space="$refs.dropdown.show()"
|
|
115
118
|
>
|
|
116
119
|
{{ t('generic.plusMore', {n: remainingIpCount}) }}
|
|
117
120
|
</RcStatusBadge>
|
|
@@ -41,6 +41,11 @@ export default {
|
|
|
41
41
|
product: this.product || EXPLORER,
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
// Having an undefined param can yield a console warning like [Vue Router warn]: Discarded invalid param(s) "namespace" when navigating
|
|
45
|
+
if (!params.namespace) {
|
|
46
|
+
delete params.namespace;
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
return { name, params };
|
|
45
50
|
},
|
|
46
51
|
|
|
@@ -24,7 +24,7 @@ describe('component: InternalExternalIP', () => {
|
|
|
24
24
|
components: { 'v-dropdown': { name: 'v-dropdown', template: '<div><slot /><slot name="popper" /></div>' } },
|
|
25
25
|
directives: {
|
|
26
26
|
'clean-tooltip': (el, binding) => {
|
|
27
|
-
el.setAttribute('v-clean-tooltip', binding.value);
|
|
27
|
+
el.setAttribute('v-clean-tooltip', binding.value.content);
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
mocks: { $store: { getters: mockGetters } }
|
package/components/nav/Group.vue
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Type from '@shell/components/nav/Type';
|
|
3
|
+
import { filterLocationValidParams } from '@shell/utils/router';
|
|
3
4
|
export default {
|
|
4
5
|
name: 'Group',
|
|
5
6
|
|
|
@@ -41,6 +42,11 @@ export default {
|
|
|
41
42
|
fixedOpen: {
|
|
42
43
|
type: Boolean,
|
|
43
44
|
default: false,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
highlightRoute: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: true,
|
|
44
50
|
}
|
|
45
51
|
},
|
|
46
52
|
|
|
@@ -73,7 +79,8 @@ export default {
|
|
|
73
79
|
const overviewRoute = grp?.route;
|
|
74
80
|
|
|
75
81
|
if (overviewRoute && grp.overview) {
|
|
76
|
-
const
|
|
82
|
+
const validRoute = filterLocationValidParams(this.$router, overviewRoute || {});
|
|
83
|
+
const route = this.$router.resolve(validRoute);
|
|
77
84
|
|
|
78
85
|
return this.$route.fullPath.split('#')[0] === route?.fullPath;
|
|
79
86
|
}
|
|
@@ -89,6 +96,10 @@ export default {
|
|
|
89
96
|
set(v) {
|
|
90
97
|
this.expanded = v;
|
|
91
98
|
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
headerRoute() {
|
|
102
|
+
return filterLocationValidParams(this.$router, this.group.children[0].route);
|
|
92
103
|
}
|
|
93
104
|
},
|
|
94
105
|
|
|
@@ -134,7 +145,9 @@ export default {
|
|
|
134
145
|
const route = item.route;
|
|
135
146
|
|
|
136
147
|
if (route) {
|
|
137
|
-
this.$router
|
|
148
|
+
const validRoute = filterLocationValidParams(this.$router, route);
|
|
149
|
+
|
|
150
|
+
this.$router.replace(validRoute);
|
|
138
151
|
} else if (item) {
|
|
139
152
|
this.routeToFirstChild(item);
|
|
140
153
|
}
|
|
@@ -143,7 +156,9 @@ export default {
|
|
|
143
156
|
|
|
144
157
|
routeToFirstChild(item) {
|
|
145
158
|
if (item.children.length && item.children[0].route) {
|
|
146
|
-
this.$router
|
|
159
|
+
const validRoute = filterLocationValidParams(this.$router, item.children[0].route);
|
|
160
|
+
|
|
161
|
+
this.$router.replace(validRoute);
|
|
147
162
|
}
|
|
148
163
|
},
|
|
149
164
|
|
|
@@ -191,7 +206,8 @@ export default {
|
|
|
191
206
|
const matchesNavLevel = navLevels.filter((param) => !this.$route.params[param] || this.$route.params[param] !== item.route.params[param]).length === 0;
|
|
192
207
|
const withoutHash = this.$route.hash ? this.$route.fullPath.slice(0, this.$route.fullPath.indexOf(this.$route.hash)) : this.$route.fullPath;
|
|
193
208
|
const withoutQuery = withoutHash.split('?')[0];
|
|
194
|
-
const
|
|
209
|
+
const validItemRoute = filterLocationValidParams(this.$router, item.route);
|
|
210
|
+
const itemFullPath = this.$router.resolve(validItemRoute).fullPath;
|
|
195
211
|
|
|
196
212
|
if (matchesNavLevel || itemFullPath === withoutQuery) {
|
|
197
213
|
return true;
|
|
@@ -233,7 +249,7 @@ export default {
|
|
|
233
249
|
<template>
|
|
234
250
|
<div
|
|
235
251
|
class="accordion"
|
|
236
|
-
:class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': isGroupActive }"
|
|
252
|
+
:class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': highlightRoute && isGroupActive }"
|
|
237
253
|
>
|
|
238
254
|
<div
|
|
239
255
|
v-if="showHeader || (!onlyHasOverview && canCollapse)"
|
|
@@ -242,7 +258,7 @@ export default {
|
|
|
242
258
|
<div
|
|
243
259
|
v-if="showHeader"
|
|
244
260
|
class="header"
|
|
245
|
-
:class="{'active': isOverview, 'noHover': !canCollapse || fixedOpen}"
|
|
261
|
+
:class="{'active': highlightRoute && isOverview, 'noHover': !canCollapse || fixedOpen}"
|
|
246
262
|
role="button"
|
|
247
263
|
:tabindex="fixedOpen ? -1 : 0"
|
|
248
264
|
:aria-label="group.labelDisplay || group.label || ''"
|
|
@@ -253,7 +269,7 @@ export default {
|
|
|
253
269
|
<slot name="header">
|
|
254
270
|
<router-link
|
|
255
271
|
v-if="hasOverview"
|
|
256
|
-
:to="
|
|
272
|
+
:to="headerRoute"
|
|
257
273
|
:exact="group.children[0].exact"
|
|
258
274
|
:tabindex="-1"
|
|
259
275
|
>
|
|
@@ -311,6 +327,7 @@ export default {
|
|
|
311
327
|
:can-collapse="canCollapse"
|
|
312
328
|
:group="child"
|
|
313
329
|
:fixed-open="fixedOpen"
|
|
330
|
+
:highlight-route="highlightRoute"
|
|
314
331
|
@selected="groupSelected($event)"
|
|
315
332
|
@expand="expandGroup($event)"
|
|
316
333
|
@close="close($event)"
|
|
@@ -322,6 +339,7 @@ export default {
|
|
|
322
339
|
:is-root="depth == 0 && !showHeader"
|
|
323
340
|
:type="child"
|
|
324
341
|
:depth="depth"
|
|
342
|
+
:highlight-route="highlightRoute"
|
|
325
343
|
@selected="selectType($event)"
|
|
326
344
|
/>
|
|
327
345
|
</template>
|
|
@@ -254,7 +254,7 @@ export default {
|
|
|
254
254
|
this.extensionHeaderActions = getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, neu);
|
|
255
255
|
this.updateExtensionActionsEnabled();
|
|
256
256
|
|
|
257
|
-
this.navHeaderRight = this.$
|
|
257
|
+
this.navHeaderRight = this.$extension?.getDynamic('component', 'NavHeaderRight');
|
|
258
258
|
}
|
|
259
259
|
},
|
|
260
260
|
immediate: true,
|
package/components/nav/Type.vue
CHANGED
|
@@ -3,6 +3,7 @@ import Favorite from '@shell/components/nav/Favorite';
|
|
|
3
3
|
import { TYPE_MODES } from '@shell/store/type-map';
|
|
4
4
|
|
|
5
5
|
import TabTitle from '@shell/components/TabTitle';
|
|
6
|
+
import { filterLocationValidParams } from '@shell/utils/router';
|
|
6
7
|
|
|
7
8
|
const showFavoritesFor = [TYPE_MODES.FAVORITE, TYPE_MODES.USED];
|
|
8
9
|
|
|
@@ -27,6 +28,11 @@ export default {
|
|
|
27
28
|
type: Number,
|
|
28
29
|
default: 0,
|
|
29
30
|
},
|
|
31
|
+
|
|
32
|
+
highlightRoute: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
default: true,
|
|
35
|
+
},
|
|
30
36
|
},
|
|
31
37
|
|
|
32
38
|
data() {
|
|
@@ -57,7 +63,7 @@ export default {
|
|
|
57
63
|
},
|
|
58
64
|
|
|
59
65
|
isActive() {
|
|
60
|
-
const typeFullPath = this.$router.resolve(this.
|
|
66
|
+
const typeFullPath = this.$router.resolve(this.typeRoute)?.fullPath.toLowerCase();
|
|
61
67
|
const pageFullPath = this.$route.fullPath?.toLowerCase().split('#')[0]; // Ignore the shebang when comparing routes
|
|
62
68
|
const routeMetaNav = this.$route.meta?.nav;
|
|
63
69
|
|
|
@@ -88,6 +94,10 @@ export default {
|
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
return typeFullPath === pageFullPath;
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
typeRoute() {
|
|
100
|
+
return filterLocationValidParams(this.$router, this.type.route);
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
},
|
|
@@ -100,7 +110,8 @@ export default {
|
|
|
100
110
|
selectType() {
|
|
101
111
|
// Prevent issues if custom NavLink is used #5047
|
|
102
112
|
if (this.type?.route) {
|
|
103
|
-
const
|
|
113
|
+
const validRoute = filterLocationValidParams(this.$router, this.type.route);
|
|
114
|
+
const typePath = this.$router.resolve(validRoute)?.fullPath;
|
|
104
115
|
|
|
105
116
|
if (typePath !== this.$route.fullPath) {
|
|
106
117
|
this.$emit('selected');
|
|
@@ -117,16 +128,16 @@ export default {
|
|
|
117
128
|
:key="type.name"
|
|
118
129
|
v-slot="{ href, navigate,isExactActive }"
|
|
119
130
|
custom
|
|
120
|
-
:to="
|
|
131
|
+
:to="typeRoute"
|
|
121
132
|
>
|
|
122
133
|
<li
|
|
123
134
|
class="child nav-type"
|
|
124
|
-
:class="{'root': isRoot, [`depth-${depth}`]: true, 'router-link-active': isActive, 'router-link-exact-active': isExactActive}"
|
|
135
|
+
:class="{'root': isRoot, [`depth-${depth}`]: true, 'router-link-active': highlightRoute && isActive, 'router-link-exact-active': highlightRoute && isExactActive}"
|
|
125
136
|
@click="navigate"
|
|
126
137
|
@keypress.enter="navigate"
|
|
127
138
|
>
|
|
128
139
|
<TabTitle
|
|
129
|
-
v-if="isExactActive"
|
|
140
|
+
v-if="highlightRoute && isExactActive"
|
|
130
141
|
:show-child="false"
|
|
131
142
|
>
|
|
132
143
|
{{ type.labelKey ? t(type.labelKey) : (type.labelDisplay || type.label) }}
|
|
@@ -136,7 +147,7 @@ export default {
|
|
|
136
147
|
:aria-label="type.labelKey ? t(type.labelKey) : (type.labelDisplay || type.label)"
|
|
137
148
|
:href="href"
|
|
138
149
|
class="type-link"
|
|
139
|
-
:aria-current="isActive ? 'page' : undefined"
|
|
150
|
+
:aria-current="highlightRoute && isActive ? 'page' : undefined"
|
|
140
151
|
@click="selectType(); navigate($event);"
|
|
141
152
|
@mouseenter="setNear(true)"
|
|
142
153
|
@mouseleave="setNear(false)"
|
|
@@ -250,6 +250,65 @@ describe('component: Type', () => {
|
|
|
250
250
|
});
|
|
251
251
|
});
|
|
252
252
|
|
|
253
|
+
describe('should respect highlightRoute prop', () => {
|
|
254
|
+
it('should not use active class when highlightRoute is false even if route is active', () => {
|
|
255
|
+
const wrapper = shallowMount(Type as any, {
|
|
256
|
+
props: { type: defaultRouteTypeProp, highlightRoute: false },
|
|
257
|
+
|
|
258
|
+
global: {
|
|
259
|
+
directives: { cleanHtml: (identity) => identity },
|
|
260
|
+
|
|
261
|
+
mocks: {
|
|
262
|
+
$store: storeMock, $router: routerMock, $route: routeMock
|
|
263
|
+
},
|
|
264
|
+
stubs: { routerLink: createChildRenderingRouterLinkStub() },
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const elementWithSelector = wrapper.find(`.${ activeClass }`);
|
|
269
|
+
|
|
270
|
+
expect(elementWithSelector.exists()).toBe(false);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('should not use exact active class when highlightRoute is false even if route is exact active', () => {
|
|
274
|
+
const wrapper = shallowMount(Type as any, {
|
|
275
|
+
props: { type: defaultRouteTypeProp, highlightRoute: false },
|
|
276
|
+
|
|
277
|
+
global: {
|
|
278
|
+
directives: { cleanHtml: (identity) => identity },
|
|
279
|
+
|
|
280
|
+
mocks: {
|
|
281
|
+
$store: storeMock, $router: routerMock, $route: routeMock
|
|
282
|
+
},
|
|
283
|
+
stubs: { routerLink: createChildRenderingRouterLinkStub({ isExactActive: true }) },
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const elementWithSelector = wrapper.find(`.${ exactActiveClass }`);
|
|
288
|
+
|
|
289
|
+
expect(elementWithSelector.exists()).toBe(false);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should use active class when highlightRoute is true (default) and route is active', () => {
|
|
293
|
+
const wrapper = shallowMount(Type as any, {
|
|
294
|
+
props: { type: defaultRouteTypeProp, highlightRoute: true },
|
|
295
|
+
|
|
296
|
+
global: {
|
|
297
|
+
directives: { cleanHtml: (identity) => identity },
|
|
298
|
+
|
|
299
|
+
mocks: {
|
|
300
|
+
$store: storeMock, $router: routerMock, $route: routeMock
|
|
301
|
+
},
|
|
302
|
+
stubs: { routerLink: createChildRenderingRouterLinkStub() },
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const elementWithSelector = wrapper.find(`.${ activeClass }`);
|
|
307
|
+
|
|
308
|
+
expect(elementWithSelector.exists()).toBe(true);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
253
312
|
describe('should handle the favorite icon appropriately', () => {
|
|
254
313
|
it('should show favorite icon if mouse is over and type is favorite', async() => {
|
|
255
314
|
const wrapper = shallowMount(Type as any, {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { inject, provide } from 'vue';
|
|
2
|
+
const IS_IN_RESOURCE_EDIT_PAGE_KEY = 'isInResourceEditKey';
|
|
3
|
+
const IS_IN_RESOURCE_CREATE_PAGE_KEY = 'isInResourceCreateKey';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Used to determine if the current component was instantiated as an ancestor of a CruResource EDIT page.
|
|
7
|
+
* @returns true if the component is an ancestor of CruResource EDIT page, otherwise false
|
|
8
|
+
*/
|
|
9
|
+
export function useIsInResourceEditPage() {
|
|
10
|
+
return inject(IS_IN_RESOURCE_EDIT_PAGE_KEY, false);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Used to determine if the current component was instantiated as an ancestor of a CruResource CREATE page.
|
|
15
|
+
* @returns true if the component is an ancestor of CruResource CREATE page, otherwise false
|
|
16
|
+
*/
|
|
17
|
+
export function useIsInResourceCreatePage() {
|
|
18
|
+
return inject(IS_IN_RESOURCE_CREATE_PAGE_KEY, false);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useResourceEditPageProvider() {
|
|
22
|
+
provide(IS_IN_RESOURCE_EDIT_PAGE_KEY, true);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useResourceCreatePageProvider() {
|
|
26
|
+
provide(IS_IN_RESOURCE_CREATE_PAGE_KEY, true);
|
|
27
|
+
}
|
package/composables/focusTrap.ts
CHANGED
|
@@ -57,8 +57,10 @@ export function useWatcherBasedSetupFocusTrapWithDestroyIncluded(watchVar:any, f
|
|
|
57
57
|
|
|
58
58
|
focusTrapInstance = createFocusTrap(focusEl, opts);
|
|
59
59
|
|
|
60
|
+
const activate = () => focusTrapInstance.activate();
|
|
61
|
+
|
|
60
62
|
nextTick(() => {
|
|
61
|
-
|
|
63
|
+
setTimeout(activate, 0);
|
|
62
64
|
});
|
|
63
65
|
});
|
|
64
66
|
} else if (!neu && focusTrapInstance && Object.keys(focusTrapInstance).length && !useUnmountHook) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { inject, provide } from 'vue';
|
|
2
|
+
|
|
3
|
+
const IS_IN_RESOURCE_DETAIL_PAGE_KEY = 'isInResourceDetailKey';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Used to determine if the current component was instantiated as an ancestor of a ResourceDetail.
|
|
7
|
+
* @returns true if the component is an ancestor of ResourceDetail, otherwise false
|
|
8
|
+
*/
|
|
9
|
+
export function useIsInResourceDetailPage() {
|
|
10
|
+
return inject(IS_IN_RESOURCE_DETAIL_PAGE_KEY, false);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useResourceDetailPageProvider() {
|
|
14
|
+
provide(IS_IN_RESOURCE_DETAIL_PAGE_KEY, true);
|
|
15
|
+
}
|
package/composables/useI18n.ts
CHANGED
|
@@ -11,7 +11,16 @@ let store: Store<any> | null = null;
|
|
|
11
11
|
* @returns A translated string or the raw value if the raw parameter is set to true.
|
|
12
12
|
*/
|
|
13
13
|
const t = (key: string, args?: unknown, raw?: boolean): string => {
|
|
14
|
-
|
|
14
|
+
if (!store) {
|
|
15
|
+
if (!!process.env.dev) {
|
|
16
|
+
// eslint-disable-next-line no-console
|
|
17
|
+
console.warn('useI18n: store not available');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return key;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return stringFor(store, key, args as any, raw);
|
|
15
24
|
};
|
|
16
25
|
|
|
17
26
|
export type I18n = { t: typeof t };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ref, computed, ComputedRef, Ref,
|
|
2
|
+
ref, computed, ComputedRef, Ref,
|
|
3
|
+
EmitFn
|
|
3
4
|
} from 'vue';
|
|
4
5
|
import { _VIEW, _EDIT } from '@shell/config/query-params';
|
|
5
6
|
|
|
@@ -72,9 +73,7 @@ export const labeledFormElementProps = {
|
|
|
72
73
|
}
|
|
73
74
|
};
|
|
74
75
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
export const useLabeledFormElement = (props: LabeledFormElementProps, emit: typeof labeledFormElementEmits): UseLabeledFormElement => {
|
|
76
|
+
export const useLabeledFormElement = (props: LabeledFormElementProps, emit: EmitFn<['update:validation']>): UseLabeledFormElement => {
|
|
78
77
|
const raised = ref(props.mode === _VIEW || !!`${ props.value }`);
|
|
79
78
|
const focused = ref(false);
|
|
80
79
|
const blurred = ref<number | null>(null);
|