Skip to content

Commit

Permalink
Merge pull request #2409 from concord-consortium/188129114-bar-graph-…
Browse files Browse the repository at this point in the history
…select

Bar graph selection
  • Loading branch information
bgoldowsky committed Sep 18, 2024
2 parents 7a65c8c + 6a918e6 commit e31b61e
Show file tree
Hide file tree
Showing 8 changed files with 407 additions and 55 deletions.
157 changes: 156 additions & 1 deletion cypress/e2e/functional/tile_tests/bar_graph_tile_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ context('Bar Graph Tile', function () {
}
});

it('Can link data ', function () {
it('Linking data', function () {
beforeTest();

clueCanvas.addTile('bargraph');
Expand Down Expand Up @@ -335,4 +335,159 @@ context('Bar Graph Tile', function () {
}
});

it('Synchronizing selection', function () {
beforeTest();

clueCanvas.addTile('bargraph');

// Table dataset for testing:
// 4 instances of X / Y / Z
// 2 instances of XX / Y / Z
// 1 instance of X / YY / Z
clueCanvas.addTile('table');
tableTile.fillTable(tableTile.getTableTile(), [
['X', 'Y', 'Z'],
['XX', 'Y', 'Z'],
['X', 'YY', 'Z'],
['X', 'Y', 'Z'],
['XX', 'Y', 'Z'],
['X', 'Y', 'Z'],
['X', 'Y', 'Z'],
]);

barGraph.getTile().click();
clueCanvas.clickToolbarButton('bargraph', 'link-tile');
cy.get('select').select('Table Data 1');
cy.get('.modal-button').contains("Graph It!").click();

cy.log("Check synchronization of case selection with one attribute");

// Selecting cases in the table should highlight the corresponding bars in the bar graph
tableTile.getSelectedRow(workspaces[0]).should('have.length', 0);
for (const workspace of workspaces) {
barGraph.getBarHighlight(workspace).should('have.length', 0);
}
tableTile.getTableIndexColumnCell().eq(0).click(); // first X Y Z case, X bar selected
tableTile.getSelectedRow(workspaces[0]).should('have.length', 1);
barGraph.getBarHighlight(workspaces[0]).should('have.length', 1);
barGraph.getBarHighlight(workspaces[1]).should('have.length', 1);
// Selection is local, volatile state, so remote workspace should not be affected
barGraph.getBarHighlight(workspaces[2]).should('have.length', 0);
tableTile.getTableIndexColumnCell().eq(1).click({shiftKey: true}); // first XX Y Z case, X and XX bars selected
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 2);
}
tableTile.getTableIndexColumnCell().eq(2).click({shiftKey: true}); // first X YY Z case, X and XX bars selected
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 2);
}
tableTile.getTableIndexColumnCell().eq(0).click({shiftKey: true});
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 2);
}
tableTile.getTableIndexColumnCell().eq(1).click({shiftKey: true});
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 1);
}
tableTile.getTableIndexColumnCell().eq(2).click({shiftKey: true});
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 0);
}

// Clicking on bars should select the corresponding cases in the table
tableTile.getSelectedRow(workspaces[0]).should('have.length', 0);
barGraph.getBar().eq(0).click();
for (const workspace of workspaces.slice(0, 2)) {
tableTile.getSelectedRow(workspace).should('have.length', 5); // All "X" cases
}
barGraph.getBar().eq(1).click();
for (const workspace of workspaces.slice(0, 2)) {
tableTile.getSelectedRow(workspace).should('have.length', 2); // All "XX" cases
}
// Unselect the two selected cases, which should be numbers 1 and 4
tableTile.getTableIndexColumnCell().eq(1).click({shiftKey: true});
tableTile.getTableIndexColumnCell().eq(4).click({shiftKey: true});
tableTile.getSelectedRow(workspaces[0]).should('have.length', 0);

cy.log("Check synchronization of case selection with two attributes");
barGraph.getSortByMenuButton().click();
barGraph.getChakraMenuItem().should('have.length', 3);
barGraph.getChakraMenuItem().eq(1).should('have.text', 'y').click();
barGraph.getBar().should("have.length", 3);

tableTile.getTableIndexColumnCell().eq(0).click(); // first X Y Z case, X Y bar selected
tableTile.getSelectedRow(workspaces[0]).should('have.length', 1);
barGraph.getBarHighlight(workspaces[0]).should('have.length', 1);
barGraph.getBarHighlight(workspaces[1]).should('have.length', 1);
// Selection is local, volatile state, so remote workspace should not be affected
barGraph.getBarHighlight(workspaces[2]).should('have.length', 0);

tableTile.getTableIndexColumnCell().eq(1).click({shiftKey: true}); // first XX Y Z case, X Y and XX Y bars selected
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 2);
}
tableTile.getTableIndexColumnCell().eq(2).click({shiftKey: true}); // first X YY Z case, X Y, XX Y, and X YY bars selected
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 3);
}
tableTile.getTableIndexColumnCell().eq(0).click({shiftKey: true});
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 2);
}
tableTile.getTableIndexColumnCell().eq(1).click({shiftKey: true});
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 1);
}
tableTile.getTableIndexColumnCell().eq(2).click({shiftKey: true});
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 0);
}

// Clicking on bars should select the corresponding cases in the table
barGraph.getBar().eq(0).click();
for (const workspace of workspaces.slice(0, 2)) {
tableTile.getSelectedRow(workspace).should('have.length', 4); // All "X / Y" cases
}
barGraph.getBar().eq(1).click();
for (const workspace of workspaces.slice(0, 2)) {
tableTile.getSelectedRow(workspace).should('have.length', 1); // All "X / YY" cases
}
barGraph.getBar().eq(2).click();
for (const workspace of workspaces.slice(0, 2)) {
tableTile.getSelectedRow(workspace).should('have.length', 2); // All "XX / Y" cases
}

// Clicking bars in local read-only view also works and changes the main view, but not the remote view.
barGraph.getBar(workspaces[1]).eq(0).click();
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 1);
tableTile.getSelectedRow(workspace).should('have.length', 4); // All "X / Y" cases
}
barGraph.getBar(workspaces[1]).eq(1).click();
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 1);
tableTile.getSelectedRow(workspace).should('have.length', 1); // All "X / YY" cases
}
barGraph.getBar(workspaces[1]).eq(2).click();
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 1);
tableTile.getSelectedRow(workspace).should('have.length', 2); // All "XX / Y" cases
}
// Unselect the two remaining selected cases
tableTile.getTableIndexColumnCell().eq(1).click({shiftKey: true});
tableTile.getTableIndexColumnCell().eq(4).click({shiftKey: true});
tableTile.getSelectedRow(workspaces[0]).should('have.length', 0);

// Clicking bars in remote read-only view does not change the main view, but does change itself.
barGraph.getBarHighlight(workspaces[2]).should('have.length', 0);
tableTile.getSelectedRow(workspaces[2]).should('have.length', 0);
barGraph.getBar(workspaces[2]).eq(0).click();
barGraph.getBarHighlight(workspaces[2]).should('have.length', 1);
tableTile.getSelectedRow(workspaces[2]).should('have.length', 4); // All "X / Y" cases
for (const workspace of workspaces.slice(0, 2)) {
barGraph.getBarHighlight(workspace).should('have.length', 0);
tableTile.getSelectedRow(workspace).should('have.length', 0); // All "XX / Y" cases
}
});

});
4 changes: 4 additions & 0 deletions cypress/support/elements/tile/BarGraphTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class BarGraphTile {
return this.getChartArea(workspaceClass, tileIndex).find(`.visx-bar`);
}

getBarHighlight(workspaceClass, tileIndex = 0) {
return this.getChartArea(workspaceClass, tileIndex).find(`.bar-highlight`);
}

getLegendArea(workspaceClass, tileIndex = 0) {
return this.getTile(workspaceClass, tileIndex).find(`.bar-graph-legend`);
}
Expand Down
3 changes: 3 additions & 0 deletions cypress/support/elements/tile/TableToolTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class TableToolTile{
getTableRow(){
return cy.get('.canvas .rdg-row');
}
getSelectedRow(workspaceClass) {
return cy.get(`${wsclass(workspaceClass)} .canvas .rdg-row.highlighted`);
}
getColumnHeaderText(i){
return cy.get('.column-header-cell .editable-header-cell .header-name').text();
}
Expand Down
97 changes: 84 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,45 @@ 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("returns expected array when a case is selected with primary attribute", () => {
const content = TestingBarGraphContentModel.create({ });
content.setSharedModel(sharedSampleDataSet());
content.setPrimaryAttribute("att-s");
content.sharedModel?.dataSet.setSelectedCases([content.sharedModel?.dataSet.cases[0].__id__]);
expect(content.dataArray).toEqual([
{ "att-s": "cat", "value": { count: 2, selected: true }},
{ "att-s": "owl", "value": { count: 2, selected: false }}
]);
content.sharedModel?.dataSet.setSelectedCases([content.sharedModel?.dataSet.cases[2].__id__]);
expect(content.dataArray).toEqual([
{ "att-s": "cat", "value": { count: 2, selected: false }},
{ "att-s": "owl","value": { count: 2, selected: true }}
]);
content.sharedModel?.dataSet.selectAllCases();
expect(content.dataArray).toEqual([
{ "att-s": "cat", "value": { count: 2, selected: true }},
{ "att-s": "owl","value": { count: 2, selected: true }}
]);

});

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,9 +155,58 @@ 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 }}
]);
});

it("returns expected array when a case is selected with primary and secondary attributes", () => {
const content = TestingBarGraphContentModel.create({ });
content.setSharedModel(sharedSampleDataSet());
content.setPrimaryAttribute("att-s");
content.setSecondaryAttribute("att-l");
content.sharedModel?.dataSet.setSelectedCases([content.sharedModel?.dataSet.cases[0].__id__]);
expect(content.dataArray).toEqual([
{ "att-s": "cat", "yard": { count: 2, selected: true }},
{ "att-s": "owl", "yard": { count: 1, selected: false }, "forest": { count: 1, selected: false }}
]);
content.sharedModel?.dataSet.setSelectedCases([content.sharedModel?.dataSet.cases[3].__id__]);
expect(content.dataArray).toEqual([
{ "att-s": "cat", "yard": { count: 2, selected: false }},
{ "att-s": "owl", "yard": { count: 1, selected: false }, "forest": { count: 1, selected: true }}
]);
content.sharedModel?.dataSet.selectAllCases();
expect(content.dataArray).toEqual([
{ "att-s": "cat", "yard": { count: 2, selected: true }},
{ "att-s": "owl", "yard": { count: 1, selected: true }, "forest": { count: 1, selected: true }}
]);
});

it("selects cases based on primary and secondary attributes", () => {
const content = TestingBarGraphContentModel.create({ });
content.setSharedModel(sharedSampleDataSet());
const dataSet = content.sharedModel?.dataSet;
expect(dataSet).toBeDefined();
content.setPrimaryAttribute("att-s");
content.setSecondaryAttribute("att-l");

content.selectCasesByValues("cat", undefined);
expect(dataSet?.selectedCaseIds.map(c => dataSet?.caseIndexFromID(c))).toEqual([0, 1]);

content.selectCasesByValues("owl", undefined);
expect(dataSet?.selectedCaseIds.map(c => dataSet?.caseIndexFromID(c))).toEqual([2, 3]);

content.selectCasesByValues("cat", "yard");
expect(dataSet?.selectedCaseIds.map(c => dataSet?.caseIndexFromID(c))).toEqual([0, 1]);

content.selectCasesByValues("owl", "yard");
expect(dataSet?.selectedCaseIds.map(c => dataSet?.caseIndexFromID(c))).toEqual([2]);

content.selectCasesByValues("owl", "forest");
expect(dataSet?.selectedCaseIds.map(c => dataSet?.caseIndexFromID(c))).toEqual([3]);

content.selectCasesByValues("cat", "forest");
expect(dataSet?.selectedCaseIds.map(c => dataSet?.caseIndexFromID(c))).toEqual([]);
});

it("fills in missing values with (no value)", () => {
Expand All @@ -146,15 +217,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
Loading

0 comments on commit e31b61e

Please sign in to comment.