Skip to content

Commit

Permalink
Add RatingsFilter and stories
Browse files Browse the repository at this point in the history
  • Loading branch information
metagrover committed Feb 26, 2017
1 parent f4938f3 commit 7efded2
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 4 deletions.
4 changes: 3 additions & 1 deletion app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import NestedList from "./sensors/NestedList";
import ToggleList from "./sensors/ToggleList";
import DynamicRangeSlider from "./sensors/DynamicRangeSlider";
import TagCloud from "./sensors/TagCloud";
import RatingsFilter from "./sensors/RatingsFilter";

const combineObj = {
NestedList,
ToggleList,
DynamicRangeSlider,
TagCloud
TagCloud,
RatingsFilter
};

Object.keys(reactivebase).forEach((component) => {
Expand Down
20 changes: 20 additions & 0 deletions app/assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,23 @@
background-color: #eee; }
.rbc.rbc-tagcloud .rbc-list-container .rbc-list-item .rbc-count {
padding: 0 0.3em; }

.rbc.rbc-ratingsfilter {
padding: 0;
margin: 0; }
.rbc.rbc-ratingsfilter .row {
margin: 4px 0; }
.rbc.rbc-ratingsfilter .rbc-label {
cursor: pointer; }
.rbc.rbc-ratingsfilter .rbc-label > div {
display: inline-block; }
.rbc.rbc-ratingsfilter .rbc-label > span {
display: inline-block;
margin-left: 8px;
position: relative;
top: -5px; }
.rbc.rbc-ratingsfilter .rbc-label:hover {
color: #424242; }
.rbc.rbc-ratingsfilter .rbc-label.active {
font-family: "Lato Bold";
color: #333; }
32 changes: 32 additions & 0 deletions app/assets/styles/partials/components/_ratingsfilter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.rbc.rbc-ratingsfilter {
padding: 0;
margin: 0;

.row {
margin: 4px 0;
}

.rbc-label {
cursor: pointer;

& > div {
display: inline-block;
}

& > span {
display: inline-block;
margin-left: 8px;
position: relative;
top: -5px;
}

&:hover {
color: #424242;
}

&.active {
font-family: $font-bold;
color: #333;
}
}
}
3 changes: 2 additions & 1 deletion app/assets/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
@import "partials/components/nestedlist",
"partials/components/togglelist",
"partials/components/dynamicrangeslider",
"partials/components/tagcloud";
"partials/components/tagcloud",
"partials/components/ratingsfilter";
165 changes: 165 additions & 0 deletions app/sensors/RatingsFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { Component } from "react";
import classNames from "classnames";
import {
TYPES,
AppbaseSensorHelper as helper
} from "@appbaseio/reactivebase";
import ReactStars from "react-stars";

export default class RatingsFilter extends Component {
constructor(props, context) {
super(props);
this.state = {
selected: null
};
this.type = "range";
this.defaultSelected = this.props.defaultSelected;
this.handleChange = this.handleChange.bind(this);
this.customQuery = this.customQuery.bind(this);
}

// Set query information
componentDidMount() {
this.setQueryInfo();
if (this.defaultSelected && this.defaultSelected.start) {
const records = this.props.data.filter(record => record.start === this.defaultSelected.start);
if (records && records.length) {
setTimeout(this.handleChange.bind(this, records[0]), 300);
}
}
}

componentWillUpdate() {
setTimeout(() => {
if (this.defaultSelected && this.defaultSelected.start !== this.props.defaultSelected.start) {
this.defaultSelected = this.props.defaultSelected;
const records = this.props.data.filter(record => record.start === this.defaultSelected.start);
if (records && records.length) {
setTimeout(this.handleChange.bind(this, records[0]), 300);
}
}
}, 300);
}

// set the query type and input data
setQueryInfo() {
const obj = {
key: this.props.componentId,
value: {
queryType: this.type,
inputData: this.props.appbaseField,
customQuery: this.props.customQuery ? this.props.customQuery : this.customQuery
}
};
helper.selectedSensor.setSensorInfo(obj);
}

// build query for this sensor only
customQuery(record) {
if (record) {
return {
range: {
[this.props.appbaseField]: {
gte: record.start,
lte: record.end,
boost: 2.0
}
}
};
}
return null;
}

// handle the input change and pass the value inside sensor info
handleChange(record) {
this.setState({
selected: record
});
const obj = {
key: this.props.componentId,
value: record
};
// pass the selected sensor value with componentId as key,
const isExecuteQuery = true;
helper.selectedSensor.set(obj, isExecuteQuery);
}

renderButtons() {
let buttons;
const selectedItem = this.state.selected && this.state.selected.start ? this.state.selected.start : this.props.data.start;
if (this.props.data) {
buttons = this.props.data.map((record) => {
const cx = selectedItem === record.start ? "active" : "";
return (
<div className="rbc-list-item row" key={record.label} onClick={() => this.handleChange(record)}>
<label className={`rbc-label ${cx}`}>
<ReactStars
count={5}
value={record.start}
size={20}
color1={"#bbb"}
edit={false}
color2={"#ffd700"}
/>
<span>{record.label}</span>
</label>
</div>
);
});
}
return buttons;
}

// render
render() {
let title = null;
if (this.props.title) {
title = (<h4 className="rbc-title col s12 col-xs-12">{this.props.title}</h4>);
}

const cx = classNames({
"rbc-title-active": this.props.title,
"rbc-title-inactive": !this.props.title
});

return (
<div className={`rbc rbc-ratingsfilter col s12 col-xs-12 card thumbnail ${cx}`}>
<div className="row">
{title}
<div className="col s12 col-xs-12 rbc-list-container">
{this.renderButtons()}
</div>
</div>
</div>
);
}
}

RatingsFilter.propTypes = {
componentId: React.PropTypes.string.isRequired,
appbaseField: React.PropTypes.string.isRequired,
title: React.PropTypes.string,
data: React.PropTypes.any.isRequired,
defaultSelected: React.PropTypes.string,
customQuery: React.PropTypes.func
};

// Default props value
RatingsFilter.defaultProps = {
title: null
};

// context type
RatingsFilter.contextTypes = {
appbaseRef: React.PropTypes.any.isRequired,
type: React.PropTypes.any.isRequired
};

RatingsFilter.types = {
componentId: TYPES.STRING,
appbaseField: TYPES.STRING,
title: TYPES.STRING,
data: TYPES.OBJECT,
defaultSelected: TYPES.STRING,
customQuery: TYPES.FUNCTION
};
113 changes: 113 additions & 0 deletions app/stories/RatingsFilter.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, { Component } from "react";
import { ReactiveBase, RatingsFilter, ReactiveList, AppbaseSensorHelper as helper } from "../app";

require("./list.css");

export default class RatingsFilterDefault extends Component {
constructor(props) {
super(props);
this.onData = this.onData.bind(this);
}

componentDidMount() {
helper.ResponsiveStory();
}

onData(res) {
let result = null;
if (res) {
let combineData = res.currentData;
if (res.mode === "historic") {
combineData = res.currentData.concat(res.newData);
} else if (res.mode === "streaming") {
combineData = helper.combineStreamData(res.currentData, res.newData);
}
if (combineData) {
result = combineData.map((markerData) => {
const marker = markerData._source;
return this.itemMarkup(marker, markerData);
});
}
}
return result;
}

itemMarkup(marker, markerData) {
return (
<a
className="full_row single-record single_record_for_clone"
key={markerData._id}
>
<div className="text-container full_row" style={{ paddingLeft: "10px" }}>
<div className="text-head text-overflow full_row">
<span className="text-head-info text-overflow">
{marker.name ? marker.name : ""} - {marker.brand ? marker.brand : ""}
</span>
<span className="text-head-city">{marker.brand ? marker.brand : ""}</span>
</div>
<div className="text-description text-overflow full_row">
<ul className="highlight_tags">
{marker.price ? `Priced at $${marker.price}` : "Free Test Drive"}
{`Rated ${marker.rating}`}
</ul>
</div>
</div>
</a>
);
}

render() {
return (
<ReactiveBase
app="car-store"
username="cf7QByt5e"
password="d2d60548-82a9-43cc-8b40-93cbbe75c34c"
>
<div className="row">
<div className="col s6 col-xs-6">
<RatingsFilter
componentId="RatingsSensor"
appbaseField={this.props.mapping.rating}
title="RatingsFilter"
data={
[{ start: 4, end: 5, label: "4 stars and up" },
{ start: 3, end: 5, label: "3 stars and up" },
{ start: 2, end: 5, label: "2 stars and up" },
{ start: 1, end: 5, label: "> 1 stars" }]
}
{...this.props}
/>
</div>

<div className="col s6 col-xs-6">
<ReactiveList
componentId="SearchResult"
appbaseField={this.props.mapping.name}
title="Results"
from={0}
size={20}
onData={this.onData}
react={{
and: "RatingsSensor"
}}
/>
</div>
</div>
</ReactiveBase>
);
}
}

RatingsFilterDefault.defaultProps = {
mapping: {
rating: "rating",
name: "name"
}
};

RatingsFilterDefault.propTypes = {
mapping: React.PropTypes.shape({
rating: React.PropTypes.number,
name: React.PropTypes.string
})
};
16 changes: 16 additions & 0 deletions app/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import NestedListReadme from "@appbaseio/reactivemaps-manual/docs/v1/components/
import ToggleButtonReadme from "@appbaseio/reactivemaps-manual/docs/v1/components/ToggleButton.md";
import RangeSliderReadme from "@appbaseio/reactivemaps-manual/docs/v1/components/RangeSlider.md";
import SingleListReadme from "@appbaseio/reactivemaps-manual/docs/v1/components/SingleList.md";
import SingleRangeReadme from "@appbaseio/reactivemaps-manual/docs/v1/components/SingleRange.md";

import NestedListDefault from "./NestedList.stories";
import ToggleListDefault from "./ToggleList.stories";
import DynamicRangeSliderDefault from "./DynamicRangeSlider.stories";
import TagCloudDefault from "./TagCloud.stories";
import RatingsFilterDefault from "./RatingsFilter.stories";

require("../../node_modules/materialize-css/dist/css/materialize.min.css");
require("../../dist/css/style.min.css");
Expand Down Expand Up @@ -113,3 +115,17 @@ storiesOf("TagCloud", module)
showCount={boolean("showCount", true)}
/>
)));

storiesOf("RatingsFilter", module)
.addDecorator(withKnobs)
.add("Basic", withReadme(removeFirstLine(SingleRangeReadme), () => (
<RatingsFilterDefault />
)))
.add("With defaultSelected", withReadme(removeFirstLine(SingleRangeReadme), () => (
<RatingsFilterDefault defaultSelected={{ start: 2, end: 5 }} />
)))
.add("Playground", withReadme(removeFirstLine(SingleRangeReadme), () => (
<RatingsFilterDefault
title={text("title", "RatingsFilter")}
/>
)));
2 changes: 1 addition & 1 deletion dist/css/style.min.css

Large diffs are not rendered by default.

0 comments on commit 7efded2

Please sign in to comment.