diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_action_sheet_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_action_sheet_bloc.dart index 52b3d3368e..fc2cfb1098 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_action_sheet_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_action_sheet_bloc.dart @@ -11,9 +11,16 @@ class FieldActionSheetBloc extends Bloc { final FieldService fieldService; - FieldActionSheetBloc({required FieldPB field, required this.fieldService}) - : super(FieldActionSheetState.initial( - FieldTypeOptionDataPB.create()..field_2 = field)) { + FieldActionSheetBloc({required GridFieldCellContext fieldCellContext}) + : fieldService = FieldService( + gridId: fieldCellContext.gridId, + fieldId: fieldCellContext.field.id, + ), + super( + FieldActionSheetState.initial( + FieldTypeOptionDataPB.create()..field_2 = fieldCellContext.field, + ), + ) { on( (event, emit) async { await event.map( @@ -31,6 +38,13 @@ class FieldActionSheetBloc (err) => Log.error(err), ); }, + showField: (_ShowField value) async { + final result = await fieldService.updateField(visibility: true); + result.fold( + (l) => null, + (err) => Log.error(err), + ); + }, deleteField: (_DeleteField value) async { final result = await fieldService.deleteField(); result.fold( @@ -62,6 +76,7 @@ class FieldActionSheetEvent with _$FieldActionSheetEvent { const factory FieldActionSheetEvent.updateFieldName(String name) = _UpdateFieldName; const factory FieldActionSheetEvent.hideField() = _HideField; + const factory FieldActionSheetEvent.showField() = _ShowField; const factory FieldActionSheetEvent.duplicateField() = _DuplicateField; const factory FieldActionSheetEvent.deleteField() = _DeleteField; const factory FieldActionSheetEvent.saveField() = _SaveField; diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart index e8622629b7..73127ed6df 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart @@ -16,8 +16,7 @@ class _GridFieldNotifier extends ChangeNotifier { List _fieldContexts = []; set fieldContexts(List fieldContexts) { - _fieldContexts = - fieldContexts.where((element) => element.visibility).toList(); + _fieldContexts = fieldContexts; notifyListeners(); } diff --git a/frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart index c6c9b1fa5b..5f777eedf7 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart @@ -23,7 +23,13 @@ class GridHeaderBloc extends Bloc { _startListening(); }, didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) { - emit(state.copyWith(fields: value.fields)); + emit( + state.copyWith( + fields: value.fields + .where((element) => element.visibility) + .toList(), + ), + ); }, moveField: (_MoveField value) async { await _moveField(value, emit); diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index bd33115cde..e34ebc7205 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -158,10 +158,7 @@ void _resolveGridDeps(GetIt getIt) { ); getIt.registerFactoryParam( - (data, _) => FieldActionSheetBloc( - field: data.field, - fieldService: FieldService(gridId: data.gridId, fieldId: data.field.id), - ), + (data, _) => FieldActionSheetBloc(fieldCellContext: data), ); getIt.registerFactoryParam( diff --git a/frontend/app_flowy/test/bloc_test/grid_test/grid_header_bloc_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/grid_header_bloc_test.dart new file mode 100644 index 0000000000..a52f98e516 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/grid_header_bloc_test.dart @@ -0,0 +1,125 @@ +import 'package:app_flowy/plugins/grid/application/field/field_action_sheet_bloc.dart'; +import 'package:app_flowy/plugins/grid/application/grid_header_bloc.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'util.dart'; + +void main() { + late AppFlowyGridTest gridTest; + + setUpAll(() async { + gridTest = await AppFlowyGridTest.ensureInitialized(); + }); + + group('GridHeaderBloc', () { + late FieldActionSheetBloc actionSheetBloc; + setUp(() async { + await gridTest.createTestGrid(); + actionSheetBloc = FieldActionSheetBloc( + fieldCellContext: gridTest.singleSelectFieldCellContext(), + ); + }); + + blocTest( + "hides property", + build: () { + final bloc = GridHeaderBloc( + gridId: gridTest.gridView.id, + fieldController: gridTest.fieldController, + )..add(const GridHeaderEvent.initial()); + return bloc; + }, + act: (bloc) async { + actionSheetBloc.add(const FieldActionSheetEvent.hideField()); + await Future.delayed(gridResponseDuration()); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.fields.length == 2); + }, + ); + + blocTest( + "shows property", + build: () { + final bloc = GridHeaderBloc( + gridId: gridTest.gridView.id, + fieldController: gridTest.fieldController, + )..add(const GridHeaderEvent.initial()); + return bloc; + }, + act: (bloc) async { + actionSheetBloc.add(const FieldActionSheetEvent.hideField()); + await Future.delayed(gridResponseDuration()); + actionSheetBloc.add(const FieldActionSheetEvent.showField()); + await Future.delayed(gridResponseDuration()); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.fields.length == 3); + }, + ); + + blocTest( + "duplicate property", + build: () { + final bloc = GridHeaderBloc( + gridId: gridTest.gridView.id, + fieldController: gridTest.fieldController, + )..add(const GridHeaderEvent.initial()); + return bloc; + }, + act: (bloc) async { + actionSheetBloc.add(const FieldActionSheetEvent.duplicateField()); + await Future.delayed(gridResponseDuration()); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.fields.length == 4); + }, + ); + + blocTest( + "delete property", + build: () { + final bloc = GridHeaderBloc( + gridId: gridTest.gridView.id, + fieldController: gridTest.fieldController, + )..add(const GridHeaderEvent.initial()); + return bloc; + }, + act: (bloc) async { + actionSheetBloc.add(const FieldActionSheetEvent.deleteField()); + await Future.delayed(gridResponseDuration()); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.fields.length == 2); + }, + ); + + blocTest( + "update name", + build: () { + final bloc = GridHeaderBloc( + gridId: gridTest.gridView.id, + fieldController: gridTest.fieldController, + )..add(const GridHeaderEvent.initial()); + return bloc; + }, + act: (bloc) async { + actionSheetBloc + .add(const FieldActionSheetEvent.updateFieldName("Hello world")); + await Future.delayed(gridResponseDuration()); + }, + wait: gridResponseDuration(), + verify: (bloc) { + final field = bloc.state.fields.firstWhere( + (element) => element.id == actionSheetBloc.fieldService.fieldId); + + assert(field.name == "Hello world"); + }, + ); + }); +} 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 0f97fb5ba7..1bf412e29e 100644 --- a/frontend/app_flowy/test/bloc_test/grid_test/util.dart +++ b/frontend/app_flowy/test/bloc_test/grid_test/util.dart @@ -1,4 +1,8 @@ +import 'dart:collection'; +import 'package:app_flowy/plugins/grid/application/block/block_cache.dart'; import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; +import 'package:app_flowy/plugins/grid/application/field/field_controller.dart'; +import 'package:app_flowy/plugins/grid/application/field/field_service.dart'; import 'package:app_flowy/plugins/grid/application/grid_data_controller.dart'; import 'package:app_flowy/plugins/grid/application/row/row_bloc.dart'; import 'package:app_flowy/plugins/grid/application/row/row_cache.dart'; @@ -12,9 +16,10 @@ import '../../util.dart'; /// Create a empty Grid for test class AppFlowyGridTest { - // ignore: unused_field final AppFlowyUnitTest _inner; late ViewPB gridView; + late GridDataController _dataController; + AppFlowyGridTest(AppFlowyUnitTest unitTest) : _inner = unitTest; static Future ensureInitialized() async { @@ -22,6 +27,31 @@ class AppFlowyGridTest { return AppFlowyGridTest(inner); } + List get rowInfos => _dataController.rowInfos; + + UnmodifiableMapView get blocks => + _dataController.blocks; + + List get fieldContexts => + _dataController.fieldController.fieldContexts; + + GridFieldController get fieldController => _dataController.fieldController; + + Future createRow() async { + await _dataController.createRow(); + } + + GridFieldContext singleSelectFieldContext() { + final fieldContext = fieldContexts + .firstWhere((element) => element.fieldType == FieldType.SingleSelect); + return fieldContext; + } + + GridFieldCellContext singleSelectFieldCellContext() { + final field = singleSelectFieldContext().field; + return GridFieldCellContext(gridId: gridView.id, field: field); + } + Future createTestGrid() async { final app = await _inner.createTestApp(); final builder = GridPluginBuilder(); @@ -32,13 +62,63 @@ class AppFlowyGridTest { pluginType: builder.pluginType, layoutType: builder.layoutType!, ); - result.fold( - (view) => gridView = view, + await result.fold( + (view) async { + gridView = view; + _dataController = GridDataController(view: view); + final result = await _dataController.loadData(); + result.fold((l) => null, (r) => throw Exception(r)); + }, (error) {}, ); } } +/// Create a new Grid for cell test +class AppFlowyGridCellTest { + final AppFlowyGridTest _gridTest; + AppFlowyGridCellTest(AppFlowyGridTest gridTest) : _gridTest = gridTest; + + static Future ensureInitialized() async { + final gridTest = await AppFlowyGridTest.ensureInitialized(); + return AppFlowyGridCellTest(gridTest); + } + + Future createTestRow() async { + await _gridTest.createRow(); + } + + Future createTestGrid() async { + await _gridTest.createTestGrid(); + } + + Future cellControllerBuilder( + String fieldId, + ) async { + final RowInfo rowInfo = _gridTest.rowInfos.last; + final blockCache = _gridTest.blocks[rowInfo.rowPB.blockId]; + final rowCache = blockCache?.rowCache; + + final rowDataController = GridRowDataController( + rowInfo: rowInfo, + fieldController: _gridTest._dataController.fieldController, + rowCache: rowCache!, + ); + + final rowBloc = RowBloc( + rowInfo: rowInfo, + dataController: rowDataController, + )..add(const RowEvent.initial()); + await gridResponseFuture(); + + return GridCellControllerBuilder( + cellId: rowBloc.state.gridCellMap[fieldId]!, + cellCache: rowCache.cellCache, + delegate: rowDataController, + ); + } +} + class AppFlowyGridSelectOptionCellTest { final AppFlowyGridCellTest _gridCellTest; @@ -63,8 +143,7 @@ class AppFlowyGridSelectOptionCellTest { assert(fieldType == FieldType.SingleSelect || fieldType == FieldType.MultiSelect); - final fieldContexts = - _gridCellTest._dataController.fieldController.fieldContexts; + final fieldContexts = _gridCellTest._gridTest.fieldContexts; final field = fieldContexts.firstWhere((element) => element.fieldType == fieldType); final builder = await _gridCellTest.cellControllerBuilder(field.id); @@ -73,55 +152,6 @@ class AppFlowyGridSelectOptionCellTest { } } -/// Create a new Grid for cell test -class AppFlowyGridCellTest { - final AppFlowyGridTest _gridTest; - late GridDataController _dataController; - AppFlowyGridCellTest(AppFlowyGridTest gridTest) : _gridTest = gridTest; - - static Future ensureInitialized() async { - final gridTest = await AppFlowyGridTest.ensureInitialized(); - return AppFlowyGridCellTest(gridTest); - } - - Future createTestRow() async { - await _dataController.createRow(); - } - - Future createTestGrid() async { - await _gridTest.createTestGrid(); - _dataController = GridDataController(view: _gridTest.gridView); - final result = await _dataController.loadData(); - result.fold((l) => null, (r) => throw Exception(r)); - } - - Future cellControllerBuilder( - String fieldId, - ) async { - final RowInfo rowInfo = _dataController.rowInfos.last; - final blockCache = _dataController.blocks[rowInfo.rowPB.blockId]; - final rowCache = blockCache?.rowCache; - - final rowDataController = GridRowDataController( - rowInfo: rowInfo, - fieldController: _dataController.fieldController, - rowCache: rowCache!, - ); - - final rowBloc = RowBloc( - rowInfo: rowInfo, - dataController: rowDataController, - )..add(const RowEvent.initial()); - await gridResponseFuture(); - - return GridCellControllerBuilder( - cellId: rowBloc.state.gridCellMap[fieldId]!, - cellCache: rowCache.cellCache, - delegate: rowDataController, - ); - } -} - Future gridResponseFuture() { return Future.delayed(gridResponseDuration(milliseconds: 200)); }