mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TimePicker: Prevent TimePicker overflowing viewport on small screens (#59808)
* render timepicker in a modal style on small screens
* remove top: -1
* apply styles
* prevent bug where selecting a relative range wouldn't apply if the absolute ranges were expanded
* Revert "prevent bug where selecting a relative range wouldn't apply if the absolute ranges were expanded"
This reverts commit 7090443c12
.
This commit is contained in:
parent
90ece9d1f3
commit
1497ad4760
@ -7,9 +7,9 @@ import { dateTimeFormat, DateTime, dateTime, GrafanaTheme2, isDateTime } from '@
|
||||
|
||||
import { Button, ClickOutsideWrapper, HorizontalGroup, Icon, InlineField, Input, Portal } from '../..';
|
||||
import { useStyles2, useTheme2 } from '../../../themes';
|
||||
import { getModalStyles } from '../../Modal/getModalStyles';
|
||||
import { TimeOfDayPicker } from '../TimeOfDayPicker';
|
||||
import { getBodyStyles } from '../TimeRangePicker/CalendarBody';
|
||||
import { getStyles as getCalendarStyles } from '../TimeRangePicker/TimePickerCalendar';
|
||||
import { isValid } from '../utils';
|
||||
|
||||
export interface Props {
|
||||
@ -29,8 +29,8 @@ export const DateTimePicker: FC<Props> = ({ date, maxDate, label, onChange }) =>
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
|
||||
const theme = useTheme2();
|
||||
const { modalBackdrop } = getModalStyles(theme);
|
||||
const isFullscreen = useMedia(`(min-width: ${theme.breakpoints.values.lg}px)`);
|
||||
const containerStyles = useStyles2(getCalendarStyles);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const onApply = useCallback(
|
||||
@ -69,7 +69,7 @@ export const DateTimePicker: FC<Props> = ({ date, maxDate, label, onChange }) =>
|
||||
<div className={styles.modal} onClick={stopPropagation}>
|
||||
<DateTimeCalendar date={date} onChange={onApply} isFullscreen={false} onClose={() => setOpen(false)} />
|
||||
</div>
|
||||
<div className={containerStyles.backdrop} onClick={stopPropagation} />
|
||||
<div className={modalBackdrop} onClick={stopPropagation} />
|
||||
</ClickOutsideWrapper>
|
||||
</Portal>
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { useDialog } from '@react-aria/dialog';
|
||||
import { FocusScope } from '@react-aria/focus';
|
||||
import { useOverlay } from '@react-aria/overlays';
|
||||
@ -16,9 +16,10 @@ import {
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { useStyles2 } from '../../themes/ThemeContext';
|
||||
import { useStyles2, useTheme2 } from '../../themes/ThemeContext';
|
||||
import { t, Trans } from '../../utils/i18n';
|
||||
import { ButtonGroup } from '../Button';
|
||||
import { getModalStyles } from '../Modal/getModalStyles';
|
||||
import { ToolbarButton } from '../ToolbarButton';
|
||||
import { Tooltip } from '../Tooltip/Tooltip';
|
||||
|
||||
@ -85,10 +86,12 @@ export function TimeRangePicker(props: TimeRangePickerProps) {
|
||||
};
|
||||
|
||||
const ref = createRef<HTMLElement>();
|
||||
const { overlayProps } = useOverlay({ onClose, isDismissable: true, isOpen }, ref);
|
||||
const { overlayProps, underlayProps } = useOverlay({ onClose, isDismissable: true, isOpen }, ref);
|
||||
const { dialogProps } = useDialog({}, ref);
|
||||
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
const { modalBackdrop } = getModalStyles(theme);
|
||||
const hasAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to);
|
||||
const variant = isSynced ? 'active' : isOnCanvas ? 'canvas' : 'default';
|
||||
|
||||
@ -122,23 +125,26 @@ export function TimeRangePicker(props: TimeRangePickerProps) {
|
||||
</ToolbarButton>
|
||||
</Tooltip>
|
||||
{isOpen && (
|
||||
<section ref={ref} {...overlayProps} {...dialogProps}>
|
||||
<FocusScope contain autoFocus>
|
||||
<TimePickerContent
|
||||
timeZone={timeZone}
|
||||
fiscalYearStartMonth={fiscalYearStartMonth}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
quickOptions={quickOptions}
|
||||
history={history}
|
||||
showHistory
|
||||
widthOverride={widthOverride}
|
||||
onChangeTimeZone={onChangeTimeZone}
|
||||
onChangeFiscalYearStartMonth={onChangeFiscalYearStartMonth}
|
||||
hideQuickRanges={hideQuickRanges}
|
||||
/>
|
||||
</FocusScope>
|
||||
</section>
|
||||
<>
|
||||
<div role="presentation" className={cx(modalBackdrop, styles.backdrop)} {...underlayProps} />
|
||||
<section className={styles.content} ref={ref} {...overlayProps} {...dialogProps}>
|
||||
<FocusScope contain autoFocus>
|
||||
<TimePickerContent
|
||||
timeZone={timeZone}
|
||||
fiscalYearStartMonth={fiscalYearStartMonth}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
quickOptions={quickOptions}
|
||||
history={history}
|
||||
showHistory
|
||||
widthOverride={widthOverride}
|
||||
onChangeTimeZone={onChangeTimeZone}
|
||||
onChangeFiscalYearStartMonth={onChangeFiscalYearStartMonth}
|
||||
hideQuickRanges={hideQuickRanges}
|
||||
/>
|
||||
</FocusScope>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
|
||||
{timeSyncButton}
|
||||
@ -219,13 +225,33 @@ const formattedRange = (value: TimeRange, timeZone?: TimeZone) => {
|
||||
return rangeUtil.describeTimeRange(adjustedTimeRange, timeZone);
|
||||
};
|
||||
|
||||
const getStyles = () => {
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
container: css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
vertical-align: middle;
|
||||
`,
|
||||
backdrop: css({
|
||||
display: 'none',
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
display: 'block',
|
||||
},
|
||||
}),
|
||||
content: css({
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: '116%',
|
||||
zIndex: theme.zIndex.dropdown,
|
||||
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
position: 'fixed',
|
||||
right: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(50%, -50%)',
|
||||
zIndex: theme.zIndex.modal,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { DateTime, GrafanaTheme2, TimeZone } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { useTheme2 } from '../../../themes';
|
||||
import { getModalStyles } from '../../Modal/getModalStyles';
|
||||
|
||||
import { Body } from './CalendarBody';
|
||||
import { Footer } from './CalendarFooter';
|
||||
@ -16,7 +17,7 @@ import { Header } from './CalendarHeader';
|
||||
export const getStyles = (theme: GrafanaTheme2, isReversed = false) => {
|
||||
return {
|
||||
container: css`
|
||||
top: -1px;
|
||||
top: 0px;
|
||||
position: absolute;
|
||||
${isReversed ? 'left' : 'right'}: 544px;
|
||||
box-shadow: ${theme.shadows.z3};
|
||||
@ -38,26 +39,17 @@ export const getStyles = (theme: GrafanaTheme2, isReversed = false) => {
|
||||
}
|
||||
`,
|
||||
modal: css`
|
||||
box-shadow: ${theme.shadows.z3};
|
||||
left: 50%;
|
||||
position: fixed;
|
||||
top: 20%;
|
||||
width: 100%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: ${theme.zIndex.modal};
|
||||
`,
|
||||
content: css`
|
||||
margin: 0 auto;
|
||||
width: 268px;
|
||||
`,
|
||||
backdrop: css`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #202226;
|
||||
opacity: 0.7;
|
||||
z-index: ${theme.zIndex.modalBackdrop};
|
||||
text-align: center;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
@ -77,6 +69,7 @@ const stopPropagation = (event: React.MouseEvent<HTMLDivElement>) => event.stopP
|
||||
|
||||
function TimePickerCalendar(props: TimePickerCalendarProps) {
|
||||
const theme = useTheme2();
|
||||
const { modalBackdrop } = getModalStyles(theme);
|
||||
const styles = getStyles(theme, props.isReversed);
|
||||
const { isOpen, isFullscreen, onClose } = props;
|
||||
const ref = React.createRef<HTMLElement>();
|
||||
@ -112,6 +105,7 @@ function TimePickerCalendar(props: TimePickerCalendarProps) {
|
||||
|
||||
return (
|
||||
<OverlayContainer>
|
||||
<div className={modalBackdrop} onClick={stopPropagation} />
|
||||
<FocusScope contain autoFocus restoreFocus>
|
||||
<section className={styles.modal} onClick={stopPropagation} ref={ref} {...overlayProps} {...dialogProps}>
|
||||
<div className={styles.content} aria-label={selectors.components.TimePicker.calendar.label}>
|
||||
@ -121,7 +115,6 @@ function TimePickerCalendar(props: TimePickerCalendarProps) {
|
||||
</div>
|
||||
</section>
|
||||
</FocusScope>
|
||||
<div className={styles.backdrop} onClick={stopPropagation} />
|
||||
</OverlayContainer>
|
||||
);
|
||||
}
|
||||
|
@ -267,10 +267,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed, hideQuickRang
|
||||
container: css`
|
||||
background: ${theme.colors.background.primary};
|
||||
box-shadow: ${theme.shadows.z3};
|
||||
position: absolute;
|
||||
z-index: ${theme.zIndex.dropdown};
|
||||
width: ${isFullscreen ? '546px' : '262px'};
|
||||
top: 116%;
|
||||
border-radius: 2px;
|
||||
border: 1px solid ${theme.colors.border.weak};
|
||||
${isReversed ? 'left' : 'right'}: 0;
|
||||
|
Loading…
Reference in New Issue
Block a user