Skip to content
This repository was archived by the owner on Aug 7, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions packages/oui-datagrid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,38 @@
Clicked row action 1: <span ng-if="$ctrl.action1Row">{{$ctrl.action1Row.lastName}}, {{$ctrl.action1Row.firstName}}</span>
```

### Selectable rows

```html:preview
<oui-datagrid rows="$ctrl.data" page-size="5" selectable-rows>
<oui-column title="'First name'" property="firstName" sortable="asc"></oui-column>
<oui-column title="'Last name'" property="lastName" sortable></oui-column>
<oui-column title="'Mother'" property="parents.mother.lastName" sortable>
{{$row.parents.mother.lastName}}, {{$row.parents.mother.firstName}}
</oui-column>
<oui-column title="'Father'" property="parents.father.lastName" sortable>
{{$row.parents.father.lastName}}, {{$row.parents.father.firstName}}
</oui-column>
<oui-column title="'Email'" property="email" sortable>
<a href="mailto:{{$value}}">{{$ctrl.label}}: {{$value}}</a>
</oui-column>
<oui-column title="'Phone'" property="phone"></oui-column>
<oui-column title="'Birth'" property="birth" sortable>
{{$value|date:short}}
</oui-column>
<oui-column title="'Selected'">
<span>{{ $isSelected }}</span>
</oui-column>
<oui-action-menu align="end" compact>
<oui-action-menu-item text="Some action" disabled="$isSelected" on-click="">
</oui-action-menu-item>
</oui-action-menu>
<extra-top>
<pre>You have selected {{ $selectedRows.length }} row(s).</pre>
</extra-top>
</oui-datagrid>
```

### Empty datagrid

```html:preview
Expand Down Expand Up @@ -656,6 +688,7 @@ call `rows-loader` and then a `row-loader` call for each line.
| `rows-loader` | function | &? | yes | | | gets all rows (returns a promise with all rows) |
| `row-loader` | function | &? | yes | | | gets row details (returns a promise with details) |
| `customizable` | boolean | <? | | | false | if the datagrid is customizable |
| `selectable-rows` | boolean | <? | | | false | if rows can be selected
| `columns-parameters` | array | <? | | | undefined | columns parameters (see below) |
| `on-columns-parameters-change` | function | & | | | | triggered on column parameter change when datagrid is customizable |

Expand Down
3 changes: 2 additions & 1 deletion packages/oui-datagrid/src/cell/cell.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default {
},
bindings: {
row: "<",
column: "<"
column: "<",
index: "<?"
}
};
8 changes: 8 additions & 0 deletions packages/oui-datagrid/src/cell/cell.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export default class {
this.$element = $element;
}

$onInit () {
this.index = this.index || 0;
}

$postLink () {
// The parent scope of datagrid is required to get parent
// values inside cells
Expand All @@ -19,6 +23,10 @@ export default class {
} else {
this._compileCell();
}

this.cellScope.$watch(() => this.datagridCtrl.selectedRows[this.index], (isSelected) => {
this.cellScope.$isSelected = isSelected || false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.cellScope.$isSelected = !!isSelected is possible no ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!! is harder to read by someone not used to this js "trick" but yes it would fit the job

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok :)

});
}

$onChanges (changes) {
Expand Down
40 changes: 39 additions & 1 deletion packages/oui-datagrid/src/datagrid.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export default class DatagridController {
this.columnElements = [];
this.actionColumnElements = [];
this.extraTopElements = [];
this.selectedRows = [];
this.selectAllRows = false;

this.config = ouiDatagridConfiguration;

Expand Down Expand Up @@ -69,6 +71,8 @@ export default class DatagridController {
this.filterableColumns = [];
this.criteria = [];

addBooleanParameter(this, "selectableRows");

if (this.id) {
this.ouiDatagridService.registerDatagrid(this);
}
Expand Down Expand Up @@ -103,7 +107,7 @@ export default class DatagridController {
}

// Manage responsiveness
if (this.hasActionMenu || this.customizable) {
if (this.hasActionMenu || this.customizable || this.selectableRows) {
this.scrollablePanel = this.$element[0].querySelector(".oui-datagrid-responsive-container__overflow-container");
if (this.scrollablePanel) {
angular.element(this.$window).on("resize", this.checkScroll);
Expand Down Expand Up @@ -271,6 +275,9 @@ export default class DatagridController {
this.displayedRows = DatagridController.createEmptyRows(this.paging.getCurrentPageSize());
}

this.selectedRows = this.selectedRows.map(() => false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this contain the actual rows instead of an array of booleans ? Or something to identify the rows ?

{
	id: rowId,
	isSelected: false
}

If we keep the array of booleans system, then it should be names something like rowSelectionStatus

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since every checkbox needs to have an ng-model i still have to created N booleans for binding ; that's why it made sense to use an array of booleans (i reuse the array)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could react to the checkbox's on-change event and add the corresponding row to the array of rows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think your suggestion would make the code "faster" : no reduce in getSelectedRows since you access array directly ; downside is that you need an additionnal array to store rows which is less clean (you still have to give an ngModel to checkbox so you also need an array of booleans).
did i understand you correcly ?

this.selectAllRows = false;

this.refreshDatagridPromise = this.$q.when((callback || angular.noop)())
.then(() => this.paging.loadData(skipSortAndFilter, forceLoadRows))
.then(result => {
Expand Down Expand Up @@ -313,6 +320,37 @@ export default class DatagridController {
};
}

getSelectedRows () {
return this.selectedRows.reduce((result, isSelected, index) => {
if (isSelected) {
result.push(this.displayedRows[index]);
}
return result;
}, []);
}

toggleRowSelection (index, isSelected) {
const rowCount = this.displayedRows.length;
this.selectedRows[index] = isSelected;
const selectedRowsCount = this.getSelectedRows().length;

if (selectedRowsCount === rowCount) {
this.selectAllRows = true;
} else if (selectedRowsCount === 0) {
this.selectAllRows = false;
} else {
this.selectAllRows = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the global comportement for tri-select box ? null is some items are selected, some are not? Because, if it's not already the selected behavior, this would require a something else than a boolean then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep checkbox component currently use "null"

}
}

toggleAllRowsSelection (modelValue) {
if (modelValue === null) {
this.selectedRows = this.displayedRows.map(() => true);
} else {
this.selectedRows = this.displayedRows.map(() => modelValue);
}
}

static createEmptyRows (pageSize) {
return Array(...{ length: pageSize })
.map(() => undefined);
Expand Down
28 changes: 22 additions & 6 deletions packages/oui-datagrid/src/datagrid.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,25 @@
items="$ctrl.appliedCriteria"></oui-chips>
</oui-criteria-container>
<div class="oui-datagrid-responsive-container"
ng-class="::{
'oui-datagrid-responsive-container_sticky-actions': $ctrl.hasActionMenu || $ctrl.customizable
ng-class="{
'oui-datagrid-responsive-container_sticky-actions': $ctrl.hasActionMenu || $ctrl.customizable,
'oui-datagrid-responsive-container_sticky-checkbox': $ctrl.selectableRows
}">
<div class="oui-datagrid-responsive-container__sticky-container"
ng-class="{
'oui-datagrid-responsive-container__sticky-container_scroll-begin': $ctrl.scrollBegin && ($ctrl.hasActionMenu || $ctrl.customizable),
'oui-datagrid-responsive-container__sticky-container_scroll-end': $ctrl.scrollEnd && $ctrl.hasCheckboxes
'oui-datagrid-responsive-container__sticky-container_scroll-end': $ctrl.scrollEnd && $ctrl.selectableRows
}">
<div class="oui-datagrid-responsive-container__overflow-container">
<table class="oui-datagrid oui-datagrid_responsive">
<thead class="oui-datagrid__headers">
<tr class="oui-datagrid__row">
<th class="oui-datagrid__header oui-datagrid__cell-sticky"
ng-if="$ctrl.selectableRows">
<oui-checkbox model="$ctrl.selectAllRows"
on-change="$ctrl.toggleAllRowsSelection(modelValue)">
</oui-checkbox>
</th>
<th class="oui-datagrid__header"
ng-class="$ctrl.getSortableClasses(column)"
ng-repeat="column in $ctrl.columns track by $index"
Expand All @@ -47,7 +54,14 @@
<tbody class="oui-datagrid__body"
ng-hide="$ctrl.loading">
<tr class="oui-datagrid__row oui-action-menu-container"
ng-repeat="row in $ctrl.displayedRows track by $index">
ng-repeat="row in $ctrl.displayedRows track by $index"
ng-init="rowIndex = $index">
<td class="oui-datagrid__cell oui-datagrid__cell-sticky"
ng-if="$ctrl.selectableRows">
<oui-checkbox model="$ctrl.selectedRows[$index]"
on-change="$ctrl.toggleRowSelection($index, modelValue)">
</oui-checkbox>
</td>
<td class="oui-datagrid__cell"
ng-class="::[column.class]"
ng-repeat="column in $ctrl.columns track by $index"
Expand All @@ -57,14 +71,16 @@
</span>
<oui-datagrid-cell ng-if="row && (!column.name || $ctrl.hasProperty(row, column.name) && column.template)"
row="row"
column="column">
column="column"
index="rowIndex">
</oui-datagrid-cell>
</td>
<td ng-if="$ctrl.hasActionMenu || $ctrl.customizable"
class="oui-datagrid__cell oui-datagrid__cell-sticky">
<oui-datagrid-cell ng-if="row && $ctrl.hasActionMenu"
row="row"
column="$ctrl.actionColumn">
column="$ctrl.actionColumn"
index="rowIndex">
</oui-datagrid-cell>
</td>
</tr>
Expand Down
3 changes: 3 additions & 0 deletions packages/oui-datagrid/src/extra-top/extra-top.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ export default {
controller,
require: {
datagridCtrl: "^^ouiDatagrid"
},
bindings: {
selectedItems: "<"
}
};
4 changes: 4 additions & 0 deletions packages/oui-datagrid/src/extra-top/extra-top.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export default class {

$postLink () {
this.extraTopScope = this.datagridCtrl.getParentScope().$new(false);
this.extraTopScope.$selectedRows = [];
this.extraTopScope.$watchCollection(() => this.datagridCtrl.getSelectedRows(), (rows) => {
this.extraTopScope.$selectedRows = rows || [];
});
this._compileElement();
}

Expand Down
88 changes: 88 additions & 0 deletions packages/oui-datagrid/src/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,94 @@ describe("ouiDatagrid", () => {
});
});

describe("Selectable rows", () => {

it("should toggle row selection", () => {
const element = TestUtils.compileTemplate(`
<oui-datagrid rows="$ctrl.rows">
<oui-column property="firstName"></oui-column>
<oui-column property="lastName"></oui-column>
</oui-datagrid>`, {
rows: fakeData.slice(0, 5)
});
const ctrl = element.controller("ouiDatagrid");

let selection = [];
ctrl.toggleRowSelection(0, true);
selection = ctrl.getSelectedRows();
expect(selection.length).toBe(1);
expect(selection[0]).toEqual(fakeData[0]);

ctrl.toggleRowSelection(0, false);
selection = ctrl.getSelectedRows();
expect(selection.length).toBe(0);
});

it("should toggle all rows", () => {
const element = TestUtils.compileTemplate(`
<oui-datagrid rows="$ctrl.rows">
<oui-column property="firstName"></oui-column>
<oui-column property="lastName"></oui-column>
</oui-datagrid>`, {
rows: fakeData.slice(0, 5)
});
const ctrl = element.controller("ouiDatagrid");

let selection = [];
expect(ctrl.selectAllRows).toBe(false);
ctrl.toggleAllRowsSelection(true);
selection = ctrl.getSelectedRows();
expect(selection.length).toBe(5);

ctrl.toggleAllRowsSelection(false);
selection = ctrl.getSelectedRows();
expect(selection.length).toBe(0);
});

it("should update global selection checkbox", () => {
const element = TestUtils.compileTemplate(`
<oui-datagrid rows="$ctrl.rows">
<oui-column property="firstName"></oui-column>
<oui-column property="lastName"></oui-column>
</oui-datagrid>`, {
rows: fakeData.slice(0, 5)
});
const ctrl = element.controller("ouiDatagrid");

expect(ctrl.selectAllRows).toBe(false);
ctrl.toggleRowSelection(0, true);
expect(ctrl.selectAllRows).toBe(null);
ctrl.toggleRowSelection(1, true);
ctrl.toggleRowSelection(2, true);
ctrl.toggleRowSelection(3, true);
expect(ctrl.selectAllRows).toBe(null);
ctrl.toggleRowSelection(4, true);
expect(ctrl.selectAllRows).toBe(true);
ctrl.toggleRowSelection(4, false);
expect(ctrl.selectAllRows).toBe(null);
ctrl.toggleRowSelection(0, false);
ctrl.toggleRowSelection(1, false);
ctrl.toggleRowSelection(2, false);
ctrl.toggleRowSelection(3, false);
expect(ctrl.selectAllRows).toBe(false);
});

it("should updates extra-top content", () => {
const element = TestUtils.compileTemplate(`
<oui-datagrid rows="$ctrl.rows">
<oui-column property="firstName"></oui-column>
<oui-column property="lastName"></oui-column>
<extra-top>{{ $selectedRows.length }}</extra-top>
</oui-datagrid>`, {
rows: fakeData.slice(0, 5)
});
const ctrl = element.controller("ouiDatagrid");
ctrl.toggleAllRowsSelection(true);
element.scope().$apply();
expect(element.find("oui-datagrid-extra-top").text()).toBe("5");
});
});

describe("Remote rows", () => {
let rowsLoaderSpy;

Expand Down