diff --git a/superset-frontend/src/GlobalStyles.tsx b/superset-frontend/src/GlobalStyles.tsx index c305fb214a0a8..7829f2ff58859 100644 --- a/superset-frontend/src/GlobalStyles.tsx +++ b/superset-frontend/src/GlobalStyles.tsx @@ -39,11 +39,16 @@ export const GlobalStyles = () => ( .echarts-tooltip[style*='visibility: hidden'] { display: none !important; } + // Ant Design is applying inline z-index styles causing troubles + // TODO: Remove z-indexes when Ant Design is fully upgraded to v5 + // Prefer vanilla Ant Design z-indexes that should work out of the box .ant-popover, .antd5-dropdown, .ant-dropdown, - .ant-select-dropdown { - z-index: ${theme.zIndex.max}; + .ant-select-dropdown, + .antd5-modal-wrap, + .antd5-modal-mask { + z-index: ${theme.zIndex.max} !important; } // TODO: Remove when buttons have been upgraded to Ant Design 5. diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/tests/CalendarFrame.test.tsx b/superset-frontend/src/explore/components/controls/DateFilterControl/tests/CalendarFrame.test.tsx new file mode 100644 index 0000000000000..163ce4caf1a4a --- /dev/null +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/tests/CalendarFrame.test.tsx @@ -0,0 +1,90 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { render, screen, fireEvent } from 'spec/helpers/testing-library'; +import { CalendarFrame } from '../components/CalendarFrame'; +import { PreviousCalendarWeek, PreviousCalendarQuarter } from '../types'; +import { CALENDAR_RANGE_OPTIONS } from '../utils/constants'; + +describe('CalendarFrame', () => { + it('calls onChange with PreviousCalendarWeek if value is not in CALENDAR_RANGE_SET', () => { + const mockOnChange = jest.fn(); + render(); + + expect(mockOnChange).toHaveBeenCalledWith(PreviousCalendarWeek); + }); + + it('renders null if value is not in CALENDAR_RANGE_SET', () => { + render(); + expect( + screen.queryByText('Configure Time Range: Previous...'), + ).not.toBeInTheDocument(); + }); + + it('renders the correct number of radio options', () => { + render(); + const radios = screen.getAllByRole('radio'); + expect(radios).toHaveLength(CALENDAR_RANGE_OPTIONS.length); + CALENDAR_RANGE_OPTIONS.forEach(option => { + expect(screen.getByText(option.label)).toBeInTheDocument(); + }); + }); + + it('calls onChange with the correct value when a radio button is selected', () => { + const mockOnChange = jest.fn(); + render( + , + ); + + const secondOption = CALENDAR_RANGE_OPTIONS[1]; + const radio = screen.getByLabelText(secondOption.label); + fireEvent.click(radio); + + expect(mockOnChange).toHaveBeenCalledWith(secondOption.value); + }); + + it('renders the section title correctly', () => { + render( + , + ); + expect( + screen.getByText('Configure Time Range: Previous...'), + ).toBeInTheDocument(); + }); + + it('ensures the third option is PreviousCalendarQuarter', () => { + render( + , + ); + + const thirdOption = CALENDAR_RANGE_OPTIONS[2]; + expect(thirdOption.value).toBe(PreviousCalendarQuarter); + + expect(screen.getByLabelText(thirdOption.label)).toBeInTheDocument(); + }); +}); diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts index 0d0fbb9724d73..c1aa843448085 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts @@ -80,10 +80,12 @@ export type CommonRangeType = export const PreviousCalendarWeek = 'previous calendar week'; export const PreviousCalendarMonth = 'previous calendar month'; +export const PreviousCalendarQuarter = 'previous calendar quarter'; export const PreviousCalendarYear = 'previous calendar year'; export type CalendarRangeType = | typeof PreviousCalendarWeek | typeof PreviousCalendarMonth + | typeof PreviousCalendarQuarter | typeof PreviousCalendarYear; export const CurrentDay = 'Current day'; diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts index 4a116fb65e444..16b24d7a0b322 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts @@ -21,6 +21,7 @@ import { SelectOptionType, PreviousCalendarWeek, PreviousCalendarMonth, + PreviousCalendarQuarter, PreviousCalendarYear, CommonRangeType, CalendarRangeType, @@ -56,6 +57,7 @@ export const COMMON_RANGE_VALUES_SET = new Set( export const CALENDAR_RANGE_OPTIONS: SelectOptionType[] = [ { value: PreviousCalendarWeek, label: t('previous calendar week') }, { value: PreviousCalendarMonth, label: t('previous calendar month') }, + { value: PreviousCalendarQuarter, label: t('previous calendar quarter') }, { value: PreviousCalendarYear, label: t('previous calendar year') }, ]; export const CALENDAR_RANGE_VALUES_SET = new Set( @@ -119,6 +121,7 @@ export const COMMON_RANGE_SET: Set = new Set([ export const CALENDAR_RANGE_SET: Set = new Set([ PreviousCalendarWeek, PreviousCalendarMonth, + PreviousCalendarQuarter, PreviousCalendarYear, ]); diff --git a/superset/utils/date_parser.py b/superset/utils/date_parser.py index cd404a3fb4733..585c837f9d06e 100644 --- a/superset/utils/date_parser.py +++ b/superset/utils/date_parser.py @@ -369,6 +369,15 @@ def get_since_until( # pylint: disable=too-many-arguments,too-many-locals,too-m and separator not in time_range ): time_range = "DATETRUNC(DATEADD(DATETIME('today'), -1, MONTH), MONTH) : DATETRUNC(DATETIME('today'), MONTH)" # pylint: disable=line-too-long,useless-suppression # noqa: E501 + if ( + time_range + and time_range.startswith("previous calendar quarter") + and separator not in time_range + ): + time_range = ( + "DATETRUNC(DATEADD(DATETIME('today'), -1, QUARTER), QUARTER) : " + "DATETRUNC(DATETIME('today'), QUARTER)" # pylint: disable=line-too-long,useless-suppression # noqa: E501 + ) if ( time_range and time_range.startswith("previous calendar year") diff --git a/tests/unit_tests/utils/date_parser_tests.py b/tests/unit_tests/utils/date_parser_tests.py index 61121a28a9d63..824b21d167431 100644 --- a/tests/unit_tests/utils/date_parser_tests.py +++ b/tests/unit_tests/utils/date_parser_tests.py @@ -19,6 +19,7 @@ from typing import Optional from unittest.mock import Mock, patch +import freezegun import pytest from dateutil.relativedelta import relativedelta @@ -316,6 +317,33 @@ def test_get_since_until_instant_time_comparison_enabled() -> None: assert result == expected +def test_previous_calendar_quarter(): + with freezegun.freeze_time("2023-01-15"): + result = get_since_until("previous calendar quarter") + expected = (datetime(2022, 10, 1), datetime(2023, 1, 1)) + assert result == expected + + with freezegun.freeze_time("2023, 4, 15"): + result = get_since_until("previous calendar quarter") + expected = (datetime(2023, 1, 1), datetime(2023, 4, 1)) + assert result == expected + + with freezegun.freeze_time("2023, 8, 15"): + result = get_since_until("previous calendar quarter") + expected = (datetime(2023, 4, 1), datetime(2023, 7, 1)) + assert result == expected + + with freezegun.freeze_time("2023, 10, 15"): + result = get_since_until("previous calendar quarter") + expected = (datetime(2023, 7, 1), datetime(2023, 10, 1)) + assert result == expected + + with freezegun.freeze_time("2024, 1, 1"): + result = get_since_until("previous calendar quarter") + expected = (datetime(2023, 10, 1), datetime(2024, 1, 1)) + assert result == expected + + @patch("superset.utils.date_parser.parse_human_datetime", mock_parse_human_datetime) def test_datetime_eval() -> None: result = datetime_eval("datetime('now')")