diff --git a/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart index 0f383432bd..4902e77555 100644 --- a/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart @@ -66,7 +66,6 @@ class BoardCardBloc extends Bloc { state.cells.map((cell) => cell.identifier.fieldInfo).toList(), ), rowPB: state.rowPB, - visible: true, ); } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 816feb76de..0ec834c526 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -287,7 +287,6 @@ class _BoardContentState extends State { gridId: gridId, fields: UnmodifiableListView(fieldController.fieldInfos), rowPB: rowPB, - visible: true, ); final dataController = GridRowDataController( diff --git a/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart b/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart index 79634168c0..07e426b385 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart @@ -39,7 +39,6 @@ class GridRowCache { UnmodifiableListView get visibleRows { var visibleRows = [..._rowList.rows]; - visibleRows.retainWhere((element) => element.visible); return UnmodifiableListView(visibleRows); } @@ -236,7 +235,6 @@ class GridRowCache { gridId: gridId, fields: _fieldNotifier.fields, rowPB: rowPB, - visible: true, ); } } @@ -264,7 +262,6 @@ class RowInfo with _$RowInfo { required String gridId, required UnmodifiableListView fields, required RowPB rowPB, - required bool visible, }) = _RowInfo; } diff --git a/frontend/app_flowy/test/bloc_test/grid_test/select_option_bloc_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart similarity index 98% rename from frontend/app_flowy/test/bloc_test/grid_test/select_option_bloc_test.dart rename to frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart index fef80e2181..8ec4dd9159 100644 --- a/frontend/app_flowy/test/bloc_test/grid_test/select_option_bloc_test.dart +++ b/frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart @@ -6,7 +6,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:bloc_test/bloc_test.dart'; -import 'util.dart'; +import '../util.dart'; void main() { late AppFlowyGridCellTest cellTest; @@ -19,9 +19,8 @@ void main() { setUp(() async { await cellTest.createTestGrid(); await cellTest.createTestRow(); - cellController = await cellTest.makeCellController( - FieldType.SingleSelect, - ); + cellController = + await cellTest.makeCellController(FieldType.SingleSelect, 0); }); blocTest( diff --git a/frontend/app_flowy/test/bloc_test/grid_test/edit_field_edit_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart similarity index 99% rename from frontend/app_flowy/test/bloc_test/grid_test/edit_field_edit_test.dart rename to frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart index 5a8173b7bd..33b15372a2 100644 --- a/frontend/app_flowy/test/bloc_test/grid_test/edit_field_edit_test.dart +++ b/frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart @@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/prelude.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'util.dart'; +import '../util.dart'; Future createEditorBloc(AppFlowyGridTest gridTest) async { final context = await gridTest.createTestGrid(); diff --git a/frontend/app_flowy/test/bloc_test/grid_test/create_filter_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart similarity index 93% rename from frontend/app_flowy/test/bloc_test/grid_test/create_filter_test.dart rename to frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart index 374c60faac..db9dfdccab 100644 --- a/frontend/app_flowy/test/bloc_test/grid_test/create_filter_test.dart +++ b/frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart @@ -4,7 +4,8 @@ import 'package:app_flowy/plugins/grid/application/grid_data_controller.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbenum.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'util.dart'; + +import '../util.dart'; void main() { late AppFlowyGridTest gridTest; @@ -16,11 +17,12 @@ void main() { final context = await gridTest.createTestGrid(); final service = FilterFFIService(viewId: context.gridView.id); final textField = context.textFieldContext(); - service.insertTextFilter( + await service.insertTextFilter( fieldId: textField.id, condition: TextFilterCondition.TextIsEmpty, content: ""); await gridResponseFuture(); + assert(context.fieldController.filterInfos.length == 1); }); @@ -28,14 +30,14 @@ void main() { final context = await gridTest.createTestGrid(); final service = FilterFFIService(viewId: context.gridView.id); final textField = context.textFieldContext(); - service.insertTextFilter( + await service.insertTextFilter( fieldId: textField.id, condition: TextFilterCondition.TextIsEmpty, content: ""); await gridResponseFuture(); final filterInfo = context.fieldController.filterInfos.first; - service.deleteFilter( + await service.deleteFilter( fieldId: textField.id, filterId: filterInfo.filter.id, fieldType: textField.fieldType, @@ -77,13 +79,13 @@ void main() { await gridResponseFuture(); final textField = context.textFieldContext(); - service.insertTextFilter( + await service.insertTextFilter( fieldId: textField.id, condition: TextFilterCondition.TextIsEmpty, content: ""); await gridResponseFuture(); - final controller = await context.makeTextCellController(); + final controller = await context.makeTextCellController(0); controller.saveCellData("edit text cell content"); await gridResponseFuture(); assert(gridBloc.state.rowInfos.length == 2); @@ -98,7 +100,7 @@ void main() { final service = FilterFFIService(viewId: context.gridView.id); final textField = context.textFieldContext(); await gridResponseFuture(); - service.insertTextFilter( + await service.insertTextFilter( fieldId: textField.id, condition: TextFilterCondition.TextIsNotEmpty, content: ""); @@ -117,7 +119,7 @@ void main() { )..add(const GridEvent.initial()); await gridResponseFuture(); - service.insertCheckboxFilter( + await service.insertCheckboxFilter( fieldId: checkboxField.id, condition: CheckboxFilterCondition.IsUnChecked, ); @@ -136,7 +138,7 @@ void main() { )..add(const GridEvent.initial()); await gridResponseFuture(); - service.insertCheckboxFilter( + await service.insertCheckboxFilter( fieldId: checkboxField.id, condition: CheckboxFilterCondition.IsChecked, ); diff --git a/frontend/app_flowy/test/bloc_test/grid_test/edit_field_change_filter_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart similarity index 97% rename from frontend/app_flowy/test/bloc_test/grid_test/edit_field_change_filter_test.dart rename to frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart index 1a8c3edf06..d1e6263b24 100644 --- a/frontend/app_flowy/test/bloc_test/grid_test/edit_field_change_filter_test.dart +++ b/frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart @@ -5,7 +5,8 @@ import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'util.dart'; + +import '../util.dart'; void main() { late AppFlowyGridTest gridTest; @@ -25,7 +26,7 @@ void main() { )..add(const GridFilterMenuEvent.initial()); // Insert filter for the text field - service.insertTextFilter( + await service.insertTextFilter( fieldId: textField.id, condition: TextFilterCondition.TextIsEmpty, content: ""); diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart new file mode 100644 index 0000000000..604b09d93d --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart @@ -0,0 +1,61 @@ +import 'package:app_flowy/plugins/grid/application/filter/filter_menu_bloc.dart'; +import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../util.dart'; + +void main() { + late AppFlowyGridTest gridTest; + setUpAll(() async { + gridTest = await AppFlowyGridTest.ensureInitialized(); + }); + + test('test filter menu after create a text filter)', () async { + final context = await gridTest.createTestGrid(); + final menuBloc = GridFilterMenuBloc( + viewId: context.gridView.id, fieldController: context.fieldController) + ..add(const GridFilterMenuEvent.initial()); + await gridResponseFuture(); + assert(menuBloc.state.creatableFields.length == 1); + + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + assert(menuBloc.state.creatableFields.isEmpty); + }); + + test('test filter menu after update existing text filter)', () async { + final context = await gridTest.createTestGrid(); + final menuBloc = GridFilterMenuBloc( + viewId: context.gridView.id, fieldController: context.fieldController) + ..add(const GridFilterMenuEvent.initial()); + await gridResponseFuture(); + + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + + // Create filter + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + + final textFilter = context.fieldController.filterInfos.first; + // Update the existing filter + await service.insertTextFilter( + fieldId: textField.id, + filterId: textFilter.filter.id, + condition: TextFilterCondition.Is, + content: "ABC"); + await gridResponseFuture(); + assert(menuBloc.state.filters.first.textFilter()!.condition == + TextFilterCondition.Is); + assert(menuBloc.state.filters.first.textFilter()!.content == "ABC"); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_test.dart new file mode 100644 index 0000000000..4644c76ac3 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_test.dart @@ -0,0 +1,99 @@ +import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../util.dart'; +import 'filter_util.dart'; + +void main() { + late AppFlowyGridTest gridTest; + setUpAll(() async { + gridTest = await AppFlowyGridTest.ensureInitialized(); + }); + + test('filter rows by text is empty or is not empty condition)', () async { + final context = await createTestFilterGrid(gridTest); + + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + // create a new filter + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + assert(context.fieldController.filterInfos.length == 1, + "expect 1 but receive ${context.fieldController.filterInfos.length}"); + assert(context.rowInfos.length == 1, + "expect 1 but receive ${context.rowInfos.length}"); + + // Update the existing filter + final textFilter = context.fieldController.filterInfos.first; + await service.insertTextFilter( + fieldId: textField.id, + filterId: textFilter.filter.id, + condition: TextFilterCondition.TextIsNotEmpty, + content: ""); + await gridResponseFuture(); + assert(context.rowInfos.length == 2); + + // delete the filter + await service.deleteFilter( + fieldId: textField.id, + filterId: textFilter.filter.id, + fieldType: textField.fieldType, + ); + await gridResponseFuture(); + assert(context.rowInfos.length == 3); + }); + + test('filter rows by text is condition)', () async { + final context = await createTestFilterGrid(gridTest); + + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + // create a new filter + await service.insertTextFilter( + fieldId: textField.id, condition: TextFilterCondition.Is, content: "A"); + await gridResponseFuture(); + assert(context.rowInfos.length == 1, + "expect 1 but receive ${context.rowInfos.length}"); + + // Update the existing filter's content from 'A' to 'B' + final textFilter = context.fieldController.filterInfos.first; + await service.insertTextFilter( + fieldId: textField.id, + filterId: textFilter.filter.id, + condition: TextFilterCondition.Is, + content: "B"); + await gridResponseFuture(); + assert(context.rowInfos.length == 1); + + // Update the existing filter's content from 'B' to 'b' + await service.insertTextFilter( + fieldId: textField.id, + filterId: textFilter.filter.id, + condition: TextFilterCondition.Is, + content: "b"); + await gridResponseFuture(); + assert(context.rowInfos.length == 1); + + // Update the existing filter with content 'C' + await service.insertTextFilter( + fieldId: textField.id, + filterId: textFilter.filter.id, + condition: TextFilterCondition.Is, + content: "C"); + await gridResponseFuture(); + assert(context.rowInfos.isEmpty); + + // delete the filter + await service.deleteFilter( + fieldId: textField.id, + filterId: textFilter.filter.id, + fieldType: textField.fieldType, + ); + await gridResponseFuture(); + assert(context.rowInfos.length == 3); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_util.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_util.dart new file mode 100644 index 0000000000..0a07e25309 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_util.dart @@ -0,0 +1,42 @@ +import 'package:app_flowy/plugins/grid/application/grid_data_controller.dart'; +import 'package:app_flowy/plugins/grid/grid.dart'; +import 'package:app_flowy/workspace/application/app/app_service.dart'; + +import '../util.dart'; + +Future createTestFilterGrid(AppFlowyGridTest gridTest) async { + final app = await gridTest.unitTest.createTestApp(); + final builder = GridPluginBuilder(); + final context = await AppService() + .createView( + appId: app.id, + name: "Filter Grid", + dataFormatType: builder.dataFormatType, + pluginType: builder.pluginType, + layoutType: builder.layoutType!, + ) + .then((result) { + return result.fold( + (view) async { + final context = GridTestContext(view, GridController(view: view)); + final result = await context.gridController.openGrid(); + + await editCells(context); + await gridResponseFuture(milliseconds: 500); + result.fold((l) => null, (r) => throw Exception(r)); + return context; + }, + (error) => throw Exception(), + ); + }); + + return context; +} + +Future editCells(GridTestContext context) async { + final controller0 = await context.makeTextCellController(0); + final controller1 = await context.makeTextCellController(1); + + controller0.saveCellData('A'); + controller1.saveCellData('B'); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/util.dart b/frontend/app_flowy/test/bloc_test/grid_test/util.dart index 8e9dd16fec..e361a8f39b 100644 --- a/frontend/app_flowy/test/bloc_test/grid_test/util.dart +++ b/frontend/app_flowy/test/bloc_test/grid_test/util.dart @@ -18,26 +18,26 @@ import '../../util.dart'; class GridTestContext { final ViewPB gridView; - final GridController _gridController; + final GridController gridController; - GridTestContext(this.gridView, this._gridController); + GridTestContext(this.gridView, this.gridController); List get rowInfos { - return _gridController.rowInfos; + return gridController.rowInfos; } UnmodifiableMapView get blocks { - return _gridController.blocks; + return gridController.blocks; } List get fieldContexts => fieldController.fieldInfos; GridFieldController get fieldController { - return _gridController.fieldController; + return gridController.fieldController; } Future createRow() async { - return _gridController.createRow(); + return gridController.createRow(); } FieldEditorBloc createFieldEditor({ @@ -60,18 +60,20 @@ class GridTestContext { return editorBloc; } - Future makeCellController(String fieldId) async { - final builder = await makeCellControllerBuilder(fieldId); + Future makeCellController( + String fieldId, int rowIndex) async { + final builder = await makeCellControllerBuilder(fieldId, rowIndex); return builder.build(); } Future makeCellControllerBuilder( String fieldId, + int rowIndex, ) async { - final RowInfo rowInfo = rowInfos.last; + final RowInfo rowInfo = rowInfos[rowIndex]; final blockCache = blocks[rowInfo.rowPB.blockId]; final rowCache = blockCache?.rowCache; - final fieldController = _gridController.fieldController; + final fieldController = gridController.fieldController; final rowDataController = GridRowDataController( rowInfo: rowInfo, @@ -125,22 +127,22 @@ class GridTestContext { } Future makeSelectOptionCellController( - FieldType fieldType) async { + FieldType fieldType, int rowIndex) async { assert(fieldType == FieldType.SingleSelect || fieldType == FieldType.MultiSelect); final field = fieldContexts.firstWhere((element) => element.fieldType == fieldType); - final cellController = - await makeCellController(field.id) as GridSelectOptionCellController; + final cellController = await makeCellController(field.id, rowIndex) + as GridSelectOptionCellController; return cellController; } - Future makeTextCellController() async { + Future makeTextCellController(int rowIndex) async { final field = fieldContexts .firstWhere((element) => element.fieldType == FieldType.RichText); final cellController = - await makeCellController(field.id) as GridCellController; + await makeCellController(field.id, rowIndex) as GridCellController; return cellController; } } @@ -171,7 +173,7 @@ class AppFlowyGridTest { return result.fold( (view) async { final context = GridTestContext(view, GridController(view: view)); - final result = await context._gridController.openGrid(); + final result = await context.gridController.openGrid(); result.fold((l) => null, (r) => throw Exception(r)); return context; }, @@ -205,12 +207,12 @@ class AppFlowyGridCellTest { } Future makeCellController( - FieldType fieldType) async { - return context.makeSelectOptionCellController(fieldType); + FieldType fieldType, int rowIndex) async { + return context.makeSelectOptionCellController(fieldType, rowIndex); } } -Future gridResponseFuture({int milliseconds = 500}) { +Future gridResponseFuture({int milliseconds = 200}) { return Future.delayed(gridResponseDuration(milliseconds: milliseconds)); }