dashboard-shell-shell 1.0.111 → 1.0.113
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/.DS_Store +0 -0
- package/assets/icons/demo.css +539 -0
- package/assets/icons/demo_index.html +1131 -0
- package/assets/icons/iconfont.css +200 -0
- package/assets/icons/iconfont.js +1 -0
- package/assets/icons/iconfont.json +296 -0
- package/assets/icons/iconfont.ttf +0 -0
- package/assets/icons/iconfont.woff +0 -0
- package/assets/icons/iconfont.woff2 +0 -0
- package/assets/images/API.svg +3 -0
- package/assets/images/login/password.svg +20 -0
- package/assets/images/login/user.svg +6 -0
- package/assets/images/login-bg.png +0 -0
- package/assets/images/login-left.png +0 -0
- package/assets/images/login-logo.svg +19 -0
- package/assets/images/logo.png +0 -0
- package/assets/images/pl/harvester.png +0 -0
- package/assets/images/promp-yellow.svg +5 -0
- package/assets/images/user.png +0 -0
- package/assets/styles/all.scss +63 -0
- package/assets/styles/app.scss +2 -0
- package/assets/styles/base/_basic.scss +8 -2
- package/assets/styles/base/_helpers.scss +4 -0
- package/assets/styles/base/_typography.scss +2 -1
- package/assets/styles/base/_variables.scss +10 -2
- package/assets/styles/global/_button.scss +37 -25
- package/assets/styles/global/_columns.scss +3 -1
- package/assets/styles/global/_form.scss +45 -13
- package/assets/styles/global/_labeled-input.scss +50 -25
- package/assets/styles/global/_layout.scss +9 -3
- package/assets/styles/global/_select.scss +20 -13
- package/assets/styles/global/_table.scss +1 -1
- package/assets/styles/global/_tooltip.scss +47 -6
- package/assets/styles/themes/_dark.scss +1 -0
- package/assets/styles/themes/_light.scss +59 -46
- package/assets/styles/themes/_suse.scss +1 -0
- package/assets/styles/vendor/vue-select.scss +18 -7
- package/assets/translations/en-us.yaml +93 -12
- package/assets/translations/zh-hans.yaml +278 -141
- package/components/ActionDropdown.vue +1 -1
- package/components/ActionDropdownShell.vue +71 -0
- package/components/ActionMenu.vue +2 -2
- package/components/ActionMenuShell.vue +1 -0
- package/components/AppModal.vue +78 -6
- package/components/AssignTo.vue +25 -11
- package/components/AsyncButton.vue +24 -7
- package/components/BannerGraphic.vue +1 -0
- package/components/ButtonDropdown.vue +26 -4
- package/components/ButtonGroup.vue +4 -0
- package/components/ButtonMultiAction.vue +1 -0
- package/components/CommunityLinks.vue +3 -3
- package/components/ConsumptionGauge.vue +24 -5
- package/components/CopyToClipboardText.vue +2 -1
- package/components/CruResource.vue +12 -7
- package/components/CruResourceFooter.vue +2 -2
- package/components/DashboardOptions.vue +21 -15
- package/components/DetailText.vue +5 -0
- package/components/DisableAuthProviderModal.vue +1 -0
- package/components/DotState.vue +84 -0
- package/components/ExplorerMembers.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +56 -14
- package/components/FixedBanner.vue +19 -12
- package/components/GlobalRoleBindings.vue +5 -1
- package/components/GrafanaDashboard.vue +4 -4
- package/components/GrowlManager.vue +4 -1
- package/components/HardwareResourceGauge.vue +39 -3
- package/components/InfoBox.vue +3 -3
- package/components/InputOrDisplay.vue +28 -2
- package/components/LabelValue.vue +16 -1
- package/components/LandingPagePreference.vue +5 -3
- package/components/LocaleSelector.vue +39 -93
- package/components/ModalManager.vue +55 -0
- package/components/ModalWithCard.vue +2 -0
- package/components/MoveModal.vue +1 -0
- package/components/PromptChangePassword.vue +1 -1
- package/components/PromptModal.vue +15 -2
- package/components/PromptRemove.vue +28 -8
- package/components/PromptRestore.vue +1 -0
- package/components/ResourceCancelModal.vue +1 -0
- package/components/ResourceDetail/Masthead.vue +188 -43
- package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
- package/components/ResourceDetail/index.vue +49 -14
- package/components/ResourceList/Masthead.vue +80 -18
- package/components/ResourceTable.vue +60 -19
- package/components/SideNav.vue +32 -12
- package/components/SlideInPanelManager.vue +126 -0
- package/components/SortableTable/THead.vue +34 -5
- package/components/SortableTable/actions.js +1 -1
- package/components/SortableTable/index.vue +649 -142
- package/components/SortableTable/paging.js +36 -28
- package/components/SortableTable/selection.js +0 -11
- package/components/StatusBadge.vue +77 -0
- package/components/Tabbed/Tab.vue +3 -3
- package/components/Tabbed/index.vue +44 -26
- package/components/Wizard.vue +2 -2
- package/components/__tests__/AsyncButton.test.ts +2 -2
- package/components/__tests__/FixedBanner.test.ts +3 -3
- package/components/__tests__/ModalManager.spec.ts +176 -0
- package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
- package/components/auth/Principal.vue +10 -3
- package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
- package/components/form/ArrayList.vue +123 -85
- package/components/form/ArrayListGrouped.vue +10 -2
- package/components/form/Command.vue +6 -15
- package/components/form/EnvVars.vue +16 -8
- package/components/form/Footer.vue +8 -5
- package/components/form/HealthCheck.vue +3 -3
- package/components/form/HookOption.vue +11 -16
- package/components/form/KeyValue.vue +16 -7
- package/components/form/LabeledSelect.vue +59 -76
- package/components/form/LifecycleHooks.vue +3 -3
- package/components/form/MatchExpressions.vue +35 -12
- package/components/form/NameNsDescription.vue +147 -115
- package/components/form/Networking.vue +20 -12
- package/components/form/NodeAffinity.vue +31 -23
- package/components/form/NodeScheduling.vue +13 -3
- package/components/form/Password.vue +11 -5
- package/components/form/PodAffinity.vue +43 -44
- 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/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 +41 -7
- package/components/form/ShellInput.vue +5 -1
- package/components/form/Tolerations.vue +5 -1
- package/components/form/UnitInput.vue +2 -2
- 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__/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/form/__tests__/UnitInput.test.ts +4 -5
- package/components/formatter/BadgeStateFormatter.vue +8 -5
- package/components/formatter/InternalExternalIP.vue +2 -0
- package/components/formatter/SecretData.vue +20 -7
- package/components/nav/Favorite.vue +5 -1
- package/components/nav/Group.vue +60 -27
- package/components/nav/Header.vue +39 -13
- package/components/nav/Jump.vue +7 -0
- package/components/nav/NamespaceFilter.vue +14 -8
- package/components/nav/Pinned.vue +1 -1
- package/components/nav/TopLevelMenu.vue +5 -17
- package/components/nav/Type.vue +32 -35
- package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
- package/components/templates/blank.vue +4 -1
- package/components/templates/default.vue +8 -0
- package/components/templates/home.vue +10 -1
- package/components/templates/plain.vue +10 -1
- package/package.json +1 -1
- package/rancher-components/Banner/Banner.vue +6 -4
- package/rancher-components/Card/Card.vue +6 -4
- package/rancher-components/Form/Checkbox/Checkbox.vue +20 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +46 -5
- package/rancher-components/Form/Radio/RadioButton.vue +32 -8
- package/rancher-components/Form/Radio/RadioGroup.vue +31 -24
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +8 -3
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +15 -3
- package/rancher-components/RcButton/RcButton.vue +1 -0
- package/rancher-components/RcButton/types.ts +1 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +54 -15
- package/rancher-components/RcDropdown/RcDropdownItem.vue +5 -4
- package/rancher-components/RcDropdown/RcDropdownMenu.vue +11 -7
- 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/rancher-components/StringList/StringList.vue +1 -1
- package/store/type-map.js +29 -2
- package/utils/error.js +30 -8
- package/utils/errorTranslate.json +916 -0
- package/vue.config.js +1 -1
- package/components/formatter/ExtensionCache.vue +0 -74
- package/components/formatter/Port.vue +0 -24
- package/components/formatter/SecretType.vue +0 -41
|
@@ -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
|
+
});
|
|
@@ -90,6 +90,7 @@ export default {
|
|
|
90
90
|
<img
|
|
91
91
|
:src="principal.avatarSrc"
|
|
92
92
|
:class="{'round': principal.roundAvatar}"
|
|
93
|
+
:alt="t('principal.alt.avatar')"
|
|
93
94
|
>
|
|
94
95
|
</div>
|
|
95
96
|
<div
|
|
@@ -98,9 +99,9 @@ export default {
|
|
|
98
99
|
>
|
|
99
100
|
<table>
|
|
100
101
|
<tbody>
|
|
101
|
-
<tr><
|
|
102
|
-
<tr><
|
|
103
|
-
<tr><
|
|
102
|
+
<tr><th>{{ t('principal.name') }}: </th><td>{{ principal.name || principal.loginName }}</td></tr>
|
|
103
|
+
<tr><th>{{ t('principal.loginName') }}: </th><td>{{ principal.loginName }}</td></tr>
|
|
104
|
+
<tr><th>{{ t('principal.type') }}: </th><td>{{ principal.displayType }}</td></tr>
|
|
104
105
|
</tbody>
|
|
105
106
|
</table>
|
|
106
107
|
</div>
|
|
@@ -164,6 +165,12 @@ export default {
|
|
|
164
165
|
grid-template-rows: auto math.div($size, 2);
|
|
165
166
|
column-gap: 10px;
|
|
166
167
|
|
|
168
|
+
th {
|
|
169
|
+
text-align: left;
|
|
170
|
+
font-weight: normal;
|
|
171
|
+
padding-right: 10px;
|
|
172
|
+
}
|
|
173
|
+
|
|
167
174
|
&.showLabels {
|
|
168
175
|
grid-template-areas:
|
|
169
176
|
"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,87 @@ 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
|
+
},
|
|
102
|
+
componentTestid: {
|
|
103
|
+
type: String,
|
|
104
|
+
default: 'array-list',
|
|
105
|
+
}
|
|
97
106
|
},
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
107
|
+
|
|
108
|
+
setup(props, { emit }) {
|
|
109
|
+
const input = (Array.isArray(props.value) ? props.value : []).slice();
|
|
110
|
+
const rows = ref([]);
|
|
101
111
|
|
|
102
112
|
for ( const value of input ) {
|
|
103
|
-
rows.push({ value });
|
|
113
|
+
rows.value.push({ value });
|
|
104
114
|
}
|
|
105
|
-
if ( !rows.length &&
|
|
106
|
-
const value =
|
|
115
|
+
if ( !rows.value.length && props.initialEmptyRow ) {
|
|
116
|
+
const value = props.defaultAddValue ? clone(props.defaultAddValue) : '';
|
|
107
117
|
|
|
108
|
-
rows.push({ value });
|
|
118
|
+
rows.value.push({ value });
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
|
|
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
|
+
};
|
|
112
177
|
},
|
|
178
|
+
|
|
113
179
|
computed: {
|
|
114
180
|
_addLabel() {
|
|
115
181
|
return this.addLabel || this.t('generic.add');
|
|
@@ -117,10 +183,6 @@ export default {
|
|
|
117
183
|
_removeLabel() {
|
|
118
184
|
return this.removeLabel || this.t('generic.remove');
|
|
119
185
|
},
|
|
120
|
-
|
|
121
|
-
isView() {
|
|
122
|
-
return this.mode === _VIEW;
|
|
123
|
-
},
|
|
124
186
|
showAdd() {
|
|
125
187
|
return this.addAllowed;
|
|
126
188
|
},
|
|
@@ -141,29 +203,7 @@ export default {
|
|
|
141
203
|
return !this.valueMultiline && this.protip;
|
|
142
204
|
}
|
|
143
205
|
},
|
|
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
206
|
created() {
|
|
166
|
-
this.queueUpdate = debounce(this.update, 50);
|
|
167
207
|
},
|
|
168
208
|
methods: {
|
|
169
209
|
add() {
|
|
@@ -189,26 +229,6 @@ export default {
|
|
|
189
229
|
this.queueUpdate();
|
|
190
230
|
},
|
|
191
231
|
|
|
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
232
|
/**
|
|
213
233
|
* Handle paste event, e.g. split multiple lines in rows
|
|
214
234
|
*/
|
|
@@ -254,7 +274,32 @@ export default {
|
|
|
254
274
|
</h3>
|
|
255
275
|
</slot>
|
|
256
276
|
</div>
|
|
257
|
-
|
|
277
|
+
<div
|
|
278
|
+
v-if="showAdd && !isView"
|
|
279
|
+
class="footer"
|
|
280
|
+
>
|
|
281
|
+
<slot
|
|
282
|
+
v-if="showAdd"
|
|
283
|
+
name="add"
|
|
284
|
+
:add="add"
|
|
285
|
+
>
|
|
286
|
+
<button
|
|
287
|
+
type="button"
|
|
288
|
+
class="btn role-primary add"
|
|
289
|
+
:disabled="loading || disableAdd"
|
|
290
|
+
:data-testid="`${componentTestid}-button`"
|
|
291
|
+
:aria-label="_addLabel"
|
|
292
|
+
role="button"
|
|
293
|
+
@click="add()"
|
|
294
|
+
>
|
|
295
|
+
<i
|
|
296
|
+
class="mr-5 icon"
|
|
297
|
+
:class="loading ? ['icon-lg', 'icon-spinner','icon-spin']: [addIcon]"
|
|
298
|
+
/>
|
|
299
|
+
{{ _addLabel }}
|
|
300
|
+
</button>
|
|
301
|
+
</slot>
|
|
302
|
+
</div>
|
|
258
303
|
<template v-if="rows.length">
|
|
259
304
|
<div v-if="showHeader">
|
|
260
305
|
<slot name="column-headers">
|
|
@@ -266,7 +311,7 @@ export default {
|
|
|
266
311
|
<div
|
|
267
312
|
v-for="(row, idx) in rows"
|
|
268
313
|
:key="idx"
|
|
269
|
-
:data-testid="
|
|
314
|
+
:data-testid="`${componentTestid}-box${ idx }`"
|
|
270
315
|
class="box"
|
|
271
316
|
>
|
|
272
317
|
<slot
|
|
@@ -290,7 +335,7 @@ export default {
|
|
|
290
335
|
v-if="valueMultiline"
|
|
291
336
|
ref="value"
|
|
292
337
|
v-model:value="row.value"
|
|
293
|
-
:data-testid="
|
|
338
|
+
:data-testid="`${componentTestid}-textarea-${idx}`"
|
|
294
339
|
:placeholder="valuePlaceholder"
|
|
295
340
|
:mode="mode"
|
|
296
341
|
:disabled="disabled"
|
|
@@ -301,7 +346,7 @@ export default {
|
|
|
301
346
|
v-else-if="rules.length > 0"
|
|
302
347
|
ref="value"
|
|
303
348
|
v-model:value="row.value"
|
|
304
|
-
:data-testid="
|
|
349
|
+
:data-testid="`${componentTestid}-labeled-input-${idx}`"
|
|
305
350
|
:placeholder="valuePlaceholder"
|
|
306
351
|
:disabled="isView || disabled"
|
|
307
352
|
:rules="rules"
|
|
@@ -313,9 +358,10 @@ export default {
|
|
|
313
358
|
v-else
|
|
314
359
|
ref="value"
|
|
315
360
|
v-model="row.value"
|
|
316
|
-
:data-testid="
|
|
361
|
+
:data-testid="`${componentTestid}-input-${idx}`"
|
|
317
362
|
:placeholder="valuePlaceholder"
|
|
318
363
|
:disabled="isView || disabled"
|
|
364
|
+
:aria-label="a11yLabel ? a11yLabel : undefined"
|
|
319
365
|
@paste="onPaste(idx, $event)"
|
|
320
366
|
>
|
|
321
367
|
</slot>
|
|
@@ -335,7 +381,9 @@ export default {
|
|
|
335
381
|
type="button"
|
|
336
382
|
:disabled="isView"
|
|
337
383
|
class="btn role-link"
|
|
338
|
-
:data-testid="
|
|
384
|
+
:data-testid="`${componentTestid}-remove-item-${idx}`"
|
|
385
|
+
:aria-label="`${_removeLabel} ${idx + 1}`"
|
|
386
|
+
role="button"
|
|
339
387
|
@click="remove(row, idx)"
|
|
340
388
|
>
|
|
341
389
|
{{ _removeLabel }}
|
|
@@ -354,30 +402,7 @@ export default {
|
|
|
354
402
|
</div>
|
|
355
403
|
</slot>
|
|
356
404
|
</div>
|
|
357
|
-
|
|
358
|
-
v-if="showAdd && !isView"
|
|
359
|
-
class="footer mt-20"
|
|
360
|
-
>
|
|
361
|
-
<slot
|
|
362
|
-
v-if="showAdd"
|
|
363
|
-
name="add"
|
|
364
|
-
:add="add"
|
|
365
|
-
>
|
|
366
|
-
<button
|
|
367
|
-
type="button"
|
|
368
|
-
class="btn role-tertiary add"
|
|
369
|
-
:disabled="loading || disableAdd"
|
|
370
|
-
data-testid="array-list-button"
|
|
371
|
-
@click="add()"
|
|
372
|
-
>
|
|
373
|
-
<i
|
|
374
|
-
class="mr-5 icon"
|
|
375
|
-
:class="loading ? ['icon-lg', 'icon-spinner','icon-spin']: [addIcon]"
|
|
376
|
-
/>
|
|
377
|
-
{{ _addLabel }}
|
|
378
|
-
</button>
|
|
379
|
-
</slot>
|
|
380
|
-
</div>
|
|
405
|
+
|
|
381
406
|
</div>
|
|
382
407
|
</template>
|
|
383
408
|
|
|
@@ -398,7 +423,12 @@ export default {
|
|
|
398
423
|
.value {
|
|
399
424
|
flex: 1;
|
|
400
425
|
INPUT {
|
|
401
|
-
height: $
|
|
426
|
+
height: $input-height;
|
|
427
|
+
border: solid var(--border-width) var(--input-border);
|
|
428
|
+
padding: 4px 11px;
|
|
429
|
+
&:hover{
|
|
430
|
+
box-shadow: 0 4px 6px 0 var(--input-border-box-shadow);
|
|
431
|
+
}
|
|
402
432
|
}
|
|
403
433
|
}
|
|
404
434
|
}
|
|
@@ -406,6 +436,7 @@ export default {
|
|
|
406
436
|
text-align: right;
|
|
407
437
|
}
|
|
408
438
|
.footer {
|
|
439
|
+
margin-bottom: 24px;
|
|
409
440
|
.protip {
|
|
410
441
|
float: right;
|
|
411
442
|
padding: 5px 0;
|
|
@@ -415,4 +446,11 @@ export default {
|
|
|
415
446
|
.required {
|
|
416
447
|
color: var(--error);
|
|
417
448
|
}
|
|
449
|
+
:deep() .labeled-input.compact-input{
|
|
450
|
+
padding: 0px;
|
|
451
|
+
}
|
|
452
|
+
:deep() .box{
|
|
453
|
+
margin-bottom: 10px;
|
|
454
|
+
}
|
|
455
|
+
|
|
418
456
|
</style>
|
|
@@ -127,8 +127,16 @@ export default {
|
|
|
127
127
|
& > .remove {
|
|
128
128
|
position: absolute;
|
|
129
129
|
|
|
130
|
-
top:
|
|
131
|
-
right:
|
|
130
|
+
top: 10px;
|
|
131
|
+
right: 10px;
|
|
132
|
+
& > .close{
|
|
133
|
+
width: 32px;
|
|
134
|
+
min-width: 32px !important;
|
|
135
|
+
color: var(--body-text);
|
|
136
|
+
&:hover{
|
|
137
|
+
color: var(--link);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
& > .info-box {
|
|
@@ -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,8 @@ 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"
|
|
118
|
+
:data-testid="`env-var-row-${i}`"
|
|
111
119
|
>
|
|
112
120
|
<ValueFromResource
|
|
113
121
|
v-model:value="row.value"
|
|
@@ -91,9 +91,10 @@ export default defineComponent({
|
|
|
91
91
|
|
|
92
92
|
<style lang='scss'>
|
|
93
93
|
.buttons {
|
|
94
|
-
display: grid;
|
|
94
|
+
/* display: grid;
|
|
95
95
|
grid-template-areas: "left right";
|
|
96
|
-
grid-template-columns: "min-content auto";
|
|
96
|
+
grid-template-columns: "min-content auto"; */
|
|
97
|
+
display: flex;
|
|
97
98
|
|
|
98
99
|
.left {
|
|
99
100
|
grid-area: left;
|
|
@@ -105,11 +106,13 @@ export default defineComponent({
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
.right {
|
|
108
|
-
grid-area: right;
|
|
109
|
-
text-align: right;
|
|
109
|
+
/* grid-area: right;
|
|
110
|
+
text-align: right; */
|
|
111
|
+
display: flex;
|
|
110
112
|
|
|
111
113
|
.btn, button {
|
|
112
|
-
margin: 0 0 0 $column-gutter;
|
|
114
|
+
/* margin: 0 0 0 $column-gutter; */
|
|
115
|
+
margin: 0 10px 0 0px;
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
}
|
|
@@ -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
|
|