diff --git a/client/configuration/eslint/eslint.js b/client/configuration/eslint/eslint.js index 6aa1a0c1..897a3bf9 100644 --- a/client/configuration/eslint/eslint.js +++ b/client/configuration/eslint/eslint.js @@ -1,7 +1,7 @@ module.exports = { root: true, parser: "babel-eslint", - extends: "airbnb", + extends: ["airbnb", "prettier", "prettier/react"], env: { browser: true, commonjs: true, es6: true }, globals: { expect: true }, parserOptions: { @@ -14,6 +14,7 @@ module.exports = { }, rules: { "no-magic-numbers": "off", + "no-nested-ternary": "off", "func-style": "off", "arrow-parens": "off", "no-use-before-define": "off", diff --git a/client/package-lock.json b/client/package-lock.json index 637e0de5..6f9ce9e3 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -2054,7 +2054,7 @@ }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", "dev": true }, @@ -2489,7 +2489,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2526,7 +2526,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2580,7 +2580,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -3258,7 +3258,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3271,7 +3271,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -3945,7 +3945,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -4376,6 +4376,23 @@ "object.entries": "^1.0.4" } }, + "eslint-config-prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-3.1.0.tgz", + "integrity": "sha512-QYGfmzuc4q4J6XIhlp8vRKdI/fI0tQfQPy1dME3UOLprE+v4ssH/3W9LM2Q7h5qBcy5m0ehCrBDU2YF8q6OY8w==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, "eslint-import-resolver-node": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", @@ -4522,7 +4539,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -4699,7 +4716,7 @@ }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, "evp_bytestokey": { @@ -4993,7 +5010,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -5717,7 +5734,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -6096,7 +6113,7 @@ }, "html-webpack-plugin": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { @@ -6172,7 +6189,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -6395,7 +6412,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -7394,7 +7411,7 @@ }, "json5": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, @@ -7484,7 +7501,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -7813,7 +7830,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -7938,7 +7955,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, @@ -7983,7 +8000,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -9695,7 +9712,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -10291,7 +10308,7 @@ }, "react-redux": { "version": "5.0.7", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz", + "resolved": "http://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz", "integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==", "requires": { "hoist-non-react-statics": "^2.5.0", @@ -10355,7 +10372,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -11373,7 +11390,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -11560,7 +11577,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -12014,7 +12031,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -12250,7 +12267,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -12889,7 +12906,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -13547,7 +13564,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -13655,7 +13672,7 @@ }, "yargs": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { diff --git a/client/package.json b/client/package.json index f0531953..2a2e363c 100644 --- a/client/package.json +++ b/client/package.json @@ -77,6 +77,7 @@ "css-loader": "^1.0.0", "eslint": "^5.3.0", "eslint-config-airbnb": "^17.1.0", + "eslint-config-prettier": "^3.1.0", "eslint-loader": "^2.1.1", "eslint-plugin-filenames": "^1.3.2", "eslint-plugin-import": "^2.14.0", diff --git a/client/src/components/brushableHistogram/index.js b/client/src/components/brushableHistogram/index.js index f1f97f78..8dc06062 100644 --- a/client/src/components/brushableHistogram/index.js +++ b/client/src/components/brushableHistogram/index.js @@ -7,10 +7,10 @@ https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771 import React from "react"; import _ from "lodash"; import { connect } from "react-redux"; -import { kvCache } from "../../util/stateManager/"; import { FaPaintBrush } from "react-icons/fa"; import * as d3 from "d3"; import memoize from "memoize-one"; +import { kvCache } from "../../util/stateManager"; import * as globals from "../../globals"; import actions from "../../actions"; @@ -33,7 +33,6 @@ class HistogramBrush extends React.Component { histogramCache.y = d3 .scaleLinear() .range([this.height - this.marginBottom, 0]); - // .range([height - margin.bottom, margin.top]); if (obsAnnotations[0][field]) { // recalculate expensive stuff @@ -116,6 +115,7 @@ class HistogramBrush extends React.Component { drawHistogram(svgRef) { const { obsAnnotations, field, ranges } = this.props; + const { brush, axis } = this.state; const histogramCache = this.calcHistogramCache( obsAnnotations, field, @@ -140,8 +140,8 @@ class HistogramBrush extends React.Component { .attr("width", d => Math.abs(x(d.x1) - x(d.x0) - 1)) .attr("height", d => y(0) - y(d.length / numValues)); - if (!this.state.brush && !this.state.axis) { - const brush = d3 + if (!brush && !axis) { + const newBrush = d3 .select(svgRef) .append("g") .attr("class", "brush") @@ -153,12 +153,6 @@ class HistogramBrush extends React.Component { .attr("class", "axis axis--x") .attr("transform", `translate(0,${this.height - this.marginBottom})`) .call(d3.axisBottom(x).ticks(5)); - // .append("text") - // .attr("x", this.width - 2) - // .attr("y", -6) - // .attr("fill", "#000") - // // .attr("text-anchor", "end") - // // .text(field); d3.select(svgRef) .selectAll(".axis--x text") @@ -173,14 +167,13 @@ class HistogramBrush extends React.Component { .selectAll(".axis--x line") .style("stroke", "rgb(230,230,230)"); - this.setState({ brush, xAxis }); + this.setState({ brush: newBrush, xAxis }); // eslint-disable-line react/no-unused-state } } handleColorAction() { const { obsAnnotations, - ranges, dispatch, field, world, diff --git a/client/src/components/brushableHistogram/util.js b/client/src/components/brushableHistogram/util.js deleted file mode 100644 index 47b56dfc..00000000 --- a/client/src/components/brushableHistogram/util.js +++ /dev/null @@ -1,26 +0,0 @@ -import * as globals from "../../globals"; -import _ from "lodash"; -import * as constants from "./constants"; - -export const calcHistogram = (ranges, allValuesForContinuousFieldAsArray) => { - const histogramData = {}; - - histogramData.x = d3 - .scaleLinear() - .domain([ranges.min, ranges.max]) - .range([0, constants.width]); - - histogramData.y = d3 - .scaleLinear() - .range([constants.height - constants.marginBottom, 0]); - // .range([height - margin.bottom, margin.top]); - - histogramData.bins = d3 - .histogram() - .domain(histogramData.x.domain()) - .thresholds(40)(allValuesForContinuousFieldAsArray); - - histogramData.numValues = allValuesForContinuousFieldAsArray.length; - - return histogramData; -}; diff --git a/client/src/components/categorical/categorical.js b/client/src/components/categorical/categorical.js index 3e804e5a..8e7721b4 100644 --- a/client/src/components/categorical/categorical.js +++ b/client/src/components/categorical/categorical.js @@ -3,169 +3,7 @@ import React from "react"; import _ from "lodash"; import { connect } from "react-redux"; -import { FaChevronRight, FaChevronDown, FaPaintBrush } from "react-icons/fa"; - -import * as globals from "../../globals"; -import Value from "./value"; -import { alphabeticallySortedValues } from "./util"; - -@connect(state => ({ - colorAccessor: state.controls.colorAccessor, - categoricalAsBooleansMap: state.controls.categoricalAsBooleansMap -})) -class Category extends React.Component { - constructor(props) { - super(props); - this.state = { - isChecked: true, - isExpanded: false - }; - } - - componentDidUpdate() { - const { categoricalAsBooleansMap, metadataField } = this.props; - - const valuesAsBool = _.values(categoricalAsBooleansMap[metadataField]); - /* count categories toggled on by counting true values */ - const categoriesToggledOn = _.values(valuesAsBool).filter(v => v).length; - - if (categoriesToggledOn === valuesAsBool.length) { - /* everything is on, so not indeterminate */ - this.checkbox.indeterminate = false; - } else if (categoriesToggledOn === 0) { - /* nothing is on, so no */ - this.checkbox.indeterminate = false; - } else if (categoriesToggledOn < valuesAsBool.length) { - /* to be explicit... */ - this.checkbox.indeterminate = true; - } - } - - handleColorChange() { - const { dispatch, metadataField } = this.props; - dispatch({ - type: "color by categorical metadata", - colorAccessor: metadataField - }); - } - - toggleAll() { - const { dispatch, metadataField } = this.props; - dispatch({ - type: "categorical metadata filter all of these", - metadataField - }); - this.setState({ isChecked: true }); - } - - toggleNone() { - const { dispatch, metadataField, value } = this.props; - dispatch({ - type: "categorical metadata filter none of these", - metadataField, - value - }); - this.setState({ isChecked: false }); - } - - handleToggleAllClick() { - const { isChecked } = this.state; - // || this.checkbox.indeterminate === false - if (isChecked) { - this.toggleNone(); - } else if (!isChecked) { - this.toggleAll(); - } - } - - renderCategoryItems() { - const { values, metadataField } = this.props; - return _.map(alphabeticallySortedValues(values), (v, i) => ( - - )); - } - - render() { - const { isExpanded, isChecked } = this.state; - const { metadataField, colorAccessor } = this.props; - return ( -
-
-

- { - this.setState({ isExpanded: !isExpanded }); - }} - > - {isExpanded ? : } - - {metadataField} - (this.checkbox = el)} - checked={isChecked} - type="checkbox" - /> - - - -

-
-
{isExpanded ? this.renderCategoryItems() : null}
-
- ); - } -} +import Category from "./category"; @connect(state => { const ranges = _.get(state.controls.world, "summary.obs", null); @@ -202,6 +40,7 @@ class Categories extends React.Component { ); } + return undefined; })} ); diff --git a/client/src/components/categorical/category.js b/client/src/components/categorical/category.js new file mode 100644 index 00000000..da0c5ad2 --- /dev/null +++ b/client/src/components/categorical/category.js @@ -0,0 +1,171 @@ +import React from "react"; +import _ from "lodash"; +import { connect } from "react-redux"; +import { FaChevronRight, FaChevronDown, FaPaintBrush } from "react-icons/fa"; + +import * as globals from "../../globals"; +import Value from "./value"; +import alphabeticallySortedValues from "./util"; + +@connect(state => ({ + colorAccessor: state.controls.colorAccessor, + categoricalAsBooleansMap: state.controls.categoricalAsBooleansMap +})) +class Category extends React.Component { + constructor(props) { + super(props); + this.state = { + isChecked: true, + isExpanded: false + }; + } + + componentDidUpdate() { + const { categoricalAsBooleansMap, metadataField } = this.props; + + const valuesAsBool = _.values(categoricalAsBooleansMap[metadataField]); + /* count categories toggled on by counting true values */ + const categoriesToggledOn = _.values(valuesAsBool).filter(v => v).length; + + if (categoriesToggledOn === valuesAsBool.length) { + /* everything is on, so not indeterminate */ + this.checkbox.indeterminate = false; + } else if (categoriesToggledOn === 0) { + /* nothing is on, so no */ + this.checkbox.indeterminate = false; + } else if (categoriesToggledOn < valuesAsBool.length) { + /* to be explicit... */ + this.checkbox.indeterminate = true; + } + } + + handleColorChange() { + const { dispatch, metadataField } = this.props; + dispatch({ + type: "color by categorical metadata", + colorAccessor: metadataField + }); + } + + toggleAll() { + const { dispatch, metadataField } = this.props; + dispatch({ + type: "categorical metadata filter all of these", + metadataField + }); + this.setState({ isChecked: true }); + } + + toggleNone() { + const { dispatch, metadataField, value } = this.props; + dispatch({ + type: "categorical metadata filter none of these", + metadataField, + value + }); + this.setState({ isChecked: false }); + } + + handleToggleAllClick() { + const { isChecked } = this.state; + // || this.checkbox.indeterminate === false + if (isChecked) { + this.toggleNone(); + } else if (!isChecked) { + this.toggleAll(); + } + } + + renderCategoryItems() { + const { values, metadataField } = this.props; + return _.map(alphabeticallySortedValues(values), (v, i) => ( + + )); + } + + render() { + const { isExpanded, isChecked } = this.state; + const { metadataField, colorAccessor } = this.props; + return ( +
+
+

+ { + this.setState({ isExpanded: !isExpanded }); + }} + > + {isExpanded ? : } + + {metadataField} + { + this.checkbox = el; + return el; + }} + checked={isChecked} + type="checkbox" + /> + + + +

+
+
{isExpanded ? this.renderCategoryItems() : null}
+
+ ); + } +} + +export default Category; diff --git a/client/src/components/categorical/util.js b/client/src/components/categorical/util.js index d55795ca..1f1dfd4a 100644 --- a/client/src/components/categorical/util.js +++ b/client/src/components/categorical/util.js @@ -1,5 +1,5 @@ // jshint esversion: 6 -export const alphabeticallySortedValues = values => +export default values => Object.keys(values).sort((a, b) => { const textA = a.toUpperCase(); const textB = b.toUpperCase(); diff --git a/client/src/components/continuous/continuous.js b/client/src/components/continuous/continuous.js index 37f0d1ba..037a0aef 100644 --- a/client/src/components/continuous/continuous.js +++ b/client/src/components/continuous/continuous.js @@ -5,18 +5,16 @@ import React from "react"; import _ from "lodash"; import { connect } from "react-redux"; -import HistogramBrush from "../brushableHistogram/"; +import HistogramBrush from "../brushableHistogram"; -@connect(state => { - return { - ranges: _.get(state.controls.world, "summary.obs", null), - metadata: _.get(state.controls.world, "obsAnnotations", null), - colorAccessor: state.controls.colorAccessor, - colorScale: state.controls.colorScale, - selectionUpdate: _.get(state.controls, "crossfilter.updateTime", null), - schema: _.get(state.controls.world, "schema", null) - }; -}) +@connect(state => ({ + ranges: _.get(state.controls.world, "summary.obs", null), + metadata: _.get(state.controls.world, "obsAnnotations", null), + colorAccessor: state.controls.colorAccessor, + colorScale: state.controls.colorScale, + selectionUpdate: _.get(state.controls, "crossfilter.updateTime", null), + schema: _.get(state.controls.world, "schema", null) +})) class Continuous extends React.Component { constructor(props) { super(props); @@ -26,6 +24,8 @@ class Continuous extends React.Component { this.state = {}; } + componentDidUpdate() {} + handleColorAction(key) { return () => { const { dispatch, ranges } = this.props; @@ -40,9 +40,10 @@ class Continuous extends React.Component { render() { const { ranges, obsAnnotations, schema } = this.props; if (schema && !this.continuousChecked) { - this.hasContinuous = _.some(schema.annotations.obs, d => { - return d.type === "int32" || d.type === "float32"; - }); + this.hasContinuous = _.some( + schema.annotations.obs, + d => d.type === "int32" || d.type === "float32" + ); this.continuousChecked = true; /* only do this once */ } diff --git a/client/src/components/continuous/util.js b/client/src/components/continuous/util.js index 212fe177..8a156726 100644 --- a/client/src/components/continuous/util.js +++ b/client/src/components/continuous/util.js @@ -39,10 +39,8 @@ export const brushstart = () => { d3.event.sourceEvent.stopPropagation(); }; -export const d3_functor = v => { - return typeof v === "function" ? v : () => v; -}; - +// Unused. +// export const d3_functor = v => (typeof v === "function" ? v : () => v); export const project = (d, dimensions, xscale) => dimensions.map((p, i) => { // check if data element has property and contains a value diff --git a/client/src/components/expression/cellSetButtons.js b/client/src/components/expression/cellSetButtons.js index ac4364ea..24cd73e1 100644 --- a/client/src/components/expression/cellSetButtons.js +++ b/client/src/components/expression/cellSetButtons.js @@ -3,32 +3,35 @@ import React from "react"; import _ from "lodash"; import { connect } from "react-redux"; import * as globals from "../../globals"; -import styles from "../framework/buttons.css"; -import actions from "../../actions"; @connect() class CellSetButton extends React.Component { set() { - const { differential } = this.props; + const { + differential, + crossfilter, + dispatch, + eitherCellSetOneOrTwo + } = this.props; - const set = _.map(this.props.crossfilter.allFiltered(), "name"); + const set = _.map(crossfilter.allFiltered(), "name"); if (!differential.diffExp) { /* diffexp needs to be cleared before we store a new set */ - this.props.dispatch({ - type: - "store current cell selection as differential set " + - this.props.eitherCellSetOneOrTwo, + dispatch({ + type: `store current cell selection as differential set ${eitherCellSetOneOrTwo}`, data: set }); } } render() { - const { differential } = this.props; + const { differential, eitherCellSetOneOrTwo } = this.props; + const cellListName = `celllist${eitherCellSetOneOrTwo}`; return ( @@ -70,17 +69,3 @@ class CellSetButton extends React.Component { } export default CellSetButton; - -// -// diff --git a/client/src/components/expression/diffExpHeatmap.js b/client/src/components/expression/diffExpHeatmap.js index 002a9711..53810e66 100644 --- a/client/src/components/expression/diffExpHeatmap.js +++ b/client/src/components/expression/diffExpHeatmap.js @@ -1,203 +1,10 @@ // jshint esversion: 6 import React from "react"; import _ from "lodash"; -import memoize from "memoize-one"; import { connect } from "react-redux"; -// import ReactAutocomplete from "react-autocomplete"; /* http://emilebres.github.io/react-virtualized-checkbox/ */ -import getContrast from "font-color-contrast"; // https://www.npmjs.com/package/font-color-contrast -import { FaPaintBrush } from "react-icons/fa"; import * as d3 from "d3"; import { interpolateGreys } from "d3-scale-chromatic"; -import * as globals from "../../globals"; -import actions from "../../actions"; - -class HeatmapSquare extends React.Component { - constructor(props) { - super(props); - this.state = { - value: "" - }; - } - - render() { - const { backgroundColor, text } = this.props; - const contrastColor = getContrast( - backgroundColor - .substring(4, backgroundColor.length - 1) - .replace(/ /g, "") - .split(",") - ); - return ( -

- {text} -

- ); - } -} - -/********************************** -*********************************** -*********************************** - Row -*********************************** -*********************************** -**********************************/ -@connect(state => ({ - scatterplotXXaccessor: state.controls.scatterplotXXaccessor, - scatterplotYYaccessor: state.controls.scatterplotYYaccessor, - colorAccessor: state.controls.colorAccessor -})) -class HeatmapRow extends React.Component { - constructor(props) { - super(props); - this.state = { - value: "" - }; - } - - handleGeneColorScaleClick() { - return () => { - const { dispatch, gene } = this.props; - dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(gene)); - }; - } - - handleSetGeneAsScatterplotX() { - return () => { - const { dispatch, gene } = this.props; - dispatch({ - type: "set scatterplot x", - data: gene - }); - }; - } - - handleSetGeneAsScatterplotY() { - return () => { - const { dispatch, gene } = this.props; - dispatch({ - type: "set scatterplot y", - data: gene - }); - }; - } - - render() { - const { - gene, - aveDiff, - set1exp, - set2exp, - greyColorScale, - scatterplotXXaccessor, - scatterplotYYaccessor, - colorAccessor - } = this.props; - return ( -
-
- - {gene} - -
- - - - {aveDiff.toFixed(2)} - - - X - - - Y - - - - -
- ); - } -} +import HeatmapRow from "./heatmapRow"; /********************************** *********************************** @@ -212,18 +19,6 @@ class HeatmapRow extends React.Component { world: state.controls.world })) class Heatmap extends React.Component { - constructor(props) { - super(props); - this.state = { - value: "" - }; - } - - // XXX TODO unused at the moment - // getAllGeneNames = memoize(world => - // _.map(this.props.world.varAnnotations, "name") - // ); - render() { const { world, differential } = this.props; if (!differential.diffExp) { diff --git a/client/src/components/expression/heatmapRow.js b/client/src/components/expression/heatmapRow.js new file mode 100644 index 00000000..5e41a6b4 --- /dev/null +++ b/client/src/components/expression/heatmapRow.js @@ -0,0 +1,154 @@ +import React from "react"; +import { connect } from "react-redux"; +import { FaPaintBrush } from "react-icons/fa"; +import HeatmapSquare from "./heatmapSquare"; +import * as globals from "../../globals"; +import actions from "../../actions"; + +/********************************** +*********************************** +*********************************** + Row +*********************************** +*********************************** +**********************************/ +@connect(state => ({ + scatterplotXXaccessor: state.controls.scatterplotXXaccessor, + scatterplotYYaccessor: state.controls.scatterplotYYaccessor, + colorAccessor: state.controls.colorAccessor +})) +class HeatmapRow extends React.Component { + handleGeneColorScaleClick() { + return () => { + const { dispatch, gene } = this.props; + dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(gene)); + }; + } + + handleSetGeneAsScatterplotX() { + return () => { + const { dispatch, gene } = this.props; + dispatch({ + type: "set scatterplot x", + data: gene + }); + }; + } + + handleSetGeneAsScatterplotY() { + return () => { + const { dispatch, gene } = this.props; + dispatch({ + type: "set scatterplot y", + data: gene + }); + }; + } + + render() { + const { + gene, + aveDiff, + set1exp, + set2exp, + greyColorScale, + scatterplotXXaccessor, + scatterplotYYaccessor, + colorAccessor + } = this.props; + return ( +
+
+ + {gene} + +
+ + + + {aveDiff.toFixed(2)} + + + X + + + Y + + + + +
+ ); + } +} + +export default HeatmapRow; diff --git a/client/src/components/expression/heatmapSquare.js b/client/src/components/expression/heatmapSquare.js new file mode 100644 index 00000000..1200b7cb --- /dev/null +++ b/client/src/components/expression/heatmapSquare.js @@ -0,0 +1,30 @@ +import React from "react"; +import getContrast from "font-color-contrast"; // https://www.npmjs.com/package/font-color-contrast + +const HeatmapSquare = props => { + const { backgroundColor, text } = props; + const contrastColor = getContrast( + backgroundColor + .substring(4, backgroundColor.length - 1) + .replace(/ /g, "") + .split(",") + ); + return ( +

+ {text} +

+ ); +}; + +export default HeatmapSquare; diff --git a/client/src/components/geneExpression/index.js b/client/src/components/geneExpression/index.js index 34d0205f..d0252a84 100644 --- a/client/src/components/geneExpression/index.js +++ b/client/src/components/geneExpression/index.js @@ -8,7 +8,6 @@ import { connect } from "react-redux"; import { FaPlusCircle } from "react-icons/fa"; import HistogramBrush from "../brushableHistogram"; import * as globals from "../../globals"; -// import ReactAutocomplete from "react-autocomplete"; /* http://emilebres.github.io/react-virtualized-checkbox/ */ import actions from "../../actions"; @connect(state => { @@ -64,7 +63,7 @@ class GeneExpression extends React.Component { } render() { - const { world, userDefinedGenes } = this.props; + const { world, userDefinedGenes, differential } = this.props; const { gene } = this.state; return ( @@ -106,43 +105,40 @@ class GeneExpression extends React.Component { ) : null} {world && userDefinedGenes.length > 0 ? _.map(userDefinedGenes, geneName => { - if (!world.varDataCache[geneName]) { + const values = world.varDataCache[geneName]; + if (!values) { return null; - } else { - const values = world.varDataCache[geneName]; - return ( - - ); } + return ( + + ); }) : null} - {this.props.differential.diffExp ? ( -

Differentially Expressed Genes

- ) : null} - {this.props.differential.diffExp - ? _.map(this.props.differential.diffExp, (value, key) => { - const name = world.varAnnotations[value[0]].name; + {differential.diffExp ?

Differentially Expressed Genes

: null} + {differential.diffExp + ? _.map(differential.diffExp, value => { + const annotations = world.varAnnotations[value[0]]; + const { name } = annotations; const values = world.varDataCache[name]; - if (!world.varDataCache[world.varAnnotations[value[0]].name]) { + if (!values) { return null; - } else { - return ( - - ); } + return ( + + ); }) : null} @@ -151,46 +147,3 @@ class GeneExpression extends React.Component { } export default GeneExpression; - -// -// item.toLowerCase().indexOf(value.toLowerCase()) > -1 -// } -// getItemValue={item => item} -// renderItem={(item, highlighted) => ( -//
-// {item} -//
-// )} -// value={this.state.value} -// onChange={e => this.setState({ value: e.target.value })} -// onSelect={value => { -// this.setState({ value }); -// this.props.dispatch( -// actions.requestSingleGeneExpressionCountsPOST( -// value -// ) -// ); -// }} -// /> - -//
-// {_.map(this.props.ranges, (value, key) => { -// const isColorField = key.includes("color") || key.includes("Color"); -// if (value.range && key !== "CellName" && !isColorField) { -// return ( -// -// ); -// } -// })} -//
diff --git a/client/src/components/graph/graph.js b/client/src/components/graph/graph.js index 80dd1c4e..b63553ba 100644 --- a/client/src/components/graph/graph.js +++ b/client/src/components/graph/graph.js @@ -6,8 +6,7 @@ import { connect } from "react-redux"; import mat4 from "gl-mat4"; import _regl from "regl"; -import { FaCrosshairs } from "react-icons/fa"; -import { FaSearchPlus } from "react-icons/fa"; +import { FaCrosshairs, FaSearchPlus } from "react-icons/fa"; import * as globals from "../../globals"; import setupSVGandBrushElements from "./setupSVGandBrush"; @@ -192,13 +191,13 @@ class Graph extends React.Component { d3.select("#graphAttachPoint") .selectAll("svg") .remove(); - const { svg, brush, brushContainer } = setupSVGandBrushElements( + const { svg: newSvg, brush, brushContainer } = setupSVGandBrushElements( this.handleBrushSelectAction.bind(this), this.handleBrushDeselectAction.bind(this), responsive, this.graphPaddingTop ); - this.setState({ svg, brush, brushContainer }); + this.setState({ svg: newSvg, brush, brushContainer }); } } diff --git a/client/src/components/scatterplot/drawPointsRegl.js b/client/src/components/scatterplot/drawPointsRegl.js index e8c6b39b..266fc63a 100644 --- a/client/src/components/scatterplot/drawPointsRegl.js +++ b/client/src/components/scatterplot/drawPointsRegl.js @@ -38,15 +38,14 @@ export default function(regl) { uniforms: { distance: regl.prop("distance"), view: regl.prop("view"), - projection: (context, props) => { - return mat4.perspective( + projection: (context, props) => + mat4.perspective( [], Math.PI / 2, - context.viewportWidth * props.scale / context.viewportHeight, + (context.viewportWidth * props.scale) / context.viewportHeight, 0.01, 1000 - ); - } + ) }, count: regl.prop("count"), diff --git a/client/src/components/scatterplot/scatterplot.js b/client/src/components/scatterplot/scatterplot.js index b1ddc8a9..5985fe12 100644 --- a/client/src/components/scatterplot/scatterplot.js +++ b/client/src/components/scatterplot/scatterplot.js @@ -238,7 +238,7 @@ class Scatterplot extends React.Component { // and it's alread defined to be a bottom axis. svg .append("g") - .attr("transform", "translate(0," + height + ")") + .attr("transform", `translate(0,${height})`) .attr("class", "x axis") .call(xAxis); @@ -279,8 +279,8 @@ class Scatterplot extends React.Component { className={styles.scatterplot} id="scatterplot" style={{ - width: width + margin.left + margin.right + "px", - height: height + margin.top + margin.bottom + "px" + width: `${width + margin.left + margin.right}px`, + height: `${height + margin.top + margin.bottom}px` }} > { - var container = d3.select("#scatterplot"); + const container = d3.select("#scatterplot"); - var svg = container + const svg = container .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + .attr("transform", `translate(${margin.left},${margin.top})`); return { svg diff --git a/client/src/components/scatterplot/util.js b/client/src/components/scatterplot/util.js index bb5fa832..5a7773af 100644 --- a/client/src/components/scatterplot/util.js +++ b/client/src/components/scatterplot/util.js @@ -1,9 +1,4 @@ // jshint esversion: 6 -import _ from "lodash"; - -const paddingRight = 120; -const continuousChartWidth = 340; - export const margin = { top: 66, right: 110, bottom: 20, left: 60 }; export const width = 340; export const height = 340 - margin.top - margin.bottom; diff --git a/client/src/util/typedCrossfilter/index.js b/client/src/util/typedCrossfilter/index.js index bf7a25ea..e2ce88ee 100644 --- a/client/src/util/typedCrossfilter/index.js +++ b/client/src/util/typedCrossfilter/index.js @@ -279,14 +279,14 @@ class ScalarDimension { let found = 0; // skip up to offset records - for (i = len - 1; 0 <= i && skip < offset; i -= 1) { + for (i = len - 1; i >= 0 && skip < offset; i -= 1) { if (selection.isSelected(index[i])) { skip += 1; } } // grab up to k records - for (; 0 <= i && found < k; i -= 1) { + for (; i >= 0 && found < k; i -= 1) { if (selection.isSelected(index[i])) { ret.push(data[index[i]]); found += 1; @@ -539,7 +539,7 @@ class ScalarGroup { // set the reduce functions to count records. reduceCount() { - return this.reduce((p, v) => p + 1, (p, v) => p - 1, () => 0); + return this.reduce(p => p + 1, p => p - 1, () => 0); } // set the reduce functions to sum records using specified value accessor. diff --git a/client/src/util/typedCrossfilter/positiveIntervals.js b/client/src/util/typedCrossfilter/positiveIntervals.js index 1f255fea..bafc68d1 100644 --- a/client/src/util/typedCrossfilter/positiveIntervals.js +++ b/client/src/util/typedCrossfilter/positiveIntervals.js @@ -30,7 +30,7 @@ class PositiveIntervals { res.push(copy[i]); } else if (copy[i][1] > res[res.length - 1][1]) { // merge this into previous - res[res.length - 1][1] = copy[i][1]; + res[res.length - 1][1] = copy[i][1]; // eslint-disable-line prefer-destructuring } } return res; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..48e341a0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +}