@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
|
@@ -44,25 +44,9 @@ export default {
|
|
|
44
44
|
},
|
|
45
45
|
|
|
46
46
|
data() {
|
|
47
|
-
const rows = clone(this.value || []).map((row) => {
|
|
48
|
-
row._showHost = false;
|
|
49
|
-
row._serviceType = row._serviceType || '';
|
|
50
|
-
row._name = row.name ? `${ row.name }` : `${ row.containerPort }${ row.protocol.toLowerCase() }${ row.hostPort || row._listeningPort || '' }`;
|
|
51
|
-
if (row.hostPort || row.hostIP) {
|
|
52
|
-
row._showHost = true;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
row._ipam = '';
|
|
56
|
-
|
|
57
|
-
return row;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// show host port column if existing port data has any host ports defined
|
|
61
|
-
const showHostPorts = !!rows.some((row) => !!row.hostPort);
|
|
62
|
-
|
|
63
47
|
return {
|
|
64
|
-
rows,
|
|
65
|
-
showHostPorts,
|
|
48
|
+
rows: [],
|
|
49
|
+
showHostPorts: false,
|
|
66
50
|
workloadPortOptions: ['TCP', 'UDP']
|
|
67
51
|
};
|
|
68
52
|
},
|
|
@@ -168,6 +152,22 @@ export default {
|
|
|
168
152
|
},
|
|
169
153
|
|
|
170
154
|
created() {
|
|
155
|
+
const rows = clone(this.value || []).map((row) => {
|
|
156
|
+
row._showHost = false;
|
|
157
|
+
row._serviceType = row._serviceType || '';
|
|
158
|
+
row._name = row.name ? `${ row.name }` : `${ row.containerPort }${ row.protocol.toLowerCase() }${ row.hostPort || row._listeningPort || '' }`;
|
|
159
|
+
if (row.hostPort || row.hostIP) {
|
|
160
|
+
row._showHost = true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
row._ipam = '';
|
|
164
|
+
|
|
165
|
+
return row;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
this.rows = rows;
|
|
169
|
+
// show host port column if existing port data has any host ports defined
|
|
170
|
+
this.showHostPorts = !!rows.some((row) => !!row.hostPort);
|
|
171
171
|
this.queueUpdate = debounce(this.update, 500);
|
|
172
172
|
this.rows.map((row) => {
|
|
173
173
|
this.setServiceType(row);
|
|
@@ -4,6 +4,8 @@ import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
|
4
4
|
import { ExtendedVue, Vue } from 'vue/types/vue';
|
|
5
5
|
import { DefaultProps } from 'vue/types/options';
|
|
6
6
|
|
|
7
|
+
jest.mock('lodash/debounce', () => jest.fn((fn) => fn));
|
|
8
|
+
|
|
7
9
|
describe('the ArrayList', () => {
|
|
8
10
|
it('is empty', () => {
|
|
9
11
|
const wrapper = mount(ArrayList, {
|
|
@@ -56,12 +58,13 @@ describe('the ArrayList', () => {
|
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
jest.useFakeTimers();
|
|
59
|
-
await (wrapper.get('[data-testid="remove-item-1"]').element as HTMLElement).click();
|
|
61
|
+
await (wrapper.get('[data-testid="array-list-remove-item-1"]').element as HTMLElement).click();
|
|
60
62
|
jest.advanceTimersByTime(50);
|
|
61
63
|
jest.useRealTimers();
|
|
62
64
|
|
|
63
|
-
expect(wrapper.find('[data-testid="remove-item-2"]').exists()).toBe(false);
|
|
65
|
+
expect(wrapper.find('[data-testid="array-list-remove-item-2"]').exists()).toBe(false);
|
|
64
66
|
expect((wrapper.emitted('remove')![0][0] as any).row.value).toStrictEqual('string 1');
|
|
67
|
+
expect(wrapper.vm.rows).toStrictEqual([{ value: 'string 0' }, { value: 'string 2' }]);
|
|
65
68
|
expect(wrapper.emitted('update:value')![0][0]).toStrictEqual(['string 0', 'string 2']);
|
|
66
69
|
});
|
|
67
70
|
|
|
@@ -24,4 +24,39 @@ describe('colorInput.vue', () => {
|
|
|
24
24
|
expect(colorWrapper.classes()).not.toContain('disabled');
|
|
25
25
|
expect(Object.keys(colorInput.attributes())).not.toContain('disabled');
|
|
26
26
|
});
|
|
27
|
+
|
|
28
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', () => {
|
|
29
|
+
const label = 'some-label';
|
|
30
|
+
const describeById = 'some-id';
|
|
31
|
+
|
|
32
|
+
const wrapper = shallowMount(ColorInput, {
|
|
33
|
+
props: { label },
|
|
34
|
+
attrs: { 'aria-describedby': describeById }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const colorInput = wrapper.find('input');
|
|
38
|
+
const ariaDisabled = colorInput.attributes('aria-disabled');
|
|
39
|
+
const ariaLabel = colorInput.attributes('aria-label');
|
|
40
|
+
const ariaDescribedBy = colorInput.attributes('aria-describedby');
|
|
41
|
+
|
|
42
|
+
expect(ariaDisabled).toBe('false');
|
|
43
|
+
expect(ariaLabel).toBe(label);
|
|
44
|
+
expect(ariaDescribedBy).toBe(describeById);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('a11y: adding aria-label ($attrs) from parent should override label-based aria-label', () => {
|
|
48
|
+
const inputLabel = 'some-label';
|
|
49
|
+
const overrideLabel = 'some-override-label';
|
|
50
|
+
|
|
51
|
+
const wrapper = shallowMount(ColorInput, {
|
|
52
|
+
props: { label: inputLabel },
|
|
53
|
+
attrs: { 'aria-label': overrideLabel }
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const colorInput = wrapper.find('input');
|
|
57
|
+
const ariaLabel = colorInput.attributes('aria-label');
|
|
58
|
+
|
|
59
|
+
expect(ariaLabel).toBe(overrideLabel);
|
|
60
|
+
expect(ariaLabel).not.toBe(inputLabel);
|
|
61
|
+
});
|
|
27
62
|
});
|
|
@@ -214,4 +214,44 @@ describe('component: LabeledSelect', () => {
|
|
|
214
214
|
expect(wrapper.vm.$data.myValue).toStrictEqual(expectation);
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
|
+
|
|
218
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', async() => {
|
|
219
|
+
const label = 'Foo';
|
|
220
|
+
const value = 'foo';
|
|
221
|
+
const ariaDescribedById = 'some-described-by-id';
|
|
222
|
+
const itemLabel = 'some-label';
|
|
223
|
+
|
|
224
|
+
const wrapper = mount(LabeledSelect, {
|
|
225
|
+
props: {
|
|
226
|
+
value,
|
|
227
|
+
label: itemLabel,
|
|
228
|
+
options: [
|
|
229
|
+
{ label, value },
|
|
230
|
+
],
|
|
231
|
+
required: true
|
|
232
|
+
},
|
|
233
|
+
attrs: { 'aria-describedby': ariaDescribedById }
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const labeledSelectContainer = wrapper.find('.labeled-select');
|
|
237
|
+
const ariaExpanded = labeledSelectContainer.attributes('aria-expanded');
|
|
238
|
+
const ariaDescribedBy = labeledSelectContainer.attributes('aria-describedby');
|
|
239
|
+
const ariaRequired = labeledSelectContainer.attributes('aria-required');
|
|
240
|
+
const containerId = labeledSelectContainer.attributes('id');
|
|
241
|
+
const labelFor = wrapper.find('label').attributes('for');
|
|
242
|
+
|
|
243
|
+
const vSelectInput = wrapper.find('.v-select');
|
|
244
|
+
|
|
245
|
+
expect(ariaExpanded).toBe('false');
|
|
246
|
+
expect(ariaDescribedBy).toBe(ariaDescribedById);
|
|
247
|
+
expect(ariaRequired).toBe('true');
|
|
248
|
+
expect(containerId).toBe(wrapper.vm.labeledSelectLabelId);
|
|
249
|
+
expect(labelFor).toBe(wrapper.vm.labeledSelectLabelId);
|
|
250
|
+
|
|
251
|
+
// make sure it's hardcoded to a "neutral" value so that
|
|
252
|
+
// in the current architecture of the component
|
|
253
|
+
// screen readers won't pick up the default "Select option" aria-label
|
|
254
|
+
// from the library
|
|
255
|
+
expect(vSelectInput.attributes('aria-label')).toBe('-');
|
|
256
|
+
});
|
|
217
257
|
});
|
|
@@ -6,9 +6,9 @@ import { nextTick } from 'vue';
|
|
|
6
6
|
describe('component: MatchExpressions', () => {
|
|
7
7
|
it('should display all the inputs', () => {
|
|
8
8
|
const wrapper = mount(MatchExpressions, {
|
|
9
|
-
props: {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
props: {
|
|
10
|
+
mode: _CREATE,
|
|
11
|
+
value: [
|
|
12
12
|
{
|
|
13
13
|
id: '123',
|
|
14
14
|
key: '123',
|
|
@@ -16,7 +16,7 @@ describe('component: MatchExpressions', () => {
|
|
|
16
16
|
values: '123'
|
|
17
17
|
}
|
|
18
18
|
]
|
|
19
|
-
}
|
|
19
|
+
},
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
const inputWraps = wrapper.findAll('[data-testid^=input-match-expression-]');
|
|
@@ -29,9 +29,9 @@ describe('component: MatchExpressions', () => {
|
|
|
29
29
|
'values',
|
|
30
30
|
])('should emit an update on %p input', async(field) => {
|
|
31
31
|
const wrapper = mount(MatchExpressions, {
|
|
32
|
-
props: {
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
props: {
|
|
33
|
+
mode: _CREATE,
|
|
34
|
+
value: [
|
|
35
35
|
{
|
|
36
36
|
id: '123',
|
|
37
37
|
key: '123',
|
|
@@ -39,7 +39,7 @@ describe('component: MatchExpressions', () => {
|
|
|
39
39
|
values: '123'
|
|
40
40
|
}
|
|
41
41
|
]
|
|
42
|
-
}
|
|
42
|
+
},
|
|
43
43
|
});
|
|
44
44
|
const input = wrapper.find(`[data-testid="input-match-expression-${ field }-0"]`).find('input');
|
|
45
45
|
const newValue = 123;
|
|
@@ -54,9 +54,9 @@ describe('component: MatchExpressions', () => {
|
|
|
54
54
|
'operator',
|
|
55
55
|
])('should emit an update on %p selection change', async(field) => {
|
|
56
56
|
const wrapper = mount(MatchExpressions, {
|
|
57
|
-
props: {
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
props: {
|
|
58
|
+
mode: _CREATE,
|
|
59
|
+
value: [
|
|
60
60
|
{
|
|
61
61
|
id: '123',
|
|
62
62
|
key: '123',
|
|
@@ -64,7 +64,7 @@ describe('component: MatchExpressions', () => {
|
|
|
64
64
|
values: '123'
|
|
65
65
|
}
|
|
66
66
|
]
|
|
67
|
-
}
|
|
67
|
+
},
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
const select = wrapper.find(`[data-testid="input-match-expression-${ field }-0"]`);
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import NameNsDescription from '@shell/components/form/NameNsDescription.vue';
|
|
3
|
+
import { createStore } from 'vuex';
|
|
3
4
|
|
|
4
5
|
describe('component: NameNsDescription', () => {
|
|
5
6
|
// Accessing to computed value due code complexity
|
|
6
7
|
it('should map namespaces to options', () => {
|
|
7
8
|
const namespaceName = 'test';
|
|
9
|
+
const store = createStore({
|
|
10
|
+
getters: {
|
|
11
|
+
allowedNamespaces: () => () => ({ [namespaceName]: true }),
|
|
12
|
+
currentStore: () => () => 'cluster',
|
|
13
|
+
'cluster/schemaFor': () => jest.fn()
|
|
14
|
+
}
|
|
15
|
+
});
|
|
8
16
|
const result = [
|
|
9
17
|
{
|
|
10
18
|
label: namespaceName,
|
|
@@ -13,20 +21,21 @@ describe('component: NameNsDescription', () => {
|
|
|
13
21
|
];
|
|
14
22
|
const wrapper = mount(NameNsDescription, {
|
|
15
23
|
props: {
|
|
16
|
-
value:
|
|
24
|
+
value: {
|
|
25
|
+
setAnnotation: jest.fn(),
|
|
26
|
+
metadata: {}
|
|
27
|
+
},
|
|
17
28
|
mode: 'create',
|
|
18
29
|
cluster: {},
|
|
19
30
|
},
|
|
20
31
|
global: {
|
|
21
|
-
|
|
32
|
+
provide: { store },
|
|
33
|
+
mocks: {
|
|
22
34
|
$store: {
|
|
23
35
|
dispatch: jest.fn(),
|
|
24
36
|
getters: {
|
|
25
|
-
namespaces:
|
|
26
|
-
|
|
27
|
-
currentStore: () => 'cluster',
|
|
28
|
-
'cluster/schemaFor': jest.fn(),
|
|
29
|
-
'i18n/t': jest.fn(),
|
|
37
|
+
namespaces: jest.fn(),
|
|
38
|
+
'i18n/t': jest.fn(),
|
|
30
39
|
},
|
|
31
40
|
},
|
|
32
41
|
},
|
|
@@ -38,27 +47,35 @@ describe('component: NameNsDescription', () => {
|
|
|
38
47
|
|
|
39
48
|
it('should emit in case of new namespace', () => {
|
|
40
49
|
const namespaceName = 'test';
|
|
50
|
+
const store = createStore({
|
|
51
|
+
getters: {
|
|
52
|
+
allowedNamespaces: () => () => ({ [namespaceName]: true }),
|
|
53
|
+
currentStore: () => () => 'cluster',
|
|
54
|
+
'cluster/schemaFor': () => jest.fn()
|
|
55
|
+
}
|
|
56
|
+
});
|
|
41
57
|
const newNamespaceName = 'bananas';
|
|
42
58
|
const wrapper = mount(NameNsDescription, {
|
|
43
59
|
props: {
|
|
44
|
-
value: {
|
|
45
|
-
|
|
60
|
+
value: {
|
|
61
|
+
setAnnotation: jest.fn(),
|
|
62
|
+
metadata: {}
|
|
63
|
+
},
|
|
64
|
+
mode: 'create',
|
|
46
65
|
},
|
|
47
66
|
global: {
|
|
48
|
-
|
|
67
|
+
provide: { store },
|
|
68
|
+
mocks: {
|
|
49
69
|
$store: {
|
|
50
70
|
dispatch: jest.fn(),
|
|
51
71
|
getters: {
|
|
52
72
|
namespaces: jest.fn(),
|
|
53
|
-
allowedNamespaces: () => ({ [namespaceName]: true }),
|
|
54
73
|
'customizations/getPreviewCluster': {
|
|
55
74
|
ready: true,
|
|
56
75
|
isLocal: false,
|
|
57
76
|
badge: {},
|
|
58
77
|
},
|
|
59
|
-
|
|
60
|
-
'cluster/schemaFor': jest.fn(),
|
|
61
|
-
'i18n/t': jest.fn(),
|
|
78
|
+
'i18n/t': jest.fn(),
|
|
62
79
|
},
|
|
63
80
|
},
|
|
64
81
|
},
|
|
@@ -69,4 +86,88 @@ describe('component: NameNsDescription', () => {
|
|
|
69
86
|
|
|
70
87
|
expect(wrapper.emitted().isNamespaceNew?.[0][0]).toBe(true);
|
|
71
88
|
});
|
|
89
|
+
|
|
90
|
+
it('renders the name input with the expected value', () => {
|
|
91
|
+
const namespaceName = 'test';
|
|
92
|
+
const store = createStore({
|
|
93
|
+
getters: {
|
|
94
|
+
allowedNamespaces: () => () => ({ [namespaceName]: true }),
|
|
95
|
+
currentStore: () => () => 'cluster',
|
|
96
|
+
'cluster/schemaFor': () => jest.fn()
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const wrapper = mount(NameNsDescription, {
|
|
100
|
+
props: {
|
|
101
|
+
value: {
|
|
102
|
+
setAnnotation: jest.fn(),
|
|
103
|
+
metadata: { name: 'Default' }
|
|
104
|
+
},
|
|
105
|
+
mode: 'create',
|
|
106
|
+
},
|
|
107
|
+
global: {
|
|
108
|
+
provide: { store },
|
|
109
|
+
mocks: {
|
|
110
|
+
$store: {
|
|
111
|
+
dispatch: jest.fn(),
|
|
112
|
+
getters: {
|
|
113
|
+
namespaces: jest.fn(),
|
|
114
|
+
'customizations/getPreviewCluster': {
|
|
115
|
+
ready: true,
|
|
116
|
+
isLocal: false,
|
|
117
|
+
badge: {},
|
|
118
|
+
},
|
|
119
|
+
'i18n/t': jest.fn(),
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const nameInput = wrapper.find('[data-testid="NameNsDescriptionNameInput"]');
|
|
127
|
+
|
|
128
|
+
expect(nameInput.element.value).toBe('Default');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('sets the name using the nameKey prop', () => {
|
|
132
|
+
const namespaceName = 'test';
|
|
133
|
+
const store = createStore({
|
|
134
|
+
getters: {
|
|
135
|
+
allowedNamespaces: () => () => ({ [namespaceName]: true }),
|
|
136
|
+
currentStore: () => () => 'cluster',
|
|
137
|
+
'cluster/schemaFor': () => jest.fn()
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
const wrapper = mount(NameNsDescription, {
|
|
141
|
+
props: {
|
|
142
|
+
value: {
|
|
143
|
+
setAnnotation: jest.fn(),
|
|
144
|
+
metadata: {},
|
|
145
|
+
spec: { displayName: 'Default' }
|
|
146
|
+
},
|
|
147
|
+
mode: 'create',
|
|
148
|
+
nameKey: 'spec.displayName'
|
|
149
|
+
},
|
|
150
|
+
global: {
|
|
151
|
+
provide: { store },
|
|
152
|
+
mocks: {
|
|
153
|
+
$store: {
|
|
154
|
+
dispatch: jest.fn(),
|
|
155
|
+
getters: {
|
|
156
|
+
namespaces: jest.fn(),
|
|
157
|
+
'customizations/getPreviewCluster': {
|
|
158
|
+
ready: true,
|
|
159
|
+
isLocal: false,
|
|
160
|
+
badge: {},
|
|
161
|
+
},
|
|
162
|
+
'i18n/t': jest.fn(),
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const nameInput = wrapper.find('[data-testid="NameNsDescriptionNameInput"]');
|
|
170
|
+
|
|
171
|
+
expect(nameInput.element.value).toBe('Default');
|
|
172
|
+
});
|
|
72
173
|
});
|
|
@@ -6,18 +6,20 @@ import { DefaultProps } from 'vue/types/options';
|
|
|
6
6
|
|
|
7
7
|
describe('component: Probe', () => {
|
|
8
8
|
describe.each([
|
|
9
|
-
['
|
|
10
|
-
[
|
|
11
|
-
[
|
|
12
|
-
])('given kind %p', (
|
|
9
|
+
[{ httpGet: { scheme: 'https' } }, ['port', 'path']],
|
|
10
|
+
[{ tcpSocket: {} }, ['socket']],
|
|
11
|
+
[{ exec: {} }, ['command']],
|
|
12
|
+
])('given kind %p', (value, extraFields) => {
|
|
13
13
|
it.each([
|
|
14
14
|
...extraFields,
|
|
15
15
|
'successThreshold',
|
|
16
16
|
'failureThreshold',
|
|
17
17
|
])('should emit an update on %p input', (field) => {
|
|
18
18
|
const wrapper = mount(Probe as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
|
|
19
|
-
props: {
|
|
20
|
-
|
|
19
|
+
props: {
|
|
20
|
+
mode: _EDIT,
|
|
21
|
+
value,
|
|
22
|
+
},
|
|
21
23
|
});
|
|
22
24
|
const input = wrapper.find(`[data-testid="input-probe-${ field }"]`).find('input');
|
|
23
25
|
const newValue = 123;
|
|
@@ -33,8 +35,10 @@ describe('component: Probe', () => {
|
|
|
33
35
|
'timeoutSeconds',
|
|
34
36
|
])('should emit an update on %p input and blur', (field) => {
|
|
35
37
|
const wrapper = mount(Probe as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
|
|
36
|
-
props: {
|
|
37
|
-
|
|
38
|
+
props: {
|
|
39
|
+
mode: _EDIT,
|
|
40
|
+
value
|
|
41
|
+
},
|
|
38
42
|
});
|
|
39
43
|
const input = wrapper.find(`[data-testid="input-probe-${ field }"]`).find('input');
|
|
40
44
|
const newValue = 123;
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
3
3
|
import SSHKnownHosts from '@shell/components/form/SSHKnownHosts/index.vue';
|
|
4
|
+
import { createStore } from 'vuex';
|
|
5
|
+
|
|
6
|
+
jest.mock('focus-trap', () => {
|
|
7
|
+
return {
|
|
8
|
+
createFocusTrap: jest.fn().mockImplementation(() => {
|
|
9
|
+
return {
|
|
10
|
+
activate: jest.fn(),
|
|
11
|
+
deactivate: jest.fn(),
|
|
12
|
+
};
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
});
|
|
4
16
|
|
|
5
17
|
describe('component: SSHKnownHosts', () => {
|
|
6
18
|
it.each([
|
|
@@ -42,18 +54,26 @@ describe('component: SSHKnownHosts', () => {
|
|
|
42
54
|
});
|
|
43
55
|
|
|
44
56
|
it('mode edit: should open edit dialog', async() => {
|
|
57
|
+
const actions = { 'management/promptModal': jest.fn() };
|
|
58
|
+
|
|
45
59
|
const wrapper = mount(SSHKnownHosts, {
|
|
46
60
|
props: {
|
|
47
61
|
mode: _EDIT,
|
|
48
62
|
value: '',
|
|
63
|
+
},
|
|
64
|
+
global: {
|
|
65
|
+
mocks: {
|
|
66
|
+
$store: createStore({ actions }),
|
|
67
|
+
$fetchState: {}
|
|
68
|
+
},
|
|
69
|
+
stubs: { transition: false }
|
|
49
70
|
}
|
|
50
71
|
});
|
|
51
72
|
|
|
52
73
|
const knownSshHostsOpenDialog = wrapper.find('[data-testid="input-known-ssh-hosts_open-dialog"]');
|
|
53
|
-
const editDialog = wrapper.vm.$refs['editDialog'] as any;
|
|
54
74
|
|
|
55
75
|
await knownSshHostsOpenDialog.trigger('click');
|
|
56
76
|
|
|
57
|
-
expect(
|
|
77
|
+
expect(actions['management/promptModal']).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ component: 'KnownHostsEditDialog' }));
|
|
58
78
|
});
|
|
59
79
|
});
|
|
@@ -27,4 +27,41 @@ describe('select.vue', () => {
|
|
|
27
27
|
// eslint-disable-next-line no-console
|
|
28
28
|
expect(console.warn).not.toHaveBeenCalled();
|
|
29
29
|
});
|
|
30
|
+
|
|
31
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', async() => {
|
|
32
|
+
const label = 'Foo';
|
|
33
|
+
const value = 'foo';
|
|
34
|
+
const ariaDescribedById = 'some-described-by-id';
|
|
35
|
+
const ariaLabelText = 'some-aria-label';
|
|
36
|
+
|
|
37
|
+
const wrapper = shallowMount(SelectComponent, {
|
|
38
|
+
props: {
|
|
39
|
+
value,
|
|
40
|
+
options: [
|
|
41
|
+
{ label, value },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
attrs: {
|
|
45
|
+
'aria-describedby': ariaDescribedById,
|
|
46
|
+
'aria-label': ariaLabelText,
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const labeledSelectContainer = wrapper.find('.unlabeled-select');
|
|
51
|
+
const ariaExpanded = labeledSelectContainer.attributes('aria-expanded');
|
|
52
|
+
const ariaDescribedBy = labeledSelectContainer.attributes('aria-describedby');
|
|
53
|
+
const ariaLabel = labeledSelectContainer.attributes('aria-label');
|
|
54
|
+
|
|
55
|
+
const vSelectInput = wrapper.find('.inline');
|
|
56
|
+
|
|
57
|
+
expect(ariaExpanded).toBe('false');
|
|
58
|
+
expect(ariaDescribedBy).toBe(ariaDescribedById);
|
|
59
|
+
expect(ariaLabel).toBe(ariaLabelText);
|
|
60
|
+
|
|
61
|
+
// make sure it's hardcoded to a "neutral" value so that
|
|
62
|
+
// in the current architecture of the component
|
|
63
|
+
// screen readers won't pick up the default "Select option" aria-label
|
|
64
|
+
// from the library
|
|
65
|
+
expect(vSelectInput.attributes('aria-label')).toBe('-');
|
|
66
|
+
});
|
|
30
67
|
});
|
|
@@ -28,6 +28,7 @@ export default {
|
|
|
28
28
|
<span>
|
|
29
29
|
<template v-if="isIp(row.externalIp)">
|
|
30
30
|
{{ row.externalIp }} <CopyToClipboard
|
|
31
|
+
:aria-label="t('internalExternalIP.copyExternalIp')"
|
|
31
32
|
label-as="tooltip"
|
|
32
33
|
:text="row.externalIp"
|
|
33
34
|
class="icon-btn"
|
|
@@ -43,6 +44,7 @@ export default {
|
|
|
43
44
|
</template>
|
|
44
45
|
<template v-else-if="isIp(row.internalIp)">
|
|
45
46
|
{{ row.internalIp }}<CopyToClipboard
|
|
47
|
+
:aria-label="t('internalExternalIP.copyInternalIp')"
|
|
46
48
|
label-as="tooltip"
|
|
47
49
|
:text="row.internalIp"
|
|
48
50
|
class="icon-btn"
|
|
@@ -13,17 +13,25 @@ export default {
|
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
beforeMount() {
|
|
17
17
|
if (this.value.issuer) {
|
|
18
18
|
const { cn, notAfter, sans = [] } = this.value;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return { isTLS: false };
|
|
20
|
+
this.expiration = notAfter;
|
|
21
|
+
this.sans = sans;
|
|
22
|
+
this.cn = cn;
|
|
23
|
+
this.isTLS = true;
|
|
25
24
|
}
|
|
26
25
|
},
|
|
26
|
+
|
|
27
|
+
data() {
|
|
28
|
+
return {
|
|
29
|
+
isTLS: false,
|
|
30
|
+
cn: null,
|
|
31
|
+
sans: [],
|
|
32
|
+
expiration: null,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
27
35
|
computed: {
|
|
28
36
|
// use 'text-warning' or 'text-error' classes if the cert is <8 days from expiring or expired respectively
|
|
29
37
|
dateClass() {
|
|
@@ -43,7 +51,12 @@ export default {
|
|
|
43
51
|
|
|
44
52
|
<template>
|
|
45
53
|
<div v-if="isTLS">
|
|
46
|
-
<t k="secret.certificate.cn" />
|
|
54
|
+
<t k="secret.certificate.cn" />
|
|
55
|
+
{{ cn }}
|
|
56
|
+
<span v-if="row.unrepeatedSans && row.unrepeatedSans.length">
|
|
57
|
+
{{ t('secret.certificate.plusMore', {n:row.unrepeatedSans.length}) }}
|
|
58
|
+
</span>
|
|
59
|
+
<br>
|
|
47
60
|
<t k="secret.certificate.expires" />: <DateComponent
|
|
48
61
|
:class="dateClass"
|
|
49
62
|
:value="expiration"
|
package/components/nav/Group.vue
CHANGED
|
@@ -99,6 +99,11 @@ export default {
|
|
|
99
99
|
},
|
|
100
100
|
|
|
101
101
|
groupSelected() {
|
|
102
|
+
// Can not click on groups that are fixed open
|
|
103
|
+
if (this.fixedOpen) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
102
107
|
// Don't auto-select first group entry if we're already expanded and contain the currently-selected nav item
|
|
103
108
|
if (this.hasActiveRoute() && this.isExpanded) {
|
|
104
109
|
return;
|
|
@@ -158,6 +163,17 @@ export default {
|
|
|
158
163
|
items = this.group;
|
|
159
164
|
}
|
|
160
165
|
|
|
166
|
+
let parentPath = '';
|
|
167
|
+
const cluster = this.$route.params?.cluster;
|
|
168
|
+
|
|
169
|
+
// Where we use nested route configuration, consider the parent route when trying to identify the nav location
|
|
170
|
+
if (this.$route.matched.length > 1) {
|
|
171
|
+
const parentRoute = this.$route.matched[this.$route.matched.length - 2];
|
|
172
|
+
|
|
173
|
+
parentPath = parentRoute.path.replace(':cluster', cluster);
|
|
174
|
+
parentPath = parentPath === '/' ? undefined : parentPath;
|
|
175
|
+
}
|
|
176
|
+
|
|
161
177
|
for (const item of items.children) {
|
|
162
178
|
if (item.children && this.hasActiveRoute(item)) {
|
|
163
179
|
return true;
|
|
@@ -166,8 +182,11 @@ export default {
|
|
|
166
182
|
const matchesNavLevel = navLevels.filter((param) => !this.$route.params[param] || this.$route.params[param] !== item.route.params[param]).length === 0;
|
|
167
183
|
const withoutHash = this.$route.hash ? this.$route.fullPath.slice(0, this.$route.fullPath.indexOf(this.$route.hash)) : this.$route.fullPath;
|
|
168
184
|
const withoutQuery = withoutHash.split('?')[0];
|
|
185
|
+
const itemFullPath = this.$router.resolve(item.route).fullPath;
|
|
169
186
|
|
|
170
|
-
if (matchesNavLevel ||
|
|
187
|
+
if (matchesNavLevel || itemFullPath === withoutQuery) {
|
|
188
|
+
return true;
|
|
189
|
+
} else if (parentPath && itemFullPath === parentPath) {
|
|
171
190
|
return true;
|
|
172
191
|
}
|
|
173
192
|
}
|
|
@@ -205,14 +224,14 @@ export default {
|
|
|
205
224
|
<template>
|
|
206
225
|
<div
|
|
207
226
|
class="accordion"
|
|
208
|
-
:class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': isGroupActive}"
|
|
227
|
+
:class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': isGroupActive }"
|
|
209
228
|
>
|
|
210
229
|
<div
|
|
211
230
|
v-if="showHeader"
|
|
212
231
|
class="header"
|
|
213
|
-
:class="{'active': isOverview, 'noHover': !canCollapse}"
|
|
232
|
+
:class="{'active': isOverview, 'noHover': !canCollapse || fixedOpen}"
|
|
214
233
|
role="button"
|
|
215
|
-
tabindex="0"
|
|
234
|
+
:tabindex="fixedOpen ? -1 : 0"
|
|
216
235
|
:aria-label="group.labelDisplay || group.label || ''"
|
|
217
236
|
@click="groupSelected()"
|
|
218
237
|
@keyup.enter="groupSelected()"
|
|
@@ -260,7 +279,7 @@ export default {
|
|
|
260
279
|
v-if="child.divider"
|
|
261
280
|
:key="idx"
|
|
262
281
|
>
|
|
263
|
-
<hr>
|
|
282
|
+
<hr role="none">
|
|
264
283
|
</li>
|
|
265
284
|
<!-- <div v-else-if="child[childrenKey] && hideGroup(child[childrenKey])" :key="child.name">
|
|
266
285
|
HIDDEN
|
|
@@ -358,6 +377,9 @@ export default {
|
|
|
358
377
|
&:hover:not(.active) {
|
|
359
378
|
background-color: var(--nav-hover);
|
|
360
379
|
}
|
|
380
|
+
&:hover:not(.active).noHover {
|
|
381
|
+
background-color: inherit;
|
|
382
|
+
}
|
|
361
383
|
}
|
|
362
384
|
}
|
|
363
385
|
|