@rancher/shell 3.0.3 → 3.0.5-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/styles/base/_basic.scss +6 -0
- package/assets/styles/global/_button.scss +1 -0
- package/assets/translations/en-us.yaml +38 -3
- package/cloud-credential/aws.vue +2 -0
- package/components/AssignTo.vue +25 -11
- package/components/AsyncButton.vue +24 -7
- package/components/BannerGraphic.vue +1 -0
- package/components/CommunityLinks.vue +3 -3
- package/components/CopyToClipboardText.vue +2 -1
- package/components/DetailText.vue +5 -0
- package/components/DisableAuthProviderModal.vue +1 -0
- package/components/ExplorerMembers.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +56 -14
- package/components/LandingPagePreference.vue +5 -3
- package/components/LocaleSelector.vue +38 -94
- package/components/ModalWithCard.vue +1 -0
- package/components/MoveModal.vue +1 -0
- package/components/PromptRemove.vue +2 -1
- package/components/PromptRestore.vue +1 -0
- package/components/ResourceCancelModal.vue +1 -0
- package/components/SortableTable/index.vue +35 -10
- package/components/StatusBadge.vue +10 -4
- package/components/__tests__/AsyncButton.test.ts +2 -2
- package/components/auth/Principal.vue +9 -3
- package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
- package/components/form/ArrayList.vue +75 -54
- package/components/form/Command.vue +6 -15
- package/components/form/EnvVars.vue +15 -8
- package/components/form/HealthCheck.vue +3 -3
- package/components/form/HookOption.vue +11 -16
- package/components/form/KeyValue.vue +1 -1
- package/components/form/LabeledSelect.vue +2 -1
- package/components/form/LifecycleHooks.vue +3 -3
- package/components/form/MatchExpressions.vue +10 -7
- package/components/form/NameNsDescription.vue +123 -103
- package/components/form/Networking.vue +20 -12
- package/components/form/NodeAffinity.vue +31 -23
- package/components/form/NodeScheduling.vue +13 -3
- package/components/form/PodAffinity.vue +43 -43
- package/components/form/Probe.vue +67 -66
- package/components/form/ResourceQuota/Project.vue +5 -1
- package/components/form/ResourceSelector.vue +7 -9
- package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +6 -3
- package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +12 -1
- package/components/form/SSHKnownHosts/index.vue +16 -2
- package/components/form/Security.vue +54 -56
- package/components/form/Select.vue +31 -6
- 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 +3 -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 +11 -0
- 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 +15 -1
- package/components/nav/Header.vue +1 -0
- package/components/nav/Type.vue +12 -1
- package/components/templates/blank.vue +4 -1
- package/components/templates/default.vue +2 -0
- package/components/templates/home.vue +4 -1
- package/components/templates/plain.vue +4 -1
- package/composables/useRuntimeFlag.ts +29 -0
- package/config/router/routes.js +20 -13
- package/core/types.ts +5 -0
- package/dialog/AddCustomBadgeDialog.vue +1 -0
- package/dialog/DeactivateDriverDialog.vue +5 -4
- package/dialog/ForceMachineRemoveDialog.vue +4 -1
- package/dialog/GitRepoForceUpdateDialog.vue +1 -1
- 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 +1 -0
- package/edit/auth/googleoauth.vue +4 -0
- package/edit/auth/oidc.vue +37 -4
- package/edit/cloudcredential.vue +1 -0
- package/edit/fleet.cattle.io.gitrepo.vue +1 -0
- package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
- 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/SelectCredential.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +25 -34
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +29 -1
- 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/list/management.cattle.io.feature.vue +1 -0
- package/list/provisioning.cattle.io.cluster.vue +20 -12
- package/machine-config/__tests__/vmwarevsphere.test.ts +48 -3
- package/machine-config/vmwarevsphere.vue +16 -0
- package/models/__tests__/namespace.test.ts +25 -1
- package/models/cloudcredential.js +5 -0
- package/models/kontainerdriver.js +6 -3
- package/models/management.cattle.io.node.js +3 -3
- package/models/namespace.js +4 -5
- package/models/nodedriver.js +6 -3
- package/models/workload.js +4 -1
- package/package.json +4 -4
- package/pages/about.vue +16 -8
- package/pages/account/index.vue +4 -1
- package/pages/auth/login.vue +11 -3
- package/pages/auth/logout.vue +4 -1
- package/pages/auth/setup.vue +1 -0
- package/pages/auth/verify.vue +4 -1
- package/pages/c/_cluster/apps/charts/chart.vue +1 -1
- package/pages/diagnostic.vue +47 -2
- package/pages/fail-whale.vue +6 -3
- package/pages/home.vue +24 -18
- package/pages/support/index.vue +4 -1
- package/promptRemove/management.cattle.io.fleetworkspace.vue +1 -1
- package/promptRemove/management.cattle.io.globalrole.vue +1 -1
- package/promptRemove/management.cattle.io.project.vue +2 -2
- package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
- package/promptRemove/pod.vue +1 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +25 -23
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +3 -3
- package/rancher-components/RcDropdown/RcDropdown.vue +6 -5
- package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -2
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +12 -2
- package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
- package/scripts/extension/publish +1 -0
- package/server/har-file.js +25 -3
- package/store/features.js +2 -1
- package/store/type-map.js +4 -0
- package/types/shell/index.d.ts +9 -2
- package/utils/__tests__/string.test.ts +2 -2
- package/utils/cluster.js +35 -0
- package/utils/string.js +1 -3
- package/utils/validators/machine-pool.ts +20 -0
- package/components/formatter/ExtensionCache.vue +0 -74
- package/components/formatter/Port.vue +0 -24
- package/components/formatter/SecretType.vue +0 -41
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { mapGetters } from 'vuex';
|
|
3
3
|
import Select from '@shell/components/form/Select.vue';
|
|
4
|
+
import { RcDropdown, RcDropdownTrigger, RcDropdownItem } from '@components/RcDropdown';
|
|
4
5
|
|
|
5
6
|
export default {
|
|
6
7
|
name: 'LocalSelector',
|
|
7
8
|
|
|
8
|
-
components: {
|
|
9
|
+
components: {
|
|
10
|
+
Select,
|
|
11
|
+
RcDropdown,
|
|
12
|
+
RcDropdownItem,
|
|
13
|
+
RcDropdownTrigger,
|
|
14
|
+
},
|
|
9
15
|
|
|
10
16
|
props: {
|
|
11
17
|
mode: {
|
|
@@ -65,71 +71,37 @@ export default {
|
|
|
65
71
|
<template>
|
|
66
72
|
<div>
|
|
67
73
|
<div v-if="mode === 'login'">
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@click="openLocaleSelector"
|
|
75
|
-
@blur.capture="closeLocaleSelector"
|
|
76
|
-
@keyup.enter="openLocaleSelector"
|
|
77
|
-
@keyup.space="openLocaleSelector"
|
|
78
|
-
>
|
|
79
|
-
<v-dropdown
|
|
80
|
-
popperClass="localeSelector"
|
|
81
|
-
:shown="isLocaleSelectorOpen"
|
|
82
|
-
placement="top"
|
|
83
|
-
distance="8"
|
|
84
|
-
skidding="12"
|
|
85
|
-
:triggers="[]"
|
|
86
|
-
:autoHide="false"
|
|
87
|
-
:flip="false"
|
|
88
|
-
:container="false"
|
|
89
|
-
@focus.capture="openLocaleSelector"
|
|
74
|
+
<rc-dropdown v-if="showLocale">
|
|
75
|
+
<rc-dropdown-trigger
|
|
76
|
+
data-testid="locale-selector"
|
|
77
|
+
link
|
|
78
|
+
class="baseline"
|
|
79
|
+
:aria-label="t('locale.menu')"
|
|
90
80
|
>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
81
|
+
{{ selectedLocaleLabel }}
|
|
82
|
+
<template
|
|
83
|
+
v-if="showIcon"
|
|
84
|
+
#after
|
|
94
85
|
>
|
|
95
|
-
|
|
96
|
-
<i
|
|
97
|
-
v-if="showIcon"
|
|
98
|
-
class="icon icon-fw icon-sort-down"
|
|
99
|
-
/>
|
|
100
|
-
</a>
|
|
101
|
-
<template #popper>
|
|
102
|
-
<ul
|
|
103
|
-
class="list-unstyled dropdown"
|
|
104
|
-
style="margin: -1px;"
|
|
105
|
-
>
|
|
106
|
-
<li
|
|
107
|
-
v-if="showNone"
|
|
108
|
-
v-t="'locale.none'"
|
|
109
|
-
class="hand"
|
|
110
|
-
tabindex="0"
|
|
111
|
-
role="menuitem"
|
|
112
|
-
@click.stop="switchLocale('none')"
|
|
113
|
-
@keyup.enter.stop="switchLocale('none')"
|
|
114
|
-
@keyup.space.stop="switchLocale('none')"
|
|
115
|
-
/>
|
|
116
|
-
<li
|
|
117
|
-
v-for="(label, name) in availableLocales"
|
|
118
|
-
:key="name"
|
|
119
|
-
tabindex="0"
|
|
120
|
-
role="menuitem"
|
|
121
|
-
class="hand"
|
|
122
|
-
:lang="name"
|
|
123
|
-
@click.stop="switchLocale(name)"
|
|
124
|
-
@keyup.enter.stop="switchLocale(name)"
|
|
125
|
-
@keyup.space.stop="switchLocale(name)"
|
|
126
|
-
>
|
|
127
|
-
{{ label }}
|
|
128
|
-
</li>
|
|
129
|
-
</ul>
|
|
86
|
+
<i class="icon icon-fw icon-sort-down" />
|
|
130
87
|
</template>
|
|
131
|
-
</
|
|
132
|
-
|
|
88
|
+
</rc-dropdown-trigger>
|
|
89
|
+
<template #dropdownCollection>
|
|
90
|
+
<rc-dropdown-item
|
|
91
|
+
v-if="showNone"
|
|
92
|
+
v-t="'locale.none'"
|
|
93
|
+
@click="switchLocale('none')"
|
|
94
|
+
/>
|
|
95
|
+
<rc-dropdown-item
|
|
96
|
+
v-for="(label, name) in availableLocales"
|
|
97
|
+
:key="name"
|
|
98
|
+
:lang="name"
|
|
99
|
+
@click.stop="switchLocale(name)"
|
|
100
|
+
>
|
|
101
|
+
{{ label }}
|
|
102
|
+
</rc-dropdown-item>
|
|
103
|
+
</template>
|
|
104
|
+
</rc-dropdown>
|
|
133
105
|
</div>
|
|
134
106
|
<div v-else>
|
|
135
107
|
<Select
|
|
@@ -142,36 +114,8 @@ export default {
|
|
|
142
114
|
</div>
|
|
143
115
|
</template>
|
|
144
116
|
|
|
145
|
-
<style lang="scss"
|
|
146
|
-
.
|
|
147
|
-
|
|
148
|
-
padding: 0 5px;
|
|
149
|
-
line-height: 40px;
|
|
150
|
-
font-size: 15px;
|
|
151
|
-
font-weight: 500;
|
|
152
|
-
}
|
|
153
|
-
.content {
|
|
154
|
-
background: var(--nav-active);
|
|
155
|
-
padding: 10px;
|
|
156
|
-
margin-top: 6px;
|
|
157
|
-
border-radius: 4px;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.hand:focus-visible {
|
|
161
|
-
@include focus-outline;
|
|
162
|
-
outline-offset: 4px;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.locale-chooser {
|
|
166
|
-
cursor: pointer;
|
|
167
|
-
|
|
168
|
-
&:hover {
|
|
169
|
-
text-decoration: none;
|
|
117
|
+
<style lang="scss">
|
|
118
|
+
.baseline {
|
|
119
|
+
align-items: baseline;
|
|
170
120
|
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.locale-login-container:focus-visible {
|
|
174
|
-
@include focus-outline;
|
|
175
|
-
outline-offset: 2px;
|
|
176
|
-
}
|
|
177
121
|
</style>
|
package/components/MoveModal.vue
CHANGED
|
@@ -339,6 +339,7 @@ export default {
|
|
|
339
339
|
:width="400"
|
|
340
340
|
height="auto"
|
|
341
341
|
styles="max-height: 100vh;"
|
|
342
|
+
:trigger-focus-trap="true"
|
|
342
343
|
@close="close"
|
|
343
344
|
>
|
|
344
345
|
<Card
|
|
@@ -354,7 +355,7 @@ export default {
|
|
|
354
355
|
<div class="mb-10">
|
|
355
356
|
<template v-if="!hasCustomRemove">
|
|
356
357
|
{{ t('promptRemove.attemptingToRemove', { type }) }} <span
|
|
357
|
-
v-clean-html="resourceNames(names, t)"
|
|
358
|
+
v-clean-html="resourceNames(names, null, t)"
|
|
358
359
|
/>
|
|
359
360
|
</template>
|
|
360
361
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { mapGetters } from 'vuex';
|
|
3
|
-
import { defineAsyncComponent,
|
|
2
|
+
import { mapGetters, useStore } from 'vuex';
|
|
3
|
+
import { defineAsyncComponent, ref, onMounted, onBeforeUnmount } from 'vue';
|
|
4
4
|
import day from 'dayjs';
|
|
5
5
|
import isEmpty from 'lodash/isEmpty';
|
|
6
6
|
import { dasherize, ucFirst } from '@shell/utils/string';
|
|
@@ -24,6 +24,7 @@ import { getParent } from '@shell/utils/dom';
|
|
|
24
24
|
import { FORMATTERS } from '@shell/components/SortableTable/sortable-config';
|
|
25
25
|
import ButtonMultiAction from '@shell/components/ButtonMultiAction.vue';
|
|
26
26
|
import ActionMenu from '@shell/components/ActionMenuShell.vue';
|
|
27
|
+
import { useRuntimeFlag } from '@shell/composables/useRuntimeFlag';
|
|
27
28
|
|
|
28
29
|
// Uncomment for table performance debugging
|
|
29
30
|
// import tableDebug from './debug';
|
|
@@ -528,7 +529,7 @@ export default {
|
|
|
528
529
|
},
|
|
529
530
|
},
|
|
530
531
|
setup(_props, { emit }) {
|
|
531
|
-
const table =
|
|
532
|
+
const table = ref(null);
|
|
532
533
|
|
|
533
534
|
const handleEnterKey = (event) => {
|
|
534
535
|
if (event.key === 'Enter' && !event.target?.classList?.contains('checkbox-custom')) {
|
|
@@ -543,6 +544,14 @@ export default {
|
|
|
543
544
|
onBeforeUnmount(() => {
|
|
544
545
|
table.value.removeEventListener('keyup', handleEnterKey);
|
|
545
546
|
});
|
|
547
|
+
|
|
548
|
+
const store = useStore();
|
|
549
|
+
const { featureDropdownMenu } = useRuntimeFlag(store);
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
table,
|
|
553
|
+
featureDropdownMenu,
|
|
554
|
+
};
|
|
546
555
|
},
|
|
547
556
|
|
|
548
557
|
created() {
|
|
@@ -763,7 +772,7 @@ export default {
|
|
|
763
772
|
});
|
|
764
773
|
|
|
765
774
|
return rows;
|
|
766
|
-
}
|
|
775
|
+
},
|
|
767
776
|
},
|
|
768
777
|
|
|
769
778
|
methods: {
|
|
@@ -1447,9 +1456,9 @@ export default {
|
|
|
1447
1456
|
:value="col.value"
|
|
1448
1457
|
:row="row.row"
|
|
1449
1458
|
:col="col.col"
|
|
1459
|
+
:get-custom-detail-link="getCustomDetailLink"
|
|
1450
1460
|
v-bind="col.col.formatterOpts"
|
|
1451
1461
|
:row-key="row.key"
|
|
1452
|
-
:get-custom-detail-link="getCustomDetailLink"
|
|
1453
1462
|
/>
|
|
1454
1463
|
<component
|
|
1455
1464
|
:is="col.component"
|
|
@@ -1487,11 +1496,27 @@ export default {
|
|
|
1487
1496
|
:row="row.row"
|
|
1488
1497
|
:index="i"
|
|
1489
1498
|
>
|
|
1490
|
-
<
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1499
|
+
<template v-if="featureDropdownMenu">
|
|
1500
|
+
<ActionMenu
|
|
1501
|
+
:resource="row.row"
|
|
1502
|
+
:data-testid="componentTestid + '-' + i + '-action-button'"
|
|
1503
|
+
:button-aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
|
|
1504
|
+
/>
|
|
1505
|
+
</template>
|
|
1506
|
+
<template v-else>
|
|
1507
|
+
<ButtonMultiAction
|
|
1508
|
+
:id="`actionButton+${i}+${(row.row && row.row.name) ? row.row.name : ''}`"
|
|
1509
|
+
:ref="`actionButton${i}`"
|
|
1510
|
+
aria-haspopup="true"
|
|
1511
|
+
aria-expanded="false"
|
|
1512
|
+
:aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
|
|
1513
|
+
:data-testid="componentTestid + '-' + i + '-action-button'"
|
|
1514
|
+
:borderless="true"
|
|
1515
|
+
@click="handleActionButtonClick(i, $event)"
|
|
1516
|
+
@keyup.enter="handleActionButtonClick(i, $event)"
|
|
1517
|
+
@keyup.space="handleActionButtonClick(i, $event)"
|
|
1518
|
+
/>
|
|
1519
|
+
</template>
|
|
1495
1520
|
</slot>
|
|
1496
1521
|
</td>
|
|
1497
1522
|
</tr>
|
|
@@ -19,10 +19,16 @@ const STATUS = {
|
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
withDefaults(
|
|
23
|
+
defineProps<{
|
|
24
|
+
status?: 'success' | 'warning' | 'info' | 'error',
|
|
25
|
+
label?: string
|
|
26
|
+
}>(),
|
|
27
|
+
{
|
|
28
|
+
status: 'success',
|
|
29
|
+
label: '',
|
|
30
|
+
}
|
|
31
|
+
);
|
|
26
32
|
|
|
27
33
|
</script>
|
|
28
34
|
<template>
|
|
@@ -42,7 +42,7 @@ describe('component: AsyncButton', () => {
|
|
|
42
42
|
expect(span.text()).toBe('some-string');
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
it('click on async button should emit click with a proper state of waiting, disabled and spinning ::: CB true', () => {
|
|
45
|
+
it('click on async button should emit click with a proper state of waiting, appear disabled and spinning ::: CB true', () => {
|
|
46
46
|
jest.useFakeTimers();
|
|
47
47
|
|
|
48
48
|
const wrapper: VueWrapper<InstanceType<typeof AsyncButton>> = mount(AsyncButton, {
|
|
@@ -65,7 +65,7 @@ describe('component: AsyncButton', () => {
|
|
|
65
65
|
expect(wrapper.emitted('click')).toHaveLength(1);
|
|
66
66
|
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.WAITING);
|
|
67
67
|
expect(wrapper.vm.isSpinning).toBe(true);
|
|
68
|
-
expect(wrapper.vm.
|
|
68
|
+
expect(wrapper.vm.appearsDisabled).toBe(true);
|
|
69
69
|
// testing cb function has been emitted
|
|
70
70
|
expect(typeof wrapper.emitted('click')![0][0]).toBe('function');
|
|
71
71
|
|
|
@@ -98,9 +98,9 @@ export default {
|
|
|
98
98
|
>
|
|
99
99
|
<table>
|
|
100
100
|
<tbody>
|
|
101
|
-
<tr><
|
|
102
|
-
<tr><
|
|
103
|
-
<tr><
|
|
101
|
+
<tr><th>{{ t('principal.name') }}: </th><td>{{ principal.name || principal.loginName }}</td></tr>
|
|
102
|
+
<tr><th>{{ t('principal.loginName') }}: </th><td>{{ principal.loginName }}</td></tr>
|
|
103
|
+
<tr><th>{{ t('principal.type') }}: </th><td>{{ principal.displayType }}</td></tr>
|
|
104
104
|
</tbody>
|
|
105
105
|
</table>
|
|
106
106
|
</div>
|
|
@@ -164,6 +164,12 @@ export default {
|
|
|
164
164
|
grid-template-rows: auto math.div($size, 2);
|
|
165
165
|
column-gap: 10px;
|
|
166
166
|
|
|
167
|
+
th {
|
|
168
|
+
text-align: left;
|
|
169
|
+
font-weight: normal;
|
|
170
|
+
padding-right: 10px;
|
|
171
|
+
}
|
|
172
|
+
|
|
167
173
|
&.showLabels {
|
|
168
174
|
grid-template-areas:
|
|
169
175
|
"avatar name";
|
|
@@ -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,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';
|
|
@@ -94,22 +95,83 @@ export default {
|
|
|
94
95
|
// we only want functions in the rules array
|
|
95
96
|
validator: (rules) => rules.every((rule) => ['function'].includes(typeof rule))
|
|
96
97
|
},
|
|
98
|
+
a11yLabel: {
|
|
99
|
+
type: String,
|
|
100
|
+
default: '',
|
|
101
|
+
},
|
|
97
102
|
},
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
103
|
+
|
|
104
|
+
setup(props, { emit }) {
|
|
105
|
+
const input = (Array.isArray(props.value) ? props.value : []).slice();
|
|
106
|
+
const rows = ref([]);
|
|
101
107
|
|
|
102
108
|
for ( const value of input ) {
|
|
103
|
-
rows.push({ value });
|
|
109
|
+
rows.value.push({ value });
|
|
104
110
|
}
|
|
105
|
-
if ( !rows.length &&
|
|
106
|
-
const value =
|
|
111
|
+
if ( !rows.value.length && props.initialEmptyRow ) {
|
|
112
|
+
const value = props.defaultAddValue ? clone(props.defaultAddValue) : '';
|
|
107
113
|
|
|
108
|
-
rows.push({ value });
|
|
114
|
+
rows.value.push({ value });
|
|
109
115
|
}
|
|
110
116
|
|
|
111
|
-
|
|
117
|
+
const isView = computed(() => {
|
|
118
|
+
return props.mode === _VIEW;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Cleanup rows and emit input
|
|
123
|
+
*/
|
|
124
|
+
const update = () => {
|
|
125
|
+
if ( isView.value ) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const out = [];
|
|
129
|
+
|
|
130
|
+
for ( const row of rows.value ) {
|
|
131
|
+
const trim = !props.valueMultiline && (typeof row.value === 'string');
|
|
132
|
+
const value = trim ? row.value.trim() : row.value;
|
|
133
|
+
|
|
134
|
+
if ( typeof value !== 'undefined' ) {
|
|
135
|
+
out.push(value);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
emit('update:value', out);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const lastUpdateWasFromValue = ref(false);
|
|
142
|
+
const queueUpdate = debounce(update, 50);
|
|
143
|
+
|
|
144
|
+
watch(
|
|
145
|
+
rows,
|
|
146
|
+
() => {
|
|
147
|
+
// lastUpdateWasFromValue is used to break a cycle where when rows are updated
|
|
148
|
+
// this was called which then forced rows to updated again
|
|
149
|
+
if (!lastUpdateWasFromValue.value) {
|
|
150
|
+
queueUpdate();
|
|
151
|
+
}
|
|
152
|
+
lastUpdateWasFromValue.value = false;
|
|
153
|
+
},
|
|
154
|
+
{ deep: true }
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
watch(
|
|
158
|
+
() => props.value,
|
|
159
|
+
() => {
|
|
160
|
+
lastUpdateWasFromValue.value = true;
|
|
161
|
+
rows.value = (props.value || []).map((v) => ({ value: v }));
|
|
162
|
+
},
|
|
163
|
+
{ deep: true }
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
rows,
|
|
168
|
+
lastUpdateWasFromValue,
|
|
169
|
+
queueUpdate,
|
|
170
|
+
isView,
|
|
171
|
+
update,
|
|
172
|
+
};
|
|
112
173
|
},
|
|
174
|
+
|
|
113
175
|
computed: {
|
|
114
176
|
_addLabel() {
|
|
115
177
|
return this.addLabel || this.t('generic.add');
|
|
@@ -117,10 +179,6 @@ export default {
|
|
|
117
179
|
_removeLabel() {
|
|
118
180
|
return this.removeLabel || this.t('generic.remove');
|
|
119
181
|
},
|
|
120
|
-
|
|
121
|
-
isView() {
|
|
122
|
-
return this.mode === _VIEW;
|
|
123
|
-
},
|
|
124
182
|
showAdd() {
|
|
125
183
|
return this.addAllowed;
|
|
126
184
|
},
|
|
@@ -141,29 +199,7 @@ export default {
|
|
|
141
199
|
return !this.valueMultiline && this.protip;
|
|
142
200
|
}
|
|
143
201
|
},
|
|
144
|
-
watch: {
|
|
145
|
-
value: {
|
|
146
|
-
deep: true,
|
|
147
|
-
handler() {
|
|
148
|
-
this.lastUpdateWasFromValue = true;
|
|
149
|
-
this.rows = (this.value || []).map((v) => ({ value: v }));
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
rows: {
|
|
154
|
-
deep: true,
|
|
155
|
-
handler(newValue, oldValue) {
|
|
156
|
-
// lastUpdateWasFromValue is used to break a cycle where when rows are updated
|
|
157
|
-
// this was called which then forced rows to updated again
|
|
158
|
-
if (!this.lastUpdateWasFromValue) {
|
|
159
|
-
this.queueUpdate();
|
|
160
|
-
}
|
|
161
|
-
this.lastUpdateWasFromValue = false;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
202
|
created() {
|
|
166
|
-
this.queueUpdate = debounce(this.update, 50);
|
|
167
203
|
},
|
|
168
204
|
methods: {
|
|
169
205
|
add() {
|
|
@@ -189,26 +225,6 @@ export default {
|
|
|
189
225
|
this.queueUpdate();
|
|
190
226
|
},
|
|
191
227
|
|
|
192
|
-
/**
|
|
193
|
-
* Cleanup rows and emit input
|
|
194
|
-
*/
|
|
195
|
-
update() {
|
|
196
|
-
if ( this.isView ) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
const out = [];
|
|
200
|
-
|
|
201
|
-
for ( const row of this.rows ) {
|
|
202
|
-
const trim = !this.valueMultiline && (typeof row.value === 'string');
|
|
203
|
-
const value = trim ? row.value.trim() : row.value;
|
|
204
|
-
|
|
205
|
-
if ( typeof value !== 'undefined' ) {
|
|
206
|
-
out.push(value);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
this.$emit('update:value', out);
|
|
210
|
-
},
|
|
211
|
-
|
|
212
228
|
/**
|
|
213
229
|
* Handle paste event, e.g. split multiple lines in rows
|
|
214
230
|
*/
|
|
@@ -316,6 +332,7 @@ export default {
|
|
|
316
332
|
:data-testid="`input-${idx}`"
|
|
317
333
|
:placeholder="valuePlaceholder"
|
|
318
334
|
:disabled="isView || disabled"
|
|
335
|
+
:aria-label="a11yLabel ? a11yLabel : undefined"
|
|
319
336
|
@paste="onPaste(idx, $event)"
|
|
320
337
|
>
|
|
321
338
|
</slot>
|
|
@@ -336,6 +353,8 @@ export default {
|
|
|
336
353
|
:disabled="isView"
|
|
337
354
|
class="btn role-link"
|
|
338
355
|
:data-testid="`remove-item-${idx}`"
|
|
356
|
+
:aria-label="`${_removeLabel} ${idx + 1}`"
|
|
357
|
+
role="button"
|
|
339
358
|
@click="remove(row, idx)"
|
|
340
359
|
>
|
|
341
360
|
{{ _removeLabel }}
|
|
@@ -368,6 +387,8 @@ export default {
|
|
|
368
387
|
class="btn role-tertiary add"
|
|
369
388
|
:disabled="loading || disableAdd"
|
|
370
389
|
data-testid="array-list-button"
|
|
390
|
+
:aria-label="_addLabel"
|
|
391
|
+
role="button"
|
|
371
392
|
@click="add()"
|
|
372
393
|
>
|
|
373
394
|
<i
|
|
@@ -43,23 +43,14 @@ export default {
|
|
|
43
43
|
},
|
|
44
44
|
|
|
45
45
|
data() {
|
|
46
|
-
const {
|
|
47
|
-
command,
|
|
48
|
-
args,
|
|
49
|
-
workingDir,
|
|
50
|
-
stdin = false,
|
|
51
|
-
stdinOnce = false,
|
|
52
|
-
tty = false,
|
|
53
|
-
} = this.value;
|
|
54
|
-
|
|
55
46
|
return {
|
|
56
|
-
args,
|
|
57
|
-
command,
|
|
47
|
+
args: this.value.args,
|
|
48
|
+
command: this.value.command,
|
|
58
49
|
commandOptions: ['No', 'Once', 'Yes'],
|
|
59
|
-
stdin,
|
|
60
|
-
stdinOnce,
|
|
61
|
-
tty,
|
|
62
|
-
workingDir,
|
|
50
|
+
stdin: this.value.stdin || false,
|
|
51
|
+
stdinOnce: this.value.stdin || false,
|
|
52
|
+
tty: this.value.tty || false,
|
|
53
|
+
workingDir: this.value.workingDir,
|
|
63
54
|
};
|
|
64
55
|
},
|
|
65
56
|
|
|
@@ -39,14 +39,10 @@ export default {
|
|
|
39
39
|
},
|
|
40
40
|
|
|
41
41
|
data() {
|
|
42
|
-
const { env = [], envFrom = [] } = this.value;
|
|
43
|
-
|
|
44
|
-
const allEnv = [...env, ...envFrom].map((row) => {
|
|
45
|
-
return { value: row, id: randomStr(4) };
|
|
46
|
-
});
|
|
47
|
-
|
|
48
42
|
return {
|
|
49
|
-
env,
|
|
43
|
+
env: [],
|
|
44
|
+
envFrom: [],
|
|
45
|
+
allEnv: [],
|
|
50
46
|
};
|
|
51
47
|
},
|
|
52
48
|
|
|
@@ -63,7 +59,18 @@ export default {
|
|
|
63
59
|
}
|
|
64
60
|
}
|
|
65
61
|
},
|
|
62
|
+
|
|
66
63
|
created() {
|
|
64
|
+
const { env = [], envFrom = [] } = this.value;
|
|
65
|
+
|
|
66
|
+
const allEnv = [...env, ...envFrom].map((row) => {
|
|
67
|
+
return { value: row, id: randomStr(4) };
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
this.env = env;
|
|
71
|
+
this.envFrom = envFrom;
|
|
72
|
+
this.allEnv = allEnv;
|
|
73
|
+
|
|
67
74
|
this.queueUpdate = debounce(this.update, 500);
|
|
68
75
|
},
|
|
69
76
|
|
|
@@ -107,7 +114,7 @@ export default {
|
|
|
107
114
|
<div :style="{'width':'100%'}">
|
|
108
115
|
<div
|
|
109
116
|
v-for="(row, i) in allEnv"
|
|
110
|
-
:key="
|
|
117
|
+
:key="row.id"
|
|
111
118
|
>
|
|
112
119
|
<ValueFromResource
|
|
113
120
|
v-model:value="row.value"
|
|
@@ -18,10 +18,10 @@ export default {
|
|
|
18
18
|
},
|
|
19
19
|
|
|
20
20
|
data() {
|
|
21
|
-
const { readinessProbe, livenessProbe, startupProbe } = this.value;
|
|
22
|
-
|
|
23
21
|
return {
|
|
24
|
-
readinessProbe
|
|
22
|
+
readinessProbe: this.value.readinessProbe,
|
|
23
|
+
livenessProbe: this.value.livenessProbe,
|
|
24
|
+
startupProbe: this.value.startupProbe,
|
|
25
25
|
};
|
|
26
26
|
},
|
|
27
27
|
|