diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index 3d1266e649a..069c69d4a30 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -15,6 +15,7 @@ export const Components = { }, TimePicker: { openButton: 'data-testid TimePicker Open Button', + overlayContent: 'data-testid TimePicker Overlay Content', fromField: 'Time Range from field', toField: 'Time Range to field', applyTimeRange: 'data-testid TimePicker submit button', diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.test.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.test.tsx index fcbdb834914..13b46255d37 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.test.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.test.tsx @@ -1,10 +1,14 @@ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import React from 'react'; import { dateTime, TimeRange } from '@grafana/data'; +import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; import { TimeRangePicker } from './TimeRangePicker'; +const selectors = e2eSelectors.components.TimePicker; + const from = dateTime('2019-12-17T07:48:27.433Z'); const to = dateTime('2019-12-18T07:48:27.433Z'); @@ -29,4 +33,25 @@ describe('TimePicker', () => { expect(container.queryByLabelText(/Time range selected/i)).toBeInTheDocument(); }); + it('switches overlay content visibility when toolbar button is clicked twice', async () => { + render( + {}} + onChange={(value) => {}} + value={value} + onMoveBackward={() => {}} + onMoveForward={() => {}} + onZoom={() => {}} + /> + ); + + const openButton = screen.getByTestId(selectors.openButton); + const overlayContent = screen.queryByTestId(selectors.overlayContent); + + expect(overlayContent).not.toBeInTheDocument(); + await userEvent.click(openButton); + expect(screen.getByTestId(selectors.overlayContent)).toBeInTheDocument(); + await userEvent.click(openButton); + expect(overlayContent).not.toBeInTheDocument(); + }); }); diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx index 1ce6ba3627e..8f6640fc32c 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx @@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css'; import { useDialog } from '@react-aria/dialog'; import { FocusScope } from '@react-aria/focus'; import { useOverlay } from '@react-aria/overlays'; -import React, { memo, FormEvent, createRef, useState } from 'react'; +import React, { memo, createRef, useState } from 'react'; import { isDateTime, @@ -75,19 +75,28 @@ export function TimeRangePicker(props: TimeRangePickerProps) { setOpen(false); }; - const onOpen = (event: FormEvent) => { - event.stopPropagation(); - event.preventDefault(); - setOpen(!isOpen); + const onToolbarButtonSwitch = () => { + setOpen((prevState) => !prevState); }; const onClose = () => { setOpen(false); }; - const ref = createRef(); - const { overlayProps, underlayProps } = useOverlay({ onClose, isDismissable: true, isOpen }, ref); - const { dialogProps } = useDialog({}, ref); + const overlayRef = createRef(); + const buttonRef = createRef(); + const { overlayProps, underlayProps } = useOverlay( + { + onClose, + isDismissable: true, + isOpen, + shouldCloseOnInteractOutside: (element) => { + return !buttonRef.current?.contains(element); + }, + }, + overlayRef + ); + const { dialogProps } = useDialog({}, overlayRef); const theme = useTheme2(); const styles = useStyles2(getStyles); @@ -109,14 +118,19 @@ export function TimeRangePicker(props: TimeRangePickerProps) { /> )} - } placement="bottom" interactive> + } + placement="bottom" + interactive + > {isOpen && ( -
+
-
+
{ const timeRange = getDefaultTimeRange(); render(); - await userEvent.click(screen.getByLabelText(/Time range selected/)); for (const [inputFrom, inputTo] of inputRanges) { await userEvent.click(screen.getByLabelText(/Time range selected/));