@rancher/shell 3.0.4 → 3.0.5-rc.2
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/images/providers/sks.svg +1 -0
- package/assets/styles/base/_basic.scss +6 -0
- package/assets/styles/base/_helpers.scss +4 -0
- package/assets/styles/base/_variables.scss +1 -0
- package/assets/styles/global/_button.scss +1 -0
- package/assets/translations/en-us.yaml +65 -15
- package/assets/translations/zh-hans.yaml +4 -3
- package/chart/monitoring/index.vue +3 -1
- package/cloud-credential/aws.vue +2 -0
- package/components/ActionDropdownShell.vue +71 -0
- package/components/AppModal.vue +18 -4
- package/components/AsyncButton.vue +24 -7
- package/components/BannerGraphic.vue +1 -0
- package/components/CommunityLinks.vue +4 -59
- package/components/CopyToClipboardText.vue +2 -1
- package/components/CruResource.vue +6 -1
- package/components/DetailText.vue +5 -0
- package/components/ExplorerMembers.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +68 -18
- package/components/GlobalRoleBindings.vue +5 -1
- package/components/GrowlManager.vue +1 -0
- package/components/LandingPagePreference.vue +7 -3
- package/components/LocaleSelector.vue +39 -95
- package/components/ModalManager.vue +55 -0
- package/components/ModalWithCard.vue +1 -0
- package/components/PromptModal.vue +47 -8
- package/components/PromptRemove.vue +1 -0
- package/components/PromptRestore.vue +1 -0
- package/components/ResourceCancelModal.vue +1 -0
- package/components/ResourceDetail/Masthead.vue +38 -12
- package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
- package/components/ResourceDetail/index.vue +47 -12
- package/components/ResourceTable.vue +54 -19
- package/components/SideNav.vue +5 -1
- package/components/SlideInPanelManager.vue +126 -0
- package/components/SortableTable/THead.vue +5 -2
- package/components/SortableTable/actions.js +1 -1
- package/components/SortableTable/index.vue +64 -51
- package/components/SortableTable/paging.js +16 -19
- package/components/SortableTable/selection.js +0 -11
- package/components/Wizard.vue +2 -2
- package/components/__tests__/AsyncButton.test.ts +2 -2
- package/components/__tests__/ModalManager.spec.ts +176 -0
- package/components/__tests__/PromptModal.test.ts +148 -0
- package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
- package/components/auth/AuthBanner.vue +13 -11
- package/components/auth/Principal.vue +1 -0
- package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
- package/components/auth/login/ldap.vue +1 -1
- package/components/fleet/FleetResources.vue +21 -6
- package/components/form/ArrayList.vue +76 -60
- package/components/form/BannerSettings.vue +17 -2
- package/components/form/ColorInput.vue +35 -6
- package/components/form/Command.vue +6 -15
- package/components/form/EnvVars.vue +16 -8
- package/components/form/HealthCheck.vue +3 -3
- package/components/form/HookOption.vue +11 -16
- package/components/form/LabeledSelect.vue +18 -22
- package/components/form/LifecycleHooks.vue +3 -3
- package/components/form/MatchExpressions.vue +14 -8
- package/components/form/NameNsDescription.vue +128 -104
- package/components/form/Networking.vue +20 -12
- package/components/form/NodeAffinity.vue +31 -23
- package/components/form/NodeScheduling.vue +13 -3
- package/components/form/NotificationSettings.vue +15 -1
- package/components/form/Password.vue +1 -0
- package/components/form/PodAffinity.vue +43 -43
- package/components/form/Probe.vue +68 -66
- package/components/form/ResourceQuota/Project.vue +5 -1
- package/components/form/ResourceSelector.vue +7 -9
- package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +16 -24
- package/components/form/SSHKnownHosts/index.vue +30 -13
- package/components/form/Security.vue +54 -56
- package/components/form/Select.vue +32 -21
- package/components/form/ShellInput.vue +5 -1
- package/components/form/Tolerations.vue +5 -1
- package/components/form/ValueFromResource.vue +134 -121
- package/components/form/WorkloadPorts.vue +18 -18
- package/components/form/__tests__/ArrayList.test.ts +5 -2
- package/components/form/__tests__/ColorInput.test.ts +35 -0
- package/components/form/__tests__/LabeledSelect.test.ts +40 -0
- package/components/form/__tests__/MatchExpressions.test.ts +12 -12
- package/components/form/__tests__/NameNsDescription.test.ts +115 -14
- package/components/form/__tests__/Probe.test.ts +12 -8
- package/components/form/__tests__/SSHKnownHosts.test.ts +22 -2
- package/components/form/__tests__/Select.test.ts +37 -0
- package/components/formatter/InternalExternalIP.vue +2 -0
- package/components/formatter/SecretData.vue +20 -7
- package/components/nav/Group.vue +27 -5
- package/components/nav/Header.vue +17 -43
- package/components/nav/NamespaceFilter.vue +134 -86
- package/components/nav/TopLevelMenu.vue +4 -5
- package/components/nav/Type.vue +12 -1
- package/components/nav/WindowManager/ContainerLogs.vue +87 -61
- package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
- package/components/templates/blank.vue +4 -1
- package/components/templates/default.vue +8 -3
- package/components/templates/home.vue +10 -1
- package/components/templates/plain.vue +10 -4
- package/composables/focusTrap.ts +12 -4
- package/composables/useRuntimeFlag.ts +29 -0
- package/config/router/routes.js +20 -13
- package/config/store.js +4 -0
- package/config/uiplugins.js +5 -1
- package/core/types.ts +12 -6
- package/detail/catalog.cattle.io.app.vue +6 -1
- package/detail/fleet.cattle.io.bundle.vue +70 -6
- package/detail/fleet.cattle.io.gitrepo.vue +1 -1
- package/detail/namespace.vue +0 -3
- package/detail/node.vue +17 -13
- package/detail/provisioning.cattle.io.cluster.vue +72 -6
- package/dialog/AddCustomBadgeDialog.vue +1 -1
- package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
- package/{components/AssignTo.vue → dialog/AssignToDialog.vue} +71 -80
- package/dialog/ChangePasswordDialog.vue +106 -0
- package/dialog/DeactivateDriverDialog.vue +1 -0
- package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
- package/dialog/DisableAuthProviderDialog.vue +101 -0
- package/dialog/DrainNode.vue +1 -1
- package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
- package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +69 -57
- package/dialog/FeatureFlagListDialog.vue +288 -0
- package/dialog/ForceMachineRemoveDialog.vue +5 -2
- package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
- package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
- package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -59
- package/dialog/MoveNamespaceDialog.vue +157 -0
- package/dialog/ScalePoolDownDialog.vue +1 -1
- package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
- package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
- package/dialog/WechatDialog.vue +57 -0
- package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
- package/edit/auth/__tests__/oidc.test.ts +152 -109
- package/edit/auth/azuread.vue +2 -1
- package/edit/auth/github.vue +1 -1
- package/edit/auth/googleoauth.vue +5 -1
- package/edit/auth/ldap/index.vue +1 -1
- package/edit/auth/oidc.vue +38 -5
- package/edit/auth/saml.vue +1 -1
- package/edit/cloudcredential.vue +24 -9
- package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
- package/edit/management.cattle.io.user.vue +28 -3
- package/edit/namespace.vue +1 -4
- package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
- package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
- package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -9
- package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
- package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +49 -41
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +33 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
- package/edit/token.vue +2 -0
- package/edit/workload/index.vue +1 -0
- package/edit/workload/mixins/workload.js +0 -2
- package/initialize/install-plugins.js +2 -1
- package/list/harvesterhci.io.management.cluster.vue +4 -1
- package/list/management.cattle.io.feature.vue +4 -287
- package/list/provisioning.cattle.io.cluster.vue +20 -12
- package/machine-config/azure.vue +16 -4
- package/mixins/vue-select-overrides.js +0 -4
- package/models/__tests__/namespace.test.ts +25 -1
- package/models/cloudcredential.js +5 -0
- package/models/fleet.cattle.io.cluster.js +8 -2
- package/models/fleet.cattle.io.gitrepo.js +8 -34
- package/models/kontainerdriver.js +6 -3
- package/models/management.cattle.io.feature.js +7 -1
- package/models/management.cattle.io.node.js +3 -3
- package/models/namespace.js +11 -6
- package/models/nodedriver.js +6 -3
- package/models/workload.js +4 -1
- package/package.json +3 -3
- package/pages/about.vue +13 -3
- package/pages/account/index.vue +16 -6
- package/pages/auth/login.vue +18 -7
- package/pages/auth/logout.vue +4 -1
- package/pages/auth/setup.vue +2 -0
- package/pages/auth/verify.vue +13 -8
- package/pages/c/_cluster/apps/charts/chart.vue +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +26 -26
- package/pages/c/_cluster/auth/config/index.vue +10 -12
- package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
- package/pages/c/_cluster/explorer/index.vue +17 -15
- package/pages/c/_cluster/istio/index.vue +2 -2
- package/pages/c/_cluster/longhorn/index.vue +1 -1
- package/pages/c/_cluster/monitoring/index.vue +1 -1
- package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
- package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
- package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
- package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
- package/pages/c/_cluster/neuvector/index.vue +1 -1
- package/pages/c/_cluster/settings/banners.vue +4 -3
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
- package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
- package/pages/c/_cluster/uiplugins/index.vue +98 -55
- package/pages/diagnostic.vue +59 -11
- package/pages/fail-whale.vue +14 -8
- package/pages/home.vue +24 -18
- package/pages/prefs.vue +7 -6
- package/pages/support/index.vue +4 -1
- package/plugins/internal-api/index.ts +37 -0
- package/plugins/internal-api/shared/base-api.ts +13 -0
- package/plugins/internal-api/shell/shell.api.ts +108 -0
- package/plugins/steve/actions.js +0 -12
- package/public/index.html +1 -0
- package/rancher-components/Card/Card.vue +1 -1
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +47 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
- package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
- package/rancher-components/Form/Radio/RadioButton.vue +20 -4
- package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
- package/rancher-components/Form/Radio/RadioGroup.vue +52 -10
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +5 -0
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
- package/rancher-components/RcButton/RcButton.vue +2 -1
- package/rancher-components/RcButton/types.ts +1 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +18 -6
- package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
- package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -0
- package/rancher-components/RcDropdown/index.ts +2 -0
- package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
- package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
- package/scripts/extension/bundle +20 -0
- package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
- package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
- package/scripts/extension/helmpatch +44 -31
- package/scripts/extension/publish +12 -12
- package/scripts/typegen.sh +2 -4
- package/server/har-file.js +25 -3
- package/store/action-menu.js +26 -56
- package/store/features.js +2 -1
- package/store/index.js +5 -0
- package/store/modal.ts +71 -0
- package/store/slideInPanel.ts +47 -0
- package/store/type-map.js +12 -1
- package/store/type-map.utils.ts +4 -4
- package/types/global-vue.d.ts +5 -0
- package/types/internal-api/shell/growl.d.ts +25 -0
- package/types/internal-api/shell/modal.d.ts +77 -0
- package/types/internal-api/shell/slideIn.d.ts +15 -0
- package/types/resources/fleet.d.ts +0 -14
- package/types/shell/index.d.ts +43 -24
- package/types/vue-shim.d.ts +4 -1
- package/utils/__mocks__/tabbable.js +13 -0
- package/utils/__tests__/object.test.ts +38 -4
- package/utils/cluster.js +35 -0
- package/utils/fleet.ts +15 -73
- package/utils/object.js +48 -5
- package/utils/validators/formRules/__tests__/index.test.ts +10 -1
- package/utils/validators/formRules/index.ts +27 -3
- package/utils/validators/machine-pool.ts +20 -0
- package/components/DisableAuthProviderModal.vue +0 -114
- package/components/MoveModal.vue +0 -166
- package/components/PromptChangePassword.vue +0 -123
- package/components/fleet/FleetBundleResources.vue +0 -86
- package/components/formatter/ExtensionCache.vue +0 -74
- package/components/formatter/Port.vue +0 -24
- package/components/formatter/SecretType.vue +0 -41
- package/types/vue-shim.d +0 -20
package/pages/home.vue
CHANGED
|
@@ -49,6 +49,21 @@ export default defineComponent({
|
|
|
49
49
|
mixins: [PageHeaderActions],
|
|
50
50
|
|
|
51
51
|
data() {
|
|
52
|
+
const options = this.$store.getters[`type-map/optionsFor`](CAPI.RANCHER_CLUSTER)?.custom || {};
|
|
53
|
+
const params = {
|
|
54
|
+
product: MANAGER,
|
|
55
|
+
cluster: BLANK_CLUSTER,
|
|
56
|
+
resource: CAPI.RANCHER_CLUSTER
|
|
57
|
+
};
|
|
58
|
+
const defaultCreateLocation = {
|
|
59
|
+
name: 'c-cluster-product-resource-create',
|
|
60
|
+
params,
|
|
61
|
+
};
|
|
62
|
+
const defaultImportLocation = {
|
|
63
|
+
...defaultCreateLocation,
|
|
64
|
+
query: { [MODE]: _IMPORT }
|
|
65
|
+
};
|
|
66
|
+
|
|
52
67
|
return {
|
|
53
68
|
HIDE_HOME_PAGE_CARDS,
|
|
54
69
|
fullVersion: getVersionInfo(this.$store).fullVersion,
|
|
@@ -87,24 +102,9 @@ export default defineComponent({
|
|
|
87
102
|
},
|
|
88
103
|
},
|
|
89
104
|
|
|
90
|
-
createLocation:
|
|
91
|
-
name: 'c-cluster-product-resource-create',
|
|
92
|
-
params: {
|
|
93
|
-
product: MANAGER,
|
|
94
|
-
cluster: BLANK_CLUSTER,
|
|
95
|
-
resource: CAPI.RANCHER_CLUSTER
|
|
96
|
-
},
|
|
97
|
-
},
|
|
105
|
+
createLocation: options.createLocation ? options.createLocation(params) : defaultCreateLocation,
|
|
98
106
|
|
|
99
|
-
importLocation:
|
|
100
|
-
name: 'c-cluster-product-resource-create',
|
|
101
|
-
params: {
|
|
102
|
-
product: MANAGER,
|
|
103
|
-
cluster: BLANK_CLUSTER,
|
|
104
|
-
resource: CAPI.RANCHER_CLUSTER
|
|
105
|
-
},
|
|
106
|
-
query: { [MODE]: _IMPORT }
|
|
107
|
-
},
|
|
107
|
+
importLocation: options.importLocation ? options.importLocation(params) : defaultImportLocation,
|
|
108
108
|
|
|
109
109
|
headers: [
|
|
110
110
|
STATE,
|
|
@@ -512,7 +512,7 @@ export default defineComponent({
|
|
|
512
512
|
:show-child="false"
|
|
513
513
|
:breadcrumb="false"
|
|
514
514
|
>
|
|
515
|
-
{{ vendor }}
|
|
515
|
+
{{ `${vendor} - ${t('landing.homepage')}` }}
|
|
516
516
|
</TabTitle>
|
|
517
517
|
<BannerGraphic
|
|
518
518
|
:small="true"
|
|
@@ -805,6 +805,12 @@ export default defineComponent({
|
|
|
805
805
|
.cluster-name {
|
|
806
806
|
display: flex;
|
|
807
807
|
align-items: center;
|
|
808
|
+
|
|
809
|
+
// Ensure long cluster names truncate with ellipsis
|
|
810
|
+
> A {
|
|
811
|
+
overflow: hidden;
|
|
812
|
+
text-overflow: ellipsis;
|
|
813
|
+
}
|
|
808
814
|
}
|
|
809
815
|
|
|
810
816
|
.cluster-description {
|
package/pages/prefs.vue
CHANGED
|
@@ -225,7 +225,7 @@ export default {
|
|
|
225
225
|
v-if="!isSingleProduct"
|
|
226
226
|
class="mt-10 mb-10"
|
|
227
227
|
>
|
|
228
|
-
<hr>
|
|
228
|
+
<hr role="none">
|
|
229
229
|
<h4 v-t="'prefs.landing.label'" />
|
|
230
230
|
<LandingPagePreference
|
|
231
231
|
data-testid="prefs__landingPagePreference"
|
|
@@ -233,7 +233,7 @@ export default {
|
|
|
233
233
|
</div>
|
|
234
234
|
<!-- Display Settings -->
|
|
235
235
|
<div class="mt-10 mb-10">
|
|
236
|
-
<hr>
|
|
236
|
+
<hr role="none">
|
|
237
237
|
<h4 v-t="'prefs.displaySettings.title'" />
|
|
238
238
|
<p class="set-landing-leadin">
|
|
239
239
|
{{ t('prefs.displaySettings.detail', {}, raw=true) }}
|
|
@@ -277,7 +277,7 @@ export default {
|
|
|
277
277
|
v-if="!isSingleProduct"
|
|
278
278
|
class="col adv-features mt-10 mb-10"
|
|
279
279
|
>
|
|
280
|
-
<hr>
|
|
280
|
+
<hr role="none">
|
|
281
281
|
<h4 v-t="'prefs.confirmationSetting.title'" />
|
|
282
282
|
<Checkbox
|
|
283
283
|
v-model:value="scalingDownPrompt"
|
|
@@ -288,7 +288,7 @@ export default {
|
|
|
288
288
|
</div>
|
|
289
289
|
<!-- Advanced Features -->
|
|
290
290
|
<div class="col adv-features mt-10 mb-10">
|
|
291
|
-
<hr>
|
|
291
|
+
<hr role="none">
|
|
292
292
|
<h4 v-t="'prefs.advFeatures.title'" />
|
|
293
293
|
<Checkbox
|
|
294
294
|
v-model:value="viewInApi"
|
|
@@ -325,13 +325,14 @@ export default {
|
|
|
325
325
|
<Checkbox
|
|
326
326
|
v-model:value="pluginDeveloper"
|
|
327
327
|
:label="t('prefs.advFeatures.pluginDeveloper', {}, true)"
|
|
328
|
+
:tooltip="t('prefs.advFeatures.pluginDeveloperTooltip')"
|
|
328
329
|
class="mt-20"
|
|
329
330
|
/>
|
|
330
331
|
</template>
|
|
331
332
|
</div>
|
|
332
333
|
<!-- YAML editor key mapping -->
|
|
333
334
|
<div class="col mt-10 mb-10">
|
|
334
|
-
<hr>
|
|
335
|
+
<hr role="none">
|
|
335
336
|
<h4 v-t="'prefs.keymap.label'" />
|
|
336
337
|
<ButtonGroup
|
|
337
338
|
v-model:value="keymap"
|
|
@@ -344,7 +345,7 @@ export default {
|
|
|
344
345
|
v-if="!isSingleProduct"
|
|
345
346
|
class="col mt-10 mb-40"
|
|
346
347
|
>
|
|
347
|
-
<hr>
|
|
348
|
+
<hr role="none">
|
|
348
349
|
<h4 v-t="'prefs.helm.label'" />
|
|
349
350
|
<ButtonGroup
|
|
350
351
|
v-model:value="showPreRelease"
|
package/pages/support/index.vue
CHANGED
|
@@ -118,7 +118,10 @@ export default {
|
|
|
118
118
|
</script>
|
|
119
119
|
<template>
|
|
120
120
|
<div>
|
|
121
|
-
<BannerGraphic
|
|
121
|
+
<BannerGraphic
|
|
122
|
+
:title="t(title, {}, true)"
|
|
123
|
+
:alt="t('support.bannerImage')"
|
|
124
|
+
/>
|
|
122
125
|
|
|
123
126
|
<IndentedPanel>
|
|
124
127
|
<div class="content mt-20">
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Store } from 'vuex';
|
|
2
|
+
|
|
3
|
+
interface PluginContext {
|
|
4
|
+
store: Store<any>;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function(context: PluginContext, inject: (key: string, value: any) => void) {
|
|
9
|
+
const { store } = context;
|
|
10
|
+
|
|
11
|
+
// Load all API modules
|
|
12
|
+
const apiContext = (require as any).context(
|
|
13
|
+
'@shell/plugins/internal-api', // the base directory
|
|
14
|
+
true, // whether to search subdirectories
|
|
15
|
+
/\.api\.ts$/ // only .api.ts files
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
apiContext.keys().forEach((relativePath: string) => {
|
|
19
|
+
const mod = apiContext(relativePath);
|
|
20
|
+
const ApiClass = mod.default;
|
|
21
|
+
|
|
22
|
+
if (typeof ApiClass === 'function') {
|
|
23
|
+
// Check for a static `apiName` property, or fallback to filename
|
|
24
|
+
let apiName: string = ApiClass.apiName();
|
|
25
|
+
|
|
26
|
+
if (!apiName) {
|
|
27
|
+
// fallback to filename (strip leading ‘./’ and extension)
|
|
28
|
+
apiName = `$${ relativePath.replace(/^\.\//, '').replace(/\.\w+$/, '') }`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const instance = new ApiClass(store);
|
|
32
|
+
|
|
33
|
+
// The inject() method automatically adds the `$` prefix
|
|
34
|
+
inject(apiName, instance);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export abstract class BaseApi {
|
|
2
|
+
// The Vuex store, available to all API classes
|
|
3
|
+
protected $store: any;
|
|
4
|
+
|
|
5
|
+
// Documented requirement: each API should define its static apiName.
|
|
6
|
+
static apiName(): string {
|
|
7
|
+
throw new Error('apiName() static method has not been implemented');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
constructor(store: any) {
|
|
11
|
+
this.$store = store;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { GrowlConfig } from '@shell/types/internal-api/shell/growl';
|
|
2
|
+
import { ModalConfig } from '@shell/types/internal-api/shell/modal';
|
|
3
|
+
import { SlideInConfig } from '@shell/types/internal-api/shell/slideIn';
|
|
4
|
+
|
|
5
|
+
import { BaseApi } from '@shell/plugins/internal-api/shared/base-api';
|
|
6
|
+
|
|
7
|
+
export default class ShellApi extends BaseApi {
|
|
8
|
+
static apiName() {
|
|
9
|
+
return 'shell';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Dispatches a growl notification.
|
|
14
|
+
*
|
|
15
|
+
* @param config - Configuration for the growl notification.
|
|
16
|
+
* - If `message` is a string, it is treated as the main content of the notification.
|
|
17
|
+
* - If `message` is a `DetailedMessage` object, `title` and `description` are extracted.
|
|
18
|
+
*
|
|
19
|
+
* Example:
|
|
20
|
+
* ```ts
|
|
21
|
+
* this.$shell.growl({ message: 'Operation successful!', type: 'success' });
|
|
22
|
+
* this.$shell.growl({ message: { title: 'Warning', description: 'Check your input.' }, type: 'warning' });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
protected growl(config: GrowlConfig): void {
|
|
26
|
+
const { type = 'error', timeout = 5000 } = config;
|
|
27
|
+
|
|
28
|
+
let title = '';
|
|
29
|
+
let description = '';
|
|
30
|
+
|
|
31
|
+
if (typeof config.message === 'string') {
|
|
32
|
+
description = config.message;
|
|
33
|
+
} else {
|
|
34
|
+
title = config.message.title || '';
|
|
35
|
+
description = config.message.description;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.$store.dispatch(
|
|
39
|
+
`growl/${ type }`,
|
|
40
|
+
{
|
|
41
|
+
title,
|
|
42
|
+
message: description,
|
|
43
|
+
timeout,
|
|
44
|
+
},
|
|
45
|
+
{ root: true }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Opens a modal by committing to the Vuex store.
|
|
51
|
+
*
|
|
52
|
+
* This method updates the store's `modal` module to show a modal with the
|
|
53
|
+
* specified configuration. The modal is rendered using the `ModalManager` component,
|
|
54
|
+
* and its content is dynamically loaded based on the `component` field in the configuration.
|
|
55
|
+
*
|
|
56
|
+
* @param config A `ModalConfig` object defining the modal’s content and behavior.
|
|
57
|
+
*
|
|
58
|
+
* Example:
|
|
59
|
+
* ```ts
|
|
60
|
+
* this.$shell.modal({
|
|
61
|
+
* component: MyCustomModal,
|
|
62
|
+
* componentProps: { title: 'Hello Modal' },
|
|
63
|
+
* resources: [someResource],
|
|
64
|
+
* modalWidth: '800px',
|
|
65
|
+
* closeOnClickOutside: false
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
protected modal(config: ModalConfig): void {
|
|
70
|
+
this.$store.commit('modal/openModal', {
|
|
71
|
+
component: config.component,
|
|
72
|
+
componentProps: config.componentProps || {},
|
|
73
|
+
resources: config.resources || [],
|
|
74
|
+
modalWidth: config.modalWidth || '600px',
|
|
75
|
+
closeOnClickOutside: config.closeOnClickOutside ?? true,
|
|
76
|
+
// modalSticky: config.modalSticky ?? false // Not implemented yet
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Opens the slide-in panel with the specified component and props.
|
|
82
|
+
*
|
|
83
|
+
* This method commits the `open` mutation to the `slideInPanel` Vuex module,
|
|
84
|
+
* which sets the current component to be rendered and its associated props.
|
|
85
|
+
* The slide-in panel becomes visible after the mutation.
|
|
86
|
+
*
|
|
87
|
+
* @param config - The configuration object for the slide-in panel.
|
|
88
|
+
*
|
|
89
|
+
* Example Usage:
|
|
90
|
+
* ```ts
|
|
91
|
+
* import MyComponent from '@/components/MyComponent.vue';
|
|
92
|
+
*
|
|
93
|
+
* this.$shell.slideInPanel({
|
|
94
|
+
* component: MyComponent,
|
|
95
|
+
* componentProps: { foo: 'bar' }
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @param config.component - A Vue component (imported SFC, functional component, etc.) to be rendered in the panel.
|
|
100
|
+
* @param config.componentProps - (Optional) Props to pass to the component. These should align with the component's defined props.
|
|
101
|
+
*/
|
|
102
|
+
protected slideInPanel(config: SlideInConfig): void {
|
|
103
|
+
this.$store.commit('slideInPanel/open', {
|
|
104
|
+
component: config.component,
|
|
105
|
+
componentProps: config.componentProps || {}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
package/plugins/steve/actions.js
CHANGED
|
@@ -196,18 +196,10 @@ export default {
|
|
|
196
196
|
}
|
|
197
197
|
},
|
|
198
198
|
|
|
199
|
-
promptMove({ commit, state }, resources) {
|
|
200
|
-
commit('action-menu/togglePromptMove', resources, { root: true });
|
|
201
|
-
},
|
|
202
|
-
|
|
203
199
|
promptRestore({ commit, state }, resources ) {
|
|
204
200
|
commit('action-menu/togglePromptRestore', resources, { root: true });
|
|
205
201
|
},
|
|
206
202
|
|
|
207
|
-
assignTo({ commit, state }, resources = []) {
|
|
208
|
-
commit('action-menu/toggleAssignTo', resources, { root: true });
|
|
209
|
-
},
|
|
210
|
-
|
|
211
203
|
async resourceAction({ getters, dispatch }, {
|
|
212
204
|
resource, actionName, body, opt,
|
|
213
205
|
}) {
|
|
@@ -234,10 +226,6 @@ export default {
|
|
|
234
226
|
}
|
|
235
227
|
},
|
|
236
228
|
|
|
237
|
-
promptUpdate({ commit, state }, resources = []) {
|
|
238
|
-
commit('action-menu/togglePromptUpdate', resources, { root: true });
|
|
239
|
-
},
|
|
240
|
-
|
|
241
229
|
async collectionAction({ getters, dispatch }, {
|
|
242
230
|
type, actionName, body, opt
|
|
243
231
|
}) {
|
package/public/index.html
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shallowMount, Wrapper } from '@vue/test-utils';
|
|
1
|
+
import { shallowMount, Wrapper, mount } from '@vue/test-utils';
|
|
2
2
|
import { Checkbox } from './index';
|
|
3
3
|
|
|
4
4
|
describe('checkbox.vue', () => {
|
|
@@ -65,4 +65,62 @@ describe('checkbox.vue', () => {
|
|
|
65
65
|
|
|
66
66
|
expect(wrapper.emitted('update:value')[0][0]).toBeNull();
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', async() => {
|
|
70
|
+
const alternateLabel = 'some-alternate-aria-label';
|
|
71
|
+
const description = 'some-description';
|
|
72
|
+
const ariaDescribedById = 'some-external-id';
|
|
73
|
+
|
|
74
|
+
const wrapper: Wrapper<InstanceType<typeof Checkbox>> = mount(
|
|
75
|
+
Checkbox,
|
|
76
|
+
{
|
|
77
|
+
propsData: {
|
|
78
|
+
value: false, alternateLabel, description
|
|
79
|
+
},
|
|
80
|
+
attrs: { 'aria-describedby': ariaDescribedById },
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const field = wrapper.find('.checkbox-custom');
|
|
85
|
+
const ariaChecked = field.attributes('aria-checked');
|
|
86
|
+
const ariaLabel = field.attributes('aria-label');
|
|
87
|
+
const ariaLabelledBy = field.attributes('aria-labelledby');
|
|
88
|
+
const ariaDescribedBy = field.attributes('aria-describedby');
|
|
89
|
+
|
|
90
|
+
// validates type of input rendered
|
|
91
|
+
expect(ariaChecked).toBe('false');
|
|
92
|
+
expect(ariaLabelledBy).toBeUndefined();
|
|
93
|
+
expect(ariaLabel).toBe(alternateLabel);
|
|
94
|
+
expect(ariaDescribedBy).toBe(`${ ariaDescribedById } ${ wrapper.vm.describedById }`);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('a11y: having a label should not render "aria-label" prop and have "aria-labelledby"', async() => {
|
|
98
|
+
const label = 'some-label';
|
|
99
|
+
|
|
100
|
+
const wrapper: Wrapper<InstanceType<typeof Checkbox>> = mount(
|
|
101
|
+
Checkbox,
|
|
102
|
+
{
|
|
103
|
+
propsData: {
|
|
104
|
+
value: true, label, disabled: true
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const field = wrapper.find('.checkbox-custom');
|
|
110
|
+
const ariaChecked = field.attributes('aria-checked');
|
|
111
|
+
const ariaLabel = field.attributes('aria-label');
|
|
112
|
+
const ariaLabelledBy = field.attributes('aria-labelledby');
|
|
113
|
+
const ariaDisabled = field.attributes('aria-disabled');
|
|
114
|
+
const tabIndex = field.attributes('tabindex');
|
|
115
|
+
|
|
116
|
+
// validates type of input rendered
|
|
117
|
+
expect(field.exists()).toBe(true);
|
|
118
|
+
expect(ariaChecked).toBe('true');
|
|
119
|
+
expect(ariaLabelledBy).toBe(wrapper.vm.idForLabel);
|
|
120
|
+
expect(ariaLabel).toBeUndefined();
|
|
121
|
+
expect(wrapper.find(`#${ wrapper.vm.idForLabel }`).text()).toBe(label);
|
|
122
|
+
|
|
123
|
+
expect(ariaDisabled).toBe('true');
|
|
124
|
+
expect(tabIndex).toBe('-1');
|
|
125
|
+
});
|
|
68
126
|
});
|
|
@@ -124,6 +124,15 @@ export default defineComponent({
|
|
|
124
124
|
type: String,
|
|
125
125
|
default: undefined
|
|
126
126
|
},
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Inherited global identifier prefix for tests
|
|
130
|
+
* Define a term based on the parent component to avoid conflicts on multiple components
|
|
131
|
+
*/
|
|
132
|
+
componentTestid: {
|
|
133
|
+
type: String,
|
|
134
|
+
default: 'checkbox'
|
|
135
|
+
},
|
|
127
136
|
},
|
|
128
137
|
|
|
129
138
|
emits: ['update:value'],
|
|
@@ -133,6 +142,18 @@ export default defineComponent({
|
|
|
133
142
|
},
|
|
134
143
|
|
|
135
144
|
computed: {
|
|
145
|
+
ariaDescribedBy(): string | undefined {
|
|
146
|
+
const inheritedDescribedBy = this.$attrs['aria-describedby'];
|
|
147
|
+
const internalDescribedBy = this.descriptionKey || this.description ? this.describedById : undefined;
|
|
148
|
+
|
|
149
|
+
if (inheritedDescribedBy && internalDescribedBy) {
|
|
150
|
+
return `${ inheritedDescribedBy } ${ internalDescribedBy }`;
|
|
151
|
+
} else if (inheritedDescribedBy || internalDescribedBy) {
|
|
152
|
+
return `${ inheritedDescribedBy || internalDescribedBy }`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return undefined;
|
|
156
|
+
},
|
|
136
157
|
/**
|
|
137
158
|
* Determines if the checkbox is disabled.
|
|
138
159
|
* @returns boolean: True when the disabled prop is true or when mode is
|
|
@@ -167,7 +188,7 @@ export default defineComponent({
|
|
|
167
188
|
},
|
|
168
189
|
|
|
169
190
|
idForLabel():string {
|
|
170
|
-
return `${
|
|
191
|
+
return `${ generateRandomAlphaString(12) }-checkbox-label`;
|
|
171
192
|
}
|
|
172
193
|
},
|
|
173
194
|
|
|
@@ -271,10 +292,11 @@ export default defineComponent({
|
|
|
271
292
|
class="checkbox-custom"
|
|
272
293
|
:class="{indeterminate: indeterminate}"
|
|
273
294
|
:tabindex="isDisabled ? -1 : 0"
|
|
295
|
+
:aria-disabled="isDisabled"
|
|
274
296
|
:aria-label="replacementLabel"
|
|
275
297
|
:aria-checked="!!value"
|
|
276
298
|
:aria-labelledby="labelKey || label ? idForLabel : undefined"
|
|
277
|
-
:aria-describedby="
|
|
299
|
+
:aria-describedby="ariaDescribedBy"
|
|
278
300
|
role="checkbox"
|
|
279
301
|
/>
|
|
280
302
|
<span
|
|
@@ -298,6 +320,7 @@ export default defineComponent({
|
|
|
298
320
|
v-clean-tooltip="{content: t(tooltipKey), triggers: ['hover', 'touch', 'focus']}"
|
|
299
321
|
v-stripped-aria-label="t(tooltipKey)"
|
|
300
322
|
class="checkbox-info icon icon-info icon-lg"
|
|
323
|
+
:data-testid="componentTestid + '-info-icon'"
|
|
301
324
|
:tabindex="isDisabled ? -1 : 0"
|
|
302
325
|
/>
|
|
303
326
|
<i
|
|
@@ -305,6 +328,7 @@ export default defineComponent({
|
|
|
305
328
|
v-clean-tooltip="{content: tooltip, triggers: ['hover', 'touch', 'focus']}"
|
|
306
329
|
v-stripped-aria-label="tooltip"
|
|
307
330
|
class="checkbox-info icon icon-info icon-lg"
|
|
331
|
+
:data-testid="componentTestid + '-info-icon'"
|
|
308
332
|
:tabindex="isDisabled ? -1 : 0"
|
|
309
333
|
/>
|
|
310
334
|
</slot>
|
|
@@ -374,7 +398,7 @@ $fontColor: var(--input-label);
|
|
|
374
398
|
|
|
375
399
|
.checkbox-info {
|
|
376
400
|
line-height: normal;
|
|
377
|
-
margin-left:
|
|
401
|
+
margin-left: 4px;
|
|
378
402
|
|
|
379
403
|
&:focus-visible {
|
|
380
404
|
@include focus-outline;
|
|
@@ -54,4 +54,51 @@ describe('component: LabeledInput', () => {
|
|
|
54
54
|
expect(subLabel.text()).toBe(hint);
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
|
+
|
|
58
|
+
describe('a11y: adding ARIA props', () => {
|
|
59
|
+
const ariaLabelVal = 'some-aria-label';
|
|
60
|
+
const subLabelVal = 'some-sublabel';
|
|
61
|
+
const ariaDescribedByIdVal = 'some-external-id';
|
|
62
|
+
const ariaRequiredVal = 'true';
|
|
63
|
+
|
|
64
|
+
it.each([
|
|
65
|
+
['text', 'input', ariaLabelVal, subLabelVal, ariaDescribedByIdVal],
|
|
66
|
+
['cron', 'input', ariaLabelVal, subLabelVal, ariaDescribedByIdVal],
|
|
67
|
+
['multiline', 'textarea', ariaLabelVal, subLabelVal, ariaDescribedByIdVal],
|
|
68
|
+
['multiline-password', 'textarea', ariaLabelVal, subLabelVal, ariaDescribedByIdVal],
|
|
69
|
+
])('for type %p should correctly fill out the appropriate fields on the component', (type, validationType, ariaLabel, subLabel, ariaDescribedById) => {
|
|
70
|
+
const wrapper = mount(LabeledInput, {
|
|
71
|
+
propsData: {
|
|
72
|
+
value: '', type, ariaLabel, subLabel, required: true
|
|
73
|
+
},
|
|
74
|
+
attrs: { 'aria-describedby': ariaDescribedById },
|
|
75
|
+
mocks: { $store: { getters: { 'i18n/t': jest.fn() } } }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const field = wrapper.find(validationType);
|
|
79
|
+
const ariaLabelProp = field.attributes('aria-label');
|
|
80
|
+
const ariaDescribedBy = field.attributes('aria-describedby');
|
|
81
|
+
const ariaRequired = field.attributes('aria-required');
|
|
82
|
+
|
|
83
|
+
// validates type of input rendered
|
|
84
|
+
expect(field.exists()).toBe(true);
|
|
85
|
+
expect(ariaLabelProp).toBe(ariaLabel);
|
|
86
|
+
expect(ariaDescribedBy).toBe(`${ ariaDescribedById } ${ wrapper.vm.describedById }`);
|
|
87
|
+
expect(ariaRequired).toBe(ariaRequiredVal);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('a11y: rendering a "label" should not render an "aria-label" prop', () => {
|
|
92
|
+
const label = 'some-label';
|
|
93
|
+
|
|
94
|
+
const wrapper = mount(LabeledInput, {
|
|
95
|
+
propsData: { type: 'text', label },
|
|
96
|
+
mocks: { $store: { getters: { 'i18n/t': jest.fn() } } }
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const mainInput = wrapper.find('input[type="text"]');
|
|
100
|
+
|
|
101
|
+
expect(mainInput.attributes('aria-label')).toBeUndefined();
|
|
102
|
+
expect(wrapper.find('label').text()).toBe(label);
|
|
103
|
+
});
|
|
57
104
|
});
|
|
@@ -161,6 +161,19 @@ export default defineComponent({
|
|
|
161
161
|
return this.isCompact ? false : !!this.label || !!this.labelKey || !!this.$slots.label;
|
|
162
162
|
},
|
|
163
163
|
|
|
164
|
+
ariaDescribedBy(): string | undefined {
|
|
165
|
+
const inheritedDescribedBy = this.$attrs['aria-describedby'];
|
|
166
|
+
const internalDescribedBy = this.cronHint || this.subLabel ? this.describedById : undefined;
|
|
167
|
+
|
|
168
|
+
if (inheritedDescribedBy && internalDescribedBy) {
|
|
169
|
+
return `${ inheritedDescribedBy } ${ internalDescribedBy }`;
|
|
170
|
+
} else if (inheritedDescribedBy || internalDescribedBy) {
|
|
171
|
+
return `${ inheritedDescribedBy || internalDescribedBy }`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return undefined;
|
|
175
|
+
},
|
|
176
|
+
|
|
164
177
|
/**
|
|
165
178
|
* Determines if the Labeled Input should display a tooltip.
|
|
166
179
|
*/
|
|
@@ -362,6 +375,7 @@ export default defineComponent({
|
|
|
362
375
|
<span
|
|
363
376
|
v-if="requiredField"
|
|
364
377
|
class="required"
|
|
378
|
+
:aria-hidden="true"
|
|
365
379
|
>*</span>
|
|
366
380
|
</label>
|
|
367
381
|
</slot>
|
|
@@ -377,11 +391,13 @@ export default defineComponent({
|
|
|
377
391
|
v-stripped-aria-label="!hasLabel && ariaLabel ? ariaLabel : undefined"
|
|
378
392
|
:maxlength="_maxlength"
|
|
379
393
|
:disabled="isDisabled"
|
|
394
|
+
:aria-disabled="isDisabled"
|
|
380
395
|
:value="value || ''"
|
|
381
396
|
:placeholder="_placeholder"
|
|
382
397
|
autocapitalize="off"
|
|
383
398
|
:class="{ conceal: type === 'multiline-password' }"
|
|
384
|
-
:aria-describedby="
|
|
399
|
+
:aria-describedby="ariaDescribedBy"
|
|
400
|
+
:aria-required="requiredField"
|
|
385
401
|
@update:value="onInput"
|
|
386
402
|
@focus="onFocus"
|
|
387
403
|
@blur="onBlur"
|
|
@@ -396,13 +412,15 @@ export default defineComponent({
|
|
|
396
412
|
v-bind="$attrs"
|
|
397
413
|
:maxlength="_maxlength"
|
|
398
414
|
:disabled="isDisabled"
|
|
415
|
+
:aria-disabled="isDisabled"
|
|
399
416
|
:type="type === 'cron' ? 'text' : type"
|
|
400
417
|
:value="value"
|
|
401
418
|
:placeholder="_placeholder"
|
|
402
419
|
autocomplete="off"
|
|
403
420
|
autocapitalize="off"
|
|
404
421
|
:data-lpignore="ignorePasswordManagers"
|
|
405
|
-
:aria-describedby="
|
|
422
|
+
:aria-describedby="ariaDescribedBy"
|
|
423
|
+
:aria-required="requiredField"
|
|
406
424
|
@input="onInput"
|
|
407
425
|
@focus="onFocus"
|
|
408
426
|
@blur="onBlur"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shallowMount } from '@vue/test-utils';
|
|
1
|
+
import { shallowMount, mount } from '@vue/test-utils';
|
|
2
2
|
import { RadioButton } from './index';
|
|
3
3
|
|
|
4
4
|
describe('radioButton.vue', () => {
|
|
@@ -30,4 +30,39 @@ describe('radioButton.vue', () => {
|
|
|
30
30
|
|
|
31
31
|
expect(wrapper.find('.radio-label').text()).toBe('Test Label - Slot');
|
|
32
32
|
});
|
|
33
|
+
|
|
34
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', async() => {
|
|
35
|
+
const val = 'foo';
|
|
36
|
+
const value = 'foo';
|
|
37
|
+
const description = 'some-description';
|
|
38
|
+
const itemLabel = 'some-label';
|
|
39
|
+
const radioOptionId = 'some-id-from-parent';
|
|
40
|
+
|
|
41
|
+
const wrapper = mount(
|
|
42
|
+
RadioButton,
|
|
43
|
+
{
|
|
44
|
+
propsData: {
|
|
45
|
+
label: itemLabel,
|
|
46
|
+
val,
|
|
47
|
+
value,
|
|
48
|
+
description,
|
|
49
|
+
radioOptionId
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const radioInputElem = wrapper.find('span[role="radio"]');
|
|
54
|
+
const role = radioInputElem.attributes('role');
|
|
55
|
+
const ariaLabel = radioInputElem.attributes('aria-label');
|
|
56
|
+
const ariaChecked = radioInputElem.attributes('aria-checked');
|
|
57
|
+
const ariaDisabled = radioInputElem.attributes('aria-disabled');
|
|
58
|
+
const ariaDescribedBy = radioInputElem.attributes('aria-describedby');
|
|
59
|
+
const itemId = radioInputElem.attributes('id');
|
|
60
|
+
|
|
61
|
+
expect(role).toBe('radio');
|
|
62
|
+
expect(ariaLabel).toBe(itemLabel);
|
|
63
|
+
expect(ariaChecked).toBe('true');
|
|
64
|
+
expect(ariaDisabled).toBe('false');
|
|
65
|
+
expect(ariaDescribedBy).toBe(wrapper.vm.describeById);
|
|
66
|
+
expect(itemId).toBe(radioOptionId);
|
|
67
|
+
});
|
|
33
68
|
});
|