Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documented 17 functions across 4 files #149

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 72 additions & 3 deletions apps/react/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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()
}
Expand Down Expand Up @@ -98,6 +107,21 @@ const App = () => {
}}
>
<Document defaultPage config={document} />
{/**
* @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.
*/}
<Workbook
config={{
...workbook,
Expand All @@ -121,6 +145,32 @@ const App = () => {
],
]}
>
{/**
* @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.
*/}
<Sheet
defaultPage
config={{
Expand All @@ -136,6 +186,25 @@ const App = () => {
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.
*/}
<Sheet
config={{
...workbook.sheets![0],
Expand Down
18 changes: 17 additions & 1 deletion apps/react/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,30 @@ import React from 'react'
import App from './App'
import { FlatfileProvider } from '@flatfile/react'

/**
* @description Generates high-quality documentation for given code and publishes it
* to Flatfile, using the specified `publishableKey`.
*
* @returns {HTML document} a functional component that uses a `FlatfileProvider` to
* load a Next.js app with a preloaded configuration.
*
* * `<FlatfileProvider>`: 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</>
return (
<FlatfileProvider
publishableKey={PUBLISHABLE_KEY}
config={{
preload: true
preload: true,
styleSheetOptions: { nonce: 'flatfile-abc123' },
}}
>
<App />
Expand Down
169 changes: 169 additions & 0 deletions packages/react/src/components/FlatfileProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 `<FlatfileContext.Provider>` 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<ExclusiveFlatfileProviderProps> = ({
children,
publishableKey,
Expand Down Expand Up @@ -73,6 +142,26 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
}, [])
const [ready, setReady] = useState<boolean>(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
Expand All @@ -98,6 +187,10 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
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({
Expand All @@ -114,6 +207,23 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
}
}

/**
* @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
Expand All @@ -134,6 +244,19 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
})
}

/**
* @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<Flatfile.SheetConfig>} 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<Flatfile.SheetConfig>
Expand All @@ -156,6 +279,15 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
})
}

/**
* @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,
Expand All @@ -175,6 +307,15 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
}))
}

/**
* @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,
Expand All @@ -185,13 +326,28 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
}))
}

/**
* @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,
space: { ...prevSpace.space, ...spaceUpdates },
}))
}

/**
* @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)

Expand All @@ -214,6 +370,14 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
// 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(() => {
Expand Down Expand Up @@ -256,6 +420,11 @@ export const FlatfileProvider: React.FC<ExclusiveFlatfileProviderProps> = ({
// 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()
Expand Down
Loading