├── .eslintrc.js
├── .github
└── images
│ └── screen.png
├── .gitignore
├── .prettierrc.js
├── README.md
├── assets
├── 1280.png
├── 1400.png
├── 440.png
└── icon.png
├── package-lock.json
├── package.json
├── package
├── .gitignore
├── README.md
├── index.js
├── index.ts
├── package-lock.json
├── package.json
├── tsconfig-cjs.json
└── tsconfig.json
├── public
├── 128.png
├── 16.png
├── 48.png
├── devtools.html
├── favicon.ico
├── icons
│ ├── arrow-down.png
│ └── arrow-right.png
├── index.html
├── manifest.json
├── robots.txt
└── scripts
│ ├── background.js
│ ├── content.js
│ ├── devtools.js
│ └── war.js
├── src
├── __mock
│ └── messagesMock.ts
├── browser-api
│ └── browser.ts
├── components
│ ├── FormikStateValues
│ │ ├── FormikStateValues.tsx
│ │ ├── index.ts
│ │ └── style.module.scss
│ ├── FormikStateValuesList
│ │ ├── FormikStateValuesList.tsx
│ │ ├── index.ts
│ │ └── style.module.scss
│ ├── StatesList
│ │ ├── StatesList.tsx
│ │ ├── index.ts
│ │ └── style.module.scss
│ ├── StatusPanel
│ │ ├── StatusPanel.tsx
│ │ ├── index.ts
│ │ └── style.module.scss
│ └── index.ts
├── containers
│ ├── FormikDevtools.tsx
│ ├── index.ts
│ └── style.module.scss
├── examples
│ ├── ExtensionLiveExample
│ │ ├── ExtensionLiveExample.tsx
│ │ └── style.module.scss
│ ├── FormikForm
│ │ └── FormikForm.tsx
│ └── index.tsx
├── helpers
│ ├── renderValue.tsx
│ ├── utils.tsx
│ └── validate.tsx
├── hooks
│ └── useMessageLoad.ts
├── index.tsx
├── interfaces
│ ├── formikState.ts
│ ├── message.ts
│ └── values.ts
├── parsers
│ ├── changesParsers.ts
│ ├── parseHelpers.ts
│ └── valuesParsers.ts
├── react-app-env.d.ts
├── styles
│ ├── colorVariables.scss
│ └── resetStyles.scss
└── validation
│ └── messageValidation.ts
└── tsconfig.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | ecmaVersion: 2020,
5 | sourceType: 'module',
6 | },
7 | settings: {
8 | react: {
9 | version: 'detect',
10 | },
11 | },
12 | extends: [
13 | 'plugin:react/recommended',
14 | 'plugin:@typescript-eslint/recommended',
15 | 'prettier/@typescript-eslint',
16 | 'plugin:prettier/recommended',
17 | ],
18 | rules: {
19 | 'react/prop-types': 'off',
20 | '@typescript-eslint/explicit-module-boundary-types': 'off',
21 | '@typescript-eslint/no-var-requires': 'off',
22 | '@typescript-eslint/no-use-before-define': 0,
23 | },
24 | globals: {
25 | chrome: 'readonly',
26 | browser: 'readonly',
27 | document: 'readonly',
28 | window: 'readonly',
29 | console: 'readonly',
30 | CustomEvent: 'readonly',
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/.github/images/screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/.github/images/screen.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /build
3 | /build_zip
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 120,
6 | tabWidth: 4,
7 | };
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Formik Devtools
2 |
3 | [](https://badge.fury.io/js/formik-devtools-extension)
4 | [](https://img.shields.io/npm/dw/formik-devtools-extension)
5 |
6 | ## Browser extension for debugging [Formik](https://github.com/formium/formik) state.
7 |
8 | ## Check Demo [here](https://petrenkovitaliy.github.io/)
9 |
10 |
11 |
12 |
13 |
14 | ## 1. Installation:
15 |
16 | ### 1.1 install [Chrome extension](https://chrome.google.com/webstore/detail/formik-devtools/dadeefbkfcpaeacnafgceahcpjlfmmjj?hl=en) or [Firefox addon](https://addons.mozilla.org/en-GB/firefox/addon/formik-devtools/)
17 |
18 | ### 1.2 install package with [npm](https://www.npmjs.com/package/formik-devtools-extension):
19 |
20 | ```bash
21 | npm i formik-devtools-extension
22 | ```
23 |
24 | ## 2. Quick Start:
25 |
26 | ### 2.1 inside your component containing `` use:
27 |
28 | ```tsx
29 | import { withFormikDevtools } from "formik-devtools-extension";
30 |
31 | /* ... */
32 |
33 |
34 | {(formikProps) => {
35 | withFormikDevtools(formikProps);
36 | return
37 | }
38 |
39 | ```
40 |
41 | OR _(both methods are equivalent)_ :
42 |
43 | ```jsx
44 | // pass props with ReactElements
45 |
46 |
47 | {(formikProps) =>
48 | withFormikDevtools(formikProps,
49 |
50 |
51 |
)
52 | }
53 |
54 | ```
55 |
56 | ### 2.2 open page you want to monitor in browser
57 |
58 | ### 2.3 open browser devtools (F12) with **"Formik tab"**
59 |
60 | ## 3. API:
61 |
62 | - _withFormikDevtools_ passes Formik props on every update and sends values to extension.
63 |
64 | ```ts
65 | withFormikDevtools(formikProps: FormikProps, children?: any): children | undefined
66 | ```
67 |
68 | - If you have more than one Formik component, you should name them. _getFormikDevtools_ returns _withFormikDevtools_ entity with binded name.
69 |
70 | ```ts
71 | getFormikDevtools(formName: string): withFormikDevtools
72 | ```
73 |
--------------------------------------------------------------------------------
/assets/1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/assets/1280.png
--------------------------------------------------------------------------------
/assets/1400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/assets/1400.png
--------------------------------------------------------------------------------
/assets/440.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/assets/440.png
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/assets/icon.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "formik-devtools",
3 | "version": "0.3.1",
4 | "description": "Formik Developer Tools for debugging React form components",
5 | "private": true,
6 | "author": {
7 | "email": "vitaliy.ptt@gmail.com",
8 | "url": "https://github.com/petrenkoVitaliy",
9 | "name": "Vitaliy Petrenko"
10 | },
11 | "license": "MIT",
12 | "dependencies": {
13 | "classnames": "^2.3.1",
14 | "formik": "^2.2.3",
15 | "formik-devtools-extension": "^0.1.4",
16 | "node-sass": "^4.14.1",
17 | "prism-react-renderer": "^1.1.1",
18 | "react": "^16.13.1",
19 | "react-dom": "^16.13.1",
20 | "react-live": "^2.2.3",
21 | "react-scripts": "^3.4.3",
22 | "yup": "^0.29.3"
23 | },
24 | "scripts": {
25 | "start": "react-scripts start",
26 | "start-examples": "REACT_APP_ENV=test react-scripts start",
27 | "build": "react-scripts build",
28 | "build-examples": "REACT_APP_ENV=test react-scripts build",
29 | "eject": "react-scripts eject",
30 | "lint": "eslint \"src/**/*.{js,ts,tsx}\" --fix",
31 | "prettier": "prettier --write --print-width 120 --trailing-comma all \"**/*.+(ts|tsx|js|jsx)\""
32 | },
33 | "pre-commit": [
34 | "prettier",
35 | "lint"
36 | ],
37 | "eslintConfig": {
38 | "extends": "react-app"
39 | },
40 | "browserslist": {
41 | "production": [
42 | ">0.2%",
43 | "not dead",
44 | "not op_mini all"
45 | ],
46 | "development": [
47 | "last 1 chrome version",
48 | "last 1 firefox version",
49 | "last 1 safari version"
50 | ]
51 | },
52 | "devDependencies": {
53 | "@types/chrome": "0.0.125",
54 | "@types/classnames": "^2.2.10",
55 | "@types/firefox-webext-browser": "^82.0.0",
56 | "@types/node": "^12.12.62",
57 | "@types/react": "^16.9.49",
58 | "@types/react-dom": "^16.9.8",
59 | "@types/yup": "^0.29.8",
60 | "@typescript-eslint/eslint-plugin": "^4.0.0",
61 | "eslint-config-prettier": "^6.12.0",
62 | "eslint-plugin-prettier": "^3.1.4",
63 | "pre-commit": "^1.2.2",
64 | "prettier": "^2.1.2",
65 | "typescript": "^3.7.5"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/package/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /build
3 | /dist
4 |
--------------------------------------------------------------------------------
/package/README.md:
--------------------------------------------------------------------------------
1 | # Formik Devtools
2 |
3 | [](https://www.npmjs.com/package/formik-devtools-extension)
4 |
5 | ## Browser extension for debugging [Formik](https://github.com/formium/formik) state.
6 |
7 | ## Check Demo [here](https://petrenkovitaliy.github.io/)
8 |
9 |
10 |
11 |
12 |
13 | ## 1. Installation:
14 |
15 | ### 1.1 install [Chrome extension](https://chrome.google.com/webstore/detail/formik-devtools/dadeefbkfcpaeacnafgceahcpjlfmmjj?hl=en) or [Firefox addon](https://addons.mozilla.org/en-GB/firefox/addon/formik-devtools/)
16 |
17 | ### 1.2 install package with [npm](https://www.npmjs.com/package/formik-devtools-extension):
18 |
19 | ```bash
20 | npm i formik-devtools-extension
21 | ```
22 |
23 | ## 2. Quick Start:
24 |
25 | ### 2.1 inside your component containing `` use:
26 |
27 | ```tsx
28 | import { withFormikDevtools } from "formik-devtools-extension";
29 |
30 | /* ... */
31 |
32 | // pass just props
33 |
34 | {(formikProps) => {
35 | withFormikDevtools(formikProps);
36 | return
37 | }
38 |
39 | ```
40 |
41 | OR _(both methods are equivalent)_ :
42 |
43 | ```jsx
44 | // pass props with ReactElements
45 |
46 |
47 | {(formikProps) =>
48 | withFormikDevtools(formikProps,
49 |
50 |
51 |
)
52 | }
53 |
54 | ```
55 |
56 | you can also use it in functional components
57 |
58 | ```jsx
59 | import { useFormik } from 'formik';
60 | import { withFormikDevtools } from 'formik-devtools-extension';
61 |
62 | export const FunctionalComponent = () => {
63 | // initializing a form with a hook
64 | const formikForm = useFormik({
65 | initialValues: {
66 | firstFormValue: '',
67 | secondFormValue: {},
68 | },
69 | onSubmit,
70 | });
71 |
72 | // call it at each render
73 | withFormikDevtools(formikForm);
74 |
75 |
76 | return (
77 | // ..your form implementation
78 | )
79 | }
80 |
81 | ```
82 |
83 | ### 2.2 open page you want to monitor in browser
84 |
85 | ### 2.3 open browser devtools (F12) with **"Formik tab"**
86 |
87 | ## 3. API:
88 |
89 | - _withFormikDevtools_ passes Formik props on every update and sends values to extension.
90 |
91 | ```ts
92 | withFormikDevtools(formikProps: FormikProps, children?: any): children | undefined
93 | ```
94 |
95 | - If you have more than one Formik component, you should name them. _getFormikDevtools_ returns _withFormikDevtools_ entity with binded name.
96 |
97 | ```ts
98 | getFormikDevtools(formName: string): withFormikDevtools
99 | ```
100 |
--------------------------------------------------------------------------------
/package/index.js:
--------------------------------------------------------------------------------
1 | export * from './dist/cjs';
2 | export * from './dist/esm';
3 |
--------------------------------------------------------------------------------
/package/index.ts:
--------------------------------------------------------------------------------
1 | interface ExtendedWindow extends Window {
2 | FORMIK_DEVTOOLS?: (formikProps: any) => void;
3 | }
4 | const extendedWindow: ExtendedWindow | null = typeof window === 'undefined' ? null : window;
5 |
6 | const FormikDevtools = () => {
7 | let isInitialRender = true;
8 | return (formikProps: T, children?: M) => {
9 | if (extendedWindow?.FORMIK_DEVTOOLS) {
10 | extendedWindow.FORMIK_DEVTOOLS({ ...formikProps, __init: isInitialRender });
11 | if (isInitialRender) {
12 | isInitialRender = false;
13 | }
14 | }
15 | return children;
16 | };
17 | };
18 |
19 | export const withFormikDevtools = FormikDevtools();
20 |
21 | const getNamedFormikDevtools = (formName: string) => {
22 | let isInitialRender = true;
23 | return (formikProps: T, children?: M) => {
24 | if (extendedWindow?.FORMIK_DEVTOOLS) {
25 | extendedWindow.FORMIK_DEVTOOLS({ ...formikProps, __init: isInitialRender, __formName: formName });
26 | if (isInitialRender) {
27 | isInitialRender = false;
28 | }
29 | }
30 | return children;
31 | };
32 | };
33 |
34 | export const getFormikDevtools = getNamedFormikDevtools;
35 |
--------------------------------------------------------------------------------
/package/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "formik-devtools-extension",
3 | "version": "0.1.8",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "formik-devtools-extension",
9 | "version": "0.1.8",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "typescript": "^4.0.5"
13 | }
14 | },
15 | "node_modules/typescript": {
16 | "version": "4.0.5",
17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
18 | "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
19 | "dev": true,
20 | "bin": {
21 | "tsc": "bin/tsc",
22 | "tsserver": "bin/tsserver"
23 | },
24 | "engines": {
25 | "node": ">=4.2.0"
26 | }
27 | }
28 | },
29 | "dependencies": {
30 | "typescript": {
31 | "version": "4.0.5",
32 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
33 | "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
34 | "dev": true
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/package/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "formik-devtools-extension",
3 | "version": "0.1.8",
4 | "description": "Formik Developer Tools for debugging React form components",
5 | "author": {
6 | "email": "vitaliy.ptt@gmail.com",
7 | "url": "https://github.com/petrenkoVitaliy",
8 | "name": "Vitaliy Petrenko"
9 | },
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/petrenkoVitaliy/formik-devtools"
14 | },
15 | "main": "dist/cjs/index.js",
16 | "module": "dist/esm/index.js",
17 | "types": "dist/esm/index.d.ts",
18 | "files": [
19 | "dist/"
20 | ],
21 | "scripts": {
22 | "build": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json"
23 | },
24 | "devDependencies": {
25 | "typescript": "^4.0.5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/package/tsconfig-cjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "CommonJS",
5 | "outDir": "./dist/cjs"
6 | },
7 | }
--------------------------------------------------------------------------------
/package/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "lib": ["es2017", "es7", "es6", "dom"],
6 | "declaration": true,
7 | "outDir": "./dist/esm",
8 | "strict": true,
9 | "esModuleInterop": true
10 | },
11 | "exclude": [
12 | "node_modules",
13 | "dist"
14 | ]
15 | }
--------------------------------------------------------------------------------
/public/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/public/128.png
--------------------------------------------------------------------------------
/public/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/public/16.png
--------------------------------------------------------------------------------
/public/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/public/48.png
--------------------------------------------------------------------------------
/public/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/public/favicon.ico
--------------------------------------------------------------------------------
/public/icons/arrow-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/public/icons/arrow-down.png
--------------------------------------------------------------------------------
/public/icons/arrow-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrenkoVitaliy/formik-devtools/fcf130300e186b249ce0c7ea3f8f24dc7c21e65f/public/icons/arrow-right.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Formik Devtools
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Formik Devtools",
4 | "description": "Formik Developer Tools for debugging React form components",
5 | "version": "0.3.1",
6 | "devtools_page": "devtools.html",
7 | "icons": {
8 | "16": "16.png",
9 | "48": "48.png",
10 | "128": "128.png"
11 | },
12 | "content_scripts": [
13 | {
14 | "matches": [""],
15 | "js": ["scripts/content.js"],
16 | "run_at": "document_start",
17 | "all_frames": true
18 | }
19 | ],
20 | "background": {
21 | "scripts": ["scripts/background.js"]
22 | },
23 | "web_accessible_resources": ["scripts/war.js"],
24 | "content_security_policy": "script-src 'self' 'unsafe-eval' 'sha256-B+54yc4gCSgqrGNlJwxG6UsPq7pv1Is8KouyCs7rpXg='; object-src 'self'"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/scripts/background.js:
--------------------------------------------------------------------------------
1 | const BROWSER = chrome || browser;
2 |
3 | let portToDevtoolsScript = null;
4 | let portToContentScript = null;
5 |
6 | BROWSER.runtime.onConnect.addListener((port) => {
7 | if (port.name === 'devtools') {
8 | portToDevtoolsScript = port;
9 |
10 | portToDevtoolsScript.onMessage.addListener((request) => {
11 | if (request.tabId) {
12 | BROWSER.tabs.sendMessage(request.tabId, { message: 'start' });
13 | }
14 | });
15 | }
16 |
17 | if (portToDevtoolsScript && port.name === 'content') {
18 | portToContentScript = port;
19 |
20 | portToContentScript.onMessage.addListener((request) => {
21 | if (request.formikProps) {
22 | portToDevtoolsScript.postMessage(request);
23 | }
24 | });
25 | }
26 | });
27 |
28 | BROWSER.tabs.onUpdated.addListener((tabId, changeInfo) => {
29 | if (portToDevtoolsScript && changeInfo.status === 'complete') {
30 | portToDevtoolsScript.postMessage({ action: 'restart' });
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/public/scripts/content.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | const BROWSER = chrome || browser;
3 |
4 | function injectScript(file) {
5 | var s = document.createElement('script');
6 | s.setAttribute('type', 'text/javascript');
7 | s.setAttribute('src', file);
8 | document.documentElement.appendChild(s);
9 | }
10 | injectScript(BROWSER.extension.getURL('/scripts/war.js'));
11 |
12 | let messagesList = [];
13 | let port = undefined;
14 |
15 | function tryToConnect() {
16 | if (port) {
17 | sendMessages();
18 | }
19 | }
20 |
21 | function sendMessages() {
22 | messagesList.forEach((message) => {
23 | port.postMessage(message);
24 | });
25 | messagesList = [];
26 | }
27 |
28 | document.addEventListener('FORMIK_DEVTOOLS_EVENT', ({ detail: formikProps }) => {
29 | messagesList.push({ formikProps });
30 | tryToConnect();
31 | });
32 |
33 | BROWSER.runtime.onMessage.addListener(function (request) {
34 | if (request.message === 'start') {
35 | port = BROWSER.runtime.connect(BROWSER.runtime.id, { name: 'content' });
36 |
37 | sendMessages();
38 |
39 | console.info('[FORMIK:DEVTOOLS] connected to extension');
40 | }
41 | });
42 | })();
43 |
--------------------------------------------------------------------------------
/public/scripts/devtools.js:
--------------------------------------------------------------------------------
1 | const BROWSER = chrome || browser;
2 |
3 | if (BROWSER && BROWSER.devtools) {
4 | BROWSER.devtools.panels.create('Formik', '16.png', 'index.html');
5 | }
6 |
--------------------------------------------------------------------------------
/public/scripts/war.js:
--------------------------------------------------------------------------------
1 | const sendFormikDevtoolsMessage = function (props) {
2 | try {
3 | if (props) {
4 | document.dispatchEvent(
5 | new CustomEvent('FORMIK_DEVTOOLS_EVENT', {
6 | detail: JSON.stringify(props),
7 | }),
8 | );
9 | }
10 | } catch (ex) {
11 | console.log(ex);
12 | }
13 | };
14 |
15 | window.FORMIK_DEVTOOLS = sendFormikDevtoolsMessage;
16 |
--------------------------------------------------------------------------------
/src/__mock/messagesMock.ts:
--------------------------------------------------------------------------------
1 | const mockMessages = [
2 | JSON.stringify({
3 | values: {
4 | form1: 'form1',
5 | },
6 | initialValues: { form1: 'form1' },
7 | errors: {},
8 | touched: {},
9 | dirty: false,
10 | __init: true,
11 | __formName: 'form1',
12 | }),
13 | JSON.stringify({
14 | values: {
15 | form1: 'form2',
16 | },
17 | initialValues: { form1: 'form2' },
18 | errors: {},
19 | touched: {},
20 | dirty: false,
21 | __init: true,
22 | __formName: 'form2',
23 | }),
24 | JSON.stringify({
25 | values: {
26 | form1: 'unn',
27 | },
28 | initialValues: { form1: 'unn' },
29 | errors: {},
30 | touched: {},
31 | __init: true,
32 | dirty: false,
33 | }),
34 | ];
35 | export { mockMessages };
36 |
--------------------------------------------------------------------------------
/src/browser-api/browser.ts:
--------------------------------------------------------------------------------
1 | const BROWSER = chrome || browser;
2 |
3 | const sendMessageInTabs = (portToBackgroundPage: browser.runtime.Port | chrome.runtime.Port) => {
4 | portToBackgroundPage.postMessage({
5 | tabId: BROWSER.devtools.inspectedWindow.tabId,
6 | });
7 | };
8 |
9 | export const addListenerToMessages = (callBack: (arg: any) => void) => {
10 | const portToBackgroundPage = BROWSER.runtime.connect(BROWSER.runtime.id, { name: 'devtools' });
11 |
12 | portToBackgroundPage.onMessage.addListener((msg: any) => {
13 | if (msg) {
14 | if (msg.action && msg.action === 'restart') {
15 | sendMessageInTabs(portToBackgroundPage);
16 | } else {
17 | callBack(msg);
18 | }
19 | }
20 | });
21 |
22 | sendMessageInTabs(portToBackgroundPage);
23 | };
24 |
25 | export const addListenerToExampleMessages = (callBack: (arg: any) => void) => {
26 | document.addEventListener('FORMIK_EXAMPLE_DEVTOOLS_EVENT', ({ detail: formikProps }: any) => {
27 | callBack({ formikProps });
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/src/components/FormikStateValues/FormikStateValues.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import classnames from 'classnames';
3 |
4 | import { IFormikValue } from '../../interfaces/formikState';
5 | import classNames from './style.module.scss';
6 | import { renderValue } from '../../helpers/renderValue';
7 | import { ValueType } from '../../interfaces/values';
8 |
9 | interface FormikStateValuesProps {
10 | formikStateValues: IFormikValue;
11 | fieldName: string;
12 | }
13 |
14 | export const FormikStateValues: React.FunctionComponent = (props) => {
15 | const [isCollapsed, setIsCollapsed] = useState(true);
16 |
17 | const { formikStateValues, fieldName } = props;
18 |
19 | const toggleCollapse = () => {
20 | setIsCollapsed(!isCollapsed);
21 | };
22 |
23 | const getColorClassName = (type: ValueType) => {
24 | switch (type) {
25 | case 'string':
26 | return classNames.stringValueColor;
27 | case 'number':
28 | return classNames.numberValueColor;
29 | case 'boolean':
30 | return classNames.booleanValueColor;
31 | case 'object':
32 | return classNames.objectValueColor;
33 | case 'undefined':
34 | return classNames.undefinedValueColor;
35 | case 'null':
36 | return classNames.nullValueColor;
37 | }
38 | };
39 |
40 | return (
41 |
47 |
48 |
49 | {isCollapsed ? (
50 |

51 | ) : (
52 |

53 | )}
54 | {fieldName}:
55 |
56 | {isCollapsed && (
57 |
58 | {formikStateValues.collapsedValue}
59 |
60 | )}
61 |
62 | {!isCollapsed && (
63 |
64 |
{renderValue(formikStateValues.value, classNames)}
65 |
66 | )}
67 |
68 | {formikStateValues.error &&
Error: {formikStateValues.error}
}
69 | {formikStateValues.touched &&
Touched
}
70 |
71 | );
72 | };
73 |
--------------------------------------------------------------------------------
/src/components/FormikStateValues/index.ts:
--------------------------------------------------------------------------------
1 | export { FormikStateValues } from './FormikStateValues';
2 |
--------------------------------------------------------------------------------
/src/components/FormikStateValues/style.module.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colorVariables.scss';
2 |
3 | .stateWrapper {
4 | width: 100%;
5 | font-family: 'Inconsolata', monospace;
6 | line-height: 17px;
7 | font-size: 14px;
8 | padding: 10px;
9 |
10 | border: 2px solid $backgroundColor;
11 | border-bottom: 10px solid $backgroundColor;
12 | border-left: 10px solid $backgroundColor;
13 |
14 | &.error {
15 | border: 2px solid $errorColor;
16 | border-bottom: 10px solid $errorColor;
17 | }
18 |
19 | &.touched {
20 | border-left: 10px solid $touchedColor;
21 | }
22 |
23 | .collapsedWrap {
24 | font-family: inherit;
25 | display: flex;
26 | justify-content: flex-start;
27 | align-items: flex-start;
28 | cursor: pointer;
29 |
30 | .name {
31 | font-family: inherit;
32 | color: $fieldName;
33 | display: flex;
34 | align-items: center;
35 | cursor: pointer;
36 |
37 | border-bottom: 1px solid $backgroundColor;
38 |
39 | &:hover {
40 | border-bottom: 1px solid $fieldName;
41 | }
42 |
43 | > img {
44 | width: 10px;
45 | height: 10px;
46 | margin-right: 10px;
47 | background: $fieldName;
48 | padding: 2px;
49 | border-radius: 50%;
50 | box-sizing: content-box;
51 | }
52 |
53 | font-weight: bold;
54 | }
55 |
56 | .collapsedValue {
57 | font-family: inherit;
58 | margin-left: 10px;
59 | color: $collapsedColor;
60 |
61 | &.stringValueColor {
62 | color: $stringValueColor;
63 | }
64 | &.numberValueColor {
65 | color: $numberValueColor;
66 | }
67 | &.booleanValueColor {
68 | color: $booleanValueColor;
69 | }
70 | &.objectValueColor {
71 | color: $objectValueColor;
72 | }
73 | &.undefinedValueColor {
74 | color: $defaultValueColor;
75 | }
76 | &.nullValueColor {
77 | color: $nullValueColor;
78 | }
79 | }
80 | }
81 |
82 | .datailedValue {
83 | .value {
84 | color: $defaultValueColor;
85 |
86 | margin-left: 40px;
87 |
88 | .string {
89 | color: $stringValueColor;
90 | }
91 | .number {
92 | color: $numberValueColor;
93 | }
94 | .boolean {
95 | color: $booleanValueColor;
96 | }
97 | .null {
98 | color: $nullValueColor;
99 | }
100 |
101 | .objectProp {
102 | color: $objectValueColor;
103 | }
104 |
105 | .inline {
106 | display: flex;
107 | justify-content: flex-start;
108 | }
109 |
110 | span {
111 | color: $symbolColor;
112 | }
113 | }
114 | }
115 |
116 | .errorText {
117 | margin-top: 10px;
118 | color: $errorColor;
119 | margin-left: 0px;
120 | font-weight: bold;
121 | font-size: 14px;
122 | }
123 |
124 | .touchedText {
125 | margin-top: 10px;
126 | color: $touchedColor;
127 | margin-left: 0px;
128 | font-size: 14px;
129 | font-weight: bold;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/components/FormikStateValuesList/FormikStateValuesList.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { IFormikValues } from '../../interfaces/formikState';
4 | import { FormikStateValues } from '../FormikStateValues';
5 | import classNames from './style.module.scss';
6 |
7 | interface FormikStateValuesListProps {
8 | formikState: IFormikValues;
9 | }
10 |
11 | export const FormikStateValuesList: React.FunctionComponent = (props) => {
12 | const { formikState } = props;
13 |
14 | return (
15 |
16 | {Object.entries(formikState).map(([fieldName, state]) => (
17 |
18 | ))}
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/src/components/FormikStateValuesList/index.ts:
--------------------------------------------------------------------------------
1 | export { FormikStateValuesList } from './FormikStateValuesList';
2 |
--------------------------------------------------------------------------------
/src/components/FormikStateValuesList/style.module.scss:
--------------------------------------------------------------------------------
1 | .statesList {
2 | width: 100%;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/StatesList/StatesList.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import { IFormikDetailedState } from '../../interfaces/formikState';
3 | import classnames from 'classnames';
4 | import classNames from './style.module.scss';
5 |
6 | interface StatesListProps {
7 | formikStates: IFormikDetailedState[];
8 | currentStep: number;
9 | isFormsList: boolean;
10 | selectStep: (step: number) => void;
11 | }
12 |
13 | export const StatesList: React.FunctionComponent = (props) => {
14 | const { formikStates, currentStep, selectStep, isFormsList } = props;
15 |
16 | const handleSelectStep = (step: number) => () => selectStep(step);
17 |
18 | const reversedStates = useMemo(() => [...formikStates].reverse(), [formikStates]);
19 |
20 | return (
21 |
22 | {reversedStates.map((state, index) => (
23 |
30 |
{formikStates.length - index}:
31 |
32 | {formikStates.length - index === 1 ? '' : state.changed.composedChangedString}
33 |
34 |
35 | ))}
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/src/components/StatesList/index.ts:
--------------------------------------------------------------------------------
1 | export { StatesList } from './StatesList';
2 |
--------------------------------------------------------------------------------
/src/components/StatesList/style.module.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colorVariables.scss';
2 |
3 | .statesList {
4 | font-family: 'Inconsolata', monospace;
5 | width: 100%;
6 | height: calc(100% - 300px);
7 | overflow-y: auto;
8 |
9 | &.withSelect {
10 | height: calc(100% - 330px);
11 | }
12 |
13 | .stateItem {
14 | font-size: 16px;
15 | user-select: none;
16 | &.selected {
17 | background: $selectedStateItem;
18 | }
19 |
20 | height: 40px;
21 | display: flex;
22 | align-items: center;
23 | padding: 0 10px;
24 |
25 | border-top: 2px solid $stateItemBorder;
26 | color: $stateItemText;
27 | background: $stateItem;
28 |
29 | .step {
30 | color: $stateItemStepText;
31 | width: 20px;
32 | text-align: right;
33 | margin-right: 10px;
34 | font-weight: bold;
35 | }
36 |
37 | .composedChanges {
38 | white-space: nowrap;
39 | overflow: hidden;
40 | text-overflow: ellipsis;
41 | }
42 |
43 | cursor: pointer;
44 | &:hover {
45 | background: $hoveredStateItem;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/StatusPanel/StatusPanel.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classnames from 'classnames';
3 |
4 | import { IFormikDetailedState } from '../../interfaces/formikState';
5 |
6 | import classNames from './style.module.scss';
7 |
8 | interface StatusPanelProps {
9 | currentStep: number;
10 | steps: number;
11 | formsList: string[];
12 | selectedForm: string;
13 | currentState?: IFormikDetailedState;
14 |
15 | handleSelectForm: (formName: string) => void;
16 | setNextStep: () => void;
17 | setPreviousStep: () => void;
18 | }
19 |
20 | export const StatusPanel: React.FunctionComponent = (props) => {
21 | const {
22 | currentStep,
23 | steps,
24 | setNextStep,
25 | setPreviousStep,
26 | currentState,
27 | formsList,
28 | selectedForm,
29 | handleSelectForm,
30 | } = props;
31 |
32 | const handleFormChange = (event: React.ChangeEvent) => {
33 | handleSelectForm(event.target.value);
34 | };
35 |
36 | return (
37 | 1 })}>
38 | {formsList.length > 1 && (
39 |
40 |
Current Form:
41 |
48 |
49 | )}
50 |
51 |
52 | Previous
53 |
54 |
55 | {currentStep} / {steps}
56 |
57 |
58 | Next
59 |
60 |
61 |
62 | {currentState && (
63 |
64 | {currentState.changed.composedChangedString &&
Changed:
}
65 |
66 | {currentState.changed.changedProperties && (
67 |
68 |
69 | properties: {currentState.changed.changedProperties}
70 |
71 |
72 | )}
73 | {currentState.changed.changedValues && (
74 |
75 |
76 | values: {currentState.changed.changedValues}
77 |
78 |
79 | )}
80 | {currentState.changed.changedErrors && (
81 |
82 |
83 | errors: {currentState.changed.changedErrors}
84 |
85 |
86 | )}
87 | {currentState.changed.changedTouches && (
88 |
89 |
90 | touched: {currentState.changed.changedTouches}
91 |
92 |
93 | )}
94 |
95 | )}
96 |
97 | );
98 | };
99 |
--------------------------------------------------------------------------------
/src/components/StatusPanel/index.ts:
--------------------------------------------------------------------------------
1 | export { StatusPanel } from './StatusPanel';
2 |
--------------------------------------------------------------------------------
/src/components/StatusPanel/style.module.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colorVariables.scss';
2 |
3 | .statusPanel {
4 | font-family: 'Inconsolata', monospace;
5 | color: $statusPanelText;
6 | background: $statusPanel;
7 | height: 300px;
8 | width: 100%;
9 | padding: 5px 10px;
10 |
11 | &.withSelect {
12 | height: 330px;
13 | }
14 |
15 | .selectWrapper {
16 | width: 100%;
17 | height: 30px;
18 |
19 | display: flex;
20 | justify-content: flex-start;
21 | align-items: center;
22 |
23 | .selectLabel {
24 | width: 120px;
25 | }
26 |
27 | > select {
28 | -moz-appearance: none;
29 | flex-grow: 1;
30 |
31 | height: 100%;
32 |
33 | font-family: 'Inconsolata', monospace;
34 | font-size: 16px;
35 | cursor: pointer;
36 | border-radius: 10px;
37 | background: $statusPanelButton;
38 | color: $statusPanelText;
39 | border: 2px solid $statusPanelBorder;
40 |
41 | &:focus {
42 | outline: none;
43 | }
44 | }
45 | }
46 |
47 | .controls {
48 | display: flex;
49 | justify-content: space-between;
50 | align-items: center;
51 | width: 100%;
52 | height: 50px;
53 |
54 | .button {
55 | font-size: 16px;
56 | user-select: none;
57 | height: 30px;
58 |
59 | display: flex;
60 | justify-content: center;
61 | align-items: center;
62 |
63 | padding: 10px;
64 | box-sizing: border-box;
65 | border: 2px solid $statusPanelBorder;
66 | border-radius: 10px;
67 | cursor: pointer;
68 |
69 | background: $statusPanelButton;
70 | color: $statusPanelButtonText;
71 |
72 | &:hover {
73 | background: $statusPanelButtonHovered;
74 | }
75 | }
76 |
77 | .steps {
78 | user-select: none;
79 | font-size: 20px;
80 | font-weight: bold;
81 | }
82 | }
83 |
84 | .changedStatusesList {
85 | margin-top: 20px;
86 | width: 100%;
87 | height: 210px;
88 | overflow-y: auto;
89 |
90 | .title {
91 | font-size: 16px;
92 | font-weight: bold;
93 | margin-bottom: 10px;
94 | }
95 |
96 | .changedStatus {
97 | margin-bottom: 10px;
98 |
99 | display: flex;
100 | justify-content: space-between;
101 | align-items: center;
102 |
103 | .statusAttr {
104 | font-size: 15px;
105 |
106 | > span {
107 | font-weight: bold;
108 | }
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './StatusPanel';
2 | export * from './FormikStateValuesList';
3 | export * from './FormikStateValues';
4 |
--------------------------------------------------------------------------------
/src/containers/FormikDevtools.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useMemo, useState } from 'react';
2 |
3 | import { useMessageLoad } from '../hooks/useMessageLoad';
4 | // import { useMessageLoadMOCK } from '../hooks/useMessageLoad';
5 |
6 | import { FormikStateValuesList, StatusPanel } from '../components';
7 | import { ChangedState, IFormikDetailedState } from '../interfaces/formikState';
8 |
9 | import { getChangedInitialProps, getChangedProps } from '../parsers/changesParsers';
10 | import { parseValues } from '../parsers/valuesParsers';
11 | import classNames from './style.module.scss';
12 | import { StatesList } from '../components/StatesList/StatesList';
13 |
14 | interface FormikDevtoolsProps {
15 | example?: boolean;
16 | }
17 |
18 | interface FormikFormsStates {
19 | [key: string]: IFormikDetailedState[];
20 | }
21 |
22 | const EMPTY_FORM_NAME = 'unnamed_form';
23 |
24 | const FormikDevtools: React.FunctionComponent = ({ example }) => {
25 | const [formikStates, setFormikStates] = useState({ [EMPTY_FORM_NAME]: [] });
26 | const [currentForm, setCurrentForm] = useState(EMPTY_FORM_NAME);
27 |
28 | const [currentStep, setCurrentStep] = useState(-1);
29 | const { message: newMessage } = useMessageLoad();
30 |
31 | useEffect(() => {
32 | if (!example) {
33 | console.log(formikStates);
34 | }
35 | }, [formikStates, example]);
36 |
37 | useEffect(() => {
38 | if (newMessage) {
39 | const formName = newMessage.__formName || EMPTY_FORM_NAME;
40 | const { state, formProps } = parseValues(newMessage);
41 |
42 | let savedStates = formikStates[formName] || [];
43 |
44 | if (state) {
45 | let changed: ChangedState | null = null;
46 |
47 | if (formProps) {
48 | savedStates = [];
49 | changed = getChangedInitialProps(formProps, state) || {};
50 | } else {
51 | if (savedStates[savedStates.length - 1]) {
52 | changed = getChangedProps(state, savedStates[savedStates.length - 1].state);
53 | }
54 | }
55 |
56 | if (changed) {
57 | const newFormikStates = { ...formikStates, [formName]: [...savedStates, { state, changed }] };
58 | setFormikStates(newFormikStates);
59 | if (currentForm === formName) {
60 | setCurrentStep(savedStates.length);
61 | }
62 | if (currentForm === EMPTY_FORM_NAME && formName !== EMPTY_FORM_NAME) {
63 | setCurrentForm(formName);
64 | setCurrentStep(savedStates.length);
65 | }
66 | }
67 | }
68 | }
69 | // eslint-disable-next-line
70 | }, [newMessage]);
71 |
72 | const formsList = useMemo(() => Object.keys(formikStates).filter((formName) => !!formikStates[formName].length), [
73 | formikStates,
74 | ]);
75 |
76 | const setNextStep = () => {
77 | if (currentStep < formikStates[currentForm].length - 1 && currentStep > -1) {
78 | setCurrentStep(currentStep + 1);
79 | }
80 | };
81 |
82 | const setPreviousStep = () => {
83 | if (currentStep > 0) {
84 | setCurrentStep(currentStep - 1);
85 | }
86 | };
87 |
88 | const setStep = (step: number) => {
89 | if (step >= 0 && step < formikStates[currentForm].length) {
90 | setCurrentStep(step);
91 | }
92 | };
93 |
94 | const handleSelectForm = (formName: string) => {
95 | if (formikStates[formName]) {
96 | setCurrentForm(formName);
97 | setCurrentStep(formikStates[formName].length - 1);
98 | }
99 | };
100 |
101 | return (
102 |
103 |
104 | {currentStep > -1 && formikStates[currentForm][currentStep] && (
105 |
106 | )}
107 |
108 |
109 | -1 && formikStates[currentForm][currentStep]
119 | ? formikStates[currentForm][currentStep]
120 | : undefined
121 | }
122 | />
123 | 1}
125 | formikStates={formikStates[currentForm]}
126 | currentStep={currentStep}
127 | selectStep={setStep}
128 | />
129 |
130 |
131 | );
132 | };
133 |
134 | export default FormikDevtools;
135 |
--------------------------------------------------------------------------------
/src/containers/index.ts:
--------------------------------------------------------------------------------
1 | export { default as FormikDevtools } from './FormikDevtools';
2 |
--------------------------------------------------------------------------------
/src/containers/style.module.scss:
--------------------------------------------------------------------------------
1 | @import '../styles/colorVariables.scss';
2 |
3 | .devtoolsWrapper {
4 | display: flex;
5 | justify-content: space-between;
6 | width: 100%;
7 | height: 100vh;
8 | background: $backgroundColor;
9 |
10 | .body {
11 | width: 60%;
12 | overflow: auto;
13 | }
14 |
15 | .sidePanel {
16 | min-width: 250px;
17 | width: 40%;
18 | background: $sidePanel;
19 | border-left: 1px solid $statusPanelBorder;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/examples/ExtensionLiveExample/ExtensionLiveExample.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import dracula from 'prism-react-renderer/themes/dracula';
3 |
4 | import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live';
5 |
6 | import { FormikDevtools } from '../../containers';
7 | import { FormikForm, FormikFormString, FormikFormStringScope } from '../FormikForm/FormikForm';
8 |
9 | import styles from './style.module.scss';
10 |
11 | const LOAD_TIMEOUT = 1;
12 | const IS_LIVE_RENDER = true;
13 |
14 | export const ExtensionLiveExample: React.FunctionComponent = () => {
15 | const [isLoaded, setIsLoaded] = useState(false);
16 |
17 | useEffect(() => {
18 | setTimeout(() => setIsLoaded(true), LOAD_TIMEOUT); // Formik renders before FormikDevtools...
19 | });
20 |
21 | return (
22 |
23 |
24 |
Formik Devtools live example
25 |
26 | Change the values in the form and track them with formik-devtools preview
27 |
28 |
*Right panel is imitating extension that could be downloaded from chrome's market
29 |
30 |
49 |
50 | {isLoaded ? (
51 | <>
52 |
Change values:
53 | {IS_LIVE_RENDER ? (
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ) : (
62 |
63 | )}
64 | >
65 | ) : null}
66 |
67 |
68 |
69 |
70 |
71 | );
72 | };
73 |
--------------------------------------------------------------------------------
/src/examples/ExtensionLiveExample/style.module.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colorVariables.scss';
2 |
3 | .exampleDevtoolsWrapper {
4 | display: flex;
5 | justify-content: space-between;
6 | width: 100%;
7 | height: 100vh;
8 | background: $statusPanelText;
9 |
10 | .extensionWrapper {
11 | width: 60%;
12 | }
13 |
14 | .formWrapper {
15 | height: 100%;
16 | overflow: scroll;
17 | font-family: 'Inconsolata', monospace;
18 | width: 40%;
19 | display: flex;
20 | flex-direction: column;
21 | padding: 20px;
22 |
23 | .links {
24 | display: flex;
25 | flex-direction: column;
26 | margin: 10px;
27 | border: 1px dotted $statusPanel;
28 | width: 167px;
29 | padding: 5px;
30 | }
31 |
32 | .editorWrapper {
33 | min-height: 400px;
34 | overflow: auto;
35 | }
36 |
37 | .fieldBlock {
38 | margin: 20px 0;
39 | user-select: none;
40 |
41 | input {
42 | margin-right: 10px;
43 | }
44 | > label {
45 | cursor: pointer;
46 | }
47 | > select {
48 | cursor: pointer;
49 | }
50 | }
51 |
52 | .subtitle {
53 | margin-bottom: 5px;
54 | }
55 |
56 | .error {
57 | color: $errorColor;
58 | font-weight: bold;
59 | margin-top: 10px;
60 | font-size: 14px;
61 | }
62 |
63 | button {
64 | font-size: 16px;
65 | user-select: none;
66 | height: 30px;
67 |
68 | display: flex;
69 | justify-content: center;
70 | align-items: center;
71 |
72 | padding: 10px;
73 | box-sizing: border-box;
74 | border: 2px solid $statusPanelBorder;
75 | border-radius: 10px;
76 | cursor: pointer;
77 |
78 | background: $statusPanelButton;
79 | color: $statusPanelButtonText;
80 |
81 | &:hover {
82 | background: $statusPanelButtonHovered;
83 | }
84 | margin-bottom: 20px;
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/examples/FormikForm/FormikForm.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as Yup from 'yup';
3 | import { Formik, Field, ErrorMessage } from 'formik';
4 | import { withFormikDevtools } from 'formik-devtools-extension';
5 |
6 | import styles from '../ExtensionLiveExample/style.module.scss';
7 |
8 | const options = [
9 | { id: 'NY', label: 'New York' },
10 | { id: 'SF', label: 'San Francisco' },
11 | { id: 'BA', label: 'Baltimore' },
12 | { id: 'OTHER', label: 'another...' },
13 | ];
14 |
15 | const validationSchema = Yup.object().shape({
16 | terms: Yup.boolean(),
17 | jobType: Yup.array(),
18 | newsletter: Yup.boolean(),
19 | location: Yup.array().min(1, 'At least 1 option'),
20 | password: Yup.string().min(2, 'Too Short!').max(8, 'Too Long!').required('Required'),
21 | email: Yup.string().email('Invalid email').required('Required'),
22 | });
23 |
24 | export const FormikForm: React.FunctionComponent = () => {
25 | return (
26 | {
41 | setTimeout(() => {
42 | alert(JSON.stringify(values, null, 2));
43 | setSubmitting(false);
44 | }, 400);
45 | }}
46 | >
47 | {(formikProps) => {
48 | withFormikDevtools(formikProps);
49 |
50 | const { handleSubmit, handleChange, handleBlur, values, isSubmitting } = formikProps;
51 |
52 | return (
53 |
158 | );
159 | }}
160 |
161 | );
162 | };
163 |
164 | export const FormikFormStringScope = {
165 | Formik,
166 | withFormikDevtools,
167 | validationSchema,
168 | styles,
169 | ErrorMessage,
170 | Field,
171 | options,
172 | };
173 |
174 | export const FormikFormString = `
175 | /*
176 | import { withFormikDevtools } from 'formik-devtools-extension';
177 | ...
178 | */
179 |
180 | {
195 | setTimeout(() => {
196 | alert(JSON.stringify(values, null, 2));
197 | setSubmitting(false);
198 | }, 400);
199 | }}
200 | >
201 | {(formikProps) => {
202 | withFormikDevtools(formikProps); // use this callback to pass props to extension
203 |
204 | const {
205 | handleSubmit,
206 | handleChange,
207 | handleBlur,
208 | values,
209 | isSubmitting,
210 | } = formikProps;
211 |
212 | return (
213 |
335 | );
336 | }}
337 | ;
338 |
339 | `;
340 |
--------------------------------------------------------------------------------
/src/examples/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ExtensionLiveExample } from './ExtensionLiveExample/ExtensionLiveExample';
3 |
4 | interface ExtendedWindow extends Window {
5 | FORMIK_DEVTOOLS?: (formikProps: any) => void;
6 | }
7 | const extendedWindow = window as ExtendedWindow;
8 | extendedWindow.FORMIK_DEVTOOLS = (formikProps: any) => {
9 | document.dispatchEvent(
10 | new CustomEvent('FORMIK_EXAMPLE_DEVTOOLS_EVENT', {
11 | detail: JSON.stringify(formikProps),
12 | }),
13 | );
14 | };
15 |
16 | const Examples = () => {
17 | return ;
18 | };
19 |
20 | export default Examples;
21 |
--------------------------------------------------------------------------------
/src/helpers/renderValue.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const OBJECT_MARGIN = 5;
4 |
5 | interface ClassNamesMap {
6 | [key: string]: string;
7 | }
8 |
9 | const renderString = (value: string, classname: string, margin = 0, key = 0) => {
10 | return (
11 |
12 | {`'${value}'`}
13 |
14 | );
15 | };
16 |
17 | const renderNumber = (value: number, classname: string, margin = 0, key = 0) => {
18 | return (
19 |
20 | {`${value}`}
21 |
22 | );
23 | };
24 |
25 | const renderBoolean = (value: boolean, classname: string, margin = 0, key = 0) => {
26 | return (
27 |
28 | {`${value}`}
29 |
30 | );
31 | };
32 |
33 | const renderUndefined = (classname: string, margin = 0, key = 0) => {
34 | return (
35 |
36 | undefined
37 |
38 | );
39 | };
40 |
41 | const renderNull = (classname: string, margin = 0, key = 0) => {
42 | return (
43 |
44 | null
45 |
46 | );
47 | };
48 |
49 | const renderArray = (valuesArray: any[], classNamesMap: ClassNamesMap, margin: number, key = 0) => {
50 | return (
51 |
52 |
[
53 |
54 | {valuesArray.map((value, index) => renderValue(value, classNamesMap, margin + OBJECT_MARGIN, index))}
55 |
56 |
]
57 |
58 | );
59 | };
60 |
61 | const renderObject = (valuesObject: { [key: string]: any }, classNamesMap: ClassNamesMap, margin: number, key = 0) => {
62 | return (
63 |
64 |
{'{'}
65 |
66 | {Object.entries(valuesObject).map(([key, valueProp]) => {
67 | return (
68 |
69 |
{key}:
70 |
{renderValue(valueProp, classNamesMap, OBJECT_MARGIN)}
71 |
72 | );
73 | })}
74 |
75 |
{'}'}
76 |
77 | );
78 | };
79 |
80 | export const renderValue = (value: any, classNamesMap: ClassNamesMap, margin = 0, index = 0) => {
81 | if (typeof value === 'string') {
82 | return renderString(value, classNamesMap.string, margin, index);
83 | }
84 | if (typeof value === 'number') {
85 | return renderNumber(value, classNamesMap.number, margin, index);
86 | }
87 | if (typeof value === 'boolean') {
88 | return renderBoolean(value, classNamesMap.boolean, margin, index);
89 | }
90 | if (value === null) {
91 | return renderNull(classNamesMap.null, margin, index);
92 | }
93 | if (value === undefined) {
94 | return renderUndefined(classNamesMap.undefined, margin, index);
95 | }
96 | if (value.length !== undefined) {
97 | return renderArray(value, classNamesMap, margin, index);
98 | }
99 | if (typeof value === 'object') {
100 | return renderObject(value, classNamesMap, margin, index);
101 | }
102 | };
103 |
--------------------------------------------------------------------------------
/src/helpers/utils.tsx:
--------------------------------------------------------------------------------
1 | export const notEmpty = (value: TValue | null | undefined): value is TValue => {
2 | return value !== null && value !== undefined;
3 | };
4 |
--------------------------------------------------------------------------------
/src/helpers/validate.tsx:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 |
3 | export const validateValue = async (
4 | value: any | null | undefined,
5 | validation: yup.ObjectSchema,
6 | ): Promise => {
7 | if (value) {
8 | const validatedValue = await validation
9 | .validate(value)
10 | .then((value) => {
11 | return value;
12 | })
13 | .catch((value) => {
14 | console.error(value.errors);
15 | console.error(value.value);
16 | return undefined;
17 | });
18 |
19 | return validatedValue as TValue | undefined;
20 | }
21 |
22 | return undefined;
23 | };
24 |
--------------------------------------------------------------------------------
/src/hooks/useMessageLoad.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { addListenerToMessages, addListenerToExampleMessages } from '../browser-api/browser';
3 | import { validateValue } from '../helpers/validate';
4 | import { IMessage } from '../interfaces/message';
5 | import { messageValidation } from '../validation/messageValidation';
6 | import { mockMessages } from '../__mock/messagesMock';
7 |
8 | export const useMessageLoad = (isExample?: boolean) => {
9 | const [message, setMessage] = useState(undefined);
10 |
11 | useEffect(() => {
12 | if (isExample) {
13 | addListenerToExampleMessages(onMessage);
14 | } else {
15 | addListenerToMessages(onMessage);
16 | }
17 | // eslint-disable-next-line
18 | }, []);
19 |
20 | const onMessage = async (message: { formikProps: string }) => {
21 | try {
22 | if (message) {
23 | const validatedMessage = await validateValue(message.formikProps, messageValidation);
24 |
25 | if (validatedMessage) {
26 | setMessage(validatedMessage);
27 | }
28 | }
29 | } catch (ex) {
30 | console.log(ex);
31 | console.log(message);
32 | }
33 | };
34 |
35 | return { message };
36 | };
37 |
38 | export const useMessageLoadMOCK = () => {
39 | const [message, setMessage] = useState(undefined);
40 | const DELAY = 100;
41 |
42 | useEffect(() => {
43 | sendMessage(mockMessages, 0);
44 | // eslint-disable-next-line
45 | }, []);
46 |
47 | const sendMessage = async (mockMessagesList: string[], index: number) => {
48 | if (mockMessagesList[index]) {
49 | const validatedMessage = await validateValue(mockMessagesList[index], messageValidation);
50 |
51 | if (validatedMessage) {
52 | setMessage(validatedMessage);
53 | }
54 | setTimeout(() => sendMessage(mockMessagesList, index + 1), DELAY);
55 | }
56 | };
57 |
58 | return { message };
59 | };
60 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import { FormikDevtools } from './containers';
5 |
6 | import Examples from './examples';
7 |
8 | import './styles/resetStyles.scss';
9 |
10 | ReactDOM.render(
11 | {process.env.REACT_APP_ENV === 'test' ? : },
12 | document.getElementById('root'),
13 | );
14 |
--------------------------------------------------------------------------------
/src/interfaces/formikState.ts:
--------------------------------------------------------------------------------
1 | import { ValueType } from './values';
2 |
3 | export interface ChangedState {
4 | changedProperties?: string;
5 | changedValues?: string;
6 | changedErrors?: string;
7 | changedTouches?: string;
8 | composedChangedString?: string;
9 | }
10 |
11 | export interface IFormikDetailedState {
12 | state: IFormikState;
13 | changed: ChangedState;
14 | }
15 |
16 | export interface IFormikState {
17 | dirty: boolean;
18 | values: IFormikValues;
19 | }
20 |
21 | export interface InitialProperties {
22 | name: string;
23 | initialValues: {
24 | [key: string]: IFormikValue;
25 | };
26 | }
27 |
28 | export interface IFormikValues {
29 | [key: string]: IFormikValue;
30 | }
31 |
32 | export interface IFormikValue {
33 | value: any;
34 | collapsedValue: string | number | boolean | undefined | null;
35 | type: ValueType;
36 | error: string | undefined;
37 | touched: boolean;
38 | }
39 |
--------------------------------------------------------------------------------
/src/interfaces/message.ts:
--------------------------------------------------------------------------------
1 | import { ValueType } from './values';
2 |
3 | export interface IMessage {
4 | values: {
5 | [key: string]: ValueType;
6 | };
7 | initialValues: {
8 | [key: string]: ValueType;
9 | };
10 | initialErrors: {
11 | [key: string]: ValueType | ValueType[];
12 | };
13 | initialTouched: {
14 | [key: string]: ValueType | ValueType[];
15 | };
16 | errors: {
17 | [key: string]: ValueType | ValueType[];
18 | };
19 | touched: {
20 | [key: string]: ValueType | ValueType[];
21 | };
22 | dirty: boolean;
23 | __init: boolean;
24 | __formName?: string;
25 | }
26 |
--------------------------------------------------------------------------------
/src/interfaces/values.ts:
--------------------------------------------------------------------------------
1 | export type ValueType = 'string' | 'number' | 'boolean' | 'object' | 'undefined' | 'null';
2 |
--------------------------------------------------------------------------------
/src/parsers/changesParsers.ts:
--------------------------------------------------------------------------------
1 | import { notEmpty } from '../helpers/utils';
2 | import { ChangedState, IFormikState, IFormikValues, InitialProperties } from '../interfaces/formikState';
3 |
4 | export const getChangedProps = (prevState: IFormikState, currentState: IFormikState): ChangedState | null => {
5 | const composedChangesSet = new Set();
6 |
7 | const changedProperties = prevState.dirty !== currentState.dirty ? 'dirty' : '';
8 | changedProperties && composedChangesSet.add(changedProperties);
9 |
10 | const changedValuesSet = new Set();
11 | [
12 | ...getChangedValues(prevState.values, currentState.values),
13 | ...getChangedValues(currentState.values, prevState.values),
14 | ].forEach((changedValue) => {
15 | changedValuesSet.add(changedValue);
16 | composedChangesSet.add(changedValue);
17 | });
18 | const changedValues = [...changedValuesSet].join(', ');
19 |
20 | const changedErrorsSet = new Set();
21 | [
22 | ...getChangedErrors(prevState.values, currentState.values),
23 | ...getChangedErrors(currentState.values, prevState.values),
24 | ].forEach((changedValue) => {
25 | changedErrorsSet.add(changedValue);
26 | composedChangesSet.add(changedValue);
27 | });
28 | const changedErrors = [...changedErrorsSet].join(', ');
29 |
30 | const changedTouchedSet = new Set();
31 | [
32 | ...getChangedTouched(prevState.values, currentState.values),
33 | ...getChangedTouched(currentState.values, prevState.values),
34 | ].forEach((changedValue) => {
35 | changedTouchedSet.add(changedValue);
36 | composedChangesSet.add(changedValue);
37 | });
38 | const changedTouches = [...changedTouchedSet].join(', ');
39 | const composedChangedString = [...composedChangesSet].join(', ');
40 |
41 | const isSomethingHasChanged = Boolean(changedErrors || changedProperties || changedTouches || changedValues);
42 |
43 | return isSomethingHasChanged
44 | ? {
45 | changedErrors,
46 | changedProperties,
47 | changedTouches,
48 | changedValues,
49 | composedChangedString,
50 | }
51 | : null;
52 | };
53 |
54 | export const getChangedInitialProps = (
55 | prevState: InitialProperties,
56 | currentState: IFormikState,
57 | ): ChangedState | null => {
58 | const changedValuesSet = new Set();
59 | [
60 | ...getChangedValues(prevState.initialValues, currentState.values),
61 | ...getChangedValues(currentState.values, prevState.initialValues),
62 | ].forEach((changedValue) => changedValuesSet.add(changedValue));
63 |
64 | const changedValues = [...changedValuesSet].join(', ');
65 |
66 | return changedValues ? { changedValues } : null;
67 | };
68 |
69 | const getChangedValues = (prevFormikValues: IFormikValues, currentFormikValues: IFormikValues): string[] => {
70 | return Object.entries(prevFormikValues)
71 | .map(([fieldName, formikValue]) => {
72 | if (formikValue.value === undefined && !currentFormikValues[fieldName]?.type) {
73 | return fieldName;
74 | }
75 | if (isValuesChangedCheck(formikValue.value, currentFormikValues[fieldName]?.value)) {
76 | return fieldName;
77 | }
78 | return undefined;
79 | })
80 | .filter(notEmpty);
81 | };
82 |
83 | const getChangedErrors = (prevFormikValues: IFormikValues, currentFormikValues: IFormikValues): string[] => {
84 | return Object.entries(prevFormikValues)
85 | .map(([fieldName, formikValue]) => {
86 | if (isValuesChangedCheck(formikValue.error, currentFormikValues[fieldName]?.error)) {
87 | return fieldName;
88 | }
89 | return undefined;
90 | })
91 | .filter(notEmpty);
92 | };
93 |
94 | const getChangedTouched = (prevFormikValues: IFormikValues, currentFormikValues: IFormikValues): string[] => {
95 | return Object.entries(prevFormikValues)
96 | .map(([fieldName, formikValue]) => {
97 | if (isValuesChangedCheck(formikValue.touched, currentFormikValues[fieldName]?.touched)) {
98 | return fieldName;
99 | }
100 | return undefined;
101 | })
102 | .filter(notEmpty);
103 | };
104 |
105 | const isValuesChangedCheck = (prevValue: any, currentValue: any): boolean => {
106 | if (typeof prevValue === 'object') {
107 | if (prevValue === null) {
108 | return prevValue !== currentValue;
109 | }
110 | if (prevValue.length !== undefined) {
111 | return (
112 | !currentValue ||
113 | (prevValue as any[])
114 | .map((value, index) => isValuesChangedCheck(value, currentValue[index]))
115 | .some(Boolean)
116 | );
117 | } else {
118 | return (
119 | !currentValue ||
120 | Object.entries(prevValue as { [key: string]: any })
121 | .map(([key, value]) => {
122 | return isValuesChangedCheck(value, currentValue[key]);
123 | })
124 | .some(Boolean)
125 | );
126 | }
127 | }
128 | return prevValue !== currentValue;
129 | };
130 |
--------------------------------------------------------------------------------
/src/parsers/parseHelpers.ts:
--------------------------------------------------------------------------------
1 | import { ValueType } from '../interfaces/values';
2 |
3 | export const isValueTouchedCheck = (
4 | key: string,
5 | touchedValues: { [key: string]: ValueType | ValueType[] } = {},
6 | ): boolean => {
7 | return !!touchedValues[key];
8 | };
9 |
10 | export const getError = (
11 | key: string,
12 | errors: {
13 | [key: string]: ValueType | ValueType[];
14 | } = {},
15 | ): string | undefined => {
16 | const error = errors[key];
17 | switch (typeof error) {
18 | case 'object':
19 | return error === null ? undefined : JSON.stringify(error);
20 | case 'undefined':
21 | return undefined;
22 | default:
23 | return String(error);
24 | }
25 | };
26 |
27 | export const getCollapsedValue = (value: any): string | number | boolean | undefined | null => {
28 | switch (typeof value) {
29 | case 'object':
30 | return value === null ? null : JSON.stringify(value);
31 | case 'string':
32 | return `'${value}'`;
33 | case 'number':
34 | return value;
35 | case 'boolean':
36 | return value.toString();
37 | case 'undefined':
38 | return undefined;
39 | default:
40 | return undefined;
41 | }
42 | };
43 |
44 | export const getValueType = (value: any): ValueType => {
45 | switch (typeof value) {
46 | case 'object':
47 | return value === null ? 'null' : 'object';
48 | case 'string':
49 | return 'string';
50 | case 'number':
51 | return 'number';
52 | case 'boolean':
53 | return 'boolean';
54 | case 'undefined':
55 | return 'undefined';
56 | default:
57 | return 'undefined';
58 | }
59 | };
60 |
--------------------------------------------------------------------------------
/src/parsers/valuesParsers.ts:
--------------------------------------------------------------------------------
1 | import { IFormikState, InitialProperties } from '../interfaces/formikState';
2 | import { IMessage } from '../interfaces/message';
3 | import { getCollapsedValue, getError, getValueType, isValueTouchedCheck } from './parseHelpers';
4 |
5 | export const parseValues = (
6 | formikState: IMessage,
7 | ): { state: IFormikState | null; formProps: InitialProperties | null } => {
8 | if (formikState) {
9 | const formProps = parseInitialValues(formikState);
10 | const formikValuesShape: IFormikState = { values: {}, dirty: !!formikState.dirty };
11 |
12 | Object.entries(formikState.values).forEach(([valueName, value]) => {
13 | const type = getValueType(value);
14 | const error = getError(valueName, formikState.errors);
15 | const touched = isValueTouchedCheck(valueName, formikState.touched);
16 | const collapsedValue = getCollapsedValue(value);
17 |
18 | formikValuesShape.values[valueName] = { value, collapsedValue, error, touched, type };
19 | });
20 |
21 | const touchedAndErrorsValuesSet = new Set();
22 | [...Object.keys(formikState.touched || {}), ...Object.keys(formikState.errors)].forEach((key) =>
23 | touchedAndErrorsValuesSet.add(key),
24 | );
25 | [...touchedAndErrorsValuesSet].forEach((key) => {
26 | if (!formikValuesShape.values[key]) {
27 | const error = getError(key, formikState.errors);
28 | const touched = isValueTouchedCheck(key, formikState.touched);
29 |
30 | formikValuesShape.values[key] = {
31 | value: undefined,
32 | collapsedValue: 'undefined',
33 | error,
34 | touched,
35 | type: 'undefined',
36 | };
37 | }
38 | });
39 |
40 | return { state: formikValuesShape, formProps };
41 | }
42 |
43 | return { state: null, formProps: null };
44 | };
45 |
46 | const parseInitialValues = (formikState: IMessage): InitialProperties | null => {
47 | if (!formikState.__init) {
48 | return null;
49 | }
50 |
51 | const formProps: InitialProperties = {
52 | name: 'Form',
53 | initialValues: {},
54 | };
55 |
56 | Object.entries(formikState.initialValues).forEach(([valueName, value]) => {
57 | const type = getValueType(value);
58 | const error = getError(valueName, formikState.initialErrors);
59 | const touched = isValueTouchedCheck(valueName, formikState.initialTouched);
60 | const collapsedValue = getCollapsedValue(value);
61 |
62 | formProps.initialValues[valueName] = { value, collapsedValue, error, touched, type };
63 | });
64 |
65 | return formProps;
66 | };
67 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/styles/colorVariables.scss:
--------------------------------------------------------------------------------
1 | $backgroundColor: #282a36;
2 | $sidePanel: #282a36;
3 |
4 | $statusPanel: #282a36;
5 | $statusPanelText: #f8f8f2;
6 |
7 | $statusPanelButton: #353746;
8 | $statusPanelButtonText: #f8f8f2;
9 | $statusPanelButtonHovered: #424450;
10 | $statusPanelBorder: #424450;
11 |
12 | $stateItem: #282a36;
13 | $stateItemBorder: #424450;
14 | $selectedStateItem: #6272a4;
15 | $hoveredStateItem: #6272a4;
16 | $stateItemText: #f8f8f2;
17 | $stateItemStepText: #e66fb4;
18 |
19 | $fieldName: #e66fb4;
20 | $defaultValueColor: #f8f8f2;
21 | $stringValueColor: #e0a464;
22 | $numberValueColor: #00afff;
23 | $booleanValueColor: #bd93f9;
24 | $nullValueColor: #50fa7b;
25 | $objectValueColor: #f1fa8c;
26 | $symbolColor: #f8f8f2;
27 |
28 | $errorColor: #ff5555;
29 | $touchedColor: #8be9fd;
30 | $collapsedColor: #f8f8f2;
31 |
--------------------------------------------------------------------------------
/src/styles/resetStyles.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Inconsolata&display=swap');
2 |
3 | * {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 |
8 | ::-webkit-scrollbar {
9 | width: 7px;
10 | }
11 |
12 | ::-webkit-scrollbar-track {
13 | background: #282a36;
14 | }
15 |
16 | ::-webkit-scrollbar-thumb {
17 | background: #424450;
18 | }
19 |
20 | ::-webkit-scrollbar-thumb:hover {
21 | background: #353746;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/validation/messageValidation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 |
3 | import { IMessage } from '../interfaces/message';
4 |
5 | export const messageValidation = yup.object().shape({
6 | values: yup.object().required(),
7 | initialValues: yup.object().required(),
8 | errors: yup.object().required(),
9 | touched: yup.object().required(),
10 | initialErrors: yup.object(),
11 | initialTouched: yup.object(),
12 |
13 | dirty: yup.boolean().required(),
14 | __init: yup.boolean(),
15 | __formName: yup.string(),
16 | });
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "experimentalDecorators": true,
6 | "downlevelIteration": true,
7 | "allowJs": true,
8 | "skipLibCheck": true,
9 | "esModuleInterop": true,
10 | "allowSyntheticDefaultImports": true,
11 | "strict": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "module": "esnext",
14 | "moduleResolution": "node",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "noEmit": true,
18 | "jsx": "react",
19 | "typeRoots": ["node_modules/@types"]
20 | },
21 | "include": ["src"],
22 | "exclude": ["node_modules"]
23 | }
24 |
--------------------------------------------------------------------------------