@rancher/shell 3.0.9-rc.6 → 3.0.10

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 (82) hide show
  1. package/assets/styles/base/_color.scss +4 -0
  2. package/assets/styles/themes/_light.scss +6 -6
  3. package/assets/styles/themes/_modern.scss +14 -6
  4. package/assets/translations/en-us.yaml +2 -5
  5. package/components/CopyToClipboard.vue +28 -0
  6. package/components/CopyToClipboardText.vue +4 -0
  7. package/components/CruResource.vue +1 -0
  8. package/components/GlobalRoleBindings.vue +1 -5
  9. package/components/IconOrSvg.vue +61 -42
  10. package/components/ResourceDetail/index.vue +0 -21
  11. package/components/SortableTable/index.vue +2 -2
  12. package/components/__tests__/CruResource.test.ts +35 -1
  13. package/components/form/BannerSettings.vue +2 -2
  14. package/components/form/NotificationSettings.vue +2 -2
  15. package/composables/useIsNewDetailPageEnabled.test.ts +98 -0
  16. package/composables/useIsNewDetailPageEnabled.ts +12 -0
  17. package/config/product/explorer.js +11 -1
  18. package/config/product/manager.js +0 -1
  19. package/config/table-headers.js +0 -9
  20. package/config/types.js +0 -1
  21. package/detail/fleet.cattle.io.cluster.vue +1 -1
  22. package/dialog/FeatureFlagListDialog.vue +1 -1
  23. package/edit/auth/github-app-steps.vue +2 -0
  24. package/edit/auth/github-steps.vue +2 -0
  25. package/edit/catalog.cattle.io.clusterrepo.vue +1 -1
  26. package/edit/management.cattle.io.user.vue +60 -35
  27. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
  28. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
  29. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
  30. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
  31. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
  32. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
  33. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
  34. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
  35. package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
  36. package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
  37. package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
  38. package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
  39. package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
  40. package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
  41. package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
  42. package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
  43. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +14 -12
  44. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -5
  45. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +18 -3
  46. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +100 -76
  47. package/edit/token.vue +29 -68
  48. package/list/provisioning.cattle.io.cluster.vue +2 -2
  49. package/models/__tests__/chart.test.ts +2 -2
  50. package/models/chart.js +3 -3
  51. package/models/token.js +0 -4
  52. package/package.json +8 -8
  53. package/pages/account/index.vue +67 -96
  54. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +108 -24
  55. package/pages/c/_cluster/apps/charts/index.vue +1 -11
  56. package/pages/c/_cluster/explorer/index.vue +2 -19
  57. package/pages/c/_cluster/explorer/tools/index.vue +1 -1
  58. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  59. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  60. package/pkg/auto-import.js +41 -0
  61. package/plugins/dashboard-store/resource-class.js +2 -2
  62. package/plugins/steve/__tests__/steve-class.test.ts +1 -1
  63. package/plugins/steve/steve-class.js +3 -3
  64. package/plugins/steve/steve-pagination-utils.ts +2 -4
  65. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +7 -7
  66. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +5 -2
  67. package/rancher-components/RcIcon/types.ts +2 -2
  68. package/rancher-components/RcItemCard/RcItemCard.vue +8 -1
  69. package/rancher-components/RcSection/RcSection.test.ts +323 -0
  70. package/rancher-components/RcSection/RcSection.vue +252 -0
  71. package/rancher-components/RcSection/RcSectionActions.test.ts +212 -0
  72. package/rancher-components/RcSection/RcSectionActions.vue +85 -0
  73. package/rancher-components/RcSection/RcSectionBadges.test.ts +149 -0
  74. package/rancher-components/RcSection/RcSectionBadges.vue +29 -0
  75. package/rancher-components/RcSection/index.ts +12 -0
  76. package/rancher-components/RcSection/types.ts +86 -0
  77. package/scripts/test-plugins-build.sh +5 -4
  78. package/types/shell/index.d.ts +93 -108
  79. package/utils/style.ts +17 -0
  80. package/utils/svg-filter.js +4 -3
  81. package/utils/units.js +14 -5
  82. package/models/ext.cattle.io.token.js +0 -48
@@ -0,0 +1,212 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import RcSectionActions from './RcSectionActions.vue';
3
+ import type { ActionConfig } from './types';
4
+
5
+ // Stubs for child components to isolate unit tests
6
+ const RcButtonStub = {
7
+ name: 'RcButton',
8
+ props: ['variant', 'size', 'leftIcon', 'ariaLabel'],
9
+ emits: ['click'],
10
+ template: '<button class="rc-button" :data-variant="variant" :data-left-icon="leftIcon" :aria-label="ariaLabel" @click="$emit(\'click\', $event)"><slot /></button>',
11
+ };
12
+
13
+ const RcDropdownStub = {
14
+ name: 'RcDropdown',
15
+ template: '<div class="rc-dropdown"><slot /><slot name="dropdownCollection" /></div>',
16
+ };
17
+
18
+ const RcDropdownTriggerStub = {
19
+ name: 'RcDropdownTrigger',
20
+ template: '<button class="rc-dropdown-trigger"><slot /></button>',
21
+ };
22
+
23
+ const RcDropdownItemStub = {
24
+ name: 'RcDropdownItem',
25
+ props: ['ariaLabel'],
26
+ emits: ['click'],
27
+ template: '<div class="rc-dropdown-item" :aria-label="ariaLabel" @click="$emit(\'click\', $event)"><slot name="before" /><slot /></div>',
28
+ };
29
+
30
+ const RcIconStub = {
31
+ name: 'RcIcon',
32
+ props: ['type', 'size'],
33
+ template: '<span class="rc-icon" :data-type="type" />',
34
+ };
35
+
36
+ const globalStubs = {
37
+ RcButton: RcButtonStub,
38
+ RcDropdown: RcDropdownStub,
39
+ RcDropdownTrigger: RcDropdownTriggerStub,
40
+ RcDropdownItem: RcDropdownItemStub,
41
+ RcIcon: RcIconStub,
42
+ };
43
+
44
+ describe('component: RcSectionActions', () => {
45
+ function createWrapper(actions: ActionConfig[]) {
46
+ return mount(RcSectionActions, {
47
+ props: { actions },
48
+ global: { stubs: globalStubs },
49
+ });
50
+ }
51
+
52
+ describe('with fewer than 3 actions', () => {
53
+ it('should render all actions as primary buttons when 1 action is provided', () => {
54
+ const action = jest.fn();
55
+ const wrapper = createWrapper([{ label: 'Edit', action }]);
56
+
57
+ const buttons = wrapper.findAll('.rc-button');
58
+
59
+ expect(buttons).toHaveLength(1);
60
+ expect(buttons[0].text()).toContain('Edit');
61
+ });
62
+
63
+ it('should render all actions as primary buttons when 2 actions are provided', () => {
64
+ const wrapper = createWrapper([
65
+ { label: 'Edit', action: jest.fn() },
66
+ { label: 'Delete', action: jest.fn() },
67
+ ]);
68
+
69
+ expect(wrapper.findAll('.rc-button')).toHaveLength(2);
70
+ });
71
+
72
+ it('should not render the overflow dropdown when fewer than 3 actions', () => {
73
+ const wrapper = createWrapper([
74
+ { label: 'Edit', action: jest.fn() },
75
+ { label: 'Delete', action: jest.fn() },
76
+ ]);
77
+
78
+ expect(wrapper.find('.rc-dropdown').exists()).toBe(false);
79
+ });
80
+ });
81
+
82
+ describe('with 3 or more actions', () => {
83
+ it('should render only the first 2 actions as primary buttons', () => {
84
+ const wrapper = createWrapper([
85
+ { label: 'A', action: jest.fn() },
86
+ { label: 'B', action: jest.fn() },
87
+ { label: 'C', action: jest.fn() },
88
+ ]);
89
+
90
+ const buttons = wrapper.findAll('.rc-button');
91
+
92
+ expect(buttons).toHaveLength(2);
93
+ expect(buttons[0].text()).toContain('A');
94
+ expect(buttons[1].text()).toContain('B');
95
+ });
96
+
97
+ it('should render the overflow dropdown', () => {
98
+ const wrapper = createWrapper([
99
+ { label: 'A', action: jest.fn() },
100
+ { label: 'B', action: jest.fn() },
101
+ { label: 'C', action: jest.fn() },
102
+ ]);
103
+
104
+ expect(wrapper.find('.rc-dropdown').exists()).toBe(true);
105
+ });
106
+
107
+ it('should place remaining actions in the overflow dropdown', () => {
108
+ const wrapper = createWrapper([
109
+ { label: 'A', action: jest.fn() },
110
+ { label: 'B', action: jest.fn() },
111
+ { label: 'C', action: jest.fn() },
112
+ { label: 'D', action: jest.fn() },
113
+ ]);
114
+
115
+ const dropdownItems = wrapper.findAll('.rc-dropdown-item');
116
+
117
+ expect(dropdownItems).toHaveLength(2);
118
+ expect(dropdownItems[0].text()).toContain('C');
119
+ expect(dropdownItems[1].text()).toContain('D');
120
+ });
121
+ });
122
+
123
+ describe('action callbacks', () => {
124
+ it('should invoke the action callback when a primary button is clicked', async() => {
125
+ const action = jest.fn();
126
+ const wrapper = createWrapper([{ label: 'Edit', action }]);
127
+
128
+ await wrapper.find('.rc-button').trigger('click');
129
+
130
+ expect(action).toHaveBeenCalledTimes(1);
131
+ });
132
+
133
+ it('should invoke the correct action callback for overflow items', async() => {
134
+ const actionC = jest.fn();
135
+ const wrapper = createWrapper([
136
+ { label: 'A', action: jest.fn() },
137
+ { label: 'B', action: jest.fn() },
138
+ { label: 'C', action: actionC },
139
+ ]);
140
+
141
+ await wrapper.find('.rc-dropdown-item').trigger('click');
142
+
143
+ expect(actionC).toHaveBeenCalledTimes(1);
144
+ });
145
+ });
146
+
147
+ describe('variant resolution', () => {
148
+ it('should use "link" variant for actions with a label', () => {
149
+ const wrapper = createWrapper([{ label: 'Edit', action: jest.fn() }]);
150
+
151
+ expect(wrapper.find('.rc-button').attributes('data-variant')).toBe('link');
152
+ });
153
+
154
+ it('should use "link" variant for icon-only actions', () => {
155
+ const wrapper = createWrapper([{ icon: 'copy', action: jest.fn() }]);
156
+
157
+ expect(wrapper.find('.rc-button').attributes('data-variant')).toBe('link');
158
+ });
159
+
160
+ it('should render icon inside button for icon-only actions', () => {
161
+ const wrapper = createWrapper([{ icon: 'copy', action: jest.fn() }]);
162
+
163
+ expect(wrapper.find('.rc-button .rc-icon').exists()).toBe(true);
164
+ expect(wrapper.find('.rc-button .rc-icon').attributes('data-type')).toBe('copy');
165
+ });
166
+
167
+ it('should use left-icon for labeled actions with an icon', () => {
168
+ const wrapper = createWrapper([{
169
+ label: 'Edit', icon: 'edit', action: jest.fn()
170
+ }]);
171
+
172
+ expect(wrapper.find('.rc-button').attributes('data-left-icon')).toBe('edit');
173
+ });
174
+ });
175
+
176
+ describe('ariaLabel', () => {
177
+ it('should set aria-label on a primary button when ariaLabel is provided', () => {
178
+ const wrapper = createWrapper([{
179
+ icon: 'copy', ariaLabel: 'Copy item', action: jest.fn()
180
+ }]);
181
+
182
+ expect(wrapper.find('.rc-button').attributes('aria-label')).toBe('Copy item');
183
+ });
184
+
185
+ it('should not set aria-label on a primary button when ariaLabel is omitted', () => {
186
+ const wrapper = createWrapper([{ label: 'Edit', action: jest.fn() }]);
187
+
188
+ expect(wrapper.find('.rc-button').attributes('aria-label')).toBeUndefined();
189
+ });
190
+
191
+ it('should set aria-label on overflow dropdown items when ariaLabel is provided', () => {
192
+ const wrapper = createWrapper([
193
+ { label: 'A', action: jest.fn() },
194
+ { label: 'B', action: jest.fn() },
195
+ {
196
+ label: 'C', ariaLabel: 'Do C action', action: jest.fn()
197
+ },
198
+ ]);
199
+
200
+ expect(wrapper.find('.rc-dropdown-item').attributes('aria-label')).toBe('Do C action');
201
+ });
202
+ });
203
+
204
+ describe('empty actions', () => {
205
+ it('should render nothing when actions array is empty', () => {
206
+ const wrapper = createWrapper([]);
207
+
208
+ expect(wrapper.findAll('.rc-button')).toHaveLength(0);
209
+ expect(wrapper.find('.rc-dropdown').exists()).toBe(false);
210
+ });
211
+ });
212
+ });
@@ -0,0 +1,85 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+ import RcButton from '@components/RcButton/RcButton.vue';
4
+ import RcDropdown from '@components/RcDropdown/RcDropdown.vue';
5
+ import RcDropdownTrigger from '@components/RcDropdown/RcDropdownTrigger.vue';
6
+ import RcDropdownItem from '@components/RcDropdown/RcDropdownItem.vue';
7
+ import RcIcon from '@components/RcIcon/RcIcon.vue';
8
+
9
+ import type { RcSectionActionsProps } from './types';
10
+
11
+ const props = defineProps<RcSectionActionsProps>();
12
+
13
+ const primaryActions = computed(() => (props.actions.length < 3 ? props.actions : props.actions.slice(0, 2)));
14
+
15
+ const overflowActions = computed(() => (props.actions.length < 3 ? [] : props.actions.slice(2)));
16
+ </script>
17
+
18
+ <template>
19
+ <RcButton
20
+ v-for="(action, index) in primaryActions"
21
+ :key="index"
22
+ :class="{ 'icon-action': !action.label }"
23
+ variant="link"
24
+ size="medium"
25
+ :left-icon="action.label && action.icon ? (action.icon as any) : undefined"
26
+ :aria-label="action.ariaLabel"
27
+ @click.stop="action.action"
28
+ >
29
+ <RcIcon
30
+ v-if="!action.label && action.icon"
31
+ :type="action.icon as any"
32
+ size="medium"
33
+ />
34
+ <template v-if="action.label">
35
+ {{ action.label }}
36
+ </template>
37
+ </RcButton>
38
+
39
+ <RcDropdown
40
+ v-if="overflowActions.length"
41
+ placement="bottom-end"
42
+ @click.stop
43
+ >
44
+ <RcDropdownTrigger
45
+ class="icon-action"
46
+ variant="link"
47
+ size="medium"
48
+ aria-label="More actions"
49
+ >
50
+ <RcIcon
51
+ type="actions"
52
+ size="medium"
53
+ />
54
+ </RcDropdownTrigger>
55
+
56
+ <template #dropdownCollection>
57
+ <RcDropdownItem
58
+ v-for="(action, index) in overflowActions"
59
+ :key="index"
60
+ :aria-label="action.ariaLabel"
61
+ @click.stop="action.action"
62
+ >
63
+ <template
64
+ v-if="action.icon"
65
+ #before
66
+ >
67
+ <RcIcon
68
+ :type="action.icon as any"
69
+ size="small"
70
+ />
71
+ </template>
72
+ {{ action.label }}
73
+ </RcDropdownItem>
74
+ </template>
75
+ </RcDropdown>
76
+ </template>
77
+
78
+ <style lang="scss" scoped>
79
+
80
+ .rc-button.btn-medium.variant-link {
81
+ &, &:hover {
82
+ color: var(--rc-section-action-color);
83
+ }
84
+ }
85
+ </style>
@@ -0,0 +1,149 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import RcSectionBadges from './RcSectionBadges.vue';
3
+
4
+ // Stub RcStatusBadge to avoid pulling in its full dependency tree
5
+ const RcStatusBadgeStub = {
6
+ name: 'RcStatusBadge',
7
+ props: ['status'],
8
+ template: '<span class="rc-status-badge" :data-status="status"><slot /></span>',
9
+ };
10
+
11
+ describe('component: RcSectionBadges', () => {
12
+ function createWrapper(badges: { label: string; status: string; tooltip?: string }[]) {
13
+ return mount(RcSectionBadges, {
14
+ props: { badges },
15
+ global: {
16
+ stubs: { RcStatusBadge: RcStatusBadgeStub },
17
+ directives: { 'clean-tooltip': () => {} },
18
+ },
19
+ });
20
+ }
21
+
22
+ it('should render all badges when count is within the limit', () => {
23
+ const badges = [
24
+ { label: 'Active', status: 'success' },
25
+ { label: 'Pending', status: 'warning' },
26
+ ];
27
+ const wrapper = createWrapper(badges);
28
+ const rendered = wrapper.findAll('.rc-status-badge');
29
+
30
+ expect(rendered).toHaveLength(2);
31
+ expect(rendered[0].text()).toContain('Active');
32
+ expect(rendered[1].text()).toContain('Pending');
33
+ });
34
+
35
+ it('should render exactly 3 badges when 3 are provided', () => {
36
+ const badges = [
37
+ { label: 'A', status: 'success' },
38
+ { label: 'B', status: 'warning' },
39
+ { label: 'C', status: 'error' },
40
+ ];
41
+ const wrapper = createWrapper(badges);
42
+
43
+ expect(wrapper.findAll('.rc-status-badge')).toHaveLength(3);
44
+ });
45
+
46
+ it('should render a maximum of 3 badges when more than 3 are provided', () => {
47
+ const badges = [
48
+ { label: 'A', status: 'success' },
49
+ { label: 'B', status: 'warning' },
50
+ { label: 'C', status: 'error' },
51
+ { label: 'D', status: 'info' },
52
+ ];
53
+ const wrapper = createWrapper(badges);
54
+ const rendered = wrapper.findAll('.rc-status-badge');
55
+
56
+ expect(rendered).toHaveLength(3);
57
+ expect(rendered[0].text()).toContain('A');
58
+ expect(rendered[1].text()).toContain('B');
59
+ expect(rendered[2].text()).toContain('C');
60
+ });
61
+
62
+ it('should pass the correct status prop to each badge', () => {
63
+ const badges = [
64
+ { label: 'A', status: 'success' },
65
+ { label: 'B', status: 'error' },
66
+ ];
67
+ const wrapper = createWrapper(badges);
68
+ const rendered = wrapper.findAll('.rc-status-badge');
69
+
70
+ expect(rendered[0].attributes('data-status')).toBe('success');
71
+ expect(rendered[1].attributes('data-status')).toBe('error');
72
+ });
73
+
74
+ it('should render nothing when badges array is empty', () => {
75
+ const wrapper = createWrapper([]);
76
+
77
+ expect(wrapper.findAll('.rc-status-badge')).toHaveLength(0);
78
+ });
79
+
80
+ it('should render a single badge', () => {
81
+ const wrapper = createWrapper([{ label: 'Only', status: 'info' }]);
82
+
83
+ expect(wrapper.findAll('.rc-status-badge')).toHaveLength(1);
84
+ expect(wrapper.find('.rc-status-badge').text()).toContain('Only');
85
+ });
86
+
87
+ describe('tooltip', () => {
88
+ it('should pass tooltip value to v-clean-tooltip directive', () => {
89
+ const badges = [
90
+ {
91
+ label: 'Active', status: 'success', tooltip: 'All systems operational'
92
+ },
93
+ ];
94
+ const wrapper = createWrapper(badges);
95
+
96
+ // The directive is registered; if the template used an unknown directive it would throw.
97
+ // Verify the badge still renders correctly when tooltip is provided.
98
+ expect(wrapper.findAll('.rc-status-badge')).toHaveLength(1);
99
+ expect(wrapper.find('.rc-status-badge').text()).toContain('Active');
100
+ });
101
+
102
+ it('should render badges without tooltip when tooltip is omitted', () => {
103
+ const badges = [
104
+ { label: 'Pending', status: 'warning' },
105
+ ];
106
+ const wrapper = createWrapper(badges);
107
+
108
+ expect(wrapper.findAll('.rc-status-badge')).toHaveLength(1);
109
+ expect(wrapper.find('.rc-status-badge').text()).toContain('Pending');
110
+ });
111
+ });
112
+
113
+ describe('console warning', () => {
114
+ it('should warn when more than 3 badges are provided', () => {
115
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
116
+ const badges = [
117
+ { label: 'A', status: 'success' },
118
+ { label: 'B', status: 'warning' },
119
+ { label: 'C', status: 'error' },
120
+ { label: 'D', status: 'info' },
121
+ ];
122
+
123
+ createWrapper(badges);
124
+
125
+ expect(warnSpy).toHaveBeenCalledWith(
126
+ expect.stringContaining('[RcSectionBadges]: Received 4 badges but only 3 are allowed')
127
+ );
128
+
129
+ warnSpy.mockRestore();
130
+ });
131
+
132
+ it('should not warn when 3 or fewer badges are provided', () => {
133
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
134
+ const badges = [
135
+ { label: 'A', status: 'success' },
136
+ { label: 'B', status: 'warning' },
137
+ { label: 'C', status: 'error' },
138
+ ];
139
+
140
+ createWrapper(badges);
141
+
142
+ expect(warnSpy).not.toHaveBeenCalledWith(
143
+ expect.stringContaining('[RcSectionBadges]')
144
+ );
145
+
146
+ warnSpy.mockRestore();
147
+ });
148
+ });
149
+ });
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+ import RcStatusBadge from '@components/Pill/RcStatusBadge';
4
+ import type { RcSectionBadgesProps } from './types';
5
+
6
+ const MAX_BADGES = 3;
7
+
8
+ const props = defineProps<RcSectionBadgesProps>();
9
+
10
+ const visibleBadges = computed(() => {
11
+ if (props.badges.length > MAX_BADGES) {
12
+ // eslint-disable-next-line no-console
13
+ console.warn(`[RcSectionBadges]: Received ${ props.badges.length } badges but only ${ MAX_BADGES } are allowed. Extra badges will be hidden.`);
14
+ }
15
+
16
+ return props.badges.slice(0, MAX_BADGES);
17
+ });
18
+ </script>
19
+
20
+ <template>
21
+ <RcStatusBadge
22
+ v-for="(badge, i) in visibleBadges"
23
+ :key="i"
24
+ v-clean-tooltip="badge.tooltip"
25
+ :status="badge.status"
26
+ >
27
+ {{ badge.label }}
28
+ </RcStatusBadge>
29
+ </template>
@@ -0,0 +1,12 @@
1
+ export { default as RcSection } from './RcSection.vue';
2
+ export { default as RcSectionBadges } from './RcSectionBadges.vue';
3
+ export { default as RcSectionActions } from './RcSectionActions.vue';
4
+ export type {
5
+ RcSectionProps,
6
+ SectionType,
7
+ SectionMode,
8
+ BadgeConfig,
9
+ RcSectionBadgesProps,
10
+ ActionConfig,
11
+ RcSectionActionsProps,
12
+ } from './types';
@@ -0,0 +1,86 @@
1
+ import type { Status } from '@components/utils/status';
2
+ import type { RcIconType } from '@components/RcIcon/types';
3
+
4
+ export type SectionType = 'primary' | 'secondary';
5
+
6
+ export type SectionMode = 'with-header' | 'no-header';
7
+
8
+ export type SectionBackground = 'primary' | 'secondary';
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Badge helpers
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export interface BadgeConfig {
15
+ /** Display text inside the badge. */
16
+ label: string;
17
+ /** Status colour of the badge. */
18
+ status: Status;
19
+ /** Optional tooltip text shown on hover. */
20
+ tooltip?: string;
21
+ }
22
+
23
+ export interface RcSectionBadgesProps {
24
+ badges: BadgeConfig[];
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Action helpers
29
+ // ---------------------------------------------------------------------------
30
+
31
+ export interface ActionConfig {
32
+ /** Button label. When omitted the button renders as icon-only (ghost). */
33
+ label?: string;
34
+ /** Icon shown on the button (left position for labeled, sole content for icon-only). */
35
+ icon?: RcIconType;
36
+ /** Accessible label for the button. Required when `label` is omitted so screen readers can describe the action. */
37
+ ariaLabel?: string;
38
+ /** Callback invoked when the action is clicked. */
39
+ action: () => void;
40
+ }
41
+
42
+ export interface RcSectionActionsProps {
43
+ actions: ActionConfig[];
44
+ }
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Section
48
+ // ---------------------------------------------------------------------------
49
+
50
+ export interface RcSectionProps {
51
+ /**
52
+ * Visual type of the section.
53
+ * - Primary: no lateral padding, border-radius, or background (inherits from parent). Bolder title (700).
54
+ * - Secondary: has lateral padding (16px), border-radius (8px), and background color. Lighter title (600).
55
+ */
56
+ type: SectionType;
57
+
58
+ /**
59
+ * Controls whether the section renders with a header or without.
60
+ * - 'with-header': renders the full header (title, badges, actions, errors).
61
+ * - 'no-header': hides the header entirely; content padding adjusts to 16px vertical.
62
+ */
63
+ mode: SectionMode;
64
+
65
+ /**
66
+ * Background color of the section. When omitted, automatically alternates
67
+ * from the parent RcSection's background (primary -> secondary -> primary).
68
+ */
69
+ background?: SectionBackground;
70
+
71
+ /**
72
+ * Whether the section can be expanded/collapsed via the header.
73
+ */
74
+ expandable: boolean;
75
+
76
+ /**
77
+ * Whether the section content is expanded. Only applies when `expandable` is true.
78
+ * Supports `v-model:expanded`.
79
+ */
80
+ expanded?: boolean;
81
+
82
+ /**
83
+ * The section title text. Can also be provided via the `title` slot.
84
+ */
85
+ title?: string;
86
+ }
@@ -235,10 +235,11 @@ function clone_repo_test_extension_build() {
235
235
 
236
236
  # Here we just add the extension that we want to include as a check (all our official extensions should be included here)
237
237
  # Don't forget to add the unit tests exception to clone_repo_test_extension_build function if a new extension has those
238
- clone_repo_test_extension_build "rancher" "kubewarden-ui" "kubewarden"
239
- clone_repo_test_extension_build "rancher" "elemental-ui" "elemental"
238
+ # TODO: ISSUE #16858 - Reenable the tests as packages migrate to node version 24
239
+ # clone_repo_test_extension_build "rancher" "kubewarden-ui" "kubewarden"
240
+ # clone_repo_test_extension_build "rancher" "elemental-ui" "elemental"
240
241
  clone_repo_test_extension_build "neuvector" "manager-ext" "neuvector-ui-ext"
241
- clone_repo_test_extension_build "StackVista" "rancher-extension-stackstate" "observability"
242
- clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
242
+ # clone_repo_test_extension_build "StackVista" "rancher-extension-stackstate" "observability"
243
+ # clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
243
244
 
244
245
  echo "All done"