From cb3d5a770a6d502e14eda3bd54f860a8adf5036e Mon Sep 17 00:00:00 2001 From: Brendan O'Brien Date: Thu, 14 Nov 2019 19:10:49 -0500 Subject: [PATCH] feat(Boolean Stat Chart): add boolean stat, move Stat component out of directory --- .../{stats/Stat.tsx => StatsChart.tsx} | 116 ++++++++++++++---- package.json | 1 + stories/0-Stats.stories.tsx | 57 ++++++--- stories/data/stats_all_types.json | 97 +++++++++++++++ yarn.lock | 14 +++ 5 files changed, 243 insertions(+), 42 deletions(-) rename app/components/{stats/Stat.tsx => StatsChart.tsx} (66%) create mode 100644 stories/data/stats_all_types.json diff --git a/app/components/stats/Stat.tsx b/app/components/StatsChart.tsx similarity index 66% rename from app/components/stats/Stat.tsx rename to app/components/StatsChart.tsx index 9400b8b1..04fc4df8 100644 --- a/app/components/stats/Stat.tsx +++ b/app/components/StatsChart.tsx @@ -1,20 +1,23 @@ import { ResponsiveBar } from '@nivo/bar' +import { ResponsivePie } from '@nivo/pie' import * as React from 'react' const primaryStatColor = '#0061A6' -interface StatProps { +interface StatsChartProps { height: number data: Record } -const Stat: React.FunctionComponent = (props: StatProps) => { +const StatsChart: React.FunctionComponent = (props: StatsChartProps) => { const { data, height = 250 } = props switch (data.type) { case 'string': return case 'numeric': return + case 'boolean': + return default: return (
@@ -26,9 +29,28 @@ const Stat: React.FunctionComponent = (props: StatProps) => { } } -export default Stat +export default StatsChart -const NumericStat: React.FunctionComponent = (props: StatProps) => { +interface StatValuesProps { + stats: Array<[string, any]> +} + +const StatValues: React.FunctionComponent = ({ stats }) => { + return ( +
+ {stats.map((stat, i) => { + return ( +
+

{stat[1]}

+ +
+ ) + })} +
+ ) +} + +const NumericStat: React.FunctionComponent = (props: StatsChartProps) => { const { data, height } = props let histogram @@ -91,26 +113,7 @@ const NumericStat: React.FunctionComponent = (props: StatProps) => { ) } -interface StatValuesProps { - stats: Array<[string, any]> -} - -const StatValues: React.FunctionComponent = ({ stats }) => { - return ( -
- {stats.map((stat, i) => { - return ( -
-

{stat[1]}

- -
- ) - })} -
- ) -} - -const StringStat: React.FunctionComponent = (props: StatProps) => { +const StringStat: React.FunctionComponent = (props: StatsChartProps) => { const { data, height } = props let frequencies @@ -176,3 +179,68 @@ const StringStat: React.FunctionComponent = (props: StatProps) => {
) } + +const BooleanStat: React.FunctionComponent = (props: StatsChartProps) => { + const { data, height } = props + let frequencies = [ + { + id: 'false', + label: 'false', + value: data.falseCount, + color: '#EE325C' + }, + { + id: 'true', + label: 'true', + value: data.trueCount, + color: primaryStatColor + }] + + const other = data.count - data.trueCount - data.falseCount + if (other !== 0) { + frequencies.push({ + id: 'other', + label: 'other', + value: other, + color: '#F4A935' + }) + } + + return ( +
+ {frequencies &&
+
+ frequencies +
+ d.color } + cornerRadius={3} + borderWidth={1} + borderColor={{ from: 'color', modifiers: [ [ 'darker', 0.2 ] ] }} + radialLabelsSkipAngle={10} + radialLabelsTextXOffset={6} + radialLabelsTextColor="#333333" + radialLabelsLinkOffset={0} + radialLabelsLinkDiagonalLength={16} + radialLabelsLinkHorizontalLength={0} + radialLabelsLinkStrokeWidth={1} + radialLabelsLinkColor={{ from: 'color' }} + slicesLabelsSkipAngle={10} + slicesLabelsTextColor="#333333" + animate={true} + motionStiffness={90} + motionDamping={15} + /> +
} + +
+ ) +} diff --git a/package.json b/package.json index 5114daca..42f3cf1a 100644 --- a/package.json +++ b/package.json @@ -218,6 +218,7 @@ "@fortawesome/free-solid-svg-icons": "5.11.2", "@fortawesome/react-fontawesome": "0.1.7", "@nivo/bar": "^0.59.2", + "@nivo/pie": "^0.59.1", "@storybook/cli": "^5.2.6", "@storybook/react": "^5.2.6", "@types/webpack-env": "1.14.1", diff --git a/stories/0-Stats.stories.tsx b/stories/0-Stats.stories.tsx index 9cc983ed..053355a2 100644 --- a/stories/0-Stats.stories.tsx +++ b/stories/0-Stats.stories.tsx @@ -1,9 +1,10 @@ import React from 'react' -import Stat from '../app/components/stats/Stat.tsx' +import StatsChart from '../app/components/StatsChart.tsx' import fjcReport from './data/stats_nyc_report_family_justice_center_stats.json' +import allTypes from './data/stats_all_types.json' export default { - title: 'Stats', + title: 'Stats|Charts', parameters: { notes: `feeding different data to the stats component. @@ -11,6 +12,27 @@ stats data from the [nyc fjc report](https://data.cityofnewyork.us/Social-Servic } } +export const overview = () => { + return ( +
+ {allTypes.map((d, i) => { + return ( +
+

{d.type}

+
+ +
+ ) + })} +
+ ) +} + +overview.story = { + name: 'All Stats', + parameters: { note: 'idealized overview of each stat' } +} + const wrap = (component) => { return (
@@ -19,29 +41,28 @@ const wrap = (component) => { ) } -export const zero = () => wrap() +export const languages = () => wrap() -zero.story = { - name: 'Many Long String', +languages.story = { + name: 'String: Languages' } -export const one = () => wrap() +export const manyLongStrings = () => wrap() -one.story = { - name: 'Empty String' +manyLongStrings.story = { + name: 'String: Many Long', } -export const two = () => wrap() +export const empty = () => wrap() -two.story = { - name: 'Yes/No/Blank String' +empty.story = { + name: 'String: Empty' } +export const yesNoBlank = () => wrap() -export const three = () => wrap() - -three.story = { - name: 'Languages String' +yesNoBlank.story = { + name: 'String: Yes/No/Blank' } const charityFdnGrant2016AmountStat = { @@ -80,9 +101,9 @@ const charityFdnGrant2016AmountStat = { "type": "numeric" } -export const four = () => wrap() +export const lopsided = () => wrap() -four.story = { - name: 'Lopsided Numeric', +lopsided.story = { + name: 'Numeric: Lopsided', parameters: { notes: 'bonkers histogram from charitable foundation donations in 2016'} } \ No newline at end of file diff --git a/stories/data/stats_all_types.json b/stories/data/stats_all_types.json new file mode 100644 index 00000000..c5cdd5e0 --- /dev/null +++ b/stories/data/stats_all_types.json @@ -0,0 +1,97 @@ +[ + { + "count": 5, + "falseCount": 3, + "key": "bool", + "trueCount": 2, + "type": "boolean" + }, + { + "count": 5, + "histogram": { + "bins": [ + 1.1, + 1.6400000000000001, + 2.18, + 2.72, + 3.2600000000000002, + 3.8000000000000003, + 4.34, + 4.880000000000001, + 5.42, + 5.960000000000001, + 6.5 + ], + "frequencies": [ + 2, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0 + ] + }, + "key": "float", + "max": 5.5, + "mean": 3.08, + "median": 3.3, + "min": 1.1, + "type": "numeric" + }, + { + "count": 5, + "histogram": { + "bins": [ + 1, + 1.5, + 2, + 2.5, + 3, + 3.5, + 4, + 4.5, + 5, + 5.5, + 6 + ], + "frequencies": [ + 2, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0 + ] + }, + "key": "int", + "max": 5, + "mean": 2.8, + "median": 3, + "min": 1, + "type": "numeric" + }, + { + "count": 5, + "key": "nil", + "type": "null" + }, + { + "count": 5, + "frequencies": { + "aaa": 2 + }, + "key": "string", + "maxLength": 5, + "minLength": 1, + "type": "string", + "unique": 3 + } +] \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index cefb42a5..5a1647e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1681,6 +1681,20 @@ lodash "^4.17.11" recompose "^0.30.0" +"@nivo/pie@^0.59.1": + version "0.59.1" + resolved "https://registry.yarnpkg.com/@nivo/pie/-/pie-0.59.1.tgz#08be965f877cabb96c14bb2eefbe6022d856877e" + integrity sha512-odQPqQ3CmAbqS9Te3N7VsbKKBByjxDPe3SqTCUPysqGC5f+/DrynKwDxRSqOSL0HG7s0mq+vvA5kaW5EIeIEPg== + dependencies: + "@nivo/colors" "0.59.0" + "@nivo/core" "0.59.1" + "@nivo/legends" "0.59.1" + "@nivo/tooltip" "0.59.1" + d3-shape "^1.3.5" + lodash "^4.17.11" + react-motion "^0.5.2" + recompose "^0.30.0" + "@nivo/tooltip@0.59.1": version "0.59.1" resolved "https://registry.yarnpkg.com/@nivo/tooltip/-/tooltip-0.59.1.tgz#82e981d6af397b1f18a8a2906c8fd60da0234881"