From 01d0bddfeefb8f0422d906b8429c21769a157e40 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Thu, 26 Sep 2024 11:52:32 -0400 Subject: [PATCH 01/12] rough draft: add static visualizations and total cohort count --- src/dataset-builder/CohortEditor.ts | 205 ++++++++++++++++++++++------ 1 file changed, 167 insertions(+), 38 deletions(-) diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index 8b52848bde..78f9c4129d 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -2,6 +2,7 @@ import { Spinner, useLoadedData } from '@terra-ui-packages/components'; import _ from 'lodash/fp'; import React, { Fragment, useEffect, useRef, useState } from 'react'; import { div, h, h2, h3, strong } from 'react-hyperscript-helpers'; +import Chart from 'src/components/Chart'; import { ButtonOutline, ButtonPrimary, GroupedSelect, Link, Select } from 'src/components/common'; import Slider from 'src/components/common/Slider'; import { icon } from 'src/components/icons'; @@ -580,6 +581,21 @@ interface CohortEditorProps { readonly addSelectedCohort: (cohort: Cohort) => void; readonly getNextCriteriaIndex: () => number; } +interface ChartLabel { + short: string; + long?: string; +} +interface ChartSeries { + name?: string; + data: number[]; +} + +interface CohortDemographics { + categories: ChartLabel[]; + series: ChartSeries[]; + title: string; + yTitle: string; +} export const CohortEditor: React.FC = (props) => { const { @@ -592,48 +608,161 @@ export const CohortEditor: React.FC = (props) => { getNextCriteriaIndex, } = props; const [cohort, setCohort] = useState(originalCohort); + const [snapshotRequestParticipantCount, setSnapshotRequestParticipantCount] = + useLoadedData(); + const [cohortDemographics1] = useState({ + categories: [ + { short: 'Female' }, + { short: 'Male' }, + { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + ], + series: [{ data: [40, 50, 30] }], + title: 'Gender identity', + yTitle: 'AVERAGE AGE', + }); + const [cohortDemographics2] = useState({ + categories: [ + { short: 'Female' }, + { short: 'Female 18-44' }, + { short: 'Female 45-64' }, + { short: 'Female 65+' }, + { short: 'Male' }, + { short: 'Male 18-44' }, + { short: 'Male 45-64' }, + { short: 'Male 65+' }, + { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + { short: 'Other 18-44', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + ], + series: [ + { name: 'Asian', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 1, 1] }, + { name: 'Black', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 1, 1] }, + { name: 'White', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 1, 1] }, + { name: 'Native American', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 0, 0] }, + { name: 'Pacific Islander', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 0, 0, 0] }, + ], + title: 'Gender identity, current age, race', + yTitle: 'OVERALL PERCENTAGE', + }); + + const chartOptions: any = (cohortDemographics: CohortDemographics) => { + return { + chart: { marginTop: 50, spacingLeft: 20, style: { fontFamily: 'inherit' }, type: 'bar' }, + // legend: { reversed: true }, + plotOptions: { series: { stacking: 'normal' } }, + series: cohortDemographics.series, + title: { + align: 'left', + style: { fontSize: '16px', fontWeight: 'bold', color: '#333f52' }, + text: cohortDemographics.title, + }, + tooltip: { + followPointer: true, + shared: true, + headerFormat: '{point.key}', + // pointFormatter() { + // // @ts-ignore + // // eslint-disable-next-line react/no-this-in-sfc + // return `
\u25CF ${ + // // @ts-ignore + // // eslint-disable-next-line react/no-this-in-sfc + // this.series.name + // }: ${ + // // @ts-ignore + // // eslint-disable-next-line react/no-this-in-sfc + // costPerWorkspace.costFormatter.format(this.y) + // }`; + // }, + }, + xAxis: { + categories: _.map('short', cohortDemographics.categories), + crosshair: true, + }, + yAxis: { + crosshair: true, + min: 0, + // labels: { text: ''}, + title: { text: cohortDemographics.yTitle }, + width: '96%', + }, + accessibility: { + // point: { + // descriptionFormatter: (point) => { + // // @ts-ignore + // return `${point.index + 1}. Workspace ${point.category}, ${ + // point.series.name + // }: ${costPerWorkspace.costFormatter.format(point.y)}.`; + // }, + // }, + }, + exporting: { buttons: { contextButton: { x: -15 } } }, + }; + }; const updateCohort = (updateCohort: (Cohort) => Cohort) => setCohort(updateCohort); - return h(Fragment, [ - h(CohortEditorContents, { - updateCohort, - cohort, - snapshotId, - snapshotBuilderSettings, - onStateChange, - getNextCriteriaIndex, - }), - // add div to cover page to footer - div( - { - style: { - display: 'flex', - backgroundColor: editorBackgroundColor, - alignItems: 'end', - flexDirection: 'row-reverse', - padding: wideMargin, + useEffect(() => { + setSnapshotRequestParticipantCount( + withErrorReporting(`Error fetching snapshot builder count for snapshot ${snapshotId}`)(async () => + DataRepo() + .snapshot(snapshotId) + .getSnapshotBuilderCount(createSnapshotBuilderCountRequest([cohort])) + ) + ); + }, [snapshotId, setSnapshotRequestParticipantCount, cohort]); + + return div({ style: { display: 'flex' } }, [ + div([ + h(CohortEditorContents, { + updateCohort, + cohort, + snapshotId, + snapshotBuilderSettings, + onStateChange, + getNextCriteriaIndex, + }), + // add div to cover page to footer + div( + { + style: { + display: 'flex', + backgroundColor: editorBackgroundColor, + alignItems: 'end', + flexDirection: 'row-reverse', + padding: '0 3rem', + }, }, - }, - [ - h( - ButtonPrimary, - { - onClick: () => { - updateCohorts((cohorts) => { - const index = _.findIndex((c) => _.equals(c.name, cohort.name), cohorts); - if (index === -1) { - // Only add to selectedCohorts on creation of new cohort - addSelectedCohort(cohort); - } - return _.set(`[${index === -1 ? cohorts.length : index}]`, cohort, cohorts); - }); - onStateChange(homepageState.new()); + [ + h( + ButtonPrimary, + { + onClick: () => { + updateCohorts((cohorts) => { + const index = _.findIndex((c) => _.equals(c.name, cohort.name), cohorts); + if (index === -1) { + // Only add to selectedCohorts on creation of new cohort + addSelectedCohort(cohort); + } + return _.set(`[${index === -1 ? cohorts.length : index}]`, cohort, cohorts); + }); + onStateChange(homepageState.new()); + }, }, - }, - ['Save cohort'] - ), - ] - ), + ['Save cohort'] + ), + ] + ), + ]), + div({ style: { backgroundColor: 'white', width: '42rem' } }, [ + h2({ style: { marginLeft: '1rem' } }, [ + 'Total participant count: ', + snapshotRequestParticipantCount.status === 'Ready' + ? formatCount(snapshotRequestParticipantCount.state.result.total) + : h(Spinner), + ]), + h(Chart, { options: chartOptions(cohortDemographics1) }), + h(Chart, { options: chartOptions(cohortDemographics2) }), + ]), ]); }; From e2d608499b5cc9425139811d04405c444d18ec41 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Thu, 26 Sep 2024 15:33:58 -0400 Subject: [PATCH 02/12] add height --- src/dataset-builder/CohortEditor.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index 78f9c4129d..c6d6697a9c 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -595,6 +595,7 @@ interface CohortDemographics { series: ChartSeries[]; title: string; yTitle: string; + height: string; } export const CohortEditor: React.FC = (props) => { @@ -619,6 +620,7 @@ export const CohortEditor: React.FC = (props) => { series: [{ data: [40, 50, 30] }], title: 'Gender identity', yTitle: 'AVERAGE AGE', + height: '250rem', }); const [cohortDemographics2] = useState({ categories: [ @@ -644,11 +646,18 @@ export const CohortEditor: React.FC = (props) => { ], title: 'Gender identity, current age, race', yTitle: 'OVERALL PERCENTAGE', + height: '500rem', }); const chartOptions: any = (cohortDemographics: CohortDemographics) => { return { - chart: { marginTop: 50, spacingLeft: 20, style: { fontFamily: 'inherit' }, type: 'bar' }, + chart: { + marginTop: 50, + spacingLeft: 20, + height: cohortDemographics.height, + style: { fontFamily: 'inherit' }, + type: 'bar', + }, // legend: { reversed: true }, plotOptions: { series: { stacking: 'normal' } }, series: cohortDemographics.series, From 0e7647ed68b7aacb582c40fd587930f3d4b0f7b0 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Fri, 27 Sep 2024 11:33:46 -0400 Subject: [PATCH 03/12] add legend enabled attribute, fix tooltip text --- src/dataset-builder/CohortEditor.ts | 59 +++++++++++++++-------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index c6d6697a9c..73a9dada26 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -596,6 +596,8 @@ interface CohortDemographics { title: string; yTitle: string; height: string; + legendEnabled: boolean; + showSeriesName: boolean; } export const CohortEditor: React.FC = (props) => { @@ -617,10 +619,12 @@ export const CohortEditor: React.FC = (props) => { { short: 'Male' }, { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, ], - series: [{ data: [40, 50, 30] }], + series: [{ name: 'Overall', data: [40, 50, 30] }], title: 'Gender identity', yTitle: 'AVERAGE AGE', height: '250rem', + legendEnabled: false, + showSeriesName: false, }); const [cohortDemographics2] = useState({ categories: [ @@ -633,9 +637,9 @@ export const CohortEditor: React.FC = (props) => { { short: 'Male 45-64' }, { short: 'Male 65+' }, { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, - { short: 'Other 18-44', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, - { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, - { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + { short: 'Other 18-44', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 18-44' }, + { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 45-64' }, + { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 65+' }, ], series: [ { name: 'Asian', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 1, 1] }, @@ -647,6 +651,8 @@ export const CohortEditor: React.FC = (props) => { title: 'Gender identity, current age, race', yTitle: 'OVERALL PERCENTAGE', height: '500rem', + legendEnabled: true, + showSeriesName: true, }); const chartOptions: any = (cohortDemographics: CohortDemographics) => { @@ -658,7 +664,7 @@ export const CohortEditor: React.FC = (props) => { style: { fontFamily: 'inherit' }, type: 'bar', }, - // legend: { reversed: true }, + legend: { enabled: cohortDemographics.legendEnabled }, plotOptions: { series: { stacking: 'normal' } }, series: cohortDemographics.series, title: { @@ -668,21 +674,20 @@ export const CohortEditor: React.FC = (props) => { }, tooltip: { followPointer: true, - shared: true, - headerFormat: '{point.key}', - // pointFormatter() { - // // @ts-ignore - // // eslint-disable-next-line react/no-this-in-sfc - // return `
\u25CF ${ - // // @ts-ignore - // // eslint-disable-next-line react/no-this-in-sfc - // this.series.name - // }: ${ - // // @ts-ignore - // // eslint-disable-next-line react/no-this-in-sfc - // costPerWorkspace.costFormatter.format(this.y) - // }`; - // }, + formatter() { + // @ts-ignore + // eslint-disable-next-line react/no-this-in-sfc + const currCategory = _.find((category) => category.short === this.x, cohortDemographics.categories); + const categoryDescription = currCategory.long || currCategory.short; + if (cohortDemographics.showSeriesName) { + // @ts-ignore + // eslint-disable-next-line react/no-this-in-sfc + return `${categoryDescription}
\u25CF ${this.series.name}
${this.y}`; + } + // @ts-ignore + // eslint-disable-next-line react/no-this-in-sfc + return `${categoryDescription}
${this.y}`; + }, }, xAxis: { categories: _.map('short', cohortDemographics.categories), @@ -696,14 +701,12 @@ export const CohortEditor: React.FC = (props) => { width: '96%', }, accessibility: { - // point: { - // descriptionFormatter: (point) => { - // // @ts-ignore - // return `${point.index + 1}. Workspace ${point.category}, ${ - // point.series.name - // }: ${costPerWorkspace.costFormatter.format(point.y)}.`; - // }, - // }, + point: { + descriptionFormatter: (point) => { + // @ts-ignore + return `${point.index + 1}. Category ${point.category}, ${point.series.name}: ${point.y}.`; + }, + }, }, exporting: { buttons: { contextButton: { x: -15 } } }, }; From 4282ced76d4e161a4d3abf7715e9d048911520a5 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Fri, 27 Sep 2024 14:37:53 -0400 Subject: [PATCH 04/12] add functions to generate to the series data --- src/dataset-builder/CohortEditor.ts | 72 +++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index 73a9dada26..6d92d9b9dc 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -600,6 +600,58 @@ interface CohortDemographics { showSeriesName: boolean; } +function generateRandomNumbers(numNumbers: number, max: number) { + const randomNumbers: number[] = []; + for (let i = 0; i < numNumbers; i++) { + const randomNumber = Math.floor(Math.random() * max) + 1; + randomNumbers.push(randomNumber); + } + return randomNumbers; +} + +function generateRandomNumbersThatAddUpTo(total: number, numNumbers: number): number[] { + const randomNumbers: number[] = []; + let remaining = total; + for (let i = 0; i < numNumbers; i++) { + const randomNumber = Math.floor(Math.random() * remaining) + 1; + remaining -= randomNumber; + randomNumbers.push(randomNumber); + } + return randomNumbers; +} + +// series and data will always be the same length +function addToSeriesData(series: ChartSeries[], data: number[]) { + for (let i = 0; i < series.length; i++) { + series[i].data.push(data[i]); + } +} + +function generateSeries(): ChartSeries[] { + // order of data points is Female, Male, Other, Female 18-44, Female 45-64, Female 65+, Male 18-44, Male + const series: ChartSeries[] = [ + { name: 'Asian', data: [] }, + { name: 'Black', data: [] }, + { name: 'White', data: [] }, + { name: 'Native American', data: [] }, + { name: 'Pacific Islander', data: [] }, + ]; + // for each of the three gender identity totals, + const genderTotals = generateRandomNumbersThatAddUpTo(100, 3); + for (const genderTotal of genderTotals) { + // generate the race breakdown for the gender totals + const genderTotalRaceBreakDown = generateRandomNumbersThatAddUpTo(genderTotal, 5); + addToSeriesData(series, genderTotalRaceBreakDown); + // get the three age group breakdowns + const ageGroupGenderTotal = generateRandomNumbersThatAddUpTo(genderTotal, 3); + // get race breakdowns for each of the gender age groups + for (const genderAgeGroupTotal of ageGroupGenderTotal) { + const genderAgeGroupRaceBreakdown = generateRandomNumbersThatAddUpTo(genderAgeGroupTotal, 5); + addToSeriesData(series, genderAgeGroupRaceBreakdown); + } + } + return series; +} export const CohortEditor: React.FC = (props) => { const { onStateChange, @@ -619,7 +671,7 @@ export const CohortEditor: React.FC = (props) => { { short: 'Male' }, { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, ], - series: [{ name: 'Overall', data: [40, 50, 30] }], + series: [{ name: 'Overall', data: generateRandomNumbers(3, 100) }], title: 'Gender identity', yTitle: 'AVERAGE AGE', height: '250rem', @@ -641,13 +693,7 @@ export const CohortEditor: React.FC = (props) => { { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 45-64' }, { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 65+' }, ], - series: [ - { name: 'Asian', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 1, 1] }, - { name: 'Black', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 1, 1] }, - { name: 'White', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 1, 1] }, - { name: 'Native American', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 1, 0, 0] }, - { name: 'Pacific Islander', data: [9, 3, 3, 3, 9, 5, 3, 1, 2, 0, 0, 0] }, - ], + series: generateSeries(), title: 'Gender identity, current age, race', yTitle: 'OVERALL PERCENTAGE', height: '500rem', @@ -655,11 +701,11 @@ export const CohortEditor: React.FC = (props) => { showSeriesName: true, }); - const chartOptions: any = (cohortDemographics: CohortDemographics) => { + function chartOptions(cohortDemographics: CohortDemographics) { return { chart: { - marginTop: 50, spacingLeft: 20, + spacingRight: 30, height: cohortDemographics.height, style: { fontFamily: 'inherit' }, type: 'bar', @@ -695,22 +741,18 @@ export const CohortEditor: React.FC = (props) => { }, yAxis: { crosshair: true, - min: 0, - // labels: { text: ''}, title: { text: cohortDemographics.yTitle }, - width: '96%', }, accessibility: { point: { descriptionFormatter: (point) => { - // @ts-ignore return `${point.index + 1}. Category ${point.category}, ${point.series.name}: ${point.y}.`; }, }, }, exporting: { buttons: { contextButton: { x: -15 } } }, }; - }; + } const updateCohort = (updateCohort: (Cohort) => Cohort) => setCohort(updateCohort); From 22960c1cc995512c52605e7cb791c5b9b3637498 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Fri, 27 Sep 2024 16:28:39 -0400 Subject: [PATCH 05/12] fix randomNumbersThatAddUpTo --- src/dataset-builder/CohortEditor.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index 6d92d9b9dc..33e1a22edf 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -612,12 +612,13 @@ function generateRandomNumbers(numNumbers: number, max: number) { function generateRandomNumbersThatAddUpTo(total: number, numNumbers: number): number[] { const randomNumbers: number[] = []; let remaining = total; - for (let i = 0; i < numNumbers; i++) { - const randomNumber = Math.floor(Math.random() * remaining) + 1; + for (let i = 0; i < numNumbers - 1; i++) { + const randomNumber = Math.floor(Math.random() * remaining); remaining -= randomNumber; randomNumbers.push(randomNumber); } - return randomNumbers; + randomNumbers.push(remaining); + return _.shuffle(randomNumbers); } // series and data will always be the same length From f2ae525af9a348cb89e79af544cd86848a63e3d3 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Mon, 30 Sep 2024 12:47:02 -0400 Subject: [PATCH 06/12] add state so graphs re-render --- src/dataset-builder/CohortEditor.ts | 133 +++++---------------------- src/dataset-builder/TestConstants.ts | 110 ++++++++++++++++++++++ 2 files changed, 135 insertions(+), 108 deletions(-) diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index 33e1a22edf..675f39cb14 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -18,6 +18,13 @@ import { ProgramDataListCriteria, ProgramDataRangeCriteria, } from 'src/dataset-builder/DatasetBuilderUtils'; +import { + cohortAgeData, + cohortDemographicData, + CohortDemographics, + generateAgeSeries, + generateDemographicSeries, +} from 'src/dataset-builder/TestConstants'; import { DataRepo, SnapshotBuilderCountResponse, @@ -581,78 +588,7 @@ interface CohortEditorProps { readonly addSelectedCohort: (cohort: Cohort) => void; readonly getNextCriteriaIndex: () => number; } -interface ChartLabel { - short: string; - long?: string; -} -interface ChartSeries { - name?: string; - data: number[]; -} - -interface CohortDemographics { - categories: ChartLabel[]; - series: ChartSeries[]; - title: string; - yTitle: string; - height: string; - legendEnabled: boolean; - showSeriesName: boolean; -} - -function generateRandomNumbers(numNumbers: number, max: number) { - const randomNumbers: number[] = []; - for (let i = 0; i < numNumbers; i++) { - const randomNumber = Math.floor(Math.random() * max) + 1; - randomNumbers.push(randomNumber); - } - return randomNumbers; -} - -function generateRandomNumbersThatAddUpTo(total: number, numNumbers: number): number[] { - const randomNumbers: number[] = []; - let remaining = total; - for (let i = 0; i < numNumbers - 1; i++) { - const randomNumber = Math.floor(Math.random() * remaining); - remaining -= randomNumber; - randomNumbers.push(randomNumber); - } - randomNumbers.push(remaining); - return _.shuffle(randomNumbers); -} -// series and data will always be the same length -function addToSeriesData(series: ChartSeries[], data: number[]) { - for (let i = 0; i < series.length; i++) { - series[i].data.push(data[i]); - } -} - -function generateSeries(): ChartSeries[] { - // order of data points is Female, Male, Other, Female 18-44, Female 45-64, Female 65+, Male 18-44, Male - const series: ChartSeries[] = [ - { name: 'Asian', data: [] }, - { name: 'Black', data: [] }, - { name: 'White', data: [] }, - { name: 'Native American', data: [] }, - { name: 'Pacific Islander', data: [] }, - ]; - // for each of the three gender identity totals, - const genderTotals = generateRandomNumbersThatAddUpTo(100, 3); - for (const genderTotal of genderTotals) { - // generate the race breakdown for the gender totals - const genderTotalRaceBreakDown = generateRandomNumbersThatAddUpTo(genderTotal, 5); - addToSeriesData(series, genderTotalRaceBreakDown); - // get the three age group breakdowns - const ageGroupGenderTotal = generateRandomNumbersThatAddUpTo(genderTotal, 3); - // get race breakdowns for each of the gender age groups - for (const genderAgeGroupTotal of ageGroupGenderTotal) { - const genderAgeGroupRaceBreakdown = generateRandomNumbersThatAddUpTo(genderAgeGroupTotal, 5); - addToSeriesData(series, genderAgeGroupRaceBreakdown); - } - } - return series; -} export const CohortEditor: React.FC = (props) => { const { onStateChange, @@ -666,41 +602,8 @@ export const CohortEditor: React.FC = (props) => { const [cohort, setCohort] = useState(originalCohort); const [snapshotRequestParticipantCount, setSnapshotRequestParticipantCount] = useLoadedData(); - const [cohortDemographics1] = useState({ - categories: [ - { short: 'Female' }, - { short: 'Male' }, - { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, - ], - series: [{ name: 'Overall', data: generateRandomNumbers(3, 100) }], - title: 'Gender identity', - yTitle: 'AVERAGE AGE', - height: '250rem', - legendEnabled: false, - showSeriesName: false, - }); - const [cohortDemographics2] = useState({ - categories: [ - { short: 'Female' }, - { short: 'Female 18-44' }, - { short: 'Female 45-64' }, - { short: 'Female 65+' }, - { short: 'Male' }, - { short: 'Male 18-44' }, - { short: 'Male 45-64' }, - { short: 'Male 65+' }, - { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, - { short: 'Other 18-44', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 18-44' }, - { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 45-64' }, - { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 65+' }, - ], - series: generateSeries(), - title: 'Gender identity, current age, race', - yTitle: 'OVERALL PERCENTAGE', - height: '500rem', - legendEnabled: true, - showSeriesName: true, - }); + const [cohortAges, setCohortAges] = useState(cohortAgeData); + const [cohortDemographics, setCohortDemographics] = useState(cohortDemographicData); function chartOptions(cohortDemographics: CohortDemographics) { return { @@ -767,6 +670,20 @@ export const CohortEditor: React.FC = (props) => { ); }, [snapshotId, setSnapshotRequestParticipantCount, cohort]); + useEffect(() => { + setTimeout(() => { + cohortAgeData.series = generateAgeSeries(3, 90); + setCohortAges(cohortAgeData); + }, 2000); + }, [snapshotRequestParticipantCount]); + + useEffect(() => { + setTimeout(() => { + cohortDemographicData.series = generateDemographicSeries(); + setCohortDemographics(cohortDemographicData); + }, 2000); + }, [snapshotRequestParticipantCount]); + return div({ style: { display: 'flex' } }, [ div([ h(CohortEditorContents, { @@ -816,8 +733,8 @@ export const CohortEditor: React.FC = (props) => { ? formatCount(snapshotRequestParticipantCount.state.result.total) : h(Spinner), ]), - h(Chart, { options: chartOptions(cohortDemographics1) }), - h(Chart, { options: chartOptions(cohortDemographics2) }), + h(Chart, { options: chartOptions(cohortAges) }), + h(Chart, { options: chartOptions(cohortDemographics) }), ]), ]); }; diff --git a/src/dataset-builder/TestConstants.ts b/src/dataset-builder/TestConstants.ts index a914bfb8de..2ee452eb77 100644 --- a/src/dataset-builder/TestConstants.ts +++ b/src/dataset-builder/TestConstants.ts @@ -175,3 +175,113 @@ const dummyConcepts = [ export const dummyGetConceptForId = (id: number): Concept => { return _.find({ id }, dummyConcepts)!; }; + +interface ChartLabel { + short: string; + long?: string; +} +interface ChartSeries { + name?: string; + data: number[]; +} + +export interface CohortDemographics { + categories: ChartLabel[]; + series: ChartSeries[]; + title: string; + yTitle: string; + height: string; + legendEnabled: boolean; + showSeriesName: boolean; +} + +export const cohortAgeData = { + categories: [ + { short: 'Female' }, + { short: 'Male' }, + { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + ], + series: generateAgeSeries(3, 90), + title: 'Gender identity', + yTitle: 'AVERAGE AGE', + height: '250rem', + legendEnabled: false, + showSeriesName: false, +}; + +export const cohortDemographicData = { + categories: [ + { short: 'Female' }, + { short: 'Female 18-44' }, + { short: 'Female 45-64' }, + { short: 'Female 65+' }, + { short: 'Male' }, + { short: 'Male 18-44' }, + { short: 'Male 45-64' }, + { short: 'Male 65+' }, + { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + { short: 'Other 18-44', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 18-44' }, + { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 45-64' }, + { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 65+' }, + ], + series: generateDemographicSeries(), + title: 'Gender identity, current age, race', + yTitle: 'OVERALL PERCENTAGE', + height: '500rem', + legendEnabled: true, + showSeriesName: true, +}; + +export function generateAgeSeries(numNumbers: number, max: number) { + const randomNumbers: number[] = []; + for (let i = 0; i < numNumbers; i++) { + const randomNumber = Math.floor(Math.random() * max) + 1; + randomNumbers.push(randomNumber); + } + return [{ name: 'Overall', data: randomNumbers }]; +} + +function generateRandomNumbersThatAddUpTo(total: number, numNumbers: number): number[] { + const randomNumbers: number[] = []; + let remaining = total; + for (let i = 0; i < numNumbers - 1; i++) { + const randomNumber = Math.floor(Math.random() * remaining); + remaining -= randomNumber; + randomNumbers.push(randomNumber); + } + randomNumbers.push(remaining); + return _.shuffle(randomNumbers); +} + +// series and data will always be the same length +function addToSeriesData(series: ChartSeries[], data: number[]) { + for (let i = 0; i < series.length; i++) { + series[i].data.push(data[i]); + } +} + +export function generateDemographicSeries(): ChartSeries[] { + // order of data points is Female, Male, Other, Female 18-44, Female 45-64, Female 65+, Male 18-44, Male + const series: ChartSeries[] = [ + { name: 'Asian', data: [] }, + { name: 'Black', data: [] }, + { name: 'White', data: [] }, + { name: 'Native American', data: [] }, + { name: 'Pacific Islander', data: [] }, + ]; + // for each of the three gender identity totals, + const genderTotals = generateRandomNumbersThatAddUpTo(100, 3); + for (const genderTotal of genderTotals) { + // generate the race breakdown for the gender totals + const genderTotalRaceBreakDown = generateRandomNumbersThatAddUpTo(genderTotal, 5); + addToSeriesData(series, genderTotalRaceBreakDown); + // get the three age group breakdowns + const ageGroupGenderTotal = generateRandomNumbersThatAddUpTo(genderTotal, 3); + // get race breakdowns for each of the gender age groups + for (const genderAgeGroupTotal of ageGroupGenderTotal) { + const genderAgeGroupRaceBreakdown = generateRandomNumbersThatAddUpTo(genderAgeGroupTotal, 5); + addToSeriesData(series, genderAgeGroupRaceBreakdown); + } + } + return series; +} From fc82127edeb0d86610d67fdca7ac41e271fad2e1 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Tue, 1 Oct 2024 10:43:59 -0400 Subject: [PATCH 07/12] add unit tests of helper functions --- src/dataset-builder/CohortEditor.ts | 4 +- src/dataset-builder/TestConstants.test.ts | 76 +++++++++++++++++++++++ src/dataset-builder/TestConstants.ts | 11 ++-- 3 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 src/dataset-builder/TestConstants.test.ts diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index 675f39cb14..c1e46141dc 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -22,8 +22,8 @@ import { cohortAgeData, cohortDemographicData, CohortDemographics, - generateAgeSeries, generateDemographicSeries, + generateRandomNumbers, } from 'src/dataset-builder/TestConstants'; import { DataRepo, @@ -672,7 +672,7 @@ export const CohortEditor: React.FC = (props) => { useEffect(() => { setTimeout(() => { - cohortAgeData.series = generateAgeSeries(3, 90); + cohortAgeData.series = [{ data: generateRandomNumbers(3, 90) }]; setCohortAges(cohortAgeData); }, 2000); }, [snapshotRequestParticipantCount]); diff --git a/src/dataset-builder/TestConstants.test.ts b/src/dataset-builder/TestConstants.test.ts new file mode 100644 index 0000000000..6b10d1f612 --- /dev/null +++ b/src/dataset-builder/TestConstants.test.ts @@ -0,0 +1,76 @@ +import { forEach } from 'lodash'; +import { + addToSeriesData, + generateDemographicSeries, + generateRandomNumbers, + generateRandomNumbersThatAddUpTo, +} from 'src/dataset-builder/TestConstants'; + +describe('TestConstants', () => { + it('generateRandomNumbers', () => { + const nums = generateRandomNumbers(3, 90); + expect(nums.length).toBe(3); + forEach(nums, (num) => { + expect(num <= 90).toBe(true); + }); + }); + + it('generateRandomNumbersThatAddUpTo', () => { + const nums = generateRandomNumbersThatAddUpTo(100, 3); + expect(nums.length).toBe(3); + expect(nums.reduce((a, b) => a + b, 0)).toBe(100); + }); + + it('addToSeriesData', () => { + const series = [{ data: [1, 2, 3] }, { data: [5, 6, 7] }]; + const data = [4, 8]; + addToSeriesData(series, data); + expect(series[0].data).toEqual([1, 2, 3, 4]); + expect(series[1].data).toEqual([5, 6, 7, 8]); + }); + + it('addToSeriesData error case', () => { + const series = [{ data: [1, 2, 3] }]; + const data = [4, 8]; + expect(() => { + addToSeriesData(series, data); + }).toThrowError('series and data must be the same length'); + }); + + it('generateDemographicsSeries', () => {}); + const series = generateDemographicSeries(); + expect(series.length).toBe(5); + forEach(series, (s) => { + expect(s.data.length).toBe(12); + }); + let female = 0; + let female18 = 0; + let female45 = 0; + let female65 = 0; + let male = 0; + let male18 = 0; + let male45 = 0; + let male65 = 0; + let nonbinary = 0; + let nonbinary18 = 0; + let nonbinary45 = 0; + let nonbinary65 = 0; + forEach(series, (s) => { + female += s.data[0]; + female18 += s.data[1]; + female45 += s.data[2]; + female65 += s.data[3]; + male += s.data[4]; + male18 += s.data[5]; + male45 += s.data[6]; + male65 += s.data[7]; + nonbinary += s.data[8]; + nonbinary18 += s.data[9]; + nonbinary45 += s.data[10]; + nonbinary65 += s.data[11]; + }); + expect(female + male + nonbinary).toBe(100); + expect(female18 + female45 + female65).toBe(female); + expect(male18 + male45 + male65).toBe(male); + expect(nonbinary18 + nonbinary45 + nonbinary65).toBe(nonbinary); +}); diff --git a/src/dataset-builder/TestConstants.ts b/src/dataset-builder/TestConstants.ts index 2ee452eb77..e64e76a68a 100644 --- a/src/dataset-builder/TestConstants.ts +++ b/src/dataset-builder/TestConstants.ts @@ -201,7 +201,7 @@ export const cohortAgeData = { { short: 'Male' }, { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, ], - series: generateAgeSeries(3, 90), + series: [{ data: generateRandomNumbers(3, 90) }], title: 'Gender identity', yTitle: 'AVERAGE AGE', height: '250rem', @@ -232,16 +232,16 @@ export const cohortDemographicData = { showSeriesName: true, }; -export function generateAgeSeries(numNumbers: number, max: number) { +export function generateRandomNumbers(numNumbers: number, max: number) { const randomNumbers: number[] = []; for (let i = 0; i < numNumbers; i++) { const randomNumber = Math.floor(Math.random() * max) + 1; randomNumbers.push(randomNumber); } - return [{ name: 'Overall', data: randomNumbers }]; + return randomNumbers; } -function generateRandomNumbersThatAddUpTo(total: number, numNumbers: number): number[] { +export function generateRandomNumbersThatAddUpTo(total: number, numNumbers: number): number[] { const randomNumbers: number[] = []; let remaining = total; for (let i = 0; i < numNumbers - 1; i++) { @@ -254,7 +254,8 @@ function generateRandomNumbersThatAddUpTo(total: number, numNumbers: number): nu } // series and data will always be the same length -function addToSeriesData(series: ChartSeries[], data: number[]) { +export function addToSeriesData(series: ChartSeries[], data: number[]) { + if (series.length !== data.length) throw new Error('series and data must be the same length'); for (let i = 0; i < series.length; i++) { series[i].data.push(data[i]); } From 01df407fda6a8908b76febf64d45c5f2e7ef7ea8 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Tue, 1 Oct 2024 12:03:10 -0400 Subject: [PATCH 08/12] block charts on return of count endpoint --- src/dataset-builder/CohortEditor.ts | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index c1e46141dc..6ac488839e 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -726,15 +726,24 @@ export const CohortEditor: React.FC = (props) => { ] ), ]), - div({ style: { backgroundColor: 'white', width: '42rem' } }, [ - h2({ style: { marginLeft: '1rem' } }, [ - 'Total participant count: ', - snapshotRequestParticipantCount.status === 'Ready' - ? formatCount(snapshotRequestParticipantCount.state.result.total) - : h(Spinner), - ]), - h(Chart, { options: chartOptions(cohortAges) }), - h(Chart, { options: chartOptions(cohortDemographics) }), - ]), + div( + { style: { backgroundColor: 'white', width: '42rem' } }, + snapshotRequestParticipantCount.status === 'Ready' + ? [ + h2({ style: { marginLeft: '1rem' } }, [ + 'Total participant count: ', + formatCount(snapshotRequestParticipantCount.state.result.total), + ]), + h(Chart, { options: chartOptions(cohortAges) }), + h(Chart, { options: chartOptions(cohortDemographics) }), + ] + : [ + h2({ style: { marginLeft: '1rem' } }, [ + 'Total participant count: ', + h(Spinner), + div({ style: { marginTop: '1rem' } }, ['Loading...']), + ]), + ] + ), ]); }; From 9193dceb6a4e66099798f3ada98a0fa2b8f473f0 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Tue, 1 Oct 2024 12:51:09 -0400 Subject: [PATCH 09/12] test shows total cohort count --- src/dataset-builder/CohortEditor.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/dataset-builder/CohortEditor.test.ts b/src/dataset-builder/CohortEditor.test.ts index 48386340d0..434ed89086 100644 --- a/src/dataset-builder/CohortEditor.test.ts +++ b/src/dataset-builder/CohortEditor.test.ts @@ -546,4 +546,12 @@ describe('CohortEditor', () => { ) ); }); + + it('renders total cohort count', async () => { + // Arrange + showCohortEditor(); + + // Assert + expect(await screen.findByText('Total participant count: 0', { exact: false })).toBeTruthy(); + }); }); From cd2e3447d30f737b005716da3caf157dd3ea5070 Mon Sep 17 00:00:00 2001 From: rjohanek Date: Tue, 1 Oct 2024 16:17:00 -0400 Subject: [PATCH 10/12] test for graph labels --- src/dataset-builder/CohortEditor.test.ts | 29 +++++++++++++++++++++++- src/dataset-builder/TestConstants.ts | 4 ++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/dataset-builder/CohortEditor.test.ts b/src/dataset-builder/CohortEditor.test.ts index 434ed89086..7fa239b99e 100644 --- a/src/dataset-builder/CohortEditor.test.ts +++ b/src/dataset-builder/CohortEditor.test.ts @@ -550,8 +550,35 @@ describe('CohortEditor', () => { it('renders total cohort count', async () => { // Arrange showCohortEditor(); - // Assert expect(await screen.findByText('Total participant count: 0', { exact: false })).toBeTruthy(); + // expect(getSnapshotBuilderCountMock().getSnapshotBuilderCount).toBeCalled(); + }); + + it('displays cohort demographic information', async () => { + // Arrange + // const mockRandomValue = 0.5; + // jest.spyOn(Math, 'random').mockReturnValue(mockRandomValue); + showCohortEditor(); + + expect(await screen.findAllByText('Female')).toHaveLength(2); + expect(await screen.findAllByText('Male')).toHaveLength(2); + expect(await screen.findAllByText('Other')).toHaveLength(2); + + expect(await screen.findByText('Female 18-44')).toBeTruthy(); + expect(await screen.findByText('Female 45-64')).toBeTruthy(); + expect(await screen.findByText('Female 65+')).toBeTruthy(); + expect(await screen.findByText('Male 18-44')).toBeTruthy(); + expect(await screen.findByText('Male 45-64')).toBeTruthy(); + expect(await screen.findByText('Male 65+')).toBeTruthy(); + expect(await screen.findByText('Other 18-44')).toBeTruthy(); + expect(await screen.findByText('Other 45-64')).toBeTruthy(); + expect(await screen.findByText('Other 65+')).toBeTruthy(); + + expect(await screen.findByText('Asian')).toBeTruthy(); + expect(await screen.findByText('Black')).toBeTruthy(); + expect(await screen.findByText('Native American')).toBeTruthy(); + expect(await screen.findByText('Pacific Islander')).toBeTruthy(); + expect(await screen.findByText('White')).toBeTruthy(); }); }); diff --git a/src/dataset-builder/TestConstants.ts b/src/dataset-builder/TestConstants.ts index e64e76a68a..ba16312f3f 100644 --- a/src/dataset-builder/TestConstants.ts +++ b/src/dataset-builder/TestConstants.ts @@ -202,7 +202,7 @@ export const cohortAgeData = { { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, ], series: [{ data: generateRandomNumbers(3, 90) }], - title: 'Gender identity', + title: 'Gender identity and current age', yTitle: 'AVERAGE AGE', height: '250rem', legendEnabled: false, @@ -225,7 +225,7 @@ export const cohortDemographicData = { { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 65+' }, ], series: generateDemographicSeries(), - title: 'Gender identity, current age, race', + title: 'Gender identity, current age, and race', yTitle: 'OVERALL PERCENTAGE', height: '500rem', legendEnabled: true, From d3926167a55d6b3dbf3b5dbdeffdcd5530aaf78a Mon Sep 17 00:00:00 2001 From: rjohanek Date: Thu, 3 Oct 2024 11:59:24 -0400 Subject: [PATCH 11/12] rm commented out code --- src/dataset-builder/CohortEditor.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/dataset-builder/CohortEditor.test.ts b/src/dataset-builder/CohortEditor.test.ts index 7fa239b99e..ca345a9f79 100644 --- a/src/dataset-builder/CohortEditor.test.ts +++ b/src/dataset-builder/CohortEditor.test.ts @@ -552,13 +552,9 @@ describe('CohortEditor', () => { showCohortEditor(); // Assert expect(await screen.findByText('Total participant count: 0', { exact: false })).toBeTruthy(); - // expect(getSnapshotBuilderCountMock().getSnapshotBuilderCount).toBeCalled(); }); it('displays cohort demographic information', async () => { - // Arrange - // const mockRandomValue = 0.5; - // jest.spyOn(Math, 'random').mockReturnValue(mockRandomValue); showCohortEditor(); expect(await screen.findAllByText('Female')).toHaveLength(2); From 17466c52bac21fc8a8459ac52e1327c796c066ab Mon Sep 17 00:00:00 2001 From: rjohanek Date: Fri, 4 Oct 2024 14:01:55 -0400 Subject: [PATCH 12/12] fix state by moving defaults into state --- src/dataset-builder/CohortEditor.test.ts | 2 +- src/dataset-builder/CohortEditor.ts | 72 ++++++++++---------- src/dataset-builder/TestConstants.ts | 86 ++++++++++++++---------- 3 files changed, 85 insertions(+), 75 deletions(-) diff --git a/src/dataset-builder/CohortEditor.test.ts b/src/dataset-builder/CohortEditor.test.ts index ca345a9f79..3ec0f24859 100644 --- a/src/dataset-builder/CohortEditor.test.ts +++ b/src/dataset-builder/CohortEditor.test.ts @@ -554,7 +554,7 @@ describe('CohortEditor', () => { expect(await screen.findByText('Total participant count: 0', { exact: false })).toBeTruthy(); }); - it('displays cohort demographic information', async () => { + it('displays cohort demographic visualization', async () => { showCohortEditor(); expect(await screen.findAllByText('Female')).toHaveLength(2); diff --git a/src/dataset-builder/CohortEditor.ts b/src/dataset-builder/CohortEditor.ts index 6ac488839e..eee3714f12 100644 --- a/src/dataset-builder/CohortEditor.ts +++ b/src/dataset-builder/CohortEditor.ts @@ -19,11 +19,11 @@ import { ProgramDataRangeCriteria, } from 'src/dataset-builder/DatasetBuilderUtils'; import { - cohortAgeData, - cohortDemographicData, CohortDemographics, - generateDemographicSeries, - generateRandomNumbers, + generateCohortAgeData, + generateCohortDemographicData, + generateRandomCohortAgeData, + generateRandomCohortDemographicData, } from 'src/dataset-builder/TestConstants'; import { DataRepo, @@ -602,9 +602,19 @@ export const CohortEditor: React.FC = (props) => { const [cohort, setCohort] = useState(originalCohort); const [snapshotRequestParticipantCount, setSnapshotRequestParticipantCount] = useLoadedData(); - const [cohortAges, setCohortAges] = useState(cohortAgeData); - const [cohortDemographics, setCohortDemographics] = useState(cohortDemographicData); - + const defaultCohortDemographicSeries = [ + { name: 'Asian', data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, + { name: 'Black', data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, + { name: 'White', data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, + { name: 'Native American', data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, + { name: 'Pacific Islander', data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, + ]; + const defaultCohortAgeSeries = [{ data: [0, 0, 0] }]; + const [cohortAges, setCohortAges] = useState(generateCohortAgeData(defaultCohortAgeSeries)); + const [cohortDemographics, setCohortDemographics] = useState( + generateCohortDemographicData(defaultCohortDemographicSeries) + ); + const countStatus = snapshotRequestParticipantCount.status; function chartOptions(cohortDemographics: CohortDemographics) { return { chart: { @@ -671,18 +681,13 @@ export const CohortEditor: React.FC = (props) => { }, [snapshotId, setSnapshotRequestParticipantCount, cohort]); useEffect(() => { - setTimeout(() => { - cohortAgeData.series = [{ data: generateRandomNumbers(3, 90) }]; - setCohortAges(cohortAgeData); - }, 2000); - }, [snapshotRequestParticipantCount]); - - useEffect(() => { - setTimeout(() => { - cohortDemographicData.series = generateDemographicSeries(); - setCohortDemographics(cohortDemographicData); - }, 2000); - }, [snapshotRequestParticipantCount]); + countStatus === 'Ready' + ? (setCohortAges(generateRandomCohortAgeData()), setCohortDemographics(generateRandomCohortDemographicData())) + : (setCohortAges(generateCohortAgeData(defaultCohortAgeSeries)), + setCohortDemographics(generateCohortDemographicData(defaultCohortDemographicSeries))); + // @ts-ignore + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [countStatus]); return div({ style: { display: 'flex' } }, [ div([ @@ -726,24 +731,15 @@ export const CohortEditor: React.FC = (props) => { ] ), ]), - div( - { style: { backgroundColor: 'white', width: '42rem' } }, - snapshotRequestParticipantCount.status === 'Ready' - ? [ - h2({ style: { marginLeft: '1rem' } }, [ - 'Total participant count: ', - formatCount(snapshotRequestParticipantCount.state.result.total), - ]), - h(Chart, { options: chartOptions(cohortAges) }), - h(Chart, { options: chartOptions(cohortDemographics) }), - ] - : [ - h2({ style: { marginLeft: '1rem' } }, [ - 'Total participant count: ', - h(Spinner), - div({ style: { marginTop: '1rem' } }, ['Loading...']), - ]), - ] - ), + div({ style: { width: '42rem' } }, [ + h2({ style: { padding: '1rem', display: 'flex', alignItems: 'center', margin: 0, backgroundColor: 'white' } }, [ + 'Total participant count: ', + snapshotRequestParticipantCount.status === 'Ready' + ? formatCount(snapshotRequestParticipantCount.state.result.total) + : h(Spinner, { style: { marginLeft: '1rem' } }), + ]), + h(Chart, { options: chartOptions(cohortAges) }), + h(Chart, { options: chartOptions(cohortDemographics) }), + ]), ]); }; diff --git a/src/dataset-builder/TestConstants.ts b/src/dataset-builder/TestConstants.ts index ba16312f3f..12068f5876 100644 --- a/src/dataset-builder/TestConstants.ts +++ b/src/dataset-builder/TestConstants.ts @@ -195,42 +195,57 @@ export interface CohortDemographics { showSeriesName: boolean; } -export const cohortAgeData = { - categories: [ - { short: 'Female' }, - { short: 'Male' }, - { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, - ], - series: [{ data: generateRandomNumbers(3, 90) }], - title: 'Gender identity and current age', - yTitle: 'AVERAGE AGE', - height: '250rem', - legendEnabled: false, - showSeriesName: false, -}; +export function generateCohortAgeData(series) { + return { + categories: [ + { short: 'Female' }, + { short: 'Male' }, + { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + ], + series, + title: 'Gender identity and current age', + yTitle: 'AVERAGE AGE', + height: '250rem', + legendEnabled: false, + showSeriesName: false, + }; +} -export const cohortDemographicData = { - categories: [ - { short: 'Female' }, - { short: 'Female 18-44' }, - { short: 'Female 45-64' }, - { short: 'Female 65+' }, - { short: 'Male' }, - { short: 'Male 18-44' }, - { short: 'Male 45-64' }, - { short: 'Male 65+' }, - { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, - { short: 'Other 18-44', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 18-44' }, - { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 45-64' }, - { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 65+' }, - ], - series: generateDemographicSeries(), - title: 'Gender identity, current age, and race', - yTitle: 'OVERALL PERCENTAGE', - height: '500rem', - legendEnabled: true, - showSeriesName: true, -}; +export function generateRandomCohortAgeSeries() { + return [{ data: generateRandomNumbers(3, 90) }]; +} +export function generateRandomCohortAgeData() { + return generateCohortAgeData(generateRandomCohortAgeSeries()); +} + +export function generateCohortDemographicData(series) { + return { + categories: [ + { short: 'Female' }, + { short: 'Female 18-44' }, + { short: 'Female 45-64' }, + { short: 'Female 65+' }, + { short: 'Male' }, + { short: 'Male 18-44' }, + { short: 'Male 45-64' }, + { short: 'Male 65+' }, + { short: 'Other', long: 'Nonbinary, 2 Spirit, Genderqueer, etc.' }, + { short: 'Other 18-44', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 18-44' }, + { short: 'Other 45-64', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 45-64' }, + { short: 'Other 65+', long: 'Nonbinary, 2 Spirit, Genderqueer, etc. 65+' }, + ], + series, + title: 'Gender identity, current age, and race', + yTitle: 'OVERALL PERCENTAGE', + height: '500rem', + legendEnabled: true, + showSeriesName: true, + }; +} + +export function generateRandomCohortDemographicData() { + return generateCohortDemographicData(generateDemographicSeries()); +} export function generateRandomNumbers(numNumbers: number, max: number) { const randomNumbers: number[] = []; @@ -262,7 +277,6 @@ export function addToSeriesData(series: ChartSeries[], data: number[]) { } export function generateDemographicSeries(): ChartSeries[] { - // order of data points is Female, Male, Other, Female 18-44, Female 45-64, Female 65+, Male 18-44, Male const series: ChartSeries[] = [ { name: 'Asian', data: [] }, { name: 'Black', data: [] },