@rancher/shell 3.0.6 → 3.0.8-rc.1

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 (146) hide show
  1. package/assets/images/pl/dark/rancher-logo.svg +131 -44
  2. package/assets/images/pl/rancher-logo.svg +120 -44
  3. package/assets/images/vendor/githubapp.svg +13 -0
  4. package/assets/styles/base/_basic.scss +2 -2
  5. package/assets/styles/base/_color-classic.scss +51 -0
  6. package/assets/styles/base/_color.scss +3 -3
  7. package/assets/styles/base/_mixins.scss +1 -1
  8. package/assets/styles/base/_typography.scss +1 -1
  9. package/assets/styles/base/_variables-classic.scss +47 -0
  10. package/assets/styles/global/_button.scss +49 -17
  11. package/assets/styles/global/_form.scss +1 -1
  12. package/assets/styles/themes/_dark.scss +4 -0
  13. package/assets/styles/themes/_light.scss +3 -69
  14. package/assets/styles/themes/_modern.scss +194 -50
  15. package/assets/styles/vendor/vue-select.scss +1 -2
  16. package/assets/translations/en-us.yaml +124 -32
  17. package/assets/translations/zh-hans.yaml +0 -4
  18. package/components/ClusterIconMenu.vue +1 -1
  19. package/components/ClusterProviderIcon.vue +1 -1
  20. package/components/CodeMirror.vue +1 -1
  21. package/components/IconOrSvg.vue +40 -29
  22. package/components/Inactivity.vue +222 -106
  23. package/components/InstallHelmCharts.vue +2 -2
  24. package/components/ResourceDetail/index.vue +2 -1
  25. package/components/SortableTable/index.vue +17 -2
  26. package/components/SortableTable/sorting.js +3 -1
  27. package/components/Tabbed/index.vue +5 -5
  28. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  29. package/components/fleet/FleetSecretSelector.vue +127 -0
  30. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  31. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  32. package/components/form/FileImageSelector.vue +13 -4
  33. package/components/form/FileSelector.vue +11 -2
  34. package/components/form/ResourceLabeledSelect.vue +1 -0
  35. package/components/form/ResourceTabs/index.vue +37 -18
  36. package/components/form/SecretSelector.vue +6 -2
  37. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  38. package/components/nav/Group.vue +29 -9
  39. package/components/nav/Header.vue +7 -8
  40. package/components/nav/NamespaceFilter.vue +1 -1
  41. package/components/nav/TopLevelMenu.helper.ts +47 -20
  42. package/components/nav/TopLevelMenu.vue +44 -14
  43. package/components/nav/Type.vue +0 -5
  44. package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
  45. package/config/pagination-table-headers.js +10 -2
  46. package/config/product/auth.js +1 -0
  47. package/config/product/explorer.js +4 -3
  48. package/config/query-params.js +1 -0
  49. package/config/settings.ts +8 -1
  50. package/config/table-headers.js +9 -0
  51. package/config/types.js +2 -0
  52. package/core/plugin.ts +18 -6
  53. package/core/types.ts +8 -0
  54. package/detail/provisioning.cattle.io.cluster.vue +1 -0
  55. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  56. package/dialog/InstallExtensionDialog.vue +71 -45
  57. package/dialog/UninstallExtensionDialog.vue +2 -1
  58. package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
  59. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  60. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  61. package/edit/auth/github-app-steps.vue +97 -0
  62. package/edit/auth/github-steps.vue +75 -0
  63. package/edit/auth/github.vue +94 -65
  64. package/edit/auth/oidc.vue +86 -16
  65. package/edit/fleet.cattle.io.helmop.vue +51 -2
  66. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  67. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  68. package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
  69. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  70. package/list/projectsecret.vue +1 -1
  71. package/machine-config/azure.vue +1 -1
  72. package/mixins/__tests__/chart.test.ts +1 -1
  73. package/mixins/chart.js +2 -2
  74. package/models/__tests__/chart.test.ts +17 -9
  75. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  76. package/models/catalog.cattle.io.app.js +1 -1
  77. package/models/chart.js +3 -1
  78. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  79. package/models/event.js +7 -0
  80. package/models/management.cattle.io.authconfig.js +1 -0
  81. package/models/provisioning.cattle.io.cluster.js +9 -0
  82. package/package.json +2 -2
  83. package/pages/auth/login.vue +5 -2
  84. package/pages/auth/verify.vue +1 -1
  85. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  86. package/pages/c/_cluster/apps/charts/chart.vue +2 -2
  87. package/pages/c/_cluster/explorer/EventsTable.vue +92 -9
  88. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  89. package/pages/c/_cluster/settings/performance.vue +13 -26
  90. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
  91. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
  92. package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
  93. package/pages/c/_cluster/uiplugins/index.vue +110 -94
  94. package/pages/home.vue +313 -12
  95. package/plugins/__tests__/subscribe.events.test.ts +194 -0
  96. package/plugins/axios.js +2 -1
  97. package/plugins/dashboard-store/actions.js +4 -1
  98. package/plugins/dashboard-store/getters.js +1 -1
  99. package/plugins/dashboard-store/resource-class.js +20 -5
  100. package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
  101. package/plugins/steve/index.js +18 -10
  102. package/plugins/steve/mutations.js +2 -2
  103. package/plugins/steve/resourceWatcher.js +2 -2
  104. package/plugins/steve/steve-pagination-utils.ts +12 -9
  105. package/plugins/steve/subscribe.js +113 -85
  106. package/plugins/subscribe-events.ts +211 -0
  107. package/rancher-components/BadgeState/BadgeState.vue +8 -6
  108. package/rancher-components/Banner/Banner.vue +2 -1
  109. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  110. package/rancher-components/Form/Radio/RadioButton.vue +3 -3
  111. package/scripts/extension/publish +1 -1
  112. package/store/auth.js +8 -3
  113. package/store/aws.js +8 -6
  114. package/store/features.js +1 -0
  115. package/store/index.js +21 -25
  116. package/store/prefs.js +6 -0
  117. package/types/extension-manager.ts +8 -1
  118. package/types/kube/kube-api.ts +2 -1
  119. package/types/rancher/index.d.ts +1 -0
  120. package/types/resources/settings.d.ts +52 -23
  121. package/types/shell/index.d.ts +412 -336
  122. package/types/store/subscribe-events.types.ts +70 -0
  123. package/types/store/subscribe.types.ts +6 -22
  124. package/utils/__tests__/cluster.test.ts +379 -1
  125. package/utils/cluster.js +157 -3
  126. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  127. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  128. package/utils/dynamic-content/__tests__/info.test.ts +263 -0
  129. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  130. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  131. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  132. package/utils/dynamic-content/config.ts +55 -0
  133. package/utils/dynamic-content/index.ts +273 -0
  134. package/utils/dynamic-content/info.ts +219 -0
  135. package/utils/dynamic-content/new-release.ts +126 -0
  136. package/utils/dynamic-content/support-notice.ts +169 -0
  137. package/utils/dynamic-content/types.d.ts +101 -0
  138. package/utils/dynamic-content/util.ts +122 -0
  139. package/utils/inactivity.ts +104 -0
  140. package/utils/pagination-utils.ts +105 -31
  141. package/utils/pagination-wrapper.ts +6 -8
  142. package/utils/release-notes.ts +1 -1
  143. package/utils/sort.js +5 -0
  144. package/utils/unit-tests/pagination-utils.spec.ts +283 -0
  145. package/utils/validators/formRules/__tests__/index.test.ts +7 -0
  146. package/utils/validators/formRules/index.ts +2 -2
@@ -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
 
@@ -3,12 +3,12 @@ import AsyncButton from '@shell/components/AsyncButton';
3
3
  import LabeledSelect from '@shell/components/form/LabeledSelect';
4
4
  import { CATALOG, MANAGEMENT } from '@shell/config/types';
5
5
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
6
- import { UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins';
6
+ import { UI_PLUGIN_NAMESPACE, isChartVersionHigher } from '@shell/config/uiplugins';
7
7
  import Banner from '@components/Banner/Banner.vue';
8
8
  import { SETTING } from '@shell/config/settings';
9
- import { getPluginChartVersion, getPluginChartVersionLabel } from '@shell/utils/uiplugins';
9
+ import { getPluginChartVersionLabel } from '@shell/utils/uiplugins';
10
10
 
11
- // Note: This dialog handles installation and update of a plugin
11
+ // Note: This dialog handles installation, upgrade and downgrade of a plugin
12
12
 
13
13
  export default {
14
14
  emits: ['close'],
@@ -29,7 +29,14 @@ export default {
29
29
  required: true
30
30
  },
31
31
  /**
32
- * The action to perform (install, update, rollback)
32
+ * The pre-selected version in the dropdown
33
+ */
34
+ initialVersion: {
35
+ type: String,
36
+ default: null
37
+ },
38
+ /**
39
+ * The action to perform (install, upgrade, downgrade)
33
40
  */
34
41
  action: {
35
42
  type: String,
@@ -63,33 +70,32 @@ export default {
63
70
  },
64
71
 
65
72
  async fetch() {
66
- const chartVersion = getPluginChartVersion(this.plugin);
67
-
68
- // Default to latest version on install (this is default on the plugin)
69
- this.version = chartVersion;
70
-
71
- if (this.action === 'update') {
72
- this.currentVersion = chartVersion;
73
-
74
- // Update to latest version, so take the first version
75
- if (this.plugin?.installableVersions?.length > 0) {
76
- this.version = this.plugin?.installableVersions?.[0]?.version;
77
- }
78
- } else if (this.action === 'rollback') {
79
- // Find the newest version once we remove the current version
80
- const versionNames = this.plugin.installableVersions.filter((v) => v.version !== chartVersion);
73
+ // Determine the currently installed version, if any
74
+ if (this.plugin.installed) {
75
+ this.currentVersion = this.plugin.installedVersion;
76
+ }
81
77
 
82
- this.currentVersion = chartVersion;
78
+ // Determine the initial version to select in the dropdown
79
+ if (this.initialVersion) {
80
+ this.version = this.initialVersion;
81
+ } else if (this.action === 'upgrade') {
82
+ // Upgrade to the latest version, so take the first version
83
+ this.version = this.plugin?.installableVersions?.[0]?.version;
84
+ } else if (this.action === 'downgrade') {
85
+ const versions = this.plugin.installableVersions;
86
+ const currentIndex = versions.findIndex((v) => v.version === this.currentVersion);
83
87
 
84
- if (versionNames.length > 0) {
85
- this.version = versionNames[0].version;
88
+ if (currentIndex !== -1 && currentIndex < versions.length - 1) {
89
+ // Select the version just below the current version
90
+ this.version = versions[currentIndex + 1].version;
86
91
  }
92
+ } else {
93
+ // Default to the latest installable version for new installs
94
+ this.version = this.plugin?.installableVersions?.[0]?.version;
87
95
  }
88
96
 
89
- // Make sure we have the version available
90
- const versionChart = this.plugin?.installableVersions?.find((v) => v.version === this.version);
91
-
92
- if (!versionChart) {
97
+ // Fallback if no version could be determined
98
+ if (!this.version) {
93
99
  this.version = this.plugin?.installableVersions?.[0]?.version;
94
100
  }
95
101
 
@@ -119,37 +125,39 @@ export default {
119
125
  },
120
126
 
121
127
  versionOptions() {
122
- if (!this.plugin) {
128
+ if (!this.plugin?.installableVersions) {
123
129
  return [];
124
130
  }
125
131
 
126
- // Don't allow update/rollback to current version
127
- const versions = this.plugin?.installableVersions?.filter((v) => {
128
- if (this.currentVersion) {
129
- return v.version !== this.currentVersion;
130
- }
131
-
132
- return true;
133
- });
132
+ // Don't allow upgrade/downgrade to current version by disabling the option
133
+ return this.plugin.installableVersions.map((v) => {
134
+ const isCurrent = v.version === this.currentVersion;
134
135
 
135
- return versions.map((version) => {
136
136
  return {
137
- label: getPluginChartVersionLabel(version),
138
- value: version.version,
137
+ label: getPluginChartVersionLabel(v) + (isCurrent ? ` (${ this.t('plugins.labels.current') })` : ''),
138
+ value: v.version,
139
+ disabled: isCurrent,
139
140
  };
140
141
  });
141
142
  },
142
143
 
143
144
  buttonMode() {
144
- if (this.action === 'rollback') {
145
- return 'rollback';
145
+ if (this.action === 'install') {
146
+ return 'install';
146
147
  }
147
148
 
148
- if (this.action === 'update') {
149
- return 'update';
149
+ if (this.currentVersion && this.version) {
150
+ if (isChartVersionHigher(this.version, this.currentVersion)) {
151
+ return 'upgrade';
152
+ }
153
+
154
+ if (isChartVersionHigher(this.currentVersion, this.version)) {
155
+ return 'downgrade';
156
+ }
150
157
  }
151
158
 
152
- return 'install';
159
+ // Fallback for safety, though should not be reached if version is selected
160
+ return this.action;
153
161
  },
154
162
 
155
163
  chartVersionLoadsWithoutAuth() {
@@ -158,6 +166,23 @@ export default {
158
166
 
159
167
  returnFocusSelector() {
160
168
  return `[data-testid="extension-card-${ this.action }-btn-${ this.plugin?.name }"]`;
169
+ },
170
+
171
+ buttonIcon() {
172
+ if (this.busy) {
173
+ return '';
174
+ }
175
+
176
+ switch (this.buttonMode) {
177
+ case 'install':
178
+ return 'icon-plus';
179
+ case 'upgrade':
180
+ return 'icon-upgrade-alt';
181
+ case 'downgrade':
182
+ return 'icon-downgrade-alt';
183
+ default:
184
+ return '';
185
+ }
161
186
  }
162
187
  },
163
188
 
@@ -295,12 +320,12 @@ export default {
295
320
  <template>
296
321
  <div class="plugin-install-dialog">
297
322
  <h4 class="mt-10">
298
- {{ t(`plugins.${ action }.title`, { name: plugin?.label }) }}
323
+ {{ t(`plugins.${ buttonMode }.title`, { name: `"${plugin?.label}"` }, true) }}
299
324
  </h4>
300
325
  <div class="custom mt-10">
301
326
  <div class="dialog-panel">
302
327
  <p>
303
- {{ t(`plugins.${ action }.prompt`) }}
328
+ {{ t(`plugins.${ buttonMode }.prompt`) }}
304
329
  </p>
305
330
  <Banner
306
331
  v-if="chartVersionLoadsWithoutAuth"
@@ -335,6 +360,7 @@ export default {
335
360
  </button>
336
361
  <AsyncButton
337
362
  :mode="buttonMode"
363
+ :icon="buttonIcon"
338
364
  data-testid="install-ext-modal-install-btn"
339
365
  @click="install"
340
366
  />
@@ -103,7 +103,7 @@ export default {
103
103
  <template>
104
104
  <div class="plugin-install-dialog">
105
105
  <h4 class="mt-10">
106
- {{ t('plugins.uninstall.title', { name: plugin?.label }) }}
106
+ {{ t('plugins.uninstall.title', { name: `"${plugin?.label}"` }, true) }}
107
107
  </h4>
108
108
  <div class="mt-10 dialog-panel">
109
109
  <div class="dialog-info">
@@ -122,6 +122,7 @@ export default {
122
122
  </button>
123
123
  <AsyncButton
124
124
  mode="uninstall"
125
+ :icon="busy ? '' : 'icon-delete'"
125
126
  data-testid="uninstall-ext-modal-uninstall-btn"
126
127
  @click="uninstall()"
127
128
  />
@@ -0,0 +1,111 @@
1
+ import { shallowMount, VueWrapper } from '@vue/test-utils';
2
+ import InstallExtensionDialog from '@shell/dialog/InstallExtensionDialog.vue';
3
+
4
+ jest.mock('@shell/config/uiplugins', () => ({
5
+ ...jest.requireActual('@shell/config/uiplugins'),
6
+ isChartVersionHigher: jest.fn((v1: string, v2: string) => v1 > v2),
7
+ }));
8
+
9
+ const t = (key: string): string => key;
10
+
11
+ describe('component: InstallExtensionDialog', () => {
12
+ let wrapper: VueWrapper<any>;
13
+
14
+ const mountComponent = (propsData = {}) => {
15
+ const store = { dispatch: () => Promise.resolve() };
16
+
17
+ const defaultProps = {
18
+ plugin: { installableVersions: [] },
19
+ action: 'install',
20
+ updateStatus: jest.fn(),
21
+ closed: jest.fn(),
22
+ };
23
+
24
+ return shallowMount(InstallExtensionDialog, {
25
+ propsData: {
26
+ ...defaultProps,
27
+ ...propsData,
28
+ },
29
+ global: {
30
+ mocks: {
31
+ $store: store,
32
+ t,
33
+ },
34
+ }
35
+ });
36
+ };
37
+
38
+ describe('fetch', () => {
39
+ it('should set currentVersion if plugin is installed', async() => {
40
+ wrapper = mountComponent({ plugin: { installed: true, installedVersion: '1.0.0' } });
41
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
42
+ expect(wrapper.vm.currentVersion).toBe('1.0.0');
43
+ });
44
+
45
+ it('should set version from initialVersion if provided', async() => {
46
+ wrapper = mountComponent({ initialVersion: '1.2.3', plugin: { installed: false } });
47
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
48
+ expect(wrapper.vm.version).toBe('1.2.3');
49
+ });
50
+
51
+ it('should set version to latest for upgrade action', async() => {
52
+ const plugin = { installableVersions: [{ version: '1.1.0' }, { version: '1.0.0' }] };
53
+
54
+ wrapper = mountComponent({ plugin, action: 'upgrade' });
55
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
56
+ expect(wrapper.vm.version).toBe('1.1.0');
57
+ });
58
+
59
+ it('should set version to next oldest for downgrade action', async() => {
60
+ const plugin = {
61
+ installed: true,
62
+ installedVersion: '1.1.0',
63
+ installableVersions: [{ version: '1.1.0' }, { version: '1.0.0' }]
64
+ };
65
+
66
+ wrapper = mountComponent({ plugin, action: 'downgrade' });
67
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
68
+ expect(wrapper.vm.version).toBe('1.0.0');
69
+ });
70
+ });
71
+
72
+ describe('versionOptions', () => {
73
+ it('should include and disable the current version', () => {
74
+ const plugin = {
75
+ installableVersions: [
76
+ { version: '1.1.0' },
77
+ { version: '1.0.0' },
78
+ ]
79
+ };
80
+
81
+ wrapper = mountComponent({ plugin });
82
+ wrapper.vm.currentVersion = '1.0.0';
83
+
84
+ const options = wrapper.vm.versionOptions;
85
+ const currentOption = options.find((o: any) => o.value === '1.0.0');
86
+
87
+ expect(currentOption).toBeDefined();
88
+ expect(currentOption.disabled).toBe(true);
89
+ expect(currentOption.label).toContain('(plugins.labels.current)');
90
+ });
91
+ });
92
+
93
+ describe('buttonMode', () => {
94
+ beforeEach(() => {
95
+ wrapper = mountComponent({ action: 'upgrade' });
96
+ wrapper.vm.currentVersion = '1.0.0';
97
+ });
98
+
99
+ it('should be "upgrade" if selected version is higher', async() => {
100
+ wrapper.vm.version = '1.1.0';
101
+ await wrapper.vm.$nextTick();
102
+ expect(wrapper.vm.buttonMode).toBe('upgrade');
103
+ });
104
+
105
+ it('should be "downgrade" if selected version is lower', async() => {
106
+ wrapper.vm.version = '0.9.0';
107
+ await wrapper.vm.$nextTick();
108
+ expect(wrapper.vm.buttonMode).toBe('downgrade');
109
+ });
110
+ });
111
+ });
@@ -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>