From a39f2794ed5f5e28a5476943fae94a51ddbe9139 Mon Sep 17 00:00:00 2001 From: Boris Goldowsky Date: Tue, 17 Sep 2024 11:28:22 -0400 Subject: [PATCH] Click on bars to select --- .../bar-graph/bar-graph-content.test.ts | 26 ++++---- src/plugins/bar-graph/bar-graph-content.ts | 16 +++++ src/plugins/bar-graph/bar-graph-utils.ts | 5 +- src/plugins/bar-graph/chart-area.tsx | 65 ++++++++++++------- 4 files changed, 73 insertions(+), 39 deletions(-) diff --git a/src/plugins/bar-graph/bar-graph-content.test.ts b/src/plugins/bar-graph/bar-graph-content.test.ts index 565a7f56f..79c70942b 100644 --- a/src/plugins/bar-graph/bar-graph-content.test.ts +++ b/src/plugins/bar-graph/bar-graph-content.test.ts @@ -107,14 +107,14 @@ Object { content.setSharedModel(sharedSampleDataSet()); content.setPrimaryAttribute("att-s"); expect(content.dataArray).toEqual([ - { "att-s": "cat", "value": 2 }, - { "att-s": "owl","value": 2} + { "att-s": "cat", "value": { count: 2, selected: false }}, + { "att-s": "owl","value": { count: 2, selected: false }} ]); content.setPrimaryAttribute("att-l"); expect(content.dataArray).toEqual([ - { "att-l": "yard", "value": 3 }, - { "att-l": "forest", "value": 1 } + { "att-l": "yard", "value": { count: 3, selected: false }}, + { "att-l": "forest", "value": { count: 1, selected: false }} ]); }); @@ -122,8 +122,8 @@ Object { const content = TestingBarGraphContentModel.create({ }); content.setSharedModel(sharedSampleDataSet()); expect(content.dataArray).toEqual([ - { "att-s": "cat", "value": 2 }, - { "att-s": "owl","value": 2} + { "att-s": "cat", "value": { count: 2, selected: false }}, + { "att-s": "owl","value": { count: 2, selected: false }} ]); }); @@ -133,8 +133,8 @@ Object { content.setPrimaryAttribute("att-s"); content.setSecondaryAttribute("att-l"); expect(content.dataArray).toEqual([ - { "att-s": "cat", "yard": 2 }, - { "att-s": "owl", "yard": 1, "forest": 1 } + { "att-s": "cat", "yard": { count: 2, selected: false }}, + { "att-s": "owl", "yard": { count: 1, selected: false }, "forest": { count: 1, selected: false }} ]); }); @@ -146,15 +146,15 @@ Object { content.setPrimaryAttribute("att-s"); content.setSecondaryAttribute("att-l"); expect(content.dataArray).toEqual([ - { "att-s": "cat", "yard": 2 }, - { "att-s": "owl", "yard": 1, "(no value)": 1 } + { "att-s": "cat", "yard": { count: 2, selected: false }}, + { "att-s": "owl", "yard": { count: 1, selected: false}, "(no value)": { count: 1, selected: false }} ]); dataSet.dataSet?.attributes[0].setValue(3, undefined); // hide that owl entirely expect(content.dataArray).toEqual([ - { "att-s": "cat", "yard": 2 }, - { "att-s": "owl", "yard": 1 }, - { "att-s": "(no value)", "(no value)": 1 } + { "att-s": "cat", "yard": { count: 2, selected: false }}, + { "att-s": "owl", "yard": { count: 1, selected: false }}, + { "att-s": "(no value)", "(no value)": { count: 1, selected: false }} ]); }); diff --git a/src/plugins/bar-graph/bar-graph-content.ts b/src/plugins/bar-graph/bar-graph-content.ts index bbbacfc2d..7ef2a9a53 100644 --- a/src/plugins/bar-graph/bar-graph-content.ts +++ b/src/plugins/bar-graph/bar-graph-content.ts @@ -145,6 +145,22 @@ export const BarGraphContentModel = TileContentModel }, setSecondaryAttribute(attrId: string|undefined) { self.secondaryAttribute = attrId; + }, + selectCasesByValues(primaryVal: string, secondaryVal?: string) { + const dataSet = self.sharedModel?.dataSet; + const cases = self.cases; + const primaryAttribute = self.primaryAttribute; + if (!dataSet || !cases || !primaryAttribute) return; + const secondaryAttribute = self.secondaryAttribute; + if (!secondaryAttribute && secondaryVal) return; + let matchingCases = cases + .filter(caseID => displayValue(dataSet.getStrValue(caseID.__id__, primaryAttribute)) === primaryVal); + if (secondaryAttribute && secondaryVal) { + matchingCases = matchingCases + .filter(caseID => displayValue(dataSet.getStrValue(caseID.__id__, secondaryAttribute)) === secondaryVal); + } + const caseIds = matchingCases.map(caseID => caseID.__id__); + dataSet.setSelectedCases(caseIds); } })) .actions(self => ({ diff --git a/src/plugins/bar-graph/bar-graph-utils.ts b/src/plugins/bar-graph/bar-graph-utils.ts index e5a1d8681..a742c6c91 100644 --- a/src/plugins/bar-graph/bar-graph-utils.ts +++ b/src/plugins/bar-graph/bar-graph-utils.ts @@ -37,9 +37,10 @@ export function updateBarGraphContentWithNewSharedModelIds( } // Define types here to document all possible values that this tile logs -type LoggableOperation = "setPrimaryAttribute" | "setSecondaryAttribute" | "setYAxisLabel"; +type LoggableOperation = "setPrimaryAttribute" | "setSecondaryAttribute" | "setYAxisLabel" | "selectCases"; type LoggableChange = { - attributeId?: string; + attributeId?: string | string[]; + attributeValue?: string | string[]; text?: string; }; diff --git a/src/plugins/bar-graph/chart-area.tsx b/src/plugins/bar-graph/chart-area.tsx index cc7768771..d0e9d89f2 100644 --- a/src/plugins/bar-graph/chart-area.tsx +++ b/src/plugins/bar-graph/chart-area.tsx @@ -92,6 +92,16 @@ export const ChartArea = observer(function BarGraphChart({ width, height }: IPro const labelWidth = (xMax/primaryKeys.length)-10; // setting width will wrap lines when needed + function handleClick(primaryValue: string, secondaryValue?: string) { + if (!model || !model.primaryAttribute) return; + model.selectCasesByValues(primaryValue, secondaryValue); + logBarGraphEvent(model, "selectCases", { + attributeId: + model.secondaryAttribute ? [model.primaryAttribute, model.secondaryAttribute] : model.primaryAttribute, + attributeValue: secondaryValue ? [primaryValue, secondaryValue] : primaryValue + }); + } + function simpleBars() { const color = barColor(primary); return ( @@ -104,7 +114,8 @@ export const ChartArea = observer(function BarGraphChart({ width, height }: IPro const w = primaryScale.bandwidth(); const h = yMax - countScale(info.count); return ( - + handleClick(key)} /> ); })} @@ -123,29 +134,34 @@ export const ChartArea = observer(function BarGraphChart({ width, height }: IPro x1Scale={secondaryScale} yScale={((info: BarInfo) => countScale(info?.count||0)) as PositionScale} > - {(barGroups) => - - {barGroups.map((barGroup) => ( - - {barGroup.bars.map((bar) => { - if (!bar.value) return null; - // BarGroup really expects the values to be pure numeric, but we're using objects. - // Alternatively, we could drop BarGroup and build the bars manually. - const val = bar.value as unknown as BarInfo; - return ; + {(barGroups) => { + return ( + + {barGroups.map((barGroup) => { + const primaryValue = data[barGroup.index][primary] as string; + return ( + + {barGroup.bars.map((bar) => { + if (!bar.value) return null; + // BarGroup really expects the values to be pure numeric, but we're using objects. + // Alternatively, we could drop BarGroup and build the bars manually. + const val = bar.value as unknown as BarInfo; + return handleClick(primaryValue, bar.key)} />; + })} + + ); })} - ))} - - } + ); + }} ); } @@ -222,13 +238,14 @@ interface IBarWithHighlightProps { height: number; color: string; selected: boolean; + onClick: () => void; } -function BarWithHighlight({ x, y, width, height, color, selected }: IBarWithHighlightProps) { +function BarWithHighlight({ x, y, width, height, color, selected, onClick }: IBarWithHighlightProps) { return ( {selected && } - + ); }