Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bar graph link #2398

Merged
merged 24 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f36fa61
Remove inadvertent dependency on dataflow
bgoldowsky Sep 5, 2024
c242d59
Toolbar, basic linking
bgoldowsky Sep 5, 2024
0f64186
Basic legend implementation. Link/unlink handling.
bgoldowsky Sep 6, 2024
1469f42
Sort by menu
bgoldowsky Sep 6, 2024
e566501
Reorganize, legend fixes
bgoldowsky Sep 9, 2024
3a3f789
Design fidelity; tests
bgoldowsky Sep 9, 2024
0f64c3e
Extend Cypress tests, style improvements.
bgoldowsky Sep 9, 2024
2465800
Handle missing data
bgoldowsky Sep 9, 2024
edcf1f0
More consistent naming
bgoldowsky Sep 9, 2024
40f2198
Typo
bgoldowsky Sep 9, 2024
4620d40
Test for (no value). Update documentation
bgoldowsky Sep 9, 2024
91a6e2d
Merge remote-tracking branch 'origin/master' into 188127118-bar-graph…
bgoldowsky Sep 9, 2024
6865b74
Adjust tile size when legend changes.
bgoldowsky Sep 10, 2024
d8acf33
Add test for vertical layout
bgoldowsky Sep 11, 2024
3dc7595
Track shared model ID in persistent state to avoid sync issues.
bgoldowsky Sep 12, 2024
c3f02fc
Update Jest tests
bgoldowsky Sep 12, 2024
5f09a10
Read only & sync & test improvements.
bgoldowsky Sep 13, 2024
9ec6bc0
Logging and logging tests
bgoldowsky Sep 13, 2024
9c22dc6
Manage deletion of attributes
bgoldowsky Sep 13, 2024
858954b
Fix table test
bgoldowsky Sep 13, 2024
1813675
Merge pull request #2399 from concord-consortium/185294648-bar-graph-…
bgoldowsky Sep 16, 2024
f32d814
More tests
bgoldowsky Sep 16, 2024
cb197c0
Merge pull request #2404 from concord-consortium/188129121-bar-graph-…
bgoldowsky Sep 17, 2024
de397eb
Merge remote-tracking branch 'origin/master' into 188127118-bar-graph…
bgoldowsky Sep 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 108 additions & 18 deletions cypress/e2e/functional/tile_tests/bar_graph_tile_spec.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import ClueCanvas from '../../../support/elements/common/cCanvas';
import Canvas from '../../../support/elements/common/Canvas';
import BarGraphTile from '../../../support/elements/tile/BarGraphTile';

import TableToolTile
from '../../../support/elements/tile/TableToolTile';
let clueCanvas = new ClueCanvas,
barGraph = new BarGraphTile;
barGraph = new BarGraphTile,
tableTile = new TableToolTile;

// eslint-disable-next-line unused-imports/no-unused-vars
const canvas = new Canvas;

function textMatchesList(selector, expected) {
selector.should('have.length', expected.length);
selector.each(($el, index) => {
cy.wrap($el).invoke('text').then(text => cy.wrap(text).should('eq', expected[index]));
});
}

function beforeTest() {
const queryParams = `${Cypress.config("qaUnitStudent5")}`;
cy.clearQAData('all');
cy.visit(queryParams);
cy.waitForLoad();
cy.showOnlyDocumentWorkspace();
const url = "/editor/?appMode=qa&unit=./demo/units/qa/content.json";
cy.visit(url);
}

context('Bar Graph Tile', function () {

it('Can create tile', function () {
it('Basic tile operations', function () {
beforeTest();

clueCanvas.addTile('bargraph');
Expand All @@ -30,27 +36,111 @@ context('Bar Graph Tile', function () {

barGraph.getTileTitle().should("be.visible").and('have.text', 'Bar Graph 1');
barGraph.getYAxisLabel().should('have.text', 'Counts');
barGraph.getXAxisPulldownButton(0).should('have.text', 'date');
});
barGraph.getXAxisPulldownButton(0).should('have.text', 'Categories');

it('Can edit Y axis label', function () {
beforeTest();
clueCanvas.addTile('bargraph');
barGraph.getYAxisLabel().should('have.text', 'Counts');
cy.log('Change Y axis label');
barGraph.getYAxisLabelEditor().should('not.exist');
barGraph.getYAxisLabelButton().click();
barGraph.getYAxisLabelEditor().should('be.visible').type(' of something{enter}');
barGraph.getYAxisLabelEditor().should('not.exist');
barGraph.getYAxisLabel().should('have.text', 'Counts of something');

cy.log('Duplicate tile');
clueCanvas.getDuplicateTool().click();
barGraph.getTiles().should('have.length', 2);
barGraph.getTile(0)
.should('be.visible')
.and('have.class', 'bar-graph-tile')
.and('not.have.class', 'read-only');
barGraph.getTileTitle(0).should("be.visible").and('have.text', 'Bar Graph 1');
barGraph.getYAxisLabel(0).should('have.text', 'Counts of something');
barGraph.getXAxisPulldownButton(0).should('have.text', 'Categories');

barGraph.getTile(1)
.should('be.visible')
.and('have.class', 'bar-graph-tile')
.and('not.have.class', 'read-only');
barGraph.getTileTitle(1).should("be.visible").and('have.text', 'Bar Graph 2');
barGraph.getYAxisLabel(1).should('have.text', 'Counts of something');
barGraph.getXAxisPulldownButton(1).should('have.text', 'Categories');

cy.log('Delete tile');
clueCanvas.deleteTile('bargraph');
clueCanvas.deleteTile('bargraph');
barGraph.getTiles().should('have.length', 0);
});

it('Can change primary category', function () {
it('Can link data ', function () {
beforeTest();

// 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'],
]);

clueCanvas.addTile('bargraph');
barGraph.getXAxisPulldown().should('have.text', 'date');
barGraph.getTiles().click();
barGraph.getYAxisLabel().should('have.text', 'Counts');
barGraph.getXAxisPulldown().should('have.text', 'Categories');
barGraph.getYAxisTickLabel().should('not.exist');
barGraph.getXAxisTickLabel().should('not.exist');
barGraph.getLegendArea().should('not.exist');
barGraph.getBar().should('not.exist');

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

barGraph.getXAxisPulldown().should('have.text', 'x');

textMatchesList(barGraph.getXAxisTickLabel(), ['X', 'XX']);
textMatchesList(barGraph.getYAxisTickLabel(), ['0', '1', '2', '3', '4', '5']);
barGraph.getBar().should('have.length', 2);
barGraph.getDatasetLabel().should('have.text', 'Table Data 1');
barGraph.getSortByMenuButton().should('have.text', 'None');
barGraph.getSecondaryValueName().should('have.length', 1).and('have.text', 'x');

cy.log('Change Sort By');
barGraph.getSortByMenuButton().click();
barGraph.getChakraMenuItem().should('have.length', 3);
barGraph.getChakraMenuItem().eq(1).should('have.text', 'y').click();
textMatchesList(barGraph.getXAxisTickLabel(), ['X', 'XX']);
textMatchesList(barGraph.getYAxisTickLabel(), ['0', '1', '2', '3', '4', '5']);
barGraph.getBar().should('have.length', 3);
barGraph.getDatasetLabel().should('have.text', 'Table Data 1');
barGraph.getSortByMenuButton().should('have.text', 'y');
textMatchesList(barGraph.getSecondaryValueName(), ['Y', 'YY']);

cy.log('Change Category');
barGraph.getXAxisPulldownButton().click();
barGraph.getXAxisPulldownMenuItem().eq(1).click();
barGraph.getXAxisPulldown().should('have.text', 'location');
barGraph.getChakraMenuItem().should('have.length', 3);
barGraph.getChakraMenuItem().eq(1).should('have.text', 'y').click();
barGraph.getXAxisPulldown().should('have.text', 'y');
textMatchesList(barGraph.getXAxisTickLabel(), ['Y', 'YY']);
textMatchesList(barGraph.getYAxisTickLabel(), ['0', '2', '4', '6', '8', '10']); // there are 6 Ys in this view so scale expands.
barGraph.getBar().should('have.length', 2);
barGraph.getDatasetLabel().should('have.text', 'Table Data 1');
barGraph.getSortByMenuButton().should('have.text', 'None');
barGraph.getSecondaryValueName().should('have.length', 1).and('have.text', 'y');

cy.log('Unlink data');
barGraph.getDatasetUnlinkButton().click();
barGraph.getXAxisPulldown().should('have.text', 'Categories');
barGraph.getYAxisTickLabel().should('not.exist');
barGraph.getXAxisTickLabel().should('not.exist');
barGraph.getLegendArea().should('not.exist');
barGraph.getBar().should('not.exist');
});

});
30 changes: 6 additions & 24 deletions cypress/e2e/functional/tile_tests/xy_plot_tool_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,6 @@ const queryParamsPlotVariables = `${Cypress.config("qaNoGroupShareUnitStudent5")

const problemDoc = '1.1 Unit Toolbar Configuration';

// Construct and fill in a table tile with the given data (a list of lists)
function buildTable(data) {
// at least two cols, or as many as the longest row in the data array
const cols = Math.max(2, ...data.map(row => row.length));
clueCanvas.addTile('table');
tableToolTile.getTableTile().last().should('be.visible');
tableToolTile.getTableTile().last().within((tile) => {
// tile will start with two columns; make more if desired
for (let i=2; i<cols; i++) {
tile.getAddColumnButton().click();
}
for (let i=0; i<data.length; i++) {
for (let j=0; j<data[i].length; j++) {
const cellContent = data[i][j];
tableToolTile.typeInTableCellXY(i, j, cellContent);
tableToolTile.getTableCellXY(i, j).should('contain', cellContent);
}
}
});
}

// Parse `transform` attributes (used for point positioning)
function xAttributeOfTransform(matcher) {
return attributeOfTransform(matcher, 1);
Expand Down Expand Up @@ -257,7 +236,8 @@ context('XYPlot Tool Tile', function () {
beforeTest(queryParamsMultiDataset);
cy.collapseResourceTabs();

buildTable([[1, 2], [2, 4], [3, 9], [4, 16]]);
clueCanvas.addTile("table");
tableToolTile.fillTable(tableToolTile.getTableTile(), [[1, 2], [2, 4], [3, 9], [4, 16]]);

clueCanvas.addTile("graph");
xyTile.getTile().should("have.length", 1).should('be.visible');
Expand Down Expand Up @@ -433,8 +413,10 @@ context('XYPlot Tool Tile', function () {
clueCanvas.addTile("graph");
xyTile.getTile().should('be.visible');

buildTable([[1, 2], [2, 4], [3, 9]]);
buildTable([[1, 1], [2, 5], [3, 1], [4, 5]]);
clueCanvas.addTile("table");
tableToolTile.fillTable(tableToolTile.getTableTile().last(), [[1, 2], [2, 4], [3, 9]]);
clueCanvas.addTile("table");
tableToolTile.fillTable(tableToolTile.getTableTile().last(), [[1, 1], [2, 5], [3, 1], [4, 5]]);

tableToolTile.getTableTile().should('have.length', 2);

Expand Down
5 changes: 5 additions & 0 deletions cypress/support/elements/common/cCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ExpressionToolTile from '../tile/ExpressionToolTile';
import Canvas from './Canvas';
import Dialog from './Dialog';
import XYPlotToolTile from '../tile/XYPlotToolTile';
import BarGraphTile from '../tile/BarGraphTile';

let graphToolTile = new GeometryToolTile,
imageToolTile = new ImageToolTile,
Expand All @@ -23,6 +24,7 @@ let graphToolTile = new GeometryToolTile,
numberlineToolTile = new NumberlineToolTile,
expressionToolTile = new ExpressionToolTile,
xyPlotToolTile = new XYPlotToolTile,
barGraphTile = new BarGraphTile,
canvas = new Canvas,
dialog = new Dialog;

Expand Down Expand Up @@ -295,6 +297,9 @@ class ClueCanvas {
case 'xyplot':
tileElement = xyPlotToolTile.getTile().last().click({ force: true });
break;
case 'bargraph':
tileElement = barGraphTile.getTile().last().click({ force: true });
break;
}
tileElement.should('have.class','selected');
}
Expand Down
48 changes: 42 additions & 6 deletions cypress/support/elements/tile/BarGraphTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,64 @@ class BarGraphTile {
return this.getTile(tileIndex, workspaceClass).find(`.editable-tile-title-text`);
}

getChakraMenuItem(tileIndex = 0, workspaceClass) {
return cy.get(`body .chakra-portal button`).filter(':visible');
}

getChartArea(tileIndex = 0, workspaceClass) {
return this.getTile(tileIndex, workspaceClass).find(`svg.bar-graph-svg`);
}

getYAxisLabel(tileIndex = 0, workspaceClass) {
return this.getTile(tileIndex, workspaceClass).find(`.editable-axis-label`);
return this.getChartArea(tileIndex, workspaceClass).find(`.editable-axis-label`);
}

getYAxisLabelButton(tileIndex = 0, workspaceClass) {
return this.getTile(tileIndex, workspaceClass).find(`[data-testid="axis-label-button"]`);
return this.getChartArea(tileIndex, workspaceClass).find(`[data-testid="axis-label-button"]`);
}

getYAxisLabelEditor(tileIndex = 0, workspaceClass) {
return this.getTile(tileIndex, workspaceClass).find(`[data-testid="axis-label-editor"] input`);
return this.getChartArea(tileIndex, workspaceClass).find(`[data-testid="axis-label-editor"] input`);
}

getXAxisPulldown(tileIndex = 0, workspaceClass) {
return this.getTile(tileIndex, workspaceClass).find(`[data-testid="category-pulldown"]`);
return this.getChartArea(tileIndex, workspaceClass).find(`[data-testid="category-pulldown"]`);
}

getXAxisPulldownButton(tileIndex = 0, workspaceClass) {
return this.getXAxisPulldown(tileIndex, workspaceClass).find(`button`);
}

getXAxisPulldownMenuItem(tileIndex = 0, workspaceClass) {
return cy.get(`body .chakra-portal button`).filter(':visible');
getYAxisTickLabel(tileIndex = 0, workspaceClass) {
return this.getChartArea(tileIndex, workspaceClass).find(`.visx-axis-left text`);
}

getXAxisTickLabel(tileIndex = 0, workspaceClass) {
return this.getChartArea(tileIndex, workspaceClass).find(`.visx-axis-bottom text`);
}

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

getLegendArea(tileIndex = 0, workspaceClass) {
return this.getTile(tileIndex, workspaceClass).find(`.bar-graph-legend`);
}

getDatasetLabel(tileIndex = 0, workspaceClass) {
return this.getLegendArea(tileIndex, workspaceClass).find(`.dataset-header .dataset-name`);
}

getDatasetUnlinkButton(tileIndex = 0, workspaceClass) {
return this.getLegendArea(tileIndex, workspaceClass).find(`.dataset-header .dataset-icon a`);
}

getSortByMenuButton(tileIndex = 0, workspaceClass) {
return this.getLegendArea(tileIndex, workspaceClass).find(`.sort-by button.chakra-menu__menu-button`);
}

getSecondaryValueName(tileIndex = 0, workspaceClass) {
return this.getLegendArea(tileIndex, workspaceClass).find(`.secondary-values .secondary-value-name`);
}

}
Expand Down
20 changes: 20 additions & 0 deletions cypress/support/elements/tile/TableToolTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,26 @@ class TableToolTile{
getTableIndexColumnCell(){
return cy.get('.canvas-area .rdg-cell.index-column');
}
// Fill in a table tile with the given data (a list of lists)
// Table tile should in the default state (2 columns, no rows)
fillTable($tile, data) {
// at least two cols, or as many as the longest row in the data array
const cols = Math.max(2, ...data.map(row => row.length));
$tile.within((tile) => {
// tile will start with two columns; make more if desired
for (let i=2; i<cols; i++) {
this.getAddColumnButton().click();
}
for (let i=0; i<data.length; i++) {
for (let j=0; j<data[i].length; j++) {
const cellContent = data[i][j];
this.typeInTableCellXY(i, j, cellContent);
this.getTableCellXY(i, j).should('contain', cellContent);
}
}
});
}

getLinkGraphButton(){
return cy.get('.link-tile-button');
}
Expand Down
6 changes: 6 additions & 0 deletions docs/unit-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ Under 'dataset', there is one option:

- `cellsSelectCases`: boolean

#### Bar Graph

Common toolbar framework. One default button:

- `link-tile`: bring up the linking dialog to connect/disconnect a dataset

#### Data Deck

Not updated to common toolbar framework. However, supports toolbar configuration in a similar manner. Default buttons:
Expand Down
5 changes: 5 additions & 0 deletions src/clue/app-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@
"placeholderText": "",
"stamps": [],
"settings": {
"bargraph": {
"tools": [
"link-tile"
]
},
"dataset": {
"cellsSelectCases": false
},
Expand Down
Loading
Loading