From 8c0537adb697e553ec2fa6cd0b2298af8ee6dfc2 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Mon, 16 Oct 2023 17:47:05 +0200 Subject: [PATCH] :sparkles: [#2] Define type for file component, pt. 1 Mostly describes the base schema and how a single file upload looks like, i.e. what data is chucked over the wire when submitting the form. --- src/formio/components/file.ts | 97 +++++++++++++++++++++++++ src/formio/components/index.ts | 1 + src/formio/index.ts | 2 + test-d/formio/components/file.test-d.ts | 66 +++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 src/formio/components/file.ts create mode 100644 test-d/formio/components/file.test-d.ts diff --git a/src/formio/components/file.ts b/src/formio/components/file.ts new file mode 100644 index 0000000..aea4d84 --- /dev/null +++ b/src/formio/components/file.ts @@ -0,0 +1,97 @@ +import {DisplayConfig, HasValidation, OFExtensions, StrictComponentSchema} from '../base'; + +type UnusedFileProperties = 'hideLabel' | 'placeholder' | 'disabled' | 'widget' | 'validate'; + +type Validator = 'required'; +type TranslatableKeys = 'label' | 'description' | 'tooltip'; + +/** + * Shape of a single file upload from Form.io to the backend. + */ +export interface FileUploadData { + data: { + /** + * Full backend URL of the uploaded file (API endpoint). + * + * The value appears to be identical to the root `url` key. + */ + url: string; + /** + * Does not seems to be set to a meaningful value. + */ + form: ''; + /** + * File name of uploaded file. + * + * The value is different from the root `name` key. + */ + name: string; + /** + * File size in bytes, (positive) integer value. + * + * The value appears to be identical to the root `size` key. + */ + size: number; + /** + * Formio base URL configuration option, set to the root of our own API. + */ + baseUrl: string; + /** + * Does not seems to be set to a meaningful value. + */ + project: ''; + }; + name: string; + originalName: string; + /** + * File size in bytes, (positive) integer value. + */ + size: number; + /** + * We only support file uploads to a backend URL. + */ + storage: 'url'; + /** + * MIME type, determined by the browser during upload. If the OS/browser doesn't know + * it, it seems to be an empty string (see https://github.com/open-formulieren/open-forms-sdk/ + * blob/27877938249cdd627294871b70291ab8dc66fd61/src/formio/components/FileField.js#L279) + */ + type: string; + /** + * Full backend URL of the uploaded file (API endpoint). + */ + url: string; +} + +/** + * @group Form.io components + * @category Base types + */ +export interface BaseFileComponentSchema + extends Omit, UnusedFileProperties | 'errors'>, + DisplayConfig, + OFExtensions, + HasValidation { + type: 'file'; + multiple?: boolean; +} + +export type SingleFileComponentSchema = BaseFileComponentSchema & { + multiple?: false; + defaultValue?: [] | [FileUploadData]; +}; + +export type MultipleFileComponentSchema = BaseFileComponentSchema & { + multiple: true; + defaultValue?: FileUploadData[]; +}; + +/** + * @group Form.io components + * @category Concrete types + * + * Note that while `defaultValue` is defined here, this is only to be able to type + * define the submission data type. A file upload component cannot actually have + * default values. + */ +export type FileComponentSchema = SingleFileComponentSchema | MultipleFileComponentSchema; diff --git a/src/formio/components/index.ts b/src/formio/components/index.ts index 605f02a..54aa360 100644 --- a/src/formio/components/index.ts +++ b/src/formio/components/index.ts @@ -7,6 +7,7 @@ export * from './time'; export * from './phonenumber'; export * from './postcode'; export * from './number'; +export * from './file'; // Layout components export * from './content'; diff --git a/src/formio/index.ts b/src/formio/index.ts index bf0a86a..a7e6e6a 100644 --- a/src/formio/index.ts +++ b/src/formio/index.ts @@ -3,6 +3,7 @@ import { DateComponentSchema, DateTimeComponentSchema, EmailComponentSchema, + FileComponentSchema, NumberComponentSchema, PhoneNumberComponentSchema, PostcodeComponentSchema, @@ -42,6 +43,7 @@ export type AnyComponentSchema = | TimeComponentSchema | PhoneNumberComponentSchema | PostcodeComponentSchema + | FileComponentSchema | NumberComponentSchema // layout | ContentComponentSchema; diff --git a/test-d/formio/components/file.test-d.ts b/test-d/formio/components/file.test-d.ts new file mode 100644 index 0000000..5ca05c7 --- /dev/null +++ b/test-d/formio/components/file.test-d.ts @@ -0,0 +1,66 @@ +import {expectAssignable, expectNotAssignable} from 'tsd'; + +import {FileComponentSchema, FileUploadData} from '../../../lib'; + +// Grabbed from test env file upload, URLs obfuscated. +const anUpload: FileUploadData = { + url: 'http://localhost:8000/api/v2/submissions/files/54cc40ed-f1c4-4206-ba76-76d376ba4c3a', + data: { + url: 'http://localhost:8000/api/v2/submissions/files/54cc40ed-f1c4-4206-ba76-76d376ba4c3a', + form: '', + name: 'maykin_logo.png', + size: 8725, + baseUrl: 'http://localhost:8000/api/v2/', + project: '', + }, + name: 'maykin_logo-e0568045-45f6-46d1-909a-8895c5ee061e.png', + size: 8725, + type: 'image/png', + storage: 'url', + originalName: 'maykin_logo.png', +}; + +// minimal file component schema +expectAssignable({ + id: 'yejak', + type: 'file', + key: 'someFile', + label: 'Attachment', +}); + +// Behaviour of single vs. multiple file uploads + +const explicitSingleUpload: FileComponentSchema = { + id: 'yejak', + type: 'file', + key: 'someFile', + label: 'Attachment', + multiple: false, +}; +type ExplicitSingleUploadValue = (typeof explicitSingleUpload)['defaultValue']; +expectAssignable([]); +expectAssignable([anUpload]); +expectNotAssignable([anUpload, anUpload]); + +const explicitMultipleUpload: FileComponentSchema = { + id: 'yejak', + type: 'file', + key: 'someFile', + label: 'Attachment', + multiple: true, +}; +type ExplicitMultipleUploadValue = (typeof explicitMultipleUpload)['defaultValue']; +expectAssignable([]); +expectAssignable([anUpload]); +expectAssignable([anUpload, anUpload]); + +const implicitSingleUpload: FileComponentSchema = { + id: 'yejak', + type: 'file', + key: 'someFile', + label: 'Attachment', +}; +type ImplicitSingleUploadValue = (typeof implicitSingleUpload)['defaultValue']; +expectAssignable([]); +expectAssignable([anUpload]); +expectNotAssignable([anUpload, anUpload]);