Skip to content

feat(#370): adds file and video upload#742

Merged
garethbowen merged 17 commits intomainfrom
370-file-upload
Mar 26, 2026
Merged

feat(#370): adds file and video upload#742
garethbowen merged 17 commits intomainfrom
370-file-upload

Conversation

@garethbowen
Copy link
Collaborator

@garethbowen garethbowen commented Mar 20, 2026

Closes #370 #702 #622 #703

I have verified this PR works in these browsers (latest versions):

  • Chrome
  • Firefox
  • Safari (macOS)
  • Safari (iOS)
  • Chrome for Android
  • Not applicable

What else has been done to verify that this works as intended?

Why is this the best possible solution? Were any other approaches considered?

I went away from the primevue fileupload control as there wasn't enough overlap between it and our designs, for example, we don't actually upload the file until submitting the form.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

Do we need any specific form for testing your changes? If so, please attach one.

The three forms in /packages/common/src/fixtures/upload/

What's changed

@changeset-bot
Copy link

changeset-bot bot commented Mar 20, 2026

🦋 Changeset detected

Latest commit: 665a139

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@getodk/xforms-engine Minor
@getodk/web-forms Minor
@getodk/common Minor
@getodk/xpath Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@garethbowen
Copy link
Collaborator Author

@latin-panda Can I get a review please? I'll tag QA and Aly to test.

@garethbowen garethbowen requested a review from latin-panda March 20, 2026 02:03
Copy link
Collaborator

@latin-panda latin-panda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I tested on the desktop and phone. I left a couple of comments below, but approving to unblock.

Remember to use the new CSS variables for spacing 🙂

Would you like to release after this is merged? I believe the Central minor release is coming up soon.

<template>
<div class="file-preview-content">
<span>{{ fileName }}</span>
<Button severity="secondary" @click="$emit('clear')">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

This button is difficult to locate because it's far from the file name. Could it be positioned closer or have a darker border?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA raised the same concern... I'll continue the discussion on Slack.

<!-- TODO: translations -->
<span>Choose file</span>
</Button>
<input
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there is no accept, the default on my Android phone is either camera or gallery. Is it possible that the default is "My files"? Collect opens "My files" and it is more convenient because I can search for any file easier.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell this isn't possible. Some people say it's a feature not a bug, because users should be able to choose. But it's not great in this case, because in the image upload we have a dedicated button for "take picture" so if they see this the user has already chosen to upload a file.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something that seems to work is defaulting the accept to `application/octet-stream', it opens "My Files" and I'm still able to choose video, images, audio, pdf, etc... It's opinionated, but the user experience is better, more useful.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find!

It seems like there are two places where this could be useful...

  1. When the form builder is using a generic file upload (not image, video, or audio) and hasn't specified any mimetype or accept parameter. In this case, I would argue it is a better UX to allow the user to choose if they want to capture the file, or select one from the phone. If you default it as you say then they would have to use the camera app first and then select from the file picker. Also, I'm not sure if that's behaviour is a bug or a feature, so I'm reluctant to rely on it.
  2. When the form builder is using an image control (or video and audio when capture for those is supported) and the user selects the "Choose image" button. In this case the user has already made the choice NOT to take a photo, so prompting them again is annoying. However in this case we actually do have an accept parameter, for example, image/*. If we replace this with application/octet-stream then we lose the automatic filtering to show only images in the file picker which can be very helpful.

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you default it as you say then they would have to use the camera app first and then select from the file picker. Also, I'm not sure if that's behaviour is a bug or a feature, so I'm reluctant to rely on it.

I'm confused by this piece. Are we perhaps seeing different behaviors in Android?

I tried accept="application/octet-stream" in the default file question type, when no mimetype and accept parameter where provided. Then, when tapping on the button to pick a file, it opens the file inspector (no camera, no gallery). From there, I can pick any file type, which seems correct since the Form Designer didn't specify any details about it.

I would use accept="application/octet-stream" only in that case where any file/binary data is accepted.

When the form builder is using an image control (or video and audio when capture for those is supported) and the user selects the "Choose image" button...

Yes, I wouldn't use application/octet-stream and would default to image/* (or video/*, audio/*, respectively) when the file types weren't provided by the Form Designer. The OS will decide how to handle that situation. However, for the 'take picture' button, the camera can be enforced using the capture attribute, which is supported by all mobile browsers. The difference between the two buttons is so small that it might make sense to just combine them and always use the camera with capture

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@latin-panda and I caught up in meet regarding this and as it mostly comes down to user experience decided to merge this as is and iterate based on user feedback.

@garethbowen garethbowen linked an issue Mar 24, 2026 that may be closed by this pull request
8 tasks
@garethbowen garethbowen merged commit 2eccd28 into main Mar 26, 2026
54 checks passed
@garethbowen garethbowen deleted the 370-file-upload branch March 26, 2026 04:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants