@rancher/shell 3.0.2-rc.2 → 3.0.2-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/styles/base/_basic.scss +5 -7
- package/assets/styles/global/_button.scss +10 -0
- package/assets/styles/global/_tooltip.scss +2 -2
- package/assets/styles/themes/_dark.scss +14 -2
- package/assets/styles/themes/_light.scss +7 -2
- package/assets/styles/vendor/vue-select.scss +4 -0
- package/assets/translations/en-us.yaml +44 -5
- package/components/BannerGraphic.vue +0 -42
- package/components/ButtonMultiAction.vue +1 -1
- package/components/Carousel.vue +36 -29
- package/components/CommunityLinks.vue +6 -1
- package/components/GrowlManager.vue +9 -2
- package/components/LocaleSelector.vue +8 -1
- package/components/PaginatedResourceTable.vue +4 -7
- package/components/ProgressBarMulti.vue +14 -0
- package/components/Questions/Reference.vue +57 -28
- package/components/SelectIconGrid.vue +12 -1
- package/components/SideNav.vue +12 -38
- package/components/SortableTable/index.vue +1 -0
- package/components/Tabbed/index.vue +12 -1
- package/components/YamlEditor.vue +1 -0
- package/components/auth/Principal.vue +5 -3
- package/components/fleet/FleetClusters.vue +82 -1
- package/components/fleet/FleetRepos.vue +13 -30
- package/components/fleet/ForceDirectedTreeChart/index.vue +2 -2
- package/components/form/ChangePassword.vue +2 -0
- package/components/form/ColorInput.vue +24 -1
- package/components/form/FileSelector.vue +2 -0
- package/components/form/KeyValue.vue +230 -160
- package/components/form/LabeledSelect.vue +1 -1
- package/components/form/PlusMinus.vue +14 -2
- package/components/form/ResourceLabeledSelect.vue +13 -53
- package/components/form/ResourceSelector.vue +1 -0
- package/components/form/ResourceTabs/index.vue +79 -36
- package/components/form/SecretSelector.vue +2 -2
- package/components/form/__tests__/KeyValue.test.ts +1 -1
- package/components/formatter/FleetClusterSummaryGraph.vue +2 -2
- package/components/formatter/FleetSummaryGraph.vue +6 -7
- package/components/formatter/WorkloadHealthScale.vue +7 -0
- package/components/nav/Group.vue +30 -4
- package/components/nav/Header.vue +82 -114
- package/components/nav/HeaderPageActionMenu.vue +27 -131
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/Type.vue +15 -0
- package/config/home-links.js +21 -13
- package/config/labels-annotations.js +2 -0
- package/config/page-actions.js +1 -0
- package/config/pagination-table-headers.js +15 -1
- package/config/product/explorer.js +7 -17
- package/config/table-headers.js +6 -0
- package/config/version.js +5 -1
- package/core/plugin.ts +41 -1
- package/core/plugins.js +125 -72
- package/core/types-provisioning.ts +91 -2
- package/core/types.ts +55 -0
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +12 -3
- package/detail/catalog.cattle.io.app.vue +1 -1
- package/detail/fleet.cattle.io.cluster.vue +3 -3
- package/detail/namespace.vue +13 -19
- package/detail/networking.k8s.io.ingress.vue +13 -53
- package/detail/provisioning.cattle.io.cluster.vue +12 -1
- package/detail/workload/index.vue +3 -3
- package/dialog/AddCustomBadgeDialog.vue +5 -1
- package/edit/auth/ldap/__tests__/config.test.ts +18 -0
- package/edit/auth/ldap/config.vue +24 -0
- package/edit/auth/saml.vue +8 -6
- package/edit/fleet.cattle.io.gitrepo.vue +7 -1
- package/edit/logging-flow/index.vue +4 -19
- package/edit/networking.k8s.io.ingress/index.vue +18 -65
- package/edit/networking.k8s.io.networkpolicy/index.vue +4 -5
- package/edit/provisioning.cattle.io.cluster/index.vue +13 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -115
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/tabs/networking/ACE.vue +14 -28
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +25 -12
- package/edit/service.vue +1 -2
- package/list/networking.k8s.io.ingress.vue +1 -1
- package/list/node.vue +15 -8
- package/list/persistentvolume.vue +12 -4
- package/list/service.vue +1 -1
- package/list/workload.vue +4 -0
- package/mixins/chart.js +4 -1
- package/models/catalog.cattle.io.app.js +3 -1
- package/models/catalog.cattle.io.clusterrepo.js +56 -7
- package/models/fleet.cattle.io.bundle.js +0 -11
- package/models/fleet.cattle.io.cluster.js +17 -1
- package/models/fleet.cattle.io.gitrepo.js +86 -50
- package/models/provisioning.cattle.io.cluster.js +47 -2
- package/models/service.js +1 -0
- package/models/workload.js +19 -1
- package/package.json +5 -4
- package/pages/c/_cluster/apps/charts/index.vue +4 -0
- package/pages/c/_cluster/explorer/ConfigBadge.vue +8 -7
- package/pages/c/_cluster/explorer/index.vue +13 -6
- package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +3 -3
- package/pages/c/_cluster/fleet/index.vue +75 -89
- package/pages/c/_cluster/settings/links.vue +2 -2
- package/pages/diagnostic.vue +17 -15
- package/pages/home.vue +32 -6
- package/plugins/clean-html.js +50 -0
- package/plugins/dashboard-store/resource-class.js +4 -0
- package/plugins/plugin.js +54 -49
- package/plugins/steve/mutations.js +1 -1
- package/plugins/steve/steve-class.js +8 -0
- package/plugins/steve/steve-pagination-utils.ts +3 -1
- package/rancher-components/Accordion/Accordion.vue +4 -4
- package/rancher-components/BadgeState/BadgeState.vue +7 -0
- package/rancher-components/Card/Card.vue +27 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +9 -2
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +18 -1
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +39 -2
- package/rancher-components/RcButton/RcButton.vue +90 -0
- package/rancher-components/RcButton/index.ts +2 -0
- package/rancher-components/RcButton/types.ts +17 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +111 -0
- package/rancher-components/RcDropdown/RcDropdownItem.vue +127 -0
- package/rancher-components/RcDropdown/RcDropdownSeparator.vue +6 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +43 -0
- package/rancher-components/RcDropdown/index.ts +4 -0
- package/rancher-components/RcDropdown/types.ts +22 -0
- package/rancher-components/RcDropdown/useDropdownCollection.ts +45 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +83 -0
- package/scripts/test-plugins-build.sh +2 -0
- package/scripts/typegen.sh +2 -0
- package/store/catalog.js +1 -1
- package/tsconfig.json +2 -1
- package/types/components/paginatedResourceTable.ts +25 -0
- package/types/components/resourceLabeledSelect.ts +48 -0
- package/types/resources/fleet.d.ts +17 -0
- package/types/shell/index.d.ts +61 -0
- package/utils/auth.js +5 -1
- package/utils/cluster.js +106 -0
- package/utils/fleet.ts +35 -3
- package/utils/ingress.ts +64 -0
- package/utils/uiplugins.ts +56 -44
- package/utils/validators/cron-schedule.js +7 -2
- package/utils/validators/formRules/__tests__/index.test.ts +53 -17
- package/utils/validators/formRules/index.ts +20 -5
- package/vue.config.js +1 -1
- package/components/RelatedWorkloadsTable.vue +0 -50
|
@@ -8,10 +8,14 @@ import Tab from '@shell/components/Tabbed/Tab';
|
|
|
8
8
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
9
9
|
import Conditions from '@shell/components/form/Conditions';
|
|
10
10
|
import { EVENT } from '@shell/config/types';
|
|
11
|
-
import
|
|
11
|
+
import PaginatedResourceTable from '@shell/components/PaginatedResourceTable.vue';
|
|
12
12
|
import { _VIEW } from '@shell/config/query-params';
|
|
13
13
|
import RelatedResources from '@shell/components/RelatedResources';
|
|
14
14
|
import { isConditionReadyAndWaiting } from '@shell/plugins/dashboard-store/resource-class';
|
|
15
|
+
import { PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
16
|
+
import { MESSAGE, REASON } from '@shell/config/table-headers';
|
|
17
|
+
import { STEVE_EVENT_LAST_SEEN, STEVE_EVENT_TYPE, STEVE_NAME_COL } from '@shell/config/pagination-table-headers';
|
|
18
|
+
import { headerFromSchemaColString } from '@shell/store/type-map.utils';
|
|
15
19
|
|
|
16
20
|
export default {
|
|
17
21
|
|
|
@@ -21,7 +25,7 @@ export default {
|
|
|
21
25
|
Tabbed,
|
|
22
26
|
Tab,
|
|
23
27
|
Conditions,
|
|
24
|
-
|
|
28
|
+
PaginatedResourceTable,
|
|
25
29
|
RelatedResources,
|
|
26
30
|
},
|
|
27
31
|
|
|
@@ -69,14 +73,25 @@ export default {
|
|
|
69
73
|
|
|
70
74
|
data() {
|
|
71
75
|
const inStore = this.$store.getters['currentStore'](EVENT);
|
|
76
|
+
const eventSchema = this.$store.getters[`${ inStore }/schemaFor`](EVENT); // @TODO be smarter about which resources actually ever have events
|
|
72
77
|
|
|
73
78
|
return {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
selectedTab:
|
|
77
|
-
didLoadEvents: false,
|
|
79
|
+
eventSchema,
|
|
80
|
+
EVENT,
|
|
81
|
+
selectedTab: this.defaultTab,
|
|
78
82
|
inStore,
|
|
79
|
-
showConditions:
|
|
83
|
+
showConditions: false,
|
|
84
|
+
paginationHeaders: [
|
|
85
|
+
STEVE_EVENT_LAST_SEEN,
|
|
86
|
+
STEVE_EVENT_TYPE,
|
|
87
|
+
REASON,
|
|
88
|
+
headerFromSchemaColString('Subobject', eventSchema, this.$store.getters, true),
|
|
89
|
+
headerFromSchemaColString('Source', eventSchema, this.$store.getters, true),
|
|
90
|
+
MESSAGE,
|
|
91
|
+
headerFromSchemaColString('First Seen', eventSchema, this.$store.getters, true),
|
|
92
|
+
headerFromSchemaColString('Count', eventSchema, this.$store.getters, true),
|
|
93
|
+
STEVE_NAME_COL,
|
|
94
|
+
]
|
|
80
95
|
};
|
|
81
96
|
},
|
|
82
97
|
|
|
@@ -92,7 +107,7 @@ export default {
|
|
|
92
107
|
|
|
93
108
|
computed: {
|
|
94
109
|
showEvents() {
|
|
95
|
-
return this.isView && this.needEvents && this.
|
|
110
|
+
return this.isView && this.needEvents && this.eventSchema;
|
|
96
111
|
},
|
|
97
112
|
showRelated() {
|
|
98
113
|
return this.isView && this.needRelated;
|
|
@@ -128,18 +143,6 @@ export default {
|
|
|
128
143
|
},
|
|
129
144
|
];
|
|
130
145
|
},
|
|
131
|
-
events() {
|
|
132
|
-
return this.allEvents.filter((event) => {
|
|
133
|
-
return event.involvedObject?.uid === this.value?.metadata?.uid;
|
|
134
|
-
}).map((event) => {
|
|
135
|
-
return {
|
|
136
|
-
reason: (`${ event.reason || this.t('generic.unknown') }${ event.count > 1 ? ` (${ event.count })` : '' }`).trim(),
|
|
137
|
-
message: event.message || this.t('generic.unknown'),
|
|
138
|
-
date: event.lastTimestamp || event.firstTimestamp || event.metadata.creationTimestamp,
|
|
139
|
-
eventType: event.eventType
|
|
140
|
-
};
|
|
141
|
-
});
|
|
142
|
-
},
|
|
143
146
|
conditionsHaveIssues() {
|
|
144
147
|
if (this.showConditions) {
|
|
145
148
|
return this.value.status?.conditions?.filter((cond) => !isConditionReadyAndWaiting(cond)).some((cond) => cond.error);
|
|
@@ -153,15 +156,6 @@ export default {
|
|
|
153
156
|
// Ensures we only fetch events and show the table when the events tab has been activated
|
|
154
157
|
tabChange(neu) {
|
|
155
158
|
this.selectedTab = neu?.selectedName;
|
|
156
|
-
|
|
157
|
-
if (!this.didLoadEvents && this.selectedTab === 'events') {
|
|
158
|
-
const inStore = this.$store.getters['currentStore'](EVENT);
|
|
159
|
-
|
|
160
|
-
this.$store.dispatch(`${ inStore }/findAll`, { type: EVENT }).then((events) => {
|
|
161
|
-
this.allEvents = events;
|
|
162
|
-
this.didLoadEvents = true;
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
159
|
},
|
|
166
160
|
|
|
167
161
|
/**
|
|
@@ -180,6 +174,54 @@ export default {
|
|
|
180
174
|
this.showConditions = this.$store.getters[`${ this.inStore }/pathExistsInSchema`](this.value.type, 'status.conditions');
|
|
181
175
|
}
|
|
182
176
|
},
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Filter out hidden repos from list of all repos
|
|
180
|
+
*/
|
|
181
|
+
filterEventsLocal(rows) {
|
|
182
|
+
return rows.filter((event) => event.involvedObject?.uid === this.value?.metadata?.uid);
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Filter out hidden repos via api
|
|
187
|
+
*
|
|
188
|
+
* pagination: PaginationArgs
|
|
189
|
+
* returns: PaginationArgs
|
|
190
|
+
*/
|
|
191
|
+
filterEventsApi(pagination) {
|
|
192
|
+
if (!pagination.filters) {
|
|
193
|
+
pagination.filters = [];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const field = `involvedObject.uid`; // Pending API Support - https://github.com/rancher/rancher/issues/48603
|
|
197
|
+
|
|
198
|
+
// of type PaginationParamFilter
|
|
199
|
+
let existing = null;
|
|
200
|
+
|
|
201
|
+
for (let i = 0; i < pagination.filters.length; i++) {
|
|
202
|
+
const filter = pagination.filters[i];
|
|
203
|
+
|
|
204
|
+
if (!!filter.fields.find((f) => f.field === field)) {
|
|
205
|
+
existing = filter;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const required = PaginationParamFilter.createSingleField({
|
|
211
|
+
field,
|
|
212
|
+
exact: true,
|
|
213
|
+
value: this.value.metadata.uid,
|
|
214
|
+
equals: true
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!!existing) {
|
|
218
|
+
Object.assign(existing, required);
|
|
219
|
+
} else {
|
|
220
|
+
pagination.filters.push(required);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return pagination;
|
|
224
|
+
}
|
|
183
225
|
}
|
|
184
226
|
};
|
|
185
227
|
</script>
|
|
@@ -208,15 +250,16 @@ export default {
|
|
|
208
250
|
name="events"
|
|
209
251
|
:weight="-2"
|
|
210
252
|
>
|
|
211
|
-
|
|
253
|
+
<!-- namespaced: false given we don't want the default handling of namespaced resource (apply header filter) -->
|
|
254
|
+
<PaginatedResourceTable
|
|
212
255
|
v-if="selectedTab === 'events'"
|
|
213
|
-
:
|
|
256
|
+
:schema="eventSchema"
|
|
257
|
+
:local-filter="filterEventsLocal"
|
|
258
|
+
:api-filter="filterEventsApi"
|
|
259
|
+
:use-query-params-for-simple-filtering="false"
|
|
214
260
|
:headers="eventHeaders"
|
|
215
|
-
|
|
216
|
-
:
|
|
217
|
-
:table-actions="false"
|
|
218
|
-
:row-actions="false"
|
|
219
|
-
default-sort-by="date"
|
|
261
|
+
:paginationHeaders="paginationHeaders"
|
|
262
|
+
:namespaced="false"
|
|
220
263
|
/>
|
|
221
264
|
</Tab>
|
|
222
265
|
|
|
@@ -70,7 +70,7 @@ export default {
|
|
|
70
70
|
secrets: null,
|
|
71
71
|
SECRET,
|
|
72
72
|
allSecretsSettings: {
|
|
73
|
-
|
|
73
|
+
updateResources: (secrets) => {
|
|
74
74
|
const allSecretsInNamespace = secrets.filter((secret) => this.types.includes(secret._type) && secret.namespace === this.namespace);
|
|
75
75
|
const mappedSecrets = this.mapSecrets(allSecretsInNamespace.sort((a, b) => a.name.localeCompare(b.name)));
|
|
76
76
|
|
|
@@ -81,7 +81,7 @@ export default {
|
|
|
81
81
|
},
|
|
82
82
|
paginateSecretsSetting: {
|
|
83
83
|
requestSettings: this.paginatePageOptions,
|
|
84
|
-
|
|
84
|
+
updateResources: (secrets) => {
|
|
85
85
|
const mappedSecrets = this.mapSecrets(secrets);
|
|
86
86
|
|
|
87
87
|
this.secrets = secrets; // We need the key from the selected secret. When paginating we won't touch the store, so just pass back here
|
|
@@ -127,7 +127,7 @@ describe('component: KeyValue', () => {
|
|
|
127
127
|
expect(secondKeyInput.exists()).toBe(false);
|
|
128
128
|
expect(secondValueInput.exists()).toBe(false);
|
|
129
129
|
|
|
130
|
-
const addButton = wrapper.find('[data-testid="
|
|
130
|
+
const addButton = wrapper.find('[data-testid="add_row_item_button"]');
|
|
131
131
|
|
|
132
132
|
addButton.trigger('click');
|
|
133
133
|
await nextTick();
|
|
@@ -11,7 +11,7 @@ export default {
|
|
|
11
11
|
required: true
|
|
12
12
|
},
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
clusterId: {
|
|
15
15
|
type: String,
|
|
16
16
|
required: true
|
|
17
17
|
}
|
|
@@ -22,6 +22,6 @@ export default {
|
|
|
22
22
|
<template>
|
|
23
23
|
<FleetSummaryGraph
|
|
24
24
|
:row="row"
|
|
25
|
-
:
|
|
25
|
+
:clusterId="clusterId"
|
|
26
26
|
/>
|
|
27
27
|
</template>
|
|
@@ -14,7 +14,7 @@ export default {
|
|
|
14
14
|
required: true
|
|
15
15
|
},
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
clusterId: {
|
|
18
18
|
type: String,
|
|
19
19
|
required: false,
|
|
20
20
|
default: null,
|
|
@@ -23,10 +23,8 @@ export default {
|
|
|
23
23
|
|
|
24
24
|
computed: {
|
|
25
25
|
summary() {
|
|
26
|
-
if (this.
|
|
27
|
-
return this.row.
|
|
28
|
-
return x.clusterLabel === this.clusterLabel;
|
|
29
|
-
})?.status.resourceCounts || {};
|
|
26
|
+
if (this.clusterId) {
|
|
27
|
+
return this.row.statusResourceCountsForCluster(this.clusterId);
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
return this.row.status?.resourceCounts || {};
|
|
@@ -37,7 +35,8 @@ export default {
|
|
|
37
35
|
},
|
|
38
36
|
|
|
39
37
|
stateParts() {
|
|
40
|
-
const
|
|
38
|
+
const summary = this.summary;
|
|
39
|
+
const keys = Object.keys(summary).filter((x) => !x.startsWith('desired'));
|
|
41
40
|
|
|
42
41
|
const out = keys.map((key) => {
|
|
43
42
|
const textColor = colorForState(key);
|
|
@@ -46,7 +45,7 @@ export default {
|
|
|
46
45
|
label: ucFirst(key),
|
|
47
46
|
color: textColor.replace(/text-/, 'bg-'),
|
|
48
47
|
textColor,
|
|
49
|
-
value:
|
|
48
|
+
value: summary[key],
|
|
50
49
|
sort: stateSort(textColor, key),
|
|
51
50
|
};
|
|
52
51
|
}).filter((x) => x.value > 0);
|
|
@@ -182,14 +182,21 @@ export default {
|
|
|
182
182
|
<div
|
|
183
183
|
id="trigger"
|
|
184
184
|
class="hs-popover__trigger"
|
|
185
|
+
aria-role="button"
|
|
186
|
+
tabindex="0"
|
|
185
187
|
:class="{expanded}"
|
|
188
|
+
:aria-roledescription="t('workload.scaleWorkloads')"
|
|
189
|
+
:aria-label="t('workload.healthScaleToggle')"
|
|
190
|
+
:aria-expanded="expanded"
|
|
186
191
|
@click="expanded = !expanded"
|
|
192
|
+
@keyup.enter.space="expanded = !expanded"
|
|
187
193
|
>
|
|
188
194
|
<ProgressBarMulti
|
|
189
195
|
v-if="parts"
|
|
190
196
|
class="health"
|
|
191
197
|
:values="parts"
|
|
192
198
|
:show-zeros="true"
|
|
199
|
+
:aria-describedby="t('workload.healthWorkloads')"
|
|
193
200
|
/>
|
|
194
201
|
<i :class="{icon: true, 'icon-chevron-up': expanded, 'icon-chevron-down': !expanded}" />
|
|
195
202
|
</div>
|
package/components/nav/Group.vue
CHANGED
|
@@ -211,26 +211,40 @@ export default {
|
|
|
211
211
|
v-if="showHeader"
|
|
212
212
|
class="header"
|
|
213
213
|
:class="{'active': isOverview, 'noHover': !canCollapse}"
|
|
214
|
+
role="button"
|
|
215
|
+
tabindex="0"
|
|
216
|
+
:aria-label="group.labelDisplay || group.label || ''"
|
|
214
217
|
@click="groupSelected()"
|
|
218
|
+
@keyup.enter="groupSelected()"
|
|
219
|
+
@keyup.space="groupSelected()"
|
|
215
220
|
>
|
|
216
221
|
<slot name="header">
|
|
217
222
|
<router-link
|
|
218
223
|
v-if="hasOverview"
|
|
219
224
|
:to="group.children[0].route"
|
|
220
225
|
:exact="group.children[0].exact"
|
|
226
|
+
:tabindex="-1"
|
|
221
227
|
>
|
|
222
|
-
<h6
|
|
228
|
+
<h6>
|
|
229
|
+
<span v-clean-html="group.labelDisplay || group.label" />
|
|
230
|
+
</h6>
|
|
223
231
|
</router-link>
|
|
224
232
|
<h6
|
|
225
233
|
v-else
|
|
226
|
-
|
|
227
|
-
|
|
234
|
+
>
|
|
235
|
+
<span v-clean-html="group.labelDisplay || group.label" />
|
|
236
|
+
</h6>
|
|
228
237
|
</slot>
|
|
229
238
|
<i
|
|
230
239
|
v-if="!onlyHasOverview && canCollapse"
|
|
231
|
-
class="icon toggle"
|
|
240
|
+
class="icon toggle toggle-accordion"
|
|
232
241
|
:class="{'icon-chevron-right': !isExpanded, 'icon-chevron-down': isExpanded}"
|
|
242
|
+
role="button"
|
|
243
|
+
tabindex="0"
|
|
244
|
+
:aria-label="t('nav.ariaLabel.collapseExpand')"
|
|
233
245
|
@click="peek($event, true)"
|
|
246
|
+
@keyup.enter="peek($event, true)"
|
|
247
|
+
@keyup.space="peek($event, true)"
|
|
234
248
|
/>
|
|
235
249
|
</div>
|
|
236
250
|
<ul
|
|
@@ -288,6 +302,7 @@ export default {
|
|
|
288
302
|
cursor: pointer;
|
|
289
303
|
color: var(--body-text);
|
|
290
304
|
height: 33px;
|
|
305
|
+
outline: none;
|
|
291
306
|
|
|
292
307
|
H6 {
|
|
293
308
|
color: var(--body-text);
|
|
@@ -315,6 +330,17 @@ export default {
|
|
|
315
330
|
|
|
316
331
|
.accordion {
|
|
317
332
|
.header {
|
|
333
|
+
&:focus-visible {
|
|
334
|
+
h6 span {
|
|
335
|
+
@include focus-outline;
|
|
336
|
+
outline-offset: 2px;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
.toggle-accordion:focus-visible {
|
|
340
|
+
@include focus-outline;
|
|
341
|
+
outline-offset: -6px;
|
|
342
|
+
}
|
|
343
|
+
|
|
318
344
|
&.active {
|
|
319
345
|
color: var(--primary-hover-text);
|
|
320
346
|
background-color: var(--primary-hover-bg);
|
|
@@ -24,6 +24,12 @@ import IconOrSvg from '@shell/components/IconOrSvg';
|
|
|
24
24
|
import { wait } from '@shell/utils/async';
|
|
25
25
|
import { configType } from '@shell/models/management.cattle.io.authconfig';
|
|
26
26
|
import HeaderPageActionMenu from './HeaderPageActionMenu.vue';
|
|
27
|
+
import {
|
|
28
|
+
RcDropdown,
|
|
29
|
+
RcDropdownItem,
|
|
30
|
+
RcDropdownSeparator,
|
|
31
|
+
RcDropdownTrigger
|
|
32
|
+
} from '@components/RcDropdown';
|
|
27
33
|
|
|
28
34
|
export default {
|
|
29
35
|
|
|
@@ -39,6 +45,10 @@ export default {
|
|
|
39
45
|
IconOrSvg,
|
|
40
46
|
AppModal,
|
|
41
47
|
HeaderPageActionMenu,
|
|
48
|
+
RcDropdown,
|
|
49
|
+
RcDropdownItem,
|
|
50
|
+
RcDropdownSeparator,
|
|
51
|
+
RcDropdownTrigger,
|
|
42
52
|
},
|
|
43
53
|
|
|
44
54
|
props: {
|
|
@@ -641,27 +651,18 @@ export default {
|
|
|
641
651
|
</button>
|
|
642
652
|
</div>
|
|
643
653
|
|
|
644
|
-
<
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
class="user user-menu"
|
|
650
|
-
data-testid="nav_header_showUserMenu"
|
|
651
|
-
tabindex="0"
|
|
652
|
-
@blur="showMenu(false)"
|
|
653
|
-
@click="showMenu(true)"
|
|
654
|
-
@focus.capture="showMenu(true)"
|
|
655
|
-
>
|
|
656
|
-
<v-dropdown
|
|
657
|
-
:triggers="[]"
|
|
658
|
-
:shown="isUserMenuOpen"
|
|
659
|
-
:autoHide="false"
|
|
660
|
-
:flip="false"
|
|
661
|
-
:container="false"
|
|
662
|
-
:placement="'bottom-end'"
|
|
654
|
+
<div class="center-self">
|
|
655
|
+
<header-page-action-menu v-if="showPageActions" />
|
|
656
|
+
<rc-dropdown
|
|
657
|
+
v-if="showUserMenu"
|
|
658
|
+
:aria-label="t('nav.userMenu.label')"
|
|
663
659
|
>
|
|
664
|
-
<
|
|
660
|
+
<rc-dropdown-trigger
|
|
661
|
+
ghost
|
|
662
|
+
small
|
|
663
|
+
data-testid="nav_header_showUserMenu"
|
|
664
|
+
:aria-label="t('nav.userMenu.button.label')"
|
|
665
|
+
>
|
|
665
666
|
<img
|
|
666
667
|
v-if="principal && principal.avatarSrc"
|
|
667
668
|
:src="principal.avatarSrc"
|
|
@@ -673,88 +674,47 @@ export default {
|
|
|
673
674
|
v-else
|
|
674
675
|
class="icon icon-user icon-3x avatar"
|
|
675
676
|
/>
|
|
676
|
-
</
|
|
677
|
-
<template #
|
|
678
|
-
<
|
|
679
|
-
class="user-
|
|
677
|
+
</rc-dropdown-trigger>
|
|
678
|
+
<template #dropdownCollection>
|
|
679
|
+
<template v-if="authEnabled">
|
|
680
|
+
<div class="user-info">
|
|
681
|
+
<div class="user-name">
|
|
682
|
+
<i class="icon icon-lg icon-user" /> {{ principal.loginName }}
|
|
683
|
+
</div>
|
|
684
|
+
<div class="text-small">
|
|
685
|
+
<template v-if="principal.loginName !== principal.name">
|
|
686
|
+
{{ principal.name }}
|
|
687
|
+
</template>
|
|
688
|
+
</div>
|
|
689
|
+
</div>
|
|
690
|
+
<rc-dropdown-separator />
|
|
691
|
+
</template>
|
|
692
|
+
<rc-dropdown-item
|
|
693
|
+
v-if="showPreferencesLink"
|
|
694
|
+
@click="$router.push({ name: 'prefs'})"
|
|
680
695
|
>
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
v-slot="{ href, navigate }"
|
|
702
|
-
custom
|
|
703
|
-
:to="{name: 'prefs'}"
|
|
704
|
-
>
|
|
705
|
-
<li
|
|
706
|
-
class="user-menu-item"
|
|
707
|
-
@click="navigate"
|
|
708
|
-
@keypress.enter="navigate"
|
|
709
|
-
>
|
|
710
|
-
<a :href="href">{{ t('nav.userMenu.preferences') }}</a>
|
|
711
|
-
</li>
|
|
712
|
-
</router-link>
|
|
713
|
-
<router-link
|
|
714
|
-
v-if="showAccountAndApiKeyLink"
|
|
715
|
-
v-slot="{ href, navigate }"
|
|
716
|
-
custom
|
|
717
|
-
:to="{name: 'account'}"
|
|
718
|
-
>
|
|
719
|
-
<li
|
|
720
|
-
class="user-menu-item"
|
|
721
|
-
@click="navigate"
|
|
722
|
-
@keypress.enter="navigate"
|
|
723
|
-
>
|
|
724
|
-
<a :href="href">{{ t('nav.userMenu.accountAndKeys', {}, true) }}</a>
|
|
725
|
-
</li>
|
|
726
|
-
</router-link>
|
|
727
|
-
<!-- SLO modal -->
|
|
728
|
-
<li
|
|
729
|
-
v-if="authEnabled && shouldShowSloLogoutModal"
|
|
730
|
-
class="user-menu-item no-link"
|
|
731
|
-
@click="showSloModal"
|
|
732
|
-
@keypress.enter="showSloModal"
|
|
733
|
-
>
|
|
734
|
-
<span>{{ t('nav.userMenu.logOut') }}</span>
|
|
735
|
-
</li>
|
|
736
|
-
<!-- logout -->
|
|
737
|
-
<router-link
|
|
738
|
-
v-else-if="authEnabled"
|
|
739
|
-
v-slot="{ href, navigate }"
|
|
740
|
-
custom
|
|
741
|
-
:to="generateLogoutRoute"
|
|
742
|
-
>
|
|
743
|
-
<li
|
|
744
|
-
class="user-menu-item"
|
|
745
|
-
@click="navigate"
|
|
746
|
-
@keypress.enter="navigate"
|
|
747
|
-
>
|
|
748
|
-
<a
|
|
749
|
-
:href="href"
|
|
750
|
-
@blur="showMenu(false)"
|
|
751
|
-
>{{ t('nav.userMenu.logOut') }}</a>
|
|
752
|
-
</li>
|
|
753
|
-
</router-link>
|
|
754
|
-
</ul>
|
|
755
|
-
</div>
|
|
696
|
+
{{ t('nav.userMenu.preferences') }}
|
|
697
|
+
</rc-dropdown-item>
|
|
698
|
+
<rc-dropdown-item
|
|
699
|
+
v-if="showAccountAndApiKeyLink"
|
|
700
|
+
@click="$router.push({ name: 'account'})"
|
|
701
|
+
>
|
|
702
|
+
{{ t('nav.userMenu.accountAndKeys', {}, true) }}
|
|
703
|
+
</rc-dropdown-item>
|
|
704
|
+
<rc-dropdown-item
|
|
705
|
+
v-if="authEnabled && shouldShowSloLogoutModal"
|
|
706
|
+
@click="showSloModal"
|
|
707
|
+
>
|
|
708
|
+
{{ t('nav.userMenu.logOut') }}
|
|
709
|
+
</rc-dropdown-item>
|
|
710
|
+
<rc-dropdown-item
|
|
711
|
+
v-else-if="authEnabled"
|
|
712
|
+
@click="$router.push(generateLogoutRoute)"
|
|
713
|
+
>
|
|
714
|
+
{{ t('nav.userMenu.logOut') }}
|
|
715
|
+
</rc-dropdown-item>
|
|
756
716
|
</template>
|
|
757
|
-
</
|
|
717
|
+
</rc-dropdown>
|
|
758
718
|
</div>
|
|
759
719
|
</div>
|
|
760
720
|
</header>
|
|
@@ -957,7 +917,7 @@ export default {
|
|
|
957
917
|
width: 40px;
|
|
958
918
|
}
|
|
959
919
|
|
|
960
|
-
:deep()
|
|
920
|
+
:deep() div .btn.role-tertiary {
|
|
961
921
|
border: 1px solid var(--header-btn-bg);
|
|
962
922
|
border: none;
|
|
963
923
|
background: var(--header-btn-bg);
|
|
@@ -1010,8 +970,9 @@ export default {
|
|
|
1010
970
|
position: relative;
|
|
1011
971
|
}
|
|
1012
972
|
|
|
1013
|
-
.
|
|
1014
|
-
|
|
973
|
+
.avatar-round {
|
|
974
|
+
border: 0;
|
|
975
|
+
border-radius: 50%;
|
|
1015
976
|
}
|
|
1016
977
|
|
|
1017
978
|
> .user {
|
|
@@ -1047,11 +1008,14 @@ export default {
|
|
|
1047
1008
|
}
|
|
1048
1009
|
|
|
1049
1010
|
background-color: var(--header-bg);
|
|
1011
|
+
}
|
|
1050
1012
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1013
|
+
> .center-self {
|
|
1014
|
+
align-self: center;
|
|
1015
|
+
display: flex;
|
|
1016
|
+
gap: 1rem;
|
|
1017
|
+
align-items: center;
|
|
1018
|
+
padding-right: 1rem;
|
|
1055
1019
|
}
|
|
1056
1020
|
}
|
|
1057
1021
|
}
|
|
@@ -1063,14 +1027,17 @@ export default {
|
|
|
1063
1027
|
justify-content: space-between;
|
|
1064
1028
|
padding: 10px;
|
|
1065
1029
|
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1066
1032
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1033
|
+
div {
|
|
1034
|
+
&.user-info {
|
|
1035
|
+
padding: 0 8px;
|
|
1036
|
+
margin: 0 9px;
|
|
1037
|
+
min-width: 200px;
|
|
1038
|
+
display: flex;
|
|
1039
|
+
gap: 5px;
|
|
1040
|
+
flex-direction: column;
|
|
1074
1041
|
}
|
|
1075
1042
|
}
|
|
1076
1043
|
|
|
@@ -1152,4 +1119,5 @@ export default {
|
|
|
1152
1119
|
}
|
|
1153
1120
|
}
|
|
1154
1121
|
}
|
|
1122
|
+
|
|
1155
1123
|
</style>
|