Skip to content

Commit

Permalink
Click on bars to select
Browse files Browse the repository at this point in the history
  • Loading branch information
bgoldowsky committed Sep 17, 2024
1 parent 7041be4 commit a39f279
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 39 deletions.
26 changes: 13 additions & 13 deletions src/plugins/bar-graph/bar-graph-content.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,23 @@ 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 }}
]);
});

it("sets first dataset attribute as the primary attribute by default", () => {
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 }}
]);
});

Expand All @@ -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 }}
]);
});

Expand All @@ -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 }}
]);

});
Expand Down
16 changes: 16 additions & 0 deletions src/plugins/bar-graph/bar-graph-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => ({
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/bar-graph/bar-graph-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
65 changes: 41 additions & 24 deletions src/plugins/bar-graph/chart-area.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -104,7 +114,8 @@ export const ChartArea = observer(function BarGraphChart({ width, height }: IPro
const w = primaryScale.bandwidth();
const h = yMax - countScale(info.count);
return (
<BarWithHighlight key={key} x={x} y={y} width={w} height={h} color={color} selected={info.selected} />
<BarWithHighlight key={key} x={x} y={y} width={w} height={h} color={color} selected={info.selected}
onClick={() => handleClick(key)} />
);
})}
</Group>
Expand All @@ -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) =>
<Group className="visx-bar-group">
{barGroups.map((barGroup) => (
<Group key={`bar-group-${barGroup.index}`} left={barGroup.x0}>
{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 <BarWithHighlight
key={`bar-group-bar-${barGroup.index}-${bar.index}`}
x={bar.x}
y={bar.y}
width={bar.width}
height={bar.height}
color={bar.color}
selected={val.selected}
/>;
{(barGroups) => {
return (
<Group className="visx-bar-group">
{barGroups.map((barGroup) => {
const primaryValue = data[barGroup.index][primary] as string;
return (
<Group key={`bar-group-${barGroup.index}`} left={barGroup.x0}>
{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 <BarWithHighlight
key={`bar-group-bar-${barGroup.index}-${bar.index}`}
x={bar.x}
y={bar.y}
width={bar.width}
height={bar.height}
color={bar.color}
selected={val.selected}
onClick={() => handleClick(primaryValue, bar.key)} />;
})}
</Group>
);
})}
</Group>
))}
</Group>
}
);
}}
</BarGroup>
);
}
Expand Down Expand Up @@ -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 (
<Group>
{selected && <BarHighlight x={x} y={y} width={width} height={height} />}
<Bar x={x} y={y} width={width} height={height} fill={color} />
<Bar onClick={onClick} x={x} y={y} width={width} height={height} fill={color} />
</Group>
);
}

0 comments on commit a39f279

Please sign in to comment.