From 2fbe99c1beaa6018bbaaa7c03edde0b2d7171230 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Wed, 15 Jun 2022 09:51:32 +0300 Subject: [PATCH] RolePicker: Fix submenu position on horizontal space overflow (#50769) --- .../core/components/RolePicker/RolePicker.tsx | 22 ++++++++----- .../components/RolePicker/RolePickerMenu.tsx | 31 +++++++++++++------ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/public/app/core/components/RolePicker/RolePicker.tsx b/public/app/core/components/RolePicker/RolePicker.tsx index 439ddec4f8e..e850cf1a355 100644 --- a/public/app/core/components/RolePicker/RolePicker.tsx +++ b/public/app/core/components/RolePicker/RolePicker.tsx @@ -5,7 +5,7 @@ import { Role, OrgRole } from 'app/types'; import { RolePickerInput } from './RolePickerInput'; import { RolePickerMenu } from './RolePickerMenu'; -import { MENU_MAX_HEIGHT } from './constants'; +import { MENU_MAX_HEIGHT, ROLE_PICKER_WIDTH } from './constants'; export interface Props { builtInRole?: OrgRole; @@ -37,7 +37,7 @@ export const RolePicker = ({ const [selectedRoles, setSelectedRoles] = useState(appliedRoles); const [selectedBuiltInRole, setSelectedBuiltInRole] = useState(builtInRole); const [query, setQuery] = useState(''); - const [offset, setOffset] = useState(0); + const [offset, setOffset] = useState({ vertical: 0, horizontal: 0 }); const ref = useRef(null); useEffect(() => { @@ -50,14 +50,22 @@ export const RolePicker = ({ if (!dimensions || !isOpen) { return; } - const { bottom, top } = dimensions; + const { bottom, top, left, right } = dimensions; const distance = window.innerHeight - bottom; - const offset = bottom - top + 10; // Add extra 10px to offset to account for border and outline + const offsetVertical = bottom - top + 10; // Add extra 10px to offset to account for border and outline + const offsetHorizontal = right - left; + let horizontal = -offsetHorizontal; + let vertical = -offsetVertical; + if (distance < MENU_MAX_HEIGHT + 20) { - setOffset(offset); - } else { - setOffset(-offset); + vertical = offsetVertical; } + + if (window.innerWidth - right < ROLE_PICKER_WIDTH) { + horizontal = offsetHorizontal; + } + + setOffset({ horizontal, vertical }); }, [isOpen, selectedRoles]); const onOpen = useCallback( diff --git a/public/app/core/components/RolePicker/RolePickerMenu.tsx b/public/app/core/components/RolePicker/RolePickerMenu.tsx index 29487fae656..a85fd804381 100644 --- a/public/app/core/components/RolePicker/RolePickerMenu.tsx +++ b/public/app/core/components/RolePicker/RolePickerMenu.tsx @@ -42,7 +42,7 @@ interface RolePickerMenuProps { onUpdate: (newRoles: Role[], newBuiltInRole?: OrgRole) => void; onClear?: () => void; updateDisabled?: boolean; - offset: number; + offset: { vertical: number; horizontal: number }; } export const RolePickerMenu = ({ @@ -181,9 +181,10 @@ export const RolePickerMenu = ({ className={cx( styles.menu, customStyles.menuWrapper, + { [customStyles.menuLeft]: offset.horizontal > 0 }, css` - bottom: ${offset > 0 ? `${offset}px` : 'unset'}; - top: ${offset < 0 ? `${Math.abs(offset)}px` : 'unset'}; + bottom: ${offset.vertical > 0 ? `${offset.vertical}px` : 'unset'}; + top: ${offset.vertical < 0 ? `${Math.abs(offset.vertical)}px` : 'unset'}; ` )} > @@ -226,6 +227,7 @@ export const RolePickerMenu = ({ selectedOptions={selectedOptions} onSelect={onChange} onClear={onClearSubMenu} + showOnLeft={offset.horizontal > 0} /> )} @@ -278,7 +280,7 @@ export const RolePickerMenu = ({ -
+
); }; @@ -317,6 +319,7 @@ interface RolePickerSubMenuProps { disabledOptions?: Role[]; onSelect: (option: Role) => void; onClear?: () => void; + showOnLeft?: boolean; } export const RolePickerSubMenu = ({ @@ -325,6 +328,7 @@ export const RolePickerSubMenu = ({ disabledOptions, onSelect, onClear, + showOnLeft, }: RolePickerSubMenuProps): JSX.Element => { const theme = useTheme2(); const styles = getSelectStyles(theme); @@ -337,7 +341,10 @@ export const RolePickerSubMenu = ({ }; return ( -
+
{options.map((option, i) => ( @@ -506,7 +513,7 @@ export const RoleMenuGroupOption = React.forwardRef
{data.displayName || data.name} - +
{root && children && ( @@ -552,19 +559,25 @@ export const getStyles = (theme: GrafanaTheme2) => { padding-top: ${theme.spacing(1)}; } `, + menuLeft: css` + right: 0; + flex-direction: row-reverse; + `, subMenu: css` height: 100%; min-width: 260px; display: flex; flex-direction: column; - border-left-style: solid; - border-left-width: 1px; - border-left-color: ${theme.components.input.borderColor}; + border-left: 1px solid ${theme.components.input.borderColor}; & > div { padding-top: ${theme.spacing(1)}; } `, + subMenuLeft: css` + border-right: 1px solid ${theme.components.input.borderColor}; + border-left: unset; + `, groupHeader: css` padding: ${theme.spacing(0, 4)}; display: flex;