@rancher/shell 3.0.11 → 3.0.12-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/entraid-black.svg +4 -0
- package/assets/images/providers/entraid.svg +9 -0
- package/assets/images/vendor/entraid.svg +9 -0
- package/assets/styles/app.scss +0 -1
- package/assets/styles/base/_mixins.scss +31 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -5
- package/assets/translations/en-us.yaml +24 -21
- package/assets/translations/zh-hans.yaml +4 -11
- package/chart/__tests__/S3.test.ts +10 -3
- package/components/CountBox.vue +20 -0
- package/components/CreateDriver.vue +0 -12
- package/components/DetailText.vue +12 -3
- package/components/EmptyProductPage.vue +76 -0
- package/components/Resource/Detail/CopyToClipboard.vue +1 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +9 -3
- package/components/Resource/Detail/TitleBar/__tests__/__snapshots__/index.test.ts.snap +31 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +45 -1
- package/components/Resource/Detail/TitleBar/index.vue +1 -1
- package/components/Resource/Detail/ViewOptions/__tests__/__snapshots__/index.test.ts.snap +9 -0
- package/components/Resource/Detail/ViewOptions/__tests__/index.test.ts +62 -0
- package/components/Resource/Detail/ViewOptions/index.vue +2 -1
- package/components/ResourceList/Masthead.vue +25 -2
- package/components/SelectIconGrid.vue +5 -0
- package/components/SideNav.vue +13 -0
- package/components/__tests__/CountBox.test.ts +72 -0
- package/components/__tests__/DetailText.test.ts +113 -0
- package/components/__tests__/PromptModal.test.ts +2 -0
- package/components/fleet/FleetClusterTargets/index.vue +18 -1
- package/components/fleet/FleetClusters.vue +1 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
- package/components/form/InputWithSelect.vue +18 -10
- package/components/form/KeyValue.vue +17 -1
- package/components/form/LabeledSelect.vue +82 -24
- package/components/form/NodeScheduling.vue +17 -3
- package/components/form/PrivateRegistry.vue +69 -0
- package/components/form/Select.vue +73 -56
- package/components/form/ServiceNameSelect.vue +13 -11
- package/components/form/__tests__/KeyValue.test.ts +66 -0
- package/components/form/__tests__/NodeScheduling.test.ts +9 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
- package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
- package/components/formatter/WorkloadHealthScale.vue +3 -1
- package/components/nav/Group.vue +33 -9
- package/components/nav/Header.vue +56 -10
- package/components/nav/NotificationCenter/Notification.vue +4 -1
- package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
- package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
- package/components/nav/TopLevelMenu.vue +15 -1
- package/components/nav/Type.vue +8 -7
- package/components/nav/WindowManager/index.vue +2 -1
- package/components/nav/WorkspaceSwitcher.vue +13 -0
- package/components/nav/__tests__/Group.test.ts +67 -0
- package/components/nav/__tests__/Header.test.ts +235 -0
- package/components/nav/__tests__/Type.test.ts +20 -3
- package/components/templates/default.vue +34 -4
- package/components/templates/home.vue +12 -25
- package/components/templates/plain.vue +13 -26
- package/composables/useLabeledFormElement.ts +10 -2
- package/composables/useLabeledSelect.ts +60 -0
- package/composables/useUserRetentionValidation.ts +1 -49
- package/config/cookies.js +0 -1
- package/config/labels-annotations.js +1 -0
- package/config/pagination-table-headers.js +8 -1
- package/config/product/apps.js +2 -1
- package/config/product/auth.js +1 -0
- package/config/product/backup.js +1 -0
- package/config/product/compliance.js +1 -1
- package/config/product/explorer.js +25 -6
- package/config/product/fleet.js +1 -0
- package/config/product/gatekeeper.js +1 -0
- package/config/product/istio.js +1 -0
- package/config/product/logging.js +1 -0
- package/config/product/longhorn.js +2 -1
- package/config/product/manager.js +1 -0
- package/config/product/monitoring.js +1 -0
- package/config/product/navlinks.js +1 -0
- package/config/product/neuvector.js +2 -1
- package/config/product/settings.js +1 -0
- package/config/product/uiplugins.js +1 -0
- package/config/query-params.js +1 -0
- package/config/router/routes.js +0 -8
- package/core/__tests__/plugin-products-helpers.test.ts +454 -0
- package/core/__tests__/plugin-products.test.ts +3810 -0
- package/core/extension-manager-impl.js +30 -1
- package/core/plugin-products-base.ts +392 -0
- package/core/plugin-products-extending.ts +44 -0
- package/core/plugin-products-helpers.ts +263 -0
- package/core/plugin-products-top-level.ts +66 -0
- package/core/plugin-products-type-guards.ts +33 -0
- package/core/plugin-products.ts +50 -0
- package/core/plugin-types.ts +237 -0
- package/core/plugin.ts +45 -10
- package/core/productDebugger.js +48 -0
- package/core/types.ts +97 -11
- package/detail/__tests__/__snapshots__/fleet.cattle.io.bundle.test.ts.snap +52 -0
- package/detail/__tests__/fleet.cattle.io.bundle.test.ts +171 -0
- package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
- package/detail/fleet.cattle.io.bundle.vue +21 -34
- package/detail/management.cattle.io.fleetworkspace.vue +49 -0
- package/dialog/ExtensionCatalogInstallDialog.vue +1 -1
- package/dialog/InstallExtensionDialog.vue +6 -27
- package/dialog/UninstallExistingExtensionDialog.vue +141 -0
- package/dialog/UninstallExtensionDialog.vue +4 -26
- package/dialog/__tests__/UninstallExistingExtensionDialog.test.ts +114 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
- package/edit/__tests__/kontainerDriver.test.ts +0 -13
- package/edit/__tests__/nodeDriver.test.ts +5 -11
- package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auth/__tests__/oidc.test.ts +54 -0
- package/edit/auth/azuread.vue +1 -1
- package/edit/auth/oidc.vue +8 -0
- package/edit/kontainerDriver.vue +1 -2
- package/edit/nodeDriver.vue +0 -2
- package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +70 -99
- package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
- package/initialize/App.vue +29 -2
- package/initialize/install-plugins.js +0 -2
- package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
- package/list/catalog.cattle.io.app.vue +25 -5
- package/list/management.cattle.io.feature.vue +1 -1
- package/list/management.cattle.io.fleetworkspace.vue +8 -0
- package/list/provisioning.cattle.io.cluster.vue +0 -1
- package/list/workload.vue +11 -4
- package/machine-config/amazonec2.vue +1 -0
- package/mixins/chart.js +40 -9
- package/mixins/resource-fetch.js +12 -3
- package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
- package/models/__tests__/chart.test.ts +99 -6
- package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
- package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
- package/models/catalog.cattle.io.app.js +21 -17
- package/models/catalog.cattle.io.clusterrepo.js +39 -11
- package/models/chart.js +33 -19
- package/models/fleet-application.js +1 -1
- package/models/fleet.cattle.io.bundle.js +1 -1
- package/models/kontainerdriver.js +11 -0
- package/models/management.cattle.io.authconfig.js +5 -1
- package/models/management.cattle.io.cluster.js +0 -53
- package/models/management.cattle.io.feature.js +3 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -26
- package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
- package/models/nodedriver.js +7 -0
- package/models/pod.js +18 -0
- package/models/workload.js +20 -2
- package/package.json +13 -13
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +189 -0
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
- package/pages/c/_cluster/apps/charts/chart.vue +217 -33
- package/pages/c/_cluster/apps/charts/index.vue +2 -2
- package/pages/c/_cluster/apps/charts/install.vue +8 -3
- package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -7
- package/pages/c/_cluster/settings/brand.vue +4 -4
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +39 -2
- package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +246 -23
- package/pages/c/_cluster/uiplugins/index.vue +166 -62
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -0
- package/plugins/dashboard-store/actions.js +3 -2
- package/plugins/dashboard-store/resource-class.js +62 -6
- package/plugins/plugin.js +16 -0
- package/plugins/steve/steve-pagination-utils.ts +7 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +205 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +82 -4
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
- package/scripts/test-plugins-build.sh +5 -2
- package/scripts/typegen.sh +13 -1
- package/server/server-middleware.js +2 -2
- package/static/humans.txt +1 -0
- package/static/robots.txt +34 -0
- package/static/welcome-cow.svg +18 -0
- package/store/__tests__/catalog.test.ts +161 -11
- package/store/__tests__/type-map.test.ts +84 -24
- package/store/auth.js +0 -3
- package/store/catalog.js +60 -8
- package/store/type-map.js +42 -3
- package/tsconfig.paths.json +1 -0
- package/types/resources/pod.ts +18 -0
- package/types/shell/index.d.ts +8539 -2938
- package/types/store/dashboard-store.types.ts +5 -0
- package/types/store/pagination.types.ts +6 -0
- package/utils/__tests__/git.test.ts +270 -0
- package/utils/__tests__/inactivity.test.ts +316 -0
- package/utils/__tests__/object.test.ts +77 -0
- package/utils/__tests__/time.test.ts +14 -1
- package/utils/__tests__/url.test.ts +246 -0
- package/utils/axios.js +1 -4
- package/utils/dynamic-importer.js +3 -2
- package/utils/object.js +33 -2
- package/utils/pagination-utils.ts +1 -1
- package/utils/time.ts +5 -0
- package/utils/uiplugins.ts +12 -16
- package/utils/validators/__tests__/private-registry.test.ts +76 -0
- package/utils/validators/private-registry.ts +28 -0
- package/vue.config.js +0 -9
- package/assets/images/providers/azuread-black.svg +0 -22
- package/assets/images/providers/azuread.svg +0 -25
- package/assets/images/vendor/azuread.svg +0 -18
- package/assets/styles/fonts/_dots.scss +0 -18
- package/components/EmberPage.vue +0 -622
- package/components/EmberPageView.vue +0 -39
- package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
- package/mixins/labeled-form-element.ts +0 -225
- package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
- package/pages/c/_cluster/manager/pages/_page.vue +0 -22
- package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
- package/plugins/ember-cookie.js +0 -17
- package/utils/ember-page.js +0 -30
package/components/nav/Type.vue
CHANGED
|
@@ -68,8 +68,9 @@ export default {
|
|
|
68
68
|
},
|
|
69
69
|
|
|
70
70
|
isActive() {
|
|
71
|
-
|
|
72
|
-
const
|
|
71
|
+
// Use .path instead of .fullPath to ignore query parameters and hashes when comparing routes
|
|
72
|
+
const typePath = this.$router.resolve(this.typeRoute)?.path.toLowerCase();
|
|
73
|
+
const pagePath = this.$route.path?.toLowerCase();
|
|
73
74
|
const routeMetaNav = this.$route.meta?.nav;
|
|
74
75
|
|
|
75
76
|
// If the route explicitly declares the nav path that should be highlighted, then use that
|
|
@@ -80,14 +81,14 @@ export default {
|
|
|
80
81
|
.replace(':cluster', cluster)
|
|
81
82
|
.replace(':product', product);
|
|
82
83
|
|
|
83
|
-
if (navPath ===
|
|
84
|
+
if (navPath === typePath) {
|
|
84
85
|
return true;
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
if ( !this.type.exact) {
|
|
89
|
-
const typeSplit =
|
|
90
|
-
const pageSplit =
|
|
90
|
+
const typeSplit = typePath.split('/');
|
|
91
|
+
const pageSplit = pagePath.split('/');
|
|
91
92
|
|
|
92
93
|
for (let index = 0; index < typeSplit.length; ++index) {
|
|
93
94
|
if ( index >= pageSplit.length || typeSplit[index] !== pageSplit[index] ) {
|
|
@@ -98,7 +99,7 @@ export default {
|
|
|
98
99
|
return true;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
return
|
|
102
|
+
return typePath === pagePath;
|
|
102
103
|
},
|
|
103
104
|
|
|
104
105
|
typeRoute() {
|
|
@@ -131,7 +132,7 @@ export default {
|
|
|
131
132
|
<router-link
|
|
132
133
|
v-if="type.route"
|
|
133
134
|
:key="type.name"
|
|
134
|
-
v-slot="{ href, navigate,isExactActive }"
|
|
135
|
+
v-slot="{ href, navigate, isExactActive }"
|
|
135
136
|
custom
|
|
136
137
|
:to="typeRoute"
|
|
137
138
|
>
|
|
@@ -36,7 +36,7 @@ const props = defineProps({
|
|
|
36
36
|
|
|
37
37
|
const { loadComponent } = useComponentsMount();
|
|
38
38
|
|
|
39
|
-
const { isPanelEnabled } = usePanelsHandler(
|
|
39
|
+
const { isPanelEnabled } = usePanelsHandler(props);
|
|
40
40
|
const { tabs } = useTabsHandler();
|
|
41
41
|
</script>
|
|
42
42
|
|
|
@@ -66,6 +66,7 @@ const { tabs } = useTabsHandler();
|
|
|
66
66
|
:active="true"
|
|
67
67
|
:height="tab.containerHeight"
|
|
68
68
|
:width="tab.containerWidth"
|
|
69
|
+
:layout="layout"
|
|
69
70
|
v-bind="tab.attrs"
|
|
70
71
|
/>
|
|
71
72
|
</keep-alive>
|
|
@@ -8,6 +8,13 @@ export default {
|
|
|
8
8
|
name: 'WorkspaceSwitcher',
|
|
9
9
|
components: { Select },
|
|
10
10
|
|
|
11
|
+
props: {
|
|
12
|
+
disabled: {
|
|
13
|
+
type: Boolean,
|
|
14
|
+
default: false,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
|
|
11
18
|
computed: {
|
|
12
19
|
...mapState(['allWorkspaces', 'workspace', 'allNamespaces', 'defaultNamespace', 'getActiveNamespaces']),
|
|
13
20
|
|
|
@@ -94,6 +101,7 @@ export default {
|
|
|
94
101
|
label="label"
|
|
95
102
|
:options="options"
|
|
96
103
|
:clearable="false"
|
|
104
|
+
:disabled="disabled"
|
|
97
105
|
:reduce="(opt) => opt.value"
|
|
98
106
|
/>
|
|
99
107
|
<!--button v-shortkey.once="['w']" class="hide" @shortkey="focus()" /-->
|
|
@@ -187,4 +195,9 @@ export default {
|
|
|
187
195
|
.filter :deep() .unlabeled-select INPUT[type='search'] {
|
|
188
196
|
padding: 7px;
|
|
189
197
|
}
|
|
198
|
+
|
|
199
|
+
.filter :deep() .unlabeled-select.disabled {
|
|
200
|
+
opacity: 0.5;
|
|
201
|
+
cursor: not-allowed;
|
|
202
|
+
}
|
|
190
203
|
</style>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import Group from '@shell/components/nav/Group.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: Group', () => {
|
|
5
|
+
it('isOverview ignores query parameters and hash strings when checking active state', () => {
|
|
6
|
+
const group = {
|
|
7
|
+
name: 'test',
|
|
8
|
+
children: [
|
|
9
|
+
{
|
|
10
|
+
route: { name: 'overview-route' },
|
|
11
|
+
overview: true
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const wrapper = shallowMount(Group as any, {
|
|
17
|
+
props: {
|
|
18
|
+
group, canCollapse: true, idPrefix: ''
|
|
19
|
+
},
|
|
20
|
+
global: {
|
|
21
|
+
mocks: {
|
|
22
|
+
$route: { path: '/test/route', fullPath: '/test/route?query=val#hash' },
|
|
23
|
+
$router: {
|
|
24
|
+
resolve: jest.fn().mockReturnValue({ path: '/test/route', fullPath: '/test/route' }),
|
|
25
|
+
getRoutes: jest.fn().mockReturnValue([])
|
|
26
|
+
},
|
|
27
|
+
t: (key: string) => key
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect((wrapper.vm as any).isOverview).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('hasActiveRoute ignores query parameters when checking item paths', () => {
|
|
36
|
+
const group = {
|
|
37
|
+
name: 'test',
|
|
38
|
+
children: [
|
|
39
|
+
{ route: { name: 'child-route', params: {} } }
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const wrapper = shallowMount(Group as any, {
|
|
44
|
+
props: {
|
|
45
|
+
group, canCollapse: true, idPrefix: ''
|
|
46
|
+
},
|
|
47
|
+
global: {
|
|
48
|
+
mocks: {
|
|
49
|
+
$route: {
|
|
50
|
+
params: {},
|
|
51
|
+
hash: '#hash',
|
|
52
|
+
path: '/child/route',
|
|
53
|
+
fullPath: '/child/route?query=val#hash',
|
|
54
|
+
matched: []
|
|
55
|
+
},
|
|
56
|
+
$router: {
|
|
57
|
+
resolve: jest.fn().mockReturnValue({ path: '/child/route', fullPath: '/child/route' }),
|
|
58
|
+
getRoutes: jest.fn().mockReturnValue([])
|
|
59
|
+
},
|
|
60
|
+
t: (key: string) => key
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect((wrapper.vm as any).hasActiveRoute()).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import Header from '@shell/components/nav/Header.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: Header', () => {
|
|
5
|
+
const defaultStoreMock = {
|
|
6
|
+
getters: {
|
|
7
|
+
clusterReady: false,
|
|
8
|
+
isExplorer: false,
|
|
9
|
+
isRancher: false,
|
|
10
|
+
currentCluster: null,
|
|
11
|
+
currentProduct: null,
|
|
12
|
+
rootProduct: { name: 'fleet' },
|
|
13
|
+
backToRancherLink: '',
|
|
14
|
+
backToRancherGlobalLink: '',
|
|
15
|
+
pageActions: [],
|
|
16
|
+
isSingleProduct: false,
|
|
17
|
+
isRancherInHarvester: false,
|
|
18
|
+
showTopLevelMenu: false,
|
|
19
|
+
showWorkspaceSwitcher: true,
|
|
20
|
+
'management/schemaFor': () => null,
|
|
21
|
+
'management/all': () => [],
|
|
22
|
+
'rancher/schemaFor': () => null,
|
|
23
|
+
'rancher/byId': () => null,
|
|
24
|
+
'rancher/all': () => [],
|
|
25
|
+
'auth/principalId': 'test',
|
|
26
|
+
'auth/enabled': false,
|
|
27
|
+
'i18n/withFallback': () => '',
|
|
28
|
+
},
|
|
29
|
+
dispatch: jest.fn(),
|
|
30
|
+
commit: jest.fn(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const defaultRouteMock = {
|
|
34
|
+
name: 'c-cluster-fleet-application-resource',
|
|
35
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo',
|
|
36
|
+
params: { resource: 'fleet.cattle.io.gitrepo' },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const defaultConfigMock = { rancherEnv: 'web' };
|
|
40
|
+
|
|
41
|
+
function createWrapper(routeOverride = {}, storeOverride = {}) {
|
|
42
|
+
const routeMock = {
|
|
43
|
+
...defaultRouteMock,
|
|
44
|
+
...routeOverride,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const storeMock = {
|
|
48
|
+
...defaultStoreMock,
|
|
49
|
+
getters: {
|
|
50
|
+
...defaultStoreMock.getters,
|
|
51
|
+
...storeOverride,
|
|
52
|
+
},
|
|
53
|
+
dispatch: jest.fn(),
|
|
54
|
+
commit: jest.fn(),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return shallowMount(Header as any, {
|
|
58
|
+
global: {
|
|
59
|
+
mocks: {
|
|
60
|
+
$store: storeMock,
|
|
61
|
+
$route: routeMock,
|
|
62
|
+
$config: defaultConfigMock,
|
|
63
|
+
$extension: { getDynamic: jest.fn() },
|
|
64
|
+
},
|
|
65
|
+
stubs: {
|
|
66
|
+
'router-link': { template: '<a><slot /></a>' },
|
|
67
|
+
BrandImage: { template: '<span />' },
|
|
68
|
+
ClusterProviderIcon: { template: '<span />' },
|
|
69
|
+
ClusterBadge: { template: '<span />' },
|
|
70
|
+
TopLevelMenu: { template: '<div />' },
|
|
71
|
+
NamespaceFilter: { template: '<div />' },
|
|
72
|
+
WorkspaceSwitcher: { template: '<div />' },
|
|
73
|
+
IconOrSvg: { template: '<span />' },
|
|
74
|
+
AppModal: { template: '<div />' },
|
|
75
|
+
NotificationCenter: { template: '<div />' },
|
|
76
|
+
HeaderPageActionMenu: { template: '<div />' },
|
|
77
|
+
RcDropdown: { template: '<div><slot /><slot name="dropdownCollection" /></div>' },
|
|
78
|
+
RcDropdownItem: { template: '<div><slot /></div>' },
|
|
79
|
+
RcDropdownSeparator: { template: '<hr />' },
|
|
80
|
+
RcDropdownTrigger: { template: '<button><slot /></button>' },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
describe('disableWorkspaceSwitcher', () => {
|
|
87
|
+
it('should return false on a list page', () => {
|
|
88
|
+
const wrapper = createWrapper({
|
|
89
|
+
name: 'c-cluster-fleet-application-resource',
|
|
90
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo',
|
|
91
|
+
params: { resource: 'fleet.cattle.io.gitrepo' },
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect((wrapper.vm as any).disableWorkspaceSwitcher).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should return true on a detail page (route has an id param)', () => {
|
|
98
|
+
const wrapper = createWrapper({
|
|
99
|
+
name: 'c-cluster-fleet-application-resource-namespace-id',
|
|
100
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo/fleet-default/my-repo',
|
|
101
|
+
params: {
|
|
102
|
+
resource: 'fleet.cattle.io.gitrepo', namespace: 'fleet-default', id: 'my-repo'
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect((wrapper.vm as any).disableWorkspaceSwitcher).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should return true on a create page (route name ends with -create)', () => {
|
|
110
|
+
const wrapper = createWrapper({
|
|
111
|
+
name: 'c-cluster-fleet-application-resource-create',
|
|
112
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo/create',
|
|
113
|
+
params: { resource: 'fleet.cattle.io.gitrepo' },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect((wrapper.vm as any).disableWorkspaceSwitcher).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should return true on the application create page', () => {
|
|
120
|
+
const wrapper = createWrapper({
|
|
121
|
+
name: 'c-cluster-fleet-application-create',
|
|
122
|
+
path: '/c/local/fleet/application/create',
|
|
123
|
+
params: {},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect((wrapper.vm as any).disableWorkspaceSwitcher).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should return false on the Workspaces list page', () => {
|
|
130
|
+
const wrapper = createWrapper({
|
|
131
|
+
name: 'c-cluster-fleet-application-resource',
|
|
132
|
+
path: '/c/local/fleet/application/management.cattle.io.fleetworkspace',
|
|
133
|
+
params: { resource: 'management.cattle.io.fleetworkspace' },
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect((wrapper.vm as any).disableWorkspaceSwitcher).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should return false on a non-workspace resource list page', () => {
|
|
140
|
+
const wrapper = createWrapper({
|
|
141
|
+
name: 'c-cluster-fleet-application-resource',
|
|
142
|
+
path: '/c/local/fleet/application/fleet.cattle.io.cluster',
|
|
143
|
+
params: { resource: 'fleet.cattle.io.cluster' },
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect((wrapper.vm as any).disableWorkspaceSwitcher).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should return true on an edit page (route has an id param)', () => {
|
|
150
|
+
const wrapper = createWrapper({
|
|
151
|
+
name: 'c-cluster-fleet-application-resource-namespace-id',
|
|
152
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo/fleet-default/my-repo?mode=edit',
|
|
153
|
+
params: {
|
|
154
|
+
resource: 'fleet.cattle.io.gitrepo', namespace: 'fleet-default', id: 'my-repo'
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect((wrapper.vm as any).disableWorkspaceSwitcher).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('showFilter', () => {
|
|
163
|
+
it('should return true on a list page when showWorkspaceSwitcher is enabled', () => {
|
|
164
|
+
const wrapper = createWrapper(
|
|
165
|
+
{
|
|
166
|
+
name: 'c-cluster-fleet-application-resource',
|
|
167
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo',
|
|
168
|
+
params: { resource: 'fleet.cattle.io.gitrepo' },
|
|
169
|
+
},
|
|
170
|
+
{ currentProduct: { showWorkspaceSwitcher: true } },
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect((wrapper.vm as any).showFilter).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should return true when showWorkspaceSwitcher is enabled on a detail page (switcher visible but disabled)', () => {
|
|
177
|
+
const wrapper = createWrapper(
|
|
178
|
+
{
|
|
179
|
+
name: 'c-cluster-fleet-application-resource-namespace-id',
|
|
180
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo/fleet-default/my-repo',
|
|
181
|
+
params: {
|
|
182
|
+
resource: 'fleet.cattle.io.gitrepo', namespace: 'fleet-default', id: 'my-repo'
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{ currentProduct: { showWorkspaceSwitcher: true } },
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect((wrapper.vm as any).showFilter).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should return true when showWorkspaceSwitcher is enabled on a create page (switcher visible but disabled)', () => {
|
|
192
|
+
const wrapper = createWrapper(
|
|
193
|
+
{
|
|
194
|
+
name: 'c-cluster-fleet-application-resource-create',
|
|
195
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo/create',
|
|
196
|
+
params: { resource: 'fleet.cattle.io.gitrepo' },
|
|
197
|
+
},
|
|
198
|
+
{ currentProduct: { showWorkspaceSwitcher: true } },
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect((wrapper.vm as any).showFilter).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should return false when showWorkspaceSwitcher is false in the store (e.g. Workspaces page)', () => {
|
|
205
|
+
const wrapper = createWrapper(
|
|
206
|
+
{
|
|
207
|
+
name: 'c-cluster-fleet-application-resource',
|
|
208
|
+
path: '/c/local/fleet/application/management.cattle.io.fleetworkspace',
|
|
209
|
+
params: { resource: 'management.cattle.io.fleetworkspace' },
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
currentProduct: { showWorkspaceSwitcher: true },
|
|
213
|
+
showWorkspaceSwitcher: false,
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
expect((wrapper.vm as any).showFilter).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should return true when showNamespaceFilter is enabled regardless of route', () => {
|
|
221
|
+
const wrapper = createWrapper(
|
|
222
|
+
{
|
|
223
|
+
name: 'c-cluster-fleet-application-resource-namespace-id',
|
|
224
|
+
path: '/c/local/fleet/application/fleet.cattle.io.gitrepo/fleet-default/my-repo',
|
|
225
|
+
params: {
|
|
226
|
+
resource: 'fleet.cattle.io.gitrepo', namespace: 'fleet-default', id: 'my-repo'
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
{ currentCluster: { id: 'local' }, currentProduct: { showNamespaceFilter: true } },
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
expect((wrapper.vm as any).showFilter).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
@@ -28,10 +28,10 @@ describe('component: Type', () => {
|
|
|
28
28
|
};
|
|
29
29
|
const routerMock = {
|
|
30
30
|
resolve: jest.fn((route) => {
|
|
31
|
-
return { fullPath: route };
|
|
31
|
+
return { fullPath: route, path: route };
|
|
32
32
|
})
|
|
33
33
|
};
|
|
34
|
-
const routeMock = { fullPath: 'route' };
|
|
34
|
+
const routeMock = { fullPath: 'route', path: 'route' };
|
|
35
35
|
|
|
36
36
|
describe('should pass props correctly', () => {
|
|
37
37
|
it('should forward Type props to router-link', () => {
|
|
@@ -104,7 +104,7 @@ describe('component: Type', () => {
|
|
|
104
104
|
directives: { cleanHtml: (identity) => identity },
|
|
105
105
|
|
|
106
106
|
mocks: {
|
|
107
|
-
$store: storeMock, $router: routerMock, $route: { fullPath: 'bad' }
|
|
107
|
+
$store: storeMock, $router: routerMock, $route: { fullPath: 'bad', path: 'bad' }
|
|
108
108
|
},
|
|
109
109
|
stubs: { routerLink: createChildRenderingRouterLinkStub() },
|
|
110
110
|
},
|
|
@@ -152,6 +152,23 @@ describe('component: Type', () => {
|
|
|
152
152
|
|
|
153
153
|
expect(elementWithSelector.exists()).toBe(false);
|
|
154
154
|
});
|
|
155
|
+
it('should use active and exact active classes when route matches but includes query and hash', () => {
|
|
156
|
+
const wrapper = shallowMount(Type as any, {
|
|
157
|
+
props: { type: defaultRouteTypeProp, highlightRoute: true },
|
|
158
|
+
|
|
159
|
+
global: {
|
|
160
|
+
directives: { cleanHtml: (identity) => identity },
|
|
161
|
+
|
|
162
|
+
mocks: {
|
|
163
|
+
$store: storeMock, $router: routerMock, $route: { fullPath: 'route?repo=test#myhash', path: 'route' }
|
|
164
|
+
},
|
|
165
|
+
stubs: { routerLink: createChildRenderingRouterLinkStub({ isExactActive: true }) },
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(wrapper.find(`.${ activeClass }`).exists()).toBe(true);
|
|
170
|
+
expect(wrapper.find(`.${ exactActiveClass }`).exists()).toBe(true);
|
|
171
|
+
});
|
|
155
172
|
});
|
|
156
173
|
|
|
157
174
|
describe('should use classes if preconditions are met', () => {
|
|
@@ -9,7 +9,6 @@ import ActionMenu from '@shell/components/ActionMenu';
|
|
|
9
9
|
import GrowlManager from '@shell/components/GrowlManager';
|
|
10
10
|
import ModalManager from '@shell/components/ModalManager';
|
|
11
11
|
import SlideInPanelManager from '@shell/components/SlideInPanelManager';
|
|
12
|
-
import WindowManager from '@shell/components/nav/WindowManager';
|
|
13
12
|
import PromptRemove from '@shell/components/PromptRemove';
|
|
14
13
|
import PromptRestore from '@shell/components/PromptRestore';
|
|
15
14
|
import PromptModal from '@shell/components/PromptModal';
|
|
@@ -39,7 +38,6 @@ export default {
|
|
|
39
38
|
GrowlManager,
|
|
40
39
|
ModalManager,
|
|
41
40
|
SlideInPanelManager,
|
|
42
|
-
WindowManager,
|
|
43
41
|
FixedBanner,
|
|
44
42
|
AwsComplianceBanner,
|
|
45
43
|
Inactivity,
|
|
@@ -48,10 +46,11 @@ export default {
|
|
|
48
46
|
|
|
49
47
|
mixins: [PageHeaderActions, Brand, BrowserTabVisibility],
|
|
50
48
|
|
|
49
|
+
inject: ['notifyWmContainerReady'],
|
|
50
|
+
|
|
51
51
|
// Note - This will not run on route change
|
|
52
52
|
data() {
|
|
53
53
|
return {
|
|
54
|
-
layout: Layout.default,
|
|
55
54
|
noLocaleShortcut: process.env.dev || false,
|
|
56
55
|
wantNavSync: false,
|
|
57
56
|
};
|
|
@@ -104,6 +103,10 @@ export default {
|
|
|
104
103
|
},
|
|
105
104
|
},
|
|
106
105
|
|
|
106
|
+
mounted() {
|
|
107
|
+
this.notifyWmContainerReady(Layout.default);
|
|
108
|
+
},
|
|
109
|
+
|
|
107
110
|
methods: {
|
|
108
111
|
|
|
109
112
|
handlePageAction(action) {
|
|
@@ -159,6 +162,10 @@ export default {
|
|
|
159
162
|
|
|
160
163
|
<template>
|
|
161
164
|
<div class="dashboard-root">
|
|
165
|
+
<a
|
|
166
|
+
href="#main-content"
|
|
167
|
+
class="skip-to-content btn role-primary"
|
|
168
|
+
>{{ t('nav.skipToContent') }}</a>
|
|
162
169
|
<FixedBanner :header="true" />
|
|
163
170
|
<AwsComplianceBanner v-if="managementReady" />
|
|
164
171
|
<div
|
|
@@ -173,8 +180,10 @@ export default {
|
|
|
173
180
|
/>
|
|
174
181
|
<main
|
|
175
182
|
v-if="clusterAndRouteReady"
|
|
183
|
+
id="main-content"
|
|
176
184
|
class="main-layout"
|
|
177
185
|
:aria-label="t('layouts.default')"
|
|
186
|
+
tabindex="-1"
|
|
178
187
|
>
|
|
179
188
|
<router-view
|
|
180
189
|
:key="$route.path"
|
|
@@ -211,15 +220,22 @@ export default {
|
|
|
211
220
|
<!-- Ensure there's an outlet to show the error (404) page -->
|
|
212
221
|
<main
|
|
213
222
|
v-else-if="unmatchedRoute"
|
|
223
|
+
id="main-content"
|
|
214
224
|
class="main-layout"
|
|
215
225
|
:aria-label="t('layouts.default')"
|
|
226
|
+
tabindex="-1"
|
|
216
227
|
>
|
|
217
228
|
<router-view
|
|
218
229
|
:key="$route.path"
|
|
219
230
|
class="outlet"
|
|
220
231
|
/>
|
|
221
232
|
</main>
|
|
222
|
-
|
|
233
|
+
<!-- Teleport target for WindowManager (unique per layout) -->
|
|
234
|
+
<!-- display: contents makes child panels become grid items of the parent grid -->
|
|
235
|
+
<div
|
|
236
|
+
id="wm-container-default"
|
|
237
|
+
style="display: contents;"
|
|
238
|
+
/>
|
|
223
239
|
</div>
|
|
224
240
|
<FixedBanner :footer="true" />
|
|
225
241
|
<GrowlManager />
|
|
@@ -227,3 +243,17 @@ export default {
|
|
|
227
243
|
<Inactivity />
|
|
228
244
|
</div>
|
|
229
245
|
</template>
|
|
246
|
+
|
|
247
|
+
<style lang="scss" scoped>
|
|
248
|
+
.skip-to-content {
|
|
249
|
+
position: fixed;
|
|
250
|
+
top: 0;
|
|
251
|
+
left: 0;
|
|
252
|
+
z-index: 9999;
|
|
253
|
+
transform: translateY(-100%);
|
|
254
|
+
|
|
255
|
+
&:focus {
|
|
256
|
+
transform: translate(1rem, 1rem);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
</style>
|
|
@@ -11,7 +11,6 @@ import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
|
11
11
|
import Inactivity from '@shell/components/Inactivity';
|
|
12
12
|
import { mapState, mapGetters } from 'vuex';
|
|
13
13
|
import PromptModal from '@shell/components/PromptModal';
|
|
14
|
-
import WindowManager from '@shell/components/nav/WindowManager';
|
|
15
14
|
import { Layout } from '@shell/types/window-manager';
|
|
16
15
|
|
|
17
16
|
export default {
|
|
@@ -25,14 +24,14 @@ export default {
|
|
|
25
24
|
AwsComplianceBanner,
|
|
26
25
|
Inactivity,
|
|
27
26
|
PromptModal,
|
|
28
|
-
WindowManager
|
|
29
27
|
},
|
|
30
28
|
|
|
31
29
|
mixins: [Brand, BrowserTabVisibility],
|
|
32
30
|
|
|
31
|
+
inject: ['notifyWmContainerReady'],
|
|
32
|
+
|
|
33
33
|
data() {
|
|
34
34
|
return {
|
|
35
|
-
layout: Layout.home,
|
|
36
35
|
// Assume home pages have routes where the name is the key to use for string lookup
|
|
37
36
|
name: this.$route.name,
|
|
38
37
|
noLocaleShortcut: process.env.dev || false,
|
|
@@ -45,6 +44,10 @@ export default {
|
|
|
45
44
|
...mapGetters(['showTopLevelMenu']),
|
|
46
45
|
},
|
|
47
46
|
|
|
47
|
+
mounted() {
|
|
48
|
+
this.notifyWmContainerReady(Layout.home);
|
|
49
|
+
},
|
|
50
|
+
|
|
48
51
|
methods: {
|
|
49
52
|
toggleTheme() {
|
|
50
53
|
this.$store.dispatch('prefs/toggleTheme');
|
|
@@ -82,7 +85,12 @@ export default {
|
|
|
82
85
|
class="outlet"
|
|
83
86
|
/>
|
|
84
87
|
</main>
|
|
85
|
-
|
|
88
|
+
<!-- Teleport target for WindowManager (unique per layout) -->
|
|
89
|
+
<!-- display: contents makes child panels become grid items of the parent grid -->
|
|
90
|
+
<div
|
|
91
|
+
id="wm-container-home"
|
|
92
|
+
style="display: contents;"
|
|
93
|
+
/>
|
|
86
94
|
</div>
|
|
87
95
|
<FixedBanner :footer="true" />
|
|
88
96
|
<GrowlManager />
|
|
@@ -125,27 +133,6 @@ export default {
|
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
|
|
128
|
-
.wm {
|
|
129
|
-
grid-area: wm;
|
|
130
|
-
overflow-y: hidden;
|
|
131
|
-
z-index: z-index('windowsManager');
|
|
132
|
-
position: relative;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.wm-vr {
|
|
136
|
-
grid-area: wm-vr;
|
|
137
|
-
overflow-y: hidden;
|
|
138
|
-
z-index: z-index('windowsManager');
|
|
139
|
-
position: relative;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.wm-vl {
|
|
143
|
-
grid-area: wm-vl;
|
|
144
|
-
overflow-y: hidden;
|
|
145
|
-
z-index: z-index('windowsManager');
|
|
146
|
-
position: relative;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
136
|
MAIN {
|
|
150
137
|
grid-area: main;
|
|
151
138
|
overflow: auto;
|