diff --git a/app/actuators/ResultCard.js b/app/actuators/ResultCard.js index 92991edce9..602091927c 100644 --- a/app/actuators/ResultCard.js +++ b/app/actuators/ResultCard.js @@ -102,7 +102,7 @@ export default class ResultCard extends Component { // check the height and set scroll if scroll not exists componentDidUpdate() { - if (!this.state.showPlaceholder) { + if (!this.state.showPlaceholder && !this.props.scrollOnTarget) { this.applyScroll(); } } @@ -593,7 +593,15 @@ export default class ResultCard extends Component { }; helper.selectedSensor.set(obj, true, "sortChange"); } - + getComponentStyle() { + let componentStyle = {}; + if(this.props.scrollOnTarget) { + componentStyle.maxHeight = "none"; + componentStyle.height = "auto"; + } + componentStyle = Object.assign(componentStyle, this.props.componentStyle); + return componentStyle; + } render() { let title = null, placeholder = null, @@ -639,7 +647,7 @@ export default class ResultCard extends Component { return (
-
{ this.listParentElement = div }} className={`rbc-resultcard-container card thumbnail ${cx}`} style={this.props.componentStyle}> +
{ this.listParentElement = div }} className={`rbc-resultcard-container card thumbnail ${cx}`} style={this.props.componentStyle} style={this.getComponentStyle()}> {title} {sortOptions} {this.props.showResultStats && this.state.resultStats.resultFound ? () : null} diff --git a/app/actuators/ResultList.js b/app/actuators/ResultList.js index 8f1721002c..b2c1dcd0cc 100644 --- a/app/actuators/ResultList.js +++ b/app/actuators/ResultList.js @@ -102,7 +102,7 @@ export default class ResultList extends Component { // check the height and set scroll if scroll not exists componentDidUpdate() { - if (!this.state.showPlaceholder) { + if (!this.state.showPlaceholder && !this.props.scrollOnTarget) { this.applyScroll(); } } @@ -597,6 +597,16 @@ export default class ResultList extends Component { helper.selectedSensor.set(obj, true, "sortChange"); } + getComponentStyle() { + let componentStyle = {}; + if(this.props.scrollOnTarget) { + componentStyle.maxHeight = "none"; + componentStyle.height = "auto"; + } + componentStyle = Object.assign(componentStyle, this.props.componentStyle); + return componentStyle; + } + render() { let title = null, placeholder = null, @@ -642,7 +652,7 @@ export default class ResultList extends Component { return (
{ this.resultListContainer = div }} className="rbc rbc-resultlist"> -
{ this.listParentElement = div }} className={`rbc-resultlist-container card thumbnail ${cx}`} style={this.props.componentStyle}> +
{ this.listParentElement = div }} className={`rbc-resultlist-container card thumbnail ${cx}`} style={this.getComponentStyle()}> {title} {sortOptions} {this.props.showResultStats && this.state.resultStats.resultFound ? () : null} diff --git a/app/sensors/CategorySearch.js b/app/sensors/CategorySearch.js index e4d63cdd22..fdcecb9bf1 100644 --- a/app/sensors/CategorySearch.js +++ b/app/sensors/CategorySearch.js @@ -27,6 +27,7 @@ export default class CategorySearch extends Component { this.type = "match_phrase"; this.channelId = null; this.channelListener = null; + this.urlParams = helper.URLParams.get(this.props.componentId); this.fieldType = typeof props.appbaseField; this.handleSearch = this.handleSearch.bind(this); this.optionRenderer = this.optionRenderer.bind(this); @@ -268,8 +269,9 @@ export default class CategorySearch extends Component { } checkDefault() { - if (this.props.defaultSelected && this.defaultSelected !== this.props.defaultSelected) { - this.defaultSelected = this.props.defaultSelected; + const defaultValue = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; + if (defaultValue && this.defaultSelected !== defaultValue) { + this.defaultSelected = defaultValue; setTimeout(this.setValue.bind(this, this.defaultSelected), 100); this.handleSearch({ value: this.defaultSelected @@ -297,7 +299,7 @@ export default class CategorySearch extends Component { if(this.props.onValueChange) { this.props.onValueChange(obj.value); } - + helper.URLParams.update(this.props.componentId, finalVal.value, this.props.URLParam); helper.selectedSensor.set(obj, true); this.setState({ currentValue: value @@ -363,14 +365,16 @@ CategorySearch.propTypes = { React.PropTypes.string, React.PropTypes.arrayOf(React.PropTypes.string) ]), - componentStyle: React.PropTypes.object + componentStyle: React.PropTypes.object, + URLParam: React.PropTypes.bool }; // Default props value CategorySearch.defaultProps = { placeholder: "Search", highlight: false, - componentStyle: {} + componentStyle: {}, + URLParam: false }; // context type @@ -388,5 +392,6 @@ CategorySearch.types = { placeholder: TYPES.STRING, defaultSelected: TYPES.STRING, customQuery: TYPES.FUNCTION, - highlight: TYPES.BOOLEAN + highlight: TYPES.BOOLEAN, + URLParam: TYPES.BOOLEAN }; diff --git a/app/sensors/DynamicRangeSlider.js b/app/sensors/DynamicRangeSlider.js index 6390e87257..aada007f6e 100644 --- a/app/sensors/DynamicRangeSlider.js +++ b/app/sensors/DynamicRangeSlider.js @@ -34,6 +34,7 @@ export default class DynamicRangeSlider extends Component { this.type = "range"; this.channelId = null; this.channelListener = null; + this.urlParams = helper.URLParams.get(this.props.componentId, false, true); this.handleValuesChange = this.handleValuesChange.bind(this); this.handleResults = this.handleResults.bind(this); this.customQuery = this.customQuery.bind(this); @@ -47,7 +48,8 @@ export default class DynamicRangeSlider extends Component { } componentWillReceiveProps(nextProps) { - this.updateValues(nextProps.defaultSelected); + const defaultValue = this.urlParams !== null ? this.urlParams : nextProps.defaultSelected; + this.updateValues(defaultValue); } // stop streaming request and remove listener when component will unmount @@ -85,23 +87,40 @@ export default class DynamicRangeSlider extends Component { this.setRangeValue(); } - setRangeValue() { + setRangeValue(value="range") { const objValue = { key: `${this.props.componentId}-internal`, - value: this.state.range + value }; helper.selectedSensor.set(objValue, true); } histogramQuery() { - return { - [this.props.appbaseField]: { - "histogram": { - "field": this.props.appbaseField, - "interval": this.props.interval + let query; + const isHistogramQuery = helper.selectedSensor.get(`${this.props.componentId}-internal`); + if(isHistogramQuery === "histogram") { + query = { + [this.props.appbaseField]: { + "histogram": { + "field": this.props.appbaseField, + "interval": this.props.interval ? this.props.interval : Math.ceil((this.state.range.max - this.state.range.min)/10) + } } - } - }; + }; + } else { + query = { + "max": { + "max": { + "field": this.props.appbaseField, + } + }, "min": { + "min": { + "field": this.props.appbaseField, + } + } + }; + } + return query; } // Create a channel which passes the react and receive results whenever react changes @@ -131,18 +150,27 @@ export default class DynamicRangeSlider extends Component { } if (res.appliedQuery) { const data = res.data; - let rawData; - if (res.mode === "streaming") { - rawData = this.state.rawData; - rawData.hits.hits.push(res.data); - } else if (res.mode === "historic") { - rawData = data; + if(data.aggregations.max && data.aggregations.min) { + this.setState({ + range: { + min: data.aggregations.min.value, + max: data.aggregations.max.value + } + }, this.setRangeValue.bind(this, "histogram")); + } else { + let rawData; + if (res.mode === "streaming") { + rawData = this.state.rawData; + rawData.hits.hits.push(res.data); + } else if (res.mode === "historic") { + rawData = data; + } + this.setState({ + queryStart: false, + rawData + }); + this.setData(data); } - this.setState({ - queryStart: false, - rawData - }); - this.setData(data); } }); this.listenLoadingChannel(channelObj); @@ -216,27 +244,28 @@ export default class DynamicRangeSlider extends Component { if (itemLength > 1) { this.setState({ counts: this.countCalc(min, max, newItems), - range: { min, max }, values: { min, max } }, () => { this.handleResults(null, { min, max }); }); } - this.updateValues(this.props.defaultSelected); + const defaultValue = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; + this.updateValues(defaultValue); } updateValues(defaultSelected) { if (defaultSelected) { const { min, max } = this.state.range; - const { start, end } = defaultSelected(min, max); + const { start, end } = this.urlParams !== null ? this.urlParams : defaultSelected(min, max); if (start >= min && end <= max) { + const values = { + min: start, + max: end + }; this.setState({ - values: { - min: start, - max: end - } - }); + values + }, this.handleResults.bind(this, null, values)); } else { console.error(`defaultSelected values must lie between ${min} and ${max}`); } @@ -272,7 +301,7 @@ export default class DynamicRangeSlider extends Component { if(this.props.onValueChange) { this.props.onValueChange(obj.value); } - + helper.URLParams.update(this.props.componentId, this.setURLParam(obj.value), this.props.URLParam); helper.selectedSensor.set(obj, true); this.setState({ @@ -280,6 +309,16 @@ export default class DynamicRangeSlider extends Component { }); } + setURLParam(value) { + if("from" in value && "to" in value) { + value = { + start: value.from, + end: value.to + }; + } + return JSON.stringify(value); + } + render() { let title = null, histogram = null, @@ -354,15 +393,16 @@ DynamicRangeSlider.propTypes = { react: React.PropTypes.object, onValueChange: React.PropTypes.func, interval: React.PropTypes.number, - componentStyle: React.PropTypes.object + componentStyle: React.PropTypes.object, + URLParam: React.PropTypes.bool }; DynamicRangeSlider.defaultProps = { title: null, stepValue: 1, showHistogram: true, - interval: 1, - componentStyle: {} + componentStyle: {}, + URLParam: false }; // context type @@ -381,5 +421,6 @@ DynamicRangeSlider.types = { stepValue: TYPES.NUMBER, showHistogram: TYPES.BOOLEAN, customQuery: TYPES.FUNCTION, - initialLoader: TYPES.OBJECT + initialLoader: TYPES.OBJECT, + URLParam: TYPES.BOOLEAN }; diff --git a/app/sensors/NestedList.js b/app/sensors/NestedList.js index 11672a6f20..fe3d00c0de 100644 --- a/app/sensors/NestedList.js +++ b/app/sensors/NestedList.js @@ -34,7 +34,8 @@ export default class NestedList extends Component { }; this.channelId = null; this.channelListener = null; - this.defaultSelected = this.props.defaultSelected; + this.urlParams = helper.URLParams.get(this.props.componentId, true); + this.defaultSelected = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; this.filterBySearch = this.filterBySearch.bind(this); this.onItemSelect = this.onItemSelect.bind(this); this.customQuery = this.customQuery.bind(this); @@ -50,15 +51,14 @@ export default class NestedList extends Component { } componentDidMount() { - if (this.props.defaultSelected) { - this.defaultSelected = this.props.defaultSelected; + if (this.defaultSelected) { setTimeout(this.handleSelect.bind(this), 100); } } handleSelect() { - if (this.props.defaultSelected) { - this.props.defaultSelected.forEach((value, index) => { + if (this.defaultSelected) { + this.defaultSelected.forEach((value, index) => { this.onItemSelect(value, index); }); } @@ -66,8 +66,9 @@ export default class NestedList extends Component { componentWillUpdate() { setTimeout(() => { - if (!_.isEqual(this.defaultSelected, this.props.defaultSelected)) { - this.defaultSelected = this.props.defaultSelected; + const defaultValue = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; + if (!_.isEqual(this.defaultSelected, defaultValue)) { + this.defaultSelected = defaultValue; let items = this.state.items; items = items.map((item) => { item.key = item.key.toString(); @@ -312,6 +313,7 @@ export default class NestedList extends Component { if(this.props.onValueChange) { this.props.onValueChange(obj.value); } + helper.URLParams.update(this.props.componentId, value, this.props.URLParam); helper.selectedSensor.set(obj, isExecuteQuery); } @@ -481,7 +483,8 @@ NestedList.propTypes = { ]), react: React.PropTypes.object, onValueChange: React.PropTypes.func, - componentStyle: React.PropTypes.object + componentStyle: React.PropTypes.object, + URLParam: React.PropTypes.bool }; // Default props value @@ -492,7 +495,8 @@ NestedList.defaultProps = { showSearch: false, title: null, placeholder: "Search", - componentStyle: {} + componentStyle: {}, + URLParam: false }; // context type @@ -513,5 +517,6 @@ NestedList.types = { showSearch: TYPES.BOOLEAN, defaultSelected: TYPES.ARRAY, customQuery: TYPES.FUNCTION, - initialLoader: TYPES.OBJECT + initialLoader: TYPES.OBJECT, + URLParam: TYPES.BOOLEAN }; diff --git a/app/sensors/RatingsFilter.js b/app/sensors/RatingsFilter.js index 72a409dd6f..f6acb5075f 100644 --- a/app/sensors/RatingsFilter.js +++ b/app/sensors/RatingsFilter.js @@ -13,7 +13,8 @@ export default class RatingsFilter extends Component { selected: null }; this.type = "range"; - this.defaultSelected = props.defaultSelected; + this.urlParams = helper.URLParams.get(this.props.componentId, false, true); + this.defaultSelected = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; this.handleChange = this.handleChange.bind(this); this.customQuery = this.customQuery.bind(this); } @@ -32,8 +33,9 @@ export default class RatingsFilter extends Component { componentWillUpdate() { setTimeout(() => { - if (this.defaultSelected && this.defaultSelected.start !== this.props.defaultSelected.start) { - this.defaultSelected = this.props.defaultSelected; + const defaultValue = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; + if (this.defaultSelected && this.defaultSelected.start !== defaultValue.start) { + this.defaultSelected = defaultValue; const records = this.props.data.filter(record => (record.start === this.defaultSelected.start && record.end === this.defaultSelected.end)); if (records && records.length) { @@ -86,6 +88,7 @@ export default class RatingsFilter extends Component { } // pass the selected sensor value with componentId as key, const isExecuteQuery = true; + helper.URLParams.update(this.props.componentId, JSON.stringify(record), this.props.URLParam); helper.selectedSensor.set(obj, isExecuteQuery); } @@ -156,13 +159,15 @@ RatingsFilter.propTypes = { defaultSelected: React.PropTypes.object, customQuery: React.PropTypes.func, onValueChange: React.PropTypes.func, - componentStyle: React.PropTypes.object + componentStyle: React.PropTypes.object, + URLParam: React.PropTypes.bool }; // Default props value RatingsFilter.defaultProps = { title: null, - componentStyle: {} + componentStyle: {}, + URLParam: false }; // context type @@ -177,5 +182,6 @@ RatingsFilter.types = { title: TYPES.STRING, data: TYPES.OBJECT, defaultSelected: TYPES.OBJECT, - customQuery: TYPES.FUNCTION + customQuery: TYPES.FUNCTION, + URLParam: TYPES.BOOLEAN }; diff --git a/app/sensors/TagCloud.js b/app/sensors/TagCloud.js index 5670cc5e69..cac5f88954 100644 --- a/app/sensors/TagCloud.js +++ b/app/sensors/TagCloud.js @@ -30,7 +30,8 @@ export default class TagCloud extends Component { this.previousSelectedSensor = {}; this.channelId = null; this.channelListener = null; - this.defaultSelected = props.defaultSelected; + this.urlParams = helper.URLParams.get(this.props.componentId, this.props.multiSelect); + this.defaultSelected = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; this.type = this.props.multiSelect ? "Terms" : "Term"; this.customQuery = this.customQuery.bind(this); this.defaultCustomQuery = this.defaultCustomQuery.bind(this); @@ -45,8 +46,9 @@ export default class TagCloud extends Component { componentWillUpdate() { setTimeout(() => { - if (this.props.multiSelect && !_.isEqual(this.defaultSelected, this.props.defaultSelected)) { - this.defaultSelected = this.props.defaultSelected; + const defaultValue = this.urlParams !== null ? this.urlParams : this.props.defaultSelected; + if (this.props.multiSelect && !_.isEqual(this.defaultSelected, defaultValue)) { + this.defaultSelected = defaultValue; const items = this.state.items.map((item) => { item.status = ((this.defaultSelected && this.defaultSelected.indexOf(item.key) > -1) || (this.selectedValue && this.selectedValue.indexOf(item.key) > -1)); return item; @@ -64,9 +66,10 @@ export default class TagCloud extends Component { key: this.props.componentId, value: this.selectedValue }; + helper.URLParams.update(this.props.componentId, obj.value, this.props.URLParam); helper.selectedSensor.set(obj, true); - } else if (!this.props.multiSelect && this.defaultSelected !== this.props.defaultSelected) { - this.defaultSelected = this.props.defaultSelected; + } else if (!this.props.multiSelect && this.defaultSelected !== defaultValue) { + this.defaultSelected = defaultValue; const items = this.state.items.map((item) => { if (this.defaultSelected && this.defaultSelected === item.key) { item.status = !item.status; @@ -88,6 +91,7 @@ export default class TagCloud extends Component { key: this.props.componentId, value: this.selectedValue }; + helper.URLParams.update(this.props.componentId, obj.value, this.props.URLParam); helper.selectedSensor.set(obj, true); } }, 300); @@ -266,6 +270,7 @@ export default class TagCloud extends Component { key: this.props.componentId, value: this.selectedValue }; + helper.URLParams.update(this.props.componentId, obj.value, this.props.URLParam); helper.selectedSensor.set(obj, true); } @@ -342,7 +347,8 @@ TagCloud.propTypes = { ]), react: React.PropTypes.object, onValueChange: React.PropTypes.func, - componentStyle: React.PropTypes.object + componentStyle: React.PropTypes.object, + URLParam: React.PropTypes.bool }; TagCloud.defaultProps = { @@ -350,7 +356,8 @@ TagCloud.defaultProps = { multiSelect: false, size: 100, title: null, - componentStyle: {} + componentStyle: {}, + URLParam: false }; TagCloud.contextTypes = { @@ -368,5 +375,6 @@ TagCloud.types = { customQuery: TYPES.FUNCTION, initialLoader: TYPES.STRING, defaultSelected: TYPES.STRING, - react: TYPES.OBJECT + react: TYPES.OBJECT, + URLParam: TYPES.BOOLEAN }; diff --git a/app/sensors/ToggleList.js b/app/sensors/ToggleList.js index aa9a1af6ec..b76ef5daf4 100644 --- a/app/sensors/ToggleList.js +++ b/app/sensors/ToggleList.js @@ -14,6 +14,7 @@ export default class ToggleList extends Component { selected: [] }; this.type = "term"; + this.urlParams = helper.URLParams.get(this.props.componentId, true); this.defaultSelected = null; this.handleChange = this.handleChange.bind(this); this.customQuery = this.customQuery.bind(this); @@ -30,13 +31,38 @@ export default class ToggleList extends Component { } initialize(props) { - if (props.defaultSelected) { - if (!props.multiSelect) { - if (typeof props.defaultSelected === "string") { - if (this.defaultSelected !== props.defaultSelected) { - this.defaultSelected = props.defaultSelected; - const records = props.data.filter(record => this.defaultSelected.indexOf(record.label) > -1); + setTimeout(() => { + const defaultValue = this.urlParams !== null ? this.urlParams : props.defaultSelected; + if (defaultValue) { + if (!props.multiSelect) { + if (typeof defaultValue === "string") { + if (this.defaultSelected !== defaultValue) { + this.defaultSelected = defaultValue; + const records = props.data.filter(record => this.defaultSelected.indexOf(record.label) > -1); + this.setState({ + selected: records + }); + if(this.props.onValueChange) { + this.props.onValueChange(obj.value); + } + const obj = { + key: props.componentId, + value: records + }; + helper.URLParams.update(this.props.componentId, this.setURLParam(obj.value), this.props.URLParam); + helper.selectedSensor.set(obj, true); + } + } else { + console.error(`${props.componentId} - defaultSelected prop should be of type "string"`); + } + } else if (typeof defaultValue === "object") { + if (!_.isEqual(this.defaultSelected, defaultValue)) { + this.defaultSelected = defaultValue; + let records = []; + this.defaultSelected.forEach((item) => { + records = records.concat(props.data.filter(record => item.indexOf(record.label) > -1)); + }); this.setState({ selected: records }); @@ -47,34 +73,14 @@ export default class ToggleList extends Component { key: props.componentId, value: records }; + helper.URLParams.update(this.props.componentId, this.setURLParam(obj.value), this.props.URLParam); helper.selectedSensor.set(obj, true); } } else { - console.error(`${props.componentId} - defaultSelected prop should be of type "string"`); - } - } else if (typeof props.defaultSelected === "object") { - if (!_.isEqual(this.defaultSelected, props.defaultSelected)) { - this.defaultSelected = props.defaultSelected; - let records = []; - this.defaultSelected.forEach((item) => { - records = records.concat(props.data.filter(record => item.indexOf(record.label) > -1)); - }); - this.setState({ - selected: records - }); - if(this.props.onValueChange) { - this.props.onValueChange(obj.value); - } - const obj = { - key: props.componentId, - value: records - }; - helper.selectedSensor.set(obj, true); + console.error(`${props.componentId} - defaultSelected prop should be an "array"`); } - } else { - console.error(`${props.componentId} - defaultSelected prop should be an "array"`); } - } + }, 100); } // set the query type and input data @@ -148,9 +154,14 @@ export default class ToggleList extends Component { } // pass the selected sensor value with componentId as key, const isExecuteQuery = true; + helper.URLParams.update(this.props.componentId, this.setURLParam(obj.value), this.props.URLParam); helper.selectedSensor.set(obj, isExecuteQuery); } + setURLParam(value) { + return value.map(item => item.label); + } + renderList() { let list; const selectedText = this.state.selected.map(record => record.label); @@ -216,13 +227,15 @@ ToggleList.propTypes = { multiSelect: React.PropTypes.bool, customQuery: React.PropTypes.func, onValueChange: React.PropTypes.func, - componentStyle: React.PropTypes.object + componentStyle: React.PropTypes.object, + URLParam: React.PropTypes.bool }; // Default props value ToggleList.defaultProps = { multiSelect: true, - componentStyle: {} + componentStyle: {}, + URLParam: false }; // context type @@ -238,5 +251,6 @@ ToggleList.types = { data: TYPES.OBJECT, defaultSelected: TYPES.ARRAY, multiSelect: TYPES.BOOLEAN, - customQuery: TYPES.FUNCTION + customQuery: TYPES.FUNCTION, + URLParam: TYPES.BOOLEAN }; diff --git a/examples/news/news.scss b/examples/news/news.scss index 73af08df00..61b60b6d63 100644 --- a/examples/news/news.scss +++ b/examples/news/news.scss @@ -112,8 +112,6 @@ nav { } .rbc-resultlist-scroll-container { - height: auto !important; - .rbc-resultlist-item { border-bottom: 1px solid #eaeaea; padding: 15px; diff --git a/package.json b/package.json index 962e899021..2dc278f24d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@appbaseio/reactivesearch", - "version": "0.4.6", + "version": "0.4.7", "description": "A React UI components library for building search experiences", "main": "lib/app.js", "scripts": { @@ -32,8 +32,8 @@ }, "homepage": "https://github.com/appbaseio/reactivesearch#readme", "dependencies": { - "@appbaseio/reactivebase": "1.0.0-alpha22", - "@appbaseio/reactivemaps": "1.0.0-alpha16", + "@appbaseio/reactivebase": "1.0.0-alpha23", + "@appbaseio/reactivemaps": "1.0.0-alpha17", "classnames": "^2.2.5", "jquery": "^3.1.1", "lodash": "^4.15.0",