@rancher/shell 3.0.5-rc.2 → 3.0.5-rc.3
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/data/aws-regions.json +2 -0
- package/assets/styles/global/_layout.scss +0 -1
- package/assets/translations/en-us.yaml +61 -19
- package/assets/translations/zh-hans.yaml +0 -10
- package/chart/monitoring/index.vue +1 -1
- package/components/AsyncButton.vue +2 -0
- package/components/CodeMirror.vue +3 -3
- package/components/CruResource.vue +103 -15
- package/components/ExplorerProjectsNamespaces.vue +7 -2
- package/components/FixedBanner.vue +19 -5
- package/components/PaginatedResourceTable.vue +7 -0
- package/components/ResourceDetail/Masthead.vue +0 -1
- package/components/ResourceList/index.vue +2 -1
- package/components/SlideInPanelManager.vue +1 -2
- package/components/SortableTable/selection.js +1 -1
- package/components/Tabbed/index.vue +6 -0
- package/components/__tests__/AsyncButton.test.ts +39 -0
- package/components/__tests__/CruResource.test.ts +63 -0
- package/components/__tests__/PromptModal.test.ts +0 -2
- package/components/form/ArrayList.vue +134 -118
- package/components/form/BannerSettings.vue +145 -96
- package/components/form/KeyValue.vue +10 -7
- package/components/form/LabeledSelect.vue +9 -2
- package/components/form/MatchExpressions.vue +5 -1
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/ResourceSelector.vue +26 -23
- package/components/form/ResourceTabs/index.vue +2 -1
- package/components/form/Select.vue +9 -2
- package/components/form/UnitInput.vue +13 -0
- package/components/form/__tests__/ArrayList.test.ts +32 -0
- package/components/form/__tests__/KeyValue.test.ts +36 -0
- package/components/form/__tests__/LabeledSelect.test.ts +33 -0
- package/components/form/__tests__/Select.test.ts +34 -1
- package/components/form/__tests__/UnitInput.test.ts +23 -1
- package/components/formatter/ClusterLink.vue +5 -8
- package/components/formatter/Description.vue +30 -0
- package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/WindowManager/index.vue +1 -0
- package/config/product/explorer.js +16 -13
- package/config/product/manager.js +1 -28
- package/config/settings.ts +11 -13
- package/config/table-headers.js +7 -5
- package/detail/catalog.cattle.io.app.vue +0 -1
- package/detail/provisioning.cattle.io.cluster.vue +13 -3
- package/detail/service.vue +0 -1
- package/detail/workload/index.vue +21 -34
- package/dialog/ExtensionCatalogUninstallDialog.vue +14 -8
- package/edit/__tests__/service.test.ts +2 -1
- package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
- package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
- package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
- package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
- package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
- package/edit/service.vue +13 -28
- package/list/workload.vue +6 -1
- package/mixins/resource-fetch-api-pagination.js +55 -43
- package/mixins/resource-fetch.js +14 -5
- package/models/__tests__/workload.test.ts +1 -0
- package/models/cluster/node.js +1 -0
- package/models/cluster.js +32 -2
- package/models/management.cattle.io.cluster.js +0 -20
- package/models/management.cattle.io.node.js +7 -22
- package/models/management.cattle.io.nodepool.js +12 -0
- package/models/namespace.js +5 -0
- package/models/provisioning.cattle.io.cluster.js +18 -64
- package/models/service.js +24 -9
- package/models/workload.js +70 -31
- package/package.json +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +0 -1
- package/pages/c/_cluster/explorer/index.vue +11 -0
- package/pages/c/_cluster/longhorn/index.vue +2 -2
- package/pages/c/_cluster/settings/banners.vue +56 -2
- package/pages/c/_cluster/settings/performance.vue +7 -26
- package/pages/home.vue +11 -52
- package/plugins/clean-html.js +2 -0
- package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
- package/plugins/dashboard-store/actions.js +122 -21
- package/plugins/dashboard-store/getters.js +74 -3
- package/plugins/dashboard-store/mutations.js +10 -5
- package/plugins/dashboard-store/resource-class.js +23 -3
- package/plugins/steve/__tests__/getters.test.ts +18 -11
- package/plugins/steve/__tests__/steve-class.test.ts +1 -0
- package/plugins/steve/actions.js +34 -12
- package/plugins/steve/getters.js +39 -10
- package/plugins/steve/steve-class.js +5 -0
- package/plugins/steve/steve-pagination-utils.ts +199 -37
- package/plugins/steve/worker/web-worker.advanced.js +3 -1
- package/rancher-components/Banner/Banner.test.ts +51 -3
- package/rancher-components/Banner/Banner.vue +28 -6
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +5 -1
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +21 -1
- package/store/features.js +0 -1
- package/store/type-map.utils.ts +45 -2
- package/types/fleet.d.ts +1 -1
- package/types/kube/kube-api.ts +22 -0
- package/types/resources/settings.d.ts +0 -4
- package/types/shell/index.d.ts +346 -289
- package/types/store/dashboard-store.types.ts +24 -1
- package/types/store/pagination.types.ts +19 -2
- package/utils/cluster.js +24 -20
- package/utils/grafana.js +1 -0
- package/utils/object.js +0 -12
- package/utils/pagination-utils.ts +6 -2
- package/utils/perf-setting.utils.ts +28 -0
- package/utils/selector-typed.ts +205 -0
- package/utils/selector.js +29 -6
- package/utils/uiplugins.ts +10 -6
- package/utils/v-sphere.ts +5 -1
- package/components/formatter/RKETemplateName.vue +0 -37
- package/dialog/SaveAsRKETemplateDialog.vue +0 -139
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"ap-southeast-3",
|
|
12
12
|
"ap-southeast-4",
|
|
13
13
|
"ap-southeast-5",
|
|
14
|
+
"ap-southeast-7",
|
|
14
15
|
"ca-central-1",
|
|
15
16
|
"ca-west-1",
|
|
16
17
|
"cn-north-1",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"il-central-1",
|
|
27
28
|
"me-central-1",
|
|
28
29
|
"me-south-1",
|
|
30
|
+
"mx-central-1",
|
|
29
31
|
"sa-east-1",
|
|
30
32
|
"us-east-1",
|
|
31
33
|
"us-east-2",
|
|
@@ -7,6 +7,9 @@ generic:
|
|
|
7
7
|
userMenu: user menu
|
|
8
8
|
actionMenu: action menu
|
|
9
9
|
namespaceFilter: namespace filter menu
|
|
10
|
+
banners:
|
|
11
|
+
altCloseBanner: Close banner icon
|
|
12
|
+
bannerIcon: Banner icon
|
|
10
13
|
filterNamespaces: filter namespaces
|
|
11
14
|
clearSearch: clear search
|
|
12
15
|
clearFilters: clear filters
|
|
@@ -129,8 +132,16 @@ generic:
|
|
|
129
132
|
}
|
|
130
133
|
basic: Basic
|
|
131
134
|
ariaLabel:
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
keyValue: Key-Value input
|
|
136
|
+
key: Key for row {index}
|
|
137
|
+
value: Value for row {index}
|
|
138
|
+
remove: remove row {index}
|
|
139
|
+
addKeyValue: Add a new Key-Value row
|
|
140
|
+
readKeyValue: read Key-Values from file
|
|
141
|
+
genericAddRow: Add a new row
|
|
142
|
+
arrayList: Array list input
|
|
143
|
+
genericRow: row {index}
|
|
144
|
+
|
|
134
145
|
|
|
135
146
|
locale:
|
|
136
147
|
menu: Locale selector menu
|
|
@@ -181,7 +192,6 @@ nav:
|
|
|
181
192
|
restoreSnapshot: Restore Snapshot
|
|
182
193
|
rotateCertificates: Rotate Certificates
|
|
183
194
|
rotateEncryptionKeys: Rotate Encryption Keys
|
|
184
|
-
saveAsRKETemplate: Save as RKE Template
|
|
185
195
|
takeSnapshot: Take Snapshot
|
|
186
196
|
seeAllClusters: See all clusters
|
|
187
197
|
seeAllClustersCollapsed: See all
|
|
@@ -717,6 +727,8 @@ assignTo:
|
|
|
717
727
|
workspace: Workspace
|
|
718
728
|
|
|
719
729
|
asyncButton:
|
|
730
|
+
alt:
|
|
731
|
+
iconAlt: Async button icon
|
|
720
732
|
apply:
|
|
721
733
|
action: Apply
|
|
722
734
|
success: Applied
|
|
@@ -1936,9 +1948,8 @@ cluster:
|
|
|
1936
1948
|
=1 { {pool_name}: The provided value for {fields} was not found in the list of expected values. This can happen with clusters provisioned outside of Rancher or when options for the provider have changed. }
|
|
1937
1949
|
other { {pool_name}: The provided values for {fields} were not found in the list of expected values. This can happen with clusters provisioned outside of Rancher or when options for the provider have changed. }
|
|
1938
1950
|
}
|
|
1939
|
-
rke1DeprecationMessage: 'Rancher Kubernetes Engine (RKE / RKE1)
|
|
1940
|
-
|
|
1941
|
-
rkeTemplateUpgrade: Template revision {name} available for upgrade
|
|
1951
|
+
rke1DeprecationMessage: 'Rancher Kubernetes Engine (RKE / RKE1) has reached end of life and these clusters are no longer supported. We recommend replatforming RKE1 clusters to RKE2 to ensure continued support and security updates. Learn more about the transition <a href="https://www.suse.com/support/kb/doc/?id=000021518" target="_blank" rel="noopener noreferrer nofollow">here</a>.'
|
|
1952
|
+
rke1Unsupported: RKE1 Clusters are no longer supported
|
|
1942
1953
|
cloudCredentials:
|
|
1943
1954
|
renew: Renew Cloud Credentials
|
|
1944
1955
|
expired: Cloud Credential expired, please Renew Cloud Credentials
|
|
@@ -3209,8 +3220,6 @@ landing:
|
|
|
3209
3220
|
title: Commercial Support
|
|
3210
3221
|
body: Learn about commercial support
|
|
3211
3222
|
landingPrefs:
|
|
3212
|
-
title: You can change what you see when you login via preferences
|
|
3213
|
-
userPrefs: Preferences
|
|
3214
3223
|
body: "You can change where you land when you login"
|
|
3215
3224
|
ariaLabelTakeMeToCluster: Select which cluster to take me to after login
|
|
3216
3225
|
options:
|
|
@@ -3908,6 +3917,7 @@ nameNsDescription:
|
|
|
3908
3917
|
placeholder: Any text you want that better describes this resource
|
|
3909
3918
|
|
|
3910
3919
|
namespace:
|
|
3920
|
+
cancelCreateAriaLabel: Cancel create namespace
|
|
3911
3921
|
containerResourceLimit: Container Resource Limit
|
|
3912
3922
|
resourceQuotas: Resource Quotas
|
|
3913
3923
|
project:
|
|
@@ -4981,12 +4991,6 @@ promptRollback:
|
|
|
4981
4991
|
multipleWorkloadError: "Only one workload can be rolled back at a time."
|
|
4982
4992
|
singleRevisionBanner: There are no revisions to roll back to.
|
|
4983
4993
|
|
|
4984
|
-
promptSaveAsRKETemplate:
|
|
4985
|
-
title: Convert {cluster} to new RKE Template
|
|
4986
|
-
name: Cluster Template Name
|
|
4987
|
-
description: Create a new RKE cluster template and initial revision from the current cluster configuration.
|
|
4988
|
-
warning: This will modify the cluster, setting it up to use the newly created cluster template and revision. This can not be undone.
|
|
4989
|
-
|
|
4990
4994
|
promptRotateEncryptionKey:
|
|
4991
4995
|
title: Rotate Encryption Keys
|
|
4992
4996
|
description: The last backup {name} was performed on {date}
|
|
@@ -7826,15 +7830,12 @@ performance:
|
|
|
7826
7830
|
authUserTTL: This timeout cannot be higher than the user session timeout auth-user-session-ttl-minutes, which is currently {current} minutes.
|
|
7827
7831
|
serverPagination:
|
|
7828
7832
|
label: Server-side Pagination
|
|
7829
|
-
description: By default Lists will fetch all resources and paginate
|
|
7830
|
-
checkboxLabel: Enable Server-side Pagination
|
|
7833
|
+
description: By default Lists will fetch all resources and paginate (create the page given sort and filter settings) locally in the browser. Server-Side Pagination moves this out from the browser to the server. This improves performance of the UI, especially in systems with lots of resources.
|
|
7831
7834
|
applicable: "Server-side pagination applies to Resource Types"
|
|
7832
|
-
|
|
7833
|
-
featureFlag: The <a href="{ffUrl}">Feature Flag</a> `ui-sql-cache` must be enabled to use this feature
|
|
7835
|
+
featureFlag: Enabling/Disabling <i class="mr-5">"Server-side Pagination"</i> is now solely done so via the <i class="mr-5">ui-sql-cache</i> <a href="{ffUrl}">Feature Flag</a>
|
|
7834
7836
|
resources:
|
|
7835
7837
|
generic: most resources in the cluster's 'More Resources' section
|
|
7836
7838
|
all: All Resources
|
|
7837
|
-
experimental: This setting is experimental and may be removed or updated in future versions. We do not recommend enabling this setting in production environments.
|
|
7838
7839
|
banner:
|
|
7839
7840
|
label: Fixed Banners
|
|
7840
7841
|
settingName: Banners
|
|
@@ -7867,6 +7868,12 @@ banner:
|
|
|
7867
7868
|
consent: Consent Banner
|
|
7868
7869
|
consentFootnote: "Tip: Use \\n character for line break"
|
|
7869
7870
|
individualSetting: 'This banner is managed by the setting "{name}" and can not be modified in the UI'
|
|
7871
|
+
type:
|
|
7872
|
+
html: HTML
|
|
7873
|
+
text: Text
|
|
7874
|
+
htmlWarning: 'Not all HTML elements or attriutes are permitted and the HTML entered here will be sanitized before display. For HTML anchor elements, it is recommended to use the target attribute to have links open in a blank tab.'
|
|
7875
|
+
htmlContent: HTML Content
|
|
7876
|
+
toggleTextHtml: Toggle between text and html
|
|
7870
7877
|
|
|
7871
7878
|
branding:
|
|
7872
7879
|
label: Branding
|
|
@@ -8167,3 +8174,38 @@ gitPicker:
|
|
|
8167
8174
|
networkAttachmentDefinition:
|
|
8168
8175
|
tabs:
|
|
8169
8176
|
config: Config
|
|
8177
|
+
|
|
8178
|
+
errors:
|
|
8179
|
+
actionNotAvailable: This action is not currently available
|
|
8180
|
+
missingRequired: is required
|
|
8181
|
+
notUnique: is not unique
|
|
8182
|
+
notNullable: must be set
|
|
8183
|
+
invalidOption: is not a valid option
|
|
8184
|
+
invalidCharacters: contains invalid characters
|
|
8185
|
+
minLengthExceeded: is not long enough
|
|
8186
|
+
maxLengthExceeded: is too long
|
|
8187
|
+
minLimitExceeded: is too small
|
|
8188
|
+
maxLimitExceded: is too big
|
|
8189
|
+
messageAndDetail: '{message} ({detail})'
|
|
8190
|
+
messageOrDetail: '{val}'
|
|
8191
|
+
failedInApi:
|
|
8192
|
+
withName:
|
|
8193
|
+
withCodeExplanation:
|
|
8194
|
+
withMessageDetail: 'Validation failed in API: {name} {codeExplanation}: {messageDetail}'
|
|
8195
|
+
withoutMessageDetail: 'Validation failed in API: {name} {codeExplanation}'
|
|
8196
|
+
withMessageDetail: 'Validation failed in API: {name} {messageDetail}'
|
|
8197
|
+
withoutAnythingElse: 'Validation failed in API: {name} '
|
|
8198
|
+
withoutName:
|
|
8199
|
+
withMessageDetail:
|
|
8200
|
+
withCodeExplanation: 'Validation failed in API: {codeExplanation}: {messageDetail}'
|
|
8201
|
+
withoutCodeExplanation: 'Validation failed in API: {messageDetail}'
|
|
8202
|
+
withCode:
|
|
8203
|
+
withCodeExplanation: 'Validation failed in API: {code} {codeExplanation}'
|
|
8204
|
+
withoutCodeExplanation: 'Validation failed in API: {code}'
|
|
8205
|
+
withoutAnything: Validation failed in API.
|
|
8206
|
+
notFound:
|
|
8207
|
+
withUrl: '{msg}: {url}'
|
|
8208
|
+
withoutUrl: '{msg}'
|
|
8209
|
+
|
|
8210
|
+
|
|
8211
|
+
|
|
@@ -137,7 +137,6 @@ nav:
|
|
|
137
137
|
restoreSnapshot: 还原快照
|
|
138
138
|
rotateCertificates: 轮换证书
|
|
139
139
|
rotateEncryptionKeys: 轮换加密密钥
|
|
140
|
-
saveAsRKETemplate: 保存为 RKE 模板
|
|
141
140
|
takeSnapshot: 拍摄快照
|
|
142
141
|
group:
|
|
143
142
|
cluster: 集群
|
|
@@ -1669,7 +1668,6 @@ cluster:
|
|
|
1669
1668
|
rke2-k3-reprovisioning: '更改集群配置可能导致节点重新配置。详情请参见 <a target="blank" href="{docsBase}/how-to-guides/new-user-guides/launch-kubernetes-with-rancher/rke1-vs-rke2-differences#cluster-api" target="_blank" rel="noopener nofollow">文档</a>。'
|
|
1670
1669
|
desiredNodeGroupWarning: 没有可用于运行 Cluster Agent 的节点。要让集群变为 Active 状态,至少需要有 1 个可用的节点。
|
|
1671
1670
|
haveArgInfo: 所选 Kubernetes 版本的配置信息不可用。此屏幕中可用的选项将受到限制,你可能需要使用 YAML 编辑器。
|
|
1672
|
-
rkeTemplateUpgrade: 模板修订版 {name} 可用于升级
|
|
1673
1671
|
|
|
1674
1672
|
availabilityWarnings:
|
|
1675
1673
|
node: 节点 {name} 处于非活动状态
|
|
@@ -2731,8 +2729,6 @@ landing:
|
|
|
2731
2729
|
title: 商业支持
|
|
2732
2730
|
body: 了解商业支持
|
|
2733
2731
|
landingPrefs:
|
|
2734
|
-
title: 更改登录后显示的页面:
|
|
2735
|
-
userPrefs: 偏好设置
|
|
2736
2732
|
body: "登录后显示:"
|
|
2737
2733
|
options:
|
|
2738
2734
|
homePage: 主页
|
|
@@ -4447,12 +4443,6 @@ promptRollback:
|
|
|
4447
4443
|
multipleWorkloadError: "一次只能回滚一个工作负载。"
|
|
4448
4444
|
singleRevisionBanner: 没有可用于回滚的修订版本。
|
|
4449
4445
|
|
|
4450
|
-
promptSaveAsRKETemplate:
|
|
4451
|
-
title: 将 {cluster} 转换为新的 RKE 模板
|
|
4452
|
-
name: 集群模板名称
|
|
4453
|
-
description: 创建新的集群模板,并使用当前集群配置发起修改。
|
|
4454
|
-
warning: 此操作将对集群进行修改,即把新创建的集群模板和修改应用到集群中。此操作不能撤销。
|
|
4455
|
-
|
|
4456
4446
|
promptRotateEncryptionKey:
|
|
4457
4447
|
title: 轮换加密密钥
|
|
4458
4448
|
description: 上次备份 {name} 的备份时间为 {date}
|
|
@@ -55,7 +55,7 @@ export default {
|
|
|
55
55
|
async fetch() {
|
|
56
56
|
const { $store } = this;
|
|
57
57
|
|
|
58
|
-
// Fetch all the resources required for all the tabs
|
|
58
|
+
// Fetch all the resources required for all the tabs asynchronously up front
|
|
59
59
|
const hashPromises = {
|
|
60
60
|
namespaces: $store.getters['namespaces'](),
|
|
61
61
|
pvcs: $store.dispatch('cluster/findAll', { type: PVC }),
|
|
@@ -314,11 +314,13 @@ export default defineComponent({
|
|
|
314
314
|
v-if="displayIcon"
|
|
315
315
|
v-clean-tooltip="tooltip"
|
|
316
316
|
:class="{icon: true, 'icon-lg': true, [displayIcon]: true, 'mr-0': isManualRefresh}"
|
|
317
|
+
:alt="t('asyncButton.alt.iconAlt')"
|
|
317
318
|
/>
|
|
318
319
|
<span
|
|
319
320
|
v-if="labelAs === 'text' && displayLabel"
|
|
320
321
|
v-clean-tooltip="tooltip"
|
|
321
322
|
v-clean-html="displayLabel"
|
|
323
|
+
data-testid="async-btn-display-label"
|
|
322
324
|
/>
|
|
323
325
|
</button>
|
|
324
326
|
</template>
|
|
@@ -88,7 +88,7 @@ export default {
|
|
|
88
88
|
|
|
89
89
|
// fixes https://github.com/rancher/dashboard/issues/13653
|
|
90
90
|
// we can't use the inert HTML prop on the parent because it disables all interaction
|
|
91
|
-
out.readOnly = this.isDisabled
|
|
91
|
+
out.readOnly = !!this.isDisabled;
|
|
92
92
|
|
|
93
93
|
return out;
|
|
94
94
|
},
|
|
@@ -227,8 +227,8 @@ export default {
|
|
|
227
227
|
},
|
|
228
228
|
|
|
229
229
|
onFocus() {
|
|
230
|
-
this.isCodeMirrorFocused =
|
|
231
|
-
this.$emit('onFocus',
|
|
230
|
+
this.isCodeMirrorFocused = !this.isDisabled;
|
|
231
|
+
this.$emit('onFocus', this.isCodeMirrorFocused);
|
|
232
232
|
},
|
|
233
233
|
|
|
234
234
|
onBlur() {
|
|
@@ -265,7 +265,7 @@ export default {
|
|
|
265
265
|
return !this.errors ? {} : this.errorsMap || this.errors.reduce((acc, error) => ({
|
|
266
266
|
...acc,
|
|
267
267
|
[error]: {
|
|
268
|
-
message: error,
|
|
268
|
+
message: this.formatError(error),
|
|
269
269
|
icon: null
|
|
270
270
|
}
|
|
271
271
|
}), {});
|
|
@@ -371,19 +371,23 @@ export default {
|
|
|
371
371
|
},
|
|
372
372
|
|
|
373
373
|
async clickSave(buttonDone) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// is successful, save the resource.
|
|
379
|
-
this.$emit('finish', buttonDone);
|
|
380
|
-
} catch (err) {
|
|
374
|
+
if (this.createNamespace) {
|
|
375
|
+
try {
|
|
376
|
+
await this.createNamespaceIfNeeded();
|
|
377
|
+
} catch (err) {
|
|
381
378
|
// After the attempt to create the namespace,
|
|
382
379
|
// show any applicable errors if the namespace is
|
|
383
380
|
// invalid.
|
|
384
|
-
|
|
385
|
-
|
|
381
|
+
this.$emit('error', exceptionToErrorsArray(err.message));
|
|
382
|
+
buttonDone(false);
|
|
383
|
+
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
386
|
}
|
|
387
|
+
|
|
388
|
+
// If the attempt to create the new namespace
|
|
389
|
+
// was successful or no ns needs to be created, save the resource.
|
|
390
|
+
this.$emit('finish', buttonDone);
|
|
387
391
|
},
|
|
388
392
|
|
|
389
393
|
save() {
|
|
@@ -395,17 +399,13 @@ export default {
|
|
|
395
399
|
const newNamespaceName = get(this.resource, this.namespaceKey);
|
|
396
400
|
let namespaceAlreadyExists = false;
|
|
397
401
|
|
|
398
|
-
if (!this.createNamespace) {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
402
|
try {
|
|
403
403
|
// This is in a try-catch block because the call to fetch
|
|
404
404
|
// a namespace throws an error if the namespace is not found.
|
|
405
405
|
namespaceAlreadyExists = !!(await this.$store.dispatch(`${ inStore }/find`, { type: NAMESPACE, id: newNamespaceName }));
|
|
406
406
|
} catch {}
|
|
407
407
|
|
|
408
|
-
if (
|
|
408
|
+
if (!namespaceAlreadyExists) {
|
|
409
409
|
try {
|
|
410
410
|
const newNamespace = await this.$store.dispatch(`${ inStore }/createNamespace`, { name: newNamespaceName }, { root: true });
|
|
411
411
|
|
|
@@ -427,6 +427,94 @@ export default {
|
|
|
427
427
|
|
|
428
428
|
shouldProvideSlot(slot) {
|
|
429
429
|
return slot !== 'default' && typeof this.$slots[slot] === 'function';
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
formatError(err) {
|
|
433
|
+
if ( typeof err === 'string') {
|
|
434
|
+
return err;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if ( err?.code === 'ActionNotAvailable' ) {
|
|
438
|
+
return this.t('errors.actionNotAvailable');
|
|
439
|
+
}
|
|
440
|
+
const msg = !!err?.message ? err.message : '';
|
|
441
|
+
let messageDetail = '';
|
|
442
|
+
|
|
443
|
+
if (!!err?.message && !!err.detail) {
|
|
444
|
+
messageDetail = this.t('errors.messageAndDetail', { message: err.message, detail: err.detail });
|
|
445
|
+
} else if (!!err?.message || !!err.detail) {
|
|
446
|
+
const val = err.message ? err.message : err.detail;
|
|
447
|
+
|
|
448
|
+
messageDetail = this.t('errors.messageOrDetail', { val });
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if ( err?.status === 422 ) {
|
|
452
|
+
const name = err?.fieldName;
|
|
453
|
+
const code = err?.code;
|
|
454
|
+
let codeExplanation = '';
|
|
455
|
+
|
|
456
|
+
switch ( err?.code ) {
|
|
457
|
+
case 'MissingRequired':
|
|
458
|
+
codeExplanation = this.t('errors.missingRequired'); break;
|
|
459
|
+
case 'NotUnique':
|
|
460
|
+
codeExplanation = this.t('errors.notUnique'); break;
|
|
461
|
+
case 'NotNullable':
|
|
462
|
+
codeExplanation = this.t('errors.notNullable'); break;
|
|
463
|
+
case 'InvalidOption':
|
|
464
|
+
codeExplanation = this.t('errors.invalidOption'); break;
|
|
465
|
+
case 'InvalidCharacters':
|
|
466
|
+
codeExplanation = this.t('errors.invalidCharacters'); break;
|
|
467
|
+
case 'MinLengthExceeded':
|
|
468
|
+
codeExplanation = this.t('errors.minLengthExceeded'); break;
|
|
469
|
+
case 'MaxLengthExceeded':
|
|
470
|
+
codeExplanation = this.t('errors.maxLengthExceeded'); break;
|
|
471
|
+
case 'MinLimitExceeded':
|
|
472
|
+
codeExplanation = this.t('errors.minLimitExceeded'); break;
|
|
473
|
+
case 'MaxLimitExceded':
|
|
474
|
+
codeExplanation = this.t('errors.maxLimitExceded'); break;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (!!name) {
|
|
478
|
+
if (!!codeExplanation) {
|
|
479
|
+
if (!!messageDetail) {
|
|
480
|
+
return this.t('errors.failedInApi.withName.withCodeExplanation.withMessageDetail', {
|
|
481
|
+
name, codeExplanation, messageDetail
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return this.t('errors.failedInApi.withName.withCodeExplanation.withoutMessageDetail', { name, codeExplanation });
|
|
486
|
+
}
|
|
487
|
+
if (!!messageDetail) {
|
|
488
|
+
return this.t('errors.failedInApi.withName.withMessageDetail', { name, messageDetail });
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return this.t('errors.failedInApi.withName.withoutAnythingElse', { name });
|
|
492
|
+
} else {
|
|
493
|
+
if (!!messageDetail) {
|
|
494
|
+
if (!!codeExplanation) {
|
|
495
|
+
return this.t('errors.failedInApi.withoutName.withMessageDetail.withCodeExplanation', { codeExplanation, messageDetail });
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return this.t('errors.failedInApi.withoutName.withMessageDetail.withoutCodeExplanation', { messageDetail });
|
|
499
|
+
} else if (!!code) {
|
|
500
|
+
if (!!codeExplanation) {
|
|
501
|
+
return this.t('errors.failedInApi.withoutName.withCode.withCodeExplanation', { code, codeExplanation });
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return this.t('errors.failedInApi.withoutName.withCode.withoutCodeExplanation', { code });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return this.t('errors.failedInApi.withoutAnything');
|
|
508
|
+
}
|
|
509
|
+
} else if ( err?.status === 404 ) {
|
|
510
|
+
if (!!err?.opt?.url) {
|
|
511
|
+
return this.t('errors.notFound.withUrl', { msg, url: err.opt.url });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return this.t('errors.notFound.withoutUrl', { msg });
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return messageDetail.length > 0 ? messageDetail : err;
|
|
430
518
|
}
|
|
431
519
|
},
|
|
432
520
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { mapGetters, useStore } from 'vuex';
|
|
3
3
|
import ResourceTable, { defaultTableSortGenerationFn } from '@shell/components/ResourceTable';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
STATE, AGE, NAME, NS_SNAPSHOT_QUOTA, DESCRIPTION
|
|
6
|
+
} from '@shell/config/table-headers';
|
|
5
7
|
import { uniq } from '@shell/utils/array';
|
|
6
8
|
import { MANAGEMENT, NAMESPACE, VIRTUAL_TYPES, HCI } from '@shell/config/types';
|
|
7
9
|
import { PROJECT_ID, FLAT_VIEW } from '@shell/config/query-params';
|
|
@@ -15,6 +17,7 @@ import { NAMESPACE_FILTER_ALL_ORPHANS } from '@shell/utils/namespace-filter';
|
|
|
15
17
|
import ResourceFetch from '@shell/mixins/resource-fetch';
|
|
16
18
|
import DOMPurify from 'dompurify';
|
|
17
19
|
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
20
|
+
import perfSettingsUtils from '@shell/utils/perf-setting.utils';
|
|
18
21
|
import ActionMenu from '@shell/components/ActionMenuShell.vue';
|
|
19
22
|
import { useRuntimeFlag } from '@shell/composables/useRuntimeFlag';
|
|
20
23
|
|
|
@@ -110,7 +113,7 @@ export default {
|
|
|
110
113
|
return !this.currentCluster || this.namespaces.length ? false : this.$fetchState.pending;
|
|
111
114
|
},
|
|
112
115
|
showIncrementalLoadingIndicator() {
|
|
113
|
-
return this.perfConfig
|
|
116
|
+
return perfSettingsUtils.incrementalLoadingUtils.isEnabled(this.calcCanPaginate(), this.perfConfig);
|
|
114
117
|
},
|
|
115
118
|
isNamespaceCreatable() {
|
|
116
119
|
return (this.schema?.collectionMethods || []).includes('POST');
|
|
@@ -122,6 +125,7 @@ export default {
|
|
|
122
125
|
const headers = [
|
|
123
126
|
STATE,
|
|
124
127
|
NAME,
|
|
128
|
+
DESCRIPTION
|
|
125
129
|
];
|
|
126
130
|
|
|
127
131
|
if (this.groupPreference === 'none') {
|
|
@@ -502,6 +506,7 @@ export default {
|
|
|
502
506
|
<ActionMenu
|
|
503
507
|
v-if="showProjectActionButton(group.group)"
|
|
504
508
|
:resource="getProjectActions(group.group)"
|
|
509
|
+
data-testid="action-button"
|
|
505
510
|
:button-aria-label="t('projectNamespaces.tableActionsLabel', { resource: projectResource(group.group) })"
|
|
506
511
|
/>
|
|
507
512
|
<div
|
|
@@ -164,7 +164,7 @@ export default {
|
|
|
164
164
|
class="banner"
|
|
165
165
|
data-testid="fixed__banner"
|
|
166
166
|
:style="bannerStyle"
|
|
167
|
-
:class="{'banner-consent': consent}"
|
|
167
|
+
:class="{'banner-consent': consent, 'banner-text': !!banner.text}"
|
|
168
168
|
>
|
|
169
169
|
<!-- text as array to support line breaks programmatically rather than just exposing HTML -->
|
|
170
170
|
<div v-if="isTextAnArray">
|
|
@@ -177,11 +177,16 @@ export default {
|
|
|
177
177
|
</div>
|
|
178
178
|
</div>
|
|
179
179
|
<div
|
|
180
|
-
v-else
|
|
180
|
+
v-else-if="banner.text"
|
|
181
181
|
class="single-row"
|
|
182
182
|
>
|
|
183
183
|
{{ banner.text }}
|
|
184
184
|
</div>
|
|
185
|
+
<div
|
|
186
|
+
v-else-if="banner.html"
|
|
187
|
+
v-clean-html="banner.html"
|
|
188
|
+
class="single-row"
|
|
189
|
+
/>
|
|
185
190
|
</div>
|
|
186
191
|
<div v-else-if="showDialog">
|
|
187
192
|
<div class="banner-dialog-glass" />
|
|
@@ -205,13 +210,19 @@ export default {
|
|
|
205
210
|
</div>
|
|
206
211
|
</div>
|
|
207
212
|
<div
|
|
208
|
-
v-else
|
|
213
|
+
v-else-if="banner.text"
|
|
209
214
|
class="single-row"
|
|
210
215
|
>
|
|
211
216
|
{{ banner.text }}
|
|
212
217
|
</div>
|
|
218
|
+
<div
|
|
219
|
+
v-else-if="banner.html"
|
|
220
|
+
v-clean-html="banner.html"
|
|
221
|
+
class="single-row"
|
|
222
|
+
/>
|
|
213
223
|
</div>
|
|
214
224
|
<button
|
|
225
|
+
data-testid="login-confirmation-accept-button"
|
|
215
226
|
class="btn role-primary"
|
|
216
227
|
@click="hideDialog()"
|
|
217
228
|
>
|
|
@@ -225,10 +236,13 @@ export default {
|
|
|
225
236
|
|
|
226
237
|
<style lang="scss" scoped>
|
|
227
238
|
.banner {
|
|
228
|
-
text-align: center;
|
|
229
239
|
line-height: 2em;
|
|
230
240
|
width: 100%;
|
|
231
|
-
|
|
241
|
+
|
|
242
|
+
&.banner-text {
|
|
243
|
+
padding: 0 20px;
|
|
244
|
+
text-align: center;
|
|
245
|
+
}
|
|
232
246
|
|
|
233
247
|
&.banner-consent {
|
|
234
248
|
height: unset;
|
|
@@ -95,6 +95,12 @@ export default defineComponent({
|
|
|
95
95
|
|
|
96
96
|
return customHeaders || this.$store.getters['type-map/headersFor'](this.schema, this.canPaginate);
|
|
97
97
|
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
methods: {
|
|
101
|
+
clearSelection() {
|
|
102
|
+
this.$refs.table.clearSelection();
|
|
103
|
+
},
|
|
98
104
|
}
|
|
99
105
|
});
|
|
100
106
|
|
|
@@ -103,6 +109,7 @@ export default defineComponent({
|
|
|
103
109
|
<template>
|
|
104
110
|
<div>
|
|
105
111
|
<ResourceTable
|
|
112
|
+
ref="table"
|
|
106
113
|
v-bind="$attrs"
|
|
107
114
|
:schema="schema"
|
|
108
115
|
:rows="rows"
|
|
@@ -545,7 +545,6 @@ export default {
|
|
|
545
545
|
{{ value.createdBy.displayName }}
|
|
546
546
|
</span>
|
|
547
547
|
</span>
|
|
548
|
-
<span v-if="value.showPodRestarts">{{ t("resourceDetail.masthead.restartCount") }}:<span class="live-data"> {{ value.restartCount }}</span></span>
|
|
549
548
|
</div>
|
|
550
549
|
</div>
|
|
551
550
|
<slot name="right">
|
|
@@ -9,6 +9,7 @@ import { ResourceListComponentName } from './resource-list.config';
|
|
|
9
9
|
import { PanelLocation, ExtensionPoint } from '@shell/core/types';
|
|
10
10
|
import ExtensionPanel from '@shell/components/ExtensionPanel';
|
|
11
11
|
import { sameContents } from '@shell/utils/array';
|
|
12
|
+
import perfSettingsUtils from '@shell/utils/perf-setting.utils';
|
|
12
13
|
|
|
13
14
|
export default {
|
|
14
15
|
name: ResourceListComponentName,
|
|
@@ -138,7 +139,7 @@ export default {
|
|
|
138
139
|
},
|
|
139
140
|
|
|
140
141
|
showIncrementalLoadingIndicator() {
|
|
141
|
-
return this.perfConfig
|
|
142
|
+
return perfSettingsUtils.incrementalLoadingUtils.isEnabled(this.calcCanPaginate(), this.perfConfig);
|
|
142
143
|
},
|
|
143
144
|
|
|
144
145
|
},
|
|
@@ -61,6 +61,11 @@ export default {
|
|
|
61
61
|
tabsOnly: {
|
|
62
62
|
type: Boolean,
|
|
63
63
|
default: false,
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
resource: {
|
|
67
|
+
type: Object,
|
|
68
|
+
default: () => {}
|
|
64
69
|
}
|
|
65
70
|
},
|
|
66
71
|
|
|
@@ -355,6 +360,7 @@ export default {
|
|
|
355
360
|
>
|
|
356
361
|
<component
|
|
357
362
|
:is="tab.component"
|
|
363
|
+
:resource="resource"
|
|
358
364
|
/>
|
|
359
365
|
</Tab>
|
|
360
366
|
</div>
|
|
@@ -145,4 +145,43 @@ describe('component: AsyncButton', () => {
|
|
|
145
145
|
expect(spyDone).toHaveBeenCalledWith('cancelled');
|
|
146
146
|
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.ACTION);
|
|
147
147
|
});
|
|
148
|
+
|
|
149
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', () => {
|
|
150
|
+
const mockExists = jest.fn().mockReturnValue(true);
|
|
151
|
+
const mockT = jest.fn().mockReturnValue('some-string');
|
|
152
|
+
const ariaLabel = 'some-aria-label';
|
|
153
|
+
const ariaLabelledBy = 'some-aria-labelledby';
|
|
154
|
+
|
|
155
|
+
const wrapper: VueWrapper<InstanceType<typeof AsyncButton>> = mount(AsyncButton, {
|
|
156
|
+
props: { icon: 'some-icon', disabled: true },
|
|
157
|
+
attrs: { 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy },
|
|
158
|
+
global: {
|
|
159
|
+
mocks: {
|
|
160
|
+
$store: {
|
|
161
|
+
getters: {
|
|
162
|
+
'i18n/exists': mockExists,
|
|
163
|
+
'i18n/t': mockT
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const item = wrapper.find('button');
|
|
171
|
+
|
|
172
|
+
const itemRole = item.attributes('role');
|
|
173
|
+
const itemAriaLabel = item.attributes('aria-label');
|
|
174
|
+
const itemAriaLabelledBy = item.attributes('aria-labelledby');
|
|
175
|
+
const itemAriaDisabled = item.attributes('aria-disabled');
|
|
176
|
+
|
|
177
|
+
// let's check some attributes passing...
|
|
178
|
+
expect(itemAriaLabel).toBe(ariaLabel);
|
|
179
|
+
expect(itemAriaLabelledBy).toBe(ariaLabelledBy);
|
|
180
|
+
|
|
181
|
+
// rest of the checks
|
|
182
|
+
expect(itemRole).toBe('button');
|
|
183
|
+
expect(itemAriaDisabled).toBe('true');
|
|
184
|
+
expect(item.find('span[data-testid="async-btn-display-label"]').attributes('id')).toBe(wrapper.vm.describedbyId);
|
|
185
|
+
expect(item.find('i').attributes('alt')).toBeDefined();
|
|
186
|
+
});
|
|
148
187
|
});
|