Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions .eslintrc → .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
var path = require('path');

module.exports = {
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier"
Expand All @@ -7,7 +9,7 @@
"@typescript-eslint"
],
"parserOptions": {
"project": "./tsconfig.lint.json"
"project": path.join(__dirname, "tsconfig.lint.json"),
Comment thread
wbrgss marked this conversation as resolved.
},
"rules": {
"arrow-parens": [
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- [#545](https://github.com/plotly/dash-table/issues/545)
- Case insensitive filtering
- New props: `filter_options` - to control case of all filters, `columns.filter_options` - to control filter case for each column
- New operators: `i=`, `ieq`, `i>=`, `ige`, `i>`, `igt`, `i<=`, `ile`, `i<`, `ilt`, `i!=`, `ine`, `icontains` - for case-insensitive filtering, `s=`, `seq`, `s>=`, `sge`, `s>`, `sgt`, `s<=`, `sle`, `s<`, `slt`, `s!=`, `sne`, `scontains` - to force case-sensitive filtering on case-insensitive columns

## [4.11.3] - 2021-04-08
### Changed
- [#862](https://github.com/plotly/dash-table/pull/862) - update docstrings per https://github.com/plotly/dash/issues/1205
Expand Down
7 changes: 5 additions & 2 deletions src/core/syntax-tree/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface ILexerResult {

export interface ILexemeResult {
lexeme: ILexeme;
flags?: string;
value?: string;
}

Expand Down Expand Up @@ -37,8 +38,10 @@ export default function lexer(lexicon: Lexicon, query: string): ILexerResult {
return {lexemes: result, valid: false, error: query};
}

const value = (query.match(next.regexp) || [])[next.regexpMatch || 0];
result.push({lexeme: next, value});
const match = query.match(next.regexp) ?? [];
const value = match[next.regexpMatch || 0];
const flags = match[next.regexpFlags || -1];
result.push({lexeme: next, flags, value});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a lexicon perspective treat i|s prefix to applicable relational operators as "flags" that can be used to determine the exact processing to do during evaluation. value is the "base" operator + flags (e.g. icontains)


query = query.substring(value.length);
}
Expand Down
1 change: 1 addition & 0 deletions src/core/syntax-tree/lexicon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface IUnboundedLexeme {
nesting?: number;
priority?: number;
regexp: RegExp;
regexpFlags?: number;
regexpMatch?: number;
syntaxer?: (lexs: any[], pivot: any, pivotIndex: number) => any;
transform?: (value: any) => any;
Expand Down
19 changes: 17 additions & 2 deletions src/dash-table/components/Filter/Column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import React, {CSSProperties, PureComponent} from 'react';

import IsolatedInput from 'core/components/IsolatedInput';

import {ColumnId} from 'dash-table/components/Table/props';
import {ColumnId, IFilterOptions} from 'dash-table/components/Table/props';
import TableClipboardHelper from 'dash-table/utils/TableClipboardHelper';
import FilterOptions from 'dash-table/components/Filter/FilterOptions';

type SetFilter = (ev: any) => void;

interface IColumnFilterProps {
className: string;
columnId: ColumnId;
filterOptions: IFilterOptions;
isValid: boolean;
setFilter: SetFilter;
style?: CSSProperties;
toggleFilterOptions: () => void;
value?: string;
}

Expand Down Expand Up @@ -41,7 +44,15 @@ export default class ColumnFilter extends PureComponent<
};

render() {
const {className, columnId, isValid, style, value} = this.props;
const {
className,
columnId,
filterOptions,
isValid,
style,
toggleFilterOptions,
value
} = this.props;

return (
<th
Expand All @@ -62,6 +73,10 @@ export default class ColumnFilter extends PureComponent<
stopPropagation={true}
submit={this.submit}
/>
<FilterOptions
filterOptions={filterOptions}
toggleFilterOptions={toggleFilterOptions}
/>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New component displayed in the filter cell to toggle filtering options

</th>
);
}
Expand Down
25 changes: 25 additions & 0 deletions src/dash-table/components/Filter/FilterOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import {FilterCase, IFilterOptions} from 'dash-table/components/Table/props';

interface IFilterCaseButtonProps {
filterOptions: IFilterOptions;
toggleFilterOptions: () => void;
}

export default ({
filterOptions,
toggleFilterOptions
}: IFilterCaseButtonProps) => (
<input
Comment thread
wbrgss marked this conversation as resolved.
type='button'
className={`dash-filter--case ${
filterOptions.case === FilterCase.Sensitive
? 'dash-filter--case--sensitive'
: 'dash-filter--case--insensitive'
}`}
onClick={toggleFilterOptions}
title='Toggle filter case sensitivity'
value='Aa'
/>
);
26 changes: 24 additions & 2 deletions src/dash-table/components/FilterFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,27 @@ export default class FilterFactory {
updateColumnFilter(map, column, operator, value, setFilter);
};

private onToggleChange = (
column: IColumn,
map: Map<string, SingleColumnSyntaxTree>,
operator: FilterLogicalOperator,
setFilter: SetFilter,
toggleFilterOptions: (column: IColumn) => IColumn,
value: any
) => {
const newColumn = toggleFilterOptions(column);

updateColumnFilter(map, newColumn, operator, value, setFilter);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trigger a setProps and return the new column configuration, use the new column configuration to update the column's query (and trigger a refresh)

};

private filter = memoizerCache<[ColumnId, number]>()(
(
column: IColumn,
index: number,
map: Map<string, SingleColumnSyntaxTree>,
operator: FilterLogicalOperator,
setFilter: SetFilter
setFilter: SetFilter,
toggleFilterOptions: (column: IColumn) => IColumn
) => {
const ast = map.get(column.id.toString());

Expand All @@ -72,6 +86,7 @@ export default class FilterFactory {
key={`column-${index}`}
className={`dash-filter column-${index}`}
columnId={column.id}
filterOptions={column.filter_options}
isValid={!ast || ast.isValid}
setFilter={this.onChange.bind(
this,
Expand All @@ -80,6 +95,11 @@ export default class FilterFactory {
operator,
setFilter
)}
// Running into TypeScript binding issues with many parameters..
// bind with no more than 4 params each time.. sigh..
toggleFilterOptions={this.onToggleChange
.bind(this, column, map, operator, setFilter)
.bind(this, toggleFilterOptions, ast && ast.query)}
Comment thread
wbrgss marked this conversation as resolved.
value={ast && ast.query}
/>
);
Expand Down Expand Up @@ -107,6 +127,7 @@ export default class FilterFactory {
style_cell_conditional,
style_filter,
style_filter_conditional,
toggleFilterOptions,
visibleColumns
} = this.props;

Expand Down Expand Up @@ -139,7 +160,8 @@ export default class FilterFactory {
index,
map,
filter_action.operator,
setFilter
setFilter,
toggleFilterOptions
);
},
visibleColumns
Expand Down
34 changes: 29 additions & 5 deletions src/dash-table/components/Table/Table.less
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,23 @@
top: 0;
height: 100%;
width: 100%;

&.dash-filter--case {
position: relative;
left: auto;
top: auto;
width: auto;
height: 16px;
line-height: 0px;
padding: 1px;
}
&.dash-filter--case--sensitive {
border-color: hotpink;
border-radius: 3px;
border-style: solid;
border-width: 2px;
color: hotpink;
}
}
}

Expand Down Expand Up @@ -670,7 +687,7 @@
}

.dash-spreadsheet-inner {
[class^='column-header--'] {
[class^='column-header--'], [class^='dash-filter--'] {
cursor: pointer;
float: left;
}
Expand All @@ -685,6 +702,7 @@
}


.dash-filter--case,
.column-header--clear,
.column-header--delete,
.column-header--edit,
Expand All @@ -695,10 +713,16 @@
}

th:hover {
[class^='column-header--']:not(.disabled) {
color: var(--accent);
opacity: 1;
}
[class^='column-header--'], [class^='dash-filter--'] {
&:not(.disabled) {
color: var(--accent);
opacity: 1;
}
}
}

.dash-filter--case {
font-size: 10px;
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/dash-table/components/Table/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export enum ExportHeaders {
Display = 'display'
}

export enum FilterCase {
Insensitive = 'insensitive',
Sensitive = 'sensitive'
}

export enum SortMode {
Single = 'single',
Multi = 'multi'
Expand All @@ -49,6 +54,10 @@ export enum TableAction {
None = 'none'
}

export interface IFilterOptions {
case?: FilterCase;
}

export interface IDerivedData {
data: Data;
indices: Indices;
Expand Down Expand Up @@ -193,6 +202,7 @@ export interface IBaseColumn {
clearable?: boolean | boolean[] | 'first' | 'last';
deletable?: boolean | boolean[] | 'first' | 'last';
editable: boolean;
filter_options: IFilterOptions;
hideable?: boolean | boolean[] | 'first' | 'last';
renamable?: boolean | boolean[] | 'first' | 'last';
selectable?: boolean | boolean[] | 'first' | 'last';
Expand Down Expand Up @@ -327,6 +337,7 @@ export interface IProps {
data?: Data;
editable?: boolean;
fill_width?: boolean;
filter_options?: IFilterOptions;
filter_query?: string;
filter_action?: TableAction | IFilterAction;
hidden_columns?: string[];
Expand Down Expand Up @@ -381,6 +392,7 @@ interface IDefaultProps {
export_format: ExportFormat;
export_headers: ExportHeaders;
fill_width: boolean;
filter_options?: IFilterOptions;
filter_query: string;
filter_action: TableAction;
fixed_columns: Fixed;
Expand Down Expand Up @@ -492,6 +504,7 @@ export interface IFilterFactoryProps {
style_cell_conditional: Cells;
style_filter: Style;
style_filter_conditional: BasicFilters;
toggleFilterOptions: (column: IColumn) => IColumn;
visibleColumns: Columns;
}

Expand Down
26 changes: 26 additions & 0 deletions src/dash-table/dash/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const defaultProps = {
dropdown_data: [],

fill_width: true,
filter_options: {},
fixed_columns: {
headers: false,
data: 0
Expand Down Expand Up @@ -181,6 +182,19 @@ export const propTypes = {
*/
editable: PropTypes.bool,

/**
* There are two `filter_options` props in the table.
* This is the column-level filter_options prop and there is
* also the table-level `filter_options` prop.
* These props determine whether the applicable filter relational
* operators will default to `sensitive` or `insensitive` comparison.
* If the column-level `filter_options` prop is set it overrides
* the table-level `filter_options` prop for that column.
*/
filter_options: PropTypes.shape({
case: PropTypes.oneOf(['sensitive', 'insensitive'])
}),

/**
* If true, the user can hide the column by clicking on the `hide`
* action button on the column. If there are multiple header rows, true
Expand Down Expand Up @@ -1142,6 +1156,18 @@ export const propTypes = {
})
]),

/**
* There are two `filter_options` props in the table.
* This is the table-level filter_options prop and there is
* also the column-level `filter_options` prop.
* These props determine whether the applicable filter relational
* operators will default to `sensitive` or `insensitive` comparison.
* If the column-level `filter_options` prop is set it overrides
* the table-level `filter_options` prop for that column.
*/
filter_options: PropTypes.shape({
case: PropTypes.oneOf(['sensitive', 'insensitive'])
}),
/**
* The `sort_action` property enables data to be
* sorted on a per-column basis.
Expand Down
Loading