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

Fix #2169 added autocomplete combobox as cell editor in the feature grid #2228

Merged
2 changes: 2 additions & 0 deletions web/client/components/data/featuregrid/FeatureGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require("./featuregrid.css");
*/
class FeatureGrid extends React.PureComponent {
static propTypes = {
autocompleteEnabled: PropTypes.bool,
gridOpts: PropTypes.object,
changes: PropTypes.object,
selectBy: PropTypes.object,
Expand All @@ -50,6 +51,7 @@ class FeatureGrid extends React.PureComponent {
isProperty: PropTypes.func
};
static defaultProps = {
autocompleteEnabled: false,
gridComponent: AdaptiveGrid,
changes: {},
gridEvents: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class AttributeEditor extends editors.SimpleTextEditor {
// Otherwise this will trigger before other events out of the editors
// and so the tempChanges seems to be not present.
if (this.props.onTemporaryChanges) {
setTimeout( () => this.props.onTemporaryChanges(false), 300);
setTimeout( () => this.props.onTemporaryChanges(false), 500);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2017, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const PropTypes = require('prop-types');
const AttributeEditor = require('./AttributeEditor');
const {AutocompleteCombobox} = require('../../../misc/AutocompleteCombobox');
const {createPagedUniqueAutompleteStream} = require('../../../../observables/autocomplete');

class AutocompleteEditor extends AttributeEditor {
static propTypes = {
column: PropTypes.object,
dataType: PropTypes.string,
inputProps: PropTypes.object,
isValid: PropTypes.func,
onBlur: PropTypes.func,
autocompleteStreamFactory: PropTypes.func,
url: PropTypes.string,
typeName: PropTypes.string,
value: PropTypes.string
};
static defaultProps = {
isValid: () => true,
dataType: "string"
};
constructor(props) {
super(props);
this.validate = (value) => {
try {
return this.props.isValid(value[this.props.column && this.props.column.key]);
} catch (e) {
return false;
}
};
this.getValue = () => {
const updated = super.getValue();
return updated;
};
}
render() {
return <AutocompleteCombobox {...this.props} autocompleteStreamFactory={createPagedUniqueAutompleteStream}/>;
}
}

module.exports = AutocompleteEditor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const React = require('react');
const ReactDOM = require('react-dom');
const AutocompleteEditor = require('../AutocompleteEditor');
const {createPagedUniqueAutompleteStream} = require('../../../../../observables/autocomplete');

var expect = require('expect');

let testColumn = {
key: 'columnKey'
};
const value = "1.1";
describe('FeatureGrid AutocompleteEditor component', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});
it('AutocompleteEditor Editor no stream provided', () => {
const cmp = ReactDOM.render(<AutocompleteEditor
value={value}
rowIdx={1}
column={testColumn}/>, document.getElementById("container"));
expect(cmp.getValue().columnKey).toBe(value);
expect(cmp.validate(value)).toBe(true);
});
it('AutocompleteEditor Editor no stream provided', () => {
const cmp = ReactDOM.render(<AutocompleteEditor
value={value}
rowIdx={1}
autocompleteStreamFactory={createPagedUniqueAutompleteStream}
column={testColumn}/>, document.getElementById("container"));
expect(cmp).toExist();
});
});
6 changes: 5 additions & 1 deletion web/client/components/data/featuregrid/editors/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
const React = require('react');
const Editor = require('./AttributeEditor');
const NumberEditor = require('./NumberEditor');
const AutocompleteEditor = require('./AutocompleteEditor');

const types = {
"defaultEditor": (props) => <Editor {...props}/>,
"int": (props) => <NumberEditor dataType="int" inputProps={{step: 1, type: "number"}} {...props}/>,
"number": (props) => <NumberEditor dataType="number" inputProps={{step: 1, type: "number"}} {...props}/>
"number": (props) => <NumberEditor dataType="number" inputProps={{step: 1, type: "number"}} {...props}/>,
"string": (props) => props.autocompleteEnabled ?
<AutocompleteEditor dataType="string" {...props}/> :
<Editor dataType="string" {...props}/>
};
module.exports = (type, props) => types[type] ? types[type](props) : types.defaultEditor(props);
20 changes: 19 additions & 1 deletion web/client/components/data/featuregrid/enhancers/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const {getFilterRenderer} = require('../filterRenderers');
const {manageFilterRendererState} = require('../enhancers/filterRenderers');
const featuresToGrid = compose(
defaultProps({
autocompleteEnabled: false,
url: "",
typeName: "",
enableColumnFilters: false,
columns: [],
features: [],
Expand All @@ -20,6 +23,18 @@ const featuresToGrid = compose(
["enableColumnFilters"],
props => ({displayFilters: props.enableColumnFilters})
),
withPropsOnChange(
["autocompleteEnabled"],
props => ({autocompleteEnabled: props.autocompleteEnabled})
),
withPropsOnChange(
["url"],
props => ({url: props.url})
),
withPropsOnChange(
["typeName"],
props => ({typeName: props.typeName})
),
withPropsOnChange(
["features", "newFeatures", "changes"],
props => ({
Expand Down Expand Up @@ -55,7 +70,10 @@ const featuresToGrid = compose(
sortable: !props.isFocused
}, {
getEditor: ({localType=""} = {}) => props.editors(localType, {
onTemporaryChanges: props.gridEvents && props.gridEvents.onTemporaryChanges
onTemporaryChanges: props.gridEvents && props.gridEvents.onTemporaryChanges,
autocompleteEnabled: props.autocompleteEnabled,
url: props.url,
typeName: props.typeName
}),
getFilterRenderer: ({localType=""} = {}, name) => {
if (props.filterRenderers && props.filterRenderers[name]) {
Expand Down
147 changes: 147 additions & 0 deletions web/client/components/data/query/AutocompleteFieldHOC.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright 2017, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/


const PropTypes = require('prop-types');
const React = require('react');
const assign = require('object-assign');
const AutocompleteListItem = require('./AutocompleteListItem');
const PagedCombobox = require('../../misc/PagedCombobox');
const {isLikeOrIlike} = require('../../../utils/FilterUtils');
const HTML = require('../../../components/I18N/HTML');

class AutocompleteFieldHOC extends React.Component {
static propTypes = {
disabled: PropTypes.bool,
filterField: PropTypes.object,
label: PropTypes.string,
itemComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
maxFeaturesWPS: PropTypes.number,
onUpdateField: PropTypes.func,
pagination: PropTypes.object,
textField: PropTypes.string,
tooltip: PropTypes.object,
toggleMenu: PropTypes.func,
valueField: PropTypes.string
};

static contextTypes = {
messages: PropTypes.object
};

static defaultProps = {
label: null,
onUpdateField: () => {},
pagination: {
paginated: true,
nextPageIcon: "chevron-right",
prevPageIcon: "chevron-left"
},
itemComponent: AutocompleteListItem,
toggleMenu: () => {}
};

getOptions = () => {
return this.props.filterField &&
this.props.filterField.options &&
this.props.filterField.options[this.props.filterField.attribute] &&
this.props.filterField.options[this.props.filterField.attribute].map(o => {
return { value: o, label: o };
});
};

getPagination = (options) => {
const numberOfPages = Math.ceil(this.props.filterField.fieldOptions.valuesCount / this.props.maxFeaturesWPS);
const firstPage = this.props.filterField.fieldOptions.currentPage === 1 || !this.props.filterField.fieldOptions.currentPage;
const lastPage = this.props.filterField.fieldOptions.currentPage === numberOfPages || !this.props.filterField.fieldOptions.currentPage;
return assign({}, this.props.pagination, {
paginated: options.length !== 1,
firstPage,
lastPage,
loadPrevPage: () => this.props.onUpdateField(this.props.filterField.rowId, "value", this.props.filterField.value, "string", {currentPage: this.props.filterField.fieldOptions.currentPage - 1, delayDebounce: 0}),
loadNextPage: () => this.props.onUpdateField(this.props.filterField.rowId, "value", this.props.filterField.value, "string", {currentPage: this.props.filterField.fieldOptions.currentPage + 1, delayDebounce: 0})
});
};

getTooltip = () => {
return assign({}, this.props.tooltip, {
enabled: isLikeOrIlike(this.props.filterField.operator),
id: "autocompleteField-tooltip" + this.props.filterField && this.props.filterField.rowId,
message: (<HTML msgId="queryform.attributefilter.tooltipTextField"/>),
overlayTriggerKey: "autocompleteField-overlay" + this.props.filterField && this.props.filterField.rowId,
placement: "top"
});
};
renderField = () => {
let selectedValue;
// CHECK this.props.filterField.value AS ""
if (this.props.filterField && this.props.filterField.value && this.props.filterField.value !== "*") {
selectedValue = {
'value': this.props.filterField.value,
'label': this.props.filterField.value
};
}
let options = this.getOptions() ? this.getOptions().slice(0) : [];

return (<PagedCombobox
busy={this.props.filterField.loading}
data={this.props.filterField.loading ? [] : options}
disabled={this.props.filterField.operator === "isNull"}
itemComponent={this.props.itemComponent}
open={this.props.filterField.openAutocompleteMenu}
onChange={(value) => this.handleChange(value)}
onFocus={() => this.handleFocus(options)}
onSelect={() => this.handleSelect()}
onToggle={() => this.handleToggle(options)}
pagination={this.getPagination(options)}
selectedValue={selectedValue && selectedValue.value}
tooltip={this.getTooltip()}
/>);
}
render() {
let label = this.props.label ? (<label>{this.props.label}</label>) : (<span/>);
return (
<div className="autocompleteField">
{label}
{this.renderField()}
</div>);
}

// called before onChange
handleSelect = () => {
this.selected = true;
};

handleChange = (input) => {
if (this.selected) {
this.selected = false;
if (input && input.value !== "") {
this.props.onUpdateField(this.props.filterField.rowId, "value", input.value, "string", {currentPage: 1, selected: "selected", delayDebounce: 0});
}
} else {
this.props.onUpdateField(this.props.filterField.rowId, "value", typeof input === "string" ? input : input.value, "string", {currentPage: 1, delayDebounce: 1000});
}
};

// called before onToggle
handleFocus = (options) => {
this.loadWithoutfilter(options);
};

handleToggle = () => {
this.props.toggleMenu(this.props.filterField.rowId, !this.props.filterField.openAutocompleteMenu);
};

loadWithoutfilter = (options) => {
if (options.length === 0 && !this.props.filterField.value) {
this.props.onUpdateField(this.props.filterField.rowId, "value", "", "string", {currentPage: 1, delayDebounce: 0});
}
};
}

module.exports = AutocompleteFieldHOC;
2 changes: 1 addition & 1 deletion web/client/components/data/query/ComboField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ComboField extends React.Component {
style: PropTypes.object,
valueField: PropTypes.string,
textField: PropTypes.string,
placeholder: PropTypes.string,
placeholder: PropTypes.object,
fieldOptions: PropTypes.array,
fieldName: PropTypes.string,
fieldRowId: PropTypes.number,
Expand Down
2 changes: 1 addition & 1 deletion web/client/components/data/query/GroupField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ComboField = require('./ComboField');
const DateField = require('./DateField');
const NumberField = require('./NumberField');
const TextField = require('./TextField');
const AutocompleteField = require('./AutocompleteField');
const AutocompleteField = require('./AutocompleteFieldHOC');

const LocaleUtils = require('../../../utils/LocaleUtils');
const I18N = require('../../I18N/I18N');
Expand Down
Loading