diff --git a/js/components/FileChooser.tsx b/js/components/FileChooser.tsx
index 88492cf..46d947d 100644
--- a/js/components/FileChooser.tsx
+++ b/js/components/FileChooser.tsx
@@ -60,17 +60,24 @@ const InputFileChooser = ({
//
// which allows pretty generous interpretations.
//
- // Update(whscullin) - Adding the MIME type seems to block loading anything
- // from iCloud when using Safari, so reverting to simply using extensions for
- // now.
- const newAccept = [];
+ // However, it turns out that Safari does not like MIME types when
+ // reading from iCloud. It may be related to:
+ //
+ // https://bugs.webkit.org/show_bug.cgi?id=244666
+ //
+ // or it could be related to some permissions issue with iCloud. For
+ // the moment, not adding the MIME type is sufficient.
+ const newAccept: string[] = [];
for (const type of accept) {
- for (const [/* typeString */, suffixes] of Object.entries(type.accept)) {
+ for (let [/* typeString */, suffixes] of Object.entries(type.accept)) {
// newAccept.push(typeString);
- if (Array.isArray(suffixes)) {
- newAccept.push(...suffixes);
- } else {
- newAccept.push(suffixes);
+ if (!Array.isArray(suffixes)) {
+ suffixes = [suffixes];
+ }
+ for (const suffix of suffixes) {
+ if (!newAccept.includes(suffix)) {
+ newAccept.push(suffix);
+ }
}
}
}
@@ -157,6 +164,10 @@ const FilePickerChooser = ({
* Using `window.showOpenFilePicker` has the advantage of allowing read/write
* access to the file, whereas the regular input element only gives read
* access.
+ *
+ * The FileChooser takes an optional `accept` parameter that specifies which
+ * types of files can be opened. The parameter is a map of MIME type to file
+ * extension. If the MIME type is the empty string, t
*/
export const FileChooser = ({
onChange,
diff --git a/test/components/FileChooser.spec.tsx b/test/components/FileChooser.spec.tsx
index 99d7c76..1f137a9 100644
--- a/test/components/FileChooser.spec.tsx
+++ b/test/components/FileChooser.spec.tsx
@@ -22,6 +22,17 @@ const FAKE_FILE_HANDLE = {
isDirectory: false,
} as const;
+const TEST_FILE_TYPES = [
+ {
+ description: 'Test description 1',
+ accept: { 'mime1': ['.ext1', '.ext2'] }
+ },
+ {
+ description: 'Test description 2',
+ accept: { 'mime2': ['.ext1', '.ext2'] }
+ }];
+
+
const NOP = () => { /* do nothing */ };
// eslint-disable-next-line no-undef
@@ -44,6 +55,13 @@ describe('FileChooser', () => {
expect(inputElement.type).toBe('file');
});
+ it('should pass the correct MIME types and file extensions to the picker', async () => {
+ const onChange = jest.fn();
+ render();
+ const inputElement = await screen.findByRole('button') as HTMLInputElement;
+ expect(inputElement.accept).toBe('.ext1,.ext2');
+ });
+
it('should fire a callback with empty list when no files are selected', async () => {
const onChange = jest.fn();
render();
@@ -97,6 +115,23 @@ describe('FileChooser', () => {
expect(container).not.toBeNull();
});
+ it('should pass the correct MIME types and file extensions to the picker', async () => {
+ mockFilePicker.mockResolvedValueOnce([]);
+ const onChange = jest.fn();
+ render();
+
+ fireEvent.click(await screen.findByText('Choose File'));
+
+ await waitFor(() => {
+ expect(mockFilePicker).toBeCalledWith<[OpenFilePickerOptions]>({
+ 'excludeAcceptAllOption': true,
+ 'multiple': false,
+ 'types': TEST_FILE_TYPES
+ });
+ expect(onChange).toBeCalledWith([]);
+ });
+ });
+
it('should fire a callback with empty list when no files are selected', async () => {
mockFilePicker.mockResolvedValueOnce([]);
const onChange = jest.fn();