@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.4
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.
- package/assets/styles/global/_layout.scss +4 -0
- package/assets/translations/en-us.yaml +144 -41
- package/assets/translations/zh-hans.yaml +1 -7
- package/chart/monitoring/ClusterSelector.vue +0 -21
- package/chart/monitoring/prometheus/index.vue +6 -3
- package/components/CruResource.vue +161 -14
- package/components/ExplorerMembers.vue +8 -4
- package/components/ExplorerProjectsNamespaces.vue +10 -6
- package/components/GrowlManager.vue +4 -0
- package/components/MgmtNodeList.vue +184 -0
- package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
- package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
- package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
- package/components/ResourceDetail/index.vue +1 -1
- package/components/ResourceList/Masthead.vue +7 -1
- package/components/ResourceList/index.vue +82 -1
- package/components/RichTranslation.vue +5 -2
- package/components/Setting.vue +1 -0
- package/components/SubtleLink.vue +31 -6
- package/components/Tabbed/Tab.vue +29 -3
- package/components/Tabbed/index.vue +25 -3
- package/components/TableOfContents/TableOfContents.vue +109 -0
- package/components/TableOfContents/composables.ts +258 -0
- package/components/Window/ContainerShell.vue +21 -11
- package/components/Window/__tests__/ContainerShell.test.ts +107 -37
- package/components/Wizard.vue +9 -4
- package/components/fleet/AppCoChartGrid.vue +401 -0
- package/components/fleet/AppCoEmptyState.vue +127 -0
- package/components/fleet/AppCoPageHeader.vue +119 -0
- package/components/fleet/AppCoVersionSelect.vue +70 -0
- package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
- package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
- package/components/fleet/FleetClusterTargets/index.vue +189 -146
- package/components/fleet/FleetIntro.vue +7 -3
- package/components/fleet/FleetNoWorkspaces.vue +7 -3
- package/components/fleet/FleetSecretSelector.vue +5 -3
- package/components/fleet/FleetValuesFrom.vue +8 -2
- package/components/fleet/GitRepoTargetTab.vue +0 -2
- package/components/fleet/HelmOpAdvancedTab.vue +19 -53
- package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
- package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
- package/components/fleet/HelmOpResourcesSection.vue +82 -0
- package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
- package/components/fleet/HelmOpTargetTab.vue +64 -60
- package/components/fleet/HelmOpValuesTab.vue +129 -105
- package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
- package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
- package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
- package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
- package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
- package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
- package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
- package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
- package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
- package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
- package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
- package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
- package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
- package/components/fleet/dashboard/Empty.vue +8 -4
- package/components/fleet/dashboard/ResourceCard.vue +28 -0
- package/components/fleet/dashboard/ResourceDetails.vue +28 -0
- package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
- package/components/form/ArrayList.vue +61 -4
- package/components/form/KeyValue.vue +23 -2
- package/components/form/LabeledSelect.vue +39 -1
- package/components/form/Labels.vue +22 -3
- package/components/form/NameNsDescription.vue +13 -5
- package/components/form/ResourceTabs/index.vue +1 -0
- package/components/form/__tests__/NameNsDescription.test.ts +75 -0
- package/components/formatter/InternalExternalIP.vue +10 -4
- package/components/formatter/ServiceTargets.vue +26 -7
- package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
- package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
- package/components/nav/Header.vue +4 -0
- package/components/nav/TopLevelMenu.vue +7 -2
- package/components/nav/__tests__/Header.test.ts +15 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
- package/components/templates/default.vue +9 -4
- package/components/templates/home.vue +9 -4
- package/components/templates/plain.vue +9 -4
- package/composables/useHelmOpResources.test.ts +56 -0
- package/composables/useHelmOpResources.ts +32 -0
- package/composables/useStateColor.test.ts +325 -0
- package/composables/useStateColor.ts +128 -0
- package/config/home-links.js +1 -1
- package/config/labels-annotations.js +1 -0
- package/config/product/explorer.js +17 -4
- package/config/product/manager.js +2 -0
- package/config/router/index.js +16 -0
- package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
- package/config/router/navigation-guards/authentication.js +10 -4
- package/config/router/routes.js +20 -6
- package/config/settings.ts +0 -2
- package/config/table-headers.js +3 -4
- package/config/types.js +9 -0
- package/core/plugin-products-base.ts +3 -3
- package/core/plugin-types.ts +83 -30
- package/core/plugin.ts +3 -0
- package/core/types-provisioning.ts +34 -1
- package/core/types.ts +15 -2
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
- package/detail/__tests__/workload.test.ts +3 -152
- package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +30 -4
- package/detail/workload/index.vue +12 -55
- package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
- package/edit/auth/__tests__/azuread.test.ts +34 -9
- package/edit/auth/__tests__/github.test.ts +234 -0
- package/edit/auth/__tests__/oidc.test.ts +26 -6
- package/edit/auth/__tests__/saml.test.ts +196 -0
- package/edit/auth/azuread.vue +128 -95
- package/edit/auth/github.vue +72 -13
- package/edit/auth/ldap/__tests__/index.test.ts +206 -0
- package/edit/auth/ldap/config.vue +8 -0
- package/edit/auth/ldap/index.vue +75 -1
- package/edit/auth/oidc.vue +119 -73
- package/edit/auth/saml.vue +76 -12
- package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
- package/edit/fleet.cattle.io.helmop.vue +491 -136
- package/edit/management.cattle.io.user.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/list/group.principal.vue +5 -4
- package/list/harvesterhci.io.management.cluster.vue +8 -9
- package/list/management.cattle.io.user.vue +12 -9
- package/list/provisioning.cattle.io.cluster.vue +16 -10
- package/mixins/__tests__/auth-config.test.ts +90 -0
- package/mixins/__tests__/chart.test.ts +94 -0
- package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
- package/mixins/auth-config.js +7 -0
- package/mixins/chart.js +11 -2
- package/mixins/child-hook.js +12 -6
- package/mixins/create-edit-view/impl.js +5 -3
- package/mixins/resource-fetch-api-pagination.js +21 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
- package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
- package/models/__tests__/fleet-application.test.ts +175 -0
- package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
- package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
- package/models/__tests__/management.cattle.io.node.ts +22 -0
- package/models/__tests__/namespace.test.ts +36 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +49 -0
- package/models/__tests__/workload.test.ts +401 -26
- package/models/catalog.cattle.io.clusterrepo.js +28 -4
- package/models/compliance.cattle.io.clusterscan.js +39 -4
- package/models/fleet-application.js +4 -0
- package/models/fleet.cattle.io.helmop.js +20 -1
- package/models/management.cattle.io.cluster.js +18 -2
- package/models/management.cattle.io.node.js +44 -3
- package/models/namespace.js +1 -1
- package/models/pod.js +33 -1
- package/models/provisioning.cattle.io.cluster.js +5 -5
- package/models/workload.js +108 -13
- package/models/workload.service.js +5 -0
- package/package.json +14 -13
- package/pages/about.vue +5 -6
- package/pages/auth/login.vue +0 -35
- package/pages/auth/setup.vue +11 -0
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
- package/pages/c/_cluster/apps/charts/chart.vue +2 -1
- package/pages/c/_cluster/apps/charts/index.vue +48 -10
- package/pages/c/_cluster/apps/charts/install.vue +122 -116
- package/pages/c/_cluster/auth/roles/index.vue +5 -4
- package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
- package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
- package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
- package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
- package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
- package/pages/c/_cluster/fleet/application/create.vue +187 -136
- package/pages/c/_cluster/fleet/application/index.vue +5 -3
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
- package/pages/c/_cluster/fleet/index.vue +2 -2
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
- package/pages/c/_cluster/uiplugins/index.vue +15 -0
- package/pages/fail-whale.vue +16 -11
- package/pages/home.vue +16 -46
- package/plugins/clean-html.d.ts +9 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
- package/plugins/dashboard-store/resource-class.js +62 -7
- package/plugins/steve/__tests__/actions.test.ts +212 -0
- package/plugins/steve/actions.js +96 -0
- package/plugins/steve/steve-pagination-utils.ts +1 -1
- package/rancher-components/Accordion/Accordion.vue +53 -9
- package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
- package/rancher-components/Form/Radio/RadioButton.vue +17 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
- package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
- package/rancher-components/RcButton/RcButton.test.ts +103 -0
- package/rancher-components/RcButton/RcButton.vue +94 -15
- package/rancher-components/RcButton/types.ts +3 -0
- package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
- package/rancher-components/RcSection/RcSection.vue +28 -3
- package/scripts/extension/helm/package/Dockerfile +1 -1
- package/scripts/test-plugins-build.sh +2 -1
- package/store/__tests__/notifications.test.ts +434 -0
- package/store/catalog.js +57 -0
- package/store/plugins.js +7 -4
- package/types/components/buttonGroup.ts +5 -0
- package/types/shell/index.d.ts +104 -70
- package/utils/__tests__/auth.test.ts +273 -0
- package/utils/__tests__/computed.test.ts +193 -0
- package/utils/__tests__/cspAdaptor.test.ts +163 -0
- package/utils/__tests__/dom.test.ts +81 -0
- package/utils/__tests__/duration.test.ts +37 -1
- package/utils/__tests__/dynamic-importer.test.ts +102 -0
- package/utils/__tests__/fleet-appco.test.ts +312 -0
- package/utils/__tests__/monitoring.test.ts +130 -0
- package/utils/__tests__/object.test.ts +22 -0
- package/utils/__tests__/platform.test.ts +91 -0
- package/utils/__tests__/position.test.ts +237 -0
- package/utils/__tests__/provider.test.ts +51 -1
- package/utils/__tests__/queue.test.ts +232 -0
- package/utils/__tests__/release-notes.test.ts +221 -0
- package/utils/__tests__/router.test.js +254 -1
- package/utils/__tests__/select.test.ts +208 -0
- package/utils/__tests__/time.test.ts +265 -1
- package/utils/__tests__/title.test.ts +47 -0
- package/utils/__tests__/width.test.ts +53 -0
- package/utils/__tests__/window.test.ts +158 -0
- package/utils/__tests__/xccdf.test.ts +126 -6
- package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
- package/utils/crypto/__tests__/index.test.ts +144 -0
- package/utils/duration.ts +104 -0
- package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
- package/utils/dynamic-content/info.ts +2 -1
- package/utils/error.js +13 -0
- package/utils/fleet-appco.ts +323 -0
- package/utils/object.js +22 -2
- package/utils/provider.ts +12 -0
- package/utils/validators/__tests__/container-images.test.ts +104 -0
- package/utils/validators/__tests__/flow-output.test.ts +91 -0
- package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
- package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
- package/utils/xccdf.ts +39 -42
- package/vue.config.js +1 -1
- package/pages/support/index.vue +0 -264
- package/utils/duration.js +0 -43
|
@@ -125,6 +125,14 @@ export default defineComponent({
|
|
|
125
125
|
default: undefined
|
|
126
126
|
},
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Use body text color for the label instead of the default input-label color.
|
|
130
|
+
*/
|
|
131
|
+
useBodyTextColor: {
|
|
132
|
+
type: Boolean,
|
|
133
|
+
default: false
|
|
134
|
+
},
|
|
135
|
+
|
|
128
136
|
/**
|
|
129
137
|
* Inherited global identifier prefix for tests
|
|
130
138
|
* Define a term based on the parent component to avoid conflicts on multiple components
|
|
@@ -316,6 +324,7 @@ export default defineComponent({
|
|
|
316
324
|
<span
|
|
317
325
|
v-if="labelKey"
|
|
318
326
|
:id="idForLabel"
|
|
327
|
+
:class="{ 'body-text-color': useBodyTextColor }"
|
|
319
328
|
>
|
|
320
329
|
<t
|
|
321
330
|
:k="labelKey"
|
|
@@ -325,6 +334,7 @@ export default defineComponent({
|
|
|
325
334
|
<span
|
|
326
335
|
v-else-if="label"
|
|
327
336
|
:id="idForLabel"
|
|
337
|
+
:class="{ 'body-text-color': useBodyTextColor }"
|
|
328
338
|
>{{ label }}</span>
|
|
329
339
|
<i
|
|
330
340
|
v-if="tooltipKey"
|
|
@@ -403,6 +413,10 @@ $fontColor: var(--input-label);
|
|
|
403
413
|
display: inline-flex;
|
|
404
414
|
margin: 0px 10px 0px 5px;
|
|
405
415
|
|
|
416
|
+
.body-text-color {
|
|
417
|
+
color: var(--body-text);
|
|
418
|
+
}
|
|
419
|
+
|
|
406
420
|
&.checkbox-primary {
|
|
407
421
|
color: inherit;
|
|
408
422
|
font-weight: 600;
|
|
@@ -80,6 +80,14 @@ export default defineComponent({
|
|
|
80
80
|
default: false
|
|
81
81
|
},
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Use body text color for the label instead of the default input-label color.
|
|
85
|
+
*/
|
|
86
|
+
useBodyTextColor: {
|
|
87
|
+
type: Boolean,
|
|
88
|
+
default: false
|
|
89
|
+
},
|
|
90
|
+
|
|
83
91
|
/**
|
|
84
92
|
* Radio option Id - used to link to aria-activedescendant
|
|
85
93
|
* when using inside of the context of a Radio Group
|
|
@@ -189,7 +197,11 @@ export default defineComponent({
|
|
|
189
197
|
/>
|
|
190
198
|
<div class="labeling">
|
|
191
199
|
<label
|
|
192
|
-
|
|
200
|
+
class="radio-label m-0"
|
|
201
|
+
:class="{
|
|
202
|
+
'text-muted': muteLabel,
|
|
203
|
+
'body-text-color': useBodyTextColor
|
|
204
|
+
}"
|
|
193
205
|
:for="name"
|
|
194
206
|
>
|
|
195
207
|
<slot
|
|
@@ -314,6 +326,10 @@ $fontColor: var(--input-label);
|
|
|
314
326
|
flex-direction: column;
|
|
315
327
|
|
|
316
328
|
margin: 3px 10px 0px 5px;
|
|
329
|
+
|
|
330
|
+
.body-text-color {
|
|
331
|
+
color: var(--body-text);
|
|
332
|
+
}
|
|
317
333
|
}
|
|
318
334
|
}
|
|
319
335
|
|
|
@@ -12,6 +12,7 @@ interface Option {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export default defineComponent({
|
|
15
|
+
name: 'RadioGroup',
|
|
15
16
|
components: { RadioButton },
|
|
16
17
|
props: {
|
|
17
18
|
/**
|
|
@@ -102,6 +103,14 @@ export default defineComponent({
|
|
|
102
103
|
row: {
|
|
103
104
|
type: Boolean,
|
|
104
105
|
default: false
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Use body text color for the label instead of the default input-label color.
|
|
110
|
+
*/
|
|
111
|
+
useBodyTextColor: {
|
|
112
|
+
type: Boolean,
|
|
113
|
+
default: false
|
|
105
114
|
}
|
|
106
115
|
},
|
|
107
116
|
|
|
@@ -302,6 +311,7 @@ export default defineComponent({
|
|
|
302
311
|
:disabled="isDisabled"
|
|
303
312
|
:data-testid="`radio-button-${i}`"
|
|
304
313
|
:mode="mode"
|
|
314
|
+
:use-body-text-color="useBodyTextColor"
|
|
305
315
|
:prevent-focus-on-radio-groups="true"
|
|
306
316
|
@update:value="$emit('update:value', $event)"
|
|
307
317
|
/>
|
|
@@ -28,7 +28,8 @@ const emit = defineEmits(['close']);
|
|
|
28
28
|
<style lang="scss" scoped>
|
|
29
29
|
.rc-tag {
|
|
30
30
|
display: inline-flex;
|
|
31
|
-
|
|
31
|
+
min-height: 24px;
|
|
32
|
+
padding: 2px 8px;
|
|
32
33
|
align-items: center;
|
|
33
34
|
gap: 8px;
|
|
34
35
|
|
|
@@ -41,7 +42,7 @@ const emit = defineEmits(['close']);
|
|
|
41
42
|
font-size: 13px;
|
|
42
43
|
font-style: normal;
|
|
43
44
|
font-weight: 400;
|
|
44
|
-
line-height:
|
|
45
|
+
line-height: 20px;
|
|
45
46
|
color: var(--body-text);
|
|
46
47
|
|
|
47
48
|
button {
|
|
@@ -139,6 +139,109 @@ describe('rcButton.vue', () => {
|
|
|
139
139
|
});
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
describe('space key navigation', () => {
|
|
143
|
+
it('triggers click when Space is pressed on a link button', async() => {
|
|
144
|
+
const to = { name: 'some-route' };
|
|
145
|
+
const wrapper = mount(RcButton, {
|
|
146
|
+
props: { to },
|
|
147
|
+
global: { stubs: { RouterLink: RouterLinkStub } },
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const link = wrapper.findComponent(RouterLinkStub);
|
|
151
|
+
const clickSpy = jest.spyOn(link.element, 'click');
|
|
152
|
+
|
|
153
|
+
await link.trigger('keyup', { key: ' ' });
|
|
154
|
+
|
|
155
|
+
expect(clickSpy).toHaveBeenCalledWith();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('does not manually trigger click when Space is pressed on a regular button', async() => {
|
|
159
|
+
const wrapper = mount(RcButton);
|
|
160
|
+
const button = wrapper.find('button');
|
|
161
|
+
const clickSpy = jest.spyOn(button.element, 'click');
|
|
162
|
+
|
|
163
|
+
await button.trigger('keyup', { key: ' ' });
|
|
164
|
+
|
|
165
|
+
expect(clickSpy).not.toHaveBeenCalled();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('to and href mutual exclusion', () => {
|
|
170
|
+
it('warns when both "to" and "href" props are provided', () => {
|
|
171
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
172
|
+
|
|
173
|
+
mount(RcButton, {
|
|
174
|
+
props: { to: '/foo', href: 'https://example.com' },
|
|
175
|
+
global: { stubs: { RouterLink: RouterLinkStub } },
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
expect(warnSpy).toHaveBeenCalledWith('[RcButton] "to" and "href" are mutually exclusive. Provide only one.');
|
|
179
|
+
warnSpy.mockRestore();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('does not warn when only "to" is provided', () => {
|
|
183
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
184
|
+
|
|
185
|
+
mount(RcButton, {
|
|
186
|
+
props: { to: '/foo' },
|
|
187
|
+
global: { stubs: { RouterLink: RouterLinkStub } },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(warnSpy).not.toHaveBeenCalledWith('[RcButton] "to" and "href" are mutually exclusive. Provide only one.');
|
|
191
|
+
warnSpy.mockRestore();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('does not warn when only "href" is provided', () => {
|
|
195
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
196
|
+
|
|
197
|
+
mount(RcButton, { props: { href: 'https://example.com' } });
|
|
198
|
+
|
|
199
|
+
expect(warnSpy).not.toHaveBeenCalledWith('[RcButton] "to" and "href" are mutually exclusive. Provide only one.');
|
|
200
|
+
warnSpy.mockRestore();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('href prop', () => {
|
|
205
|
+
it('renders as an <a> element when "href" prop is provided', () => {
|
|
206
|
+
const wrapper = mount(RcButton, { props: { href: 'https://example.com' } });
|
|
207
|
+
|
|
208
|
+
expect(wrapper.find('a').exists()).toBe(true);
|
|
209
|
+
expect(wrapper.find('button').exists()).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('sets the href attribute on the rendered anchor', () => {
|
|
213
|
+
const href = 'https://example.com';
|
|
214
|
+
const wrapper = mount(RcButton, { props: { href } });
|
|
215
|
+
|
|
216
|
+
expect(wrapper.find('a').attributes('href')).toStrictEqual(href);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('sets role="link" when rendered as an anchor', () => {
|
|
220
|
+
const wrapper = mount(RcButton, { props: { href: 'https://example.com' } });
|
|
221
|
+
|
|
222
|
+
expect(wrapper.find('a').attributes('role')).toStrictEqual('link');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('applies button classes when rendered as an anchor', () => {
|
|
226
|
+
const wrapper = mount(RcButton, { props: { href: 'https://example.com', variant: 'secondary' } });
|
|
227
|
+
const anchor = wrapper.find('a');
|
|
228
|
+
|
|
229
|
+
expect(anchor.classes()).toContain('rc-button');
|
|
230
|
+
expect(anchor.classes()).toContain('btn');
|
|
231
|
+
expect(anchor.classes()).toContain('variant-secondary');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('triggers click when Space is pressed on an anchor', async() => {
|
|
235
|
+
const wrapper = mount(RcButton, { props: { href: 'https://example.com' } });
|
|
236
|
+
const anchor = wrapper.find('a');
|
|
237
|
+
const clickSpy = jest.spyOn(anchor.element, 'click');
|
|
238
|
+
|
|
239
|
+
await anchor.trigger('keyup', { key: ' ' });
|
|
240
|
+
|
|
241
|
+
expect(clickSpy).toHaveBeenCalledWith();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
142
245
|
describe('to prop', () => {
|
|
143
246
|
it('renders as a <button> when no "to" prop is provided', () => {
|
|
144
247
|
const wrapper = mount(RcButton);
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* <rc-button variant="primary" @click="doAction">Perform an Action</rc-button>
|
|
9
9
|
*/
|
|
10
|
-
import { computed, ref, resolveComponent } from 'vue';
|
|
11
|
-
import type { RouteLocationRaw } from 'vue-router';
|
|
10
|
+
import { computed, ref, resolveComponent, watchEffect } from 'vue';
|
|
12
11
|
import {
|
|
13
12
|
ButtonVariantProps,
|
|
14
13
|
ButtonSizeProps,
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
ButtonSizeNewProps,
|
|
17
16
|
ButtonSize,
|
|
18
17
|
IconProps,
|
|
18
|
+
NavigationProps,
|
|
19
19
|
} from './types';
|
|
20
20
|
import RcIcon from '@components/RcIcon/RcIcon.vue';
|
|
21
21
|
|
|
@@ -44,16 +44,48 @@ const props = withDefaults(
|
|
|
44
44
|
ButtonSizeProps &
|
|
45
45
|
ButtonVariantNewProps &
|
|
46
46
|
ButtonSizeNewProps &
|
|
47
|
-
IconProps &
|
|
47
|
+
IconProps &
|
|
48
|
+
NavigationProps
|
|
48
49
|
>(),
|
|
49
50
|
{
|
|
50
51
|
size: 'medium',
|
|
51
52
|
to: undefined,
|
|
53
|
+
href: undefined,
|
|
52
54
|
}
|
|
53
55
|
);
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
58
|
+
watchEffect(() => {
|
|
59
|
+
if (props.to && props.href) {
|
|
60
|
+
console.warn('[RcButton] "to" and "href" are mutually exclusive. Provide only one.'); // eslint-disable-line no-console
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const tag = computed(() => {
|
|
66
|
+
if (props.to) {
|
|
67
|
+
return resolveComponent('RouterLink');
|
|
68
|
+
}
|
|
69
|
+
if (props.href) {
|
|
70
|
+
return 'a';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return 'button';
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const role = computed(() => (props.to || props.href ? 'link' : 'button'));
|
|
77
|
+
|
|
78
|
+
const linkProps = computed(() => {
|
|
79
|
+
if (props.to) {
|
|
80
|
+
return { to: props.to };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (props.href) {
|
|
84
|
+
return { href: props.href };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {};
|
|
88
|
+
});
|
|
57
89
|
|
|
58
90
|
const activeVariantClassName = computed(() => {
|
|
59
91
|
if (props.variant === 'multiAction' || props.multiAction) {
|
|
@@ -110,6 +142,22 @@ const focus = () => {
|
|
|
110
142
|
RcFocusTarget?.value?.focus();
|
|
111
143
|
};
|
|
112
144
|
|
|
145
|
+
const preventScroll = (event: KeyboardEvent) => {
|
|
146
|
+
if (tag.value === 'button') {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const handleSpace = (event: KeyboardEvent) => {
|
|
154
|
+
if (tag.value === 'button') {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
(event.target as HTMLElement).click();
|
|
159
|
+
};
|
|
160
|
+
|
|
113
161
|
defineExpose({ focus });
|
|
114
162
|
</script>
|
|
115
163
|
|
|
@@ -118,8 +166,10 @@ defineExpose({ focus });
|
|
|
118
166
|
:is="tag"
|
|
119
167
|
ref="RcFocusTarget"
|
|
120
168
|
:role="role"
|
|
121
|
-
|
|
169
|
+
v-bind="linkProps"
|
|
122
170
|
:class="{ ...buttonClass }"
|
|
171
|
+
@keydown.space="preventScroll"
|
|
172
|
+
@keyup.space="handleSpace"
|
|
123
173
|
>
|
|
124
174
|
<slot
|
|
125
175
|
v-if="$slots.before || props.leftIcon"
|
|
@@ -185,9 +235,12 @@ defineExpose({ focus });
|
|
|
185
235
|
}
|
|
186
236
|
|
|
187
237
|
&:disabled {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
238
|
+
&, &:hover, &:focus {
|
|
239
|
+
color: var(--disabled-text);
|
|
240
|
+
background: var(--disabled-bg);
|
|
241
|
+
border-color: var(--disabled-bg);
|
|
242
|
+
cursor: not-allowed;
|
|
243
|
+
}
|
|
191
244
|
}
|
|
192
245
|
}
|
|
193
246
|
|
|
@@ -210,6 +263,15 @@ defineExpose({ focus });
|
|
|
210
263
|
@include focus-outline;
|
|
211
264
|
outline-offset: 2px;
|
|
212
265
|
}
|
|
266
|
+
|
|
267
|
+
&:disabled {
|
|
268
|
+
&, &:hover, &:focus {
|
|
269
|
+
color: var(--disabled-text);
|
|
270
|
+
background: var(--disabled-bg);
|
|
271
|
+
border-color: var(--disabled-bg);
|
|
272
|
+
cursor: not-allowed;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
213
275
|
}
|
|
214
276
|
|
|
215
277
|
&.variant-tertiary {
|
|
@@ -231,6 +293,15 @@ defineExpose({ focus });
|
|
|
231
293
|
@include focus-outline;
|
|
232
294
|
outline-offset: 2px;
|
|
233
295
|
}
|
|
296
|
+
|
|
297
|
+
&:disabled {
|
|
298
|
+
&, &:hover, &:focus {
|
|
299
|
+
color: var(--disabled-text);
|
|
300
|
+
background: var(--disabled-bg);
|
|
301
|
+
border-color: var(--disabled-bg);
|
|
302
|
+
cursor: not-allowed;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
234
305
|
}
|
|
235
306
|
|
|
236
307
|
&.variant-link {
|
|
@@ -283,11 +354,15 @@ defineExpose({ focus });
|
|
|
283
354
|
&.btn-small {
|
|
284
355
|
//:not(.btn-sm) is being used to make the style more specific to override global styles. We may want to get rid of those styles at some point.
|
|
285
356
|
&, &:not(.btn-sm) {
|
|
286
|
-
line-height
|
|
357
|
+
// Unitless ratio chosen so font-size * line-height (12px * 4/3 = 16px)
|
|
358
|
+
// is a whole pixel value. A fractional computed line-height (e.g. the
|
|
359
|
+
// previous 140% = 16.8px) shifts text ~1px off-center within the
|
|
360
|
+
// flex-centered button in Chrome, but not in Firefox.
|
|
361
|
+
line-height: calc(4 / 3);
|
|
287
362
|
font-size: 12px;
|
|
288
363
|
min-height: 24px;
|
|
289
364
|
|
|
290
|
-
padding: 0 8px;
|
|
365
|
+
padding: var(--rc-button-padding, 0 8px);
|
|
291
366
|
gap: 8px;
|
|
292
367
|
}
|
|
293
368
|
}
|
|
@@ -295,11 +370,13 @@ defineExpose({ focus });
|
|
|
295
370
|
&.btn-medium {
|
|
296
371
|
//:not(.btn-sm) is being used to make the style more specific to override global styles. We may want to get rid of those styles at some point.
|
|
297
372
|
&, &:not(.btn-sm) {
|
|
298
|
-
line-height
|
|
373
|
+
// Unitless ratio chosen so font-size * line-height (14px * 10/7 = 20px)
|
|
374
|
+
// is a whole pixel value. See note in .btn-small for why this matters.
|
|
375
|
+
line-height: calc(10 / 7);
|
|
299
376
|
font-size: 14px;
|
|
300
377
|
min-height: 32px;
|
|
301
378
|
|
|
302
|
-
padding: 0 12px;
|
|
379
|
+
padding: var(--rc-button-padding, 0 12px);
|
|
303
380
|
gap: 8px;
|
|
304
381
|
}
|
|
305
382
|
}
|
|
@@ -307,11 +384,13 @@ defineExpose({ focus });
|
|
|
307
384
|
&.btn-large {
|
|
308
385
|
// This is the default size brought by the global button styling
|
|
309
386
|
&, &:not(.btn-sm) {
|
|
310
|
-
line-height
|
|
387
|
+
// Unitless ratio chosen so font-size * line-height (16px * 1.5 = 24px)
|
|
388
|
+
// is a whole pixel value. See note in .btn-small for why this matters.
|
|
389
|
+
line-height: 1.5;
|
|
311
390
|
font-size: 16px;
|
|
312
391
|
min-height: 40px;
|
|
313
392
|
|
|
314
|
-
padding: 0 16px;
|
|
393
|
+
padding: var(--rc-button-padding, 0 16px);
|
|
315
394
|
gap: 12px;
|
|
316
395
|
}
|
|
317
396
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
1
2
|
import { RcIconType } from '@components/RcIcon/types';
|
|
2
3
|
|
|
4
|
+
export type NavigationProps = { to?: RouteLocationRaw; href?: string }
|
|
5
|
+
|
|
3
6
|
// TODO: 13211 Investigate why `InstanceType<typeof RcButton>` fails prod builds
|
|
4
7
|
// export type RcButtonType = InstanceType<typeof RcButton>
|
|
5
8
|
export type RcButtonType = {
|
|
@@ -36,6 +36,24 @@ describe('rcItemCard', () => {
|
|
|
36
36
|
expect(wrapper.findAll(`[data-testid="item-card-header-statuses-status"]`)).toHaveLength(2);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
// Regression: when image.src is falsy, LazyImage must still render so its own
|
|
40
|
+
// empty-src fallback (generic catalog icon) is shown. Previously the template
|
|
41
|
+
// was gated by `v-else-if="image.src"` and showed an empty box for missing icons.
|
|
42
|
+
it.each(['medium', 'small'] as const)('renders LazyImage with an empty src when image.src is falsy (%s variant)', (variant) => {
|
|
43
|
+
const wrapper = mount(RcItemCard, {
|
|
44
|
+
props: {
|
|
45
|
+
...baseProps,
|
|
46
|
+
variant,
|
|
47
|
+
image: { src: '', alt: { text: 'Logo' } },
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const lazy = wrapper.findComponent({ name: 'LazyImage' });
|
|
52
|
+
|
|
53
|
+
expect(lazy.exists()).toBe(true);
|
|
54
|
+
expect(lazy.props('src')).toBe('');
|
|
55
|
+
});
|
|
56
|
+
|
|
39
57
|
it('renders pill only in medium variant', () => {
|
|
40
58
|
const wrapper = mount(RcItemCard, {
|
|
41
59
|
props: {
|
|
@@ -208,7 +208,7 @@ const cursorValue = computed(() => props.clickable ? 'pointer' : 'auto');
|
|
|
208
208
|
size="xxlarge"
|
|
209
209
|
/>
|
|
210
210
|
</template>
|
|
211
|
-
<template v-else
|
|
211
|
+
<template v-else>
|
|
212
212
|
<LazyImage
|
|
213
213
|
:src="image.src"
|
|
214
214
|
:alt="imageAlt"
|
|
@@ -255,7 +255,7 @@ const cursorValue = computed(() => props.clickable ? 'pointer' : 'auto');
|
|
|
255
255
|
size="xlarge"
|
|
256
256
|
/>
|
|
257
257
|
</template>
|
|
258
|
-
<template v-else
|
|
258
|
+
<template v-else>
|
|
259
259
|
<LazyImage
|
|
260
260
|
:src="image.src"
|
|
261
261
|
:alt="imageAlt"
|
|
@@ -33,9 +33,12 @@
|
|
|
33
33
|
* <p>Section content here</p>
|
|
34
34
|
* </RcSection>
|
|
35
35
|
*/
|
|
36
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
computed, inject, provide, useTemplateRef, type Ref
|
|
38
|
+
} from 'vue';
|
|
37
39
|
import RcButton from '@components/RcButton/RcButton.vue';
|
|
38
40
|
import RcIcon from '@components/RcIcon/RcIcon.vue';
|
|
41
|
+
import { useInSummary } from '@shell/components/TableOfContents/composables';
|
|
39
42
|
import type { RcSectionProps, SectionBackground } from './types';
|
|
40
43
|
|
|
41
44
|
const RC_SECTION_BG_KEY = 'rc-section-background';
|
|
@@ -58,6 +61,24 @@ provide(RC_SECTION_BG_KEY, resolvedBackground);
|
|
|
58
61
|
|
|
59
62
|
const expanded = defineModel<boolean>('expanded', { default: true });
|
|
60
63
|
|
|
64
|
+
// Expose summary, name, and a display label on the component public instance so
|
|
65
|
+
// TOC discovery can access component
|
|
66
|
+
const displayTitle = computed(() => props.title);
|
|
67
|
+
|
|
68
|
+
const name = 'RcSection';
|
|
69
|
+
|
|
70
|
+
// Register this section in form summary/table-of-contents context (if provided)
|
|
71
|
+
const sectionRef = useTemplateRef<HTMLElement>('rc-section-summarized-container');
|
|
72
|
+
const { summary } = useInSummary({
|
|
73
|
+
label: displayTitle,
|
|
74
|
+
scrollTo: () => sectionRef.value?.scrollIntoView(true),
|
|
75
|
+
elementRef: sectionRef,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
defineExpose({
|
|
79
|
+
summary, displayTitle, name
|
|
80
|
+
});
|
|
81
|
+
|
|
61
82
|
const hasHeader = computed(() => {
|
|
62
83
|
return props.mode === 'with-header';
|
|
63
84
|
});
|
|
@@ -84,7 +105,10 @@ function toggle() {
|
|
|
84
105
|
</script>
|
|
85
106
|
|
|
86
107
|
<template>
|
|
87
|
-
<div
|
|
108
|
+
<div
|
|
109
|
+
ref="rc-section-summarized-container"
|
|
110
|
+
:class="sectionClass"
|
|
111
|
+
>
|
|
88
112
|
<div
|
|
89
113
|
v-if="hasHeader"
|
|
90
114
|
class="section-header"
|
|
@@ -203,7 +227,8 @@ function toggle() {
|
|
|
203
227
|
color: var(--body-text, inherit);
|
|
204
228
|
}
|
|
205
229
|
|
|
206
|
-
|
|
230
|
+
// TODO: Considering removing specificity override when RcButton sizes are refactored (#18062)
|
|
231
|
+
.left-wrapper :deep(button.toggle-button.btn-medium:not(.btn-sm)) {
|
|
207
232
|
flex-shrink: 0;
|
|
208
233
|
font-size: 16px;
|
|
209
234
|
color: var(--body-text, inherit);
|
|
@@ -240,7 +240,8 @@ clone_repo_test_extension_build "rancher" "kubewarden-ui" "kubewarden"
|
|
|
240
240
|
# clone_repo_test_extension_build "rancher" "elemental-ui" "elemental"
|
|
241
241
|
clone_repo_test_extension_build "neuvector" "manager-ext" "neuvector-ui-ext"
|
|
242
242
|
# clone_repo_test_extension_build "StackVista" "rancher-extension-stackstate" "observability"
|
|
243
|
-
|
|
243
|
+
# Uncomment once https://github.com/harvester/harvester/issues/10691 is resolved
|
|
244
|
+
#clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
|
|
244
245
|
clone_repo_test_extension_build "rancher" "ali-ui" "ali"
|
|
245
246
|
clone_repo_test_extension_build "rancher" "virtual-clusters-ui" "virtual-clusters"
|
|
246
247
|
# clone_repo_test_extension_build "rancher" "rancher-ai-ui" "rancher-ai-ui"
|