diff --git a/.ci/scripts/dart_unit_tests_pathified.sh b/.ci/scripts/dart_unit_tests_pathified.sh index 8a4bac50d3aac..e5b5c1feeb572 100755 --- a/.ci/scripts/dart_unit_tests_pathified.sh +++ b/.ci/scripts/dart_unit_tests_pathified.sh @@ -12,7 +12,7 @@ set -e # re-checked. dart ./script/tool/bin/flutter_plugin_tools.dart dart-test --run-on-dirty-packages \ --log-timing --exclude=script/configs/dart_unit_tests_exceptions.yaml \ - $PACKAGE_SHARDING + "$@" $PACKAGE_SHARDING # Restore the tree to a clean state, to avoid accidental issues if # other script steps are added to the enclosing task. git checkout . diff --git a/.ci/targets/dart_unit_tests.yaml b/.ci/targets/dart_unit_tests.yaml index e64bba8fa1d46..3d06b600fd559 100644 --- a/.ci/targets/dart_unit_tests.yaml +++ b/.ci/targets/dart_unit_tests.yaml @@ -9,3 +9,4 @@ tasks: # that depend on it. - name: Dart unit tests - pathified script: .ci/scripts/dart_unit_tests_pathified.sh + args: ["--platform=vm"] diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 073f1c20ed825..5bcbc2c16973b 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.9.2 + +* Adds and propagates `cancel` event on file selection. +* Changes `openFile` to return `null` when no files are selected/selection is canceled, + as in other platforms. + ## 0.9.1 * Adds `getSaveLocation` and deprecates `getSavePath`. diff --git a/packages/file_selector/file_selector_web/README.md b/packages/file_selector/file_selector_web/README.md index 1c152100c3502..3906b2f508251 100644 --- a/packages/file_selector/file_selector_web/README.md +++ b/packages/file_selector/file_selector_web/README.md @@ -13,3 +13,15 @@ should add it to your `pubspec.yaml` as usual. [1]: https://pub.dev/packages/file_selector [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin + +## Limitations on the Web platform + +### `cancel` event + +The `cancel` event used by the web plugin to detect when users close the file +selector without picking a file is relatively new, and will only work in +recent browsers. + +See: + +* https://caniuse.com/mdn-api_htmlinputelement_cancel_event diff --git a/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart index ee1af8cb62fd8..d6cb47fe45b63 100644 --- a/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart @@ -21,9 +21,17 @@ void main() { return dataTransfer.files as FileList?; } - void setFilesAndTriggerChange(List files) { + void setFilesAndTriggerEvent(List files, Event event) { input.files = createFileList(files); - input.dispatchEvent(Event('change')); + input.dispatchEvent(event); + } + + void setFilesAndTriggerChange(List files) { + setFilesAndTriggerEvent(files, Event('change')); + } + + void setFilesAndTriggerCancel(List files) { + setFilesAndTriggerEvent(files, Event('cancel')); } setUp(() { @@ -57,6 +65,18 @@ void main() { expect(await files[1].lastModified(), isNotNull); }); + testWidgets('"cancel" returns an empty selection', (_) async { + final Future> futureFiles = domHelper.getFiles( + input: input, + ); + + setFilesAndTriggerCancel([mockFile1, mockFile2]); + + final List files = await futureFiles; + + expect(files.length, 0); + }); + testWidgets('works multiple times', (_) async { Future> futureFiles; List files; diff --git a/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart index 664c40871f49c..f64c08de05649 100644 --- a/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart @@ -33,14 +33,30 @@ void main() { webWildCards: ['image/*'], ); - final XFile file = + final XFile? file = await plugin.openFile(acceptedTypeGroups: [typeGroup]); - expect(file.name, mockFile.name); + expect(file, isNotNull); + expect(file!.name, mockFile.name); expect(await file.length(), 4); expect(await file.readAsString(), '1001'); expect(await file.lastModified(), isNotNull); }); + + testWidgets('returns null when getFiles returns an empty list', + (WidgetTester _) async { + // Simulate returning an empty list of files from the DomHelper... + final MockDomHelper mockDomHelper = MockDomHelper( + files: [], + ); + + final FileSelectorWeb plugin = + FileSelectorWeb(domHelper: mockDomHelper); + + final XFile? file = await plugin.openFile(); + + expect(file, isNull); + }); }); group('openFiles', () { diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 2380e274c46a1..53e0c6d5ab33b 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -29,14 +29,14 @@ class FileSelectorWeb extends FileSelectorPlatform { } @override - Future openFile({ + Future openFile({ List? acceptedTypeGroups, String? initialDirectory, String? confirmButtonText, }) async { final List files = await _openFiles(acceptedTypeGroups: acceptedTypeGroups); - return files.first; + return files.isNotEmpty ? files.first : null; } @override diff --git a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart index 1c3442f8dab51..e600778b7dc73 100644 --- a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart +++ b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart @@ -52,6 +52,12 @@ class DomHelper { completer.completeError(platformException); }); + inputElement.addEventListener('cancel', (Event event) { + inputElement.remove(); + completer.complete([]); + }); + + // TODO(dit): Reimplement this with the showPicker() API, https://github.com/flutter/flutter/issues/130365 inputElement.click(); return completer.future; diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 81ba11a9634a9..a34e9c75548f7 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.1 +version: 0.9.2 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/file_selector/file_selector_web/test/utils_test.dart b/packages/file_selector/file_selector_web/test/utils_test.dart index e207f3d45df58..497d3ae48849d 100644 --- a/packages/file_selector/file_selector_web/test/utils_test.dart +++ b/packages/file_selector/file_selector_web/test/utils_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +@TestOn('chrome') // web-only package. + import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_web/src/utils.dart'; import 'package:flutter_test/flutter_test.dart';