Fixed Schedules modals dragging/initial positioning (#4953)

# What this PR does

Closes https://github.com/grafana/oncall/issues/4944
This commit is contained in:
Rares Mardare 2024-08-29 14:16:10 +03:00 committed by GitHub
parent bd919f6e74
commit 754fa8728e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 55 additions and 15 deletions

View file

@ -191,26 +191,24 @@ export function getDraggableModalCoordinatesOnInit(
return undefined;
}
const scrollBarReferenceElements = document.querySelectorAll<HTMLElement>('.scrollbar-view');
// top navbar display has 2 scrollbar-view elements (navbar & content)
const baseReferenceElRect = (
scrollBarReferenceElements.length === 1 ? scrollBarReferenceElements[0] : scrollBarReferenceElements[1]
).getBoundingClientRect();
const body = document.body;
const baseReferenceElRect = body.getBoundingClientRect();
const { innerHeight } = window;
const { right, bottom } = baseReferenceElRect;
return isTopNavbar()
? {
// values are adjusted by any padding/margin differences
left: -data.node.offsetLeft + 4,
left: -data.node.offsetLeft + 12,
right: right - (data.node.offsetLeft + data.node.offsetWidth) - 12,
top: -offsetTop + GRAFANA_HEADER_HEIGHT + 4,
bottom: bottom - data.node.offsetHeight - offsetTop - 12,
top: -offsetTop + GRAFANA_HEADER_HEIGHT + 12,
bottom: innerHeight - data.node.offsetHeight - offsetTop - 12,
}
: {
left: -data.node.offsetLeft + 4 + GRAFANA_LEGACY_SIDEBAR_WIDTH,
left: -data.node.offsetLeft + 12 + GRAFANA_LEGACY_SIDEBAR_WIDTH,
right: right - (data.node.offsetLeft + data.node.offsetWidth) - 12,
top: -offsetTop + 4,
top: -offsetTop + 12,
bottom: bottom - data.node.offsetHeight - offsetTop - 12,
};
}

View file

@ -809,7 +809,8 @@ export const RotationForm = observer((props: RotationFormProps) => {
return;
}
setDraggableBounds(getDraggableModalCoordinatesOnInit(data, offsetTop));
const bounds = getDraggableModalCoordinatesOnInit(data, offsetTop);
setDraggableBounds(bounds);
}
});

View file

@ -3,20 +3,23 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Button, Field, IconButton, Input, TextArea, Stack } from '@grafana/ui';
import cn from 'classnames/bind';
import dayjs from 'dayjs';
import Draggable from 'react-draggable';
import Draggable, { DraggableData, DraggableEvent } from 'react-draggable';
import { Modal } from 'components/Modal/Modal';
import { Tag } from 'components/Tag/Tag';
import { Text } from 'components/Text/Text';
import { WithConfirm } from 'components/WithConfirm/WithConfirm';
import { calculateScheduleFormOffset } from 'containers/Rotations/Rotations.helpers';
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
import { SHIFT_SWAP_COLOR } from 'models/schedule/schedule.helpers';
import { Schedule, ShiftSwap } from 'models/schedule/schedule.types';
import { getUTCString } from 'pages/schedule/Schedule.helpers';
import { useStore } from 'state/useStore';
import { UserActions } from 'utils/authorization/authorization';
import { StackSize } from 'utils/consts';
import { GRAFANA_HEADER_HEIGHT, StackSize } from 'utils/consts';
import { useDebouncedCallback, useResize } from 'utils/hooks';
import { getDraggableModalCoordinatesOnInit } from './RotationForm.helpers';
import { DateTimePicker } from './parts/DateTimePicker';
import { UserItem } from './parts/UserItem';
@ -36,6 +39,15 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => {
const { onUpdate, onHide, id, scheduleId, params: defaultParams } = props;
const [shiftSwap, setShiftSwap] = useState({ ...defaultParams });
const [offsetTop, setOffsetTop] = useState(GRAFANA_HEADER_HEIGHT + 10);
const [draggablePosition, setDraggablePosition] = useState<{ x: number; y: number }>(undefined);
const [bounds, setDraggableBounds] = useState<{ left: number; right: number; top: number; bottom: number }>(
undefined
);
const debouncedOnResize = useDebouncedCallback(onResize, 250);
useResize(debouncedOnResize);
const store = useStore();
const {
@ -44,6 +56,12 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => {
timezoneStore: { selectedTimezoneOffset },
} = store;
useEffect(() => {
(async () => {
setOffsetTop(await calculateScheduleFormOffset(`.${cx('draggable')}`));
})();
}, []);
useEffect(() => {
(async () => {
if (id !== 'new') {
@ -131,7 +149,15 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => {
width="430px"
onDismiss={handleHide}
contentElement={(props, children) => (
<Draggable handle=".drag-handler" defaultClassName={cx('draggable')} positionOffset={{ x: 0, y: 200 }}>
<Draggable
handle=".drag-handler"
defaultClassName={cx('draggable')}
positionOffset={{ x: 0, y: offsetTop }}
position={draggablePosition}
bounds={{ ...bounds } || 'body'}
onStart={onDraggableInit}
onStop={(_e, data) => setDraggablePosition({ x: data.x, y: data.y })}
>
<div {...props}>{children}</div>
</Draggable>
)}
@ -235,4 +261,19 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => {
</div>
</Modal>
);
async function onResize() {
setOffsetTop(await calculateScheduleFormOffset(`.${cx('draggable')}`));
setDraggablePosition({ x: 0, y: 0 });
}
function onDraggableInit(_e: DraggableEvent, data: DraggableData) {
if (!data) {
return;
}
const bounds = getDraggableModalCoordinatesOnInit(data, offsetTop);
setDraggableBounds(bounds);
}
};

View file

@ -10,7 +10,7 @@ export const calculateScheduleFormOffset = async (queryClassName: string) => {
const modal = await waitForElement(queryClassName);
const modalHeight = modal.clientHeight;
return document.documentElement.scrollHeight / 2 - modalHeight / 2;
return window.innerHeight / 2 - modalHeight / 2;
};
// DatePickers will convert the date passed to local timezone, instead we want to use the date in the given timezone