Skip to content

feat(filter): Add Slider Range Inputs Option for Numerical Range Filters #33170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4cf6b1e
Re-implement Slider in Numerical Range filter
payose Apr 17, 2025
d481ebe
fixed tooltip icon
payose Apr 17, 2025
38f0f9c
User can create a numerical range filter with different display modes
payose Apr 22, 2025
06d07ff
User can create a numerical range filter with different display modes
payose Apr 23, 2025
7bf77e6
Update e2e.ts
payose Apr 23, 2025
d728856
filter has default value should have default value
payose Apr 23, 2025
b2ebe9d
korbit ai review changes
payose Apr 23, 2025
6a99111
fix for when single value is enabled
payose Apr 28, 2025
978a627
fixes for styled divs
payose Apr 30, 2025
45d0102
single value test update
payose Apr 30, 2025
fa16d56
Fix range filter validation logic for required values
payose May 2, 2025
e7edc76
fix(Native Filters): Keep default filter values when configuring crea…
geido Apr 22, 2025
bc2ca68
chore(🦾): bump python pandas subpackage(s) (#33263)
github-actions[bot] Apr 29, 2025
effabe2
chore(🦾): bump python deprecation subpackage(s) (#33260)
github-actions[bot] Apr 29, 2025
16bdd74
chore(🦾): bump python packaging 24.2 -> 25.0 (#33259)
github-actions[bot] Apr 29, 2025
de6a501
removed un-needed comment
payose May 2, 2025
6c3e611
use min/max as base value for increment/decrememt
payose May 5, 2025
ea3361d
CI fix
payose May 6, 2025
6ae0e46
review fixes
payose May 8, 2025
6a85928
fix for typed input defaulting to min/max value
payose May 10, 2025
6d9c393
fix for overlapping filter items in horizontal orientation
payose May 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ describe('Native filters', () => {
validateFilterContentOnDashboard(testItems.topTenChart.filterColumnYear);
});

it('User can create a numerical range filter', () => {
it('User can create a numerical range filter with different display modes', () => {
visitDashboard();
enterNativeFilterEditModal(false);
fillNativeFilterForm(
Expand All @@ -192,35 +192,140 @@ describe('Native filters', () => {
testItems.datasetForNativeFilter,
testItems.filterNumericalColumn,
);
saveNativeFilterSettings([]);

// Assertions
cy.get('[data-test="range-filter-from-input"]')
// 1. First test "Range Inputs" mode
// First expand the Filter Configuration section if it's not already expanded
cy.get('.ant-collapse-header')
.contains('Filter Configuration')
.then($header => {
// Check if panel is already expanded
const isExpanded = $header
.closest('.ant-collapse-item')
.hasClass('ant-collapse-item-active');
if (!isExpanded) {
cy.wrap($header).click();
cy.wait(1000); // Give it more time to fully expand
}
});

cy.contains('Range Type')
.should('be.visible')
.click();
.closest('.ant-form-item')
.find('.ant-select-selector')
.click({ force: true });

// Select Range Inputs option from dropdown
cy.get('.ant-select-dropdown:visible')
.contains('.ant-select-item-option', 'Range Input')
.click({ force: true });

saveNativeFilterSettings([]);

// Test inputs functionality
cy.get('[data-test="range-filter-from-input"]').should('be.visible');
cy.get('[data-test="range-filter-from-input"]').click();
cy.get('[data-test="range-filter-from-input"]').type('{selectall}5');

cy.get('[data-test="range-filter-to-input"]').should('be.visible');
cy.get('[data-test="range-filter-to-input"]').click();
cy.get('[data-test="range-filter-to-input"]').type('{selectall}50');

cy.get(nativeFilters.applyFilter).click();

// Verify inputs have correct values
cy.get('[data-test="range-filter-from-input"]')
.invoke('val')
.should('equal', '5');

cy.get('[data-test="range-filter-to-input"]')
.invoke('val')
.should('equal', '50');

// 2. Now test "Slider" mode
enterNativeFilterEditModal(false);

cy.get('.ant-collapse-header')
.contains('Filter Configuration')
.then($header => {
const isExpanded = $header
.closest('.ant-collapse-item')
.hasClass('ant-collapse-item-active');
if (!isExpanded) {
cy.wrap($header).click();
cy.wait(1000); // Give it more time to fully expand
}
});

cy.contains('Range Type')
.should('be.visible')
.click();
.closest('.ant-form-item')
.find('.ant-select-selector')
.click({ force: true });

// Select Slider option from dropdown
cy.get('.ant-select-dropdown:visible')
.contains('.ant-select-item-option', 'Slider')
.click({ force: true });

saveNativeFilterSettings([]);

// Verify slider exists and inputs don't

cy.get('[data-test="range-filter-from-input"]').should('not.exist');
cy.get('[data-test="range-filter-to-input"]').should('not.exist');

// 3. Finally test "Slider and range input" mode (default)
enterNativeFilterEditModal(false);

// First expand the Filter Configuration section if it's not already expanded
cy.get('.ant-collapse-header')
.contains('Filter Configuration')
.then($header => {
// Check if panel is already expanded
const isExpanded = $header
.closest('.ant-collapse-item')
.hasClass('ant-collapse-item-active');
if (!isExpanded) {
cy.wrap($header).click();
cy.wait(1000); // Give it more time to fully expand
}
});

// Now find the Range Type field in the filter modal
cy.contains('Range Type')
.should('be.visible')
.closest('.ant-form-item')
.find('.ant-select-selector')
.click({ force: true });

// Select Slider and range input option from dropdown
cy.get('.ant-select-dropdown:visible')
.contains('.ant-select-item-option', 'Slider and range input')
.click({ force: true });

saveNativeFilterSettings([]);

// Test inputs still work in combined mode
cy.get('[data-test="range-filter-from-input"]').click();
cy.get('[data-test="range-filter-from-input"]').type('{selectall}10');

cy.get('[data-test="range-filter-to-input"]').click();
cy.get('[data-test="range-filter-to-input"]').type('{selectall}40');

cy.get('[data-test="range-filter-to-input"]').type('{selectall}50');
cy.get(nativeFilters.applyFilter).click();

// Assert that the URL contains 'native_filters'
// Final assertions - both inputs should have updated values
cy.url().then(u => {
const ur = new URL(u);
expect(ur.search).to.include('native_filters');

cy.get('[data-test="range-filter-from-input"]')
.invoke('val')
.should('equal', '5');
.should('equal', '10');

// Assert that the "To" input has the correct value
cy.get('[data-test="range-filter-to-input"]')
.invoke('val')
.should('equal', '50');
.should('equal', '40');
});
});

Expand Down
44 changes: 33 additions & 11 deletions superset-frontend/cypress-base/cypress/e2e/dashboard/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,15 +293,20 @@ export function fillNativeFilterForm(
}
cy.get(nativeFilters.silentLoading).should('not.exist');
if (filterColumn) {
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.click({ force: true });
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.type(filterColumn);
cy.get(nativeFilters.filtersPanel.inputDropdown)
.should('be.visible', { timeout: 20000 })
.last()
// Target and alias the column field
cy.contains('label', 'Column').closest('.ant-form-item').as('columnField');

cy.get('@columnField').find('.ant-select-selector').click();

cy.get('@columnField')
.find('.ant-select-selection-search-input')
.should('be.visible')
.type(filterColumn, { delay: 100 });

cy.get('.ant-select-dropdown')
.should('be.visible')
.contains('.ant-select-item-option', filterColumn)
.should('be.visible')
.click();
}
cy.get(nativeFilters.silentLoading).should('not.exist');
Expand Down Expand Up @@ -362,9 +367,26 @@ export function saveNativeFilterSettings(charts: ChartSpec[]) {
cy.get(nativeFilters.modal.footer)
.contains('Save')
.should('be.visible')
.click();
.click({ force: true });

// Wait for modal to either close or remain open
cy.get('body').should($body => {
const modalExists = $body.find(nativeFilters.modal.container).length > 0;
if (modalExists) {
cy.get(nativeFilters.modal.footer)
.contains('Save')
.should('be.visible')
.click({ force: true });
}
});

// Ensure modal is closed
cy.get(nativeFilters.modal.container).should('not.exist');
charts.forEach(waitForChartLoad);

// Wait for all charts to load
charts.forEach(chart => {
waitForChartLoad(chart);
});
}

/** ************************************************************************
Expand Down
21 changes: 20 additions & 1 deletion superset-frontend/src/components/DropdownContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,11 @@ const DropdownContainer = forwardRef(
? items.length - overflowedItems.length
: index;

// After determining overflow index, check if the last visible item overlaps with dropdown button

const button = current?.children.item(1);
if (width > previousWidth) {
// Calculates remaining space in the container
const button = current?.children.item(1);
const buttonRight = button?.getBoundingClientRect().right || 0;
const containerRight = current?.getBoundingClientRect().right || 0;
const remainingSpace = containerRight - buttonRight;
Expand All @@ -271,6 +273,23 @@ const DropdownContainer = forwardRef(
}
}

// Check if the last visible item would overlap with the dropdown button
if (
button &&
newOverflowingIndex > 0 &&
childrenArray[newOverflowingIndex - 1]
) {
const lastVisibleItem = childrenArray[newOverflowingIndex - 1];
const lastItemRect = lastVisibleItem.getBoundingClientRect();
const buttonRect = button.getBoundingClientRect();

// If the gap between last item and button is less than 16px, or there's an overlap,
// move the last item to the dropdown
const minimumGap = 16; // pixels
if (buttonRect.left - lastItemRect.right < minimumGap) {
newOverflowingIndex = Math.max(0, newOverflowingIndex - 1);
}
}
setOverflowingIndex(newOverflowingIndex);
}
}, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ const HorizontalFormItem = styled(StyledFormItem)<{
}

.ant-form-item-control {
width: ${({ inverseSelection }) => (inverseSelection ? 252 : 164)}px;
min-width: ${({ inverseSelection }) => (inverseSelection ? 252 : 164)}px;
}

.select-container {
Expand Down
Loading
Loading