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

Fixes #1441: allow query with no filter set #1673

Merged
merged 1 commit into from
Apr 3, 2017
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
8 changes: 7 additions & 1 deletion web/client/components/data/query/QueryBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ const QueryBuilder = React.createClass({
pagination: React.PropTypes.object,
sortOptions: React.PropTypes.object,
hits: React.PropTypes.bool,
maxHeight: React.PropTypes.number
maxHeight: React.PropTypes.number,
allowEmptyFilter: React.PropTypes.bool,
emptyFilterWarning: React.PropTypes.bool
},
getDefaultProps() {
return {
Expand All @@ -74,6 +76,8 @@ const QueryBuilder = React.createClass({
sortOptions: null,
hits: false,
maxHeight: 830,
allowEmptyFilter: false,
emptyFilterWarning: false,
attributeFilterActions: {
onAddGroupField: () => {},
onAddFilterField: () => {},
Expand Down Expand Up @@ -138,6 +142,8 @@ const QueryBuilder = React.createClass({
pagination={this.props.pagination}
sortOptions={this.props.sortOptions}
hits={this.props.hits}
allowEmptyFilter={this.props.allowEmptyFilter}
emptyFilterWarning={this.props.emptyFilterWarning}
/>
<div className="querypanel" style={{maxHeight: this.props.maxHeight - 170}}>
<GroupField
Expand Down
28 changes: 20 additions & 8 deletions web/client/components/data/query/QueryToolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
*/
const React = require('react');

const {Button, Glyphicon, ButtonToolbar} = require('react-bootstrap');
const {Button, Glyphicon, ButtonToolbar, Tooltip} = require('react-bootstrap');

const Modal = require('../../misc/Modal');
const OverlayTrigger = require('../../misc/OverlayTrigger');

const I18N = require('../../I18N/I18N');

Expand All @@ -32,7 +33,9 @@ const QueryToolbar = React.createClass({
resultTitle: React.PropTypes.string,
pagination: React.PropTypes.object,
sortOptions: React.PropTypes.object,
hits: React.PropTypes.bool
hits: React.PropTypes.bool,
allowEmptyFilter: React.PropTypes.bool,
emptyFilterWarning: React.PropTypes.bool
},
getDefaultProps() {
return {
Expand All @@ -49,6 +52,8 @@ const QueryToolbar = React.createClass({
pagination: null,
sortOptions: null,
hits: false,
allowEmptyFilter: false,
emptyFilterWarning: false,
actions: {
onQuery: () => {},
onReset: () => {},
Expand All @@ -59,14 +64,24 @@ const QueryToolbar = React.createClass({
render() {
let fieldsExceptions = this.props.filterFields.filter((field) => field.exception).length > 0;
// let fieldsWithoutValues = this.props.filterFields.filter((field) => !field.value).length > 0;
let fieldsWithValues = this.props.filterFields.filter((field) => field.value).length > 0;
let fieldsWithValues = this.props.filterFields.filter((field) => field.value).length > 0 || this.props.allowEmptyFilter;

let queryDisabled =
// fieldsWithoutValues ||
fieldsExceptions ||
!this.props.toolbarEnabled ||
(!fieldsWithValues && !this.props.spatialField.geometry);

const tooltip = <Tooltip id="query-warning-tooltip"><I18N.Message msgId="queryform.emptyfilter"/></Tooltip>;
const btn = (<Button disabled={queryDisabled} bsSize="xs" id="query-toolbar-query" onClick={this.search}>
<Glyphicon glyph="glyphicon glyphicon-search"/>
<span><strong><I18N.Message msgId={"queryform.query"}/></strong></span>
</Button>);
const showTooltip = this.props.emptyFilterWarning && this.props.filterFields.filter((field) => field.value).length === 0 && !this.props.spatialField.geometry;
const queryButton = showTooltip ? (
<OverlayTrigger placement="bottom" key="query-button-tooltip" overlay={tooltip}>
{btn}
</OverlayTrigger>
) : btn;
return (
<div className="container-fluid query-toolbar">
<div id="query-toolbar-title"><I18N.Message msgId={"queryform.title"}/></div>
Expand All @@ -75,10 +90,7 @@ const QueryToolbar = React.createClass({
<Glyphicon glyph="glyphicon glyphicon-refresh"/>
<span><strong><I18N.Message msgId={"queryform.reset"}/></strong></span>
</Button>
<Button disabled={queryDisabled} bsSize="xs" id="query" onClick={this.search}>
<Glyphicon glyph="glyphicon glyphicon-search"/>
<span><strong><I18N.Message msgId={"queryform.query"}/></strong></span>
</Button>
{queryButton}
</ButtonToolbar>
<Modal show={this.props.showGeneratedFilter ? true : false} bsSize="large">
<Modal.Header>
Expand Down
106 changes: 106 additions & 0 deletions web/client/components/data/query/__tests__/QueryBuilder-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,55 @@ describe('QueryBuilder', () => {
expect(childNodes.length).toBe(2);
});

it('creates the QueryBuilder component no filter set', () => {
const groupLevels = 5;

const groupFields = [];

const filterFields = [{
rowId: 100,
groupId: 1,
attribute: "",
operator: null,
value: null,
exception: null
}];

const attributes = [{
id: "Attribute",
type: "list",
values: [
"attribute1",
"attribute2",
"attribute3",
"attribute4",
"attribute5"
]
}];

const querybuilder = ReactDOM.render(
<QueryBuilder
filterFields={filterFields}
attributes={attributes}
groupFields={groupFields}
groupLevels={groupLevels}
/>,
document.getElementById("container")
);

expect(querybuilder).toExist();

const queryBuilderDOMNode = expect(ReactDOM.findDOMNode(querybuilder));

expect(queryBuilderDOMNode).toExist();
let childNodes = queryBuilderDOMNode.actual.childNodes;
expect(childNodes.length).toBe(2);

const queryButton = document.getElementById('query-toolbar-query');
expect(queryButton).toExist();
expect(queryButton.getAttribute("disabled")).toBe('');
});

it('creates the QueryBuilder component in error state', () => {

let attributeFilterActions = {
Expand All @@ -111,4 +160,61 @@ describe('QueryBuilder', () => {
expect(querybuilder).toExist();
expect(spy.calls.length).toEqual(1);
});

it('creates the QueryBuilder component with empty filter support', () => {
const groupLevels = 5;

const groupFields = [{
id: 1,
logic: "OR",
index: 0
}];

const filterFields = [{
rowId: 100,
groupId: 1,
attribute: "",
operator: null,
value: null,
exception: null
}, {
rowId: 200,
groupId: 1,
attribute: "Attribute",
operator: "=",
value: "attribute1",
exception: null
}];

const attributes = [{
id: "Attribute",
type: "list",
values: [
"attribute1",
"attribute2",
"attribute3",
"attribute4",
"attribute5"
]
}];

const querybuilder = ReactDOM.render(
<QueryBuilder
filterFields={filterFields}
attributes={attributes}
groupFields={groupFields}
groupLevels={groupLevels}
allowEmptyFilter={true}
/>,
document.getElementById("container")
);

expect(querybuilder).toExist();

const queryBuilderDOMNode = expect(ReactDOM.findDOMNode(querybuilder));
expect(queryBuilderDOMNode).toExist();
const queryButton = document.getElementById('query-toolbar-query');
expect(queryButton).toExist();
expect(queryButton.getAttribute("disabled")).toNotExist();
});
});
2 changes: 2 additions & 0 deletions web/client/plugins/TOC.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ const SmartQueryForm = connect((state) => {
params: {typeName: state.query && state.query.typeName},
resultTitle: "Query Result",
showGeneratedFilter: false,
allowEmptyFilter: true,
emptyFilterWarning: true,
maxHeight: state.map && state.map.present && state.map.present.size && state.map.present.size.height
};
}, dispatch => {
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.de-DE
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
"header": "Finde im Datensatz",
"dataset_header": "Datensatz"
},
"emptyfilter": "Kein Filtersatz. Die Suche nach dem Timeout, wenn die Paginierung vom Server nicht unterstützt wird.",
"attributefilter":{
"add_condition": " Füge Bedingung hinzu",
"add_group": " Füge Gruppe hinzu",
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.en-US
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
"header": "Find in the dataset",
"dataset_header": "Dataset"
},
"emptyfilter": "No filter set. Searching could timeout if pagination is not supported by the server.",
"attributefilter":{
"add_condition": " Add Condition",
"add_group": " Add Group",
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.fr-FR
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@
"header": "Rechercher dans les données",
"dataset_header": "Ensemble des données"
},
"emptyfilter": "Aucun ensemble de filtres. La recherche pourrait expirer si la pagination n'est pas prise en charge par le serveur.",
"attributefilter":{
"add_condition": " Ajouter une condition",
"add_group": " Ajouter un groupe",
Expand Down
1 change: 1 addition & 0 deletions web/client/translations/data.it-IT
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
"header": "Cerca nel dataset",
"dataset_header": "Dataset"
},
"emptyfilter": "Nessun filtro selezionato. La ricerca potrebbe andare in timeout se il server non supporta la paginazione.",
"attributefilter":{
"add_condition": " Aggiungi Condizione",
"add_group": " Aggiungi Gruppo",
Expand Down