@rancher/shell 3.0.5-rc.2 → 3.0.5-rc.5

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 (289) hide show
  1. package/assets/data/aws-regions.json +2 -0
  2. package/assets/images/icons/document.svg +3 -0
  3. package/assets/images/vendor/cognito.svg +1 -0
  4. package/assets/styles/app.scss +1 -0
  5. package/assets/styles/base/_basic.scss +10 -0
  6. package/assets/styles/base/_spacing.scss +29 -0
  7. package/assets/styles/global/_layout.scss +1 -2
  8. package/assets/styles/themes/_dark.scss +25 -0
  9. package/assets/styles/themes/_light.scss +65 -0
  10. package/assets/translations/en-us.yaml +377 -37
  11. package/assets/translations/zh-hans.yaml +8 -15
  12. package/chart/monitoring/index.vue +1 -1
  13. package/components/AsyncButton.vue +2 -0
  14. package/components/Certificates.vue +5 -0
  15. package/components/CodeMirror.vue +3 -3
  16. package/components/CruResource.vue +103 -15
  17. package/components/ExplorerProjectsNamespaces.vue +7 -2
  18. package/components/FilterPanel.vue +156 -0
  19. package/components/FixedBanner.vue +19 -5
  20. package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
  21. package/components/IconOrSvg.vue +14 -35
  22. package/components/PaginatedResourceTable.vue +7 -0
  23. package/components/PromptRemove.vue +5 -1
  24. package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
  25. package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
  26. package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
  27. package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
  28. package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
  29. package/components/Resource/Detail/Card/Scaler.vue +89 -0
  30. package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
  31. package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
  32. package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
  33. package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
  34. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
  35. package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
  36. package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
  37. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
  38. package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
  39. package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
  40. package/components/Resource/Detail/Card/index.vue +56 -0
  41. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
  42. package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
  43. package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
  44. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
  45. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
  46. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
  47. package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
  48. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
  49. package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
  50. package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
  51. package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
  52. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
  53. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
  54. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
  55. package/components/Resource/Detail/Metadata/composables.ts +29 -0
  56. package/components/Resource/Detail/Metadata/index.vue +66 -0
  57. package/components/Resource/Detail/Page.vue +22 -0
  58. package/components/Resource/Detail/PercentageBar.vue +40 -0
  59. package/components/Resource/Detail/ResourceRow.vue +119 -0
  60. package/components/Resource/Detail/SpacedRow.vue +14 -0
  61. package/components/Resource/Detail/StatusBar.vue +59 -0
  62. package/components/Resource/Detail/StatusRow.vue +61 -0
  63. package/components/Resource/Detail/TitleBar/Title.vue +13 -0
  64. package/components/Resource/Detail/TitleBar/Top.vue +14 -0
  65. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
  66. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
  67. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
  68. package/components/Resource/Detail/TitleBar/composable.ts +31 -0
  69. package/components/Resource/Detail/TitleBar/index.vue +124 -0
  70. package/components/Resource/Detail/Top/index.vue +34 -0
  71. package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
  72. package/components/ResourceDetail/Masthead.vue +0 -1
  73. package/components/ResourceDetail/__tests__/index.test.ts +114 -0
  74. package/components/ResourceDetail/index.vue +64 -562
  75. package/components/ResourceDetail/legacy.vue +545 -0
  76. package/components/ResourceList/index.vue +2 -1
  77. package/components/ResourceTable.vue +41 -7
  78. package/components/SlideInPanelManager.vue +77 -10
  79. package/components/SortableTable/index.vue +13 -2
  80. package/components/SortableTable/selection.js +22 -9
  81. package/components/StatusBadge.vue +6 -4
  82. package/components/SubtleLink.vue +25 -0
  83. package/components/Tabbed/index.vue +6 -0
  84. package/components/Wizard.vue +12 -1
  85. package/components/YamlEditor.vue +1 -1
  86. package/components/__tests__/AsyncButton.test.ts +39 -0
  87. package/components/__tests__/CruResource.test.ts +63 -0
  88. package/components/__tests__/FilterPanel.test.ts +81 -0
  89. package/components/__tests__/PromptModal.test.ts +0 -2
  90. package/components/auth/AuthBanner.vue +2 -3
  91. package/components/auth/RoleDetailEdit.vue +45 -3
  92. package/components/auth/login/oidc.vue +6 -1
  93. package/components/fleet/FleetApplications.vue +181 -0
  94. package/components/fleet/FleetHelmOps.vue +115 -0
  95. package/components/fleet/FleetIntro.vue +58 -28
  96. package/components/fleet/FleetNoWorkspaces.vue +5 -1
  97. package/components/fleet/FleetOCIStorageSecret.vue +171 -0
  98. package/components/fleet/FleetRepos.vue +38 -76
  99. package/components/fleet/FleetResources.vue +50 -22
  100. package/components/fleet/FleetSummary.vue +26 -51
  101. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
  102. package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
  103. package/components/fleet/dashboard/Empty.vue +73 -0
  104. package/components/fleet/dashboard/ResourceCard.vue +183 -0
  105. package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
  106. package/components/fleet/dashboard/ResourceDetails.vue +196 -0
  107. package/components/fleet/dashboard/ResourcePanel.vue +376 -0
  108. package/components/form/ArrayList.vue +139 -117
  109. package/components/form/BannerSettings.vue +145 -96
  110. package/components/form/KeyValue.vue +10 -7
  111. package/components/form/LabeledSelect.vue +9 -2
  112. package/components/form/MatchExpressions.vue +5 -1
  113. package/components/form/NameNsDescription.vue +1 -1
  114. package/components/form/ResourceSelector.vue +26 -23
  115. package/components/form/ResourceTabs/index.vue +2 -1
  116. package/components/form/Select.vue +9 -2
  117. package/components/form/SimpleSecretSelector.vue +8 -2
  118. package/components/form/UnitInput.vue +13 -0
  119. package/components/form/ValueFromResource.vue +31 -19
  120. package/components/form/__tests__/ArrayList.test.ts +32 -0
  121. package/components/form/__tests__/KeyValue.test.ts +36 -0
  122. package/components/form/__tests__/LabeledSelect.test.ts +33 -0
  123. package/components/form/__tests__/Select.test.ts +34 -1
  124. package/components/form/__tests__/UnitInput.test.ts +23 -1
  125. package/components/formatter/ClusterLink.vue +5 -8
  126. package/components/formatter/Description.vue +30 -0
  127. package/components/formatter/FleetApplicationClustersReady.vue +77 -0
  128. package/components/formatter/FleetApplicationSource.vue +71 -0
  129. package/components/formatter/FleetSummaryGraph.vue +7 -0
  130. package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
  131. package/components/nav/Header.vue +8 -7
  132. package/components/nav/NamespaceFilter.vue +1 -1
  133. package/components/nav/TopLevelMenu.helper.ts +55 -34
  134. package/components/nav/TopLevelMenu.vue +11 -0
  135. package/components/nav/Type.vue +4 -1
  136. package/components/nav/WindowManager/index.vue +1 -0
  137. package/composables/useI18n.ts +12 -11
  138. package/config/labels-annotations.js +14 -11
  139. package/config/product/auth.js +1 -0
  140. package/config/product/explorer.js +16 -13
  141. package/config/product/fleet.js +70 -17
  142. package/config/product/manager.js +1 -28
  143. package/config/query-params.js +3 -1
  144. package/config/roles.ts +1 -0
  145. package/config/router/routes.js +20 -2
  146. package/config/secret.ts +15 -0
  147. package/config/settings.ts +14 -15
  148. package/config/table-headers.js +59 -27
  149. package/config/types.js +2 -0
  150. package/core/plugin-helpers.ts +3 -2
  151. package/detail/catalog.cattle.io.app.vue +0 -1
  152. package/detail/fleet.cattle.io.cluster.vue +28 -15
  153. package/detail/fleet.cattle.io.gitrepo.vue +10 -1
  154. package/detail/fleet.cattle.io.helmop.vue +157 -0
  155. package/detail/provisioning.cattle.io.cluster.vue +13 -3
  156. package/detail/service.vue +0 -1
  157. package/detail/workload/index.vue +21 -34
  158. package/dialog/ExtensionCatalogUninstallDialog.vue +14 -8
  159. package/dialog/HelmOpForceUpdateDialog.vue +132 -0
  160. package/dialog/RedeployWorkloadDialog.vue +164 -0
  161. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
  162. package/edit/__tests__/service.test.ts +2 -1
  163. package/edit/auth/oidc.vue +159 -93
  164. package/edit/fleet.cattle.io.gitrepo.vue +26 -33
  165. package/edit/fleet.cattle.io.helmop.vue +997 -0
  166. package/edit/management.cattle.io.fleetworkspace.vue +43 -10
  167. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
  168. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
  169. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
  170. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
  171. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
  172. package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
  173. package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
  174. package/edit/service.vue +13 -28
  175. package/list/fleet.cattle.io.gitrepo.vue +1 -1
  176. package/list/fleet.cattle.io.helmop.vue +108 -0
  177. package/list/namespace.vue +5 -2
  178. package/list/workload.vue +6 -1
  179. package/mixins/auth-config.js +8 -1
  180. package/mixins/preset.js +100 -0
  181. package/mixins/resource-fetch-api-pagination.js +57 -43
  182. package/mixins/resource-fetch.js +15 -6
  183. package/mixins/resource-table-watch.js +45 -0
  184. package/models/__tests__/chart.test.ts +273 -0
  185. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  186. package/models/__tests__/workload.test.ts +1 -0
  187. package/models/chart.js +144 -2
  188. package/models/cluster/node.js +1 -0
  189. package/models/cluster.js +32 -2
  190. package/models/fleet-application.js +385 -0
  191. package/models/fleet.cattle.io.bundle.js +9 -8
  192. package/models/fleet.cattle.io.gitrepo.js +41 -365
  193. package/models/fleet.cattle.io.helmop.js +228 -0
  194. package/models/management.cattle.io.authconfig.js +1 -0
  195. package/models/management.cattle.io.cluster.js +0 -20
  196. package/models/management.cattle.io.fleetworkspace.js +12 -0
  197. package/models/management.cattle.io.node.js +7 -22
  198. package/models/management.cattle.io.nodepool.js +12 -0
  199. package/models/namespace.js +5 -0
  200. package/models/provisioning.cattle.io.cluster.js +18 -64
  201. package/models/service.js +24 -9
  202. package/models/workload.js +84 -49
  203. package/package.json +2 -1
  204. package/pages/auth/verify.vue +13 -1
  205. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
  208. package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
  209. package/pages/c/_cluster/apps/charts/index.vue +302 -484
  210. package/pages/c/_cluster/apps/charts/install.vue +0 -1
  211. package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
  212. package/pages/c/_cluster/explorer/index.vue +11 -0
  213. package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
  214. package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
  215. package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
  216. package/pages/c/_cluster/fleet/application/create.vue +340 -0
  217. package/pages/c/_cluster/fleet/application/index.vue +139 -0
  218. package/pages/c/_cluster/fleet/graph/config.js +277 -0
  219. package/pages/c/_cluster/fleet/index.vue +772 -330
  220. package/pages/c/_cluster/longhorn/index.vue +2 -2
  221. package/pages/c/_cluster/settings/banners.vue +56 -2
  222. package/pages/c/_cluster/settings/performance.vue +7 -26
  223. package/pages/explorer/resource/detail/configmap.vue +19 -0
  224. package/pages/home.vue +11 -52
  225. package/plugins/clean-html.js +2 -0
  226. package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
  227. package/plugins/dashboard-store/actions.js +153 -30
  228. package/plugins/dashboard-store/getters.js +108 -24
  229. package/plugins/dashboard-store/mutations.js +61 -12
  230. package/plugins/dashboard-store/resource-class.js +36 -4
  231. package/plugins/steve/__tests__/getters.test.ts +18 -11
  232. package/plugins/steve/__tests__/steve-class.test.ts +1 -0
  233. package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
  234. package/plugins/steve/actions.js +37 -12
  235. package/plugins/steve/getters.js +39 -10
  236. package/plugins/steve/steve-class.js +5 -0
  237. package/plugins/steve/steve-pagination-utils.ts +213 -50
  238. package/plugins/steve/subscribe.js +229 -42
  239. package/plugins/steve/worker/web-worker.advanced.js +3 -1
  240. package/rancher-components/BadgeState/BadgeState.vue +3 -1
  241. package/rancher-components/Banner/Banner.test.ts +51 -3
  242. package/rancher-components/Banner/Banner.vue +28 -6
  243. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
  244. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +5 -1
  245. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +21 -1
  246. package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
  247. package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
  248. package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
  249. package/rancher-components/RcItemCard/index.ts +2 -0
  250. package/store/auth.js +1 -0
  251. package/store/catalog.js +62 -24
  252. package/store/features.js +0 -1
  253. package/store/index.js +33 -14
  254. package/store/slideInPanel.ts +6 -0
  255. package/store/type-map.js +1 -0
  256. package/store/type-map.utils.ts +45 -2
  257. package/types/fleet.d.ts +36 -1
  258. package/types/kube/kube-api.ts +22 -0
  259. package/types/resources/settings.d.ts +19 -5
  260. package/types/shell/index.d.ts +595 -471
  261. package/types/store/dashboard-store.types.ts +41 -4
  262. package/types/store/pagination.types.ts +25 -3
  263. package/types/store/subscribe.types.ts +50 -0
  264. package/utils/auth.js +32 -3
  265. package/utils/cluster.js +24 -20
  266. package/utils/fleet-types.ts +0 -0
  267. package/utils/fleet.ts +200 -1
  268. package/utils/grafana.js +1 -0
  269. package/utils/object.js +0 -12
  270. package/utils/pagination-utils.ts +32 -3
  271. package/utils/pagination-wrapper.ts +132 -50
  272. package/utils/perf-setting.utils.ts +28 -0
  273. package/utils/selector-typed.ts +205 -0
  274. package/utils/selector.js +29 -6
  275. package/utils/settings.ts +4 -1
  276. package/utils/style.ts +39 -0
  277. package/utils/uiplugins.ts +10 -6
  278. package/utils/v-sphere.ts +5 -1
  279. package/utils/validators/formRules/__tests__/index.test.ts +36 -3
  280. package/utils/validators/formRules/index.ts +10 -3
  281. package/utils/window.js +11 -7
  282. package/components/__tests__/ApplicationCard.test.ts +0 -27
  283. package/components/cards/ApplicationCard.vue +0 -145
  284. package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
  285. package/components/formatter/RKETemplateName.vue +0 -37
  286. package/config/secret.js +0 -14
  287. package/dialog/SaveAsRKETemplateDialog.vue +0 -139
  288. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
  289. /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <div class="top">
3
+ <slot name="default" />
4
+ </div>
5
+ </template>
6
+
7
+ <style lang="scss" scoped>
8
+ .top {
9
+ display: flex;
10
+ flex-direction: row;
11
+ justify-content: space-between;
12
+ align-items: center;
13
+ }
14
+ </style>
@@ -0,0 +1,17 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Title from '@shell/components/Resource/Detail/TitleBar/Title.vue';
3
+
4
+ describe('component: TitleBar/Title', () => {
5
+ it('shoulder render container with class title', async() => {
6
+ const wrapper = mount(Title);
7
+
8
+ expect(wrapper.find('.title').exists()).toBeTruthy();
9
+ });
10
+
11
+ it('should render default slot', async() => {
12
+ const content = 'CONTENT';
13
+ const wrapper = mount(Title, { slots: { default: content } });
14
+
15
+ expect(wrapper.find('.title').element.innerHTML).toStrictEqual(content);
16
+ });
17
+ });
@@ -0,0 +1,17 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Top from '@shell/components/Resource/Detail/TitleBar/Top.vue';
3
+
4
+ describe('component: TitleBar/Top', () => {
5
+ it('shoulder render container with class top', async() => {
6
+ const wrapper = mount(Top);
7
+
8
+ expect(wrapper.find('.top').exists()).toBeTruthy();
9
+ });
10
+
11
+ it('should render default slot', async() => {
12
+ const content = 'CONTENT';
13
+ const wrapper = mount(Top, { slots: { default: content } });
14
+
15
+ expect(wrapper.find('.top').element.innerHTML).toStrictEqual(content);
16
+ });
17
+ });
@@ -0,0 +1,142 @@
1
+ import { mount, RouterLinkStub } from '@vue/test-utils';
2
+ import TitleBar from '@shell/components/Resource/Detail/TitleBar/index.vue';
3
+ import ActionMenu from '@shell/components/ActionMenuShell.vue';
4
+ import { createStore } from 'vuex';
5
+
6
+ jest.mock(`@shell/assets/images/icons/document.svg`, () => `@shell/assets/images/icons/document.svg`);
7
+
8
+ describe('component: TitleBar/index', () => {
9
+ const resourceTypeLabel = 'RESOURCE_TYPE_LABEL';
10
+ const resourceTo = 'RESOURCE_TO';
11
+ const resourceName = 'RESOURCE_NAME';
12
+ const store = createStore({});
13
+
14
+ it('shoulder render container with class title-bar', async() => {
15
+ const wrapper = mount(TitleBar, {
16
+ props: {
17
+ resourceTypeLabel,
18
+ resourceName
19
+ },
20
+ global: { stubs: { 'router-link': RouterLinkStub }, provide: { store } }
21
+ });
22
+
23
+ expect(wrapper.find('.title-bar').exists()).toBeTruthy();
24
+ });
25
+
26
+ it('should render type label as a link when resourceTo is provided', async() => {
27
+ const wrapper = mount(TitleBar, {
28
+ props: {
29
+ resourceTypeLabel, resourceTo, resourceName
30
+ },
31
+ global: { stubs: { 'router-link': RouterLinkStub }, provide: { store } }
32
+ });
33
+
34
+ const link = wrapper.find('.top .title .resource-link').findComponent(RouterLinkStub);
35
+
36
+ expect(link.props('to')).toStrictEqual(resourceTo);
37
+ expect(link.element.innerHTML.trim()).toStrictEqual(`${ resourceTypeLabel }:`);
38
+ });
39
+
40
+ it('should render type label as text when resourceTo is not provided', async() => {
41
+ const wrapper = mount(TitleBar, {
42
+ props: { resourceTypeLabel, resourceName },
43
+ global: { stubs: { 'router-link': RouterLinkStub }, provide: { store } }
44
+ });
45
+
46
+ const span = wrapper.find('.top > .title > .resource-text');
47
+
48
+ expect(span.element.innerHTML.trim()).toStrictEqual(`${ resourceTypeLabel }:`);
49
+ });
50
+
51
+ it('should render resourceName', async() => {
52
+ const wrapper = mount(TitleBar, {
53
+ props: { resourceTypeLabel, resourceName },
54
+ global: { stubs: { 'router-link': RouterLinkStub }, provide: { store } }
55
+ });
56
+
57
+ const span = wrapper.find('.top > .title > .resource-name');
58
+
59
+ expect(span.element.innerHTML).toStrictEqual(resourceName);
60
+ });
61
+
62
+ it('should hide the ShowConfiguration button if onShowConfiguration is not defined', async() => {
63
+ const wrapper = mount(TitleBar, {
64
+ props: { resourceTypeLabel, resourceName },
65
+ global: { stubs: { 'router-link': RouterLinkStub }, provide: { store } }
66
+ });
67
+
68
+ expect(wrapper.find('.top > .actions > .show-configuration').exists()).toBeFalsy();
69
+ });
70
+
71
+ it('should pass appropriate props to RcButton if onShowConfiguration is defined and emits show-configuration', async() => {
72
+ const wrapper = mount(TitleBar, {
73
+ props: {
74
+ resourceTypeLabel, resourceName, onShowConfiguration: () => {}
75
+ },
76
+ global: { stubs: { 'router-link': RouterLinkStub, RcButton: true }, provide: { store } }
77
+ });
78
+
79
+ const button = wrapper.find('.top > .actions > .show-configuration');
80
+ const buttonComponent = button.getComponent<any>('rc-button-stub');
81
+
82
+ expect(buttonComponent.props('primary')).toStrictEqual(true);
83
+ button.trigger('click');
84
+
85
+ expect(wrapper.emitted()).toHaveProperty('show-configuration');
86
+ });
87
+
88
+ it('should hide ActionMenu if actionMenuResource is not defined', async() => {
89
+ const wrapper = mount(TitleBar, {
90
+ props: { resourceTypeLabel, resourceName },
91
+ global: { stubs: { 'router-link': RouterLinkStub, ActionMenu }, provide: { store } }
92
+ });
93
+
94
+ expect(wrapper.find('.top > .actions > [data-testid="masthead-action-menu"]').exists()).toBeFalsy();
95
+ });
96
+
97
+ it('should pass appropriate props to ActionMenu if actionMenuResource', async() => {
98
+ const actionMenuResource = { resource: 'test' };
99
+ const wrapper = mount(TitleBar, {
100
+ props: {
101
+ resourceTypeLabel, resourceName, actionMenuResource
102
+ },
103
+ global: {
104
+ stubs: { 'router-link': RouterLinkStub, ActionMenu: true },
105
+ provide: { store }
106
+ }
107
+ });
108
+
109
+ const actions = wrapper.find('.top > .actions');
110
+ const actionMenuComponent = actions.getComponent<any>('action-menu-stub');
111
+
112
+ expect(actionMenuComponent.props('buttonRole')).toStrictEqual('multiAction');
113
+ expect(actionMenuComponent.props('resource')).toStrictEqual(actionMenuResource);
114
+ });
115
+
116
+ it('should hide the description element if description is not passed', async() => {
117
+ const wrapper = mount(TitleBar, {
118
+ props: {
119
+ resourceTypeLabel,
120
+ resourceName
121
+ },
122
+ global: { stubs: { 'router-link': RouterLinkStub }, provide: { store } }
123
+ });
124
+
125
+ expect(wrapper.find('.bottom.description').exists()).toBeFalsy();
126
+ });
127
+
128
+ it('should show the description element if description is passed', async() => {
129
+ const description = 'DESCRIPTION';
130
+ const wrapper = mount(TitleBar, {
131
+ props: {
132
+ resourceTypeLabel,
133
+ resourceName,
134
+ description
135
+ },
136
+ global: { stubs: { 'router-link': RouterLinkStub }, provide: { store } }
137
+ });
138
+
139
+ expect(wrapper.find('.bottom.description').exists()).toBeTruthy();
140
+ expect(wrapper.find('.bottom.description').element.innerHTML).toStrictEqual(description);
141
+ });
142
+ });
@@ -0,0 +1,31 @@
1
+ import { TitleBarProps } from '@shell/components/Resource/Detail/TitleBar/index.vue';
2
+ import { computed, Ref, toValue } from 'vue';
3
+ import { useRoute } from 'vue-router';
4
+ import { useStore } from 'vuex';
5
+
6
+ export const useDefaultTitleBarData = (resource: any): Ref<TitleBarProps> => {
7
+ const route = useRoute();
8
+ const store = useStore();
9
+
10
+ const resourceValue = toValue(resource);
11
+
12
+ return computed(() => ({
13
+ resourceTypeLabel: store.getters['type-map/labelFor']({ id: resourceValue.type }),
14
+ resourceTo: {
15
+ name: 'c-cluster-product-resource',
16
+ params: {
17
+ product: 'explorer',
18
+ cluster: route.params.cluster,
19
+ namespace: resourceValue.namespace,
20
+ resource: resourceValue.type
21
+ }
22
+ },
23
+ resourceName: resourceValue.nameDisplay,
24
+ actionMenuResource: resourceValue,
25
+ badge: {
26
+ color: resourceValue.stateBackground,
27
+ label: resourceValue.stateDisplay
28
+ },
29
+ onShowConfiguration: () => resourceValue.goToEdit()
30
+ }));
31
+ };
@@ -0,0 +1,124 @@
1
+ <script lang="ts">
2
+ import BadgeState from '@pkg/rancher-components/src/components/BadgeState/BadgeState.vue';
3
+ import { RouteLocationRaw } from 'vue-router';
4
+ import Title from '@shell/components/Resource/Detail/TitleBar/Title.vue';
5
+ import Top from '@shell/components/Resource/Detail/TitleBar/Top.vue';
6
+ import ActionMenu from '@shell/components/ActionMenuShell.vue';
7
+ import { useStore } from 'vuex';
8
+ import { useI18n } from '@shell/composables/useI18n';
9
+ import RcButton from '~/pkg/rancher-components/src/components/RcButton/RcButton.vue';
10
+
11
+ export interface Badge {
12
+ color: 'bg-success' | 'bg-error' | 'bg-warning' | 'bg-info';
13
+ label: string;
14
+ }
15
+
16
+ export interface TitleBarProps {
17
+ resourceTypeLabel: string;
18
+ resourceName: string;
19
+
20
+ resourceTo?: RouteLocationRaw;
21
+ description?: string;
22
+ badge?: Badge;
23
+
24
+ // This should be replaced with a list of menu items we want to render.
25
+ // I don't have the time right now to swap this out though.
26
+ actionMenuResource?: any;
27
+
28
+ onShowConfiguration?: () => void;
29
+ }
30
+
31
+ const showConfigurationIcon = require(`@shell/assets/images/icons/document.svg`);
32
+ </script>
33
+
34
+ <script setup lang="ts">
35
+ const {
36
+ resourceTypeLabel, resourceTo, resourceName, description, badge, onShowConfiguration
37
+ } = defineProps<TitleBarProps>();
38
+
39
+ const store = useStore();
40
+ const i18n = useI18n(store);
41
+
42
+ const emit = defineEmits(['show-configuration']);
43
+ </script>
44
+
45
+ <template>
46
+ <div class="title-bar">
47
+ <Top>
48
+ <Title>
49
+ <router-link
50
+ v-if="resourceTo"
51
+ :to="resourceTo"
52
+ class="resource-link"
53
+ >
54
+ {{ resourceTypeLabel }}:
55
+ </router-link>
56
+ <span
57
+ v-else
58
+ class="resource-text"
59
+ >
60
+ {{ resourceTypeLabel }}:
61
+ </span>
62
+ <span class="resource-name">
63
+ {{ resourceName }}
64
+ </span>
65
+ <BadgeState
66
+ v-if="badge"
67
+ :color="badge.color"
68
+ :label="badge.label"
69
+ />
70
+ </Title>
71
+ <div class="actions">
72
+ <RcButton
73
+ v-if="onShowConfiguration"
74
+ class="show-configuration"
75
+ :primary="true"
76
+ :aria-label="i18n.t('component.resource.detail.titleBar.ariaLabel.showConfiguration', { resource: resourceName })"
77
+ @click="emit('show-configuration')"
78
+ >
79
+ <img
80
+ :src="showConfigurationIcon"
81
+ class="mmr-3"
82
+ >
83
+ {{ i18n.t('component.resource.detail.titleBar.showConfiguration') }}
84
+ </RcButton>
85
+ <ActionMenu
86
+ v-if="actionMenuResource"
87
+ class="title-bar-action-menu"
88
+ button-role="multiAction"
89
+ :resource="actionMenuResource"
90
+ data-testid="masthead-action-menu"
91
+ />
92
+ </div>
93
+ </Top>
94
+ <div
95
+ v-if="description"
96
+ class="bottom description"
97
+ >
98
+ {{ description }}
99
+ </div>
100
+ </div>
101
+ </template>
102
+
103
+ <style lang="scss" scoped>
104
+ .title-bar {
105
+ &:deep() .badge-state {
106
+ font-size: 16px;
107
+ margin-left: 4px;
108
+ top: -4px;
109
+ position: relative;
110
+ }
111
+
112
+ &:deep() button[data-testid="masthead-action-menu"] {
113
+ border-radius: 4px;
114
+ width: 35px;
115
+ height: 40px;
116
+ margin-left: 16px;
117
+
118
+ display: inline-flex;
119
+ flex-direction: row;
120
+ justify-content: center;
121
+ align-items: center;
122
+ }
123
+ }
124
+ </style>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ </script>
3
+
4
+ <template>
5
+ <div class="resource-detail-page-top">
6
+ <div
7
+ v-if="$slots['header']"
8
+ class="header"
9
+ >
10
+ <slot name="header" />
11
+ </div>
12
+ <div
13
+ v-if="$slots['system-messages']"
14
+ class="system-messages"
15
+ >
16
+ <slot name="system-messages" />
17
+ </div>
18
+ <div
19
+ v-if="$slots['metadata']"
20
+ class="metadata"
21
+ >
22
+ <slot name="metadata" />
23
+ </div>
24
+ <div
25
+ v-if="$slots['extension-content']"
26
+ class="extension-content"
27
+ >
28
+ <slot name="extension-content" />
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <style lang="scss" scoped>
34
+ </style>
@@ -0,0 +1,32 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Page from '@shell/components/Resource/Detail/Page.vue';
3
+
4
+ describe('component: ResourceDetailPage', () => {
5
+ it('should render container with resource-detail-class and hide the slots not used', async() => {
6
+ const wrapper = mount(Page);
7
+
8
+ expect(wrapper.find('.resource-detail-page').exists()).toBeTruthy();
9
+
10
+ expect(wrapper.find('.top-area').exists()).toBeFalsy();
11
+ expect(wrapper.find('.middle-area').exists()).toBeFalsy();
12
+ expect(wrapper.find('.bottom-area').exists()).toBeFalsy();
13
+ });
14
+
15
+ it('should render each of the slots with appropriate content', async() => {
16
+ const topArea = 'TOP_AREA';
17
+ const middleArea = 'MIDDLE_AREA';
18
+ const bottomArea = 'BOTTOM_AREA';
19
+
20
+ const wrapper = mount(Page, {
21
+ slots: {
22
+ 'top-area': topArea,
23
+ 'middle-area': middleArea,
24
+ 'bottom-area': bottomArea
25
+ }
26
+ });
27
+
28
+ expect(wrapper.find('.top-area').element.innerHTML.trim()).toStrictEqual(topArea);
29
+ expect(wrapper.find('.middle-area').element.innerHTML.trim()).toStrictEqual(middleArea);
30
+ expect(wrapper.find('.bottom-area').element.innerHTML.trim()).toStrictEqual(bottomArea);
31
+ });
32
+ });
@@ -545,7 +545,6 @@ export default {
545
545
  {{ value.createdBy.displayName }}
546
546
  </span>
547
547
  </span>
548
- <span v-if="value.showPodRestarts">{{ t("resourceDetail.masthead.restartCount") }}:<span class="live-data"> {{ value.restartCount }}</span></span>
549
548
  </div>
550
549
  </div>
551
550
  <slot name="right">
@@ -0,0 +1,114 @@
1
+ import { mount } from '@vue/test-utils';
2
+
3
+ import ResourceDetail from '@shell/components/ResourceDetail/index.vue';
4
+ import { _EDIT, _VIEW, LEGACY, MODE } from '@shell/config/query-params';
5
+ import flushPromises from 'flush-promises';
6
+
7
+ const mockQuery: any = {};
8
+ const mockParams: any = {};
9
+
10
+ jest.mock('@shell/components/ResourceDetail/legacy.vue', () => ({
11
+ template: '<div class="legacy">Legacy</div>',
12
+ name: 'Legacy',
13
+ props: ['flexContent', 'componentTestId', 'storeOverride', 'resourceOverride', 'parentRouteOverride', 'errorsMap']
14
+ }));
15
+
16
+ jest.mock('@shell/components/Loading.vue', () => ({
17
+ template: '<div class="loading">Loading</div>',
18
+ name: 'Loading'
19
+ }));
20
+
21
+ jest.mock('@shell/pages/explorer/resource/detail/configmap.vue', () => ({
22
+ template: '<div class="configmap">configmap</div>',
23
+ name: 'configmap',
24
+ }));
25
+
26
+ jest.mock('vue-router', () => ({
27
+ useRoute: () => ({
28
+ query: mockQuery,
29
+ params: mockParams
30
+ })
31
+ }));
32
+
33
+ describe('component: ResourceDetail/index', () => {
34
+ const resourceName = 'configmap';
35
+
36
+ it('should render legacy component with default props if LEGACY=false is not present', async() => {
37
+ mockParams.resource = resourceName;
38
+ mockQuery[MODE] = _VIEW;
39
+
40
+ const wrapper = mount(ResourceDetail, { });
41
+ const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
42
+
43
+ expect(legacyComponent.props('flexContent')).toStrictEqual(false);
44
+ expect(legacyComponent.props('componentTestId')).toStrictEqual('resource-details');
45
+ expect(legacyComponent.props('storeOverride')).toBeUndefined();
46
+ expect(legacyComponent.props('resourceOverride')).toBeUndefined();
47
+ expect(legacyComponent.props('parentRouteOverride')).toBeUndefined();
48
+ expect(legacyComponent.props('errorsMap')).toBeUndefined();
49
+ });
50
+
51
+ it('should render legacy component with default props if LEGACY=false is present but resourceName has not been added to our mapping', async() => {
52
+ mockParams.resource = 'notMapped';
53
+ mockQuery[MODE] = _VIEW;
54
+
55
+ const wrapper = mount(ResourceDetail, {});
56
+ const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
57
+
58
+ expect(legacyComponent.exists()).toBeTruthy();
59
+ });
60
+
61
+ it('should render legacy component with default props if LEGACY=false is present but mode is not VIEW', async() => {
62
+ mockParams.resource = resourceName;
63
+ mockQuery[MODE] = _EDIT;
64
+
65
+ const wrapper = mount(ResourceDetail, {});
66
+ const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
67
+
68
+ expect(legacyComponent.exists()).toBeTruthy();
69
+ });
70
+
71
+ it('should render legacy component while forwarding props', async() => {
72
+ mockParams.resource = resourceName;
73
+ mockQuery[MODE] = _VIEW;
74
+
75
+ const flexContent = true;
76
+ const componentTestId = 'componentTestId';
77
+ const storeOverride = 'storeOverride';
78
+ const resourceOverride = 'resourceOverride';
79
+ const parentRouteOverride = 'parentRouteOverride';
80
+ const errorsMap = { error: 'test' };
81
+
82
+ const wrapper = mount(ResourceDetail, {
83
+ props: {
84
+ flexContent,
85
+ componentTestId,
86
+ storeOverride,
87
+ resourceOverride,
88
+ parentRouteOverride,
89
+ errorsMap,
90
+ }
91
+ });
92
+ const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
93
+
94
+ expect(legacyComponent.props('flexContent')).toStrictEqual(flexContent);
95
+ expect(legacyComponent.props('componentTestId')).toStrictEqual(componentTestId);
96
+ expect(legacyComponent.props('storeOverride')).toStrictEqual(storeOverride);
97
+ expect(legacyComponent.props('resourceOverride')).toStrictEqual(resourceOverride);
98
+ expect(legacyComponent.props('parentRouteOverride')).toStrictEqual(parentRouteOverride);
99
+ expect(legacyComponent.props('errorsMap')).toStrictEqual(errorsMap);
100
+ });
101
+
102
+ it('should render new component if LEGACY=false is present', async() => {
103
+ mockParams.resource = resourceName;
104
+ mockQuery[MODE] = _VIEW;
105
+ mockQuery[LEGACY] = 'false';
106
+
107
+ const wrapper = mount(ResourceDetail);
108
+
109
+ await flushPromises();
110
+ const configmapComponent = wrapper.findComponent<any>({ name: 'configmap' });
111
+
112
+ expect(configmapComponent.exists()).toBeTruthy();
113
+ });
114
+ });