From ef25d297d60d077b0886a99ab846b89df7073e42 Mon Sep 17 00:00:00 2001 From: Karl Persson Date: Mon, 22 Aug 2022 14:21:12 +0200 Subject: [PATCH] RBAC: Display groups for custom roles (#54020) * RolePicker: Default to "Other" for roles without group * RolePicker: Add GroupType enum and calculate group options based on group type * RolePicker: Display groups for custom roles * RolePicker: Remove unused code * RolePicker: Restructure Co-authored-by: Alex Khomenko --- .../components/RolePicker/RolePickerMenu.tsx | 201 +++++++++++------- 1 file changed, 119 insertions(+), 82 deletions(-) diff --git a/public/app/core/components/RolePicker/RolePickerMenu.tsx b/public/app/core/components/RolePicker/RolePickerMenu.tsx index 925bc841771..f65bf33be19 100644 --- a/public/app/core/components/RolePicker/RolePickerMenu.tsx +++ b/public/app/core/components/RolePicker/RolePickerMenu.tsx @@ -19,6 +19,11 @@ import { OrgRole, Role } from 'app/types'; import { MENU_MAX_HEIGHT } from './constants'; +enum GroupType { + fixed = 'fixed', + custom = 'custom', +} + const BasicRoles = Object.values(OrgRole); const BasicRoleOption: Array> = BasicRoles.map((r) => ({ label: r, @@ -94,15 +99,15 @@ export const RolePickerMenu = ({ return selectedGroupOptions; }; - const groupSelected = (group: string) => { + const groupSelected = (groupType: GroupType, group: string) => { const selectedGroupOptions = getSelectedGroupOptions(group); - const groupOptions = optionGroups.find((g) => g.value === group); + const groupOptions = optionGroups[groupType].find((g) => g.value === group); return selectedGroupOptions.length > 0 && selectedGroupOptions.length >= groupOptions!.options.length; }; - const groupPartiallySelected = (group: string) => { + const groupPartiallySelected = (groupType: GroupType, group: string) => { const selectedGroupOptions = getSelectedGroupOptions(group); - const groupOptions = optionGroups.find((g) => g.value === group); + const groupOptions = optionGroups[groupType].find((g) => g.value === group); return selectedGroupOptions.length > 0 && selectedGroupOptions.length < groupOptions!.options.length; }; @@ -114,11 +119,11 @@ export const RolePickerMenu = ({ } }; - const onGroupChange = (value: string) => { - const group = optionGroups.find((g) => { + const onGroupChange = (groupType: GroupType, value: string) => { + const group = optionGroups[groupType].find((g) => { return g.value === value; }); - if (groupSelected(value) || groupPartiallySelected(value)) { + if (groupSelected(groupType, value) || groupPartiallySelected(groupType, value)) { if (group) { setSelectedOptions(selectedOptions.filter((role) => !group.options.find((option) => role.uid === option.uid))); } @@ -131,10 +136,10 @@ export const RolePickerMenu = ({ } }; - const onOpenSubMenu = (value: string) => { + const onOpenSubMenu = (groupType: GroupType, value: string) => { setOpenedMenuGroup(value); setShowSubMenu(true); - const group = optionGroups.find((g) => { + const group = optionGroups[groupType].find((g) => { return g.value === value; }); if (group) { @@ -165,12 +170,6 @@ export const RolePickerMenu = ({ }; const onUpdateInternal = () => { - const selectedCustomRoles: string[] = []; - // TODO: needed? - for (const key in selectedOptions) { - const roleUID = selectedOptions[key]?.uid; - selectedCustomRoles.push(roleUID); - } onUpdate(selectedOptions, selectedBuiltInRole); }; @@ -201,68 +200,93 @@ export const RolePickerMenu = ({ /> )} - {!!fixedRoles.length && - (showGroups && !!optionGroups.length ? ( -
-
Fixed roles
-
- {optionGroups.map((option, i) => ( - - {showSubMenu && openedMenuGroup === option.value && ( - 0} - /> - )} - - ))} -
+ {!!fixedRoles.length && ( +
+
Fixed roles
+
+ {showGroups && !!optionGroups.fixed.length + ? optionGroups.fixed.map((option, i) => ( + onGroupChange(GroupType.fixed, group)} + onOpenSubMenu={(group: string) => onOpenSubMenu(GroupType.fixed, group)} + onCloseSubMenu={onCloseSubMenu} + root={subMenuNode?.current!} + isFocused={showSubMenu && openedMenuGroup === option.value} + > + {showSubMenu && openedMenuGroup === option.value && ( + 0} + /> + )} + + )) + : fixedRoles.map((option, i) => ( + opt.uid === option.uid))} + disabled={isNotDelegatable(option)} + onChange={onChange} + hideDescription + /> + ))}
- ) : ( -
-
Fixed roles
-
- {fixedRoles.map((option, i) => ( - opt.uid === option.uid))} - disabled={isNotDelegatable(option)} - onChange={onChange} - hideDescription - /> - ))} -
-
- ))} +
+ )} {!!customRoles.length && ( -
+
Custom roles
- {customRoles.map((option, i) => ( - opt.uid === option.uid))} - disabled={isNotDelegatable(option)} - onChange={onChange} - hideDescription - /> - ))} + {showGroups && !!optionGroups.custom.length + ? optionGroups.custom.map((option, i) => ( + onGroupChange(GroupType.custom, group)} + onOpenSubMenu={(group: string) => onOpenSubMenu(GroupType.custom, group)} + onCloseSubMenu={onCloseSubMenu} + root={subMenuNode?.current!} + isFocused={showSubMenu && openedMenuGroup === option.value} + > + {showSubMenu && openedMenuGroup === option.value && ( + 0} + /> + )} + + )) + : customRoles.map((option, i) => ( + opt.uid === option.uid))} + disabled={isNotDelegatable(option)} + onChange={onChange} + hideDescription + /> + ))}
)} @@ -288,15 +312,14 @@ const filterFixedRoles = (option: Role) => option.name?.startsWith('fixed:'); const getOptionGroups = (options: Role[]) => { const groupsMap: { [key: string]: Role[] } = {}; + const customGroupsMap: { [key: string]: Role[] } = {}; options.forEach((role) => { - if (role.name.startsWith('fixed:')) { - const groupName = getRoleGroup(role); - if (groupsMap[groupName]) { - groupsMap[groupName].push(role); - } else { - groupsMap[groupName] = [role]; - } + const m = role.name.startsWith('fixed:') ? groupsMap : customGroupsMap; + const groupName = getRoleGroup(role); + if (!m[groupName]) { + m[groupName] = []; } + m[groupName].push(role); }); const groups = []; @@ -308,7 +331,21 @@ const getOptionGroups = (options: Role[]) => { options: groupOptions, }); } - return groups.sort((a, b) => a.name.localeCompare(b.name)); + + const customGroups = []; + for (const groupName of Object.keys(customGroupsMap)) { + const groupOptions = customGroupsMap[groupName].sort(sortRolesByName); + customGroups.push({ + name: capitalize(groupName), + value: groupName, + options: groupOptions, + }); + } + + return { + fixed: groups.sort((a, b) => a.name.localeCompare(b.name)), + custom: customGroups.sort((a, b) => a.name.localeCompare(b.name)), + }; }; interface RolePickerSubMenuProps { @@ -527,7 +564,7 @@ export const RoleMenuGroupOption = React.forwardRef { - return role.group ?? 'Other'; + return role.group || 'Other'; }; const capitalize = (s: string): string => {