Skip to content
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
Binary file modified .gitignore
Binary file not shown.
15 changes: 15 additions & 0 deletions apps/docs/.firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"projects": {
"default": "agprojects"
},
"targets": {
"agprojects": {
"hosting": {
"formik-form-builder": [
"formik-form-builder"
]
}
}
},
"etags": {}
}
108 changes: 86 additions & 22 deletions apps/docs/README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,105 @@
# Website
# Formik Form Builder

This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
**Formik Form Builder** is a configuration-driven form library built on **Formik**, **MUI** (Material UI or Joy UI), and **Yup**.
It allows you to create fully functional, validated forms using just a JSON configuration—no repetitive boilerplate.

---

## Features

- **Declarative & JSON-driven**: Define fields, layout, and validations via a single JSON object.
- **Built-in validation**: Supports Yup validation rules.
- **Multiple input types**: Text, Multi-text, Checkbox, Radio, Select, AutoComplete, Dropdown, etc.
- **Conditional rendering**: Show, hide, enable, or disable fields dynamically.
- **MUI & Joy UI styling**: Fully compatible with Material UI and Joy themes.
- **Extensible**: Add custom components, validation rules, or UI tweaks.

---

## Installation

```bash
yarn
npm install formik-form-builder
# or
yarn add formik-form-builder
```

## Local Development
**Peer dependencies**:
- `formik`
- `yup`
- `@mui/material` or `@mui/joy`

```bash
yarn start
```
---

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
## Quick Example

## Build
```tsx
import { Box, Button } from "@mui/joy";
import { Formik } from "formik";
import { FormBuilder, InputTypes, useFormBuilder } from "formik-form-builder";

```bash
yarn build
function FullNameForm() {
const fields = [
{
field: "name",
type: InputTypes.TEXT,
initialValue: "",
label: "Full Name",
validation: { required: true, message: "Required" },
},
];

const { initialValues, yupSchemaValidation } = useFormBuilder(fields);

return (
<Formik
initialValues={initialValues}
validationSchema={yupSchemaValidation}
onSubmit={(values, actions) => {
console.log(values);
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}}
>
{({ values }) => (
<form>
<FormBuilder group="form" values={values} fields={fields} />
<Box mt={2} display="flex" justifyContent="center">
<Button type="submit">Continue</Button>
</Box>
</form>
)}
</Formik>
);
}

export default FullNameForm;
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.
---

## Deployment
## Usage

Using SSH:
- **`<FormikRenderer />`**: Quick setup for a full form, including Formik integration, validation, and submission.
- **`<FormBuilder />`**: Render form fields based on your field config.
- **`useFormBuilder`**: Generates `initialValues` and Yup schema from your field config for custom Formik setups.

```bash
USE_SSH=true yarn deploy
```
---

Not using SSH:
## Field Configuration

```bash
GIT_USER=<Your GitHub username> yarn deploy
```
Each field is defined by an object with:

- `field` – unique key
- `type` – input type (`TEXT`, `CHECKBOX`, `RADIO`, etc.)
- `initialValue` – default value
- `label` / `groupLabel` – display labels
- `validation` – Yup rules
- `options` – for choice-based fields
- `conditions` – dynamic show/hide/enable/disable rules
- `muiProps` – additional MUI/Joy props
- etc based on the needs

For more detail, please see the **full documentation**: [Docs](https://formik-form-builder.web.app/)

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
---
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
id: conditional-checkbox
title: Conditional checkbox
sidebar_label: Conditional Checkbox
sidebar_position: 5
sidebar_position: 4
description: Conditional checkbox example of checkbox using formik form builder.
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const giftWrapFields: FieldType[] = [
label: "Special Instructions",
initialValue: "",
conditions: {
action: ConditionAction.ENABLE,
action: ConditionAction.SHOW,
groups: [
{
group: "gift",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
id: to-do-list-checkbox
title: Todo List
sidebar_label: Todo List
sidebar_position: 4
sidebar_position: 3
description: Todo list example of checkbox using formik form builder.
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const confirmAction: FieldType[] = [
{
field: "confirmDelete",
type: InputTypes.RADIO,
initialValue:"",
groupLabel: "This action is irreversible. Are you sure you want to delete?",
validation: { required: true, message: "You must acknowledge." },
options: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ const GenderRadioButton = () => {
{
field: "gender",
type: InputTypes.RADIO,
initialValue: "",
groupLabel: "What's your gender?",
validation: { required: true, minLength: 1 },
validation: { required: true, message: "Please select your gender"},
options: [{ label: "Male", value: "Male" }, { label: "Female", value: "Female" }]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const Rating = () => {
field: "rating",
type: InputTypes.RADIO,
direction: "column",
initialValue:"",
groupLabel: "Please rate your experience",
options: [
{ label: "Very Bad", value: 1 },
Expand Down
17 changes: 17 additions & 0 deletions apps/docs/firebase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"hosting": {
"target": "formik-form-builder",
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
21 changes: 21 additions & 0 deletions apps/docs/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @type {import('jest').Config} */
module.exports = {
preset: "ts-jest",
testEnvironment: "jsdom",
roots: ["<rootDir>/src"],
testMatch: ["**/__tests__/**/*.test.[jt]s?(x)"],

moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
},

setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],

moduleNameMapper: {
"\\.(css|less|scss|sass)$": "identity-obj-proxy",
"^formik-form-builder$": "<rootDir>/../../packages/formik-form-builder/src",
},

transformIgnorePatterns: ["/node_modules/"],
};
2 changes: 2 additions & 0 deletions apps/docs/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '@testing-library/jest-dom';
global.alert = jest.fn();
12 changes: 11 additions & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
"typecheck": "tsc",
"test": "jest --config jest.config.js --passWithNoTests"
},
"dependencies": {
"@docusaurus/core": "3.8.1",
Expand All @@ -34,6 +35,15 @@
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/tsconfig": "3.8.1",
"@docusaurus/types": "3.8.1",
"@testing-library/jest-dom": "^6.7.0",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/jest": "29.5.12",
"@types/testing-library__user-event": "^4.2.0",
"identity-obj-proxy": "^3.0.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"ts-jest": "29.1.1",
"typescript": "~5.8.3"
},
"browserslist": {
Expand Down
102 changes: 102 additions & 0 deletions apps/docs/src/__tests__/auto-complete/basic-auto.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// BasicAuto.test.tsx
import React from "react";
import { Box, Button } from "@mui/joy";
import { FormikRenderer, InputTypes, type FieldType } from "formik-form-builder";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

// ---------------- Component ----------------
export function BasicAuto() {
const basic: FieldType[] = [
{
field: "countryName",
type: InputTypes.AUTO_COMPLETE,
label: "Which country are you from?",
initialValue: '',
validation: {
required: true,
message: "Please enter your country."
},
options: [
{ label: "United States", value: "US" },
{ label: "India", value: "IN" },
{ label: "United Kingdom", value: "GB" },
{ label: "Canada", value: "CA" },
{ label: "Australia", value: "AU" },
{ label: "Germany", value: "DE" },
{ label: "France", value: "FR" },
{ label: "Japan", value: "JP" },
{ label: "China", value: "CN" },
{ label: "Brazil", value: "BR" }
],
muiProps: { variant: "outlined" },
},
];

return (
<FormikRenderer
fields={basic}
onSubmit={(values, actions) => {
console.log(values);
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}}
>
<Box display="flex" justifyContent="center" alignItems="center">
<Button variant="solid" type="submit">
Continue
</Button>
</Box>
</FormikRenderer>
);
}

// ---------------- Test ----------------
describe("BasicAuto Form", () => {
beforeAll(() => {
global.alert = jest.fn();
});

beforeEach(() => {
jest.clearAllMocks();
});

it("should submit a random valid country from the autocomplete", async () => {
render(<BasicAuto />);
const input = screen.getByRole("combobox");

// Define the available options
const options = [
{ label: "United States", value: "US" },
{ label: "India", value: "IN" },
{ label: "United Kingdom", value: "GB" },
{ label: "Canada", value: "CA" },
{ label: "Australia", value: "AU" },
{ label: "Germany", value: "DE" },
{ label: "France", value: "FR" },
{ label: "Japan", value: "JP" },
{ label: "China", value: "CN" },
{ label: "Brazil", value: "BR" }
];

// Pick a random option
const selectedOption = options[Math.floor(Math.random() * options.length)];

// Type the label to filter autocomplete options
await userEvent.type(input, selectedOption.label);

// Wait for the option to appear and click it
const optionNode = await screen.findByText(selectedOption.label);
await userEvent.click(optionNode);

// Submit the form
await userEvent.click(screen.getByRole("button", { name: /Continue/i }));

// Assert alert contains the selected value
await waitFor(() => {
expect(global.alert).toHaveBeenCalledWith(
JSON.stringify({ countryName: selectedOption.value }, null, 2)
);
});
});
});
Loading