From af7cffa046912dafd33d5caafcb685e6dbb8e7d1 Mon Sep 17 00:00:00 2001 From: skuhlmann Date: Tue, 3 Oct 2023 10:58:31 -0600 Subject: [PATCH 1/2] scaffolds and implements wizard-form-builder --- libs/wizard-form-builder/.babelrc | 12 + libs/wizard-form-builder/.eslintrc.json | 18 ++ libs/wizard-form-builder/README.md | 78 +++++++ libs/wizard-form-builder/jest.config.ts | 10 + libs/wizard-form-builder/package.json | 4 + libs/wizard-form-builder/project.json | 59 +++++ .../src/WizardFormBuilder.tsx | 206 ++++++++++++++++++ .../src/components/Confirmation.tsx | 84 +++++++ .../src/components/WizardFormFooter.tsx | 145 ++++++++++++ .../src/components/index.ts | 2 + libs/wizard-form-builder/src/index.ts | 5 + libs/wizard-form-builder/src/types/index.ts | 1 + .../src/types/wizardFormLegoTypes.ts | 49 +++++ libs/wizard-form-builder/tsconfig.json | 25 +++ libs/wizard-form-builder/tsconfig.lib.json | 23 ++ libs/wizard-form-builder/tsconfig.spec.json | 20 ++ tsconfig.base.json | 3 +- 17 files changed, 743 insertions(+), 1 deletion(-) create mode 100644 libs/wizard-form-builder/.babelrc create mode 100644 libs/wizard-form-builder/.eslintrc.json create mode 100644 libs/wizard-form-builder/README.md create mode 100644 libs/wizard-form-builder/jest.config.ts create mode 100644 libs/wizard-form-builder/package.json create mode 100644 libs/wizard-form-builder/project.json create mode 100644 libs/wizard-form-builder/src/WizardFormBuilder.tsx create mode 100644 libs/wizard-form-builder/src/components/Confirmation.tsx create mode 100644 libs/wizard-form-builder/src/components/WizardFormFooter.tsx create mode 100644 libs/wizard-form-builder/src/components/index.ts create mode 100644 libs/wizard-form-builder/src/index.ts create mode 100644 libs/wizard-form-builder/src/types/index.ts create mode 100644 libs/wizard-form-builder/src/types/wizardFormLegoTypes.ts create mode 100644 libs/wizard-form-builder/tsconfig.json create mode 100644 libs/wizard-form-builder/tsconfig.lib.json create mode 100644 libs/wizard-form-builder/tsconfig.spec.json diff --git a/libs/wizard-form-builder/.babelrc b/libs/wizard-form-builder/.babelrc new file mode 100644 index 00000000..ccae900b --- /dev/null +++ b/libs/wizard-form-builder/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nrwl/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/wizard-form-builder/.eslintrc.json b/libs/wizard-form-builder/.eslintrc.json new file mode 100644 index 00000000..734ddace --- /dev/null +++ b/libs/wizard-form-builder/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/wizard-form-builder/README.md b/libs/wizard-form-builder/README.md new file mode 100644 index 00000000..8401cdd7 --- /dev/null +++ b/libs/wizard-form-builder/README.md @@ -0,0 +1,78 @@ +# @daohaus/wizard-form-builder + +The Wizard Form Builder library is an extension of the Form Builder and enables multi-step form building. + +#### Related packages + +- [**Form-builder**](https://github.com/HausDAO/monorepo/tree/develop/libs/form-builder) +- [**TX-Builder**](https://github.com/HausDAO/monorepo/tree/develop/libs/tx-builder) +- [**Moloch V3 Legos**](https://github.com/HausDAO/monorepo/tree/develop/libs/moloch-v3-legos) +- [**Moloch V3 Fields**](https://github.com/HausDAO/monorepo/tree/develop/libs/moloch-v3-fields) + +### [View on NPM](https://www.npmjs.com/package/@daohaus/wizard-form-builder) + +## Usage + +### Installation + +```bash +yarn add @daohaus/wizardform-builder +``` + +**How to add to you application** + +```jsx +import { WizardFormBuilder } from '@daohaus/form-builder'; +import { MolochFields } from '@daohaus/moloch-v3-fields'; + +import { TARGET_DAO } from '../targetDao'; + +const WIZARD_FORM_LEGO = { + id: 'WIZZ', + tx: TX.SOME_TX_LEGO, + log: true, + submitButtonText: 'Deploy', + confirmTitle: 'Review Data', + confirmDescription: 'These settings cannot be changed once on-chain. Some other confirmation text.', + steps: [ + { + id: 'stepOne', + title: 'Name Your DAO', + description: 'You are summoning a Moloch DAO. Enter a name for your on-chain organization. ', + fields: [ + { + type: 'input', + id: 'daoName', + label: 'DAO', + placeholder: 'Name', + rules: { + maxLength: { + value: 128, + message: 'DAO name must be 128 characters or less', + }, + }, + }, + ], + requiredFields: { daoName: true }, + }, + { + id: 'stepTwo', + title: 'Describe Your DAO', + description: 'What does your DAO do?', + fields: [ + { + type: 'textarea', + id: 'daoDescription', + label: 'Description', + placeholder: 'Description', + }, + ], + requiredFields: { daoDescription: true }, + }, + ], +}; + +export const FormPage = () => { + return ; +}; +``` diff --git a/libs/wizard-form-builder/jest.config.ts b/libs/wizard-form-builder/jest.config.ts new file mode 100644 index 00000000..6ce223d0 --- /dev/null +++ b/libs/wizard-form-builder/jest.config.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ +export default { + displayName: 'wizard-form-builder', + preset: '../../jest.preset.js', + transform: { + '^.+\\.[tj]sx?$': 'babel-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/wizard-form-builder', +}; diff --git a/libs/wizard-form-builder/package.json b/libs/wizard-form-builder/package.json new file mode 100644 index 00000000..4e0a7652 --- /dev/null +++ b/libs/wizard-form-builder/package.json @@ -0,0 +1,4 @@ +{ + "name": "@daohaus/wizard-form-builder", + "version": "0.0.1" +} diff --git a/libs/wizard-form-builder/project.json b/libs/wizard-form-builder/project.json new file mode 100644 index 00000000..fef270d1 --- /dev/null +++ b/libs/wizard-form-builder/project.json @@ -0,0 +1,59 @@ +{ + "name": "wizard-form-builder", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/wizard-form-builder/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nrwl/web:rollup", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/wizard-form-builder", + "tsConfig": "libs/wizard-form-builder/tsconfig.lib.json", + "project": "libs/wizard-form-builder/package.json", + "entryFile": "libs/wizard-form-builder/src/index.ts", + "external": ["react/jsx-runtime"], + "rollupConfig": "@nrwl/react/plugins/bundle-rollup", + "compiler": "babel", + "buildableProjectDepsInPackageJsonType": "dependencies", + "assets": [ + { + "glob": "libs/wizard-form-builder/README.md", + "input": ".", + "output": "." + } + ] + } + }, + "publish": { + "executor": "nx:run-commands", + "options": { + "command": "node tools/scripts/publish.mjs wizard-form-builder {args.ver} {args.tag}" + }, + "dependsOn": ["build"] + }, + "npmPublish": { + "executor": "ngx-deploy-npm:deploy", + "options": { + "access": "public", + "noBuild": true + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/wizard-form-builder/**/*.{ts,tsx,js,jsx}"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/wizard-form-builder/jest.config.ts", + "passWithNoTests": true + } + } + } +} diff --git a/libs/wizard-form-builder/src/WizardFormBuilder.tsx b/libs/wizard-form-builder/src/WizardFormBuilder.tsx new file mode 100644 index 00000000..391776b1 --- /dev/null +++ b/libs/wizard-form-builder/src/WizardFormBuilder.tsx @@ -0,0 +1,206 @@ +import { useState } from 'react'; +import { FieldValues } from 'react-hook-form'; + +import { TXLifeCycleFns, useTxBuilder } from '@daohaus/tx-builder'; +import { FormBuilderBase } from '@daohaus/form-builder-base'; +import { DataSm, FormLayout, H3, useToast } from '@daohaus/ui'; +import { useDHConnect } from '@daohaus/connect'; +import { handleErrorMessage, LookupType } from '@daohaus/utils'; +import { CoreFieldLookup } from '@daohaus/form-builder'; + +import { WizardFormLego } from './types/wizardFormLegoTypes'; +import { Confirmation } from './components/Confirmation'; +import { WizardFormFooter } from './components'; + +type BuilderProps = { + form: WizardFormLego; + defaultValues?: FieldValues; + customFields?: LookupType; + lifeCycleFns?: TXLifeCycleFns; + targetNetwork?: string; + onSubmit?: ( + formValues: FieldValues + ) => void | Promise<(formValues: FieldValues) => void>; + customConfirm?: React.ElementType; +}; + +export enum StatusMsg { + Compile = 'Compiling Transaction Data', + Request = 'Requesting Signature', + Await = 'Transaction Submitted', + TxErr = 'Transaction Error', + TxSuccess = 'Transaction Success', + PollStart = 'Syncing TX (Subgraph)', + PollSuccess = 'Success: TX Confirmed!', + PollError = 'Sync Error (Subgraph)', + NoContext = 'Missing TXBuilder Context', +} + +export const WizardFormBuilder = ({ + form, + defaultValues, + customFields = CoreFieldLookup, + targetNetwork, + lifeCycleFns, + onSubmit, + customConfirm, +}: BuilderProps) => { + const { chainId } = useDHConnect(); + const { fireTransaction } = useTxBuilder(); + const { defaultToast, errorToast, successToast } = useToast(); + const { title, description, subtitle } = form; + + const [isLoading, setIsLoading] = useState(false); + const [status, setStatus] = useState(null); + const [txHash, setTxHash] = useState(null); + const [currentStepIndex, setCurrentStepIndex] = useState(0); + const [confirmationData, setConfirmationData] = + useState(); + + const isSameNetwork = targetNetwork ? targetNetwork === chainId : true; + const submitDisabled = isLoading || !isSameNetwork; + + const handleConfirm = (formValues: FieldValues) => { + setConfirmationData(formValues); + setCurrentStepIndex(currentStepIndex + 1); + }; + + const handleSubmitButton = () => { + if (!confirmationData) return; + handleSubmit(confirmationData); + }; + + const handleSubmit = async (formValues: FieldValues) => { + if (form.tx) { + setIsLoading(true); + setTxHash(null); + setStatus(StatusMsg.Compile); + const executed = await fireTransaction({ + tx: form.tx, + callerState: { + formValues, + }, + lifeCycleFns: { + onRequestSign() { + setStatus(StatusMsg.Request); + lifeCycleFns?.onRequestSign?.(); + }, + onTxHash(txHash) { + setTxHash(txHash); + setStatus(StatusMsg.Await); + lifeCycleFns?.onTxHash?.(txHash); + }, + onTxError(error) { + setStatus(StatusMsg.TxErr); + const errMsg = handleErrorMessage({ + error, + fallback: 'Could not decode error message', + }); + + setIsLoading(false); + lifeCycleFns?.onTxError?.(error); + errorToast({ title: StatusMsg.TxErr, description: errMsg }); + }, + onTxSuccess(...args) { + setStatus( + form.tx?.disablePoll ? StatusMsg.PollSuccess : StatusMsg.TxSuccess + ); + lifeCycleFns?.onTxSuccess?.(...args); + defaultToast({ + title: StatusMsg.TxSuccess, + description: form.tx?.disablePoll + ? 'Transaction cycle complete.' + : 'Please wait for subgraph to sync', + }); + }, + onPollStart() { + setStatus(StatusMsg.PollStart); + lifeCycleFns?.onPollStart?.(); + }, + onPollError(error) { + setStatus(StatusMsg.PollError); + const errMsg = handleErrorMessage({ + error, + fallback: 'Could not decode poll error message', + }); + setIsLoading(false); + lifeCycleFns?.onPollError?.(error); + errorToast({ title: StatusMsg.PollError, description: errMsg }); + }, + onPollSuccess(...args) { + setStatus(StatusMsg.PollSuccess); + setIsLoading(false); + successToast({ + title: StatusMsg.PollSuccess, + description: 'Transaction cycle complete.', + }); + lifeCycleFns?.onPollSuccess?.(...args); + }, + }, + }); + if (executed === undefined) { + setStatus(StatusMsg.NoContext); + return; + } + return executed; + } + if (onSubmit) { + return await onSubmit?.(formValues); + } + console.error('FormBuilder: onSubmit not implemented'); + }; + + const currentStep = form.steps[currentStepIndex]; + + return ( + + {currentStepIndex < form.steps.length && ( + <> + {currentStep.title && ( +

{currentStep.title}

+ )} + {currentStep.description && ( + + {currentStep.description} + + )} + + } + /> + + )} + + {currentStepIndex === form.steps.length && confirmationData && ( + + )} +
+ ); +}; diff --git a/libs/wizard-form-builder/src/components/Confirmation.tsx b/libs/wizard-form-builder/src/components/Confirmation.tsx new file mode 100644 index 00000000..008b63db --- /dev/null +++ b/libs/wizard-form-builder/src/components/Confirmation.tsx @@ -0,0 +1,84 @@ +import { Dispatch, SetStateAction } from 'react'; +import { FieldValues } from 'react-hook-form'; +import styled from 'styled-components'; + +import { Button, DataMd, H3 } from '@daohaus/ui'; +import { useFormBuilder } from '@daohaus/form-builder-base'; +import { WizardFormLego } from '../types'; + +const ButtonRow = styled.div` + display: flex; + flex-direction: row; + justify-content: flex-end; + flex-wrap: wrap; + gap: 2rem; +`; + +const ConfirmTitle = styled.div` + margin-bottom: 3rem; +`; + +type ConfirmationProps = { + formValues: FieldValues; + setCurrentStepIndex: Dispatch>; + currentStepIndex: number; + handleSubmit: () => void; + isLoading: boolean; + form: WizardFormLego; + customConfirm?: React.ElementType; +}; + +export const Confirmation = ({ + formValues, + setCurrentStepIndex, + currentStepIndex, + handleSubmit, + isLoading = false, + form, + customConfirm, +}: ConfirmationProps) => { + const { submitDisabled } = useFormBuilder() || {}; + + const { confirmTitle, confirmDescription, submitButtonText } = form; + + const CustomConfirm = customConfirm as React.ElementType; + + return ( + <> + <> + { + + {confirmTitle &&

{confirmTitle}

} + {!confirmTitle &&

Confirm

} +
+ } + {confirmDescription && {confirmDescription}} + {!confirmDescription && ( + + Look though the previous steps to review your inputs before + submitting. + + )} + + + {customConfirm && } + + + + + + + ); +}; diff --git a/libs/wizard-form-builder/src/components/WizardFormFooter.tsx b/libs/wizard-form-builder/src/components/WizardFormFooter.tsx new file mode 100644 index 00000000..88ef4149 --- /dev/null +++ b/libs/wizard-form-builder/src/components/WizardFormFooter.tsx @@ -0,0 +1,145 @@ +import { Dispatch, SetStateAction } from 'react'; +import styled, { useTheme } from 'styled-components'; +import { RiCheckLine, RiErrorWarningLine } from 'react-icons/ri/index.js'; + +import { ExplorerLink } from '@daohaus/connect'; +import { useFormBuilder } from '@daohaus/form-builder-base'; +import { Button, ParSm, Loading, Theme } from '@daohaus/ui'; + +enum StatusMsg { + Compile = 'Compiling Transaction Data', + Request = 'Requesting Signature', + Await = 'Transaction Submitted', + TxErr = 'Transaction Error', + TxSuccess = 'Transaction Success', + PollStart = 'Syncing TX (Subgraph)', + PollSuccess = 'Success: TX Confirmed!', + PollError = 'Sync Error (Subgraph)', + NoContext = 'Missing TXBuilder Context', +} + +const FooterBox = styled.div` + a { + margin-bottom: 1.6rem; + } +`; + +const ButtonRow = styled.div` + display: flex; + flex-direction: row; + justify-content: flex-end; + flex-wrap: wrap; + gap: 2rem; +`; + +export const WizardFormFooter = ({ + status, + txHash, + setCurrentStepIndex, + currentStepIndex, + stepCount, +}: { + submitDisabled?: boolean; + status: StatusMsg | null; + txHash: string | null; + setCurrentStepIndex: Dispatch>; + currentStepIndex: number; + stepCount: number; +}) => { + /*Form Alert Component goes here*/ + const { submitDisabled } = useFormBuilder() || {}; + + return ( + + {txHash && ( + + See Transaction Here + + )} + {status && } + + + {currentStepIndex > 0 && ( + + )} + + {currentStepIndex < stepCount - 1 && ( + + )} + + {currentStepIndex === stepCount - 1 && ( + + )} + + + ); +}; + +const getStatusColor = (status: StatusMsg, theme: Theme) => { + if (status === StatusMsg.PollSuccess) { + return theme.success.step9; + } + if ( + status === StatusMsg.PollError || + status === StatusMsg.TxErr || + status === StatusMsg.NoContext + ) { + return theme.danger.step9; + } else { + return theme.secondary.step9; + } +}; +const getStatusElement = (status: StatusMsg, theme: Theme) => { + if (status === StatusMsg.PollSuccess) { + return ; + } + if ( + status === StatusMsg.PollError || + status === StatusMsg.TxErr || + status === StatusMsg.NoContext + ) { + return ; + } else return ; +}; + +const StatusBox = styled.div<{ status: StatusMsg }>` + border-radius: ${({ theme }) => theme['card'].radius}; + border: 1px ${({ theme, status }) => getStatusColor(status, theme as Theme)} + solid; + padding: 1.5rem; + margin-bottom: 2rem; + .inner { + display: flex; + justify-content: space-between; + align-items: center; + p { + color: ${({ theme, status }) => getStatusColor(status, theme as Theme)}; + margin-right: auto; + } + } +`; + +const FormStatusDisplay = ({ status }: { status: StatusMsg }) => { + const theme = useTheme(); + return ( + +
+ {status} + {getStatusElement(status, theme as Theme)} +
+
+ ); +}; diff --git a/libs/wizard-form-builder/src/components/index.ts b/libs/wizard-form-builder/src/components/index.ts new file mode 100644 index 00000000..8e720c45 --- /dev/null +++ b/libs/wizard-form-builder/src/components/index.ts @@ -0,0 +1,2 @@ +export * from './WizardFormFooter'; +export * from './Confirmation'; diff --git a/libs/wizard-form-builder/src/index.ts b/libs/wizard-form-builder/src/index.ts new file mode 100644 index 00000000..82793370 --- /dev/null +++ b/libs/wizard-form-builder/src/index.ts @@ -0,0 +1,5 @@ +export * from './components'; +export * from './types'; +export * from './WizardFormBuilder'; +export { useFormBuilder } from '@daohaus/form-builder-base'; +export { FormBuilderFactory } from '@daohaus/form-builder-base'; diff --git a/libs/wizard-form-builder/src/types/index.ts b/libs/wizard-form-builder/src/types/index.ts new file mode 100644 index 00000000..35eb7fe5 --- /dev/null +++ b/libs/wizard-form-builder/src/types/index.ts @@ -0,0 +1 @@ +export * from "./wizardFormLegoTypes"; diff --git a/libs/wizard-form-builder/src/types/wizardFormLegoTypes.ts b/libs/wizard-form-builder/src/types/wizardFormLegoTypes.ts new file mode 100644 index 00000000..1843175c --- /dev/null +++ b/libs/wizard-form-builder/src/types/wizardFormLegoTypes.ts @@ -0,0 +1,49 @@ +import { JSXElementConstructor } from 'react'; +import { RegisterOptions } from 'react-hook-form'; + +import { + FieldLegoBase, + FormLegoBase, + LookupType, + TXLego, +} from '@daohaus/utils'; +import { CoreFieldLookup } from '@daohaus/form-builder'; + +export type CoreFields = typeof CoreFieldLookup; + +export type FieldLego = + FieldLegoBase; +export type FormLego = + FormLegoBase; + +declare type FieldBase = Record< + string, + JSXElementConstructor<{ + id: string; + disabled?: boolean; + rules?: RegisterOptions; + [property: string]: any; + }> +>; +export declare type WizardFormLego = WizardFormLegoBase; + +export declare type WizardFormLegoBase = + { + id: string; + title?: string; + subtitle?: string; + description?: string; + confirmTitle?: string; + confirmDescription?: string; + steps: { + id: string; + title?: string; + description?: string; + fields: FieldLegoBase[]; + requiredFields?: Record; + }[]; + tx?: TXLego; + log?: boolean; + devtool?: boolean; + submitButtonText?: string; + }; diff --git a/libs/wizard-form-builder/tsconfig.json b/libs/wizard-form-builder/tsconfig.json new file mode 100644 index 00000000..4c089585 --- /dev/null +++ b/libs/wizard-form-builder/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/wizard-form-builder/tsconfig.lib.json b/libs/wizard-form-builder/tsconfig.lib.json new file mode 100644 index 00000000..af84f21c --- /dev/null +++ b/libs/wizard-form-builder/tsconfig.lib.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": [ + "jest.config.ts", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx" + ], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/wizard-form-builder/tsconfig.spec.json b/libs/wizard-form-builder/tsconfig.spec.json new file mode 100644 index 00000000..ff08addd --- /dev/null +++ b/libs/wizard-form-builder/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "**/*.d.ts" + ] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 64599637..6a8afa62 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -34,7 +34,8 @@ "@daohaus/profile-data": ["libs/profile-data/src/index.ts"], "@daohaus/tx-builder": ["libs/tx-builder/src/index.ts"], "@daohaus/ui": ["libs/ui/src/index.ts"], - "@daohaus/utils": ["libs/utils/src/index.ts"] + "@daohaus/utils": ["libs/utils/src/index.ts"], + "@daohaus/wizard-form-builder": ["libs/wizard-form-builder/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] From e16672f6e8d66a814a7cc933898387b8e6a2e95c Mon Sep 17 00:00:00 2001 From: skuhlmann Date: Tue, 3 Oct 2023 11:28:02 -0600 Subject: [PATCH 2/2] spins up a sandbox app and tests the wizard form builder --- apps/admin/src/main.tsx | 4 +- apps/dev-sandbox/.babelrc | 11 +++ apps/dev-sandbox/.browserslistrc | 16 ++++ apps/dev-sandbox/.eslintrc.json | 18 ++++ apps/dev-sandbox/jest.config.ts | 11 +++ apps/dev-sandbox/project.json | 84 ++++++++++++++++++ apps/dev-sandbox/src/App.tsx | 13 +++ apps/dev-sandbox/src/Routes.tsx | 46 ++++++++++ apps/dev-sandbox/src/assets/.gitkeep | 0 .../src/components/HomeContent.tsx | 71 +++++++++++++++ .../src/environments/environment.prod.ts | 3 + .../src/environments/environment.ts | 6 ++ apps/dev-sandbox/src/favicon.ico | Bin 0 -> 15406 bytes apps/dev-sandbox/src/index.html | 14 +++ apps/dev-sandbox/src/layout/DaoContainer.tsx | 58 ++++++++++++ apps/dev-sandbox/src/layout/HomeContainer.tsx | 18 ++++ apps/dev-sandbox/src/legos/fieldConfig.ts | 12 +++ apps/dev-sandbox/src/legos/forms.ts | 52 +++++++++++ apps/dev-sandbox/src/main.tsx | 34 +++++++ apps/dev-sandbox/src/pages/DaoOverview.tsx | 17 ++++ apps/dev-sandbox/src/pages/FormTest.tsx | 15 ++++ apps/dev-sandbox/src/pages/Home.tsx | 7 ++ apps/dev-sandbox/src/polyfills.ts | 7 ++ apps/dev-sandbox/tsconfig.app.json | 23 +++++ apps/dev-sandbox/tsconfig.json | 25 ++++++ apps/dev-sandbox/tsconfig.spec.json | 24 +++++ libs/wizard-form-builder/src/types/index.ts | 2 +- package.json | 3 +- yarn.lock | 52 +++++++---- 29 files changed, 628 insertions(+), 18 deletions(-) create mode 100644 apps/dev-sandbox/.babelrc create mode 100644 apps/dev-sandbox/.browserslistrc create mode 100644 apps/dev-sandbox/.eslintrc.json create mode 100644 apps/dev-sandbox/jest.config.ts create mode 100644 apps/dev-sandbox/project.json create mode 100644 apps/dev-sandbox/src/App.tsx create mode 100644 apps/dev-sandbox/src/Routes.tsx create mode 100644 apps/dev-sandbox/src/assets/.gitkeep create mode 100644 apps/dev-sandbox/src/components/HomeContent.tsx create mode 100644 apps/dev-sandbox/src/environments/environment.prod.ts create mode 100644 apps/dev-sandbox/src/environments/environment.ts create mode 100755 apps/dev-sandbox/src/favicon.ico create mode 100644 apps/dev-sandbox/src/index.html create mode 100644 apps/dev-sandbox/src/layout/DaoContainer.tsx create mode 100644 apps/dev-sandbox/src/layout/HomeContainer.tsx create mode 100644 apps/dev-sandbox/src/legos/fieldConfig.ts create mode 100644 apps/dev-sandbox/src/legos/forms.ts create mode 100644 apps/dev-sandbox/src/main.tsx create mode 100644 apps/dev-sandbox/src/pages/DaoOverview.tsx create mode 100644 apps/dev-sandbox/src/pages/FormTest.tsx create mode 100644 apps/dev-sandbox/src/pages/Home.tsx create mode 100644 apps/dev-sandbox/src/polyfills.ts create mode 100644 apps/dev-sandbox/tsconfig.app.json create mode 100644 apps/dev-sandbox/tsconfig.json create mode 100644 apps/dev-sandbox/tsconfig.spec.json diff --git a/apps/admin/src/main.tsx b/apps/admin/src/main.tsx index b56f5a06..ee02821a 100644 --- a/apps/admin/src/main.tsx +++ b/apps/admin/src/main.tsx @@ -1,8 +1,10 @@ -import { HausThemeProvider } from '@daohaus/ui'; import { StrictMode } from 'react'; import * as ReactDOM from 'react-dom/client'; import { HashRouter } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from 'react-query'; + +import { HausThemeProvider } from '@daohaus/ui'; + import { App } from './App'; const root = ReactDOM.createRoot( diff --git a/apps/dev-sandbox/.babelrc b/apps/dev-sandbox/.babelrc new file mode 100644 index 00000000..61641ec8 --- /dev/null +++ b/apps/dev-sandbox/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": [ + [ + "@nrwl/react/babel", + { + "runtime": "automatic" + } + ] + ], + "plugins": [] +} diff --git a/apps/dev-sandbox/.browserslistrc b/apps/dev-sandbox/.browserslistrc new file mode 100644 index 00000000..f1d12df4 --- /dev/null +++ b/apps/dev-sandbox/.browserslistrc @@ -0,0 +1,16 @@ +# This file is used by: +# 1. autoprefixer to adjust CSS to support the below specified browsers +# 2. babel preset-env to adjust included polyfills +# +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# +# If you need to support different browsers in production, you may tweak the list below. + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major version +last 2 iOS major versions +Firefox ESR +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/apps/dev-sandbox/.eslintrc.json b/apps/dev-sandbox/.eslintrc.json new file mode 100644 index 00000000..734ddace --- /dev/null +++ b/apps/dev-sandbox/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/dev-sandbox/jest.config.ts b/apps/dev-sandbox/jest.config.ts new file mode 100644 index 00000000..6c97e710 --- /dev/null +++ b/apps/dev-sandbox/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'dev-sandbox', + preset: '../../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest', + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/react/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/apps/dev-sandbox', +}; diff --git a/apps/dev-sandbox/project.json b/apps/dev-sandbox/project.json new file mode 100644 index 00000000..530bc538 --- /dev/null +++ b/apps/dev-sandbox/project.json @@ -0,0 +1,84 @@ +{ + "name": "dev-sandbox", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/dev-sandbox/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "compiler": "babel", + "outputPath": "dist/apps/dev-sandbox", + "index": "apps/dev-sandbox/src/index.html", + "baseHref": "/", + "main": "apps/dev-sandbox/src/main.tsx", + "polyfills": "apps/dev-sandbox/src/polyfills.ts", + "tsConfig": "apps/dev-sandbox/tsconfig.app.json", + "assets": [ + "apps/dev-sandbox/src/favicon.ico", + "apps/dev-sandbox/src/assets" + ], + "styles": [], + "scripts": [], + "webpackConfig": "@nrwl/react/plugins/webpack" + }, + "configurations": { + "development": { + "extractLicenses": false, + "optimization": false, + "sourceMap": true, + "vendorChunk": true + }, + "production": { + "fileReplacements": [ + { + "replace": "apps/dev-sandbox/src/environments/environment.ts", + "with": "apps/dev-sandbox/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false + } + } + }, + "serve": { + "executor": "@nrwl/webpack:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "dev-sandbox:build", + "hmr": true + }, + "configurations": { + "development": { + "buildTarget": "dev-sandbox:build:development" + }, + "production": { + "buildTarget": "dev-sandbox:build:production", + "hmr": false + } + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/dev-sandbox/**/*.{ts,tsx,js,jsx}"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/dev-sandbox/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": [] +} diff --git a/apps/dev-sandbox/src/App.tsx b/apps/dev-sandbox/src/App.tsx new file mode 100644 index 00000000..78ddc7f9 --- /dev/null +++ b/apps/dev-sandbox/src/App.tsx @@ -0,0 +1,13 @@ +import { DHConnectProvider } from '@daohaus/connect'; +import { useState } from 'react'; +import { Routes } from './Routes'; + +export const App = () => { + const [daoChainId, setDaoChainId] = useState(); + + return ( + + + + ); +}; diff --git a/apps/dev-sandbox/src/Routes.tsx b/apps/dev-sandbox/src/Routes.tsx new file mode 100644 index 00000000..018e6467 --- /dev/null +++ b/apps/dev-sandbox/src/Routes.tsx @@ -0,0 +1,46 @@ +import { useEffect } from 'react'; +import { + matchPath, + useLocation, + Routes as RoutesDom, + Route, +} from 'react-router-dom'; + +import { ReactSetter } from '@daohaus/utils'; +import { MULTI_DAO_ROUTER } from '@daohaus/moloch-v3-hooks'; + +import { DaoContainer } from './layout/DaoContainer'; +import { HomeContainer } from './layout/HomeContainer'; +import { DaoOverview } from './pages/DaoOverview'; +import { Home } from './pages/Home'; +import { FormTest } from './pages/FormTest'; + +export const Routes = ({ + setDaoChainId, +}: { + setDaoChainId: ReactSetter; +}) => { + const location = useLocation(); + const pathMatch = matchPath('molochv3/:daochain/:daoid/*', location.pathname); + + useEffect(() => { + if (pathMatch?.params?.daochain) { + setDaoChainId(pathMatch?.params?.daochain); + } + if (!pathMatch?.params?.daochain) { + setDaoChainId(undefined); + } + }, [pathMatch?.params?.daochain, setDaoChainId]); + + return ( + + }> + } /> + + }> + } /> + } /> + + + ); +}; diff --git a/apps/dev-sandbox/src/assets/.gitkeep b/apps/dev-sandbox/src/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apps/dev-sandbox/src/components/HomeContent.tsx b/apps/dev-sandbox/src/components/HomeContent.tsx new file mode 100644 index 00000000..72828808 --- /dev/null +++ b/apps/dev-sandbox/src/components/HomeContent.tsx @@ -0,0 +1,71 @@ +import styled from 'styled-components'; + +import { breakpoints, H1, ParXl } from '@daohaus/ui'; + +const ViewBox = styled.div` + grid-area: body; + width: 100%; + height: 70rem; + display: flex; + background-image: url('assets/hub-illustration.svg'); + background-size: auto 60rem; + background-repeat: no-repeat; + background-position: -10% 180%; + margin-top: 6.3rem; + .text-section { + width: 100%; + max-width: 40rem; + min-width: 28rem; + } + .hero { + font-size: 6rem; + font-weight: 900; + } + .tag-line { + font-size: 1.6rem; + margin-bottom: 3.2rem; + font-weight: 700; + } + ul { + margin-top: 2.4rem; + padding-inline-start: 2.4rem; + margin-top: 2.4rem; + } + li { + font-size: 1.6rem; + } + @media (min-width: ${breakpoints.xs}) { + height: 80rem; + background-size: auto 70rem; + background-position: 6rem 8rem; + } + @media (min-width: ${breakpoints.sm}) { + height: 90rem; + background-size: auto 80rem; + background-position: 20rem 10rem; + .hero { + font-size: 6.6rem; + } + .tag-line { + font-size: 3.2rem; + } + } + @media (min-width: ${breakpoints.md}) { + .text-section { + max-width: 52rem; + } + height: 100rem; + background-size: auto 90rem; + background-position: 110% 30%; + } +`; +export const HomeContent = () => { + return ( + +
+

Home

+ Development Sandbox +
+
+ ); +}; diff --git a/apps/dev-sandbox/src/environments/environment.prod.ts b/apps/dev-sandbox/src/environments/environment.prod.ts new file mode 100644 index 00000000..c9669790 --- /dev/null +++ b/apps/dev-sandbox/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/apps/dev-sandbox/src/environments/environment.ts b/apps/dev-sandbox/src/environments/environment.ts new file mode 100644 index 00000000..7ed83767 --- /dev/null +++ b/apps/dev-sandbox/src/environments/environment.ts @@ -0,0 +1,6 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// When building for production, this file is replaced with `environment.prod.ts`. + +export const environment = { + production: false, +}; diff --git a/apps/dev-sandbox/src/favicon.ico b/apps/dev-sandbox/src/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..d21849281ec54179d9b2f1a9e10e126df1344bc9 GIT binary patch literal 15406 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 c*K0Igy)_#8Z_K9I#g+p8SNnfyK(2xR1? + + + + DAOHaus DevSandbox + + + + + + +
+ + diff --git a/apps/dev-sandbox/src/layout/DaoContainer.tsx b/apps/dev-sandbox/src/layout/DaoContainer.tsx new file mode 100644 index 00000000..b81d1fed --- /dev/null +++ b/apps/dev-sandbox/src/layout/DaoContainer.tsx @@ -0,0 +1,58 @@ +import { Outlet, useLocation, useParams } from 'react-router-dom'; + +import { DHLayout, useDHConnect } from '@daohaus/connect'; +import { CurrentDaoProvider, useDaoData } from '@daohaus/moloch-v3-hooks'; +import { ValidNetwork } from '@daohaus/keychain-utils'; +import { TXBuilder } from '@daohaus/tx-builder'; +import { Footer } from '@daohaus/ui'; + +export const DaoContainer = () => { + const { address, publicClient } = useDHConnect(); + const { daoChain, daoId, proposalId, memberAddress } = useParams<{ + daoChain: ValidNetwork; + daoId: string; + proposalId: string; + memberAddress: string; + }>(); + + const { dao } = useDaoData({ + daoId: daoId as string, + daoChain: daoChain as string, + }); + + const location = useLocation(); + + const navLinks = [ + { label: 'Home', href: `/` }, + { label: 'DAO', href: `/molochv3/${daoChain}/${daoId}` }, + { label: 'Form Test', href: `/molochv3/${daoChain}/${daoId}/formtest` }, + ]; + + return ( + + } + > + + + + + + ); +}; diff --git a/apps/dev-sandbox/src/layout/HomeContainer.tsx b/apps/dev-sandbox/src/layout/HomeContainer.tsx new file mode 100644 index 00000000..a39f38ec --- /dev/null +++ b/apps/dev-sandbox/src/layout/HomeContainer.tsx @@ -0,0 +1,18 @@ +import { DHLayout } from '@daohaus/connect'; +import { Footer, H4 } from '@daohaus/ui'; +import { Outlet, useLocation } from 'react-router-dom'; + +export const HomeContainer = () => { + const location = useLocation(); + + return ( + DAOhaus Admin Tool} + pathname={location.pathname} + navLinks={[{ label: 'Hub', href: `/` }]} + footer={