From c5a007f9381b7857492f796cf676ba6bc5abf1da Mon Sep 17 00:00:00 2001 From: Brian Lambert Date: Wed, 10 Apr 2024 13:36:17 -0700 Subject: [PATCH] Wire up row filtering UI with backend RPCs in data explorer (#2728) * Row filters are hooked up * Take into account filters when returning table shape * Add clearing filter support --------- Co-authored-by: Wes McKinney --- .../positron_ipykernel/data_explorer.py | 9 +- .../tests/test_data_explorer.py | 6 +- .../addEditRowFilterModalPopup.tsx | 37 ++-- .../{rowFilter.ts => rowFilterDescriptor.ts} | 57 +++--- .../components/rowFilterWidget.tsx | 18 +- .../components/rowFilterBar/rowFilterBar.tsx | 184 ++++++++++++++---- .../languageRuntimeDataExplorerClient.ts | 13 +- .../browser/tableDataDataGridInstance.tsx | 21 +- 8 files changed, 251 insertions(+), 94 deletions(-) rename src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/{rowFilter.ts => rowFilterDescriptor.ts} (74%) diff --git a/extensions/positron-python/python_files/positron/positron_ipykernel/data_explorer.py b/extensions/positron-python/python_files/positron/positron_ipykernel/data_explorer.py index 29efcad3f68..f9d6cf19dcb 100644 --- a/extensions/positron-python/python_files/positron/positron_ipykernel/data_explorer.py +++ b/extensions/positron-python/python_files/positron/positron_ipykernel/data_explorer.py @@ -130,7 +130,7 @@ def get_data_values(self, request: GetDataValuesRequest): ).dict() def set_row_filters(self, request: SetRowFiltersRequest): - return self._set_row_filters(request.params.filters) + return self._set_row_filters(request.params.filters).dict() def set_sort_columns(self, request: SetSortColumnsRequest): self.sort_keys = request.params.sort_keys @@ -688,8 +688,13 @@ def _prof_histogram(self, column_index: int): raise NotImplementedError def _get_state(self) -> TableState: + if self.view_indices is not None: + num_rows = len(self.view_indices) + else: + num_rows = self.table.shape[0] + return TableState( - table_shape=TableShape(num_rows=self.table.shape[0], num_columns=self.table.shape[1]), + table_shape=TableShape(num_rows=num_rows, num_columns=self.table.shape[1]), row_filters=self.filters, sort_keys=self.sort_keys, ) diff --git a/extensions/positron-python/python_files/positron/positron_ipykernel/tests/test_data_explorer.py b/extensions/positron-python/python_files/positron/positron_ipykernel/tests/test_data_explorer.py index efff8e6e771..e429cb26566 100644 --- a/extensions/positron-python/python_files/positron/positron_ipykernel/tests/test_data_explorer.py +++ b/extensions/positron-python/python_files/positron/positron_ipykernel/tests/test_data_explorer.py @@ -395,7 +395,11 @@ def check_filter_case(self, table, filter_set, expected_table): self.register_table(ex_id, expected_table) response = self.set_row_filters(table_id, filters=filter_set) - assert response == FilterResult(selected_num_rows=len(expected_table)) + + ex_num_rows = len(expected_table) + assert response == FilterResult(selected_num_rows=ex_num_rows) + + assert self.get_state(table_id)["table_shape"]["num_rows"] == ex_num_rows self.compare_tables(table_id, ex_id, table.shape) def check_sort_case(self, table, sort_keys, expected_table, filters=None): diff --git a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/addEditRowFilterModalPopup.tsx b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/addEditRowFilterModalPopup.tsx index 0d30ee59b15..94d0d6ee4a5 100644 --- a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/addEditRowFilterModalPopup.tsx +++ b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/addEditRowFilterModalPopup.tsx @@ -21,7 +21,7 @@ import { DataExplorerClientInstance } from 'vs/workbench/services/languageRuntim import { DropDownListBox, DropDownListBoxEntry } from 'vs/workbench/browser/positronComponents/dropDownListBox/dropDownListBox'; import { RowFilterParameter } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/components/rowFilterParameter'; import { DropDownColumnSelector } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/components/dropDownColumnSelector'; -import { RangeRowFilter, RowFilter, RowFilterCondition, RowFilterIsBetween, RowFilterIsEmpty, RowFilterIsEqualTo, RowFilterIsGreaterThan, RowFilterIsLessThan, RowFilterIsNotBetween, RowFilterIsNotEmpty, SingleValueRowFilter } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilter'; +import { RangeRowFilterDescriptor, RowFilterDescriptor, RowFilterCondition, RowFilterDescriptorIsBetween, RowFilterDescriptorIsEmpty, RowFilterDescriptorIsEqualTo, RowFilterDescriptorIsGreaterThan, RowFilterDescriptorIsLessThan, RowFilterDescriptorIsNotBetween, RowFilterDescriptorIsNotEmpty, SingleValueRowFilterDescriptor } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilterDescriptor'; /** * Validates a row filter value. @@ -81,8 +81,8 @@ interface AddEditRowFilterModalPopupProps { dataExplorerClientInstance: DataExplorerClientInstance; renderer: PositronModalReactRenderer; anchor: HTMLElement; - editRowFilter?: RowFilter; - onApplyRowFilter: (rowFilter: RowFilter) => void; + editRowFilter?: RowFilterDescriptor; + onApplyRowFilter: (rowFilter: RowFilterDescriptor) => void; } /** @@ -103,16 +103,16 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp props.editRowFilter?.rowFilterCondition ); const [firstRowFilterValue, setFirstRowFilterValue] = useState(() => { - if (props.editRowFilter instanceof SingleValueRowFilter) { + if (props.editRowFilter instanceof SingleValueRowFilterDescriptor) { return props.editRowFilter.value; - } else if (props.editRowFilter instanceof RangeRowFilter) { + } else if (props.editRowFilter instanceof RangeRowFilterDescriptor) { return props.editRowFilter.lowerLimit; } else { return ''; } }); const [secondRowFilterValue, setSecondRowFilterValue] = useState(() => { - if (props.editRowFilter instanceof RangeRowFilter) { + if (props.editRowFilter instanceof RangeRowFilterDescriptor) { return props.editRowFilter.upperLimit; } else { return ''; @@ -449,7 +449,7 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp * Applies a row filter. * @param rowFilter The row filter to add. */ - const applyRowFilter = (rowFilter: RowFilter) => { + const applyRowFilter = (rowFilter: RowFilterDescriptor) => { setErrorText(undefined); props.renderer.dispose(); props.onApplyRowFilter(rowFilter); @@ -459,13 +459,13 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp switch (selectedCondition) { // Apply the is empty row filter. case RowFilterCondition.CONDITION_IS_EMPTY: { - applyRowFilter(new RowFilterIsEmpty(selectedColumnSchema)); + applyRowFilter(new RowFilterDescriptorIsEmpty(selectedColumnSchema)); break; } // Apply the is not empty row filter. case RowFilterCondition.CONDITION_IS_NOT_EMPTY: { - applyRowFilter(new RowFilterIsNotEmpty(selectedColumnSchema)); + applyRowFilter(new RowFilterDescriptorIsNotEmpty(selectedColumnSchema)); break; } @@ -474,7 +474,10 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp if (!validateFirstRowFilterValue()) { return; } - applyRowFilter(new RowFilterIsLessThan(selectedColumnSchema, firstRowFilterValue)); + applyRowFilter(new RowFilterDescriptorIsLessThan( + selectedColumnSchema, + firstRowFilterValue + )); break; } @@ -483,7 +486,10 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp if (!validateFirstRowFilterValue()) { return; } - applyRowFilter(new RowFilterIsGreaterThan(selectedColumnSchema, firstRowFilterValue)); + applyRowFilter(new RowFilterDescriptorIsGreaterThan( + selectedColumnSchema, + firstRowFilterValue + )); break; } @@ -492,7 +498,10 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp if (!validateFirstRowFilterValue()) { return; } - applyRowFilter(new RowFilterIsEqualTo(selectedColumnSchema, firstRowFilterValue)); + applyRowFilter(new RowFilterDescriptorIsEqualTo( + selectedColumnSchema, + firstRowFilterValue + )); break; } @@ -504,7 +513,7 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp if (!validateSecondRowFilterValue()) { return; } - applyRowFilter(new RowFilterIsBetween( + applyRowFilter(new RowFilterDescriptorIsBetween( selectedColumnSchema, firstRowFilterValue, secondRowFilterValue @@ -520,7 +529,7 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp if (!validateSecondRowFilterValue()) { return; } - applyRowFilter(new RowFilterIsNotBetween( + applyRowFilter(new RowFilterDescriptorIsNotBetween( selectedColumnSchema, firstRowFilterValue, secondRowFilterValue diff --git a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilter.ts b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilterDescriptor.ts similarity index 74% rename from src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilter.ts rename to src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilterDescriptor.ts index af899b68472..3434a0b968b 100644 --- a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilter.ts +++ b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilterDescriptor.ts @@ -24,9 +24,9 @@ export enum RowFilterCondition { } /** - * BaseRowFilter class. + * BaseRowFilterDescriptor class. */ -abstract class BaseRowFilter { +abstract class BaseRowFilterDescriptor { /** * Gets the identifier. */ @@ -47,9 +47,9 @@ abstract class BaseRowFilter { } /** - * RowFilterIsEmpty class. + * RowFilterDescriptorIsEmpty class. */ -export class RowFilterIsEmpty extends BaseRowFilter { +export class RowFilterDescriptorIsEmpty extends BaseRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -67,9 +67,9 @@ export class RowFilterIsEmpty extends BaseRowFilter { } /** - * RowFilterIsNotEmpty class. + * RowFilterDescriptorIsNotEmpty class. */ -export class RowFilterIsNotEmpty extends BaseRowFilter { +export class RowFilterDescriptorIsNotEmpty extends BaseRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -87,9 +87,9 @@ export class RowFilterIsNotEmpty extends BaseRowFilter { } /** - * SingleValueRowFilter class. + * SingleValueRowFilterDescriptor class. */ -export abstract class SingleValueRowFilter extends BaseRowFilter { +export abstract class SingleValueRowFilterDescriptor extends BaseRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -101,9 +101,9 @@ export abstract class SingleValueRowFilter extends BaseRowFilter { } /** - * RowFilterIsLessThan row filter. + * RowFilterDescriptorIsLessThan class. */ -export class RowFilterIsLessThan extends SingleValueRowFilter { +export class RowFilterDescriptorIsLessThan extends SingleValueRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -122,9 +122,9 @@ export class RowFilterIsLessThan extends SingleValueRowFilter { } /** - * RowFilterIsGreaterThan row filter. + * RowFilterDescriptorIsGreaterThan class. */ -export class RowFilterIsGreaterThan extends SingleValueRowFilter { +export class RowFilterDescriptorIsGreaterThan extends SingleValueRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -143,9 +143,9 @@ export class RowFilterIsGreaterThan extends SingleValueRowFilter { } /** - * RowFilterIsEqualTo row filter. + * RowFilterDescriptorIsEqualTo class. */ -export class RowFilterIsEqualTo extends SingleValueRowFilter { +export class RowFilterDescriptorIsEqualTo extends SingleValueRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -164,9 +164,9 @@ export class RowFilterIsEqualTo extends SingleValueRowFilter { } /** - * RangeRowFilter class. + * RangeRowFilterDescriptor class. */ -export abstract class RangeRowFilter extends BaseRowFilter { +export abstract class RangeRowFilterDescriptor extends BaseRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -183,9 +183,9 @@ export abstract class RangeRowFilter extends BaseRowFilter { } /** - * RowFilterIsBetween row filter. + * RowFilterDescriptorIsBetween class. */ -export class RowFilterIsBetween extends RangeRowFilter { +export class RowFilterDescriptorIsBetween extends RangeRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -205,9 +205,9 @@ export class RowFilterIsBetween extends RangeRowFilter { } /** - * RowFilterIsNotBetween row filter. + * RowFilterDescriptorIsNotBetween class. */ -export class RowFilterIsNotBetween extends RangeRowFilter { +export class RowFilterDescriptorIsNotBetween extends RangeRowFilterDescriptor { /** * Constructor. * @param columnSchema The column schema. @@ -227,12 +227,13 @@ export class RowFilterIsNotBetween extends RangeRowFilter { } /** - * RowFilter type. + * RowFilterDescriptor type. */ -export type RowFilter = - RowFilterIsEmpty | - RowFilterIsNotEmpty | - RowFilterIsLessThan | - RowFilterIsGreaterThan | - RowFilterIsBetween | - RowFilterIsNotBetween; +export type RowFilterDescriptor = + RowFilterDescriptorIsEmpty | + RowFilterDescriptorIsNotEmpty | + RowFilterDescriptorIsLessThan | + RowFilterDescriptorIsGreaterThan | + RowFilterDescriptorIsEqualTo | + RowFilterDescriptorIsBetween | + RowFilterDescriptorIsNotBetween; diff --git a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/components/rowFilterWidget.tsx b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/components/rowFilterWidget.tsx index 277f09f7a69..25bbc0ea8ae 100644 --- a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/components/rowFilterWidget.tsx +++ b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/components/rowFilterWidget.tsx @@ -12,13 +12,13 @@ import { forwardRef } from 'react'; // eslint-disable-line no-duplicate-imports // Other dependencies. import { localize } from 'vs/nls'; import { Button } from 'vs/base/browser/ui/positronComponents/button/button'; -import { RowFilter, RowFilterIsBetween, RowFilterIsEmpty, RowFilterIsEqualTo, RowFilterIsGreaterThan, RowFilterIsLessThan, RowFilterIsNotBetween, RowFilterIsNotEmpty } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilter'; +import { RowFilterDescriptor, RowFilterDescriptorIsBetween, RowFilterDescriptorIsEmpty, RowFilterDescriptorIsEqualTo, RowFilterDescriptorIsGreaterThan, RowFilterDescriptorIsLessThan, RowFilterDescriptorIsNotBetween, RowFilterDescriptorIsNotEmpty } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilterDescriptor'; /** * RowFilterWidgetProps interface. */ interface RowFilterWidgetProps { - rowFilter: RowFilter; + rowFilter: RowFilterDescriptor; booleanOperator?: 'and'; onEdit: () => void; onClear: () => void; @@ -32,39 +32,39 @@ interface RowFilterWidgetProps { export const RowFilterWidget = forwardRef((props, ref) => { // Compute the title. const title = (() => { - if (props.rowFilter instanceof RowFilterIsEmpty) { + if (props.rowFilter instanceof RowFilterDescriptorIsEmpty) { return <> {props.rowFilter.columnSchema.column_name} {localize('positron.dataExplorer.rowFilterWidget.isEmpty', "is empty")} ; - } else if (props.rowFilter instanceof RowFilterIsNotEmpty) { + } else if (props.rowFilter instanceof RowFilterDescriptorIsNotEmpty) { return <> {props.rowFilter.columnSchema.column_name} {localize('positron.dataExplorer.rowFilterWidget.isNotEmpty', "is not empty")} ; - } else if (props.rowFilter instanceof RowFilterIsLessThan) { + } else if (props.rowFilter instanceof RowFilterDescriptorIsLessThan) { return <> {props.rowFilter.columnSchema.column_name} < {props.rowFilter.value} ; - } else if (props.rowFilter instanceof RowFilterIsGreaterThan) { + } else if (props.rowFilter instanceof RowFilterDescriptorIsGreaterThan) { return <> {props.rowFilter.columnSchema.column_name} > {props.rowFilter.value} ; - } else if (props.rowFilter instanceof RowFilterIsEqualTo) { + } else if (props.rowFilter instanceof RowFilterDescriptorIsEqualTo) { return <> {props.rowFilter.columnSchema.column_name} = {props.rowFilter.value} ; - } else if (props.rowFilter instanceof RowFilterIsBetween) { + } else if (props.rowFilter instanceof RowFilterDescriptorIsBetween) { return <> {props.rowFilter.columnSchema.column_name} >= @@ -76,7 +76,7 @@ export const RowFilterWidget = forwardRef<= {props.rowFilter.upperLimit} ; - } else if (props.rowFilter instanceof RowFilterIsNotBetween) { + } else if (props.rowFilter instanceof RowFilterDescriptorIsNotBetween) { return <> {props.rowFilter.columnSchema.column_name} < diff --git a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/rowFilterBar.tsx b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/rowFilterBar.tsx index c24ee5d628e..095ef1611ad 100644 --- a/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/rowFilterBar.tsx +++ b/src/vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/rowFilterBar.tsx @@ -18,9 +18,92 @@ import { ContextMenuItem } from 'vs/workbench/browser/positronComponents/context import { ContextMenuSeparator } from 'vs/workbench/browser/positronComponents/contextMenu/contextMenuSeparator'; import { usePositronDataExplorerContext } from 'vs/workbench/browser/positronDataExplorer/positronDataExplorerContext'; import { PositronModalReactRenderer } from 'vs/workbench/browser/positronModalReactRenderer/positronModalReactRenderer'; -import { RowFilter } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilter'; +import { CompareFilterParamsOp, RowFilter, RowFilterType } from 'vs/workbench/services/languageRuntime/common/positronDataExplorerComm'; import { RowFilterWidget } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/rowFilterBar/components/rowFilterWidget'; import { AddEditRowFilterModalPopup } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/addEditRowFilterModalPopup'; +import { RowFilterDescriptor, RowFilterDescriptorIsEmpty, RowFilterDescriptorIsNotEmpty, RowFilterDescriptorIsLessThan, RowFilterDescriptorIsGreaterThan, RowFilterDescriptorIsEqualTo, RowFilterDescriptorIsBetween, RowFilterDescriptorIsNotBetween } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilterDescriptor'; + +/** + * Creates row filters from row filter descriptors. + * @param rowFilterDescriptors The row filter descriptors. + * @returns The row filters. + */ +const createRowFilters = (rowFilterDescriptors: RowFilterDescriptor[]) => { + // Create the set of row filters. + return rowFilterDescriptors.reduce(( + rowFilters, + rowFilterDescriptor + ) => { + // + if (rowFilterDescriptor instanceof RowFilterDescriptorIsEmpty) { + rowFilters.push({ + filter_id: rowFilterDescriptor.identifier, + filter_type: RowFilterType.IsNull, + column_index: rowFilterDescriptor.columnSchema.column_index + }); + } else if (rowFilterDescriptor instanceof RowFilterDescriptorIsNotEmpty) { + rowFilters.push({ + filter_id: rowFilterDescriptor.identifier, + filter_type: RowFilterType.IsNull, + column_index: rowFilterDescriptor.columnSchema.column_index + }); + } else if (rowFilterDescriptor instanceof RowFilterDescriptorIsLessThan) { + rowFilters.push({ + filter_id: rowFilterDescriptor.identifier, + filter_type: RowFilterType.Compare, + column_index: rowFilterDescriptor.columnSchema.column_index, + compare_params: { + op: CompareFilterParamsOp.Lt, + value: rowFilterDescriptor.value + } + }); + } else if (rowFilterDescriptor instanceof RowFilterDescriptorIsGreaterThan) { + rowFilters.push({ + filter_id: rowFilterDescriptor.identifier, + filter_type: RowFilterType.Compare, + column_index: rowFilterDescriptor.columnSchema.column_index, + compare_params: { + op: CompareFilterParamsOp.Gt, + value: rowFilterDescriptor.value + } + }); + } else if (rowFilterDescriptor instanceof RowFilterDescriptorIsEqualTo) { + rowFilters.push({ + filter_id: rowFilterDescriptor.identifier, + filter_type: RowFilterType.Compare, + column_index: rowFilterDescriptor.columnSchema.column_index, + compare_params: { + op: CompareFilterParamsOp.Eq, + value: rowFilterDescriptor.value + } + }); + } else if (rowFilterDescriptor instanceof RowFilterDescriptorIsBetween) { + rowFilters.push({ + filter_id: rowFilterDescriptor.identifier, + filter_type: RowFilterType.Between, + column_index: rowFilterDescriptor.columnSchema.column_index, + between_params: { + left_value: rowFilterDescriptor.lowerLimit, + right_value: rowFilterDescriptor.upperLimit + } + }); + } else if (rowFilterDescriptor instanceof RowFilterDescriptorIsNotBetween) { + rowFilters.push({ + filter_id: rowFilterDescriptor.identifier, + filter_type: RowFilterType.NotBetween, + column_index: rowFilterDescriptor.columnSchema.column_index, + between_params: { + left_value: rowFilterDescriptor.lowerLimit, + right_value: rowFilterDescriptor.upperLimit + } + }); + } + + // Return the row filters. + return rowFilters; + }, []); +}; + /** * RowFilterBar component. @@ -32,19 +115,22 @@ export const RowFilterBar = () => { // Reference hooks. const ref = useRef(undefined!); - const filterButtonRef = useRef(undefined!); + const rowFilterButtonRef = useRef(undefined!); const rowFilterWidgetRefs = useRef<(HTMLButtonElement)[]>([]); const addFilterButtonRef = useRef(undefined!); // State hooks. - const [rowFilters, setRowFilters] = useState([]); - const [filtersHidden, setFiltersHidden] = useState(false); + const [rowFilterDescriptors, setRowFilterDescriptors] = useState([]); + const [rowFiltersHidden, setRowFiltersHidden] = useState(false); /** * Shows the add / edit row filter modal popup. - * @param rowFilterToEdit The row filter to edit, or undefined, to add a row filter. + * @param editRowFilterDescriptor The row filter to edit, or undefined, to add a row filter. */ - const showAddEditRowFilterModalPopup = (anchor: HTMLElement, rowFilterToEdit?: RowFilter) => { + const showAddEditRowFilterModalPopup = ( + anchor: HTMLElement, + editRowFilterDescriptor?: RowFilterDescriptor + ) => { // Create the renderer. const renderer = new PositronModalReactRenderer({ keybindingService: context.keybindingService, @@ -54,27 +140,36 @@ export const RowFilterBar = () => { /** * onApplyRowFilter event handler. - * @param rowFilterToApply The row filter to apply. + * @param applyRowFilterDescriptor The row filter descriptor to apply. */ - const applyRowFilterHandler = (rowFilterToApply: RowFilter) => { - // If this is a new row filter, append it to the array of row filters. Otherwise, - // replace the row filter that was edited. - if (!rowFilterToEdit) { - setRowFilters(rowFilters => [...rowFilters, rowFilterToApply]); + const applyRowFilterHandler = async (applyRowFilterDescriptor: RowFilterDescriptor) => { + // Create the new row filter descriptors. If this is a new row filter, append it; + // otherwise, replace the row filter that was edited. + let newRowFilterDescriptors: RowFilterDescriptor[]; + if (!editRowFilterDescriptor) { + // Update the row filters. + newRowFilterDescriptors = [...rowFilterDescriptors, applyRowFilterDescriptor]; } else { - // Find the index of the row filter to edit. - const index = rowFilters.findIndex(rowFilter => - rowFilterToEdit.identifier === rowFilter.identifier + // Find the index of the row filter. + const index = rowFilterDescriptors.findIndex(rowFilter => + editRowFilterDescriptor.identifier === rowFilter.identifier ); - setRowFilters(rowFilters => - [ - ...rowFilters.slice(0, index), - rowFilterToApply, - ...rowFilters.slice(index + 1) - ] - ); + // Update the row filters. + newRowFilterDescriptors = [ + ...rowFilterDescriptors.slice(0, index), + applyRowFilterDescriptor, + ...rowFilterDescriptors.slice(index + 1) + ]; } + + // Set the new row filter descriptors. + setRowFilterDescriptors(newRowFilterDescriptors); + + // Set the new row filters. + await context.instance.tableDataDataGridInstance.setRowFilters(createRowFilters( + newRowFilterDescriptors + )); }; // Show the add /edit row filter modal popup. @@ -83,7 +178,7 @@ export const RowFilterBar = () => { dataExplorerClientInstance={context.instance.dataExplorerClientInstance} renderer={renderer} anchor={anchor} - editRowFilter={rowFilterToEdit} + editRowFilter={editRowFilterDescriptor} onApplyRowFilter={applyRowFilterHandler} /> ); @@ -98,36 +193,42 @@ export const RowFilterBar = () => { entries.push(new ContextMenuItem({ label: localize('positron.dataExplorer.addFilter', "Add filter"), icon: 'positron-add-filter', - onSelected: () => showAddEditRowFilterModalPopup(filterButtonRef.current) + onSelected: () => showAddEditRowFilterModalPopup(rowFilterButtonRef.current) })); entries.push(new ContextMenuSeparator()); - if (!filtersHidden) { + if (!rowFiltersHidden) { entries.push(new ContextMenuItem({ label: localize('positron.dataExplorer.hideFilters', "Hide filters"), icon: 'positron-hide-filters', - disabled: rowFilters.length === 0, - onSelected: () => setFiltersHidden(true) + disabled: rowFilterDescriptors.length === 0, + onSelected: () => setRowFiltersHidden(true) })); } else { entries.push(new ContextMenuItem({ label: localize('positron.dataExplorer.showFilters', "Show filters"), icon: 'positron-show-filters', - onSelected: () => setFiltersHidden(false) + onSelected: () => setRowFiltersHidden(false) })); } entries.push(new ContextMenuSeparator()); entries.push(new ContextMenuItem({ label: localize('positron.dataExplorer.clearFilters', "Clear filters"), icon: 'positron-clear-row-filters', - disabled: rowFilters.length === 0, - onSelected: () => setRowFilters([]) + disabled: rowFilterDescriptors.length === 0, + onSelected: async () => { + // Clear the row filter descriptors. + setRowFilterDescriptors([]); + + // Clear the row filters. + await context.instance.tableDataDataGridInstance.setRowFilters([]); + } })); // Show the context menu. await showContextMenu( context.keybindingService, context.layoutService, - filterButtonRef.current, + rowFilterButtonRef.current, 'left', 200, entries @@ -138,9 +239,18 @@ export const RowFilterBar = () => { * Clears the row filter at the specified row filter index. * @param rowFilterIndex The row filter index. */ - const clearRowFilter = (identifier: string) => { - setRowFilters(rowFilters => rowFilters.filter(rowFilter => + const clearRowFilter = async (identifier: string): Promise => { + // Remove the row filter. + const newRowFilterDescriptors = rowFilterDescriptors.filter(rowFilter => identifier !== rowFilter.identifier + ); + + // Set the new row filter descriptors. + setRowFilterDescriptors(newRowFilterDescriptors); + + // Set the new row filters. + await context.instance.tableDataDataGridInstance.setRowFilters(createRowFilters( + newRowFilterDescriptors )); }; @@ -148,16 +258,16 @@ export const RowFilterBar = () => { return (
- {!filtersHidden && rowFilters.map((rowFilter, index) => + {!rowFiltersHidden && rowFilterDescriptors.map((rowFilter, index) => { if (ref) { @@ -175,7 +285,7 @@ export const RowFilterBar = () => { ); } }} - onClear={() => clearRowFilter(rowFilter.identifier)} /> + onClear={async () => await clearRowFilter(rowFilter.identifier)} /> )}