@rancher/shell 3.0.12-rc.1 → 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/translations/en-us.yaml +19 -17
- package/assets/translations/zh-hans.yaml +4 -8
- 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/SelectIconGrid.vue +5 -0
- package/components/__tests__/CountBox.test.ts +72 -0
- package/components/__tests__/DetailText.test.ts +113 -0
- package/components/fleet/FleetClusterTargets/index.vue +18 -1
- package/components/form/InputWithSelect.vue +18 -10
- package/components/form/KeyValue.vue +17 -1
- package/components/form/LabeledSelect.vue +82 -24
- 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/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
- package/components/nav/Group.vue +7 -6
- package/components/nav/Header.vue +24 -3
- 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/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/query-params.js +1 -0
- package/config/router/routes.js +0 -8
- package/core/__tests__/plugin-products.test.ts +616 -25
- package/core/plugin-products-base.ts +31 -14
- package/core/plugin-products-helpers.ts +5 -4
- package/core/plugin-types.ts +18 -3
- package/core/types.ts +3 -1
- package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
- package/detail/management.cattle.io.fleetworkspace.vue +49 -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/index.vue +70 -99
- 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/machine-config/amazonec2.vue +1 -0
- package/mixins/chart.js +40 -9
- 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/package.json +13 -12
- 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/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 +15 -10
- package/pages/c/_cluster/uiplugins/index.vue +23 -25
- 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/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/auth.js +0 -3
- package/store/catalog.js +60 -8
- package/types/shell/index.d.ts +26 -22
- 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/object.js +33 -2
- package/utils/time.ts +5 -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
|
@@ -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;
|
|
@@ -14,7 +14,6 @@ import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
|
14
14
|
import Inactivity from '@shell/components/Inactivity';
|
|
15
15
|
import { mapGetters } from 'vuex';
|
|
16
16
|
import PromptModal from '@shell/components/PromptModal';
|
|
17
|
-
import WindowManager from '@shell/components/nav/WindowManager';
|
|
18
17
|
import { Layout } from '@shell/types/window-manager';
|
|
19
18
|
|
|
20
19
|
export default {
|
|
@@ -31,17 +30,17 @@ export default {
|
|
|
31
30
|
SlideInPanelManager,
|
|
32
31
|
AwsComplianceBanner,
|
|
33
32
|
Inactivity,
|
|
34
|
-
WindowManager
|
|
35
33
|
},
|
|
36
34
|
|
|
37
35
|
mixins: [Brand, BrowserTabVisibility],
|
|
38
36
|
|
|
37
|
+
inject: ['notifyWmContainerReady'],
|
|
38
|
+
|
|
39
39
|
data() {
|
|
40
40
|
return {
|
|
41
41
|
// Assume home pages have routes where the name is the key to use for string lookup
|
|
42
42
|
name: this.$route.name,
|
|
43
|
-
noLocaleShortcut: process.env.dev || false
|
|
44
|
-
layout: Layout.plain,
|
|
43
|
+
noLocaleShortcut: process.env.dev || false
|
|
45
44
|
};
|
|
46
45
|
},
|
|
47
46
|
|
|
@@ -50,6 +49,10 @@ export default {
|
|
|
50
49
|
...mapGetters(['showTopLevelMenu']),
|
|
51
50
|
},
|
|
52
51
|
|
|
52
|
+
mounted() {
|
|
53
|
+
this.notifyWmContainerReady(Layout.plain);
|
|
54
|
+
},
|
|
55
|
+
|
|
53
56
|
methods: {
|
|
54
57
|
toggleTheme() {
|
|
55
58
|
this.$store.dispatch('prefs/toggleTheme');
|
|
@@ -98,7 +101,12 @@ export default {
|
|
|
98
101
|
@shortkey="toggleNoneLocale()"
|
|
99
102
|
/>
|
|
100
103
|
</main>
|
|
101
|
-
|
|
104
|
+
<!-- Teleport target for WindowManager (unique per layout) -->
|
|
105
|
+
<!-- display: contents makes child panels become grid items of the parent grid -->
|
|
106
|
+
<div
|
|
107
|
+
id="wm-container-plain"
|
|
108
|
+
style="display: contents;"
|
|
109
|
+
/>
|
|
102
110
|
</div>
|
|
103
111
|
|
|
104
112
|
<FixedBanner :footer="true" />
|
|
@@ -130,27 +138,6 @@ export default {
|
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
|
|
133
|
-
.wm {
|
|
134
|
-
grid-area: wm;
|
|
135
|
-
overflow-y: hidden;
|
|
136
|
-
z-index: z-index('windowsManager');
|
|
137
|
-
position: relative;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.wm-vr {
|
|
141
|
-
grid-area: wm-vr;
|
|
142
|
-
overflow-y: hidden;
|
|
143
|
-
z-index: z-index('windowsManager');
|
|
144
|
-
position: relative;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.wm-vl {
|
|
148
|
-
grid-area: wm-vl;
|
|
149
|
-
overflow-y: hidden;
|
|
150
|
-
z-index: z-index('windowsManager');
|
|
151
|
-
position: relative;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
141
|
MAIN {
|
|
155
142
|
grid-area: main;
|
|
156
143
|
overflow: auto;
|
|
@@ -6,7 +6,7 @@ import { _VIEW, _EDIT } from '@shell/config/query-params';
|
|
|
6
6
|
|
|
7
7
|
interface LabeledFormElementProps {
|
|
8
8
|
mode: string;
|
|
9
|
-
value: string | number | Record<string, any>
|
|
9
|
+
value: string | number | Record<string, any> | null
|
|
10
10
|
required: boolean;
|
|
11
11
|
disabled: boolean;
|
|
12
12
|
rules: Array<any>;
|
|
@@ -18,6 +18,8 @@ interface UseLabeledFormElement {
|
|
|
18
18
|
focused: Ref<boolean>;
|
|
19
19
|
blurred: Ref<number | null>;
|
|
20
20
|
requiredField: ComputedRef<any>;
|
|
21
|
+
empty: ComputedRef<boolean>;
|
|
22
|
+
isView: ComputedRef<boolean>;
|
|
21
23
|
isDisabled: ComputedRef<any>;
|
|
22
24
|
validationMessage: ComputedRef<any>;
|
|
23
25
|
onFocusLabeled: () => void;
|
|
@@ -46,7 +48,7 @@ export const labeledFormElementProps = {
|
|
|
46
48
|
default: null
|
|
47
49
|
},
|
|
48
50
|
value: {
|
|
49
|
-
type: [String, Number, Object],
|
|
51
|
+
type: [String, Number, Object, null],
|
|
50
52
|
default: ''
|
|
51
53
|
},
|
|
52
54
|
mode: {
|
|
@@ -82,6 +84,10 @@ export const useLabeledFormElement = (props: LabeledFormElementProps, emit: Emit
|
|
|
82
84
|
return props.required || props.rules?.some((rule: any) => rule?.name === 'required');
|
|
83
85
|
});
|
|
84
86
|
|
|
87
|
+
const empty = computed(() => {
|
|
88
|
+
return !!`${ props.value }`;
|
|
89
|
+
});
|
|
90
|
+
|
|
85
91
|
const isView = computed(() => {
|
|
86
92
|
return props.mode === _VIEW;
|
|
87
93
|
});
|
|
@@ -143,6 +149,8 @@ export const useLabeledFormElement = (props: LabeledFormElementProps, emit: Emit
|
|
|
143
149
|
raised,
|
|
144
150
|
focused,
|
|
145
151
|
blurred,
|
|
152
|
+
empty,
|
|
153
|
+
isView,
|
|
146
154
|
onFocusLabeled,
|
|
147
155
|
onBlurLabeled,
|
|
148
156
|
isDisabled,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { computed, ComputedRef, Ref, nextTick } from 'vue';
|
|
2
|
+
import { getWidth, setWidth } from '@shell/utils/width';
|
|
3
|
+
|
|
4
|
+
interface LabeledSelectProps {
|
|
5
|
+
options?: Array<any>;
|
|
6
|
+
searchable?: boolean;
|
|
7
|
+
filterable?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface UseLabeledSelect {
|
|
11
|
+
isSearchable: ComputedRef<boolean>;
|
|
12
|
+
isFilterable: ComputedRef<boolean>;
|
|
13
|
+
resizeHandler: (selectRef: Ref<HTMLElement | null>) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const useLabeledSelect = (props: LabeledSelectProps, canPaginate?: ComputedRef<boolean>): UseLabeledSelect => {
|
|
17
|
+
const isSearchable = computed(() => {
|
|
18
|
+
if (canPaginate?.value) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
const options = props.options || [];
|
|
22
|
+
|
|
23
|
+
if (props.searchable || options.length >= 10) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return false;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const isFilterable = computed(() => {
|
|
31
|
+
if (canPaginate?.value) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return props.filterable ?? true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const resizeHandler = (selectRef: Ref<HTMLElement | null>) => {
|
|
39
|
+
// since the DD is positioned there is no way to 'inherit' the size of the input, this calcs the size of the parent and set the dd width if it is smaller. If not let it grow with the regular styles
|
|
40
|
+
nextTick(() => {
|
|
41
|
+
if (!selectRef.value) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DD = selectRef.value.querySelector('ul.vs__dropdown-menu');
|
|
46
|
+
const selectWidth = getWidth(selectRef.value) || 0;
|
|
47
|
+
const dropWidth = getWidth(DD as Element) || 0;
|
|
48
|
+
|
|
49
|
+
if (dropWidth < selectWidth) {
|
|
50
|
+
setWidth(DD as Element, selectWidth);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
isSearchable,
|
|
57
|
+
isFilterable,
|
|
58
|
+
resizeHandler
|
|
59
|
+
};
|
|
60
|
+
};
|