From 2a702267ac20465cc0c75ac84eda18ea2296e105 Mon Sep 17 00:00:00 2001 From: Raymond Z Date: Fri, 2 Jun 2023 18:25:05 -0400 Subject: [PATCH] tsify --- client/src/pages/report/COMPONENT_TEMPLATE.ts | 110 ------ client/src/pages/report/STATELESS_TEMPLATE.ts | 11 - .../src/pages/report/components/barChart.tsx | 2 +- .../report/components/beeswarm/beeswarm.tsx | 244 ++++++++----- .../report/components/boxPlot/boxPlot.tsx | 57 --- .../report/components/boxPlot/drawBoxPlot.tsx | 172 --------- .../pages/report/components/boxPlot/lib.tsx | 328 ------------------ .../src/pages/report/components/comment.tsx | 127 ++++--- .../components/commentsGraph/comments.tsx | 39 ++- .../commentsGraph/commentsGraph.tsx | 125 ------- .../report/components/controls/controls.tsx | 74 ++-- .../components/correlationMatrix/matrix.tsx | 210 ----------- .../report/components/framework/Footer.tsx | 2 +- .../report/components/framework/checkbox.tsx | 118 ++++--- .../report/components/framework/flex.tsx | 68 ++-- .../components/framework/logoLargeShort.tsx | 240 ++++++++++--- .../components/framework/logoSmallLong.tsx | 8 +- .../src/pages/report/components/graphAxes.tsx | 2 +- .../lists/allCommentsModeratedIn.tsx | 112 ++++-- .../report/components/lists/commentList.tsx | 113 +++--- .../components/lists/participantGroup.tsx | 31 +- .../components/lists/participantGroups.tsx | 186 +++++----- .../components/participantsGraph/hull.tsx | 18 +- .../participantsGraph/participantsGraph.tsx | 124 ++++--- client/src/pages/report/index.tsx | 59 +++- client/src/pages/report/store/index.ts | 4 +- client/src/pages/report/util/graphUtil.ts | 2 + client/src/pages/report/util/net.ts | 1 + client/src/pages/report/util/url.ts | 2 +- client/tsconfig.json | 3 + 30 files changed, 997 insertions(+), 1595 deletions(-) delete mode 100644 client/src/pages/report/COMPONENT_TEMPLATE.ts delete mode 100644 client/src/pages/report/STATELESS_TEMPLATE.ts delete mode 100644 client/src/pages/report/components/boxPlot/boxPlot.tsx delete mode 100644 client/src/pages/report/components/boxPlot/drawBoxPlot.tsx delete mode 100644 client/src/pages/report/components/boxPlot/lib.tsx delete mode 100644 client/src/pages/report/components/commentsGraph/commentsGraph.tsx delete mode 100644 client/src/pages/report/components/correlationMatrix/matrix.tsx diff --git a/client/src/pages/report/COMPONENT_TEMPLATE.ts b/client/src/pages/report/COMPONENT_TEMPLATE.ts deleted file mode 100644 index 5ff28037f..000000000 --- a/client/src/pages/report/COMPONENT_TEMPLATE.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -import React from "react"; -import Radium from "radium"; -import PropTypes from "prop-types"; -// import _ from "lodash"; -// import Flex from "./framework/flex"; -// import { connect } from "react-redux"; -// import { FOO } from "../actions"; - - -// @connect(state => { -// return state.FOO; -// }) -@Radium -class ComponentName extends React.Component { - constructor(props) { - super(props); - this.state = { - - }; - } - static propTypes = { - /* react */ - // dispatch: PropTypes.func, - params: PropTypes.object, - routes: PropTypes.array, - /* component api */ - style: PropTypes.object, - // foo: PropTypes.string - } - static defaultProps = { - // foo: "bar" - } - getStyles() { - return { - base: { - - } - }; - } - render() { - const styles = this.getStyles(); - return ( -
- {"ComponentName"} -
- ); - } -} - -export default ComponentName; - -/* - -propTypes: { - // You can declare that a prop is a specific JS primitive. By default, these - // are all optional. - optionalArray: PropTypes.array, - optionalBool: PropTypes.bool, - optionalFunc: PropTypes.func, - optionalNumber: PropTypes.number, - optionalObject: PropTypes.object, - optionalString: PropTypes.string, - - // Anything that can be rendered: numbers, strings, elements or an array - // (or fragment) containing these types. - optionalNode: PropTypes.node, - - // A React element. - optionalElement: PropTypes.element, - - // You can also declare that a prop is an instance of a class. This uses - // JS's instanceof operator. - optionalMessage: PropTypes.instanceOf(Message), - - // You can ensure that your prop is limited to specific values by treating - // it as an enum. - optionalEnum: PropTypes.oneOf(['News', 'Photos']), - - // An object that could be one of many types - optionalUnion: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - PropTypes.instanceOf(Message) - ]), - - // An array of a certain type - optionalArrayOf: PropTypes.arrayOf(PropTypes.number), - - // An object with property values of a certain type - optionalObjectOf: PropTypes.objectOf(PropTypes.number), - - // An object taking on a particular shape - optionalObjectWithShape: PropTypes.shape({ - color: PropTypes.string, - fontSize: PropTypes.number - }), - - // You can chain any of the above with `isRequired` to make sure a warning - // is shown if the prop isn't provided. - requiredFunc: PropTypes.func.isRequired, - - // A value of any data type - requiredAny: PropTypes.any.isRequired, - -*/ diff --git a/client/src/pages/report/STATELESS_TEMPLATE.ts b/client/src/pages/report/STATELESS_TEMPLATE.ts deleted file mode 100644 index 65a8ea0e8..000000000 --- a/client/src/pages/report/STATELESS_TEMPLATE.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -import React from "react"; - -const ComponentName = () => { - return ( -

- ); -}; - -export default ComponentName; diff --git a/client/src/pages/report/components/barChart.tsx b/client/src/pages/report/components/barChart.tsx index 50d24b5cc..482df64e4 100644 --- a/client/src/pages/report/components/barChart.tsx +++ b/client/src/pages/report/components/barChart.tsx @@ -2,7 +2,7 @@ import React from "react"; -const BarChart = ({comment, groupVotes, ptptCount/*, conversation*/}) => { +const BarChart = ({ comment, groupVotes, ptptCount, conversation }: { comment, groupVotes?, ptptCount, conversation }) => { const rectStartX = 70; const barHeight = 15; diff --git a/client/src/pages/report/components/beeswarm/beeswarm.tsx b/client/src/pages/report/components/beeswarm/beeswarm.tsx index a8427ad09..d280eb98a 100644 --- a/client/src/pages/report/components/beeswarm/beeswarm.tsx +++ b/client/src/pages/report/components/beeswarm/beeswarm.tsx @@ -1,9 +1,11 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -import CommentList from "../lists/commentList"; -import * as globals from "../globals"; -import _ from "lodash"; +import React from "react" +import CommentList from "../lists/commentList" +import * as globals from "../globals" +import _ from "lodash" +import d3 from "d3" + // import Flex from "../framework/flex" // function type(d) { @@ -43,13 +45,16 @@ import _ from "lodash"; // ) // } - -class VoronoiCells extends React.Component { - +class VoronoiCells extends React.Component<{ + probabilitiesTids + probabilities + currentBeeswarmComment + voronoi + onHoverCallback +}> { /* https://bl.ocks.org/mbostock/6526445e2b44303eebf21da3b6627320 */ getFill(cell) { - /* find the index of cell.data.tid in probabilitiesTids and use the index of it as the accessor */ // if ( @@ -58,13 +63,15 @@ class VoronoiCells extends React.Component { // // !this.props.probabilitiesTids // ) { // return - if (this.props.currentBeeswarmComment && this.props.currentBeeswarmComment.tid === cell.data.tid) { - return "rgb(255,0,0)"; + if ( + this.props.currentBeeswarmComment && + this.props.currentBeeswarmComment.tid === cell.data.tid + ) { + return "rgb(255,0,0)" } else { return "black" } - // else { // /* for the given comment tid (as voronoi cell), find the index in the tid array that gives the coordinate in the matrix */ // const prob = this.props.probabilities[this.props.probabilitiesTids.indexOf(cell.data.tid)]; @@ -79,49 +86,63 @@ class VoronoiCells extends React.Component { } render() { - return ( - { - this.props.voronoi.map((cell, i) => { - return ( - - - - - - ) - }) - } + {this.props.voronoi.map((cell, i) => { + return ( + + + + + + ) + })} ) } } -class Beeswarm extends React.Component { +class Beeswarm extends React.Component<{ + comments + extremity + probabilities + probabilitiesTids + ptptCount?: number + math + conversation + formatTid?: Function + voteColors +}, { + x? + voronoi? + currentBeeswarmComment + commentsWithExtremity + axesRendered +}> { + svgWidth: number + svgHeight: number + margin: any + widthMinusMargins: number + heightMinusMargins: number + constructor(props) { - super(props); + super(props) - this.svgWidth = 960; - this.svgHeight = 200; - this.margin = {top: 10, right: 10, bottom: 10, left: 10}; - this.widthMinusMargins = 960 - this.margin.left - this.margin.right; - this.heightMinusMargins = 200 - this.margin.top - this.margin.bottom; + this.svgWidth = 960 + this.svgHeight = 200 + this.margin = { top: 10, right: 10, bottom: 10, left: 10 } + this.widthMinusMargins = 960 - this.margin.left - this.margin.right + this.heightMinusMargins = 200 - this.margin.top - this.margin.bottom this.state = { currentBeeswarmComment: null, commentsWithExtremity: null, axesRendered: false, - }; + } } onHoverCallback(d) { return () => { - this.setState({currentBeeswarmComment: d.data}); + this.setState({ currentBeeswarmComment: d.data }) } } @@ -131,86 +152,119 @@ class Beeswarm extends React.Component { this.props.extremity && !this.state.commentsWithExtremity /* if we poll, change this so it is auto updating */ ) { - this.setup(); + this.setup() } } - setup () { - const commentsWithExtremity = []; - _.each(this.props.comments, (comment) => { - if (this.props.extremity[comment.tid] > 0) { - const cwe = Object.assign({}, comment, {extremity: this.props.extremity[comment.tid]}); - commentsWithExtremity.push(cwe) - } - }) + setup() { + const commentsWithExtremity = [] + _.each(this.props.comments, (comment) => { + if (this.props.extremity[comment.tid] > 0) { + const cwe = Object.assign({}, comment, { extremity: this.props.extremity[comment.tid] }) + commentsWithExtremity.push(cwe) + } + }) - var x = d3.scaleLinear() - .rangeRound([0, this.widthMinusMargins]); + var x = d3.scaleLinear().rangeRound([0, this.widthMinusMargins]) - x.domain(d3.extent(commentsWithExtremity, function(d) { return d.extremity; })); + x.domain( + d3.extent(commentsWithExtremity, function (d) { + return d.extremity + }) + ) - var simulation = d3.forceSimulation(commentsWithExtremity) - .force("x", d3.forceX(function(d) { - return x(d.extremity); - }).strength(1)) - .force("y", d3.forceY(this.heightMinusMargins / 2)) - .force("collide", d3.forceCollide(4)) - .stop(); + var simulation = d3 + .forceSimulation(commentsWithExtremity) + .force( + "x", + d3 + .forceX(function (d) { + return x(d.extremity) + }) + .strength(1) + ) + .force("y", d3.forceY(this.heightMinusMargins / 2)) + .force("collide", d3.forceCollide(4)) + .stop() - for (var i = 0; i < 120; ++i) simulation.tick(); + for (var i = 0; i < 120; ++i) simulation.tick() - const voronoi = d3.voronoi() - .extent([[-this.margin.left, -this.margin.top], [this.widthMinusMargins + this.margin.right, this.heightMinusMargins + this.margin.top]]) - .x(function(d) { return d.x; }) - .y(function(d) { return d.y; }) + const voronoi = d3 + .voronoi() + .extent([ + [-this.margin.left, -this.margin.top], + [this.widthMinusMargins + this.margin.right, this.heightMinusMargins + this.margin.top], + ]) + .x(function (d) { + return d.x + }) + .y(function (d) { + return d.y + }) .polygons(commentsWithExtremity) - // if (!this.state.axesRendered) { - // d3.select("#beeswarmAxisAttachPointD3").append("g") - // .attr("class", "axis axis--x") - // .attr("transform", "translate(0," + this.heightMinusMargins + ")") - // .call(d3.axisBottom(x).ticks(3)); - // } - - this.setState({ - x, - voronoi, - commentsWithExtremity, - axesRendered: true - }) + // if (!this.state.axesRendered) { + // d3.select("#beeswarmAxisAttachPointD3").append("g") + // .attr("class", "axis axis--x") + // .attr("transform", "translate(0," + this.heightMinusMargins + ")") + // .call(d3.axisBottom(x).ticks(3)); + // } + + this.setState({ + x, + voronoi, + commentsWithExtremity, + axesRendered: true, + }) } render() { return ( -
+

How divisive was the conversation?

- Statements (here as little circles) to the left were voted on the same way—either everyone agreed or everyone disagreed. Statements to the right were divisive—participants were split between agreement and disagreement. + Statements (here as little circles) to the left were voted on the same way—either everyone + agreed or everyone disagreed. Statements to the right were divisive—participants were + split between agreement and disagreement.

- How to use this: Hover to see the statement text. Start on the far right to find out what the most divisive statement was. + How to use this: Hover to see the statement text. Start on the far right + to find out what the most divisive statement was.

- - { - this.state.commentsWithExtremity ? + + {this.state.commentsWithExtremity ? ( : null - } + onHoverCallback={this.onHoverCallback.bind(this)} + /> + ) : null} - + -
-

Consensus statements

-

Divisive statements

+
+

Consensus statements

+

Divisive statements

{/**/} -
- { this.state.currentBeeswarmComment ? - +
+ {this.state.currentBeeswarmComment ? ( : null - } + voteColors={this.props.voteColors} + /> + ) : null}
-
- ); + ) } } -export default Beeswarm; +export default Beeswarm // . - -import React from "react"; -import _ from "lodash"; -import * as globals from "../globals"; -import drawBoxPlot from "./drawBoxPlot"; - -class BoxPlot extends React.Component { - - componentDidMount() { - drawBoxPlot(this.createBoxplotDataset()); - } - - createBoxplotDataset() { - const dataset = []; - _.each(this.props.groupVotes, (g, ii) => { /* for each comment each group voted on ... */ - dataset[ii] = []; /* initialize empty array which will have two entries: label and array of votes */ - dataset[ii][0] = globals.groupLabels[g.id] /* g.id = 0, so go get "A" for the label */ - dataset[ii][1] = []; - _.forEach(g.votes, (v) => { /* extract agrees as percent */ - if (v["S"] > 0) { /* make sure someone saw it */ - dataset[ii][1].push(Math.floor(v["A"] / v["S"] * 100)) /* agreed / saw ... so perhaps 5 agreed / 10 saw for 50% */ - } - }); - }); - return dataset; - } - - render() { - return ( -
-

Average level of agreement per group

-

- Which group agreed the most, across all statements? - The line in the middle of the blue boxes below shows the mean (average) percentage agreement by a given group across all statements. - The lower the line in the middle of the blue box, the more a group disagreed. The higher the line, the more they agreed. -

-

- If the mean, and the colored box is higher, it means people in the group agreed more overall. - This would suggest their views are represented. -

-

- If the colored box is lower, it means the group, on avereage, disagreed on more across all statements. - A group with a lower than average agreement may be a group that needs to comment more, - so that its views are properly represented. -

-

- - How to read a box plot - (3 minute video).

-
-
- ); - } -} - -export default BoxPlot; diff --git a/client/src/pages/report/components/boxPlot/drawBoxPlot.tsx b/client/src/pages/report/components/boxPlot/drawBoxPlot.tsx deleted file mode 100644 index b5775498e..000000000 --- a/client/src/pages/report/components/boxPlot/drawBoxPlot.tsx +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -import "./lib"; - -/********************************************************************* -********************************************************************** - Draw box plot -********************************************************************** -*********************************************************************/ - -const drawBoxPlot = (dataset) => { - - // console.log("drawBoxPlot", dataset) - - var labels = true; // show the text labels beside individual boxplots? - - var margin = {top: 30, right: 50, bottom: 70, left: 50}; - var width = 800 - margin.left - margin.right; - var height = 400 - margin.top - margin.bottom; - - // var min = Infinity, - // max = -Infinity; - - // parse in the data - // d3.csv("https://gist.githubusercontent.com/jensgrubert/7789216/raw/2d40368c5e8dcc88f26e8e80e2128bd4c2adc942/data.csv", function(error, csv) { - // using an array of arrays with - // data[n][2] - // where n = number of columns in the csv file - // data[i][0] = name of the ith column - // data[i][1] = array of values of ith column - - /* - - var data = []; - data[0] = []; - data[1] = []; - data[2] = []; - data[3] = []; - // add more rows if your csv file has more columns - - // add here the header of the csv file - data[0][0] = "Q1"; - data[1][0] = "Q2"; - data[2][0] = "Q3"; - data[3][0] = "Q4"; - // add more rows if your csv file has more columns - - data[0][1] = []; - data[1][1] = []; - data[2][1] = []; - data[3][1] = []; - - csv.forEach(function(x) { - var v1 = Math.floor(x.Q1), - v2 = Math.floor(x.Q2), - v3 = Math.floor(x.Q3), - v4 = Math.floor(x.Q4); - // add more variables if your csv file has more columns - - console.log('v1', v1) - - var rowMax = Math.max(v1, Math.max(v2, Math.max(v3,v4))); - var rowMin = Math.min(v1, Math.min(v2, Math.min(v3,v4))); - - data[0][1].push(v1); - data[1][1].push(v2); - data[2][1].push(v3); - data[3][1].push(v4); - // add more rows if your csv file has more columns - - if (rowMax > max) max = rowMax; - if (rowMin < min) min = rowMin; - }); - - console.log('parsed looks like ', data) - - */ - - var chart = d3.box() - .whiskers(iqr(1.5)) - .height(height) - .domain([0, 100]) - .showLabels(labels); - - var svg = d3.select("#boxPlot").append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .attr("class", "box") - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - // // the x-axis - // var x = d3.scale.ordinal() - // .domain( data.map(function(d) { console.log(d); return d[0] } ) ) - // .rangeRoundBands([0 , width], 0.7, 0.3); - - var x = d3.scaleBand() - .domain(dataset.map(function(d) { return d[0] } )) - .range([0, width]) - .paddingInner(0.7) - .paddingOuter(0.3) - - var xAxis = d3.axisBottom() - .scale(x) - - // the y-axis - var y = d3.scaleLinear() - .domain([0, 100]) /* percent, never truncated */ - .range([height + margin.top, 0 + margin.top]); - - var yAxis = d3.axisLeft() - .scale(y) - - // draw the boxplots - svg.selectAll(".box") - .data(dataset) - .enter().append("g") - .attr("transform", function(d) { return "translate(" + x(d[0]) + "," + margin.top + ")"; } ) - .call(chart.width(x.bandwidth())); - - - // add a title - svg.append("text") - .attr("x", (width / 2)) - .attr("y", 0 + (margin.top / 2)) - .attr("text-anchor", "middle") - .style("font-size", "18px") - //.style("text-decoration", "underline") - .text(""); // agreement across all comments per group ? - - // draw y axis - svg.append("g") - .attr("class", "y axis") - .call(yAxis) - .append("text") // and text1 - .attr("transform", "rotate(-90)") - .attr("y", 6) - .attr("dy", ".71em") - .style("text-anchor", "end") - .style("font-size", "16px") - .text("Revenue in €"); - - // draw x axis - svg.append("g") - .attr("class", "x axis") - .attr("transform", "translate(0," + (height + margin.top + 10) + ")") - .call(xAxis) - .append("text") // text label for the x axis - .attr("x", (width / 2) ) - .attr("y", 10 ) - .attr("dy", ".71em") - .style("text-anchor", "middle") - .style("font-size", "16px") - .text("Quarter"); - - // Returns a function to compute the interquartile range. - function iqr(k) { - return function(d, i) { - var q1 = d.quartiles[0], - q3 = d.quartiles[2], - iqr = (q3 - q1) * k, - // i = -1, - j = d.length; - while (d[++i] < q1 - iqr); - while (d[--j] > q3 + iqr); - return [i, j]; - }; - } - -} - -export default drawBoxPlot; diff --git a/client/src/pages/report/components/boxPlot/lib.tsx b/client/src/pages/report/components/boxPlot/lib.tsx deleted file mode 100644 index bdca56061..000000000 --- a/client/src/pages/report/components/boxPlot/lib.tsx +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -d3.functor = function functor(v) { - return typeof v === "function" ? v : function() { - return v; - }; -}; - -(function() { - - // Inspired by http://informationandvisualization.de/blog/box-plot - d3.box = function() { - var width = 1, - height = 1, - duration = 0, - domain = null, - value = Number, - whiskers = boxWhiskers, - quartiles = boxQuartiles, - showLabels = true, // whether or not to show text labels - // numBars = 4, - // curBar = 1, - tickFormat = null; - - // For each small multiple… - function box(g) { - g.each(function(data, i) { - //d = d.map(value).sort(d3.ascending); - //var boxIndex = data[0]; - //var boxIndex = 1; - var d = data[1].sort(d3.ascending); - - // console.log(boxIndex); - // console.log(d); - - var g = d3.select(this), - n = d.length, - min = d[0], - max = d[n - 1]; - - // Compute quartiles. Must return exactly 3 elements. - var quartileData = d.quartiles = quartiles(d); - - // Compute whiskers. Must return exactly 2 elements, or null. - var whiskerIndices = whiskers && whiskers.call(this, d, i), - whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; }); - - // Compute outliers. If no whiskers are specified, all data are "outliers". - // We compute the outliers as indices, so that we can join across transitions! - var outlierIndices = whiskerIndices - ? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n)) - : d3.range(n); - - // Compute the new x-scale. - var x1 = d3.scaleLinear() - .domain(domain && domain.call(this, d, i) || [min, max]) - .range([height, 0]); - - // Retrieve the old x-scale, if this is an update. - var x0 = this.__chart__ || d3.scaleLinear() - .domain([0, Infinity]) - // .domain([0, max]) - .range(x1.range()); - - // Stash the new scale. - this.__chart__ = x1; - - // Note: the box, median, and box tick elements are fixed in number, - // so we only have to handle enter and update. In contrast, the outliers - // and other elements are variable, so we need to exit them! Variable - // elements also fade in and out. - - // Update center line: the vertical line spanning the whiskers. - var center = g.selectAll("line.center") - .data(whiskerData ? [whiskerData] : []); - - //vertical line - center.enter().insert("line", "rect") - .attr("class", "center") - .attr("x1", width / 2) - .attr("y1", function(d) { return x0(d[0]); }) - .attr("x2", width / 2) - .attr("y2", function(d) { return x0(d[1]); }) - .style("opacity", 1e-6) - .transition() - .duration(duration) - .style("opacity", 1) - .attr("y1", function(d) { return x1(d[0]); }) - .attr("y2", function(d) { return x1(d[1]); }); - - center.transition() - .duration(duration) - .style("opacity", 1) - .attr("y1", function(d) { return x1(d[0]); }) - .attr("y2", function(d) { return x1(d[1]); }); - - center.exit().transition() - .duration(duration) - .style("opacity", 1e-6) - .attr("y1", function(d) { return x1(d[0]); }) - .attr("y2", function(d) { return x1(d[1]); }) - .remove(); - - // Update innerquartile box. - var box = g.selectAll("rect.box") - .data([quartileData]); - - box.enter().append("rect") - .attr("class", "box") - .attr("x", 0) - .attr("y", function(d) { return x0(d[2]); }) - .attr("width", width) - .attr("height", function(d) { return x0(d[0]) - x0(d[2]); }) - .transition() - .duration(duration) - .attr("y", function(d) { return x1(d[2]); }) - .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); - - box.transition() - .duration(duration) - .attr("y", function(d) { return x1(d[2]); }) - .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); - - // Update median line. - var medianLine = g.selectAll("line.median") - .data([quartileData[1]]); - - medianLine.enter().append("line") - .attr("class", "median") - .attr("x1", 0) - .attr("y1", x0) - .attr("x2", width) - .attr("y2", x0) - .transition() - .duration(duration) - .attr("y1", x1) - .attr("y2", x1); - - medianLine.transition() - .duration(duration) - .attr("y1", x1) - .attr("y2", x1); - - // Update whiskers. - var whisker = g.selectAll("line.whisker") - .data(whiskerData || []); - - whisker.enter().insert("line", "circle, text") - .attr("class", "whisker") - .attr("x1", 0) - .attr("y1", x0) - .attr("x2", 0 + width) - .attr("y2", x0) - .style("opacity", 1e-6) - .transition() - .duration(duration) - .attr("y1", x1) - .attr("y2", x1) - .style("opacity", 1); - - whisker.transition() - .duration(duration) - .attr("y1", x1) - .attr("y2", x1) - .style("opacity", 1); - - whisker.exit().transition() - .duration(duration) - .attr("y1", x1) - .attr("y2", x1) - .style("opacity", 1e-6) - .remove(); - - // Update outliers. - var outlier = g.selectAll("circle.outlier") - .data(outlierIndices, Number); - - outlier.enter().insert("circle", "text") - .attr("class", "outlier") - .attr("r", 5) - .attr("cx", width / 2) - .attr("cy", function(i) { return x0(d[i]); }) - .style("opacity", 1e-6) - .transition() - .duration(duration) - .attr("cy", function(i) { return x1(d[i]); }) - .style("opacity", 1); - - outlier.transition() - .duration(duration) - .attr("cy", function(i) { return x1(d[i]); }) - .style("opacity", 1); - - outlier.exit().transition() - .duration(duration) - .attr("cy", function(i) { return x1(d[i]); }) - .style("opacity", 1e-6) - .remove(); - - // Compute the tick format. - var format = tickFormat || x1.tickFormat(8); - - // Update box ticks. - var boxTick = g.selectAll("text.box") - .data(quartileData); - if(showLabels == true) { - boxTick.enter().append("text") - .attr("class", "box") - .attr("dy", ".3em") - .attr("dx", function(d, i) { return i & 1 ? 6 : -6 }) - .attr("x", function(d, i) { return i & 1 ? + width : 0 }) - .attr("y", x0) - .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; }) - .text(format) - .transition() - .duration(duration) - .attr("y", x1); - } - - boxTick.transition() - .duration(duration) - .text(format) - .attr("y", x1); - - // Update whisker ticks. These are handled separately from the box - // ticks because they may or may not exist, and we want don't want - // to join box ticks pre-transition with whisker ticks post-. - var whiskerTick = g.selectAll("text.whisker") - .data(whiskerData || []); - if(showLabels == true) { - whiskerTick.enter().append("text") - .attr("class", "whisker") - .attr("dy", ".3em") - .attr("dx", 6) - .attr("x", width) - .attr("y", x0) - .text(format) - .style("opacity", 1e-6) - .transition() - .duration(duration) - .attr("y", x1) - .style("opacity", 1); - } - whiskerTick.transition() - .duration(duration) - .text(format) - .attr("y", x1) - .style("opacity", 1); - - whiskerTick.exit().transition() - .duration(duration) - .attr("y", x1) - .style("opacity", 1e-6) - .remove(); - }); - // d3.timer.flush(); - } - - box.width = function(x) { - if (!arguments.length) return width; - width = x; - return box; - }; - - box.height = function(x) { - if (!arguments.length) return height; - height = x; - return box; - }; - - box.tickFormat = function(x) { - if (!arguments.length) return tickFormat; - tickFormat = x; - return box; - }; - - box.duration = function(x) { - if (!arguments.length) return duration; - duration = x; - return box; - }; - - box.domain = function(x) { - if (!arguments.length) return domain; - domain = x == null ? x : d3.functor(x); - return box; - }; - - box.value = function(x) { - if (!arguments.length) return value; - value = x; - return box; - }; - - box.whiskers = function(x) { - if (!arguments.length) return whiskers; - whiskers = x; - return box; - }; - - box.showLabels = function(x) { - if (!arguments.length) return showLabels; - showLabels = x; - return box; - }; - - box.quartiles = function(x) { - if (!arguments.length) return quartiles; - quartiles = x; - return box; - }; - - return box; - }; - - function boxWhiskers(d) { - return [0, d.length - 1]; - } - - function boxQuartiles(d) { - return [ - d3.quantile(d, .25), - d3.quantile(d, .5), - d3.quantile(d, .75) - ]; - } - -})(); diff --git a/client/src/pages/report/components/comment.tsx b/client/src/pages/report/components/comment.tsx index 064802fce..460e60ea3 100644 --- a/client/src/pages/report/components/comment.tsx +++ b/client/src/pages/report/components/comment.tsx @@ -1,15 +1,21 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -import Radium from "radium"; -import PropTypes from "prop-types"; -import Flex from "./flex"; +import React from "react" +import Radium from "radium" +import PropTypes from "prop-types" +import Flex from "./framework/flex" // import ParticipantHeader from "./participant-header"; -import * as globals from "./globals"; -import BarChart from "./barChart"; +import * as globals from "./globals" +import BarChart from "./barChart" @Radium -class Comment extends React.Component { +class Comment extends React.Component<{ + formatTid + comment: any // Comment + index: number + ptptCount: number + conversation: any // Conversation +}> { static propTypes = { dispatch: PropTypes.func, params: PropTypes.object, @@ -19,14 +25,19 @@ class Comment extends React.Component { rejectClickHandler: PropTypes.func, } getDate() { - const date = new Date(+this.props.comment.created); - return `${date.getMonth()+1} / ${date.getUTCDate()} / ${date.getFullYear()}` + const date = new Date(+this.props.comment.created) + return `${date.getMonth() + 1} / ${date.getUTCDate()} / ${date.getFullYear()}` } getVoteBreakdown(/*comment*/) { if (typeof this.props.comment.agree_count !== "undefined") { - return ({this.props.comment.agree_count} agreed, {this.props.comment.disagree_count} disagreed, {this.props.comment.pass_count} passed); + return ( + + ({this.props.comment.agree_count} agreed, {this.props.comment.disagree_count} disagreed,{" "} + {this.props.comment.pass_count} passed) + + ) } - return ""; + return "" } render() { @@ -34,53 +45,57 @@ class Comment extends React.Component { return ( - - - {this.props.formatTid(this.props.comment.tid)} - - {this.props.comment.is_meta ? "Metadata: " : ''} - { this.props.comment.txt } + styleOverrides={{ + width: "100%", + marginBottom: 50, + background: this.props.index % 2 !== 0 ? "none" : "none", + }} + direction="row" + justifyContent="flex-start" + alignItems={"flex-start"} + > + + + {this.props.formatTid(this.props.comment.tid)} + + + {this.props.comment.is_meta ? "Metadata: " : ""} + + {this.props.comment.txt} + + + + + - - - - - - ); - } + ) } +} - export default Comment; +export default Comment - //

{this.props.comment.demographics.gender}

- //

{this.props.comment.demographics.age}

+//

{this.props.comment.demographics.gender}

+//

{this.props.comment.demographics.age}

- // { - // showAsAnon ? - // "Anonymous" : - // - // } +// { +// showAsAnon ? +// "Anonymous" : +// +// } diff --git a/client/src/pages/report/components/commentsGraph/comments.tsx b/client/src/pages/report/components/commentsGraph/comments.tsx index ee2455cb6..d635865a2 100644 --- a/client/src/pages/report/components/commentsGraph/comments.tsx +++ b/client/src/pages/report/components/commentsGraph/comments.tsx @@ -1,12 +1,29 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -import _ from "lodash"; +import React from "react" +import _ from "lodash" // import textWrap from "svg-text-wrap"; -class Comments extends React.Component { +class Comments extends React.Component< + { + points: any + comments: any[] + xScaleup + yScaleup + xCenter + yCenter + xx + yy + handleClick: Function + formatTid: Function + parentGraph: string + }, + { + // + } +> { createComments() { - console.log("in create comments, comments graph", this.props.points[0]); + console.log("in create comments, comments graph", this.props.points[0]) // const _points = this.props.points.slice(0); // @@ -25,15 +42,15 @@ class Comments extends React.Component { return this.props.points.map((comment, i) => { /* find the original comment */ const _comment = _.find(this.props.comments, (c) => { - return c.tid === comment.tid; - }); + return c.tid === comment.tid + }) /* see if it's meta or consensus */ if ( _comment.is_meta // this.props.math.pca["comment-extremity"][comment.tid] < globals.maxCommentExtremityToShow ) { - return; + return } /* break the text up into pieces */ @@ -62,13 +79,13 @@ class Comments extends React.Component { ) })*/}
- ); - }); + ) + }) } render() { - return {this.createComments()}; + return {this.createComments()} } } -export default Comments; +export default Comments diff --git a/client/src/pages/report/components/commentsGraph/commentsGraph.tsx b/client/src/pages/report/components/commentsGraph/commentsGraph.tsx deleted file mode 100644 index 02009929b..000000000 --- a/client/src/pages/report/components/commentsGraph/commentsGraph.tsx +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -import React from "react"; -// import _ from "lodash"; -import * as globals from "../globals"; -import graphUtil from "../../util/graphUtil"; -import Axes from "../graphAxes"; -import Comments from "./comments"; - -// const TextSegment = ({ t, i }) => ( -// -// {t} -// -// ); - -class CommentsGraph extends React.Component { - constructor(props) { - super(props); - this.Viewer = null; - this.state = { - selectedComment: null, - }; - } - - handleCommentClick(selectedComment) { - return () => { - this.setState({ selectedComment }); - }; - } - - render() { - if (!this.props.math) { - return null; - } - - const { - xx, - yy, - commentsPoints, - xCenter, - yCenter, - // baseClustersScaled, - commentScaleupFactorX, - commentScaleupFactorY, - // hulls, - } = graphUtil(this.props.comments, this.props.math, this.props.badTids); - - return ( -
-
-

Statements

-

- How do statements relate to each other? Did people who agreed with one comment also - agree with another? -

-

- In this graph, statements that met disagreement are closer together if they were voted - on similarly. Those statements which were voted on differently are further apart. -

-

- This is important because it is the basis on which we will lay out and cluster - participants in a 2d space in later steps (closer to the statements on which they - agreed). There are no meaningful axes, but there are regions of statements that lend a - certain personality to a given area. -

-
-

- {this.state.selectedComment - ? "#" + this.state.selectedComment.tid + ". " + this.state.selectedComment.txt - : "Click a statement, identified by its number, to explore regions of the graph."} -

- - {/* Comment https://bl.ocks.org/mbostock/7555321 */} - - - - - {/* {} */} - {/* {this.props.math["group-clusters"].map((cluster, i) => { - return ( Renzi Supporters ) - }) : null} */} - {/* { - hulls.map((hull) => { - let gid = hull.group[0].gid; - if (_.isNumber(this.props.showOnlyGroup)) { - if (gid !== this.props.showOnlyGroup) { - return ""; - } - } - return - }) - } */} - {commentsPoints ? ( - - ) : null} - -
- ); - } -} - -export default CommentsGraph; diff --git a/client/src/pages/report/components/controls/controls.tsx b/client/src/pages/report/components/controls/controls.tsx index 630c6abd7..65eb9d53c 100644 --- a/client/src/pages/report/components/controls/controls.tsx +++ b/client/src/pages/report/components/controls/controls.tsx @@ -1,23 +1,30 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -import Checkbox from '../framework/checkbox'; -import React from "react"; -import settings from "../../settings"; - -class Controls extends React.Component { +import Checkbox from "../framework/checkbox" +import React from "react" +import settings from "../../settings" + +class Controls extends React.Component<{ + autoRefreshEnabled: boolean + onAutoRefreshEnabled: Function + onAutoRefreshDisabled: Function + colorBlindMode: boolean + handleColorblindModeClick: Function +}> { + autoRefreshEnabledRef: any + colorBlindModeRef: any constructor(props) { - super(props); - this.autoRefreshEnabledRef = React.createRef(); - this.colorBlindModeRef = React.createRef(); + super(props) + this.autoRefreshEnabledRef = React.createRef() + this.colorBlindModeRef = React.createRef() } checkboxGroupChanged(newVal) { if (newVal) { - this.props.onAutoRefreshEnabled(); + this.props.onAutoRefreshEnabled() } else { - this.props.onAutoRefreshDisabled(); + this.props.onAutoRefreshDisabled() } } @@ -27,29 +34,30 @@ class Controls extends React.Component { render() { return (
- - + +
- ); + ) } - } - // +// -export default Controls; +export default Controls diff --git a/client/src/pages/report/components/correlationMatrix/matrix.tsx b/client/src/pages/report/components/correlationMatrix/matrix.tsx deleted file mode 100644 index a55e7bad2..000000000 --- a/client/src/pages/report/components/correlationMatrix/matrix.tsx +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -import React from "react"; - -import _ from "lodash"; -import * as globals from "../globals"; - -var leftOffset = 34; -var topOffset = 60; - -var scale = d3.scaleLinear().domain([-1, 1]).range([0, 1]); - -const square = 20; - -class Matrix extends React.Component { - onMouseEnterCell(row, column, correlation) { - this.setState({ - mouseOverRow: row, - mouseOverColumn: column, - mouseOverCorrelation: correlation, - }); - } - onMouseExitCell(/*row, column*/) { - this.setState({ - mouseOverRow: null, - mouseOverColumn: null, - mouseOverCorrelation: null, - }); - } - - makeRect(comment, row, column) { - return ( - - { - return this.onMouseEnterCell(row, column, comment); - }} - width={square} - height={square} - /> - 30 ? "white" : "rgb(60,60,60)"} - style={{ - fontFamily: globals.sans, - fontSize: 10, - pointerEvents: "none", - }} - > - {Math.floor(comment * 100)} - - - ); - } - - makeColumn(comments, row) { - return comments.map((comment, column) => { - let markup = null; - if (column < row) { - /* diagonal matrix */ - markup = ( - - {/* this translate places the top text labels where they should go, rotated */} - {/* this translate places the columns where they should go, and creates a gutter */} - - {this.makeRect(comment, row, column)} - - - ); - } else if (column === row) { - const comment = _.find(this.props.comments, (comment) => { - return comment.tid === this.props.tids[column]; - }); - markup = ( - - { - return this.onMouseExitCell(); - }} - onMouseLeave={() => { - return this.onMouseExitCell(); - }} - transform={"translate(" + (column * square + 10) + ", 46), rotate(315)"} - fill={ - column === this.state.mouseOverColumn || column === this.state.mouseOverRow - ? "rgba(0,0,0,1)" - : "rgba(0,0,0,0.5)" - } - style={{ - fontFamily: "Helvetica, sans-serif", - fontSize: 10, - fontWeight: 400, - }} - > - {comment ? comment.txt : "[[[none found]]]"} - - - ); - } - return markup; - }); - } - - makeRow(comments, row) { - // {/* this translate seperates the rows */} - // - // {this.props.formatTid(this.props.tids[row])} - // - return ( - - {/* this translate moves just the colored squares over to make a gutter, not the text */} - {this.makeColumn(comments, row)} - - ); - } - - renderMatrix() { - // console.log("mouseOverCorrelation", this.state.mouseOverCorrelation) - let side = this.props.probabilities.length * square + 200; - return ( -
-

{this.props.title}

-

- What is the chance that a participant who agreed (or disagreed) with a given comment also - agreed (or disagreed) with another given comment? -

-

- Patterns emerge when we evaluate groups of statements that tended to be voted on - similarly. -

-

- This is an important bit of math (called a correlation matrix) that goes into making the - graph above. -

- - - { - return this.onMouseExitCell(); - }} - onMouseLeave={() => { - return this.onMouseExitCell(); - }} - width={side} - height={side} - /> - - {!this.state.mouseOverCorrelation ? ( - " " - ) : ( - - {`${ - Math.round(this.state.mouseOverCorrelation * 1000) / 10 - }% chance of casting the same vote on these two statements`} - - )} - - - {this.props.probabilities.map((comments, row) => { - return {this.makeRow(comments, row)}; - })} - - -
- ); - } - renderError(err) { - return ( -
-
error loading matrix
-
{err}
-
- ); - } - renderLoading() { - return
loading matrix... (may take up to a minute)
; - } - render() { - if (this.props.error) { - return this.renderError(); - } else if (this.props.probabilities) { - return this.renderMatrix(); - } else { - return this.renderLoading(); - } - } -} - -export default Matrix; - -// This is a matrix showing every comment by every comment (all comments are shown, in order of being submitted, on each axis). Each square represents the likihood that if someone agreed with one comment, they would agree with the other. For instance, [n%] of people who agreed with comment [n] also agreed with comment [m]. diff --git a/client/src/pages/report/components/framework/Footer.tsx b/client/src/pages/report/components/framework/Footer.tsx index 70e5d7e0c..c5646af14 100644 --- a/client/src/pages/report/components/framework/Footer.tsx +++ b/client/src/pages/report/components/framework/Footer.tsx @@ -11,7 +11,7 @@ const Footer = (/*{conversation}*/) => { marginTop: 40, marginBottom: 60, }}> - +
) }; diff --git a/client/src/pages/report/components/framework/checkbox.tsx b/client/src/pages/report/components/framework/checkbox.tsx index d8ba417ee..9230a122c 100644 --- a/client/src/pages/report/components/framework/checkbox.tsx +++ b/client/src/pages/report/components/framework/checkbox.tsx @@ -1,46 +1,64 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -import Radium from "radium"; -import Color from "color"; -import settings from "../../settings"; +import React from "react" +import Radium from "radium" +import Color from "color" +import settings from "../../settings" @Radium -export default class Checkbox extends React.Component { +export default class Checkbox extends React.Component< + { + checked: boolean + disabled?: boolean + labelPosition: string + clickHandler: Function + labelWrapperColor: string + color: string + helpText?: string + label: string + }, + { + active: boolean + checked: boolean + } +> { constructor(props) { - super(props); + super(props) this.state = { checked: this.props.checked, - active: false - }; + active: false, + } } static defaultProps = { - checked: true, - clickHandler: (x) => { return x }, - color: settings.darkGray, - + checked: true, + clickHandler: (x) => { + return x + }, + color: settings.darkGray, } - activeHandler () { - this.setState({ active: !this.state.active }); + activeHandler() { + this.setState({ active: !this.state.active }) } - clickHandler () { - var newState = !this.state.checked; - this.setState({ checked: newState }); - if (this.props.clickHandler) { this.props.clickHandler(newState); } + clickHandler() { + var newState = !this.state.checked + this.setState({ checked: newState }) + if (this.props.clickHandler) { + this.props.clickHandler(newState) + } } - getWrapperStyles () { + getWrapperStyles(): any { return { display: "block", marginBottom: 10, - position: "relative" - }; + position: "relative", + } } - getLabelWrapperStyles () { + getLabelWrapperStyles() { return { color: this.props.labelWrapperColor, cursor: "pointer", @@ -51,11 +69,11 @@ export default class Checkbox extends React.Component { lineHeight: "20px", paddingLeft: 22, // "-webkit-user-select": "none" - }; + } } - getCheckboxStyles () { - const activeColor = Color(this.props.color).lighten(0.2).hex(); + getCheckboxStyles() { + const activeColor = Color(this.props.color).lighten(0.2).hex() return { base: { @@ -67,27 +85,27 @@ export default class Checkbox extends React.Component { position: "relative", top: 1, transition: "background-color ease .3s", - width: 12 + width: 12, }, checked: { - backgroundColor: this.props.color + backgroundColor: this.props.color, }, active: { - backgroundColor: activeColor - } - }; + backgroundColor: activeColor, + }, + } } - getLabelStyles () { + getLabelStyles(): any { return { display: "inline", left: -12, marginRight: 4, - position: "relative" - }; + position: "relative", + } } - getHelpTextStyles () { + getHelpTextStyles() { return { color: "#ccc", cursor: "pointer", @@ -96,12 +114,12 @@ export default class Checkbox extends React.Component { fontSize: 12, fontWeight: 200, lineHeight: "20px", - marginLeft: 5 - }; + marginLeft: 5, + } } - render () { - const checkboxStyles = this.getCheckboxStyles(); + render() { + const checkboxStyles = this.getCheckboxStyles() return (
@@ -109,23 +127,23 @@ export default class Checkbox extends React.Component { style={this.getLabelWrapperStyles()} onClick={this.clickHandler.bind(this)} onMouseDown={this.activeHandler.bind(this)} - onMouseUp={this.activeHandler.bind(this)}> - - + onMouseUp={this.activeHandler.bind(this)} + > + {this.props.label} {this.props.helpText ? ( - - ({this.props.helpText}) - - ) : null } + ({this.props.helpText}) + ) : null}
- ); + ) } } diff --git a/client/src/pages/report/components/framework/flex.tsx b/client/src/pages/report/components/framework/flex.tsx index cba01cae9..77ca9760d 100644 --- a/client/src/pages/report/components/framework/flex.tsx +++ b/client/src/pages/report/components/framework/flex.tsx @@ -1,8 +1,8 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import PropTypes from "prop-types"; -import React from "react"; -import Radium from "radium"; +import PropTypes from "prop-types" +import React, { CSSProperties } from "react" +import Radium from "radium" /** @@ -15,30 +15,45 @@ import Radium from "radium"; **/ -class Flex extends React.Component { +class Flex extends React.Component<{ + children + direction + wrap + justifyContent + alignItems + alignContent + order + grow + shrink + basis + alignSelf + styleOverrides + clickHandler +}> { static propTypes = { - direction: PropTypes.oneOf([ - "row", "rowReverse", "column", "columnReverse" - ]), - wrap: PropTypes.oneOf([ - "nowrap", "wrap", "wrap-reverse" - ]), + direction: PropTypes.oneOf(["row", "rowReverse", "column", "columnReverse"]), + wrap: PropTypes.oneOf(["nowrap", "wrap", "wrap-reverse"]), justifyContent: PropTypes.oneOf([ - "flex-start", "flex-end", "center", "space-between", "space-around" - ]), - alignItems: PropTypes.oneOf([ - "flex-start", "flex-end", "center", "baseline", "stretch" + "flex-start", + "flex-end", + "center", + "space-between", + "space-around", ]), + alignItems: PropTypes.oneOf(["flex-start", "flex-end", "center", "baseline", "stretch"]), alignContent: PropTypes.oneOf([ - "flex-start", "flex-end", "center", "space-between", "space-around", "stretch" + "flex-start", + "flex-end", + "center", + "space-between", + "space-around", + "stretch", ]), grow: PropTypes.number, shrink: PropTypes.number, basis: PropTypes.string, order: PropTypes.number, - alignSelf: PropTypes.oneOf([ - "auto", "flex-start", "flex-end", "center", "baseline", "stretch" - ]), + alignSelf: PropTypes.oneOf(["auto", "flex-start", "flex-end", "center", "baseline", "stretch"]), styleOverrides: PropTypes.object, children: PropTypes.node, clickHandler: PropTypes.func, @@ -54,7 +69,7 @@ class Flex extends React.Component { basis: "auto", alignSelf: "auto", order: 0, - styleOverrides: {} + styleOverrides: {}, } getStyles() { return { @@ -71,24 +86,19 @@ class Flex extends React.Component { flexBasis: this.props.basis, alignSelf: this.props.alignSelf, }, - styleOverrides: this.props.styleOverrides + styleOverrides: this.props.styleOverrides, } } render() { - const styles = this.getStyles(); + const styles = this.getStyles() return ( -
+
{this.props.children}
- ); + ) } } -export default Radium(Flex); +export default Radium(Flex) diff --git a/client/src/pages/report/components/framework/logoLargeShort.tsx b/client/src/pages/report/components/framework/logoLargeShort.tsx index d04da88dd..6f2a4fcc3 100644 --- a/client/src/pages/report/components/framework/logoLargeShort.tsx +++ b/client/src/pages/report/components/framework/logoLargeShort.tsx @@ -1,57 +1,217 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; +import React from "react" -class PolisLogo extends React.Component { +class PolisLogo extends React.Component<{ + invert?: any +}> { styles() { return { link: { textDecoration: "none", cursor: "pointer", - padding: "8px 0px 4px 10px" - } + padding: "8px 0px 4px 10px", + }, } } render() { return ( - - - - - - - - - p. - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + p. + + + + + + + + + + + + + + + + + + + + + + + + + - - ); + + ) } } -export default PolisLogo; +export default PolisLogo diff --git a/client/src/pages/report/components/framework/logoSmallLong.tsx b/client/src/pages/report/components/framework/logoSmallLong.tsx index ea94b90a0..add46fa88 100644 --- a/client/src/pages/report/components/framework/logoSmallLong.tsx +++ b/client/src/pages/report/components/framework/logoSmallLong.tsx @@ -1,8 +1,8 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; +import React from "react" -class PolisLogo extends React.Component { +class PolisLogo extends React.Component<{}, {}> { render() { return ( - ); + ) } } -export default PolisLogo; +export default PolisLogo diff --git a/client/src/pages/report/components/graphAxes.tsx b/client/src/pages/report/components/graphAxes.tsx index 784e97879..255052dd9 100644 --- a/client/src/pages/report/components/graphAxes.tsx +++ b/client/src/pages/report/components/graphAxes.tsx @@ -3,7 +3,7 @@ import React from "react"; import * as globals from "./globals"; -const GraphAxes = ({yCenter, xCenter/*, report*/}) => { +const GraphAxes = ({ yCenter, xCenter, report }) => { return ( . -import React from "react"; -import _ from "lodash"; -import CommentList from "./commentList"; -import * as globals from "../globals"; - +import React from "react" +import _ from "lodash" +import CommentList from "./commentList" +import * as globals from "../globals" function sortByTid(comments) { - return _.map(comments, (comment) => comment.tid).sort((a, b) => a - b); + return _.map(comments, (comment) => comment.tid).sort((a, b) => a - b) } function sortByVoteCount(comments) { - return _.map(_.reverse(_.sortBy(comments, "count")), (c) => {return c.tid;}); + return _.map(_.reverse(_.sortBy(comments, "count")), (c) => { + return c.tid + }) } function sortByGroupAwareConsensus(comments) { - return _.map(_.reverse(_.sortBy(comments, (c) => {return c["group-aware-consensus"];})), (c) => {return c.tid;}); + return _.map( + _.reverse( + _.sortBy(comments, (c) => { + return c["group-aware-consensus"] + }) + ), + (c) => { + return c.tid + } + ) } function sortByPctAgreed(comments) { - return _.map(_.reverse(_.sortBy(comments, (c) => {return c["pctAgreed"];})), (c) => {return c.tid;}); + return _.map( + _.reverse( + _.sortBy(comments, (c) => { + return c["pctAgreed"] + }) + ), + (c) => { + return c.tid + } + ) } function sortByPctDisagreed(comments) { - return _.map(_.reverse(_.sortBy(comments, (c) => {return c["pctDisagreed"];})), (c) => {return c.tid;}); + return _.map( + _.reverse( + _.sortBy(comments, (c) => { + return c["pctDisagreed"] + }) + ), + (c) => { + return c.tid + } + ) } function sortByPctPassed(comments) { - return _.map(_.reverse(_.sortBy(comments, (c) => {return c["pctPassed"];})), (c) => {return c.tid;}); + return _.map( + _.reverse( + _.sortBy(comments, (c) => { + return c["pctPassed"] + }) + ), + (c) => { + return c.tid + } + ) } -class allCommentsModeratedIn extends React.Component { - +class allCommentsModeratedIn extends React.Component< + { + conversation: any + ptptCount: any + math: any + formatTid: any + comments: any + voteColors: any + }, + { + sortStyle + } +> { constructor(props) { - super(props); + super(props) this.state = { sortStyle: globals.allCommentsSortDefault, - }; + } } onSortChanged(event) { this.setState({ sortStyle: event.target.value, - }); + }) } render() { - if (!this.props.conversation) { return
Loading allCommentsModeratedIn...
} - let sortFunction = null; + let sortFunction = null if (this.state.sortStyle === "tid") { - sortFunction = sortByTid; + sortFunction = sortByTid } else if (this.state.sortStyle === "numvotes") { - sortFunction = sortByVoteCount; + sortFunction = sortByVoteCount } else if (this.state.sortStyle === "consensus") { - sortFunction = sortByGroupAwareConsensus; + sortFunction = sortByGroupAwareConsensus } else if (this.state.sortStyle === "pctAgreed") { - sortFunction = sortByPctAgreed; + sortFunction = sortByPctAgreed } else if (this.state.sortStyle === "pctDisagreed") { - sortFunction = sortByPctDisagreed; + sortFunction = sortByPctDisagreed } else if (this.state.sortStyle === "pctPassed") { - sortFunction = sortByPctPassed; + sortFunction = sortByPctPassed } else { - console.error('missing sort function', this.state.sortStyle); + console.error("missing sort function", this.state.sortStyle) } return ( @@ -71,7 +118,11 @@ class allCommentsModeratedIn extends React.Component { Group votes across all statements, excluding those statements which were moderated out.

- @@ -79,7 +130,7 @@ class allCommentsModeratedIn extends React.Component { -
+
+ voteColors={this.props.voteColors} + />
- ); + ) } } -export default allCommentsModeratedIn; +export default allCommentsModeratedIn diff --git a/client/src/pages/report/components/lists/commentList.tsx b/client/src/pages/report/components/lists/commentList.tsx index 722eeaaf1..98094663d 100644 --- a/client/src/pages/report/components/lists/commentList.tsx +++ b/client/src/pages/report/components/lists/commentList.tsx @@ -1,40 +1,40 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -import _ from "lodash"; -import * as globals from "../globals"; +import React from "react" +import _ from "lodash" +import * as globals from "../globals" -const BarChartCompact = ({ comment, voteCounts, nMembers, voteColors }) => { - if (!comment) return null; +const BarChartCompact = ({ comment, voteCounts, nMembers, voteColors, index }) => { + if (!comment) return null - let w = 100; - let agrees = 0; - let disagrees = 0; - let sawTheComment = 0; - let missingCounts = false; + let w = 100 + let agrees = 0 + let disagrees = 0 + let sawTheComment = 0 + let missingCounts = false if (typeof voteCounts != "undefined") { - agrees = voteCounts.A; - disagrees = voteCounts.D; - sawTheComment = voteCounts.S; + agrees = voteCounts.A + disagrees = voteCounts.D + sawTheComment = voteCounts.S } else { - missingCounts = true; + missingCounts = true } - let passes = sawTheComment - (agrees + disagrees); + let passes = sawTheComment - (agrees + disagrees) // let totalVotes = agrees + disagrees + passes; - const agree = (agrees / nMembers) * w; - const disagree = (disagrees / nMembers) * w; - const pass = (passes / nMembers) * w; + const agree = (agrees / nMembers) * w + const disagree = (disagrees / nMembers) * w + const pass = (passes / nMembers) * w // const blank = nMembers - (sawTheComment / nMembers) * w; - const agreeSaw = (agrees / sawTheComment) * w; - const disagreeSaw = (disagrees / sawTheComment) * w; - const passSaw = (passes / sawTheComment) * w; + const agreeSaw = (agrees / sawTheComment) * w + const disagreeSaw = (disagrees / sawTheComment) * w + const passSaw = (passes / sawTheComment) * w - const agreeString = (agreeSaw << 0) + "%"; - const disagreeString = (disagreeSaw << 0) + "%"; - const passString = (passSaw << 0) + "%"; + const agreeString = (agreeSaw << 0) + "%" + const disagreeString = (disagreeSaw << 0) + "%" + const passString = (passSaw << 0) + "%" return (
{ )}
- ); -}; + ) +} -const CommentRow = ({ comment, groups, voteColors }) => { +const CommentRow = ({ comment, groups, voteColors, index }) => { if (!comment) { - console.error("WHY IS THERE NO COMMENT 3452354235", comment); - return null; + console.error("WHY IS THERE NO COMMENT 3452354235", comment) + return null } // const percentAgreed = Math.floor(groupVotesForThisGroup.votes[comment.tid].A / groupVotesForThisGroup.votes[comment.tid].S * 100); - let BarCharts = []; - let totalMembers = 0; + let BarCharts = [] + let totalMembers = 0 // groups _.forEach(groups, (g, i) => { - let nMembers = g["n-members"]; - totalMembers += nMembers; - let gVotes = g.votes[comment.tid]; + let nMembers = g["n-members"] + totalMembers += nMembers + let gVotes = g.votes[comment.tid] BarCharts.push( { nMembers={nMembers} voteColors={voteColors} /> - ); - }); + ) + }) // totals column // let globalCounts = { @@ -124,7 +124,7 @@ const CommentRow = ({ comment, groups, voteColors }) => { nMembers={totalMembers} voteColors={voteColors} /> - ); + ) return (
{ {BarCharts}
- ); -}; + ) +} -class CommentList extends React.Component { +class CommentList extends React.Component< + { + math + conversation + comments + tidsToRender + voteColors + ptptCount + formatTid + }, + { + // + } +> { getGroupLabels() { function makeLabel(key, label, numMembers) { return ( @@ -183,22 +196,22 @@ class CommentList extends React.Component { {numMembers} - ); + ) } - let labels = []; + let labels = [] // totals - labels.push(makeLabel(99, "Overall", this.props.ptptCount)); + labels.push(makeLabel(99, "Overall", this.props.ptptCount)) _.each(this.props.math["group-votes"], (g, i) => { - labels.push(makeLabel(i, globals.groupLabels[i], g["n-members"])); - }); + labels.push(makeLabel(i, globals.groupLabels[i], g["n-members"])) + }) - return labels; + return labels } render() { - const comments = _.keyBy(this.props.comments, "tid"); + const comments = _.keyBy(this.props.comments, "tid") return (
@@ -234,11 +247,11 @@ class CommentList extends React.Component { comment={comments[tid]} voteColors={this.props.voteColors} /> - ); + ) })}
- ); + ) } } -export default CommentList; +export default CommentList diff --git a/client/src/pages/report/components/lists/participantGroup.tsx b/client/src/pages/report/components/lists/participantGroup.tsx index 19f580a07..d5b668d9c 100644 --- a/client/src/pages/report/components/lists/participantGroup.tsx +++ b/client/src/pages/report/components/lists/participantGroup.tsx @@ -1,10 +1,11 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -import * as globals from "../globals"; +import React from "react" +import * as globals from "../globals" +import _ from "lodash" // import Flex from "../framework/flex" // import style from "../../util/style"; -import CommentList from "./commentList"; +import CommentList from "./commentList" const ParticipantGroup = ({ gid, @@ -17,21 +18,22 @@ const ParticipantGroup = ({ ptptCount, groupName, formatTid, + badTids, // groupNames, math, voteColors, }) => { - - let groupLabel = groupName; + let groupLabel = groupName if (typeof groupLabel === "undefined") { - groupLabel = "Group " + globals.groupLabels[gid]; + groupLabel = "Group " + globals.groupLabels[gid] } return (
+ }} + >

{groupLabel}: {groupVotesForThisGroup["n-members"]} participants

@@ -41,15 +43,12 @@ const ParticipantGroup = ({ ptptCount={ptptCount} math={math} formatTid={formatTid} - tidsToRender={_.map(groupComments, 'tid') /* uncertainTids would be funnier */} + tidsToRender={_.map(groupComments, "tid") /* uncertainTids would be funnier */} comments={comments} - voteColors={voteColors}/> - - - - + voteColors={voteColors} + />
- ); -}; + ) +} -export default ParticipantGroup; +export default ParticipantGroup diff --git a/client/src/pages/report/components/lists/participantGroups.tsx b/client/src/pages/report/components/lists/participantGroups.tsx index d2bed6971..e23778f4e 100644 --- a/client/src/pages/report/components/lists/participantGroups.tsx +++ b/client/src/pages/report/components/lists/participantGroups.tsx @@ -1,108 +1,118 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -// import _ from "lodash"; -import Group from "./participantGroup"; +import React from "react" +import _ from "lodash" +import Group from "./participantGroup" // import style from "../../util/style"; -import * as globals from "../globals"; -import Metadata from "./metadata"; +import * as globals from "../globals" +import Metadata from "./metadata" -class ParticipantGroups extends React.Component { +class ParticipantGroups extends React.Component<{ + math + comments + conversation + ptptCount + voteColors + formatTid + demographics + groupNames + badTids + repfulAgreeTidsByGroup + repfulDisageeTidsByGroup + report + style? +}> { constructor(props) { - super(props); - this.state = { - - }; + super(props) + this.state = {} } getStyles() { return { - base: { - - } - }; + base: {}, + } } render() { - const styles = this.getStyles(); + const styles = this.getStyles() if (!this.props.conversation) { - return
Loading Groups
; + return
Loading Groups
} return ( -
-
-

Opinion Groups

-

- Across {this.props.ptptCount} total participants, {this.props.math["group-votes"].length} opinion groups emerged. There are two factors that define an opinion group. First, each opinion group is made up of a number of participants who tended to vote similarly on multiple statements. Second, each group of participants who voted similarly will have also voted distinctly differently from other groups. -

- - { - this.props.math && this.props.comments ? _.map(this.props.math["repness"], (groupComments, gid) => { - gid = Number(gid); - - let otherGroupVotes = { - votes: [], - "n-members": 0, - }; +
+
+

Opinion Groups

+

+ Across {this.props.ptptCount} total participants,{" "} + {this.props.math["group-votes"].length} opinion groups emerged. There are two factors + that define an opinion group. First, each opinion group is made up of a number of + participants who tended to vote similarly on multiple statements. Second, each group of + participants who voted similarly will have also voted distinctly differently from other + groups. +

+ + {this.props.math && this.props.comments + ? _.map(this.props.math["repness"], (groupComments, gid) => { + gid = Number(gid) - let MAX_CLUSTERS = 50; - let temp = this.props.math["group-votes"]; - for (let ogid = 0; ogid < MAX_CLUSTERS; ogid++) { - if (ogid === gid || !temp[ogid]) { - continue; - } - otherGroupVotes["n-members"] += temp[ogid]["n-members"]; - let commentVotes = temp[ogid].votes; - _.each(commentVotes, (voteObj, tid) => { - tid = Number(tid); - if (voteObj) { - if (!otherGroupVotes.votes[tid]) { - otherGroupVotes.votes[tid] = {A: 0, D: 0, S: 0}; + let otherGroupVotes = { + votes: [], + "n-members": 0, } - otherGroupVotes.votes[tid].A += voteObj.A; - otherGroupVotes.votes[tid].D += voteObj.D; - otherGroupVotes.votes[tid].S += voteObj.S; - } - }); - } - return ( - - ); - }) : "Loading Groups" - } -
+ let MAX_CLUSTERS = 50 + let temp = this.props.math["group-votes"] + for (let ogid = 0; ogid < MAX_CLUSTERS; ogid++) { + if (ogid === gid || !temp[ogid]) { + continue + } + otherGroupVotes["n-members"] += temp[ogid]["n-members"] + let commentVotes = temp[ogid].votes + _.each(commentVotes, (voteObj, tid) => { + tid = Number(tid) + if (voteObj) { + if (!otherGroupVotes.votes[tid]) { + otherGroupVotes.votes[tid] = { A: 0, D: 0, S: 0 } + } + otherGroupVotes.votes[tid].A += voteObj.A + otherGroupVotes.votes[tid].D += voteObj.D + otherGroupVotes.votes[tid].S += voteObj.S + } + }) + } + return ( + + ) + }) + : "Loading Groups"} +
- ); + ) } } -export default ParticipantGroups; +export default ParticipantGroups diff --git a/client/src/pages/report/components/participantsGraph/hull.tsx b/client/src/pages/report/components/participantsGraph/hull.tsx index ee0fde054..4f2104108 100644 --- a/client/src/pages/report/components/participantsGraph/hull.tsx +++ b/client/src/pages/report/components/participantsGraph/hull.tsx @@ -1,11 +1,12 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; +import React from "react" +import d3 from "d3" // import * as globals from "../globals"; -const Hull = ({hull}) => { - const line = d3.line().curve(d3.curveLinear); - const pathString = line(hull.hull); +const Hull = ({ hull }) => { + const line = d3.line().curve(d3.curveLinear) + const pathString = line(hull.hull) return ( { strokeDasharray="5, 5" stroke={"rgb(90,90,90)"} fill="none" - fillOpacity={.4}/> - ); -}; + fillOpacity={0.4} + /> + ) +} -export default Hull; +export default Hull // fill={"rgba(0,0,0,.2)" /*globals.groupColor(hull.group[0].gid)*/} diff --git a/client/src/pages/report/components/participantsGraph/participantsGraph.tsx b/client/src/pages/report/components/participantsGraph/participantsGraph.tsx index e48c9f977..1aa158ada 100644 --- a/client/src/pages/report/components/participantsGraph/participantsGraph.tsx +++ b/client/src/pages/report/components/participantsGraph/participantsGraph.tsx @@ -1,33 +1,32 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -import React from "react"; -import _ from "lodash"; -import * as globals from "../globals"; -import graphUtil from "../../util/graphUtil"; -import Axes from "../graphAxes"; -import * as d3contour from "d3-contour"; -import * as d3chromatic from "d3-scale-chromatic"; +import React from "react" +import _ from "lodash" +import * as globals from "../globals" +import graphUtil from "../../util/graphUtil" +import Axes from "../graphAxes" +import d3 from "d3" +import * as d3contour from "d3-contour" +import * as d3chromatic from "d3-scale-chromatic" // import GroupLabels from "./groupLabels"; -import Comments from "../commentsGraph/comments"; -import Hull from "./hull"; -import CommentList from "../lists/commentList"; +import Comments from "../commentsGraph/comments" +import Hull from "./hull" +import CommentList from "../lists/commentList" -const pointsPerSquarePixelMax = 0.0017; /* choose dynamically ? */ -const contourBandwidth = 20; -const colorScaleDownFactor = 0.5; /* The colors are too dark. This helps. */ +const pointsPerSquarePixelMax = 0.0017 /* choose dynamically ? */ +const contourBandwidth = 20 +const colorScaleDownFactor = 0.5 /* The colors are too dark. This helps. */ -const color = d3 - .scaleSequential(d3chromatic.interpolateYlGnBu) - .domain([0, pointsPerSquarePixelMax]); -const geoPath = d3.geoPath(); +const color = d3.scaleSequential(d3chromatic.interpolateYlGnBu).domain([0, pointsPerSquarePixelMax]) +const geoPath = d3.geoPath() const Contour = ({ contour }) => ( -); +) const Participants = ({ points, math }) => { if (!points) { - return null; + return null } return ( @@ -47,7 +46,7 @@ const Participants = ({ points, math }) => { {globals.groupSymbols[pt.gid]} - ); + ) // return ( { // ) })} - ); -}; + ) +} -class ParticipantsGraph extends React.Component { +class ParticipantsGraph extends React.Component< + { + math: any + height?: any + formatTid: any + comments: any + badTids: any + colorBlindMode: any + showOnlyGroup?: any + voteColors: any + consensusDivisionColorScale?: any + conversation?: any + ptptCount?: any + report: any + }, + { + selectedComment: any + showContour: any + showGroupLabels: any + showParticipants: any + showGroupOutline: any + showComments: any + showAxes: any + showRadialAxes: any + consensusDivisionColorScale?: any + } +> { constructor(props) { - super(props); - this.Viewer = null; + super(props) this.state = { selectedComment: null, showContour: false, @@ -81,28 +105,28 @@ class ParticipantsGraph extends React.Component { showComments: true, showAxes: true, showRadialAxes: true, - }; + } } handleCommentClick(selectedComment) { return () => { - this.setState({ selectedComment }); - }; + this.setState({ selectedComment }) + } } getInnerRadialAxisColor() { - let color = globals.brandColors.lightgrey; + let color = globals.brandColors.lightgrey if (this.props.consensusDivisionColorScale && this.props.colorBlindMode) { - color = globals.brandColors.blue; + color = globals.brandColors.blue } else if (this.props.consensusDivisionColorScale && !this.props.colorBlindMode) { - color = this.props.voteColors.agree; + color = this.props.voteColors.agree } - return color; + return color } render() { if (!this.props.math) { - return null; + return null } const { @@ -115,19 +139,19 @@ class ParticipantsGraph extends React.Component { commentScaleupFactorX, commentScaleupFactorY, hulls, - } = graphUtil(this.props.comments, this.props.math, this.props.badTids); + } = graphUtil(this.props.comments, this.props.math, this.props.badTids) const contours = d3contour .contourDensity() .x(function (d) { - return d.x; + return d.x }) .y(function (d) { - return d.y; + return d.y }) .size([globals.side, globals.side]) // .bandwidth(10)(baseClustersScaled) - .bandwidth(contourBandwidth)(baseClustersScaled); + .bandwidth(contourBandwidth)(baseClustersScaled) return (
@@ -174,7 +198,7 @@ class ParticipantsGraph extends React.Component { marginRight: 20, }} onClick={() => { - this.setState({ showAxes: !this.state.showAxes }); + this.setState({ showAxes: !this.state.showAxes }) }} > Axes @@ -192,7 +216,7 @@ class ParticipantsGraph extends React.Component { onClick={() => { this.setState({ showRadialAxes: !this.state.showRadialAxes, - }); + }) }} > Radial axes @@ -224,7 +248,7 @@ class ParticipantsGraph extends React.Component { marginRight: 20, }} onClick={() => { - this.setState({ showComments: !this.state.showComments }); + this.setState({ showComments: !this.state.showComments }) }} > Statements @@ -240,7 +264,7 @@ class ParticipantsGraph extends React.Component { marginRight: 20, }} onClick={() => { - this.setState({ showParticipants: !this.state.showParticipants }); + this.setState({ showParticipants: !this.state.showParticipants }) }} > Participants (bucketized) @@ -256,7 +280,7 @@ class ParticipantsGraph extends React.Component { marginRight: 20, }} onClick={() => { - this.setState({ showGroupOutline: !this.state.showGroupOutline }); + this.setState({ showGroupOutline: !this.state.showGroupOutline }) }} > Group outline @@ -272,7 +296,7 @@ class ParticipantsGraph extends React.Component { marginRight: 20, }} onClick={() => { - this.setState({ showGroupLabels: !this.state.showGroupLabels }); + this.setState({ showGroupLabels: !this.state.showGroupLabels }) }} > Group labels @@ -311,7 +335,7 @@ class ParticipantsGraph extends React.Component { {`${globals.groupLabels[i]}`}{" "} - ); + ) })}

) : null} @@ -377,13 +401,13 @@ class ParticipantsGraph extends React.Component { ) : null} {this.state.showGroupOutline ? hulls.map((hull) => { - let gid = hull.group[0].gid; + let gid = hull.group[0].gid if (_.isNumber(this.props.showOnlyGroup)) { if (gid !== this.props.showOnlyGroup) { - return ""; + return "" } } - return ; + return }) : null} {this.state.showParticipants ? ( @@ -422,7 +446,7 @@ class ParticipantsGraph extends React.Component { > {globals.groupLabels[g.id]} - ); + ) }) : null} {this.state.consensusDivisionColorScale ? ( @@ -444,8 +468,8 @@ class ParticipantsGraph extends React.Component { ) : null}
- ); + ) } } -export default ParticipantsGraph; +export default ParticipantsGraph diff --git a/client/src/pages/report/index.tsx b/client/src/pages/report/index.tsx index 663bf5f73..50366a6a2 100644 --- a/client/src/pages/report/index.tsx +++ b/client/src/pages/report/index.tsx @@ -26,9 +26,7 @@ import ParticipantsGraph from "./components/participantsGraph/participantsGraph" import Beeswarm from "./components/beeswarm/beeswarm" import Controls from "./components/controls/controls" -import net from "../components/util/net" - -import $ from "jquery" +import net from "./util/net" var pathname = window.location.pathname // "/report/2arcefpshi" var report_id = pathname.split("/")[2] @@ -39,7 +37,39 @@ function assertExists(obj, key) { } } -class App extends React.Component { +class App extends React.Component<{ +}, { + loading + consensus + comments + participants + conversation + groupDemographics + colorBlindMode + dimensions + shouldPoll + voteColors + groupNames? + math? + report? + ptptCount? + extremity? + errorText? + error? + uncertainty? + demographics?, + ptptCountTotal? + filteredCorrelationMatrix? + filteredCorrelationTids? + badTids? + repfulAgreeTidsByGroup? + repfulDisageeTidsByGroup? + formatTid? + computedStats? + nothingToShow? +}> { + corMatRetries: any + constructor(props) { super(props) this.state = { @@ -281,7 +311,7 @@ class App extends React.Component { } var tidWidth = ("" + maxTid).length - function pad(n, width, z) { + function pad(n, width, z?) { z = z || "0" n = n + "" return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n @@ -362,7 +392,7 @@ class App extends React.Component { loading: false, math: mathResult, consensus: mathResult.consensus, - extremity: extremity, + extremity, uncertainty: uncertainty.map((c) => { return c.tid }), @@ -485,7 +515,7 @@ class App extends React.Component { colorBlindMode={this.state.colorBlindMode} onAutoRefreshDisabled={this.onAutoRefreshDisabled.bind(this)} autoRefreshEnabled={this.state.shouldPoll} - voteColors={this.state.voteColors} + /*voteColors={this.state.voteColors}*/ /> {/* This may eventually need to go back in below */} @@ -493,12 +523,12 @@ class App extends React.Component { : ""} */} @@ -591,6 +620,4 @@ class App extends React.Component { } } -export default App - -window.$ = $ +export default App \ No newline at end of file diff --git a/client/src/pages/report/store/index.ts b/client/src/pages/report/store/index.ts index 90d873609..102bdf82a 100644 --- a/client/src/pages/report/store/index.ts +++ b/client/src/pages/report/store/index.ts @@ -3,7 +3,7 @@ import { createStore, applyMiddleware, compose } from "redux"; import thunk from "redux-thunk"; -import rootReducer from "../reducers"; +import rootReducer from "../../../reducers"; const middleware = [thunk]; @@ -14,7 +14,7 @@ if (process.env.NODE_ENV === "production") { } else { finalCreateStore = compose( applyMiddleware(...middleware), - window.devToolsExtension ? window.devToolsExtension() : (f) => f + // window["devToolsExtension"] ? window["devToolsExtension"]() : (f) => f )(createStore); } diff --git a/client/src/pages/report/util/graphUtil.ts b/client/src/pages/report/util/graphUtil.ts index 5a3c33d89..b11a65ee1 100644 --- a/client/src/pages/report/util/graphUtil.ts +++ b/client/src/pages/report/util/graphUtil.ts @@ -1,5 +1,7 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +import _ from "lodash"; +import d3 from "d3"; import * as globals from "../components/globals"; import createHull from "hull.js"; diff --git a/client/src/pages/report/util/net.ts b/client/src/pages/report/util/net.ts index d2161a3dc..6a42c6d07 100644 --- a/client/src/pages/report/util/net.ts +++ b/client/src/pages/report/util/net.ts @@ -1,6 +1,7 @@ // Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . import URLs from "./url"; +import _ from "lodash"; var urlPrefix = URLs.urlPrefix; var basePath = ""; diff --git a/client/src/pages/report/util/url.ts b/client/src/pages/report/util/url.ts index 112afbfce..c8d070850 100644 --- a/client/src/pages/report/util/url.ts +++ b/client/src/pages/report/util/url.ts @@ -35,7 +35,7 @@ const getDomainPrefix = () => { if (serviceUrl) return `${serviceUrl}/`; - return `${document.origin}/`; + return `${document.location.origin}/`; }; const urlPrefix = getDomainPrefix(); diff --git a/client/tsconfig.json b/client/tsconfig.json index da4cda8cd..3591df31b 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -9,6 +9,9 @@ "sourceMap": true, "moduleResolution": "node" }, + "rules": { + "@typescript-eslint/no-unused-vars": "off", + }, "include": [ "src/**/*.ts", "src/**/*.tsx"