Skip to content
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

✨ Custom filter placeholder text for data table #2261

Merged
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: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## UNRELEASED
## Unreleased

### Added

- [#2261](https://github.com/plotly/dash/pull/2261) Added new `placeholder_text` property to `filterOptions` for DataTable which allows overriding the default filter field placeholder.

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default class ColumnFilter extends PureComponent<
e.stopPropagation();
}}
value={value}
placeholder={'filter data...'}
placeholder={filterOptions.placeholder_text}
stopPropagation={true}
submit={this.submit}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export enum TableAction {

export interface IFilterOptions {
case?: FilterCase;
placeholder_text?: string;
}

export interface IDerivedData {
Expand Down
23 changes: 17 additions & 6 deletions components/dash-table/src/dash-table/dash/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,18 @@ export const propTypes = {
* 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'])
/**
* (default: 'sensitive') Determine whether the applicable filter relational operators will default to `sensitive` or `insensitive` comparison.
*/
case: PropTypes.oneOf(['sensitive', 'insensitive']),
/**
* (default: 'filter data...') The filter cell placeholder text.
*/
placeholder_text: PropTypes.string
}),

/**
Expand Down Expand Up @@ -776,14 +781,20 @@ 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'])
/**
* (default: 'sensitive') Determine whether the applicable filter relational operators will default to `sensitive` or `insensitive` comparison.
*/
case: PropTypes.oneOf(['sensitive', 'insensitive']),
/**
* (default: 'filter data...') The filter cell placeholder text.
*/
placeholder_text: PropTypes.string
}),

/**
* The `sort_action` property enables data to be
* sorted on a per-column basis.
Expand Down
4 changes: 3 additions & 1 deletion components/dash-table/src/dash-table/dash/Sanitizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ const D3_DEFAULT_LOCALE: INumberLocale = {
const DEFAULT_NULLY = '';
const DEFAULT_SPECIFIER = '';
const NULL_SELECTED_CELLS: SelectedCells = [];
const DEFAULT_FILTER_PLACEHOLDER_TEXT = 'filter data...';

const DEFAULT_FILTER_OPTIONS = {
case: FilterCase.Sensitive
case: FilterCase.Sensitive,
placeholder_text: DEFAULT_FILTER_PLACEHOLDER_TEXT
};

const data2number = (data?: any) => +data || 0;
Expand Down
7 changes: 7 additions & 0 deletions components/dash-table/tests/selenium/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,13 @@ def filter_value(self, value=None):
self.filter_clear()
self.mixin.driver.switch_to.active_element.send_keys(value + Keys.ENTER)

def filter_placeholder(self):
return (
self.filter()
.find_element(By.CSS_SELECTOR, "input")
.get_attribute("placeholder")
)


class DataTableRowFacade(object):
@preconditions(_validate_id, _validate_mixin, _validate_row, _validate_state)
Expand Down
94 changes: 71 additions & 23 deletions components/dash-table/tests/selenium/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_filt001_basic(test, props, expect):


@pytest.mark.parametrize(
"filter_options,column_filter_options",
"filter_case_options,column_case_filter_options",
[
("sensitive", None),
("sensitive", None),
Expand All @@ -91,45 +91,47 @@ def test_filt001_basic(test, props, expect):
("insensitive", "sensitive"),
],
)
def test_filt002_sensitivity(test, filter_options, column_filter_options):
def test_filt002_sensitivity(test, filter_case_options, column_case_filter_options):
props = dict(
id="table",
data=[dict(a="abc", b="abc", c="abc"), dict(a="ABC", b="ABC", c="ABC")],
columns=[
dict(
id="a",
name="a",
filter_options=dict(case=column_filter_options)
if column_filter_options is not None
filter_options=dict(case=column_case_filter_options)
if column_case_filter_options is not None
else None,
type="any",
),
dict(
id="b",
name="b",
filter_options=dict(case=column_filter_options)
if column_filter_options is not None
filter_options=dict(case=column_case_filter_options)
if column_case_filter_options is not None
else None,
type="text",
),
dict(
id="c",
name="c",
filter_options=dict(case=column_filter_options)
if column_filter_options is not None
filter_options=dict(case=column_case_filter_options)
if column_case_filter_options is not None
else None,
type="numeric",
),
],
filter_action="native",
filter_options=dict(case=filter_options)
if filter_options is not None
filter_options=dict(case=filter_case_options)
if filter_case_options is not None
else None,
style_cell=dict(width=100, min_width=100, max_width=100),
)

sensitivity = (
filter_options if column_filter_options is None else column_filter_options
filter_case_options
if column_case_filter_options is None
else column_case_filter_options
)

test.start_server(get_app(props))
Expand Down Expand Up @@ -197,7 +199,7 @@ def test_filt002_sensitivity(test, filter_options, column_filter_options):


@pytest.mark.parametrize(
"filter_options,column_filter_options",
"filter_case_options,column_case_filter_options",
[
("sensitive", None),
("sensitive", None),
Expand All @@ -207,51 +209,58 @@ def test_filt002_sensitivity(test, filter_options, column_filter_options):
("insensitive", "sensitive"),
],
)
def test_filt003_sensitivity(test, filter_options, column_filter_options):
def test_filt003_sensitivity(test, filter_case_options, column_case_filter_options):
column_b_filter_option = dict(placeholder_text="some descriptive text")
if column_case_filter_options is not None:
column_b_filter_option["case"] = column_case_filter_options

props = dict(
id="table",
data=[dict(a="abc", b="abc", c="abc"), dict(a="ABC", b="ABC", c="ABC")],
columns=[
dict(
id="a",
name="a",
filter_options=dict(case=column_filter_options)
if column_filter_options is not None
filter_options=dict(case=column_case_filter_options)
if column_case_filter_options is not None
else None,
type="any",
),
dict(
id="b",
name="b",
filter_options=dict(case=column_filter_options)
if column_filter_options is not None
else None,
filter_options=column_b_filter_option,
type="text",
),
dict(
id="c",
name="c",
filter_options=dict(case=column_filter_options)
if column_filter_options is not None
filter_options=dict(case=column_case_filter_options)
if column_case_filter_options is not None
else None,
type="numeric",
),
],
filter_action="native",
filter_options=dict(case=filter_options)
if filter_options is not None
filter_options=dict(case=filter_case_options)
if filter_case_options is not None
else None,
style_cell=dict(width=100, min_width=100, max_width=100),
)

sensitivity = (
filter_options if column_filter_options is None else column_filter_options
filter_case_options
if column_case_filter_options is None
else column_case_filter_options
)

test.start_server(get_app(props))

target = test.table("table")

target.column("a").filter_placeholder() == "filter data..."
target.column("b").filter_placeholder() == "some descriptive text"

target.column("a").filter_value("contains A")
if sensitivity == "sensitive":
assert target.cell(0, "a").get_text() == "ABC"
Expand Down Expand Up @@ -301,3 +310,42 @@ def test_filt003_sensitivity(test, filter_options, column_filter_options):
else:
assert target.cell(0, "c").get_text() == "abc"
assert target.cell(1, "c").get_text() == "ABC"


@pytest.mark.parametrize(
"column_placeholder_setting,table_placeholder_setting,expected_placeholder",
[
("abc", None, "abc"),
(None, "def", "def"),
("gah", "ijk", "gah"),
("", None, ""),
(None, None, "filter data..."),
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved
],
)
def test_filt004_placeholder(
test, column_placeholder_setting, table_placeholder_setting, expected_placeholder
):
column_filter_setting = dict(case="sensitive")
if column_placeholder_setting is not None:
column_filter_setting["placeholder_text"] = column_placeholder_setting

props = dict(
id="table",
data=[],
columns=[
dict(
id="a",
name="a",
type="any",
filter_options=column_filter_setting,
),
],
filter_action="native",
filter_options=dict(placeholder_text=table_placeholder_setting)
if table_placeholder_setting is not None
else None,
)

test.start_server(get_app(props))
target = test.table("table")
assert target.column("a").filter_placeholder() == expected_placeholder