AG-420 Improve React implementation

This commit is contained in:
Sean Landsman
2017-05-19 11:28:58 +01:00
parent 8677118a27
commit a438cb6169
9 changed files with 293 additions and 3049 deletions

View File

@@ -48,6 +48,7 @@ export default class extends Component {
// grid events
this.onGridReady = this.onGridReady.bind(this);
this.onRowClicked = this.onRowClicked.bind(this);
// component events
this.updateQuote = this.updateQuote.bind(this);
@@ -70,6 +71,10 @@ export default class extends Component {
this.gridApi.sizeColumnsToFit();
}
onRowClicked(params) {
this.props.onRowClicked(params.data.symbol);
}
componentWillMount() {
this.props.selectedExchange.supportedStocks.forEach(symbol => {
this.exchangeService.addSubscriber(this.updateQuote, symbol);
@@ -151,7 +156,8 @@ export default class extends Component {
enableSorting="true"
// events
onGridReady={this.onGridReady}>
onGridReady={this.onGridReady}
onRowClicked={this.onRowClicked}>
</AgGridReact>
</div>
);

View File

@@ -1,5 +1,7 @@
import React, {Component} from "react";
import ExchangeService from "../services/ExchangeService.jsx";
import StockPriceDeltaPanel from "./StockPriceDeltaPanel.jsx";
import StockTimestampPanel from "./StockTimestampPanel.jsx";
import StockSummaryPanel from "./StockSummaryPanel.jsx";
@@ -8,15 +10,42 @@ import StockHistoricalChartPanel from "./StockHistoricalChartPanel.jsx";
export default class extends Component {
constructor(props) {
super(props);
this.exchangeService = new ExchangeService();
this.state = {
priceDelta: null,
timestamp: null,
tickerSummary: null,
historicalData: null
}
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.selectedSymbol !== this.props.selectedSymbol;
}
componentWillReceiveProps(nextProps, nextState) {
if (nextProps.selectedSymbol !== this.props.selectedSymbol) {
let stockDetail = this.exchangeService.getTickerDetail(nextProps.selectedSymbol);
this.setState({
pricingDelta: stockDetail.pricingDelta,
timestamp: stockDetail.timestamp,
tickerSummary: stockDetail.tickerSummary,
historicalData: stockDetail.historicalData
})
}
}
render() {
return (
<div>
<StockPriceDeltaPanel />
<StockTimestampPanel />
<StockSummaryPanel />
<StockHistoricalChartPanel />
<StockPriceDeltaPanel pricingDelta={this.state.pricingDelta}/>
<StockTimestampPanel timestamp={this.state.timestamp}
exchangeName={this.props.exchangeName}/>
<StockSummaryPanel tickerSummary={this.state.tickerSummary}/>
<StockHistoricalChartPanel historicalData={this.state.historicalData} />
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,16 +6,30 @@ import StockDetailPanel from "./StockDetailPanel.jsx";
export default class extends Component {
constructor(props) {
super(props);
this.state = {
selectedSymbol: null
};
this.onRowClicked = this.onRowClicked.bind(this);
}
onRowClicked(selectedSymbol) {
this.setState({
selectedSymbol
})
}
render() {
return (
<div>
<div style={{float: "left", marginRight: 25}}>
<LiveUpdatesGrid selectedExchange={this.props.selectedExchange}></LiveUpdatesGrid>
<LiveUpdatesGrid selectedExchange={this.props.selectedExchange}
onRowClicked={this.onRowClicked} />
</div>
<div style={{float: "left"}}>
<StockDetailPanel></StockDetailPanel>
<StockDetailPanel selectedSymbol={this.state.selectedSymbol}
exchangeName={this.props.selectedExchange.name}/>
</div>
</div>
);

View File

@@ -1,8 +1,38 @@
import React, {Component} from "react";
import isEqual from "lodash/isEqual";
export default class extends Component {
constructor(props) {
super(props);
this.state = {
currentPrice: null,
delta: null,
deltaPercentage: null
}
}
shouldComponentUpdate(nextProps, nextState) {
return !isEqual(this.props.pricingDelta, nextProps.pricingDelta);
}
componentWillReceiveProps(nextProps, nextState) {
if (!isEqual(this.props.pricingDelta, nextProps.pricingDelta)) {
let pricingDelta = nextProps.pricingDelta;
let delta = pricingDelta.currentPrice - pricingDelta.previousPrice;
let deltaPercentage = (pricingDelta.currentPrice - pricingDelta.previousPrice) / pricingDelta.currentPrice;
this.setState({
currentPrice: pricingDelta.currentPrice,
delta,
deltaPercentage
})
}
}
numberFormatter(input) {
return input.toFixed(2);
}
render() {
@@ -32,18 +62,25 @@ export default class extends Component {
marginRight: 5
};
return (
<div>
<span style={priceStyle}>
155.47
</span>
<div style={containerStyle}>
<span style={deltaStyle}>
<span style={negativeSwingStyle}>-0.23</span>
<span style={negativeSwingStyle}>(-0.15%)</span>
let swingStyle = this.state.delta >= 0 ? positiveSwingStyle : negativeSwingStyle;
if(!this.props.pricingDelta) {
return null;
} else {
return (
<div>
<span style={priceStyle}>
{this.numberFormatter(this.state.currentPrice)}
</span>
<div style={containerStyle}>
<span style={deltaStyle}>
<span style={swingStyle}>{this.numberFormatter(this.state.delta)}</span>
<span
style={swingStyle}>({this.numberFormatter(this.state.deltaPercentage)}%)</span>
</span>
</div>
</div>
</div>
);
);
}
}
}

View File

@@ -13,9 +13,9 @@ export default class extends Component {
let tableStyle = {
display: "inline-block",
verticalAlign: "top",
borderCollapse:"collapse"
borderCollapse: "collapse"
};
};
let keyStyle = {
color: "#666"
@@ -25,70 +25,54 @@ export default class extends Component {
textAlign: "right"
};
return (
<div style={containerStyle}>
<table style={tableStyle}>
<tbody>
<tr>
<td style={keyStyle}>Range
</td>
<td style={valueStyle}>154.72 - 156.06
</td>
</tr>
<tr>
<td style={keyStyle}>52 week
</td>
<td style={valueStyle}>91.50 - 156.65
</td>
</tr>
<tr>
<td style={keyStyle}>Open
</td>
<td style={valueStyle}>155.94
</td>
</tr>
<tr>
<td style={keyStyle}>
Vol / Avg.
</td>
<td style={valueStyle}>15,931.00/24.94M
</td>
</tr>
</tbody>
</table>
<table style={tableStyle}>
<tbody>
<tr>
<td style={keyStyle}>
Div/yield
</td>
<td style={valueStyle}>0.63/1.62
</td>
</tr>
<tr>
<td style={keyStyle}>
EPS
</td>
<td style={valueStyle}>8.55
</td>
</tr>
<tr>
<td style={keyStyle}>
Shares
</td>
<td style={valueStyle}>5,213.84M
</td>
</tr>
<tr>
<td style={keyStyle}>
Mkt cap
</td>
<td style={valueStyle}>808,518.60M
</td>
</tr>
</tbody>
</table>
</div>
);
if (!this.props.tickerSummary) {
return null;
} else {
return (
<div style={containerStyle}>
<table style={tableStyle}>
<tbody>
<tr>
<td style={keyStyle}>Range</td>
<td style={valueStyle}>{this.props.tickerSummary.range}</td>
</tr>
<tr>
<td style={keyStyle}>52 week</td>
<td style={valueStyle}>{this.props.tickerSummary.fiftyTwoWeek}</td>
</tr>
<tr>
<td style={keyStyle}>Open</td>
<td style={valueStyle}>{this.props.tickerSummary.open}</td>
</tr>
<tr>
<td style={keyStyle}>Vol / Avg.</td>
<td style={valueStyle}>{this.props.tickerSummary.vol}/{this.props.tickerSummary.avg}
</td>
</tr>
</tbody>
</table>
<table style={tableStyle}>
<tbody>
<tr>
<td style={keyStyle}>Div/yield</td>
<td style={valueStyle}>{this.props.tickerSummary.dividend}/{this.props.tickerSummary.yld}</td>
</tr>
<tr>
<td style={keyStyle}>EPS</td>
<td style={valueStyle}>{this.props.tickerSummary.eps}</td>
</tr>
<tr>
<td style={keyStyle}>Shares</td>
<td style={valueStyle}>{this.props.tickerSummary.shares}</td>
</tr>
<tr>
<td style={keyStyle}>Market Cap</td>
<td style={valueStyle}>{this.props.tickerSummary.marketCap}</td>
</tr>
</tbody>
</table>
</div>
);
}
}
}

View File

@@ -6,41 +6,25 @@ export default class extends Component {
}
render() {
let containerStyle = {
display: "inline-block"
};
let priceStyle = {
fontSize: "2.6em",
fontWeight: "bold",
marginRight: 10
};
let minorStyle = {
fontSize: 11,
color: "#6F6F6F"
};
let negativeSwingStyle = {
color: "#d14836",
marginRight: 5
};
let positiveSwingStyle = {
color: "#093",
marginRight: 5
};
return (
<div>
<div id="ref_304466804484872_elt">
May 17, 6:17am GMT-4
<div style={minorStyle}>
<span>NASDAQ</span>
<div>Currency in USD</div>
if (!this.props.timestamp) {
return null;
} else {
return (
<div>
<div>
{this.props.timestamp}
<div style={minorStyle}>
<span>{this.props.exchangeName}</span>
<div>Currency in USD</div>
</div>
</div>
</div>
</div>
);
);
}
}
}

View File

@@ -36,7 +36,7 @@ export default class TraderDashboard extends Component {
onExchangeChanged={this.onExchangeChanged}>
</ControlPanel>
</div>
<StockPanel selectedExchange={this.state.selectedExchange}></StockPanel>
<StockPanel selectedExchange={this.state.selectedExchange}/>
</div>
);
}

View File

@@ -17,6 +17,8 @@ export default class {
];
this.initialiseTickerData();
this.timestamp = new Date();
}
initialiseTickerData() {
@@ -95,6 +97,102 @@ export default class {
return exchange.symbol === exchangeName;
})
}
formatNumber(input) {
return input.toFixed(2);
}
formatWithDecimalPlaces(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
getTickerDetail(symbol) {
let ticker = this.getTicker(symbol);
let currentPrice = ticker.price;
let tenthOfCurrentPrice = currentPrice / 10;
let previousPrice = +currentPrice + this.random(-tenthOfCurrentPrice, tenthOfCurrentPrice);
let twentiethOfCurrentPrice = currentPrice / 20;
let yearAgoPrice = this.random(-twentiethOfCurrentPrice, twentiethOfCurrentPrice);
let range = `${this.formatNumber(previousPrice)} - ${this.formatNumber(currentPrice)}`;
let fiftyTwoWeek = `${this.formatNumber(yearAgoPrice)} - ${this.formatNumber(currentPrice)}`;
let open = this.formatNumber(ticker.bid); // not the same, but will do for demo purposes
let vol = this.formatWithDecimalPlaces(this.random(5000, 20000).toFixed(2));
let avg = `${this.formatNumber(this.random(10, 30))}M`;
let dividend = this.random(0, 1).toFixed(2);
let yld = this.random(1, 2).toFixed(2);
let eps = this.random(5, 10).toFixed(2);
let shares = `${this.random(3000, 10000).toFixed(2)}M`;
let marketCap = `${this.random(100000, 900000).toFixed(2)}M`;
let historicalData = this.generateHistoricalData(100, this.timestamp, currentPrice);
return {
pricingDelta: {
currentPrice,
previousPrice
},
timestamp: this.timestamp.toDateString(),
tickerSummary: {
range,
fiftyTwoWeek,
open,
vol,
avg,
dividend,
yld,
eps,
shares,
marketCap
},
historicalData
}
}
formatDate(date) {
// todo remove substring(2,4) and change parseTime instead
let year = ("" + date.getFullYear()).substring(2, 4);
return `${date.getDate()}-${date.getMonth()+1}-${year}`
// return `${date.getDate()}-${date.getMonth()+1}-${date.getFullYear()
}
generateHistoricalData(numberOfPoints, endDate, endPrice) {
let historicalData = [{
"date": this.formatDate(endDate),
"price": endPrice
}
];
let numberOfTransitions = 15;
let pointsPerTransition = numberOfPoints / numberOfTransitions;
let lastDate = endDate;
let lastPrice = endPrice;
for (let transition = 0; transition < numberOfTransitions; transition++) {
let swing = (Math.random() >= 0.5) ? 1 : -1;
for (let i = 0; i <= pointsPerTransition; i++) {
lastDate.setDate(lastDate.getDate() - 1);
lastPrice = lastPrice + (swing * this.random(-1, 10));
historicalData.push({
"date": this.formatDate(lastDate),
"price": lastPrice
})
}
}
historicalData = historicalData.reverse();
return historicalData;
}
}
const NASDAQ_SYMBOLS = [