diff --git a/apps/react/app/App.tsx b/apps/react/app/App.tsx index 032aff9e..82b3a802 100644 --- a/apps/react/app/App.tsx +++ b/apps/react/app/App.tsx @@ -3,7 +3,6 @@ import { document } from '@/utils/document' import { workbook } from '@/utils/workbook' import { recordHook } from '@flatfile/plugin-record-hook' import { - attachStyleSheet, Document, Sheet, Space, @@ -16,11 +15,21 @@ import { import { useEffect, useState } from 'react' import styles from './page.module.css' -attachStyleSheet({ nonce: 'flatfile-abc123' }) // add custom nonce - +/** + * @description Is responsible for rendering a Flatfile application with several + * features such as useEffect, useListener, usePlugin and useEvent. It also defines + * four buttons to trigger different actions related to opening, closing, and updating + * the portal, as well as submitting workbooks and records. + * + * @returns {Component} a React component that renders a Portal and several listeners + * for event handling. + */ const App = () => { const { open, openPortal, closePortal } = useFlatfile() const [label, setLabel] = useState('Rock') + /** + * @description Either opens or closes a portal, depending on the value of `open`. + */ const toggleOpen = () => { open ? closePortal({ reset: false }) : openPortal() } @@ -98,6 +107,21 @@ const App = () => { }} > + {/** + * @description Configures and generates high-quality documentation for given code. + * It accepts an object `workbook` with configuration properties, a `onSubmit` function + * that logs information when called, and an `onRecordHooks` array of functions that + * set property values on records before they are returned. + * + * @param {object} config - configuration options for a specific worksheet, allowing + * you to define properties such as the sheet name, slug, and record hooks. + * + * @param {object} onSubmit - 3rd sheet that will receive the submitted data after + * it has been processed and transformed. + * + * @param {array} onRecordHooks - 2 record hook functions that are executed when a + * record is inserted, updated, or deleted in the specified sheet. + */} { ], ]} > + {/** + * @description Configures a Google Sheets document with a specific name and default + * page, sets an onRecordHook function to update the "email" field with a custom + * value, and defines an onSubmit function to log a message when the sheet is submitted. + * + * @param {object} config - configuration for a specific worksheet, which includes + * the sheet name, slug, and an optional onRecordHook function to manipulate records + * before they are saved. + * + * @param {asynchronous function.} onRecordHook - callback function that is triggered + * when a new record is created or updated in the Sheet, which allows for custom + * modifications to be applied to the recorded data before it is saved. + * + * * `record`: The current record being processed in the hook, with properties + * matching the corresponding fields in the sheet's data source. + * * `sheet`: The active `Sheet` instance, used to access properties and methods for + * interacting with the Google Sheets API. + * + * @param {`async function`.} onSubmit - function that is executed when a record is + * submitted in the Sheet 3. + * + * * `onSubmit`: This is a function that will be called when a submit event occurs + * on the sheet. + * * `({ sheet })`: This is the event listener object that contains information about + * the submitted sheet. The `{ sheet }` property refers to the sheet that was submitted. + */} { console.log('onSubmit from Sheet 3', { sheet }) }} /> + {/** + * @description Configures a new Google Sheets document with a specified slug, name, + * and onRecordHook function to manipulate the contents of the sheet before it is + * created. The `onSubmit` function logs information when the sheet is submitted. + * + * @param {object} config - configuration for a new spreadsheet, including the slug + * and name of the spreadsheet, as well as overwriting the email field with the value + * `'SHEET 4 RECORDHOOK'`. + * + * @param {Anonymous function.} onRecordHook - 4th sheet's record hook, which sets + * the email field of each record to 'SHEET 4 RECORDHOOK'. + * + * * `record`: A record object that contains information about the updated record. + * * `SHEET 4 RECORDHOOK`: The email address specified as part of the onRecordHook + * hook. + * + * @param {object} onSubmit - action performed when a user submits data through the + * sheet. + */} `: This component is a wrapping container for Flatfile, which + * is a module that provides a simple way to store and retrieve data in a flat file. + * The `publishableKey` attribute within the `config` object is used to specify the + * publishable key for the Flatfile module. + * * `App`: This is the component that will be rendered inside the `FlatfileProvider`. + * It is likely that this component contains some of the logic and styling for the + * application being developed. + */ export default function Home() { const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_FLATFILE_PUBLISHABLE_KEY if (!PUBLISHABLE_KEY) return <>No Publishable Key Available @@ -11,7 +26,8 @@ export default function Home() { diff --git a/packages/react/src/components/FlatfileProvider.tsx b/packages/react/src/components/FlatfileProvider.tsx index ca4a88aa..137b9868 100644 --- a/packages/react/src/components/FlatfileProvider.tsx +++ b/packages/react/src/components/FlatfileProvider.tsx @@ -19,6 +19,8 @@ import { getSpace } from '../utils/getSpace' import { EmbeddedIFrameWrapper } from './EmbeddedIFrameWrapper' import FlatfileContext, { DEFAULT_CREATE_SPACE } from './FlatfileContext' +import { attachStyleSheet } from '../utils/attachStyleSheet' + const configDefaults: IFrameTypes = { preload: true, resetOnClose: true, @@ -31,6 +33,73 @@ interface ISessionSpace upgradedAt: string } +/** + * @description Sets up Flatfile Provider for a Portal, handling authentication, sheet + * management, and space creation/updates. It provisions an iframe to load the Portal + * content and listens for postMessages from the created iFrame to handle space updates + * and closing. + * + * @param {React Element or JSX Expression} .children - children of the FlatfileProvider + * component, which are the components that will be rendered inside the iFrame. + * + * 1/ `children`: The input passed to the `` component is + * an array of React components. + * 2/ `FLATFILE_PROVIDER_CONFIG`: An object that contains various configuration + * options for Flatfile, such as the space URL, API URL, and environment ID. This + * object is used to update the space and documents inside the space. + * 3/ `handleCreateSpace`: A function that creates a new space when called. It takes + * no arguments. + * 4/ `handleReUseSpace`: A function that updates an existing space when called. It + * takes no arguments. + * 5/ `publishableKey`: An optional string that contains the publishable key for the + * Flatfile instance. If not provided, it will be fetched from the Flatfile server. + * 6/ `internalAccessToken`: An optional string that contains the internal access + * token for the Flatfile instance. If not provided, it will be fetched from the + * Flatfile server. + * 7/ `apiUrl`: The API URL for the Flatfile instance. This is used to make requests + * to the Flatfile server. + * 8/ `environmentId`: An optional string that contains the environment ID for the + * Flatfile instance. If not provided, it will be fetched from the Flatfile server. + * 9/ `open`: An optional boolean value that indicates whether the space is open or + * closed. This is used to control the visibility of the space. + * 10/ `setOpen`: A function that sets the open status of the space. It takes a + * boolean argument indicating whether the space should be open or closed. + * 11/ `sessionSpace`: An optional object that contains information about the current + * session space, including the ID and URL. If not provided, it will be fetched from + * the Flatfile server. + * 12/ `setSessionSpace`: A function that sets the session space information. It + * takes an object with `ID` and `URL` properties as arguments. + * 13/ `setListener`: A function that sets the listener function for updates to the + * space and documents. It takes a function as an argument. + * 14/ `listener`: An optional function that is called whenever there are updates + * to the space or documents. If not provided, it will be fetched from the Flatfile + * server. + * 15/ `defaultPage`: An optional React component that contains the default page for + * the Flatfile instance. If not provided, it will be fetched from the Flatfile server. + * 16/ `setDefaultPage`: A function that sets the default page for the Flatfile + * instance. It takes a React component as an argument. + * + * @param {string} .publishableKey - 10-digit publishable key associated with the + * Space, which is required to authenticate and interact with the Flatfile platform. + * + * @param {string} .accessToken - Flatfile access token used to authenticate and make + * API requests within the provider component. + * + * @param {string} .environmentId - environment ID that Flatfile should be deployed + * to. It is used to pass the necessary configuration to the Flatfile provider component + * to properly authenticate and interact with the Flatfile API. + * + * @param {string} .apiUrl - 3D visualization API endpoint used for fetching and + * updating the virtual 3D environment. + * + * @param {object} .config - Flatfile Provider configuration object, which is used + * to determine the behavior of the `FlatfileContext.Provider` component. It specifies + * various settings and options for the provider, such as the API URL, authentication + * token, and whether to create a new space or reuse an existing one. + * + * @returns {object} a Flatfile provider that renders a space and allows for the + * creation, updating, and deleting of sheets, workbooks, and documents. + */ export const FlatfileProvider: React.FC = ({ children, publishableKey, @@ -73,6 +142,26 @@ export const FlatfileProvider: React.FC = ({ }, []) const [ready, setReady] = useState(false) + /** + * @description Automatically configure's Flatfile space using API key when provided + * with publishable key, otherwise it handles internal client side API usage and + * updates default page in Flatfile space. + * + * @returns {AccessToken} an access token for the newly created space, which is stored + * in the `window` object and used for internal client-side Flatfile API usage. + * + * * `createdSpace`: an object containing the newly created space, including its ID, + * name, and auto-configure status. + * * `apiUrl`: the URL of the Flatfile API server. + * * `publishableKey`: the publishable key for the Flatfile API. + * * `workbook`: the workbook associated with the createSpace call, or undefined if + * none was provided. + * * `document`: the document associated with the createSpace call, or undefined if + * none was provided. + * * `sessionSpace`: an object containing the current session space, including its + * ID and access token. + * * `internalAccessToken`: the internal access token for the current session space. + */ const handleCreateSpace = async () => { if (!publishableKey) { return @@ -98,6 +187,10 @@ export const FlatfileProvider: React.FC = ({ setSessionSpace(createdSpace) } + /** + * @description Verifies if an access token exists for a given space, and if so, sets + * it as the internal access token and saves it to the session space object. + */ const handleReUseSpace = async () => { if (internalAccessToken && createSpace.space.id) { const { data: reUsedSpace } = await getSpace({ @@ -114,6 +207,23 @@ export const FlatfileProvider: React.FC = ({ } } + /** + * @description Updates a pre-existing Flutter file by adding a new sheet to the + * workbook if one does not already exist with the same slug as the new sheet. + * + * @param {Flatfile.SheetConfig} newSheet - Flatfile.SheetConfig object containing + * the configuration details of a new sheet to be created within an existing workbook. + * + * @returns {updated instance of `Flatfile.WorkbookConfig} a modified version of the + * `prevSpace` object, where a new sheet has been added to the workbook. + * + * * `prevSpace`: The previous workbook state. + * * `workbook`: The updated workbook state, which includes the new sheet added to + * the previous one. It is an object with the following properties: + * + `sheets`: An array of sheets, which now contains the newly added sheet as well + * as the existing ones. + * + `slug`: The unique identifier for each sheet, used for reference in the workbook. + */ const addSheet = (newSheet: Flatfile.SheetConfig) => { setCreateSpace((prevSpace) => { // Check if the sheet already exists @@ -134,6 +244,19 @@ export const FlatfileProvider: React.FC = ({ }) } + /** + * @description Updates a specific sheet in a Flatfile workbook based on a given slug + * and partial updates. It mutates the workbook's sheets object by mapping over it + * and replacing the specified sheet with the updated one. + * + * @param {string} sheetSlug - unique identifier of the sheet for which the updates + * are being applied. + * + * @param {Partial} sheetUpdates - updates that should be applied + * to the sheet with the matching slug, which is provided as the `sheetSlug` input parameter. + * + * @returns {object} an updated `Flatfile.Workspace` object with a modified sheet. + */ const updateSheet = ( sheetSlug: string, sheetUpdates: Partial @@ -156,6 +279,15 @@ export const FlatfileProvider: React.FC = ({ }) } + /** + * @description Modifies a previously created workbook configuration based on new + * inputs provided by the `workbookUpdates` object. It prioritizes the sheets and + * actions passed in the updates, merging them with any existing sheets and actions + * in the previous workbook configuration. + * + * @param {Flatfile.CreateWorkbookConfig} workbookUpdates - updates to be applied to + * the workbook, including changes to the worksheet configuration, actions, and sheets. + */ const updateWorkbook = (workbookUpdates: Flatfile.CreateWorkbookConfig) => { setCreateSpace((prevSpace) => ({ ...prevSpace, @@ -175,6 +307,15 @@ export const FlatfileProvider: React.FC = ({ })) } + /** + * @description Updates a `Flatfile.DocumentConfig` object's `document` property by + * concatenating the original document and any update documents provided in the + * `documentUpdates` object. + * + * @param {Flatfile.DocumentConfig} documentUpdates - updates to be applied to the + * `document` field of the `Flatfile.DocumentConfig` object, which are then merged + * with the previous document configuration to produce the updated document configuration. + */ const updateDocument = (documentUpdates: Flatfile.DocumentConfig) => { setCreateSpace((prevSpace) => ({ ...prevSpace, @@ -185,6 +326,13 @@ export const FlatfileProvider: React.FC = ({ })) } + /** + * @description Updates a space configuration by combining the current space configuration + * with new space updates. The updated space configuration is then assigned to the + * `space` object within the context. + * + * @param {Flatfile.SpaceConfig} spaceUpdates - updates to be applied to the space configuration. + */ const updateSpace = (spaceUpdates: Flatfile.SpaceConfig) => { setCreateSpace((prevSpace) => ({ ...prevSpace, @@ -192,6 +340,14 @@ export const FlatfileProvider: React.FC = ({ })) } + /** + * @description Sets open to false, resets internal access token and session space, + * preloads a space URL, and updates an iframe's source tag based on configuration options. + * + * @param {ClosePortalOptions} .reset - FlatFile Provider configuration's `resetOnClose` + * property, which when set to `true`, triggers the reset of internal access token + * and space URL after the portal is closed. + */ const resetSpace = ({ reset }: ClosePortalOptions = {}) => { setOpen(false) @@ -214,6 +370,14 @@ export const FlatfileProvider: React.FC = ({ // Works but only after the iframe is visible } } + const styleSheetRef = useRef(false) + + useEffect(() => { + if (!styleSheetRef.current) { + attachStyleSheet(config?.styleSheetOptions) + styleSheetRef.current = true + } + }, [config?.styleSheetOptions, styleSheetRef]) // Listen to the postMessage event from the created iFrame useEffect(() => { @@ -256,6 +420,11 @@ export const FlatfileProvider: React.FC = ({ // Triggers handleCreateSpace or handleReUseSpace when the openPortal() is clicked and ready is true useEffect(() => { if (ready && open) { + /** + * @description Determines whether to create a new space or update an existing one + * based on two inputs: `publishableKey` and `internalAccessToken`. It executes the + * appropriate action based on the input values. + */ const createOrUpdateSpace = async () => { if (publishableKey && !internalAccessToken) { await handleCreateSpace() diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index f3472fb5..da704892 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -6,17 +6,8 @@ import type { IUserInfo, } from '@flatfile/embedded-utils' -import stylesheet from './components/style.scss' -import { styleInject } from './utils/styleInject' export { makeTheme } from './utils/makeTheme' export * from './components' export * from './hooks' export type { ISidebarConfig, ISpace, ISpaceInfo, IThemeConfig, IUserInfo } - -export function attachStyleSheet(options?: { - insertAt?: 'top' - nonce?: string -}) { - styleInject(stylesheet, options) -} diff --git a/packages/react/src/types/iFrameProps.ts b/packages/react/src/types/iFrameProps.ts index 0902e8cd..2c41ad35 100644 --- a/packages/react/src/types/iFrameProps.ts +++ b/packages/react/src/types/iFrameProps.ts @@ -1,5 +1,5 @@ import { ISpace } from '@flatfile/embedded-utils' - +import { StyleSheetOptions } from '../utils/attachStyleSheet' export type IFrameTypes = Partial< Pick< ISpace, @@ -12,5 +12,9 @@ export type IFrameTypes = Partial< | 'displayAsModal' | 'closeSpace' | 'spaceUrl' - > & { preload?: boolean; resetOnClose?: boolean } + > & { + preload?: boolean + resetOnClose?: boolean + styleSheetOptions?: StyleSheetOptions + } > diff --git a/packages/react/src/utils/attachStyleSheet.ts b/packages/react/src/utils/attachStyleSheet.ts new file mode 100644 index 00000000..c4953b3e --- /dev/null +++ b/packages/react/src/utils/attachStyleSheet.ts @@ -0,0 +1,18 @@ +import { styleInject } from '../utils/styleInject' + +import stylesheet from '../components/style.scss' +export type StyleSheetOptions = { + insertAt?: 'top' + nonce?: string +} + +/** + * @description Attaches a CSS stylesheet to the HTML document through the `styleInject` + * method, providing an optional set of options for customization. + * + * @param {StyleSheetOptions} options - configuration object for customizing the + * behavior of the style injection process. + */ +export function attachStyleSheet(options?: StyleSheetOptions) { + styleInject(stylesheet, options) +}