diff --git a/src/ui/components/DialogFilter/DialogFilter.tsx b/src/ui/components/DialogFilter/DialogFilter.tsx index 557685879f..e86aaddeba 100644 --- a/src/ui/components/DialogFilter/DialogFilter.tsx +++ b/src/ui/components/DialogFilter/DialogFilter.tsx @@ -340,6 +340,19 @@ class DialogFilter extends React.Component return null; } + if (operation.selectable && !useManualInput) { + return ( + + ); + } + const commonDataType = getCommonDataType(this.field); switch (commonDataType) { @@ -361,19 +374,6 @@ class DialogFilter extends React.Component } default: { - if (operation.selectable && !useManualInput) { - return ( - - ); - } - return ( ({...item, range: false})), ...BASE_COMPARSION_OPERATIONS.map((item) => ({...item, range: false})), ...BASE_NULL_OPERATIONS.map((item) => ({...item, range: false})), + ...BASE_SET_OPERATIONS.map((item) => ({...item, selectable: true})), ]; export const ARRAY_OPERATIONS: Operation[] = [ diff --git a/src/ui/units/wizard/utils/wizard.ts b/src/ui/units/wizard/utils/wizard.ts index caec0e632c..a62577d206 100644 --- a/src/ui/units/wizard/utils/wizard.ts +++ b/src/ui/units/wizard/utils/wizard.ts @@ -318,10 +318,20 @@ export function getFiltersFields( fields: Field[], ): {filtersFields: Field[]; chartFilters: FilterField[]} { const filtersFields: Field[] = []; - paramsPairs.forEach((paramPair) => { + const groupedParams = paramsPairs.reduce>((acc, paramPair) => { const key = paramPair[0]; const urlValue: string = paramPair[1]; + if (!(key in acc)) { + acc[key] = []; + } + + acc[key].push(urlValue); + + return acc; + }, {}); + + Object.entries(groupedParams).forEach(([key, values]) => { // Let's try to find the filtered field const foundItem = findFieldInDatasetSection({ datasetSectionFields: fields, @@ -334,49 +344,56 @@ export function getFiltersFields( } const isFilterAlreadyInChart = filters.find((filter) => filter.guid === foundItem.guid); - if (!urlValue && isFilterAlreadyInChart && Utils.isEnabledFeature(Feature.EmptySelector)) { - filtersFields.push({ - ...foundItem, - unsaved: true, - filter: { - operation: {code: Operations.NO_SELECTED_VALUES}, - value: [''], - }, - }); - } else if (urlValue === '') { - return; - } - - const defaultOperation = isDateField(foundItem) ? Operations.EQ : undefined; - const parsedFiltersOperation = resolveOperation(urlValue, defaultOperation); + const defaultOperation = + isDateField(foundItem) && values.length === 1 ? Operations.EQ : undefined; + + values.forEach((urlValue) => { + if ( + !urlValue && + isFilterAlreadyInChart && + Utils.isEnabledFeature(Feature.EmptySelector) + ) { + filtersFields.push({ + ...foundItem, + unsaved: true, + filter: { + operation: {code: Operations.NO_SELECTED_VALUES}, + value: [''], + }, + }); + } else if (urlValue === '') { + return; + } - if (!parsedFiltersOperation) { - return; - } + const parsedFiltersOperation = resolveOperation(urlValue, defaultOperation); + if (!parsedFiltersOperation) { + return; + } - const code = parsedFiltersOperation.operation; - const value: string[] = [parsedFiltersOperation.value]; + const code = parsedFiltersOperation.operation; + const value: string[] = [parsedFiltersOperation.value]; - // Let's try to find such a filter among the parameters created from search - const foundFilter = filtersFields.find((item) => { - return item.guid === key || item.title === key; - }); + // Let's try to find such a filter among the parameters created from search + const foundFilter = filtersFields.find((item) => { + return item.guid === key || item.title === key; + }); - // If such a filter is found, then we do not create a new one, but supplement the old one, but only if the operator supports multi-selection - if (foundFilter && (code === Operations.IN || code === Operations.NIN)) { - foundFilter.filter!.value = [...foundFilter.filter!.value, ...value]; - } else { - filtersFields.push({ - ...foundItem, - unsaved: true, - filter: { - operation: { - code, + // If such a filter is found, then we do not create a new one, but supplement the old one, but only if the operator supports multi-selection + if (foundFilter && (code === Operations.IN || code === Operations.NIN)) { + foundFilter.filter!.value = [...foundFilter.filter!.value, ...value]; + } else { + filtersFields.push({ + ...foundItem, + unsaved: true, + filter: { + operation: { + code, + }, + value, }, - value, - }, - }); - } + }); + } + }); }); const chartFilters = getChartFiltersWithDisabledProp({ diff --git a/tests/opensource-suites/wizard/filters/filters-with-dialog-color.test.ts b/tests/opensource-suites/wizard/filters/filters-with-dialog-color.test.ts index 264e1e1b8f..777dae678a 100644 --- a/tests/opensource-suites/wizard/filters/filters-with-dialog-color.test.ts +++ b/tests/opensource-suites/wizard/filters/filters-with-dialog-color.test.ts @@ -4,6 +4,9 @@ import {PlaceholderName} from '../../../page-objects/wizard/SectionVisualization import WizardPage from '../../../page-objects/wizard/WizardPage'; import datalensTest from '../../../utils/playwright/globalTestDefinition'; import {openTestPage} from '../../../utils'; +import {WizardVisualizationId} from '../../../../src/shared'; + +const chartNamePattern = 'e2e-wizard-filters'; const setupFilters = async (wizardPage: WizardPage) => { await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Filters, 'city'); @@ -17,15 +20,17 @@ const setupFilters = async (wizardPage: WizardPage) => { datalensTest.describe('Wizard filters', () => { datalensTest.beforeEach(async ({page, config}) => { - const wizardPage = new WizardPage({page}); - await openTestPage(page, config.wizard.urls.WizardBasicDataset); + }); - await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'city'); - - await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'city'); + datalensTest.afterEach(async ({page}) => { + await page.reload(); + const pageUrl = page.url(); - await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Colors, 'city'); + if (pageUrl.includes(chartNamePattern)) { + const wizardPage = new WizardPage({page}); + await wizardPage.deleteEntry(); + } }); datalensTest( @@ -33,6 +38,10 @@ datalensTest.describe('Wizard filters', () => { async ({page}: {page: Page}) => { const wizardPage = new WizardPage({page}); + await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'city'); + await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'city'); + await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Colors, 'city'); + await wizardPage.colorDialog.open(); const valuesWithoutFilters = await wizardPage.colorDialog.getFieldValues(); @@ -52,4 +61,34 @@ datalensTest.describe('Wizard filters', () => { expect(valuesWithFilters).toEqual(['Los Angeles']); }, ); + + datalensTest( + 'Two or more values of the Date field from the dashboard filter section should use IN operation by default', + async ({page}: {page: Page}) => { + const dateFilterValues = ['2015-01-01', '2016-01-01']; + const wizardPage = new WizardPage({page}); + await wizardPage.setVisualization(WizardVisualizationId.PieD3); + + await wizardPage.createNewFieldWithFormula( + 'order_year', + `DATETRUNC([Order_date], 'year')`, + ); + await wizardPage.createNewFieldWithFormula('sum', 'sum([Sales])'); + await wizardPage.sectionVisualization.addFieldByClick( + PlaceholderName.Colors, + 'order_year', + ); + await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Measures, 'sum'); + + await wizardPage.saveWizardEntry(wizardPage.getUniqueEntryName(chartNamePattern)); + + const pageUrl = new URL(page.url()); + dateFilterValues.forEach((d) => pageUrl.searchParams.append('order_year', d)); + await page.goto(pageUrl.toString()); + + await wizardPage.colorDialog.open(); + await wizardPage.colorDialog.checkFieldValues(dateFilterValues); + await wizardPage.colorDialog.close(); + }, + ); }); diff --git a/tests/page-objects/wizard/ColorDialog.ts b/tests/page-objects/wizard/ColorDialog.ts index c7b4a85c5b..890ec82788 100644 --- a/tests/page-objects/wizard/ColorDialog.ts +++ b/tests/page-objects/wizard/ColorDialog.ts @@ -39,6 +39,12 @@ export default class ColorDialog { await this.page.click(this.cancelButtonSelector); } + async checkFieldValues(text: string[]) { + const items = this.page.locator(this.valueLabelSelector); + await expect(items.first()).toBeVisible(); + await expect(items).toHaveText(text); + } + async getFieldValues(): Promise { await this.page.waitForSelector(this.valueLabelSelector); const elements = await this.page.$$(this.valueLabelSelector);