gristlabs_grist-core/app/client/ui/FormAPI.ts
George Gevoian 418681915e (core) Forms Improvements
Summary:
 - Forms now have a reset button.
 - Choice and Reference fields in forms now have an improved select menu.
 - Formula and attachments column types are no longer mappable or visible in forms.
 - Fields in a form widget are now removed if their column is deleted.
 - The preview button in a published form widget has been replaced with a view button. It now opens the published form in a new tab.
 - A new share menu for published form widgets, with options to copy a link or embed code.
 - Forms can now have multiple sections.
 - Form widgets now indicate when publishing is unavailable (e.g. in forks or unsaved documents).
 - General improvements to form styling.

Test Plan: Browser tests.

Reviewers: jarek

Reviewed By: jarek

Subscribers: paulfitz

Differential Revision: https://phab.getgrist.com/D4203
2024-03-21 13:01:25 -04:00

122 lines
3.8 KiB
TypeScript

import {BaseAPI, IOptions} from 'app/common/BaseAPI';
import {CellValue, ColValues} from 'app/common/DocActions';
import {addCurrentOrgToPath} from 'app/common/urlUtils';
/**
* Form and associated field metadata from a Grist view section.
*
* Includes the layout of the form, metadata such as the form title, and
* a map of data for each field in the form. All of this is used to build a
* submittable version of the form (see `FormRenderer.ts`, which handles the
* actual building of forms).
*/
export interface Form {
formFieldsById: Record<number, FormField>;
formLayoutSpec: string;
formTitle: string;
formTableId: string;
}
/**
* Metadata for a field in a form.
*
* Form fields are directly related to Grist fields; the former is based on data
* from the latter, with additional metadata specific to forms, like whether a
* form field is required. All of this is used to build a field in a submittable
* version of the form (see `FormRenderer.ts`, which handles the actual building
* of forms).
*/
export interface FormField {
/** The field label. Defaults to the Grist column label or id. */
question: string;
/** The field description. */
description: string;
/** The Grist column id of the field. */
colId: string;
/** The Grist column type of the field (e.g. "Text"). */
type: string;
/** Additional field options. */
options: FormFieldOptions;
/** Populated with data from a referenced table. Only set if `type` is a Reference type. */
refValues: [number, CellValue][] | null;
}
interface FormFieldOptions {
/** True if the field is required to submit the form. */
formRequired?: boolean;
/** Populated with a list of options. Only set if the field `type` is a Choice/Reference Liste. */
choices?: string[];
}
export interface FormAPI {
getForm(options: GetFormOptions): Promise<Form>;
createRecord(options: CreateRecordOptions): Promise<void>;
}
interface GetFormCommonOptions {
vsId: number;
}
interface GetFormWithDocIdOptions extends GetFormCommonOptions {
docId: string;
}
interface GetFormWithShareKeyOptions extends GetFormCommonOptions {
shareKey: string;
}
type GetFormOptions = GetFormWithDocIdOptions | GetFormWithShareKeyOptions;
interface CreateRecordCommonOptions {
tableId: string;
colValues: ColValues;
}
interface CreateRecordWithDocIdOptions extends CreateRecordCommonOptions {
docId: string;
}
interface CreateRecordWithShareKeyOptions extends CreateRecordCommonOptions {
shareKey: string;
}
type CreateRecordOptions = CreateRecordWithDocIdOptions | CreateRecordWithShareKeyOptions;
export class FormAPIImpl extends BaseAPI implements FormAPI {
constructor(private _homeUrl: string, options: IOptions = {}) {
super(options);
}
public async getForm(options: GetFormOptions): Promise<Form> {
if ('docId' in options) {
const {docId, vsId} = options;
return this.requestJson(`${this._url}/api/docs/${docId}/forms/${vsId}`, {method: 'GET'});
} else {
const {shareKey, vsId} = options;
return this.requestJson(`${this._url}/api/s/${shareKey}/forms/${vsId}`, {method: 'GET'});
}
}
public async createRecord(options: CreateRecordOptions): Promise<void> {
if ('docId' in options) {
const {docId, tableId, colValues} = options;
return this.requestJson(`${this._url}/api/docs/${docId}/tables/${tableId}/records`, {
method: 'POST',
body: JSON.stringify({records: [{fields: colValues}]}),
});
} else {
const {shareKey, tableId, colValues} = options;
const url = new URL(`${this._url}/api/s/${shareKey}/tables/${tableId}/records`);
url.searchParams.set('utm_source', 'grist-forms');
return this.requestJson(url.href, {
method: 'POST',
body: JSON.stringify({records: [{fields: colValues}]}),
});
}
}
private get _url(): string {
return addCurrentOrgToPath(this._homeUrl);
}
}