@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.
Files changed (123) hide show
  1. package/assets/images/vendor/githubapp.svg +13 -0
  2. package/assets/styles/base/_typography.scss +1 -1
  3. package/assets/styles/global/_layout.scss +21 -35
  4. package/assets/styles/themes/_modern.scss +5 -5
  5. package/assets/translations/en-us.yaml +102 -17
  6. package/assets/translations/zh-hans.yaml +0 -4
  7. package/components/EmberPage.vue +1 -1
  8. package/components/Inactivity.vue +222 -106
  9. package/components/InstallHelmCharts.vue +2 -2
  10. package/components/Resource/Detail/CopyToClipboard.vue +1 -1
  11. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  12. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  13. package/components/ResourceDetail/index.vue +4 -1
  14. package/components/SortableTable/index.vue +18 -2
  15. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  16. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  17. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  18. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  19. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  20. package/components/fleet/FleetSecretSelector.vue +127 -0
  21. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  22. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  23. package/components/form/FileImageSelector.vue +13 -4
  24. package/components/form/FileSelector.vue +11 -2
  25. package/components/form/ResourceLabeledSelect.vue +1 -0
  26. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  27. package/components/nav/Header.vue +34 -13
  28. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  29. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  30. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  31. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  32. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  33. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  34. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  35. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  36. package/components/nav/WindowManager/constants.ts +23 -0
  37. package/components/nav/WindowManager/index.vue +61 -575
  38. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  39. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  40. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  41. package/components/templates/default.vue +4 -40
  42. package/components/templates/home.vue +31 -5
  43. package/config/product/auth.js +1 -0
  44. package/config/query-params.js +1 -0
  45. package/config/settings.ts +8 -1
  46. package/config/store.js +4 -2
  47. package/config/types.js +2 -0
  48. package/detail/pod.vue +1 -0
  49. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  50. package/directives/ui-context.ts +97 -0
  51. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  52. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  53. package/edit/auth/github-app-steps.vue +97 -0
  54. package/edit/auth/github-steps.vue +75 -0
  55. package/edit/auth/github.vue +94 -65
  56. package/edit/fleet.cattle.io.helmop.vue +51 -2
  57. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  58. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  59. package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
  60. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  61. package/initialize/install-directives.js +2 -0
  62. package/list/projectsecret.vue +1 -1
  63. package/machine-config/azure.vue +1 -1
  64. package/mixins/chart.js +1 -1
  65. package/models/__tests__/chart.test.ts +17 -9
  66. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  67. package/models/catalog.cattle.io.app.js +1 -1
  68. package/models/chart.js +3 -1
  69. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  70. package/models/management.cattle.io.authconfig.js +1 -0
  71. package/package.json +2 -2
  72. package/pages/auth/login.vue +5 -2
  73. package/pages/auth/verify.vue +1 -1
  74. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  75. package/pages/c/_cluster/apps/charts/chart.vue +2 -2
  76. package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
  77. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  78. package/pages/c/_cluster/settings/performance.vue +12 -25
  79. package/pages/home.vue +313 -12
  80. package/plugins/axios.js +2 -1
  81. package/plugins/dashboard-store/actions.js +1 -1
  82. package/plugins/dashboard-store/resource-class.js +17 -2
  83. package/plugins/steve/steve-pagination-utils.ts +2 -2
  84. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  85. package/scripts/extension/publish +1 -1
  86. package/store/auth.js +8 -3
  87. package/store/aws.js +8 -6
  88. package/store/features.js +1 -0
  89. package/store/index.js +9 -3
  90. package/store/prefs.js +6 -0
  91. package/store/ui-context.ts +86 -0
  92. package/store/wm.ts +244 -0
  93. package/types/kube/kube-api.ts +2 -1
  94. package/types/rancher/index.d.ts +1 -0
  95. package/types/resources/settings.d.ts +29 -7
  96. package/types/shell/index.d.ts +59 -0
  97. package/types/window-manager.ts +22 -0
  98. package/utils/__tests__/cluster.test.ts +379 -1
  99. package/utils/cluster.js +157 -3
  100. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  101. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  102. package/utils/dynamic-content/__tests__/info.test.ts +263 -0
  103. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  104. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  105. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  106. package/utils/dynamic-content/config.ts +55 -0
  107. package/utils/dynamic-content/index.ts +273 -0
  108. package/utils/dynamic-content/info.ts +219 -0
  109. package/utils/dynamic-content/new-release.ts +126 -0
  110. package/utils/dynamic-content/support-notice.ts +169 -0
  111. package/utils/dynamic-content/types.d.ts +101 -0
  112. package/utils/dynamic-content/util.ts +122 -0
  113. package/utils/dynamic-importer.js +2 -2
  114. package/utils/inactivity.ts +104 -0
  115. package/utils/pagination-utils.ts +19 -4
  116. package/utils/release-notes.ts +1 -1
  117. package/assets/images/icons/document.svg +0 -3
  118. package/store/wm.js +0 -95
  119. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  120. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  121. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  122. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  123. /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;
@@ -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');
@@ -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';
@@ -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.js'), 'wm.js');
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.js',
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
- {{ t('addonConfigConfirmation.body') }}
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': (text: string) => text,
11
- 'i18n/exists': jest.fn(),
12
- t: (text: string) => text,
13
- currentStore: () => 'current_store',
14
- 'current_store/schemaFor': jest.fn(),
15
- 'current_store/all': jest.fn(),
16
- workspace: 'test',
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, value?: any) => {
67
- const initValue = new HelmOp({
70
+ const initHelmOp = (props: any, options = {}) => {
71
+ const value = new HelmOp({
68
72
  ...mockHelmOp,
69
- ...(value || {})
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: initValue,
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 v-clean-html="t('authConfig.associatedWarning', tArgs, true)" />
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>