Skip to content

Commit 1570ff0

Browse files
committed
Implement row setting
Signed-off-by: martinRenou <[email protected]>
1 parent b694767 commit 1570ff0

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

ipydatagrid/datagrid.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,10 @@ def get_cell_value(self, column_name, primary_key_value):
516516
517517
Tuples should be used to index into multi-index columns."""
518518
row_indices = self._get_row_index_of_primary_key(primary_key_value)
519+
520+
if isinstance(column_name, list):
521+
column_name = tuple(column_name)
522+
519523
return [self._data["data"][row][column_name] for row in row_indices]
520524

521525
def set_cell_value(self, column_name, primary_key_value, new_value):
@@ -529,6 +533,9 @@ def set_cell_value(self, column_name, primary_key_value, new_value):
529533
if not row_indices:
530534
return False
531535

536+
if isinstance(column_name, list):
537+
column_name = tuple(column_name)
538+
532539
# Iterate over all indices
533540
outcome = True
534541
for row_index in row_indices:
@@ -540,6 +547,32 @@ def set_cell_value(self, column_name, primary_key_value, new_value):
540547
outcome = False
541548
return outcome
542549

550+
def set_row_value(self, primary_key_value, new_value):
551+
"""Sets the value for a row by and primary key.
552+
553+
Note: This method returns a boolean to indicate if the operation
554+
was successful.
555+
"""
556+
row_indices = self._get_row_index_of_primary_key(primary_key_value)
557+
# Bail early if key could not be found
558+
if not row_indices:
559+
return False
560+
561+
# Iterate over all indices
562+
for row_index in row_indices:
563+
column_index = 0
564+
column = DataGrid._column_index_to_name(self._data, column_index)
565+
while column is not None:
566+
self._data["data"][row_index][column] = new_value[column_index]
567+
568+
column_index = column_index + 1
569+
column = DataGrid._column_index_to_name(
570+
self._data, column_index
571+
)
572+
573+
self._notify_row_change(row_index, new_value)
574+
return True
575+
543576
def get_cell_value_by_index(self, column_name, row_index):
544577
"""Gets the value for a single cell by column name and row index."""
545578
return self._data["data"][row_index][column_name]
@@ -582,6 +615,19 @@ def _notify_cell_change(self, row, column, value):
582615
}
583616
)
584617

618+
def _notify_row_change(self, row, value):
619+
# notify front-end
620+
self.comm.send(
621+
data={
622+
"method": "custom",
623+
"content": {
624+
"event_type": "row-changed",
625+
"row": row,
626+
"value": value,
627+
},
628+
}
629+
)
630+
585631
def get_visible_data(self):
586632
"""Returns a dataframe of the current View."""
587633
data = deepcopy(self._data)

js/core/viewbasedjsonmodel.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,34 @@ export class ViewBasedJSONModel extends MutableDataModel {
307307
return true;
308308
}
309309

310+
/**
311+
* Updates the row value of the currently displayed View.
312+
*
313+
* @param row - The row index of the cell of interest.
314+
*
315+
* @param value - The new value to update the indicated row with.
316+
*
317+
*/
318+
setRowData(
319+
row: number,
320+
value: any,
321+
): boolean {
322+
this.updateRowValue({
323+
row: row,
324+
value: value,
325+
});
326+
this.emitChanged({
327+
type: 'cells-changed',
328+
region: 'body',
329+
row,
330+
column: 0,
331+
rowSpan: 1,
332+
columnSpan: 1,
333+
});
334+
335+
return true;
336+
}
337+
310338
public columnNameToIndex(name: string): number {
311339
const schema = this.dataset.schema;
312340
const primaryKeysLength = schema.primaryKey.length - 1;
@@ -550,6 +578,36 @@ export class ViewBasedJSONModel extends MutableDataModel {
550578
this.currentView = this._transformState.createView(this._dataset);
551579
}
552580

581+
/**
582+
* Updates a row in the full dataset of the model.
583+
*
584+
* @param options - The options for this function.
585+
*/
586+
updateRowValue(options: ViewBasedJSONModel.IUpdateRowValuesOptions): void {
587+
// Create new row and add it to new dataset
588+
const newRow = { ...this._dataset.data[options.row] };
589+
for (const columnIndex of Array(5).keys()) {
590+
newRow[this.metadata('body', 0, columnIndex)['name']] = options.value[columnIndex];
591+
}
592+
const newData = Array.from(this._dataset.data);
593+
newData[options.row] = newRow;
594+
595+
this._dataset = {
596+
data: newData,
597+
schema: this._dataset.schema,
598+
};
599+
600+
if (options.syncData) {
601+
this.dataSync.emit({
602+
type: 'cell-updated',
603+
});
604+
}
605+
606+
// We need to rerun the transforms, as the changed cells may change the order
607+
// or visibility of other rows
608+
this.currentView = this._transformState.createView(this._dataset);
609+
}
610+
553611
/**
554612
* A signal emitted when the data model has changes to sync to the kernel.
555613
*/
@@ -704,6 +762,23 @@ export namespace ViewBasedJSONModel {
704762
syncData?: boolean;
705763
}
706764

765+
export interface IUpdateRowValuesOptions {
766+
/**
767+
* The index of the target row in the current view.
768+
*/
769+
row: number;
770+
771+
/**
772+
* The new value to replace the old one.
773+
*/
774+
value: any[];
775+
776+
/**
777+
* The flag to trigger full data sync with backend.
778+
*/
779+
syncData?: boolean;
780+
}
781+
707782
/**
708783
* A type alias for a data source for a JSON data model.
709784
*

js/datagrid.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ export class DataGridModel extends DOMWidgetModel {
9393
content.value,
9494
);
9595
}
96+
97+
if (content.event_type === 'row-changed') {
98+
this.data_model.setRowData(
99+
content.row,
100+
content.value,
101+
);
102+
}
96103
});
97104
}
98105

tests/js/datagrid.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,33 @@ describe('Test trait: data', () => {
9797
});
9898
});
9999

100+
test('Backend driven row update propagates properly', async () => {
101+
const testData = Private.createBasicTestData();
102+
const grid = await Private.createGridWidget({ data: testData.set1 });
103+
const row = 1;
104+
const value = [1.23];
105+
grid.model.set('_data', testData.set2);
106+
107+
return new Promise<void>((resolve, reject) => {
108+
grid.model.data_model.changed.connect(
109+
(model: ViewBasedJSONModel, args: any) => {
110+
if (args.type === 'cells-changed') {
111+
const updatedValue = [model.data(args.region, args.row, 0)];
112+
expect(args.row).toBe(row);
113+
expect(updatedValue).toStrictEqual(value);
114+
resolve();
115+
}
116+
},
117+
);
118+
119+
emulateCustomCommMessage(grid.model, 'iopub', {
120+
event_type: 'row-changed',
121+
row: row,
122+
value: value,
123+
});
124+
});
125+
});
126+
100127
test('Selection model updated on trait update', async () => {
101128
const testData = Private.createBasicTestData();
102129
const grid = await Private.createGridWidget({

0 commit comments

Comments
 (0)