@rancher/shell 3.0.7 → 3.0.8-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/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +1 -1
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_modern.scss +5 -5
- package/assets/translations/en-us.yaml +102 -17
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/EmberPage.vue +1 -1
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/Resource/Detail/CopyToClipboard.vue +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/ResourceDetail/index.vue +4 -1
- package/components/SortableTable/index.vue +18 -2
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/nav/Header.vue +34 -13
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/config/product/auth.js +1 -0
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/store.js +4 -2
- package/config/types.js +2 -0
- package/detail/pod.vue +1 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/directives/ui-context.ts +97 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +94 -65
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/initialize/install-directives.js +2 -0
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/chart.js +1 -1
- package/models/__tests__/chart.test.ts +17 -9
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +3 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/management.cattle.io.authconfig.js +1 -0
- package/package.json +2 -2
- package/pages/auth/login.vue +5 -2
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/chart.vue +2 -2
- package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/home.vue +313 -12
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/resource-class.js +17 -2
- package/plugins/steve/steve-pagination-utils.ts +2 -2
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- package/scripts/extension/publish +1 -1
- package/store/auth.js +8 -3
- package/store/aws.js +8 -6
- package/store/features.js +1 -0
- package/store/index.js +9 -3
- package/store/prefs.js +6 -0
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +59 -0
- package/types/window-manager.ts +22 -0
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/cluster.js +157 -3
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +263 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/index.ts +273 -0
- package/utils/dynamic-content/info.ts +219 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +101 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/dynamic-importer.js +2 -2
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +19 -4
- package/utils/release-notes.ts +1 -1
- package/assets/images/icons/document.svg +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -12,6 +12,8 @@ import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
|
12
12
|
import Inactivity from '@shell/components/Inactivity';
|
|
13
13
|
import { mapState, mapGetters } from 'vuex';
|
|
14
14
|
import PromptModal from '@shell/components/PromptModal';
|
|
15
|
+
import WindowManager from '@shell/components/nav/WindowManager';
|
|
16
|
+
import { Layout } from '@shell/types/window-manager';
|
|
15
17
|
|
|
16
18
|
export default {
|
|
17
19
|
|
|
@@ -24,16 +26,18 @@ export default {
|
|
|
24
26
|
AzureWarning,
|
|
25
27
|
AwsComplianceBanner,
|
|
26
28
|
Inactivity,
|
|
27
|
-
PromptModal
|
|
29
|
+
PromptModal,
|
|
30
|
+
WindowManager
|
|
28
31
|
},
|
|
29
32
|
|
|
30
33
|
mixins: [Brand, BrowserTabVisibility],
|
|
31
34
|
|
|
32
35
|
data() {
|
|
33
36
|
return {
|
|
37
|
+
layout: Layout.home,
|
|
34
38
|
// Assume home pages have routes where the name is the key to use for string lookup
|
|
35
39
|
name: this.$route.name,
|
|
36
|
-
noLocaleShortcut: process.env.dev || false
|
|
40
|
+
noLocaleShortcut: process.env.dev || false,
|
|
37
41
|
};
|
|
38
42
|
},
|
|
39
43
|
|
|
@@ -81,6 +85,7 @@ export default {
|
|
|
81
85
|
class="outlet"
|
|
82
86
|
/>
|
|
83
87
|
</main>
|
|
88
|
+
<WindowManager :layout="layout" />
|
|
84
89
|
</div>
|
|
85
90
|
<FixedBanner :footer="true" />
|
|
86
91
|
<GrowlManager />
|
|
@@ -112,10 +117,10 @@ export default {
|
|
|
112
117
|
flex-grow:1;
|
|
113
118
|
|
|
114
119
|
grid-template-areas:
|
|
115
|
-
"header"
|
|
116
|
-
"main";
|
|
120
|
+
"header header header"
|
|
121
|
+
"wm-vl main wm-vr";
|
|
117
122
|
|
|
118
|
-
grid-template-columns: auto;
|
|
123
|
+
grid-template-columns: var(--wm-vl-width, 0px) auto var(--wm-vr-width, 0px);
|
|
119
124
|
grid-template-rows: var(--header-height) auto;
|
|
120
125
|
|
|
121
126
|
> HEADER {
|
|
@@ -123,6 +128,27 @@ export default {
|
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
130
|
|
|
131
|
+
.wm {
|
|
132
|
+
grid-area: wm;
|
|
133
|
+
overflow-y: hidden;
|
|
134
|
+
z-index: z-index('windowsManager');
|
|
135
|
+
position: relative;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.wm-vr {
|
|
139
|
+
grid-area: wm-vr;
|
|
140
|
+
overflow-y: hidden;
|
|
141
|
+
z-index: z-index('windowsManager');
|
|
142
|
+
position: relative;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.wm-vl {
|
|
146
|
+
grid-area: wm-vl;
|
|
147
|
+
overflow-y: hidden;
|
|
148
|
+
z-index: z-index('windowsManager');
|
|
149
|
+
position: relative;
|
|
150
|
+
}
|
|
151
|
+
|
|
126
152
|
MAIN {
|
|
127
153
|
grid-area: main;
|
|
128
154
|
overflow: auto;
|
package/config/product/auth.js
CHANGED
|
@@ -177,6 +177,7 @@ export function init(store) {
|
|
|
177
177
|
});
|
|
178
178
|
|
|
179
179
|
componentForType(`${ MANAGEMENT.AUTH_CONFIG }/github`, 'auth/github');
|
|
180
|
+
componentForType(`${ MANAGEMENT.AUTH_CONFIG }/githubapp`, 'auth/github');
|
|
180
181
|
componentForType(`${ MANAGEMENT.AUTH_CONFIG }/openldap`, 'auth/ldap/index');
|
|
181
182
|
componentForType(`${ MANAGEMENT.AUTH_CONFIG }/freeipa`, 'auth/ldap/index');
|
|
182
183
|
componentForType(`${ MANAGEMENT.AUTH_CONFIG }/activedirectory`, 'auth/ldap/index');
|
package/config/query-params.js
CHANGED
|
@@ -8,6 +8,7 @@ export const STEP = 'step';
|
|
|
8
8
|
export const LOGGED_OUT = 'logged-out';
|
|
9
9
|
export const IS_SSO = 'is-sso';
|
|
10
10
|
export const IS_SLO = 'is-slo';
|
|
11
|
+
export const IS_SESSION_IDLE = 'is-session-idle';
|
|
11
12
|
export const UPGRADED = 'upgraded';
|
|
12
13
|
export const TIMED_OUT = 'timed-out';
|
|
13
14
|
export const AUTH_TEST = 'test';
|
package/config/settings.ts
CHANGED
|
@@ -52,6 +52,7 @@ export const SETTING = {
|
|
|
52
52
|
RKE_METADATA_CONFIG: 'rke-metadata-config',
|
|
53
53
|
EULA_AGREED: 'eula-agreed',
|
|
54
54
|
AUTH_USER_INFO_MAX_AGE_SECONDS: 'auth-user-info-max-age-seconds',
|
|
55
|
+
AUTH_USER_SESSION_IDLE_TTL_MINUTES: 'auth-user-session-idle-ttl-minutes',
|
|
55
56
|
AUTH_USER_SESSION_TTL_MINUTES: 'auth-user-session-ttl-minutes',
|
|
56
57
|
AUTH_USER_INFO_RESYNC_CRON: 'auth-user-info-resync-cron',
|
|
57
58
|
AUTH_LOCAL_VALIDATE_DESC: 'auth-password-requirements-description',
|
|
@@ -109,7 +110,12 @@ export const SETTING = {
|
|
|
109
110
|
SYSTEM_AGENT_UPGRADER_INSTALL_CONCURRENCY: 'system-agent-upgrader-install-concurrency',
|
|
110
111
|
IMPORTED_CLUSTER_VERSION_MANAGEMENT: 'imported-cluster-version-management',
|
|
111
112
|
CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS: 'cluster-agent-default-priority-class',
|
|
112
|
-
CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET: 'cluster-agent-default-pod-disruption-budget'
|
|
113
|
+
CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET: 'cluster-agent-default-pod-disruption-budget',
|
|
114
|
+
/**
|
|
115
|
+
* Dynamic Content settings
|
|
116
|
+
*/
|
|
117
|
+
DYNAMIC_CONTENT_ENABLED: 'ui-content-enabled',
|
|
118
|
+
DYNAMIC_CONTENT_ENDPOINT: 'ui-content-endpoint',
|
|
113
119
|
} as const;
|
|
114
120
|
|
|
115
121
|
// These are the settings that are allowed to be edited via the UI
|
|
@@ -140,6 +146,7 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
|
|
|
140
146
|
},
|
|
141
147
|
[SETTING.INGRESS_IP_DOMAIN]: {},
|
|
142
148
|
[SETTING.AUTH_USER_INFO_MAX_AGE_SECONDS]: {},
|
|
149
|
+
[SETTING.AUTH_USER_SESSION_IDLE_TTL_MINUTES]: {},
|
|
143
150
|
[SETTING.AUTH_USER_SESSION_TTL_MINUTES]: {},
|
|
144
151
|
[SETTING.AUTH_TOKEN_MAX_TTL_MINUTES]: {},
|
|
145
152
|
[SETTING.KUBECONFIG_GENERATE_TOKEN]: { kind: 'boolean' },
|
package/config/store.js
CHANGED
|
@@ -35,11 +35,12 @@ let store = {};
|
|
|
35
35
|
resolveStoreModules(require('../store/slideInPanel.ts'), 'slideInPanel.ts');
|
|
36
36
|
resolveStoreModules(require('../store/type-map.js'), 'type-map.js');
|
|
37
37
|
resolveStoreModules(require('../store/uiplugins.ts'), 'uiplugins.ts');
|
|
38
|
-
resolveStoreModules(require('../store/wm.
|
|
38
|
+
resolveStoreModules(require('../store/wm.ts'), 'wm.ts');
|
|
39
39
|
resolveStoreModules(require('../store/customisation.js'), 'customisation.js');
|
|
40
40
|
resolveStoreModules(require('../store/cru-resource.ts'), 'cru-resource.ts');
|
|
41
41
|
resolveStoreModules(require('../store/notifications.ts'), 'notifications.ts');
|
|
42
42
|
resolveStoreModules(require('../store/cookies.ts'), 'cookies.ts');
|
|
43
|
+
resolveStoreModules(require('../store/ui-context.ts'), 'ui-context.ts');
|
|
43
44
|
|
|
44
45
|
// If the environment supports hot reloading...
|
|
45
46
|
|
|
@@ -66,11 +67,12 @@ let store = {};
|
|
|
66
67
|
'../store/slideInPanel.ts',
|
|
67
68
|
'../store/type-map.js',
|
|
68
69
|
'../store/uiplugins.ts',
|
|
69
|
-
'../store/wm.
|
|
70
|
+
'../store/wm.ts',
|
|
70
71
|
'../store/customisation.js',
|
|
71
72
|
'../store/cru-resource.ts',
|
|
72
73
|
'../store/notifications.ts',
|
|
73
74
|
'../store/cookies.ts',
|
|
75
|
+
'../store/ui-context.ts',
|
|
74
76
|
], () => {
|
|
75
77
|
// Update `root.modules` with the latest definitions.
|
|
76
78
|
updateModules();
|
package/config/types.js
CHANGED
|
@@ -222,6 +222,8 @@ export const MANAGEMENT = {
|
|
|
222
222
|
OIDC_CLIENT: 'management.cattle.io.oidcclient'
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
+
export const EXT = { USER_ACTIVITY: 'ext.cattle.io.useractivity' };
|
|
226
|
+
|
|
225
227
|
export const CAPI = {
|
|
226
228
|
CAPI_CLUSTER: 'cluster.x-k8s.io.cluster',
|
|
227
229
|
MACHINE_DEPLOYMENT: 'cluster.x-k8s.io.machinedeployment',
|
package/detail/pod.vue
CHANGED
|
@@ -97,6 +97,7 @@ export default {
|
|
|
97
97
|
|
|
98
98
|
return {
|
|
99
99
|
...container,
|
|
100
|
+
kind: 'Container',
|
|
100
101
|
status: status || {},
|
|
101
102
|
stateDisplay: status ? this.value.containerStateDisplay(status) : undefined,
|
|
102
103
|
stateBackground: status ? this.value.containerStateColor(status).replace('text', 'bg') : undefined,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import AsyncButton from '@shell/components/AsyncButton';
|
|
3
3
|
import { Card } from '@components/Card';
|
|
4
|
+
import { mapGetters } from 'vuex';
|
|
5
|
+
|
|
6
|
+
import { labelForAddon } from '@shell/utils/cluster';
|
|
7
|
+
import { resourceNames } from '@shell/utils/string';
|
|
4
8
|
|
|
5
9
|
export default {
|
|
6
10
|
emits: ['close'],
|
|
@@ -17,11 +21,45 @@ export default {
|
|
|
17
21
|
registerBackgroundClosing: {
|
|
18
22
|
type: Function,
|
|
19
23
|
required: true
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* The names of the addons that have configuration conflicts.
|
|
27
|
+
*/
|
|
28
|
+
addonNames: {
|
|
29
|
+
type: Array,
|
|
30
|
+
default: () => []
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* The Kubernetes version the user is upgrading from.
|
|
34
|
+
*/
|
|
35
|
+
previousKubeVersion: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: ''
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* The Kubernetes version the user is upgrading to.
|
|
41
|
+
*/
|
|
42
|
+
newKubeVersion: {
|
|
43
|
+
type: String,
|
|
44
|
+
default: ''
|
|
20
45
|
}
|
|
21
46
|
},
|
|
22
47
|
created() {
|
|
23
48
|
this.registerBackgroundClosing(this.closing);
|
|
24
49
|
},
|
|
50
|
+
computed: {
|
|
51
|
+
...mapGetters({ t: 'i18n/t' }),
|
|
52
|
+
|
|
53
|
+
formattedAddons() {
|
|
54
|
+
if (!this.addonNames || this.addonNames.length === 0) {
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const translatedNames = this.addonNames.map((name) => labelForAddon(this.$store, name, true));
|
|
59
|
+
|
|
60
|
+
return resourceNames(translatedNames, null, this.t, false);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
25
63
|
methods: {
|
|
26
64
|
continue(value) {
|
|
27
65
|
if (this.resources[0]) {
|
|
@@ -59,7 +97,13 @@ export default {
|
|
|
59
97
|
|
|
60
98
|
<template #body>
|
|
61
99
|
<slot name="body">
|
|
62
|
-
|
|
100
|
+
<span
|
|
101
|
+
v-clean-html="t('addonConfigConfirmation.body', {
|
|
102
|
+
addons: formattedAddons,
|
|
103
|
+
previousKubeVersion,
|
|
104
|
+
newKubeVersion
|
|
105
|
+
}, true)"
|
|
106
|
+
/>
|
|
63
107
|
</slot>
|
|
64
108
|
</template>
|
|
65
109
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { get, isEmpty } from '@shell/utils/object';
|
|
2
|
+
import { generateRandomAlphaString } from '@shell/utils/string';
|
|
3
|
+
|
|
4
|
+
interface Context {
|
|
5
|
+
tag: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
value?: any;
|
|
8
|
+
hookable?: boolean;
|
|
9
|
+
description?: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
store?: any;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isValid(context: Context ): context is Context {
|
|
15
|
+
return (
|
|
16
|
+
context !== null &&
|
|
17
|
+
typeof context === 'object' &&
|
|
18
|
+
(
|
|
19
|
+
(context.path === undefined && context.value === undefined) || // both undefined is allowed
|
|
20
|
+
(typeof context.path === 'string' && context.path?.length > 0 && context.value === undefined) || // path defined, value undefined
|
|
21
|
+
(context.value !== undefined && context.path === undefined) // value defined, path undefined
|
|
22
|
+
) &&
|
|
23
|
+
typeof context.tag === 'string' && context.tag?.length > 0 &&
|
|
24
|
+
(typeof context.description === 'string' || context.description === undefined)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** UI Context Directive
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* <div v-ui-context="{ tag: 'example-tag', path: 'some.path.in.component', description: 'An example context' }"></div>
|
|
32
|
+
* or
|
|
33
|
+
* <div v-ui-context="{ tag: 'example-tag', value: 'Direct Value', description: 'An example context with direct value' }"></div>
|
|
34
|
+
* or
|
|
35
|
+
* <div v-ui-context="{ tag: 'example-tag', description: 'An example context with text content' }">Text Content Value</div>
|
|
36
|
+
*
|
|
37
|
+
* The directive will register the context on mount and unregister it before unmounting.
|
|
38
|
+
*
|
|
39
|
+
* The context object must have a 'tag' and either a 'path' or a 'value'.
|
|
40
|
+
* If both 'path' and 'value' are undefined, the element's textContent will be used as the value.
|
|
41
|
+
*
|
|
42
|
+
* !IMPORTANT:
|
|
43
|
+
* The value object will be reactive only when using 'path'.
|
|
44
|
+
* Using 'value' or textContent will not be reactive.
|
|
45
|
+
*
|
|
46
|
+
*/
|
|
47
|
+
export default {
|
|
48
|
+
async mounted(el: any, binding: { value: Context, instance: any }) {
|
|
49
|
+
const context: Context = binding.value;
|
|
50
|
+
|
|
51
|
+
if (!context || isEmpty(context)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!isValid(context)) {
|
|
56
|
+
throw new Error(`Invalid ui-context value: ${ JSON.stringify({ tag: (context as Context).tag, description: (context as Context).description }) }`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (context.path === undefined && context.value === undefined) {
|
|
60
|
+
// path and value undefined, use textContent as value
|
|
61
|
+
context.value = el.textContent;
|
|
62
|
+
} else if (context.value === undefined) {
|
|
63
|
+
// Use context.value directly if provided, otherwise resolve it from the component instance using context.path
|
|
64
|
+
const value = get(binding.instance, context.path);
|
|
65
|
+
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
throw new Error(`[ui-context] path "${ context.path }" is undefined on the component instance`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
context.value = value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
delete context.path;
|
|
74
|
+
|
|
75
|
+
if (context.hookable) {
|
|
76
|
+
const hookId = generateRandomAlphaString(12);
|
|
77
|
+
|
|
78
|
+
el.setAttribute('ux-context-hook-id', hookId);
|
|
79
|
+
(context as any).hookId = hookId;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const store = context.store || binding.instance.$store || binding.instance.store;
|
|
83
|
+
|
|
84
|
+
if (store) {
|
|
85
|
+
delete context.store;
|
|
86
|
+
el._uiContextId = await store.dispatch('ui-context/add', context);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
async beforeUnmount(el: any, binding: { value: Context, instance: any }) {
|
|
91
|
+
const store = binding.value?.store || binding.instance.$store || binding.instance.store;
|
|
92
|
+
|
|
93
|
+
if (store && el._uiContextId) {
|
|
94
|
+
await store.dispatch('ui-context/remove', el._uiContextId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
@@ -2,18 +2,22 @@ import { mount } from '@vue/test-utils';
|
|
|
2
2
|
import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
|
|
3
3
|
import HelmOp from '@shell/models/fleet.cattle.io.helmop';
|
|
4
4
|
import HelmOpComponent from '@shell/edit/fleet.cattle.io.helmop.vue';
|
|
5
|
+
import FleetSecretSelector from '@shell/components/fleet/FleetSecretSelector.vue';
|
|
6
|
+
import FleetConfigMapSelector from '@shell/components/fleet/FleetConfigMapSelector.vue';
|
|
5
7
|
|
|
6
8
|
const mockStore = {
|
|
7
9
|
dispatch: jest.fn(),
|
|
8
10
|
commit: jest.fn(),
|
|
9
11
|
getters: {
|
|
10
|
-
'i18n/t':
|
|
11
|
-
'i18n/exists':
|
|
12
|
-
t:
|
|
13
|
-
currentStore:
|
|
14
|
-
'current_store/schemaFor':
|
|
15
|
-
'current_store/all':
|
|
16
|
-
workspace:
|
|
12
|
+
'i18n/t': (text: string) => text,
|
|
13
|
+
'i18n/exists': jest.fn(),
|
|
14
|
+
t: (text: string) => text,
|
|
15
|
+
currentStore: () => 'current_store',
|
|
16
|
+
'current_store/schemaFor': jest.fn(),
|
|
17
|
+
'current_store/all': jest.fn(),
|
|
18
|
+
workspace: 'test',
|
|
19
|
+
'management/paginationEnabled': () => false,
|
|
20
|
+
'management/all': () => [],
|
|
17
21
|
},
|
|
18
22
|
rootGetters: { 'i18n/t': jest.fn() },
|
|
19
23
|
};
|
|
@@ -63,19 +67,22 @@ const mockHelmOp = {
|
|
|
63
67
|
currentRoute: () => {},
|
|
64
68
|
};
|
|
65
69
|
|
|
66
|
-
const initHelmOp = (props: any,
|
|
67
|
-
const
|
|
70
|
+
const initHelmOp = (props: any, options = {}) => {
|
|
71
|
+
const value = new HelmOp({
|
|
68
72
|
...mockHelmOp,
|
|
69
|
-
...
|
|
73
|
+
...options
|
|
70
74
|
}, {
|
|
71
75
|
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
72
76
|
dispatch: jest.fn(),
|
|
73
77
|
rootGetters: { 'i18n/t': jest.fn() },
|
|
74
78
|
});
|
|
75
79
|
|
|
80
|
+
value.applyDefaults = () => {};
|
|
81
|
+
value.metadata = { namespace: '' };
|
|
82
|
+
|
|
76
83
|
return {
|
|
77
84
|
props: {
|
|
78
|
-
value
|
|
85
|
+
value,
|
|
79
86
|
...props
|
|
80
87
|
},
|
|
81
88
|
computed: mockComputed,
|
|
@@ -221,4 +228,38 @@ describe.each([
|
|
|
221
228
|
|
|
222
229
|
expect(pollingIntervalInput.value).toBe(displayValue);
|
|
223
230
|
});
|
|
231
|
+
|
|
232
|
+
it('should update downstreamResources with new Secrets when FleetSecretSelector emits update event', async() => {
|
|
233
|
+
const wrapper = mount(HelmOpComponent, initHelmOp({ realMode: mode }));
|
|
234
|
+
|
|
235
|
+
const fleetConfigMapSelector = wrapper.findComponent(FleetConfigMapSelector);
|
|
236
|
+
const fleetSecretSelector = wrapper.findComponent(FleetSecretSelector);
|
|
237
|
+
|
|
238
|
+
expect(fleetSecretSelector.exists()).toBe(true);
|
|
239
|
+
expect(fleetConfigMapSelector.exists()).toBe(true);
|
|
240
|
+
|
|
241
|
+
await fleetSecretSelector.vm.$emit('update:value', []);
|
|
242
|
+
await fleetConfigMapSelector.vm.$emit('update:value', []);
|
|
243
|
+
|
|
244
|
+
await fleetSecretSelector.vm.$emit('update:value', ['secret2', 'secret3']);
|
|
245
|
+
|
|
246
|
+
expect(wrapper.vm.value.spec.helm.downstreamResources).toStrictEqual([{ name: 'secret2', kind: 'Secret' }, { name: 'secret3', kind: 'Secret' }]);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should update downstreamResources with new ConfigMaps when FleetConfigMapSelector emits update event', async() => {
|
|
250
|
+
const wrapper = mount(HelmOpComponent, initHelmOp({ realMode: mode }));
|
|
251
|
+
|
|
252
|
+
const fleetConfigMapSelector = wrapper.findComponent(FleetConfigMapSelector);
|
|
253
|
+
const fleetSecretSelector = wrapper.findComponent(FleetSecretSelector);
|
|
254
|
+
|
|
255
|
+
expect(fleetSecretSelector.exists()).toBe(true);
|
|
256
|
+
expect(fleetConfigMapSelector.exists()).toBe(true);
|
|
257
|
+
|
|
258
|
+
await fleetSecretSelector.vm.$emit('update:value', []);
|
|
259
|
+
await fleetConfigMapSelector.vm.$emit('update:value', []);
|
|
260
|
+
|
|
261
|
+
await fleetConfigMapSelector.vm.$emit('update:value', ['configMap2', 'configMap3']);
|
|
262
|
+
|
|
263
|
+
expect(wrapper.vm.value.spec.helm.downstreamResources).toStrictEqual([{ name: 'configMap2', kind: 'ConfigMap' }, { name: 'configMap3', kind: 'ConfigMap' }]);
|
|
264
|
+
});
|
|
224
265
|
});
|
|
@@ -28,7 +28,20 @@ export default defineComponent({
|
|
|
28
28
|
color="warning"
|
|
29
29
|
data-testid="auth-provider-admin-permissions-warning-banner"
|
|
30
30
|
>
|
|
31
|
-
<span
|
|
31
|
+
<span class="banner-content">
|
|
32
|
+
<span v-clean-html="t('authConfig.associatedWarning', tArgs, true)" />
|
|
33
|
+
<slot name="additional-warning">
|
|
34
|
+
<!--Empty slot content-->
|
|
35
|
+
</slot>
|
|
36
|
+
</span>
|
|
32
37
|
</Banner>
|
|
33
38
|
</div>
|
|
34
39
|
</template>
|
|
40
|
+
|
|
41
|
+
<style lang="scss" scoped>
|
|
42
|
+
.banner-content {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
gap: 1rem;
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import InfoBox from '@shell/components/InfoBox';
|
|
3
|
+
import CopyToClipboard from '@shell/components/CopyToClipboard';
|
|
4
|
+
|
|
5
|
+
type TArgs = {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
provider: string;
|
|
9
|
+
username: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
defineProps<{
|
|
13
|
+
name: string;
|
|
14
|
+
tArgs: TArgs;
|
|
15
|
+
}>();
|
|
16
|
+
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<InfoBox
|
|
21
|
+
:step="1"
|
|
22
|
+
class="step-box"
|
|
23
|
+
>
|
|
24
|
+
<ul class="step-list">
|
|
25
|
+
<li v-clean-html="t(`authConfig.${name}.form.prefix.1`, tArgs, true)" />
|
|
26
|
+
<li v-clean-html="t(`authConfig.${name}.form.prefix.2`, tArgs, true)" />
|
|
27
|
+
<li v-clean-html="t(`authConfig.${name}.form.prefix.3`, tArgs, true)" />
|
|
28
|
+
<li v-clean-html="t(`authConfig.${name}.form.prefix.4`, tArgs, true)" />
|
|
29
|
+
</ul>
|
|
30
|
+
</InfoBox>
|
|
31
|
+
<InfoBox
|
|
32
|
+
:step="2"
|
|
33
|
+
class="step-box"
|
|
34
|
+
>
|
|
35
|
+
<ul class="step-list">
|
|
36
|
+
<li>
|
|
37
|
+
{{ t(`authConfig.${name}.form.instruction`, tArgs, true) }}
|
|
38
|
+
<ul class="mt-10">
|
|
39
|
+
<li>
|
|
40
|
+
<b>
|
|
41
|
+
{{ t(`authConfig.${name}.form.app.label`) }}
|
|
42
|
+
</b>:
|
|
43
|
+
<span v-clean-html="t(`authConfig.${name}.form.app.value`, tArgs, true)" />
|
|
44
|
+
</li>
|
|
45
|
+
<li>
|
|
46
|
+
<b>
|
|
47
|
+
{{ t(`authConfig.${name}.form.homepage.label`) }}
|
|
48
|
+
</b>: {{ tArgs.serverUrl }}
|
|
49
|
+
<CopyToClipboard
|
|
50
|
+
label-as="tooltip"
|
|
51
|
+
:text="tArgs.serverUrl"
|
|
52
|
+
class="icon-btn"
|
|
53
|
+
action-color="bg-transparent"
|
|
54
|
+
/>
|
|
55
|
+
</li>
|
|
56
|
+
<li>
|
|
57
|
+
<b>
|
|
58
|
+
{{ t(`authConfig.${name}.form.description.label`) }}
|
|
59
|
+
</b>:
|
|
60
|
+
<span v-clean-html="t(`authConfig.${name}.form.description.value`, tArgs, true)" />
|
|
61
|
+
</li>
|
|
62
|
+
<li>
|
|
63
|
+
<b>
|
|
64
|
+
{{ t(`authConfig.${name}.form.callback.label`) }}
|
|
65
|
+
</b>:
|
|
66
|
+
{{ t(`authConfig.${name}.form.callback.value`, tArgs, true) }}
|
|
67
|
+
<CopyToClipboard
|
|
68
|
+
:text="t(`authConfig.${name}.form.callback.value`, tArgs, true)"
|
|
69
|
+
label-as="tooltip"
|
|
70
|
+
class="icon-btn"
|
|
71
|
+
action-color="bg-transparent"
|
|
72
|
+
/>
|
|
73
|
+
</li>
|
|
74
|
+
</ul>
|
|
75
|
+
</li>
|
|
76
|
+
<li>
|
|
77
|
+
{{ t(`authConfig.${name}.form.create`, tArgs, true) }}
|
|
78
|
+
</li>
|
|
79
|
+
</ul>
|
|
80
|
+
</InfoBox>
|
|
81
|
+
<InfoBox
|
|
82
|
+
:step="3"
|
|
83
|
+
class="mb-20"
|
|
84
|
+
>
|
|
85
|
+
<ul class="step-list">
|
|
86
|
+
<li v-clean-html="t(`authConfig.${name}.form.suffix.1`, tArgs, true)" />
|
|
87
|
+
<li v-clean-html="t(`authConfig.${name}.form.suffix.2`, tArgs, true)" />
|
|
88
|
+
<li v-clean-html="t(`authConfig.${name}.form.suffix.3`, tArgs, true)" />
|
|
89
|
+
</ul>
|
|
90
|
+
</InfoBox>
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<style lang="scss" scoped>
|
|
94
|
+
.step-list li:not(:last-child) {
|
|
95
|
+
margin-bottom: 8px;
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import InfoBox from '@shell/components/InfoBox';
|
|
3
|
+
import CopyToClipboard from '@shell/components/CopyToClipboard';
|
|
4
|
+
|
|
5
|
+
type TArgs = {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
provider: string;
|
|
9
|
+
username: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
defineProps<{
|
|
13
|
+
name: string;
|
|
14
|
+
tArgs: TArgs;
|
|
15
|
+
}>();
|
|
16
|
+
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<InfoBox
|
|
21
|
+
:step="1"
|
|
22
|
+
class="step-box"
|
|
23
|
+
>
|
|
24
|
+
<ul class="step-list">
|
|
25
|
+
<li v-clean-html="t(`authConfig.${name}.form.prefix.1`, tArgs, true)" />
|
|
26
|
+
<li v-clean-html="t(`authConfig.${name}.form.prefix.2`, tArgs, true)" />
|
|
27
|
+
<li v-clean-html="t(`authConfig.${name}.form.prefix.3`, tArgs, true)" />
|
|
28
|
+
</ul>
|
|
29
|
+
</InfoBox>
|
|
30
|
+
<InfoBox
|
|
31
|
+
:step="2"
|
|
32
|
+
class="step-box"
|
|
33
|
+
>
|
|
34
|
+
<ul class="step-list">
|
|
35
|
+
<li>
|
|
36
|
+
{{ t(`authConfig.${name}.form.instruction`, tArgs, true) }}
|
|
37
|
+
<ul class="mt-10">
|
|
38
|
+
<li><b>{{ t(`authConfig.${name}.form.app.label`) }}</b>: <span v-clean-html="t(`authConfig.${name}.form.app.value`, tArgs, true)" /></li>
|
|
39
|
+
<li>
|
|
40
|
+
<b>{{ t(`authConfig.${name}.form.homepage.label`) }}</b>: {{ tArgs.serverUrl }} <CopyToClipboard
|
|
41
|
+
label-as="tooltip"
|
|
42
|
+
:text="tArgs.serverUrl"
|
|
43
|
+
class="icon-btn"
|
|
44
|
+
action-color="bg-transparent"
|
|
45
|
+
/>
|
|
46
|
+
</li>
|
|
47
|
+
<li><b>{{ t(`authConfig.${name}.form.description.label`) }}</b>: <span v-clean-html="t(`authConfig.${name}.form.description.value`, tArgs, true)" /></li>
|
|
48
|
+
<li>
|
|
49
|
+
<b>{{ t(`authConfig.${name}.form.callback.label`) }}</b>: {{ tArgs.serverUrl }} <CopyToClipboard
|
|
50
|
+
:text="tArgs.serverUrl"
|
|
51
|
+
label-as="tooltip"
|
|
52
|
+
class="icon-btn"
|
|
53
|
+
action-color="bg-transparent"
|
|
54
|
+
/>
|
|
55
|
+
</li>
|
|
56
|
+
</ul>
|
|
57
|
+
</li>
|
|
58
|
+
</ul>
|
|
59
|
+
</InfoBox>
|
|
60
|
+
<InfoBox
|
|
61
|
+
:step="3"
|
|
62
|
+
class="mb-20"
|
|
63
|
+
>
|
|
64
|
+
<ul class="step-list">
|
|
65
|
+
<li v-clean-html="t(`authConfig.${name}.form.suffix.1`, tArgs, true)" />
|
|
66
|
+
<li v-clean-html="t(`authConfig.${name}.form.suffix.2`, tArgs, true)" />
|
|
67
|
+
</ul>
|
|
68
|
+
</InfoBox>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<style lang="scss" scoped>
|
|
72
|
+
.step-list li:not(:last-child) {
|
|
73
|
+
margin-bottom: 8px;
|
|
74
|
+
}
|
|
75
|
+
</style>
|