@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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { createStore, Store } from 'vuex';
|
|
3
|
+
import { nextTick } from 'vue';
|
|
4
|
+
import SlideInPanelManager from '@shell/components/SlideInPanelManager.vue';
|
|
5
|
+
|
|
6
|
+
const MockComponent = {
|
|
7
|
+
template: '<div data-testid="slide-in-panel-component">Mock Panel Content</div>',
|
|
8
|
+
props: ['width', 'title', 'extraProp']
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
describe('slideInPanelManager.vue with Teleport', () => {
|
|
12
|
+
let store: Store<any>;
|
|
13
|
+
let getters: Record<string, () => any>;
|
|
14
|
+
let slidesDiv: HTMLDivElement;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
// Create teleport target container
|
|
18
|
+
slidesDiv = document.createElement('div');
|
|
19
|
+
slidesDiv.setAttribute('id', 'slides');
|
|
20
|
+
document.body.appendChild(slidesDiv);
|
|
21
|
+
|
|
22
|
+
getters = {
|
|
23
|
+
'slideInPanel/isOpen': () => true,
|
|
24
|
+
'slideInPanel/component': () => MockComponent,
|
|
25
|
+
'slideInPanel/componentProps': () => ({
|
|
26
|
+
width: '40%', title: 'Test Title', extraProp: 'extra'
|
|
27
|
+
})
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
store = createStore({
|
|
31
|
+
getters,
|
|
32
|
+
mutations: { 'slideInPanel/close': jest.fn() }
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
// Clean up the teleport container
|
|
38
|
+
document.body.removeChild(slidesDiv);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const factory = () => {
|
|
42
|
+
return mount(SlideInPanelManager, {
|
|
43
|
+
attachTo: document.body, // attach to document so Teleport renders
|
|
44
|
+
global: { plugins: [store] }
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
it('renders slide in panel with proper style when open', async() => {
|
|
49
|
+
factory();
|
|
50
|
+
await nextTick();
|
|
51
|
+
|
|
52
|
+
const slidePanel = document.querySelector('#slides .slide-in') as HTMLElement;
|
|
53
|
+
const slideGlass = document.querySelector('[data-testid="slide-in-glass"]') as HTMLElement;
|
|
54
|
+
const slideComponent = document.querySelector('[data-testid="slide-in-panel-component"]') as HTMLElement;
|
|
55
|
+
const headerTitle = document.querySelector('#slides .slide-in .header .title') as HTMLElement;
|
|
56
|
+
|
|
57
|
+
expect(slidePanel).toBeTruthy();
|
|
58
|
+
expect(slideGlass).toBeTruthy();
|
|
59
|
+
expect(slideComponent).toBeTruthy();
|
|
60
|
+
expect(headerTitle.textContent?.trim()).toBe('Test Title');
|
|
61
|
+
|
|
62
|
+
const styleAttr = slidePanel.getAttribute('style') || '';
|
|
63
|
+
|
|
64
|
+
expect(styleAttr).toContain('width: 40%');
|
|
65
|
+
expect(styleAttr).toContain('top: 55px');
|
|
66
|
+
expect(styleAttr).toContain('height: calc(100vh - 55px)');
|
|
67
|
+
expect(styleAttr).toContain('right: 0');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('renders default panel title when no title is provided', async() => {
|
|
71
|
+
// Update getter so that no title is provided
|
|
72
|
+
getters['slideInPanel/componentProps'] = () => ({ width: '40%' });
|
|
73
|
+
store = createStore({
|
|
74
|
+
getters,
|
|
75
|
+
mutations: { 'slideInPanel/close': jest.fn() }
|
|
76
|
+
});
|
|
77
|
+
factory();
|
|
78
|
+
await nextTick();
|
|
79
|
+
|
|
80
|
+
const headerTitle = document.querySelector('#slides #slide-in-panel-manager .header .title') as HTMLElement;
|
|
81
|
+
|
|
82
|
+
expect(headerTitle.textContent?.trim()).toBe('Details');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('computes panelTop correctly when a banner exists', async() => {
|
|
86
|
+
// Create a banner element with a simulated clientHeight.
|
|
87
|
+
const banner = document.createElement('div');
|
|
88
|
+
|
|
89
|
+
banner.setAttribute('id', 'banner-header');
|
|
90
|
+
document.body.appendChild(banner);
|
|
91
|
+
// Simulate a banner with a clientHeight of 100.
|
|
92
|
+
Object.defineProperty(banner, 'clientHeight', { value: 100, configurable: true });
|
|
93
|
+
|
|
94
|
+
factory();
|
|
95
|
+
await nextTick();
|
|
96
|
+
|
|
97
|
+
const slidePanel = document.querySelector('#slides .slide-in') as HTMLElement;
|
|
98
|
+
const styleAttr = slidePanel.getAttribute('style') || '';
|
|
99
|
+
|
|
100
|
+
// Expected panelTop = HEADER_HEIGHT (55) + banner.clientHeight (100) = "155px"
|
|
101
|
+
expect(styleAttr).toContain('top: 155px');
|
|
102
|
+
expect(styleAttr).toContain('height: calc(100vh - 155px)');
|
|
103
|
+
|
|
104
|
+
document.body.removeChild(banner);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders slide in glass as hidden and panel with negative right when closed', async() => {
|
|
108
|
+
// Set isOpen to false.
|
|
109
|
+
getters['slideInPanel/isOpen'] = () => false;
|
|
110
|
+
store = createStore({
|
|
111
|
+
getters,
|
|
112
|
+
mutations: { 'slideInPanel/close': jest.fn() }
|
|
113
|
+
});
|
|
114
|
+
factory();
|
|
115
|
+
await nextTick();
|
|
116
|
+
|
|
117
|
+
const slideGlass = document.querySelector('[data-testid="slide-in-glass"]') as HTMLElement;
|
|
118
|
+
|
|
119
|
+
expect(slideGlass).toBeTruthy();
|
|
120
|
+
expect(slideGlass.style.display).toBe('none');
|
|
121
|
+
|
|
122
|
+
const slidePanel = document.querySelector('#slides .slide-in') as HTMLElement;
|
|
123
|
+
const styleAttr = slidePanel.getAttribute('style') || '';
|
|
124
|
+
|
|
125
|
+
// With currentProps width "40%", panelRight should be "-40%" when closed.
|
|
126
|
+
expect(styleAttr).toContain('right: -40%');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('calls store commit when clicking on the slide-in glass overlay', async() => {
|
|
130
|
+
const closeMutation = jest.fn();
|
|
131
|
+
|
|
132
|
+
getters['slideInPanel/isOpen'] = () => true;
|
|
133
|
+
store = createStore({
|
|
134
|
+
getters,
|
|
135
|
+
mutations: { 'slideInPanel/close': closeMutation }
|
|
136
|
+
});
|
|
137
|
+
factory();
|
|
138
|
+
await nextTick();
|
|
139
|
+
|
|
140
|
+
const slideGlass = document.querySelector('[data-testid="slide-in-glass"]') as HTMLElement;
|
|
141
|
+
|
|
142
|
+
slideGlass.click();
|
|
143
|
+
await nextTick();
|
|
144
|
+
|
|
145
|
+
expect(closeMutation).toHaveBeenCalledWith({}, undefined);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('calls store commit when clicking on the slide-in close icon', async() => {
|
|
149
|
+
const closeMutation = jest.fn();
|
|
150
|
+
|
|
151
|
+
getters['slideInPanel/isOpen'] = () => true;
|
|
152
|
+
store = createStore({
|
|
153
|
+
getters,
|
|
154
|
+
mutations: { 'slideInPanel/close': closeMutation }
|
|
155
|
+
});
|
|
156
|
+
factory();
|
|
157
|
+
await nextTick();
|
|
158
|
+
|
|
159
|
+
const closeIcon = document.querySelector('[data-testid="slide-in-close"]') as HTMLElement;
|
|
160
|
+
|
|
161
|
+
closeIcon.click();
|
|
162
|
+
await nextTick();
|
|
163
|
+
|
|
164
|
+
expect(closeMutation).toHaveBeenCalledWith({}, undefined);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
<script>
|
|
3
3
|
import { Banner } from '@components/Banner';
|
|
4
|
-
import DisableAuthProviderModal from '@shell/components/DisableAuthProviderModal';
|
|
5
4
|
|
|
6
5
|
export default {
|
|
7
|
-
components: {
|
|
8
|
-
Banner,
|
|
9
|
-
DisableAuthProviderModal
|
|
10
|
-
},
|
|
6
|
+
components: { Banner },
|
|
11
7
|
|
|
12
8
|
props: {
|
|
13
9
|
tArgs: {
|
|
@@ -35,7 +31,18 @@ export default {
|
|
|
35
31
|
|
|
36
32
|
methods: {
|
|
37
33
|
showDisableModal() {
|
|
38
|
-
this.$
|
|
34
|
+
this.$store.dispatch('management/promptModal', {
|
|
35
|
+
component: 'DisableAuthProviderDialog',
|
|
36
|
+
customClass: 'remove-modal',
|
|
37
|
+
modalWidth: '400',
|
|
38
|
+
height: 'auto',
|
|
39
|
+
styles: 'max-height: 100vh;',
|
|
40
|
+
componentProps: {
|
|
41
|
+
disableCb: () => {
|
|
42
|
+
this.disable();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
39
46
|
}
|
|
40
47
|
},
|
|
41
48
|
};
|
|
@@ -78,11 +85,6 @@ export default {
|
|
|
78
85
|
v-if="$slots.footer"
|
|
79
86
|
name="footer"
|
|
80
87
|
/>
|
|
81
|
-
|
|
82
|
-
<DisableAuthProviderModal
|
|
83
|
-
ref="disableAuthProviderModal"
|
|
84
|
-
@disable="disable"
|
|
85
|
-
/>
|
|
86
88
|
</div>
|
|
87
89
|
</template>
|
|
88
90
|
|
|
@@ -51,8 +51,9 @@ describe('component: RoleDetailEdit', () => {
|
|
|
51
51
|
const wrapper = mount(RoleDetailEdit, {
|
|
52
52
|
props: {
|
|
53
53
|
value: {
|
|
54
|
-
rules:
|
|
55
|
-
subtype:
|
|
54
|
+
rules: [{ verbs }],
|
|
55
|
+
subtype: 'GLOBAL',
|
|
56
|
+
metadata: { name: 'global-role-with-inherited' },
|
|
56
57
|
},
|
|
57
58
|
},
|
|
58
59
|
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import SortableTable from '@shell/components/SortableTable';
|
|
3
|
+
import { colorForState, stateDisplay, stateSort } from '@shell/plugins/dashboard-store/resource-class';
|
|
4
|
+
|
|
5
|
+
function stateDisplayProperties(state) {
|
|
6
|
+
const color = colorForState(state).replace('text-', 'bg-');
|
|
7
|
+
const display = stateDisplay(state);
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
stateBackground: color,
|
|
11
|
+
stateDisplay: display,
|
|
12
|
+
stateSort: stateSort(color, display),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
3
15
|
|
|
4
16
|
export default {
|
|
5
17
|
name: 'FleetResources',
|
|
@@ -7,8 +19,8 @@ export default {
|
|
|
7
19
|
components: { SortableTable },
|
|
8
20
|
|
|
9
21
|
props: {
|
|
10
|
-
|
|
11
|
-
type:
|
|
22
|
+
rows: {
|
|
23
|
+
type: Array,
|
|
12
24
|
required: true,
|
|
13
25
|
},
|
|
14
26
|
clusterId: {
|
|
@@ -19,10 +31,13 @@ export default {
|
|
|
19
31
|
},
|
|
20
32
|
|
|
21
33
|
computed: {
|
|
22
|
-
|
|
23
|
-
return this.
|
|
34
|
+
resources() {
|
|
35
|
+
return (this.rows || []).map((r) => ({
|
|
36
|
+
tableKey: r.key,
|
|
37
|
+
...stateDisplayProperties(r.state),
|
|
38
|
+
...r,
|
|
39
|
+
}));
|
|
24
40
|
},
|
|
25
|
-
|
|
26
41
|
resourceHeaders() {
|
|
27
42
|
return [
|
|
28
43
|
{
|
|
@@ -72,7 +87,7 @@ export default {
|
|
|
72
87
|
|
|
73
88
|
<template>
|
|
74
89
|
<SortableTable
|
|
75
|
-
:rows="
|
|
90
|
+
:rows="resources"
|
|
76
91
|
:headers="resourceHeaders"
|
|
77
92
|
:table-actions="false"
|
|
78
93
|
:row-actions="false"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { ref, watch, computed } from 'vue';
|
|
2
3
|
import debounce from 'lodash/debounce';
|
|
3
4
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
4
5
|
import { removeAt } from '@shell/utils/array';
|
|
@@ -98,22 +99,83 @@ export default {
|
|
|
98
99
|
type: String,
|
|
99
100
|
default: '',
|
|
100
101
|
},
|
|
102
|
+
componentTestid: {
|
|
103
|
+
type: String,
|
|
104
|
+
default: 'array-list',
|
|
105
|
+
}
|
|
101
106
|
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
107
|
+
|
|
108
|
+
setup(props, { emit }) {
|
|
109
|
+
const input = (Array.isArray(props.value) ? props.value : []).slice();
|
|
110
|
+
const rows = ref([]);
|
|
105
111
|
|
|
106
112
|
for ( const value of input ) {
|
|
107
|
-
rows.push({ value });
|
|
113
|
+
rows.value.push({ value });
|
|
108
114
|
}
|
|
109
|
-
if ( !rows.length &&
|
|
110
|
-
const value =
|
|
115
|
+
if ( !rows.value.length && props.initialEmptyRow ) {
|
|
116
|
+
const value = props.defaultAddValue ? clone(props.defaultAddValue) : '';
|
|
111
117
|
|
|
112
|
-
rows.push({ value });
|
|
118
|
+
rows.value.push({ value });
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
|
|
121
|
+
const isView = computed(() => {
|
|
122
|
+
return props.mode === _VIEW;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Cleanup rows and emit input
|
|
127
|
+
*/
|
|
128
|
+
const update = () => {
|
|
129
|
+
if ( isView.value ) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const out = [];
|
|
133
|
+
|
|
134
|
+
for ( const row of rows.value ) {
|
|
135
|
+
const trim = !props.valueMultiline && (typeof row.value === 'string');
|
|
136
|
+
const value = trim ? row.value.trim() : row.value;
|
|
137
|
+
|
|
138
|
+
if ( typeof value !== 'undefined' ) {
|
|
139
|
+
out.push(value);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
emit('update:value', out);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const lastUpdateWasFromValue = ref(false);
|
|
146
|
+
const queueUpdate = debounce(update, 50);
|
|
147
|
+
|
|
148
|
+
watch(
|
|
149
|
+
rows,
|
|
150
|
+
() => {
|
|
151
|
+
// lastUpdateWasFromValue is used to break a cycle where when rows are updated
|
|
152
|
+
// this was called which then forced rows to updated again
|
|
153
|
+
if (!lastUpdateWasFromValue.value) {
|
|
154
|
+
queueUpdate();
|
|
155
|
+
}
|
|
156
|
+
lastUpdateWasFromValue.value = false;
|
|
157
|
+
},
|
|
158
|
+
{ deep: true }
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
watch(
|
|
162
|
+
() => props.value,
|
|
163
|
+
() => {
|
|
164
|
+
lastUpdateWasFromValue.value = true;
|
|
165
|
+
rows.value = (props.value || []).map((v) => ({ value: v }));
|
|
166
|
+
},
|
|
167
|
+
{ deep: true }
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
rows,
|
|
172
|
+
lastUpdateWasFromValue,
|
|
173
|
+
queueUpdate,
|
|
174
|
+
isView,
|
|
175
|
+
update,
|
|
176
|
+
};
|
|
116
177
|
},
|
|
178
|
+
|
|
117
179
|
computed: {
|
|
118
180
|
_addLabel() {
|
|
119
181
|
return this.addLabel || this.t('generic.add');
|
|
@@ -121,10 +183,6 @@ export default {
|
|
|
121
183
|
_removeLabel() {
|
|
122
184
|
return this.removeLabel || this.t('generic.remove');
|
|
123
185
|
},
|
|
124
|
-
|
|
125
|
-
isView() {
|
|
126
|
-
return this.mode === _VIEW;
|
|
127
|
-
},
|
|
128
186
|
showAdd() {
|
|
129
187
|
return this.addAllowed;
|
|
130
188
|
},
|
|
@@ -145,29 +203,7 @@ export default {
|
|
|
145
203
|
return !this.valueMultiline && this.protip;
|
|
146
204
|
}
|
|
147
205
|
},
|
|
148
|
-
watch: {
|
|
149
|
-
value: {
|
|
150
|
-
deep: true,
|
|
151
|
-
handler() {
|
|
152
|
-
this.lastUpdateWasFromValue = true;
|
|
153
|
-
this.rows = (this.value || []).map((v) => ({ value: v }));
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
rows: {
|
|
158
|
-
deep: true,
|
|
159
|
-
handler(newValue, oldValue) {
|
|
160
|
-
// lastUpdateWasFromValue is used to break a cycle where when rows are updated
|
|
161
|
-
// this was called which then forced rows to updated again
|
|
162
|
-
if (!this.lastUpdateWasFromValue) {
|
|
163
|
-
this.queueUpdate();
|
|
164
|
-
}
|
|
165
|
-
this.lastUpdateWasFromValue = false;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
206
|
created() {
|
|
170
|
-
this.queueUpdate = debounce(this.update, 50);
|
|
171
207
|
},
|
|
172
208
|
methods: {
|
|
173
209
|
add() {
|
|
@@ -193,26 +229,6 @@ export default {
|
|
|
193
229
|
this.queueUpdate();
|
|
194
230
|
},
|
|
195
231
|
|
|
196
|
-
/**
|
|
197
|
-
* Cleanup rows and emit input
|
|
198
|
-
*/
|
|
199
|
-
update() {
|
|
200
|
-
if ( this.isView ) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
const out = [];
|
|
204
|
-
|
|
205
|
-
for ( const row of this.rows ) {
|
|
206
|
-
const trim = !this.valueMultiline && (typeof row.value === 'string');
|
|
207
|
-
const value = trim ? row.value.trim() : row.value;
|
|
208
|
-
|
|
209
|
-
if ( typeof value !== 'undefined' ) {
|
|
210
|
-
out.push(value);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
this.$emit('update:value', out);
|
|
214
|
-
},
|
|
215
|
-
|
|
216
232
|
/**
|
|
217
233
|
* Handle paste event, e.g. split multiple lines in rows
|
|
218
234
|
*/
|
|
@@ -270,7 +286,7 @@ export default {
|
|
|
270
286
|
<div
|
|
271
287
|
v-for="(row, idx) in rows"
|
|
272
288
|
:key="idx"
|
|
273
|
-
:data-testid="
|
|
289
|
+
:data-testid="`${componentTestid}-box${ idx }`"
|
|
274
290
|
class="box"
|
|
275
291
|
>
|
|
276
292
|
<slot
|
|
@@ -294,7 +310,7 @@ export default {
|
|
|
294
310
|
v-if="valueMultiline"
|
|
295
311
|
ref="value"
|
|
296
312
|
v-model:value="row.value"
|
|
297
|
-
:data-testid="
|
|
313
|
+
:data-testid="`${componentTestid}-textarea-${idx}`"
|
|
298
314
|
:placeholder="valuePlaceholder"
|
|
299
315
|
:mode="mode"
|
|
300
316
|
:disabled="disabled"
|
|
@@ -305,7 +321,7 @@ export default {
|
|
|
305
321
|
v-else-if="rules.length > 0"
|
|
306
322
|
ref="value"
|
|
307
323
|
v-model:value="row.value"
|
|
308
|
-
:data-testid="
|
|
324
|
+
:data-testid="`${componentTestid}-labeled-input-${idx}`"
|
|
309
325
|
:placeholder="valuePlaceholder"
|
|
310
326
|
:disabled="isView || disabled"
|
|
311
327
|
:rules="rules"
|
|
@@ -317,7 +333,7 @@ export default {
|
|
|
317
333
|
v-else
|
|
318
334
|
ref="value"
|
|
319
335
|
v-model="row.value"
|
|
320
|
-
:data-testid="
|
|
336
|
+
:data-testid="`${componentTestid}-input-${idx}`"
|
|
321
337
|
:placeholder="valuePlaceholder"
|
|
322
338
|
:disabled="isView || disabled"
|
|
323
339
|
:aria-label="a11yLabel ? a11yLabel : undefined"
|
|
@@ -340,7 +356,7 @@ export default {
|
|
|
340
356
|
type="button"
|
|
341
357
|
:disabled="isView"
|
|
342
358
|
class="btn role-link"
|
|
343
|
-
:data-testid="
|
|
359
|
+
:data-testid="`${componentTestid}-remove-item-${idx}`"
|
|
344
360
|
:aria-label="`${_removeLabel} ${idx + 1}`"
|
|
345
361
|
role="button"
|
|
346
362
|
@click="remove(row, idx)"
|
|
@@ -374,7 +390,7 @@ export default {
|
|
|
374
390
|
type="button"
|
|
375
391
|
class="btn role-tertiary add"
|
|
376
392
|
:disabled="loading || disableAdd"
|
|
377
|
-
data-testid="
|
|
393
|
+
:data-testid="`${componentTestid}-button`"
|
|
378
394
|
:aria-label="_addLabel"
|
|
379
395
|
role="button"
|
|
380
396
|
@click="add()"
|
|
@@ -42,7 +42,8 @@ export default ({
|
|
|
42
42
|
themeVars: {
|
|
43
43
|
bannerBgColor: getComputedStyle(document.body).getPropertyValue('--default'),
|
|
44
44
|
bannerTextColor: getComputedStyle(document.body).getPropertyValue('--banner-text-color')
|
|
45
|
-
}
|
|
45
|
+
},
|
|
46
|
+
bannerTitleId: `describe-banners-${ this.bannerType }-id`
|
|
46
47
|
};
|
|
47
48
|
},
|
|
48
49
|
|
|
@@ -107,12 +108,19 @@ export default ({
|
|
|
107
108
|
<div class="row mb-20">
|
|
108
109
|
<div class="col span-12">
|
|
109
110
|
<div class="row">
|
|
111
|
+
<p
|
|
112
|
+
:id="bannerTitleId"
|
|
113
|
+
class="sr-only"
|
|
114
|
+
>
|
|
115
|
+
{{ t(`banner.${bannerType}`) }}
|
|
116
|
+
</p>
|
|
110
117
|
<div class="col span-6">
|
|
111
118
|
<LabeledInput
|
|
112
119
|
v-model:value="value[bannerType].text"
|
|
113
120
|
:disabled="isUiDisabled"
|
|
114
121
|
:label="t('banner.text')"
|
|
115
122
|
type="multiline"
|
|
123
|
+
:aria-describedby="bannerTitleId"
|
|
116
124
|
/>
|
|
117
125
|
<p
|
|
118
126
|
v-if="isConsentBanner"
|
|
@@ -131,11 +139,13 @@ export default ({
|
|
|
131
139
|
:mode="mode"
|
|
132
140
|
:label="t('banner.showAsDialog.label')"
|
|
133
141
|
:tooltip="t('banner.showAsDialog.tooltip')"
|
|
142
|
+
:aria-describedby="bannerTitleId"
|
|
134
143
|
/>
|
|
135
144
|
<LabeledInput
|
|
136
145
|
v-model:value="buttonText"
|
|
137
146
|
:disabled="!showAsDialog || isUiDisabled"
|
|
138
147
|
:label="t('banner.buttonText')"
|
|
148
|
+
:aria-describedby="bannerTitleId"
|
|
139
149
|
/>
|
|
140
150
|
</div>
|
|
141
151
|
</div>
|
|
@@ -148,10 +158,11 @@ export default ({
|
|
|
148
158
|
:options="radioOptions.options"
|
|
149
159
|
:labels="radioOptions.labels"
|
|
150
160
|
:mode="mode"
|
|
161
|
+
:aria-label="`${t(`banner.${bannerType}`)} ${t('banner.bannerAlignment.label')}`"
|
|
151
162
|
/>
|
|
152
163
|
</div>
|
|
153
164
|
<div class="col span-2">
|
|
154
|
-
<h3>
|
|
165
|
+
<h3 id="decoration-banner-title-id">
|
|
155
166
|
{{ t('banner.bannerDecoration.label') }}
|
|
156
167
|
</h3>
|
|
157
168
|
<div
|
|
@@ -165,6 +176,7 @@ export default ({
|
|
|
165
176
|
class="banner-decoration-checkbox"
|
|
166
177
|
:mode="mode"
|
|
167
178
|
:label="o.label"
|
|
179
|
+
:aria-describedby="`${bannerTitleId} decoration-banner-title-id`"
|
|
168
180
|
/>
|
|
169
181
|
</div>
|
|
170
182
|
</div>
|
|
@@ -175,6 +187,7 @@ export default ({
|
|
|
175
187
|
:disabled="isUiDisabled"
|
|
176
188
|
:label="t('banner.bannerFontSize.label')"
|
|
177
189
|
:options="uiBannerFontSizeOptions"
|
|
190
|
+
:aria-describedby="bannerTitleId"
|
|
178
191
|
/>
|
|
179
192
|
</div>
|
|
180
193
|
</div>
|
|
@@ -185,6 +198,7 @@ export default ({
|
|
|
185
198
|
:default-value="themeVars.bannerTextColor"
|
|
186
199
|
:label="t('banner.textColor')"
|
|
187
200
|
:mode="mode"
|
|
201
|
+
:aria-label="`${t(`banner.${bannerType}`)} ${t('banner.textColor')}`"
|
|
188
202
|
/>
|
|
189
203
|
</div>
|
|
190
204
|
<div class="col span-6">
|
|
@@ -193,6 +207,7 @@ export default ({
|
|
|
193
207
|
:default-value="themeVars.bannerBgColor"
|
|
194
208
|
:label="t('banner.background')"
|
|
195
209
|
:mode="mode"
|
|
210
|
+
:aria-label="`${t(`banner.${bannerType}`)} ${t('banner.background')}`"
|
|
196
211
|
/>
|
|
197
212
|
</div>
|
|
198
213
|
</div>
|
|
@@ -4,6 +4,8 @@ import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
|
4
4
|
export default {
|
|
5
5
|
emits: ['update:value'],
|
|
6
6
|
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
|
|
7
9
|
props: {
|
|
8
10
|
value: {
|
|
9
11
|
type: String,
|
|
@@ -67,6 +69,23 @@ export default {
|
|
|
67
69
|
const disabled = this.disabled;
|
|
68
70
|
|
|
69
71
|
return this.mode !== this.editMode || disabled;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
ariaLabel() {
|
|
75
|
+
// We allow override with $attrs['aria-label'] for more control
|
|
76
|
+
if (this.$attrs['aria-label']) {
|
|
77
|
+
return this.$attrs['aria-label'];
|
|
78
|
+
} else if (this.labelKey) {
|
|
79
|
+
return this.t(this.labelKey);
|
|
80
|
+
} else if (this.label) {
|
|
81
|
+
return this.label;
|
|
82
|
+
} else {
|
|
83
|
+
return this.t('generic.colorPicker');
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
ariaDescribedBy() {
|
|
88
|
+
return this.$attrs['aria-describedby'] || undefined;
|
|
70
89
|
}
|
|
71
90
|
},
|
|
72
91
|
|
|
@@ -100,11 +119,20 @@ export default {
|
|
|
100
119
|
@keydown.space.prevent
|
|
101
120
|
@keyup.enter.space.stop="handleKeyup($event)"
|
|
102
121
|
>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
<!-- let make "label" not to be picked up by screen readers -->
|
|
123
|
+
<!-- because it's already included in aria-label (sr's announced it twice) -->
|
|
124
|
+
<label
|
|
125
|
+
v-if="labelKey || label"
|
|
126
|
+
class="text-label"
|
|
127
|
+
aria-hidden="true"
|
|
128
|
+
>
|
|
129
|
+
<t
|
|
130
|
+
v-if="labelKey"
|
|
131
|
+
:k="labelKey"
|
|
132
|
+
:raw="true"
|
|
133
|
+
/>
|
|
134
|
+
<template v-else-if="label">{{ label }}</template>
|
|
135
|
+
</label>
|
|
108
136
|
<div
|
|
109
137
|
:data-testid="componentTestid + '-color-input_preview-container'"
|
|
110
138
|
class="preview-container"
|
|
@@ -117,7 +145,8 @@ export default {
|
|
|
117
145
|
<input
|
|
118
146
|
ref="input"
|
|
119
147
|
:aria-disabled="isDisabled ? 'true' : 'false'"
|
|
120
|
-
:aria-label="
|
|
148
|
+
:aria-label="ariaLabel"
|
|
149
|
+
:aria-describedby="ariaDescribedBy"
|
|
121
150
|
type="color"
|
|
122
151
|
:disabled="isDisabled"
|
|
123
152
|
tabindex="-1"
|