@rancher/shell 3.0.6 → 3.0.7

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 (78) 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/styles/base/_basic.scss +2 -2
  4. package/assets/styles/base/_color-classic.scss +51 -0
  5. package/assets/styles/base/_color.scss +3 -3
  6. package/assets/styles/base/_mixins.scss +1 -1
  7. package/assets/styles/base/_variables-classic.scss +47 -0
  8. package/assets/styles/global/_button.scss +49 -17
  9. package/assets/styles/global/_form.scss +1 -1
  10. package/assets/styles/themes/_dark.scss +4 -0
  11. package/assets/styles/themes/_light.scss +3 -69
  12. package/assets/styles/themes/_modern.scss +194 -50
  13. package/assets/styles/vendor/vue-select.scss +1 -2
  14. package/assets/translations/en-us.yaml +33 -21
  15. package/components/ClusterIconMenu.vue +1 -1
  16. package/components/ClusterProviderIcon.vue +1 -1
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/IconOrSvg.vue +40 -29
  19. package/components/ResourceDetail/index.vue +1 -0
  20. package/components/SortableTable/sorting.js +3 -1
  21. package/components/Tabbed/index.vue +5 -5
  22. package/components/form/ResourceTabs/index.vue +37 -18
  23. package/components/form/SecretSelector.vue +6 -2
  24. package/components/nav/Group.vue +29 -9
  25. package/components/nav/Header.vue +6 -8
  26. package/components/nav/NamespaceFilter.vue +1 -1
  27. package/components/nav/TopLevelMenu.helper.ts +47 -20
  28. package/components/nav/TopLevelMenu.vue +44 -14
  29. package/components/nav/Type.vue +0 -5
  30. package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
  31. package/config/pagination-table-headers.js +10 -2
  32. package/config/product/explorer.js +4 -3
  33. package/config/table-headers.js +9 -0
  34. package/core/plugin.ts +18 -6
  35. package/core/types.ts +8 -0
  36. package/detail/provisioning.cattle.io.cluster.vue +1 -0
  37. package/dialog/InstallExtensionDialog.vue +71 -45
  38. package/dialog/UninstallExtensionDialog.vue +2 -1
  39. package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
  40. package/edit/auth/oidc.vue +86 -16
  41. package/mixins/__tests__/chart.test.ts +1 -1
  42. package/mixins/chart.js +1 -1
  43. package/models/event.js +7 -0
  44. package/models/provisioning.cattle.io.cluster.js +9 -0
  45. package/package.json +1 -1
  46. package/pages/c/_cluster/explorer/EventsTable.vue +3 -6
  47. package/pages/c/_cluster/settings/performance.vue +1 -1
  48. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
  49. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
  50. package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
  51. package/pages/c/_cluster/uiplugins/index.vue +110 -94
  52. package/plugins/__tests__/subscribe.events.test.ts +194 -0
  53. package/plugins/dashboard-store/actions.js +3 -0
  54. package/plugins/dashboard-store/getters.js +1 -1
  55. package/plugins/dashboard-store/resource-class.js +3 -3
  56. package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
  57. package/plugins/steve/index.js +18 -10
  58. package/plugins/steve/mutations.js +2 -2
  59. package/plugins/steve/resourceWatcher.js +2 -2
  60. package/plugins/steve/steve-pagination-utils.ts +12 -9
  61. package/plugins/steve/subscribe.js +113 -85
  62. package/plugins/subscribe-events.ts +211 -0
  63. package/rancher-components/BadgeState/BadgeState.vue +8 -6
  64. package/rancher-components/Banner/Banner.vue +2 -1
  65. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  66. package/rancher-components/Form/Radio/RadioButton.vue +3 -3
  67. package/store/index.js +12 -22
  68. package/types/extension-manager.ts +8 -1
  69. package/types/resources/settings.d.ts +24 -17
  70. package/types/shell/index.d.ts +352 -335
  71. package/types/store/subscribe-events.types.ts +70 -0
  72. package/types/store/subscribe.types.ts +6 -22
  73. package/utils/pagination-utils.ts +87 -28
  74. package/utils/pagination-wrapper.ts +6 -8
  75. package/utils/sort.js +5 -0
  76. package/utils/unit-tests/pagination-utils.spec.ts +283 -0
  77. package/utils/validators/formRules/__tests__/index.test.ts +7 -0
  78. package/utils/validators/formRules/index.ts +2 -2
@@ -1,14 +1,18 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
3
  import ChartReadme from '@shell/components/ChartReadme';
4
- import { Banner } from '@components/Banner';
5
4
  import LazyImage from '@shell/components/LazyImage';
6
5
  import { MANAGEMENT } from '@shell/config/types';
7
6
  import { SETTING } from '@shell/config/settings';
8
7
  import { useWatcherBasedSetupFocusTrapWithDestroyIncluded } from '@shell/composables/focusTrap';
9
8
  import { getPluginChartVersionLabel, getPluginChartVersion } from '@shell/utils/uiplugins';
9
+ import { isChartVersionHigher } from '@shell/config/uiplugins';
10
+ import RcButton from '@components/RcButton/RcButton.vue';
11
+ import AppChartCardFooter from '@shell/pages/c/_cluster/apps/charts/AppChartCardFooter.vue';
10
12
 
11
13
  export default {
14
+ emits: ['action'],
15
+
12
16
  async fetch() {
13
17
  const bannerSetting = await this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.BANNERS);
14
18
  const { showHeader, bannerHeader } = JSON.parse(bannerSetting.value);
@@ -20,9 +24,10 @@ export default {
20
24
  }
21
25
  },
22
26
  components: {
23
- Banner,
24
27
  ChartReadme,
25
- LazyImage
28
+ LazyImage,
29
+ RcButton,
30
+ AppChartCardFooter
26
31
  },
27
32
  data() {
28
33
  return {
@@ -49,6 +54,60 @@ export default {
49
54
 
50
55
  return {};
51
56
  },
57
+
58
+ panelActions() {
59
+ const actions = [];
60
+
61
+ if (!this.info) {
62
+ return actions;
63
+ }
64
+
65
+ const selectedVersion = this.infoVersion;
66
+ const installedVersion = this.info.installedVersion;
67
+
68
+ if (!this.info.installed) {
69
+ if (this.info.installableVersions?.length) {
70
+ actions.push({
71
+ label: this.t('catalog.chart.chartButton.action.install'),
72
+ action: 'install',
73
+ role: 'primary',
74
+ version: selectedVersion,
75
+ icon: 'icon-plus'
76
+ });
77
+ }
78
+ } else {
79
+ if (selectedVersion && installedVersion && selectedVersion !== installedVersion) {
80
+ if (isChartVersionHigher(selectedVersion, installedVersion)) {
81
+ actions.push({
82
+ label: this.t('catalog.chart.chartButton.action.upgrade'),
83
+ action: 'upgrade',
84
+ role: 'primary',
85
+ version: selectedVersion,
86
+ icon: 'icon-upgrade-alt'
87
+ });
88
+ } else {
89
+ actions.push({
90
+ label: this.t('catalog.chart.chartButton.action.downgrade'),
91
+ action: 'downgrade',
92
+ role: 'primary',
93
+ version: selectedVersion,
94
+ icon: 'icon-downgrade-alt'
95
+ });
96
+ }
97
+ }
98
+
99
+ if (!this.info.builtin) {
100
+ actions.push({
101
+ label: this.t('plugins.uninstall.label'),
102
+ action: 'uninstall',
103
+ role: 'secondary',
104
+ icon: 'icon-delete'
105
+ });
106
+ }
107
+ }
108
+
109
+ return actions;
110
+ }
52
111
  },
53
112
  watch: {
54
113
  showSlideIn: {
@@ -65,12 +124,18 @@ export default {
65
124
  }
66
125
  },
67
126
  methods: {
127
+ onButtonClick(button) {
128
+ this.$emit('action', { ...button, plugin: this.info });
129
+ this.hide();
130
+ },
131
+
68
132
  show(info) {
69
133
  this.info = info;
70
134
  this.showSlideIn = true;
71
135
  this.version = null;
72
136
  this.versionInfo = null;
73
137
  this.versionError = null;
138
+ this.infoVersion = undefined;
74
139
 
75
140
  this.loadPluginVersionInfo();
76
141
  },
@@ -153,7 +218,13 @@ export default {
153
218
  },
154
219
 
155
220
  getVersionLabel(version) {
156
- return getPluginChartVersionLabel(version);
221
+ const label = getPluginChartVersionLabel(version);
222
+
223
+ if (this.info.installed && version.version === this.info.installedVersion) {
224
+ return `${ label } (${ this.t('plugins.labels.current') })`;
225
+ }
226
+
227
+ return label;
157
228
  }
158
229
  }
159
230
  };
@@ -230,49 +301,66 @@ export default {
230
301
  </div>
231
302
  </div>
232
303
  </div>
233
- <div>
234
- <Banner
235
- v-if="info.builtin"
236
- color="warning"
237
- :label="t('plugins.descriptions.built-in')"
238
- class="mt-10"
239
- />
240
- <template v-else>
241
- <Banner
242
- v-if="!info.certified"
243
- color="warning"
244
- :label="t('plugins.descriptions.third-party')"
245
- class="mt-10"
246
- />
247
- <Banner
248
- v-if="info.experimental"
249
- color="warning"
250
- :label="t('plugins.descriptions.experimental')"
251
- class="mt-10"
252
- />
253
- </template>
254
- </div>
304
+ <AppChartCardFooter
305
+ v-if="info.tags && info.tags.length"
306
+ :items="info.tags"
307
+ class="plugin-tags-container"
308
+ />
255
309
 
256
- <h3 v-if="info.versions.length">
257
- {{ t('plugins.info.versions') }}
258
- </h3>
259
- <div class="plugin-versions mb-10">
310
+ <div class="plugin-versions-container">
311
+ <h3>
312
+ {{ t('plugins.info.versions') }}
313
+ </h3>
314
+ <div v-if="!info.versions.length">
315
+ <div class="version-link version-active version-builtin">
316
+ {{ info.displayVersion }}
317
+ </div>
318
+ </div>
260
319
  <div
261
- v-for="v in info.versions"
262
- :key="`${v.name}-${v.version}`"
320
+ v-else
321
+ class="plugin-versions"
263
322
  >
264
- <a
265
- v-clean-tooltip="handleVersionBtnTooltip(v)"
266
- class="version-link"
267
- :class="handleVersionBtnClass(v)"
268
- :tabindex="!v.isVersionCompatible ? -1 : 0"
269
- role="button"
270
- :aria-label="t('plugins.viewVersionDetails', {name: v.name, version: v.version})"
271
- @click="loadPluginVersionInfo(v.version)"
272
- @keyup.enter.space="loadPluginVersionInfo(v.version)"
323
+ <div
324
+ v-for="v in info.versions"
325
+ :key="`${v.name}-${v.version}`"
273
326
  >
274
- {{ getVersionLabel(v) }}
275
- </a>
327
+ <a
328
+ v-clean-tooltip="handleVersionBtnTooltip(v)"
329
+ class="version-link"
330
+ :class="handleVersionBtnClass(v)"
331
+ :tabindex="!v.isVersionCompatible ? -1 : 0"
332
+ role="button"
333
+ :aria-label="t('plugins.viewVersionDetails', {name: v.name, version: v.version})"
334
+ @click="loadPluginVersionInfo(v.version)"
335
+ @keyup.enter.space="loadPluginVersionInfo(v.version)"
336
+ >
337
+ {{ getVersionLabel(v) }}
338
+ </a>
339
+ </div>
340
+ </div>
341
+ </div>
342
+ <div class="plugin-actions-container">
343
+ <h3>
344
+ {{ t('plugins.info.actions') }}
345
+ </h3>
346
+ <div class="plugin-actions">
347
+ <template v-if="panelActions.length">
348
+ <RcButton
349
+ v-for="btn in panelActions"
350
+ :key="btn.action"
351
+ class="mmr-3 mmb-3"
352
+ :[btn.role]="true"
353
+ @click="onButtonClick(btn)"
354
+ >
355
+ <i :class="['icon', btn.icon, 'mmr-2']" />{{ btn.label }}
356
+ </RcButton>
357
+ </template>
358
+ <div
359
+ v-else
360
+ class="no-actions"
361
+ >
362
+ {{ t('plugins.info.noActions') }}
363
+ </div>
276
364
  </div>
277
365
  </div>
278
366
 
@@ -291,14 +379,6 @@ export default {
291
379
  :version-info="versionInfo"
292
380
  />
293
381
  </div>
294
- <div v-if="!info.versions.length">
295
- <h3>
296
- {{ t('plugins.info.versions') }}
297
- </h3>
298
- <div class="version-link version-active version-builtin">
299
- {{ info.displayVersion }}
300
- </div>
301
- </div>
302
382
  </div>
303
383
  </aside>
304
384
  </transition>
@@ -312,8 +392,6 @@ export default {
312
392
  z-index: 1;
313
393
 
314
394
  $slideout-width: 35%;
315
- $title-height: 50px;
316
- $padding: 5px;
317
395
  $slideout-width: 35%;
318
396
  --banner-top-offset: 0;
319
397
  $header-height: calc(54px + var(--banner-top-offset));
@@ -339,7 +417,7 @@ export default {
339
417
  z-index: 10;
340
418
  display: flex;
341
419
  flex-direction: column;
342
- padding: 10px;
420
+ padding: 12px;
343
421
 
344
422
  &.active {
345
423
  right: 0;
@@ -373,6 +451,10 @@ export default {
373
451
  flex-direction: column;
374
452
  overflow: hidden;
375
453
 
454
+ .banner.warning {
455
+ margin-top: 0;
456
+ }
457
+
376
458
  .plugin-info-detail {
377
459
  overflow: auto;
378
460
  }
@@ -380,15 +462,16 @@ export default {
380
462
 
381
463
  h3 {
382
464
  font-size: 14px;
383
- margin: 15px 0 10px 0;
384
- opacity: 0.7;
465
+ margin-bottom: 12px;
466
+ color: var(--disabled-text);
385
467
  text-transform: uppercase;
386
468
  }
387
469
 
388
470
  .plugin-header {
389
471
  border-bottom: 1px solid var(--border);
390
472
  display: flex;
391
- padding-bottom: 20px;
473
+ padding-bottom: 16px;
474
+ margin-bottom: 16px;
392
475
 
393
476
  .plugin-title {
394
477
  flex: 1;
@@ -397,8 +480,7 @@ export default {
397
480
 
398
481
  .plugin-icon {
399
482
  font-size: 40px;
400
- margin-right:10px;
401
- color: #888;
483
+ margin-right: 12px;
402
484
  width: 44px;
403
485
  height: 44px;
404
486
 
@@ -419,11 +501,26 @@ export default {
419
501
  }
420
502
  }
421
503
 
422
- .plugin-versions {
504
+ .plugin-tags-container {
505
+ margin-top: -8px;
506
+ }
507
+
508
+ .plugin-tags-container,
509
+ .plugin-versions-container,
510
+ .plugin-actions-container {
511
+ margin-bottom: 24px;
512
+ }
513
+
514
+ .plugin-versions,
515
+ .plugin-actions {
423
516
  display: flex;
424
517
  flex-wrap: wrap;
425
518
  }
426
519
 
520
+ .no-actions {
521
+ color: var(--disabled-text);
522
+ }
523
+
427
524
  .plugin-description {
428
525
  font-size: 15px;
429
526
  }
@@ -434,7 +531,7 @@ export default {
434
531
  padding: 2px 8px;
435
532
  border-radius: 5px;
436
533
  user-select: none;
437
- margin: 0 5px 5px 0;
534
+ margin: 0 4px 4px 0;
438
535
  display: block;
439
536
 
440
537
  &.version-active {
@@ -498,7 +595,7 @@ export default {
498
595
 
499
596
  height: 0;
500
597
 
501
- padding-bottom: 10px;
598
+ padding-bottom: 12px;
502
599
 
503
600
  :deep() .chart-readmes {
504
601
  flex: 1;
@@ -0,0 +1,102 @@
1
+ import { shallowMount, VueWrapper } from '@vue/test-utils';
2
+ import PluginInfoPanel from '@shell/pages/c/_cluster/uiplugins/PluginInfoPanel.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
+ jest.mock('@shell/utils/uiplugins', () => ({
10
+ getPluginChartVersionLabel: jest.fn((v) => v.version),
11
+ getPluginChartVersion: jest.fn(),
12
+ }));
13
+
14
+ const t = (key: string): string => key;
15
+
16
+ describe('component: PluginInfoPanel', () => {
17
+ let wrapper: VueWrapper<any>;
18
+
19
+ const mountComponent = () => {
20
+ return shallowMount(PluginInfoPanel, { global: { mocks: { t } } });
21
+ };
22
+
23
+ describe('panelActions', () => {
24
+ beforeEach(() => {
25
+ wrapper = mountComponent();
26
+ });
27
+
28
+ it('should be empty if no info is provided', () => {
29
+ expect(wrapper.vm.panelActions).toStrictEqual([]);
30
+ });
31
+
32
+ it('should show install action if not installed and installable versions exist', () => {
33
+ wrapper.vm.info = { installed: false, installableVersions: [{ version: '1.0.0' }] };
34
+ wrapper.vm.infoVersion = '1.0.0';
35
+ const actions = wrapper.vm.panelActions;
36
+
37
+ expect(actions).toHaveLength(1);
38
+ expect(actions[0].action).toBe('install');
39
+ });
40
+
41
+ it('should be empty if not installed and no installable versions exist', () => {
42
+ wrapper.vm.info = { installed: false, installableVersions: [] };
43
+ const actions = wrapper.vm.panelActions;
44
+
45
+ expect(actions).toHaveLength(0);
46
+ });
47
+
48
+ it('should show uninstall action if installed and not builtin', () => {
49
+ wrapper.vm.info = { installed: true, builtin: false };
50
+ const actions = wrapper.vm.panelActions;
51
+
52
+ expect(actions.some((action: any) => action.action === 'uninstall')).toBe(true);
53
+ });
54
+
55
+ it('should show upgrade action for a higher active version', () => {
56
+ wrapper.vm.info = { installed: true, installedVersion: '1.0.0' };
57
+ wrapper.vm.infoVersion = '1.1.0';
58
+ const actions = wrapper.vm.panelActions;
59
+
60
+ expect(actions.some((action: any) => action.action === 'upgrade')).toBe(true);
61
+ });
62
+
63
+ it('should show downgrade action for a lower active version', () => {
64
+ wrapper.vm.info = { installed: true, installedVersion: '1.0.0' };
65
+ wrapper.vm.infoVersion = '0.9.0';
66
+ const actions = wrapper.vm.panelActions;
67
+
68
+ expect(actions.some((action: any) => action.action === 'downgrade')).toBe(true);
69
+ });
70
+
71
+ it('should not show upgrade/downgrade if active version is same as installed', () => {
72
+ wrapper.vm.info = { installed: true, installedVersion: '1.0.0' };
73
+ wrapper.vm.infoVersion = '1.0.0';
74
+ const actions = wrapper.vm.panelActions;
75
+
76
+ expect(actions.some((action: any) => action.action === 'upgrade')).toBe(false);
77
+ expect(actions.some((action: any) => action.action === 'downgrade')).toBe(false);
78
+ });
79
+ });
80
+
81
+ describe('getVersionLabel', () => {
82
+ beforeEach(() => {
83
+ wrapper = mountComponent();
84
+ });
85
+
86
+ it('should return the version label', () => {
87
+ wrapper.vm.info = { installed: false };
88
+ const version = { version: '1.0.0' };
89
+ const label = wrapper.vm.getVersionLabel(version);
90
+
91
+ expect(label).toBe('1.0.0');
92
+ });
93
+
94
+ it('should append (current) for the installed version', () => {
95
+ wrapper.vm.info = { installed: true, installedVersion: '1.0.0' };
96
+ const version = { version: '1.0.0' };
97
+ const label = wrapper.vm.getVersionLabel(version);
98
+
99
+ expect(label).toBe('1.0.0 (plugins.labels.current)');
100
+ });
101
+ });
102
+ });