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')")