├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .storybook ├── main.js └── preview.js ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FOLO-sm.png ├── FOLO.png ├── LICENSE.md ├── README.md ├── babel.config.js ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── auto-position │ ├── .eslintrc.js │ ├── LICENSE │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── src │ │ ├── AutoPositionCell.js │ │ └── index.js │ └── test │ │ └── AutoPositionCell.test.js ├── folo-forms │ ├── LICENSE │ ├── README.md │ ├── babel.config.js │ ├── foloForms-demo.gif │ ├── package.json │ ├── src │ │ └── index.js │ └── stories-legacy │ │ ├── README.md │ │ ├── _directories.js │ │ ├── mainBasic.stories.js │ │ └── mainCustom.stories.js ├── folo-layout │ ├── .eslintrc.js │ ├── LICENSE │ ├── README.md │ ├── babel.config.js │ ├── foloLayout-demo.gif │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── Grid.js │ │ │ └── GridItem.js │ │ ├── constants.js │ │ ├── index.js │ │ ├── positionStore.js │ │ └── stories │ │ │ ├── Implicit.stories.js │ │ │ ├── examples │ │ │ ├── ExplicitGrid.js │ │ │ └── ImplicitGrid.js │ │ │ └── explicit.stories.js │ └── test │ │ ├── Grid.test.js │ │ ├── GridItem.test.js │ │ └── __snapshots__ │ │ ├── Grid.test.js.snap │ │ └── GridItem.test.js.snap ├── folo-store │ ├── .eslintrc.js │ ├── LICENSE │ ├── README.MD │ ├── babel.config.js │ ├── package.json │ ├── src │ │ ├── Registry.ts │ │ └── index.ts │ ├── test │ │ ├── __snapshots__ │ │ │ ├── subscribe.test.js.snap │ │ │ └── updater.test.js.snap │ │ ├── subscribe.test.js │ │ └── updater.test.js │ └── tsconfig.json ├── folo-utils │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ └── src │ │ ├── index.js │ │ └── utils.js ├── folo-values │ ├── .eslintrc.js │ ├── LICENSE │ ├── README.md │ ├── babel.config.js │ ├── examples │ │ ├── AddressForm.js │ │ ├── BasicForm.js │ │ └── GroupToggle.js │ ├── foloValues-demo.gif │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── Core.js │ │ │ ├── Field.js │ │ │ └── Form.js │ │ ├── constants.js │ │ ├── index.js │ │ ├── utils │ │ │ └── cellRecognizer.js │ │ └── valuesStore.js │ ├── stories │ │ ├── Field.stories.js │ │ ├── FormsSubmit.stories.js │ │ └── FormsToggle.stories.js │ └── test │ │ ├── Field.test.js │ │ ├── cellRecognizer.test.js │ │ └── setupTests.js └── folo-withcontext │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ ├── index.js │ └── withcontext.jsx │ └── test │ └── withContext.test.jsx ├── scripts ├── babel-preset-folo-dev │ ├── package.json │ └── src │ │ ├── babel.config.js │ │ └── index.js ├── eslint-config-folo-react │ ├── config.eslintrc.js │ ├── index.js │ └── package.json ├── eslint-config-folo-ts │ ├── config.eslintrc.js │ ├── index.js │ └── package.json └── eslint-config-folo │ ├── package.json │ └── src │ ├── config.eslintrc.js │ └── index.js ├── tsconfig.base.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | [{*.cjs, *.html, *.js, *.json, *.jsx, *.mjs, *.rjson, *.ts}] 2 | end_of_line = lf 3 | trim_trailing_whitespace = true 4 | insert_final_newline = true 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | lib/ 4 | draft/ 5 | pubic/ 6 | .cache/ 7 | coverage/ 8 | packages/*/dist 9 | stories-legacy 10 | folo-withcontext 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint-config-folo"], 3 | }; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | coverage 4 | dist 5 | lib 6 | _gh-pages -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: [ 3 | "../packages/folo-values/stories/*.stories.@(js|jsx|ts|tsx)", 4 | "../packages/folo-layout/src/stories/*.stories.@(js|jsx|ts|tsx)", 5 | ], 6 | addons: ["@storybook/addon-links", "@storybook/addon-essentials"], 7 | }; 8 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | 2 | export const parameters = { 3 | actions: { argTypesRegex: "^on[A-Z].*" }, 4 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at mitchell@hamil.town. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | - [Node.js](https://nodejs.org/en/) >= v10 must be installed. 4 | - [Yarn](https://classic.yarnpkg.com/en/docs/install) 5 | 6 | ## Installation 7 | 8 | - Run `yarn` in the repository's root directory to install everything you need 9 | for development. 10 | - Run `yarn build` in the root directory to build the modules. 11 | 12 | ## Running Tests 13 | 14 | - `yarn test` to run the tests in each package available in workspace. 15 | 16 | ## Dealing with packages 17 | 18 | - Use `yarn workspace` followed by Folo package name in `package.json`. 19 | So, If you deal with `folo-layout` for example you can use: 20 | 21 | `yarn workspace @folo/layout add -D dotenv` 22 | 23 | ## Documentation 24 | 25 | - Run above installation steps and then 26 | - Run `yarn storybook` runs story for each modules. 27 | -------------------------------------------------------------------------------- /FOLO-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jalal246/folo/f9d5577f0e9e86bbbf5461a22041f583222d986b/FOLO-sm.png -------------------------------------------------------------------------------- /FOLO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jalal246/folo/f9d5577f0e9e86bbbf5461a22041f583222d986b/FOLO.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | folo live example 3 |

4 | 5 |
6 | Project designed and being developed to deal with forms 7 |
8 | 9 |

10 | 11 | folo build status 12 | 13 | 14 | folo code converge status 15 | 16 | 17 | folo MIT license 18 | 19 | 20 | PRs welcome! 21 | 22 |

23 | 24 | ## Packages Overview 25 | 26 | - [**@folo/store** ](https://github.com/jalal246/folo/tree/master/packages/folo-store) JS Store Holds & Helps Controlling Data In Forms. 27 | - [**@folo/values**](https://github.com/jalal246/folo/tree/master/packages/folo-values) 28 | React Data Form components 29 | - [**@folo/layout**](https://github.com/jalal246/folo/tree/master/packages/folo-layout) 30 | CSS Grid React components 31 | - [**@folo/forms**](https://github.com/jalal246/folo/tree/master/packages/folo-forms) 32 | Full package combined of `@folo/layout` & `@folo/values` for React 33 | 34 | ## Installation 35 | 36 | Run locally: 37 | 38 | - `git clone https://github.com/jalal246/folo.git` 39 | - `yarn` 40 | - `yarn build` 41 | - `yarn storybook` 42 | - Go to http://localhost:6006/ 43 | 44 | ## Contribution 45 | 46 | Need some company in this repo. PRs welcome! :point_right: [Contribute](Contribution.md) :blue_heart: 47 | 48 | ## License 49 | 50 | This project is licensed under the [MIT License](https://github.com/jalal246/folo/blob/master/LICENSE) 51 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ["@babel/preset-env", { targets: { node: "current" } }], 4 | ["@babel/preset-typescript"], 5 | ["@babel/preset-react"], 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/en/configuration.html 4 | */ 5 | 6 | module.exports = { 7 | // All imported modules in your tests should be mocked automatically 8 | // automock: false, 9 | 10 | // Stop running tests after `n` failures 11 | // bail: 0, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "C:\\Users\\jalal\\AppData\\Local\\Temp\\jest", 15 | 16 | // Automatically clear mock calls and instances between every test 17 | clearMocks: true, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | // collectCoverage: false, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: undefined, 24 | 25 | // The directory where Jest should output its coverage files 26 | coverageDirectory: "coverage", 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | // coveragePathIgnorePatterns: [ 30 | // "\\\\node_modules\\\\" 31 | // ], 32 | 33 | // Indicates which provider should be used to instrument code for coverage 34 | // coverageProvider: "babel", 35 | 36 | // A list of reporter names that Jest uses when writing coverage reports 37 | // coverageReporters: [ 38 | // "json", 39 | // "text", 40 | // "lcov", 41 | // "clover" 42 | // ], 43 | 44 | // An object that configures minimum threshold enforcement for coverage results 45 | // coverageThreshold: undefined, 46 | 47 | // A path to a custom dependency extractor 48 | // dependencyExtractor: undefined, 49 | 50 | // Make calling deprecated APIs throw helpful error messages 51 | // errorOnDeprecated: false, 52 | 53 | // Force coverage collection from ignored files using an array of glob patterns 54 | // forceCoverageMatch: [], 55 | 56 | // A path to a module which exports an async function that is triggered once before all test suites 57 | // globalSetup: undefined, 58 | 59 | // A path to a module which exports an async function that is triggered once after all test suites 60 | // globalTeardown: undefined, 61 | 62 | // A set of global variables that need to be available in all test environments 63 | // globals: {}, 64 | 65 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 66 | // maxWorkers: "50%", 67 | 68 | // An array of directory names to be searched recursively up from the requiring module's location 69 | // moduleDirectories: [ 70 | // "node_modules" 71 | // ], 72 | 73 | // An array of file extensions your modules use 74 | // moduleFileExtensions: [ 75 | // "js", 76 | // "json", 77 | // "jsx", 78 | // "ts", 79 | // "tsx", 80 | // "node" 81 | // ], 82 | 83 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 84 | // moduleNameMapper: {}, 85 | 86 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 87 | modulePathIgnorePatterns: ["folo-withcontext"], 88 | 89 | // Activates notifications for test results 90 | // notify: false, 91 | 92 | // An enum that specifies notification mode. Requires { notify: true } 93 | // notifyMode: "failure-change", 94 | 95 | // A preset that is used as a base for Jest's configuration 96 | // preset: undefined, 97 | 98 | // Run tests from one or more projects 99 | // projects: undefined, 100 | 101 | // Use this configuration option to add custom reporters to Jest 102 | // reporters: undefined, 103 | 104 | // Automatically reset mock state between every test 105 | // resetMocks: false, 106 | 107 | // Reset the module registry before running each individual test 108 | // resetModules: false, 109 | 110 | // A path to a custom resolver 111 | // resolver: undefined, 112 | 113 | // Automatically restore mock state between every test 114 | // restoreMocks: false, 115 | 116 | // The root directory that Jest should scan for tests and modules within 117 | // rootDir: undefined, 118 | 119 | // A list of paths to directories that Jest should use to search for files in 120 | // roots: [ 121 | // "" 122 | // ], 123 | 124 | // Allows you to use a custom runner instead of Jest's default test runner 125 | // runner: "jest-runner", 126 | 127 | // The paths to modules that run some code to configure or set up the testing environment before each test 128 | // setupFiles: [], 129 | 130 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 131 | // setupFilesAfterEnv: [], 132 | 133 | // The number of seconds after which a test is considered as slow and reported as such in the results. 134 | // slowTestThreshold: 5, 135 | 136 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 137 | // snapshotSerializers: [], 138 | 139 | // The test environment that will be used for testing 140 | // testEnvironment: "jest-environment-jsdom", 141 | 142 | // Options that will be passed to the testEnvironment 143 | // testEnvironmentOptions: {}, 144 | 145 | // Adds a location field to test results 146 | // testLocationInResults: false, 147 | 148 | // The glob patterns Jest uses to detect test files 149 | // testMatch: [ 150 | // "**/__tests__/**/*.[jt]s?(x)", 151 | // "**/?(*.)+(spec|test).[tj]s?(x)" 152 | // ], 153 | 154 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 155 | // testPathIgnorePatterns: [ 156 | // "\\\\node_modules\\\\" 157 | // ], 158 | 159 | // The regexp pattern or array of patterns that Jest uses to detect test files 160 | // testRegex: [], 161 | 162 | // This option allows the use of a custom results processor 163 | // testResultsProcessor: undefined, 164 | 165 | // This option allows use of a custom test runner 166 | // testRunner: "jasmine2", 167 | 168 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 169 | // testURL: "http://localhost", 170 | 171 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 172 | // timers: "real", 173 | 174 | // A map from regular expressions to paths to transformers 175 | // transform: undefined, 176 | 177 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 178 | // transformIgnorePatterns: [ 179 | // "\\\\node_modules\\\\", 180 | // "\\.pnp\\.[^\\\\]+$" 181 | // ], 182 | 183 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 184 | // unmockedModulePathPatterns: undefined, 185 | 186 | // Indicates whether each individual test should be reported during the run 187 | // verbose: undefined, 188 | 189 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 190 | // watchPathIgnorePatterns: [], 191 | 192 | // Whether to use watchman for file crawling 193 | // watchman: true, 194 | }; 195 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "independent", 3 | "npmClient": "yarn", 4 | "packages": [ 5 | "packages/auto-position", 6 | "packages/folo-store", 7 | "packages/folo-values", 8 | "packages/folo-layout", 9 | "packages/folo-forms", 10 | "packages/folo-utils" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "folo", 3 | "private": true, 4 | "workspaces": [ 5 | "packages/**", 6 | "scripts/**" 7 | ], 8 | "repository": "https://github.com/jalal246/folo", 9 | "author": "Jalal Maskoun jimmy002020@gmail.com>", 10 | "license": "MIT", 11 | "scripts": { 12 | "test": "jest", 13 | "build": "lerna run build", 14 | "prepublish": "yarn build", 15 | "publish": "lerna publish", 16 | "storybook": "start-storybook -p 6006", 17 | "gh-pages:clean": "rimraf _gh-pages", 18 | "gh-pages:build": "build-storybook -c .storybook -o _gh-pages", 19 | "gh-pages:publish": "gh-pages -d _gh-pages", 20 | "gh-pages": "yarn gh-pages:clean && yarn gh-pages:build && yarn gh-pages:publish" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.12.10", 24 | "@babel/preset-env": "^7.12.11", 25 | "@babel/preset-react": "^7.12.10", 26 | "@babel/preset-typescript": "^7.12.7", 27 | "@storybook/addon-actions": "^6.1.11", 28 | "@storybook/addon-essentials": "^6.1.11", 29 | "@storybook/addon-links": "^6.1.11", 30 | "@storybook/react": "^6.1.11", 31 | "@testing-library/jest-dom": "^5.11.6", 32 | "@testing-library/react": "^11.2.2", 33 | "@testing-library/user-event": "^13.1.8", 34 | "babel-jest": "^26.6.3", 35 | "babel-loader": "^8.2.2", 36 | "gh-pages": "^3.1.0", 37 | "jest": "^26.6.3", 38 | "lerna": "^3.22.1", 39 | "react": "^17.0.1", 40 | "react-dom": "^17.0.1", 41 | "rimraf": "^3.0.2", 42 | "typescript": "^4.1.3" 43 | }, 44 | "dependencies": { 45 | "builderz": "0.11.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/auto-position/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint-config-folo"], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/auto-position/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 20120-20201 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/auto-position/README.md: -------------------------------------------------------------------------------- 1 | // TODO 2 | -------------------------------------------------------------------------------- /packages/auto-position/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [["@babel/preset-env"]], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/auto-position/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@folo/auto-position", 3 | "version": "0.1.2", 4 | "source": "src/index.js", 5 | "main": "dist/autoPosition.min.cjs.js", 6 | "umd:main": "dist/autoPosition.min.umd.js", 7 | "author": "Jalal Maskoun ", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "jest", 11 | "build": "builderz --output=autoPosition" 12 | }, 13 | "homepage": "https://github.com/jalal246/folo/tree/master/packages/auto-position", 14 | "repository": "https://github.com/jalal246/folo", 15 | "files": [ 16 | "src", 17 | "dist", 18 | "LICENSE" 19 | ], 20 | "keywords": [ 21 | "@folo", 22 | "@folo/auto-position", 23 | "@folo/forms", 24 | "@folo/layout", 25 | "@folo/store", 26 | "@folo/utils", 27 | "@folo/values", 28 | "@folo/withcontext", 29 | "dflex", 30 | "react", 31 | "react-component", 32 | "css-grid", 33 | "css-layout", 34 | "form-builder-api", 35 | "dynamic-forms" 36 | ], 37 | "publishConfig": { 38 | "registry": "https://registry.npmjs.org/", 39 | "access": "public" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/auto-position/src/AutoPositionCell.js: -------------------------------------------------------------------------------- 1 | class AutoPositionCell { 2 | constructor() { 3 | // store cells number according to its name 4 | this.cellPositions = {}; 5 | 6 | this.biggestRowItem = 0; 7 | } 8 | 9 | /** 10 | * Auto set the row number 11 | * If we don't have row then take the highest value 12 | * depending on biggestRowItem which updated with each grid item 13 | * Otherwise set the row do you have and update biggestRowItem 14 | * 15 | * This helps to assign position value according to highest value 16 | * If we start from 10, the next will be 11 and so on. 17 | * 18 | * @param {object} GridItem - GridItem that should be register and calculated 19 | * @param {string} GridItem.key unique key generated in GridItem 20 | * @param {number} GridItem.row 21 | * @param {number} GridItem.toRow 22 | * @return {number} row position 23 | */ 24 | autoPosition({ key, row, toRow }) { 25 | const parseRow = parseInt(row, 10); 26 | const parseToRow = parseInt(toRow, 10); 27 | 28 | if (parseToRow && parseRow) { 29 | this.cellPositions[key] = parseRow; 30 | 31 | const bigger = parseToRow > parseRow ? parseToRow : parseRow; 32 | if (bigger > this.biggestRowItem) { 33 | this.biggestRowItem = bigger; 34 | } 35 | } else if (parseRow) { 36 | this.cellPositions[key] = parseRow; 37 | 38 | if (parseRow > this.biggestRowItem) { 39 | this.biggestRowItem = parseRow; 40 | } 41 | } else { 42 | this.biggestRowItem += 1; 43 | this.cellPositions[key] = this.biggestRowItem; 44 | 45 | if (parseToRow > this.biggestRowItem) { 46 | this.biggestRowItem = parseToRow; 47 | } 48 | } 49 | 50 | return this.cellPositions[key]; 51 | } 52 | } 53 | 54 | export default AutoPositionCell; 55 | -------------------------------------------------------------------------------- /packages/auto-position/src/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AutoPositionCell"; 2 | -------------------------------------------------------------------------------- /packages/auto-position/test/AutoPositionCell.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable operator-assignment */ 2 | import AutoPositionCell from "../src"; 3 | 4 | const autoPositionTest = new AutoPositionCell(); 5 | 6 | const key = () => new Date().getTime(); 7 | 8 | let POSITION; 9 | 10 | let EXPECTED_BIGGEST_ROW; 11 | 12 | let CELL_1; 13 | let EXPECTED_ROW_1; 14 | 15 | let CELL_2; 16 | let EXPECTED_ROW_2; 17 | 18 | let CELL_3; 19 | let EXPECTED_ROW_3; 20 | 21 | let CELL_4; 22 | let EXPECTED_ROW_4; 23 | 24 | let CELL_5; 25 | let EXPECTED_ROW_5; 26 | 27 | let CELL_6; 28 | let EXPECTED_ROW_6; 29 | 30 | let CELL_7; 31 | let EXPECTED_ROW_7; 32 | 33 | let CELL_8; 34 | let EXPECTED_ROW_8; 35 | 36 | let CELL_9; 37 | let EXPECTED_ROW_9; 38 | 39 | let CELL_10; 40 | let EXPECTED_ROW_10; 41 | 42 | describe("Testing AutoPositionCell Algorithm", () => { 43 | it("Works for known row", () => { 44 | CELL_1 = { 45 | key, 46 | row: 10, 47 | toRow: 0, 48 | }; 49 | EXPECTED_ROW_1 = 10; 50 | EXPECTED_BIGGEST_ROW = 10; 51 | 52 | POSITION = autoPositionTest.autoPosition(CELL_1); 53 | 54 | expect(EXPECTED_ROW_1).toBe(10); 55 | expect(EXPECTED_BIGGEST_ROW).toBe(10); 56 | 57 | expect(POSITION).toBe(EXPECTED_ROW_1); // 10 58 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); // 10 59 | }); 60 | 61 | it("Calculates row number prev_row + 1", () => { 62 | CELL_2 = { 63 | key, 64 | row: null, 65 | toRow: 0, 66 | }; 67 | EXPECTED_ROW_2 = CELL_1.row + 1; 68 | EXPECTED_BIGGEST_ROW = EXPECTED_ROW_2; 69 | 70 | POSITION = autoPositionTest.autoPosition(CELL_2); 71 | 72 | expect(EXPECTED_ROW_2).toBe(11); 73 | expect(EXPECTED_BIGGEST_ROW).toBe(11); 74 | 75 | expect(POSITION).toBe(EXPECTED_ROW_2); 76 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 77 | }); 78 | 79 | it("No change when passing the same value", () => { 80 | // same value in cell 2 81 | CELL_3 = { 82 | key, 83 | row: EXPECTED_ROW_2, // 11 84 | toRow: 0, 85 | }; 86 | EXPECTED_ROW_3 = CELL_3.row; // no change for the same value: 11 87 | // no change for EXPECTED_BIGGEST_ROW 88 | 89 | POSITION = autoPositionTest.autoPosition(CELL_3); 90 | 91 | expect(EXPECTED_ROW_3).toBe(11); 92 | 93 | expect(POSITION).toBe(EXPECTED_ROW_3); 94 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 95 | }); 96 | 97 | it("Assigns lower row value, to test biggest value", () => { 98 | CELL_4 = { 99 | key, 100 | row: 2, 101 | toRow: 0, 102 | }; 103 | EXPECTED_ROW_4 = CELL_4.row; 104 | // no change for EXPECTED_BIGGEST_ROW 105 | 106 | POSITION = autoPositionTest.autoPosition(CELL_4); 107 | 108 | expect(EXPECTED_ROW_4).toBe(2); 109 | 110 | expect(POSITION).toBe(EXPECTED_ROW_4); 111 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 112 | }); 113 | 114 | it("No row, the expected is not prev + 1, must be biggest +1", () => { 115 | CELL_5 = { 116 | key, 117 | row: null, 118 | toRow: 0, 119 | }; 120 | EXPECTED_ROW_5 = EXPECTED_BIGGEST_ROW + 1; 121 | EXPECTED_BIGGEST_ROW = EXPECTED_ROW_5; 122 | 123 | POSITION = autoPositionTest.autoPosition(CELL_5); 124 | 125 | expect(EXPECTED_ROW_5).toBe(12); 126 | expect(EXPECTED_BIGGEST_ROW).toBe(12); 127 | 128 | expect(POSITION).toBe(EXPECTED_ROW_5); 129 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 130 | }); 131 | 132 | it("No row, but toRow, the expected is prev + 1", () => { 133 | CELL_6 = { 134 | key, 135 | row: null, 136 | toRow: 200, 137 | }; 138 | EXPECTED_ROW_6 = EXPECTED_ROW_5 + 1; 139 | EXPECTED_BIGGEST_ROW = CELL_6.toRow; 140 | 141 | POSITION = autoPositionTest.autoPosition(CELL_6); 142 | 143 | expect(EXPECTED_ROW_6).toBe(13); 144 | expect(EXPECTED_BIGGEST_ROW).toBe(200); 145 | 146 | expect(POSITION).toBe(EXPECTED_ROW_6); 147 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 148 | }); 149 | 150 | it("No row, but lower toRow, biggest + 1", () => { 151 | CELL_7 = { 152 | key, 153 | row: null, 154 | toRow: 3, 155 | }; 156 | EXPECTED_ROW_7 = EXPECTED_BIGGEST_ROW + 1; 157 | EXPECTED_BIGGEST_ROW = EXPECTED_BIGGEST_ROW + 1; 158 | 159 | POSITION = autoPositionTest.autoPosition(CELL_7); 160 | 161 | expect(EXPECTED_ROW_7).toBe(201); 162 | expect(EXPECTED_BIGGEST_ROW).toBe(201); 163 | 164 | expect(POSITION).toBe(EXPECTED_ROW_7); 165 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 166 | }); 167 | 168 | it("Passes toRow & row both smaller than prev biggest", () => { 169 | CELL_8 = { 170 | key, 171 | row: 100, 172 | toRow: 3, 173 | }; 174 | EXPECTED_ROW_8 = CELL_8.row; 175 | // no change for EXPECTED_BIGGEST_ROW 176 | 177 | POSITION = autoPositionTest.autoPosition(CELL_8); 178 | 179 | expect(EXPECTED_ROW_8).toBe(100); 180 | 181 | expect(POSITION).toBe(EXPECTED_ROW_8); 182 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 183 | }); 184 | 185 | it("Passes toRow & row both but row bigger than biggest", () => { 186 | CELL_9 = { 187 | key, 188 | row: 205, 189 | toRow: 3, 190 | }; 191 | EXPECTED_ROW_9 = CELL_9.row; 192 | EXPECTED_BIGGEST_ROW = EXPECTED_ROW_9; 193 | 194 | POSITION = autoPositionTest.autoPosition(CELL_9); 195 | 196 | expect(EXPECTED_ROW_9).toBe(205); 197 | expect(EXPECTED_BIGGEST_ROW).toBe(205); 198 | 199 | expect(POSITION).toBe(EXPECTED_ROW_9); 200 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 201 | }); 202 | 203 | it("Passes toRow & row both but toRow bigger than biggest", () => { 204 | CELL_10 = { 205 | key, 206 | row: 88, 207 | toRow: 210, 208 | }; 209 | EXPECTED_ROW_10 = CELL_10.row; 210 | EXPECTED_BIGGEST_ROW = CELL_10.toRow; 211 | 212 | POSITION = autoPositionTest.autoPosition(CELL_10); 213 | 214 | expect(EXPECTED_ROW_10).toBe(88); 215 | expect(EXPECTED_BIGGEST_ROW).toBe(210); 216 | 217 | expect(POSITION).toBe(EXPECTED_ROW_10); 218 | expect(autoPositionTest.biggestRowItem).toBe(EXPECTED_BIGGEST_ROW); 219 | }); 220 | }); 221 | -------------------------------------------------------------------------------- /packages/folo-forms/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/folo-forms/README.md: -------------------------------------------------------------------------------- 1 | # 📋 @folo/forms 2 | 3 | > React components combined of @folo/layout & @folo/values 4 | 5 | 6 | [![NPM Version](https://img.shields.io/npm/v/@folo/forms.svg)](https://www.npmjs.com/package/@folo/forms) 7 | [![NPM Download](https://img.shields.io/npm/dt/@folo/forms.svg)](https://www.npmjs.com/package/@folo/forms) 8 | [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/react.svg)](https://www.npmjs.com/package/@folo/forms) 9 | [![npm bundle size (gzip)](https://img.shields.io/bundlephobia/minzip/react.svg)](https://www.npmjs.com/package/@folo/forms) 10 | [![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/jalal246/folo/blob/master/packages/folo-forms/LICENSE) 11 | [![CI](https://img.shields.io/github/workflow/status/jalal246/folo/CI)](https://github.com/jalal246/folo/tree/master) 12 | [![Codecov](https://img.shields.io/codecov/c/github/jalal246/folo.svg)](https://codecov.io/gh/jalal246/folo) 13 | 14 | 15 | ## Installation 16 | 17 | ```sh 18 | npm install @folo/forms 19 | ``` 20 | 21 | ## Overview 22 | 23 | `@folo/forms` has two major concepts defined in 24 | [Field](https://github.com/jalal246/folo/tree/master/packages/folo-values) 25 | to deal with data. And 26 | [Grid](https://github.com/jalal246/folo/tree/master/packages/folo-layout) for 27 | designing layout. 28 | 29 | ### Components 30 | 31 | ```js 32 | import { Form, Field, Grid, GridItem } from "@folo/forms"; 33 | ``` 34 | 35 | ## Contribution 😇 36 | 37 | If you have ideas or issues don't hesitate. You are always welcome. 38 | 39 | ## License 40 | 41 | This project is licensed under the [MIT License](https://github.com/jalal246/folo/blob/master/packages/folo-forms/LICENSE) 42 | -------------------------------------------------------------------------------- /packages/folo-forms/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [["@babel/preset-env"], ["@babel/preset-react"]], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/folo-forms/foloForms-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jalal246/folo/f9d5577f0e9e86bbbf5461a22041f583222d986b/packages/folo-forms/foloForms-demo.gif -------------------------------------------------------------------------------- /packages/folo-forms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@folo/forms", 3 | "version": "1.0.2", 4 | "source": "src/index.js", 5 | "main": "dist/foloForms.min.cjs.js", 6 | "umd:main": "dist/foloForms.min.umd.js", 7 | "author": "Jalal Maskoun ", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "jest", 11 | "build": "builderz --output=foloForms" 12 | }, 13 | "homepage": "https://github.com/jalal246/folo/tree/master/packages/folo-forms", 14 | "repository": "https://github.com/jalal246/folo", 15 | "bundledDependencies": [ 16 | "@folo/layout", 17 | "@folo/values" 18 | ], 19 | "peerDependencies": { 20 | "react": ">=16" 21 | }, 22 | "files": [ 23 | "src", 24 | "dist", 25 | "LICENSE" 26 | ], 27 | "keywords": [ 28 | "@folo", 29 | "@folo/auto-position", 30 | "@folo/forms", 31 | "@folo/layout", 32 | "@folo/store", 33 | "@folo/utils", 34 | "@folo/values", 35 | "@folo/withcontext", 36 | "dflex", 37 | "react", 38 | "react-component", 39 | "css-grid", 40 | "css-layout", 41 | "form-builder-api", 42 | "dynamic-forms" 43 | ], 44 | "publishConfig": { 45 | "registry": "https://registry.npmjs.org/", 46 | "access": "public" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/folo-forms/src/index.js: -------------------------------------------------------------------------------- 1 | import { Form, Field } from "@folo/values/src"; 2 | import { GridItem, Grid } from "@folo/layout/src"; 3 | 4 | export { Form, Grid, Field, GridItem }; 5 | -------------------------------------------------------------------------------- /packages/folo-forms/stories-legacy/README.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | Migrate stories 4 | -------------------------------------------------------------------------------- /packages/folo-forms/stories-legacy/_directories.js: -------------------------------------------------------------------------------- 1 | export const MAIN_APP = "folo-forms"; 2 | 3 | export const BASIC = "basic form"; 4 | export const CUSTOM = "using costum components"; 5 | -------------------------------------------------------------------------------- /packages/folo-forms/stories-legacy/mainBasic.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { storiesOf } from "@storybook/react"; 4 | import { action } from "@storybook/addon-actions"; 5 | 6 | import { MAIN_APP, BASIC } from "./_directories"; 7 | 8 | import { Folo, Form, Grid, Cell } from "../src"; 9 | 10 | storiesOf(`${MAIN_APP}/${BASIC}`, module) 11 | .add("form with auto-grid", () => ( 12 | 13 |
21 | 22 | 23 | 24 | 28 | 34 | 43 | 44 |
45 |
46 | )) 47 | .add("form with explicit grid", () => ( 48 | 49 |
57 | 64 | 65 | 66 | 67 | 77 |
78 |
79 | )); 80 | -------------------------------------------------------------------------------- /packages/folo-forms/stories-legacy/mainCustom.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { storiesOf } from "@storybook/react"; 4 | import { action } from "@storybook/addon-actions"; 5 | 6 | import TextField from "@material-ui/core/TextField"; 7 | import List from "@material-ui/core/List"; 8 | import ListItem from "@material-ui/core/ListItem"; 9 | import ListItemText from "@material-ui/core/ListItemText"; 10 | import Checkbox from "@material-ui/core/Checkbox"; 11 | import Button from "@material-ui/core/Button"; 12 | 13 | import { MAIN_APP, CUSTOM } from "./_directories"; 14 | 15 | import { Folo, Form, Grid, Cell } from "../src"; 16 | 17 | storiesOf(`${MAIN_APP}/${CUSTOM}`, module).add( 18 | "using component form material-ui", 19 | () => ( 20 | 21 |
27 | 28 | 35 | 36 | 37 | 38 | 44 | 45 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 67 | 68 |
69 |
70 | ) 71 | ); 72 | -------------------------------------------------------------------------------- /packages/folo-layout/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint-config-folo-react"], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/folo-layout/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/folo-layout/README.md: -------------------------------------------------------------------------------- 1 | # 📋 @folo/layout 2 | 3 | > React CSS Grid Components 4 | 5 | ![live example](https://raw.githubusercontent.com/jalal246/folo/master/packages/folo-layout/foloLayout-demo.gif) 6 | 7 | 8 | [![NPM Version](https://img.shields.io/npm/v/@folo/layout.svg)](https://www.npmjs.com/package/@folo/layout) 9 | [![NPM Download](https://img.shields.io/npm/dt/@folo/layout.svg)](https://www.npmjs.com/package/@folo/layout) 10 | [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/react.svg)](https://www.npmjs.com/package/@folo/layout) 11 | [![npm bundle size (gzip)](https://img.shields.io/bundlephobia/minzip/react.svg)](https://www.npmjs.com/package/@folo/layout) 12 | [![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/jalal246/folo/blob/master/packages/folo-layout/LICENSE) 13 | [![CI](https://img.shields.io/github/workflow/status/jalal246/folo/CI)](https://github.com/jalal246/folo/tree/master) 14 | [![Codecov](https://img.shields.io/codecov/c/github/jalal246/folo.svg)](https://codecov.io/gh/jalal246/folo) 15 | 16 | 17 | ## Installation 18 | 19 | ```sh 20 | npm install @folo/layout 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```js 26 | import { Grid, GridItem } from "@folo/layout"; 27 | 28 | const MyGrid = () => ( 29 | 30 | 31 | item 32 | 33 | 34 | item 35 | 36 | 37 | item 38 | 39 | 40 | item 41 | 42 | 43 | item 44 | 45 | 46 | ); 47 | ``` 48 | 49 | ### Components 50 | 51 | ```js 52 | import { Grid, GridItem } from "@folo/layout"; 53 | ``` 54 | 55 | ### Components Props 56 | 57 | All components accept custom props. 58 | 59 | #### Grid 60 | 61 | | property | type | description | default | 62 | | ------------- | ------------- | ------------------------- | ------- | 63 | | `component` | node/function | custom render-component | `div` | 64 | | `col` | number | number of columns in grid | | 65 | | `colWidth` | string | fixed column width | | 66 | | `colMinWidth` | string | column minimum width | `auto` | 67 | | `colMaxWidth` | string | column maximum width | `1fr` | 68 | | `row` | number | number of rows in grid | | 69 | | `rowWidth` | string | fixed row width | | 70 | | `rowMinWidth` | string | row minimum width | `auto` | 71 | | `rowMaxWidth` | string | row maximum width | `1fr` | 72 | 73 | #### GridItem 74 | 75 | Used for implicit grid layout. 76 | 77 | | property | type | description | default | 78 | | -------------- | ------------- | ------------------------- | ------- | 79 | | `component` | node/function | custom render-component | `div` | 80 | | `row` | number | number of columns in grid | | 81 | | `toRow` | number | column width | | 82 | | `col` | number | column minimum width | `0` | 83 | | `toCol` | number | column maximum width | | 84 | | `isCenter` | Boolean | number of rows in grid | `false` | 85 | | `isHorizontal` | Boolean | | `true` | 86 | 87 | ## Contribution 😇 88 | 89 | If you have ideas to improve this package or issues don't hesitate. You are always welcome. 90 | 91 | ## License 92 | 93 | This project is licensed under the [MIT License](https://github.com/jalal246/folo/blob/master/packages/folo-layout/LICENSE) 94 | -------------------------------------------------------------------------------- /packages/folo-layout/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [["@babel/preset-env"], ["@babel/preset-react"]], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/folo-layout/foloLayout-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jalal246/folo/f9d5577f0e9e86bbbf5461a22041f583222d986b/packages/folo-layout/foloLayout-demo.gif -------------------------------------------------------------------------------- /packages/folo-layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@folo/layout", 3 | "version": "1.0.2", 4 | "source": "src/index.js", 5 | "main": "dist/foloLayout.min.cjs.js", 6 | "umd:main": "dist/foloLayout.min.umd.js", 7 | "author": "Jalal Maskoun ", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "jest", 11 | "build": "builderz --output=foloLayout" 12 | }, 13 | "homepage": "https://github.com/jalal246/folo/tree/master/packages/folo-layout", 14 | "repository": "https://github.com/jalal246/folo", 15 | "bundledDependencies": [ 16 | "@folo/auto-position", 17 | "@folo/values" 18 | ], 19 | "peerDependencies": { 20 | "react": ">=16" 21 | }, 22 | "devDependencies": { 23 | "react": "^17.0.1" 24 | }, 25 | "files": [ 26 | "src", 27 | "dist", 28 | "LICENSE" 29 | ], 30 | "keywords": [ 31 | "@folo", 32 | "@folo/auto-position", 33 | "@folo/forms", 34 | "@folo/layout", 35 | "@folo/store", 36 | "@folo/utils", 37 | "@folo/values", 38 | "@folo/withcontext", 39 | "dflex", 40 | "react", 41 | "react-component", 42 | "css-grid", 43 | "css-layout", 44 | "form-builder-api", 45 | "dynamic-forms" 46 | ], 47 | "publishConfig": { 48 | "registry": "https://registry.npmjs.org/", 49 | "access": "public" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/folo-layout/src/components/Grid.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { 4 | GRID, 5 | STRETCH, 6 | CENTER, 7 | SPACE_BETWEEN, 8 | AUTO, 9 | FR, 10 | AUTO_FIT, 11 | DEFAULT_GAP, 12 | } from "../constants"; 13 | 14 | /** 15 | * call repeat() CSS function 16 | * depending on length, min,max 17 | * with some enhancements 18 | * 19 | * 20 | * @param {number} length row or column number in grid 21 | * @param {string} min minimum unit for row or column 22 | * @param {string} max maximum unit for row or column 23 | * @return {string} repeat() CSS function represents a repeated fragment of the track list 24 | */ 25 | function repeat(length, fixed, min, max) { 26 | return `repeat(${length}, ${fixed || `minmax(${min}, ${max})`})`; 27 | } 28 | 29 | const Grid = (props) => { 30 | const { 31 | component: CellComponent = "div", 32 | 33 | col = null, 34 | colWidth, 35 | colMinWidth = AUTO, 36 | colMaxWidth = FR, 37 | 38 | row = null, 39 | rowWidth, 40 | rowMinWidth = AUTO, 41 | rowMaxWidth = FR, 42 | 43 | isCenter = false, 44 | 45 | style: { 46 | display = GRID, 47 | 48 | alignItems = isCenter ? CENTER : STRETCH, 49 | 50 | // eslint-disable-next-line 51 | justifyContent = col || colWidth 52 | ? SPACE_BETWEEN 53 | : isCenter 54 | ? CENTER 55 | : STRETCH, 56 | 57 | gap = DEFAULT_GAP, 58 | 59 | ...otherStyles 60 | } = {}, 61 | children, 62 | 63 | // 64 | ...rest 65 | } = props; 66 | 67 | const style = { 68 | display, 69 | 70 | ...(row 71 | ? { gridTemplateRows: repeat(row, rowWidth, rowMinWidth, rowMaxWidth) } 72 | : rowWidth && { gridAutoRows: rowWidth }), 73 | 74 | ...(colWidth && !col 75 | ? { gridAutoColumns: colWidth } 76 | : { 77 | gridTemplateColumns: repeat( 78 | col || AUTO_FIT, 79 | colWidth, 80 | colMinWidth, 81 | colMaxWidth 82 | ), 83 | }), 84 | 85 | alignItems, 86 | justifyContent, 87 | gap, 88 | ...otherStyles, 89 | }; 90 | 91 | return ( 92 | 93 | {children} 94 | 95 | ); 96 | }; 97 | 98 | export default Grid; 99 | -------------------------------------------------------------------------------- /packages/folo-layout/src/components/GridItem.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import positionStore from "../positionStore"; 3 | 4 | import { 5 | CENTER, 6 | SPACE_BETWEEN, 7 | ROW, 8 | COLUMN, 9 | DISPLAY_FLEX, 10 | STRETCH, 11 | } from "../constants"; 12 | 13 | function location(from, to) { 14 | if (from !== null && to !== null) { 15 | return `${from} / ${to}`; 16 | } 17 | return `${from}`; 18 | } 19 | 20 | /** 21 | * For implicit grid 22 | * Takes column and row number 23 | * to inform the grid parent of total positions 24 | * 25 | * It collects numbers report it to Grid 26 | */ 27 | const GridItem = (props) => { 28 | const { 29 | component: CellComponent = "div", 30 | 31 | row = null, 32 | toRow = null, 33 | 34 | col = 0, 35 | toCol = null, 36 | 37 | isCenter = false, 38 | isHorizontal = true, 39 | 40 | style: { 41 | display = DISPLAY_FLEX, 42 | 43 | flexDirection: fDirection, 44 | alignItems: aItems, 45 | ...otherStyle 46 | } = {}, 47 | 48 | id, 49 | children, 50 | 51 | ...rest 52 | } = props; 53 | 54 | const calculatedPosition = positionStore.autoPosition({ 55 | key: id || `${new Date().getTime()}`, 56 | row, 57 | toRow, 58 | }); 59 | 60 | const container = { 61 | display, 62 | 63 | ...(isHorizontal 64 | ? { 65 | flexDirection: fDirection || ROW, 66 | alignItems: aItems || CENTER, 67 | } 68 | : { 69 | flexDirection: fDirection || COLUMN, 70 | alignItems: aItems || STRETCH, 71 | }), 72 | 73 | justifyContent: isCenter ? CENTER : SPACE_BETWEEN, 74 | 75 | gridRow: location(calculatedPosition, toRow), 76 | gridColumn: location(col, toCol), 77 | 78 | ...otherStyle, 79 | }; 80 | 81 | return ( 82 | 83 | {children} 84 | 85 | ); 86 | }; 87 | 88 | export default GridItem; 89 | -------------------------------------------------------------------------------- /packages/folo-layout/src/constants.js: -------------------------------------------------------------------------------- 1 | export const GRID = "grid"; 2 | 3 | export const STRETCH = "stretch"; 4 | export const CENTER = "center"; 5 | export const SPACE_BETWEEN = "space-between"; 6 | 7 | export const FR = "1fr"; 8 | export const AUTO_FIT = "auto-fit"; 9 | export const AUTO = "auto"; 10 | export const DEFAULT_GAP = "1em"; 11 | 12 | export const ROW = "row"; 13 | export const COLUMN = "column"; 14 | export const DISPLAY_FLEX = "flex"; 15 | -------------------------------------------------------------------------------- /packages/folo-layout/src/index.js: -------------------------------------------------------------------------------- 1 | import GridItem from "./components/GridItem"; 2 | import Grid from "./components/Grid"; 3 | 4 | export { GridItem, Grid }; 5 | -------------------------------------------------------------------------------- /packages/folo-layout/src/positionStore.js: -------------------------------------------------------------------------------- 1 | import AutoPositionCell from "@folo/auto-position/src"; 2 | 3 | export default (function init() { 4 | const positionStore = new AutoPositionCell(); 5 | 6 | return positionStore; 7 | })(); 8 | -------------------------------------------------------------------------------- /packages/folo-layout/src/stories/Implicit.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Grid from "../components/Grid"; 4 | 5 | import { FriendlyLadder, ArtCollectionProps } from "./examples/ImplicitGrid"; 6 | 7 | export default { 8 | title: "Grid Layout/Implicit Layout", 9 | component: Grid, 10 | }; 11 | 12 | export { FriendlyLadder }; 13 | 14 | export const ArtCollection = (args) => ; 15 | ArtCollection.args = { 16 | items1: { col1: 1, row1: 1, toRow1: 4, backgroundColor1: "blue" }, 17 | items2: { col2: 3, row2: 1, toRow2: 2, backgroundColor2: "orange" }, 18 | items3: { col3: 2, row3: 3, toRow3: 5, backgroundColor3: "pink" }, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/folo-layout/src/stories/examples/ExplicitGrid.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Grid from "../../components/Grid"; 4 | 5 | const styleLabel = { 6 | backgroundColor: " #444", 7 | color: "#fff", 8 | padding: "20px", 9 | fontSize: "150%", 10 | }; 11 | 12 | export const FourColumnsProps = ({ col = 4 }) => ( 13 | 19 |
1
20 |
2
21 |
3
22 |
4
23 |
5
24 |
6
25 |
7
26 |
8
27 |
28 | ); 29 | 30 | export const ThreeColumnsThreeRowsProps = ({ col = 3, row = 4 }) => ( 31 | 38 |
1
39 |
2
40 |
3
41 |
4
42 |
5
43 |
6
44 |
7
45 |
8
46 |
9
47 |
48 | ); 49 | 50 | const styleLabel2 = { 51 | width: "172px", 52 | height: "81px", 53 | backgroundColor: " #444", 54 | color: "#fff", 55 | padding: "20px", 56 | fontSize: "150%", 57 | }; 58 | 59 | export const MinimumWidthProps = ({ 60 | gap = "0.3em", 61 | col = 3, 62 | row = 2, 63 | rowMinWid = "140px", 64 | colMinWidth = "300px", 65 | }) => ( 66 | 75 |
1
76 |
2
77 |
3
78 |
4
79 |
5
80 |
6
81 |
82 | ); 83 | 84 | export const CenterWithFixedWidthProps = ({ 85 | gap = 0, 86 | isCenter = true, 87 | col = 2, 88 | row = 2, 89 | rowWidth = "220px", 90 | colWidth = "500px", 91 | }) => ( 92 | 102 |
2
103 |
1
104 |
3
105 |
4
106 |
107 | ); 108 | -------------------------------------------------------------------------------- /packages/folo-layout/src/stories/examples/ImplicitGrid.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Grid from "../../components/Grid"; 4 | import GridItem from "../../components/GridItem"; 5 | 6 | const styleLabel = { 7 | backgroundColor: "red", 8 | color: "#fff", 9 | padding: "10px", 10 | fontSize: "100%", 11 | }; 12 | 13 | export const FriendlyLadder = () => ( 14 | 19 | 20 | item 21 | 22 | {/* next one will be prev + 1 */} 23 | 24 | item 25 | 26 | 27 | item 28 | 29 | 30 | item 31 | 32 | 33 | item 34 | 35 | 36 | ); 37 | 38 | export const ArtCollectionProps = () => ( 39 | 44 | 51 | 58 | 65 | 66 | ); 67 | -------------------------------------------------------------------------------- /packages/folo-layout/src/stories/explicit.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Grid from "../components/Grid"; 4 | 5 | import { 6 | FourColumnsProps, 7 | ThreeColumnsThreeRowsProps, 8 | MinimumWidthProps, 9 | CenterWithFixedWidthProps, 10 | } from "./examples/ExplicitGrid"; 11 | 12 | export default { 13 | title: "Grid Layout/Explicit Layout", 14 | component: Grid, 15 | }; 16 | 17 | export const FourColumns = (args) => ; 18 | FourColumns.args = { 19 | col: 4, 20 | }; 21 | 22 | export const ThreeColumnsThreeRows = (args) => ( 23 | 24 | ); 25 | ThreeColumnsThreeRows.args = { 26 | col: 3, 27 | row: 4, 28 | }; 29 | 30 | export const MinimumWidth = (args) => ; 31 | MinimumWidth.args = { 32 | gap: "0.3em", 33 | col: 3, 34 | row: 2, 35 | rowMinWid: "140px", 36 | colMinWidth: "300px", 37 | }; 38 | 39 | export const CenterWithFixedWidth = (args) => ( 40 | 41 | ); 42 | CenterWithFixedWidth.args = { 43 | gap: 0, 44 | isCenter: true, 45 | col: 2, 46 | row: 2, 47 | rowWidth: "220px", 48 | colWidth: "500px", 49 | }; 50 | -------------------------------------------------------------------------------- /packages/folo-layout/test/Grid.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen, cleanup } from "@testing-library/react"; 3 | 4 | import Grid from "../src/components/Grid"; 5 | 6 | const TEST_ID = "testForGrid"; 7 | 8 | const DEFAULT_DISPLAY = "grid"; 9 | const STRETCH = "stretch"; 10 | const DEFAULT_GAP = "1em"; 11 | const SPACE_BETWEEN = "space-between"; 12 | const AUTO_FIT = "auto-fit"; 13 | const CENTER = "center"; 14 | 15 | const MyGrid = (props = {}) => ( 16 | 17 |
18 | 19 | ); 20 | 21 | describe("Grid - Explicit Layout", () => { 22 | afterEach(() => { 23 | cleanup(); 24 | }); 25 | 26 | describe("Testing Style", () => { 27 | it("Returns default style", () => { 28 | render(); 29 | 30 | const { baseElement, style } = screen.getByTestId(TEST_ID); 31 | 32 | const expected = { 33 | display: DEFAULT_DISPLAY, 34 | gridTemplateColumns: `repeat(${AUTO_FIT}, minmax(auto, 1fr))`, 35 | alignItems: STRETCH, 36 | justifyContent: STRETCH, 37 | gap: DEFAULT_GAP, 38 | }; 39 | 40 | expect(baseElement).toMatchSnapshot(); 41 | expect(style).toMatchObject(expected); 42 | }); 43 | 44 | it("Returns default style", () => { 45 | const DISPLAY_INLINE = "inline-grid"; 46 | render(); 47 | 48 | const { baseElement, style } = screen.getByTestId(TEST_ID); 49 | 50 | const expected = { 51 | display: DISPLAY_INLINE, 52 | gridTemplateColumns: `repeat(${AUTO_FIT}, minmax(auto, 1fr))`, 53 | alignItems: STRETCH, 54 | justifyContent: STRETCH, 55 | gap: DEFAULT_GAP, 56 | }; 57 | 58 | expect(baseElement).toMatchSnapshot(); 59 | expect(style).toMatchObject(expected); 60 | }); 61 | }); 62 | 63 | describe("Testing alignItems", () => { 64 | it("Returns alignItems justifyContent center when no columns passed", () => { 65 | render(); 66 | const { baseElement, style } = screen.getByTestId(TEST_ID); 67 | 68 | const expected = { 69 | display: DEFAULT_DISPLAY, 70 | gridTemplateColumns: `repeat(${AUTO_FIT}, minmax(auto, 1fr))`, 71 | alignItems: CENTER, 72 | justifyContent: CENTER, 73 | gap: DEFAULT_GAP, 74 | }; 75 | 76 | expect(baseElement).toMatchSnapshot(); 77 | expect(style).toMatchObject(expected); 78 | }); 79 | 80 | it("Returns alignItems center and justifyContent when columns passed", () => { 81 | render(); 82 | const { baseElement, style } = screen.getByTestId(TEST_ID); 83 | 84 | const expected = { 85 | display: DEFAULT_DISPLAY, 86 | gridTemplateColumns: "repeat(1, minmax(auto, 1fr))", 87 | alignItems: CENTER, 88 | justifyContent: SPACE_BETWEEN, 89 | gap: DEFAULT_GAP, 90 | }; 91 | 92 | expect(baseElement).toMatchSnapshot(); 93 | expect(style).toMatchObject(expected); 94 | }); 95 | }); 96 | 97 | describe("Testing rows", () => { 98 | it("Returns gridTemplateRows when passing row with minmax", () => { 99 | render(); 100 | const { baseElement, style } = screen.getByTestId(TEST_ID); 101 | 102 | const expected = { 103 | display: DEFAULT_DISPLAY, 104 | gridTemplateColumns: `repeat(${AUTO_FIT}, minmax(auto, 1fr))`, 105 | gridTemplateRows: "repeat(1, minmax(auto, 1fr))", 106 | alignItems: STRETCH, 107 | justifyContent: STRETCH, 108 | gap: DEFAULT_GAP, 109 | }; 110 | 111 | expect(baseElement).toMatchSnapshot(); 112 | expect(style).toMatchObject(expected); 113 | }); 114 | 115 | it("Returns gridTemplateRows when passing row with width", () => { 116 | render(); 117 | const { baseElement, style } = screen.getByTestId(TEST_ID); 118 | 119 | const expected = { 120 | display: DEFAULT_DISPLAY, 121 | gridTemplateColumns: `repeat(${AUTO_FIT}, minmax(auto, 1fr))`, 122 | gridTemplateRows: "repeat(1, 20px)", 123 | alignItems: STRETCH, 124 | justifyContent: STRETCH, 125 | gap: DEFAULT_GAP, 126 | }; 127 | 128 | expect(baseElement).toMatchSnapshot(); 129 | expect(style).toMatchObject(expected); 130 | }); 131 | 132 | it("Returns gridAutoRows when passing rowWidth only", () => { 133 | render(); 134 | const { baseElement, style } = screen.getByTestId(TEST_ID); 135 | 136 | const expected = { 137 | display: DEFAULT_DISPLAY, 138 | gridTemplateColumns: `repeat(${AUTO_FIT}, minmax(auto, 1fr))`, 139 | gridAutoRows: "20px", 140 | alignItems: STRETCH, 141 | justifyContent: STRETCH, 142 | gap: DEFAULT_GAP, 143 | }; 144 | 145 | expect(baseElement).toMatchSnapshot(); 146 | expect(style).toMatchObject(expected); 147 | }); 148 | }); 149 | 150 | describe("Testing columns", () => { 151 | it("Returns gridAutoColumns when passing colWidth only", () => { 152 | render(); 153 | const { baseElement, style } = screen.getByTestId(TEST_ID); 154 | 155 | const expected = { 156 | display: DEFAULT_DISPLAY, 157 | gridAutoColumns: "20px", 158 | alignItems: STRETCH, 159 | justifyContent: SPACE_BETWEEN, 160 | gap: DEFAULT_GAP, 161 | }; 162 | 163 | expect(baseElement).toMatchSnapshot(); 164 | expect(style).toMatchObject(expected); 165 | }); 166 | 167 | it("Returns gridTemplateColumns when passing col", () => { 168 | render(); 169 | const { baseElement, style } = screen.getByTestId(TEST_ID); 170 | 171 | const expected = { 172 | display: DEFAULT_DISPLAY, 173 | gridTemplateColumns: "repeat(4, minmax(auto, 1fr))", 174 | alignItems: STRETCH, 175 | justifyContent: SPACE_BETWEEN, 176 | gap: DEFAULT_GAP, 177 | }; 178 | 179 | expect(baseElement).toMatchSnapshot(); 180 | expect(style).toMatchObject(expected); 181 | }); 182 | 183 | it("Returns gridTemplateColumns when passing colWidth and col", () => { 184 | render(); 185 | const { baseElement, style } = screen.getByTestId(TEST_ID); 186 | 187 | const expected = { 188 | display: DEFAULT_DISPLAY, 189 | gridTemplateColumns: "repeat(4, 20px)", 190 | alignItems: STRETCH, 191 | justifyContent: SPACE_BETWEEN, 192 | gap: DEFAULT_GAP, 193 | }; 194 | 195 | expect(baseElement).toMatchSnapshot(); 196 | expect(style).toMatchObject(expected); 197 | }); 198 | }); 199 | }); 200 | -------------------------------------------------------------------------------- /packages/folo-layout/test/GridItem.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen, cleanup } from "@testing-library/react"; 3 | 4 | import GridItem from "../src/components/GridItem"; 5 | 6 | const TEST_ID = "testForGrid"; 7 | 8 | const CENTER = "center"; 9 | const ROW = "row"; 10 | const COLUMN = "column"; 11 | const DISPLAY_FLEX = "flex"; 12 | const SPACE_BETWEEN = "space-between"; 13 | const STRETCH = "stretch"; 14 | 15 | const MyGridItem = (props = {}) => ( 16 | 17 | ); 18 | 19 | describe("GridItem - Implicit Layout", () => { 20 | afterEach(() => { 21 | cleanup(); 22 | }); 23 | 24 | describe("Testing Style", () => { 25 | it("Returns default style", () => { 26 | render(); 27 | const { baseElement, style } = screen.getByTestId(TEST_ID); 28 | 29 | const expected = { 30 | display: DISPLAY_FLEX, 31 | alignItems: CENTER, 32 | flexDirection: ROW, 33 | // gridRow: "0", 34 | gridColumn: "0", 35 | justifyContent: SPACE_BETWEEN, 36 | }; 37 | 38 | expect(baseElement).toMatchSnapshot(); 39 | expect(style).toMatchObject(expected); 40 | }); 41 | 42 | it("Returns flexDirection column when it is isHorizontal = false", () => { 43 | render(); 44 | const { baseElement, style } = screen.getByTestId(TEST_ID); 45 | 46 | const expected = { 47 | display: DISPLAY_FLEX, 48 | alignItems: STRETCH, 49 | flexDirection: COLUMN, 50 | // gridRow: "0", 51 | gridColumn: "0", 52 | justifyContent: SPACE_BETWEEN, 53 | }; 54 | 55 | expect(baseElement).toMatchSnapshot(); 56 | expect(style).toMatchObject(expected); 57 | }); 58 | 59 | it("Overrides styles", () => { 60 | render( 61 | 67 | ); 68 | const { baseElement, style } = screen.getByTestId(TEST_ID); 69 | 70 | const expected = { 71 | display: DISPLAY_FLEX, 72 | alignItems: STRETCH, 73 | flexDirection: COLUMN, 74 | // gridRow: "0", 75 | gridColumn: "0", 76 | justifyContent: SPACE_BETWEEN, 77 | }; 78 | 79 | expect(baseElement).toMatchSnapshot(); 80 | expect(style).toMatchObject(expected); 81 | }); 82 | 83 | it("Returns center", () => { 84 | render(); 85 | const { baseElement, style } = screen.getByTestId(TEST_ID); 86 | 87 | const expected = { 88 | display: DISPLAY_FLEX, 89 | alignItems: CENTER, 90 | flexDirection: ROW, 91 | // gridRow: "0", 92 | gridColumn: "0", 93 | justifyContent: CENTER, 94 | }; 95 | 96 | expect(baseElement).toMatchSnapshot(); 97 | expect(style).toMatchObject(expected); 98 | }); 99 | }); 100 | 101 | describe("Testing gridRow", () => { 102 | it("Returns gridRow with toRow and alignItems center", () => { 103 | render(); 104 | const { baseElement, style } = screen.getByTestId(TEST_ID); 105 | 106 | const expected = { 107 | display: DISPLAY_FLEX, 108 | alignItems: CENTER, 109 | flexDirection: ROW, 110 | // gridRow: "0 / 6", 111 | gridColumn: "0", 112 | justifyContent: SPACE_BETWEEN, 113 | }; 114 | 115 | expect(baseElement).toMatchSnapshot(); 116 | expect(style).toMatchObject(expected); 117 | }); 118 | 119 | it("Returns gridRow with toRow =0", () => { 120 | render(); 121 | const { baseElement, style } = screen.getByTestId(TEST_ID); 122 | 123 | const expected = { 124 | display: DISPLAY_FLEX, 125 | alignItems: CENTER, 126 | flexDirection: ROW, 127 | // gridRow: "0 / 0", 128 | gridColumn: "0", 129 | justifyContent: SPACE_BETWEEN, 130 | }; 131 | 132 | expect(baseElement).toMatchSnapshot(); 133 | expect(style).toMatchObject(expected); 134 | }); 135 | 136 | it("Returns gridRow with toRow =0", () => { 137 | render(); 138 | const { baseElement, style } = screen.getByTestId(TEST_ID); 139 | 140 | const expected = { 141 | display: DISPLAY_FLEX, 142 | alignItems: CENTER, 143 | flexDirection: ROW, 144 | // gridRow: "0 / 0", 145 | gridColumn: "0", 146 | justifyContent: SPACE_BETWEEN, 147 | }; 148 | 149 | expect(baseElement).toMatchSnapshot(); 150 | expect(style).toMatchObject(expected); 151 | }); 152 | }); 153 | 154 | describe("Testing gridColumn", () => { 155 | it("Returns column position with when col is provided", () => { 156 | render(); 157 | const { baseElement, style } = screen.getByTestId(TEST_ID); 158 | 159 | const expected = { 160 | display: DISPLAY_FLEX, 161 | alignItems: CENTER, 162 | flexDirection: ROW, 163 | // gridRow: "0", 164 | gridColumn: "10", 165 | justifyContent: SPACE_BETWEEN, 166 | }; 167 | 168 | expect(baseElement).toMatchSnapshot(); 169 | expect(style).toMatchObject(expected); 170 | }); 171 | 172 | it("Returns column position with justifyContent, when toCol is provided", () => { 173 | render(); 174 | const { baseElement, style } = screen.getByTestId(TEST_ID); 175 | 176 | const expected = { 177 | display: DISPLAY_FLEX, 178 | alignItems: CENTER, 179 | flexDirection: ROW, 180 | // gridRow: "0", 181 | gridColumn: "0 / 10", 182 | justifyContent: SPACE_BETWEEN, 183 | }; 184 | 185 | expect(baseElement).toMatchSnapshot(); 186 | expect(style).toMatchObject(expected); 187 | }); 188 | }); 189 | }); 190 | -------------------------------------------------------------------------------- /packages/folo-layout/test/__snapshots__/Grid.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Grid - Explicit Layout Testing Style Returns default style 1`] = `undefined`; 4 | 5 | exports[`Grid - Explicit Layout Testing Style Returns default style 2`] = `undefined`; 6 | 7 | exports[`Grid - Explicit Layout Testing alignItems Returns alignItems center and justifyContent when columns passed 1`] = `undefined`; 8 | 9 | exports[`Grid - Explicit Layout Testing alignItems Returns alignItems justifyContent center when no columns passed 1`] = `undefined`; 10 | 11 | exports[`Grid - Explicit Layout Testing columns Returns gridAutoColumns when passing colWidth only 1`] = `undefined`; 12 | 13 | exports[`Grid - Explicit Layout Testing columns Returns gridTemplateColumns when passing col 1`] = `undefined`; 14 | 15 | exports[`Grid - Explicit Layout Testing columns Returns gridTemplateColumns when passing colWidth and col 1`] = `undefined`; 16 | 17 | exports[`Grid - Explicit Layout Testing rows Returns gridAutoRows when passing rowWidth only 1`] = `undefined`; 18 | 19 | exports[`Grid - Explicit Layout Testing rows Returns gridTemplateRows when passing row with minmax 1`] = `undefined`; 20 | 21 | exports[`Grid - Explicit Layout Testing rows Returns gridTemplateRows when passing row with width 1`] = `undefined`; 22 | -------------------------------------------------------------------------------- /packages/folo-layout/test/__snapshots__/GridItem.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`GridItem - Implicit Layout Testing Style Overrides styles 1`] = `undefined`; 4 | 5 | exports[`GridItem - Implicit Layout Testing Style Returns center 1`] = `undefined`; 6 | 7 | exports[`GridItem - Implicit Layout Testing Style Returns default style 1`] = `undefined`; 8 | 9 | exports[`GridItem - Implicit Layout Testing Style Returns flexDirection column when it is isHorizontal = false 1`] = `undefined`; 10 | 11 | exports[`GridItem - Implicit Layout Testing gridColumn Returns column position with justifyContent, when toCol is provided 1`] = `undefined`; 12 | 13 | exports[`GridItem - Implicit Layout Testing gridColumn Returns column position with when col is provided 1`] = `undefined`; 14 | 15 | exports[`GridItem - Implicit Layout Testing gridRow Returns gridRow with toRow =0 1`] = `undefined`; 16 | 17 | exports[`GridItem - Implicit Layout Testing gridRow Returns gridRow with toRow =0 2`] = `undefined`; 18 | 19 | exports[`GridItem - Implicit Layout Testing gridRow Returns gridRow with toRow and alignItems center 1`] = `undefined`; 20 | -------------------------------------------------------------------------------- /packages/folo-store/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint-config-folo-ts"], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/folo-store/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-20201 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/folo-store/README.MD: -------------------------------------------------------------------------------- 1 | # 📋 @folo/store 2 | 3 | > A Plain JavaScript Store Holds & Helps Controlling Data In Forms 4 | 5 | 6 | [![NPM Version](https://img.shields.io/npm/v/@folo/store.svg)](https://www.npmjs.com/package/@folo/store) 7 | [![NPM Download](https://img.shields.io/npm/dt/@folo/store.svg)](https://www.npmjs.com/package/@folo/store) 8 | [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/react.svg)](https://www.npmjs.com/package/@folo/store) 9 | [![npm bundle size (gzip)](https://img.shields.io/bundlephobia/minzip/react.svg)](https://www.npmjs.com/package/@folo/store) 10 | [![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/jalal246/folo/blob/master/packages/folo-store/LICENSE) 11 | [![CI](https://img.shields.io/github/workflow/status/jalal246/folo/CI)](https://github.com/jalal246/folo/tree/master) 12 | [![Codecov](https://img.shields.io/codecov/c/github/jalal246/folo.svg)](https://codecov.io/gh/jalal246/folo) 13 | 14 | 15 | ## Installation 16 | 17 | ```sh 18 | npm install @folo/store 19 | ``` 20 | 21 | A store that holds data and triggers the connected components to update their 22 | values when necessary. Designed to deal optimally with forms. 23 | 24 | **How this is going to affect your UI performance?** 25 | 26 | Instead of re-mount, all elements exist in the form. This approach 27 | guarantees that only controlled elements in the UI will be triggered. This is 28 | important because usually global store and in most cases means to update all 29 | associated elements. 30 | 31 | ## Getting started 32 | 33 | Create a shared registry for your app. No matter how many forms you have, You 34 | need one registry instance. 35 | 36 | ```js 37 | import Registry from "@folo/store"; 38 | 39 | const registry = new Registry(); 40 | ``` 41 | 42 | ## API 43 | 44 | ### Subscribe 45 | 46 | - Subscribe your elements to the registry: 47 | 48 | ```ts 49 | registry.subscribe(subscribeInfo: Object, triggerHandler:? Function); 50 | ``` 51 | 52 | Where `SubscribeInfo: Object` an object contains: 53 | 54 | - `nameRef: string` - key reference to the element value. 55 | - `initValue: StoreValue` - Initial value for the element. 56 | - `groupName?: string` - Only for button group. 57 | - `storeID?: string` - In case you are running multiple forms, this value 58 | creates umbrella branch for you form. 59 | 60 | And `triggerHandler: Function` - Triggered when local value needs to be 61 | updated. This is strict to group buttons only. 62 | 63 | #### Example for subscribe 64 | 65 | ```js 66 | const btn1Info = { 67 | nameRef: "btn1", 68 | initValue: false, 69 | groupName: "choices", 70 | }; 71 | 72 | const btn1Handler = (newValue) => { 73 | console.log(`I am triggered when my siblings value change! ${newValue}`); 74 | }; 75 | 76 | registry.subscribe(btn1Info, btn1Handler); 77 | 78 | const btn2Info = { 79 | nameRef: "btn2", 80 | initValue: true, 81 | groupName: "choices", 82 | }; 83 | 84 | const btn2Handler = (newValue) => { 85 | console.log(`I am triggered when my siblings value change! ${newValue}`); 86 | }; 87 | 88 | registry.subscribe(btn2Info, btn2Handler); 89 | 90 | // ... 91 | registry.subscribe(text1Info, btn2Handler); 92 | registry.subscribe(text2Info, btn2Handler); 93 | ``` 94 | 95 | ### updater 96 | 97 | - Update value in the registry and triggered handlers if necessary: 98 | 99 | ```ts 100 | registry.updater(updaterInfo: Object); 101 | ``` 102 | 103 | Where `updaterInfo: Object` an object contains: 104 | 105 | - `nameRef: string` - key reference to the stored element. 106 | - `newValue: StoreValue` - The new value for the element. 107 | - `groupName?: string` - Only for button group. 108 | - `storeID?: string` - In case the element is registered under store id. 109 | 110 | What's going to happen is updating store values and trigger handlers for all 111 | buttons boolean values under the same group name toggling their values. 112 | 113 | #### Example for updater 114 | 115 | - Receive new value: 116 | 117 | ```js 118 | const btn2NewInfo = { 119 | nameRef: "btn2", 120 | newValue: true, 121 | groupName: "choices", 122 | }; 123 | 124 | registry.updater(btn2NewInfo); 125 | 126 | // 127 | 128 | registry.updater(text1NewInfo); 129 | registry.updater(text2NewInfo); 130 | ``` 131 | 132 | So, `btn1Handler` will be triggered with `false`. Why? Since we updated `btn1` 133 | and `btn2` values, UI should be informed to re-render. 134 | 135 | This is the only case when functions are triggered. Otherwise. data flow will be 136 | from UI to the store until submit is asking for all elements values. 137 | 138 | ### getDataByStoreID 139 | 140 | - Get store data 141 | 142 | ```ts 143 | registry.getDataByStoreID(storeID?: string): Object 144 | ``` 145 | 146 | ### clear 147 | 148 | - Clear store data 149 | 150 | ```ts 151 | registry.clear(storeID?: string) 152 | ``` 153 | 154 | ### destroy 155 | 156 | - Reset the whole registry 157 | 158 | ```ts 159 | registry.destroy(); 160 | ``` 161 | 162 | ## Test 163 | 164 | ```sh 165 | yarn test folo-store 166 | ``` 167 | 168 | ## Implementation 169 | 170 | - With react: 171 | [folo-values](https://github.com/jalal246/folo/tree/master/packages/folo-values) 172 | 173 | ## Contribution 😇 174 | 175 | If you have ideas or issues don't hesitate. You are always welcome. 176 | 177 | ## License 178 | 179 | This project is licensed under the [MIT License](https://github.com/jalal246/folo/blob/master/packages/folo-store/LICENSE) 180 | -------------------------------------------------------------------------------- /packages/folo-store/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [["@babel/preset-env"]], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/folo-store/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@folo/store", 3 | "version": "0.1.3", 4 | "source": "src/index.js", 5 | "main": "dist/foloStore.min.cjs.js", 6 | "umd:main": "dist/foloStore.min.umd.js", 7 | "author": "Jalal Maskoun ", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "jest", 11 | "compile": "tsc", 12 | "prebuild": "yarn compile", 13 | "build": "builderz --entires=./lib/index.js --output=foloStore" 14 | }, 15 | "homepage": "https://github.com/jalal246/folo/tree/master/packages/folo-store", 16 | "repository": "https://github.com/jalal246/folo", 17 | "files": [ 18 | "src", 19 | "dist", 20 | "types", 21 | "README.md", 22 | "LICENSE" 23 | ], 24 | "keywords": [ 25 | "@folo", 26 | "@folo/auto-position", 27 | "@folo/forms", 28 | "@folo/layout", 29 | "@folo/store", 30 | "@folo/utils", 31 | "@folo/values", 32 | "@folo/withcontext", 33 | "@dflex", 34 | "store", 35 | "json", 36 | "forms", 37 | "storage" 38 | ], 39 | "publishConfig": { 40 | "registry": "https://registry.npmjs.org/", 41 | "access": "public" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/folo-store/src/Registry.ts: -------------------------------------------------------------------------------- 1 | type StoreValue = string | Boolean; 2 | 3 | interface SubscribeInfo { 4 | nameRef: string; 5 | initValue: StoreValue; 6 | groupName?: string; 7 | storeID?: string; 8 | } 9 | 10 | interface UpdaterInfo { 11 | nameRef: string; 12 | newValue: StoreValue; 13 | groupName?: string; 14 | storeID?: string; 15 | } 16 | 17 | interface DataHolder { 18 | [key: string]: { [key: string]: Object }; 19 | } 20 | 21 | interface Triggers { 22 | [key: string]: { [key: string]: Function }; 23 | } 24 | 25 | interface BtnGroup { 26 | [key: string]: { [key: string]: Set }; 27 | } 28 | 29 | const DEFAULT_STORE = "unrecognized"; 30 | 31 | class Registry { 32 | dataHolder: DataHolder; 33 | 34 | triggers: Triggers; 35 | 36 | activeStore: string; 37 | 38 | activeField: string; 39 | 40 | btnGroup: BtnGroup; 41 | 42 | constructor() { 43 | this.dataHolder = {}; 44 | this.triggers = {}; 45 | this.btnGroup = {}; 46 | 47 | this.activeStore = ""; 48 | this.activeField = ""; 49 | } 50 | 51 | setActive(nameRef?: string, storeID?: string) { 52 | this.activeStore = storeID || DEFAULT_STORE; 53 | if (nameRef) this.activeField = nameRef; 54 | } 55 | 56 | initNewStore() { 57 | this.dataHolder[this.activeStore] = {}; 58 | this.btnGroup[this.activeStore] = {}; 59 | this.triggers[this.activeStore] = {}; 60 | } 61 | 62 | assignValueToStore(storeValue: StoreValue) { 63 | if (!this.dataHolder[this.activeStore]) { 64 | this.initNewStore(); 65 | } 66 | 67 | this.dataHolder[this.activeStore][this.activeField] = storeValue; 68 | } 69 | 70 | assignTrigger(triggerHandler: Function) { 71 | this.triggers[this.activeStore][this.activeField] = triggerHandler; 72 | } 73 | 74 | triggerHandler(withValue: StoreValue) { 75 | this.triggers[this.activeStore][this.activeField](withValue); 76 | } 77 | 78 | /** 79 | * Add field to dataBranches 80 | * Add groupName to btnGroup 81 | * 82 | * This function will be called when cells mount 83 | * after componentDidMount, the data in dataBranches will be moved to state 84 | * dataBranches is a temp object holds value to avoid update state while rendering 85 | * in this case will init all cells in dataBranches until rendering happens 86 | * then update the state so all the values update happen in state 87 | * 88 | * @param {object} field - new field that should be register 89 | * @param {string} field.nameRef key for value 90 | * @param {string||boolean} field.initValue value 91 | * @param {string} field.groupName group name in case the field is group-toggle 92 | */ 93 | subscribe( 94 | { nameRef, initValue, groupName, storeID }: SubscribeInfo, 95 | triggerHandler: Function 96 | ) { 97 | this.setActive(nameRef, storeID); 98 | 99 | this.assignValueToStore(initValue); 100 | 101 | // if it has group, handle it 102 | if (groupName) { 103 | this.assignTrigger(triggerHandler); 104 | 105 | if (!this.btnGroup[this.activeStore][groupName]) { 106 | /* 107 | * Check if group name not exist then add it and create its own set 108 | * then add the field to its group 109 | */ 110 | 111 | // create new set for the group 112 | this.btnGroup[this.activeStore][groupName] = new Set(); 113 | } 114 | 115 | if (!this.btnGroup[this.activeStore][groupName].has(nameRef)) { 116 | // then add the field name to where its belong 117 | // to its group 118 | this.btnGroup[this.activeStore][groupName].add(nameRef); 119 | } 120 | } 121 | } 122 | 123 | updater({ nameRef, newValue, groupName, storeID }: UpdaterInfo) { 124 | this.setActive(nameRef, storeID); 125 | this.assignValueToStore(newValue); 126 | 127 | if (groupName) { 128 | if (newValue !== false) { 129 | // update group of values 130 | 131 | // toggle group values 132 | this.btnGroup[this.activeStore][groupName].forEach((FieldNameRef) => { 133 | // toggle all except the targeted key name which called nameRef 134 | // since we already changed its value above 135 | if (FieldNameRef !== nameRef) { 136 | this.setActive(FieldNameRef, storeID); 137 | this.assignValueToStore(!newValue); 138 | this.triggerHandler(!newValue); 139 | } 140 | }); 141 | } 142 | } 143 | } 144 | 145 | getDataByStoreID(storeID?: string): Object { 146 | this.setActive(undefined, storeID); 147 | 148 | return this.dataHolder[this.activeStore]; 149 | } 150 | 151 | clear(storeID?: string) { 152 | this.setActive(undefined, storeID); 153 | 154 | this.dataHolder[this.activeStore] = {}; 155 | this.triggers[this.activeStore] = {}; 156 | this.btnGroup[this.activeStore] = {}; 157 | } 158 | 159 | destroy() { 160 | this.dataHolder = {}; 161 | this.triggers = {}; 162 | this.btnGroup = {}; 163 | } 164 | } 165 | 166 | export default Registry; 167 | -------------------------------------------------------------------------------- /packages/folo-store/src/index.ts: -------------------------------------------------------------------------------- 1 | import Registry from "./Registry"; 2 | 3 | export default Registry; 4 | -------------------------------------------------------------------------------- /packages/folo-store/test/__snapshots__/subscribe.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Testing subscribe effects on dataHolder/btnGroup Initializes data Obj holder with first register Checks button group 1`] = ` 4 | Object { 5 | "unrecognized": Object { 6 | "gr1": Set { 7 | "nameTest1", 8 | }, 9 | }, 10 | } 11 | `; 12 | 13 | exports[`Testing subscribe effects on dataHolder/btnGroup Pushing a second obj with same group name Checks button group 1`] = ` 14 | Object { 15 | "unrecognized": Object { 16 | "gr1": Set { 17 | "nameTest1", 18 | "nameTest2", 19 | }, 20 | }, 21 | } 22 | `; 23 | 24 | exports[`Testing subscribe effects on dataHolder/btnGroup Pushing a second obj with same group name Checks trigger handlers 1`] = ` 25 | Object { 26 | "unrecognized": Object { 27 | "nameTest1": [MockFunction], 28 | "nameTest2": [MockFunction], 29 | }, 30 | } 31 | `; 32 | 33 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing 2 branches of button group Checks button group 1`] = ` 34 | Object { 35 | "unrecognized": Object { 36 | "gr1": Set { 37 | "nameTest1", 38 | "nameTest2", 39 | }, 40 | "gr2": Set { 41 | "nameTest3", 42 | }, 43 | }, 44 | } 45 | `; 46 | 47 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing 2 branches of button group Checks trigger handlers 1`] = ` 48 | Object { 49 | "unrecognized": Object { 50 | "nameTest1": [MockFunction], 51 | "nameTest2": [MockFunction], 52 | "nameTest3": [MockFunction], 53 | }, 54 | } 55 | `; 56 | 57 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing registry.subscribe with non-grouped obj Checks button group 1`] = ` 58 | Object { 59 | "unrecognized": Object { 60 | "gr1": Set { 61 | "nameTest1", 62 | "nameTest2", 63 | }, 64 | "gr2": Set { 65 | "nameTest3", 66 | }, 67 | }, 68 | } 69 | `; 70 | 71 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing registry.subscribe with non-grouped obj Checks button group 2`] = ` 72 | Object { 73 | "unrecognized": Object { 74 | "gr1": Set { 75 | "nameTest1", 76 | "nameTest2", 77 | }, 78 | "gr2": Set { 79 | "nameTest3", 80 | }, 81 | }, 82 | } 83 | `; 84 | 85 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing registry.subscribe with non-grouped obj Checks trigger handlers 1`] = ` 86 | Object { 87 | "unrecognized": Object { 88 | "nameTest1": [MockFunction], 89 | "nameTest2": [MockFunction], 90 | "nameTest3": [MockFunction], 91 | }, 92 | } 93 | `; 94 | 95 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing registry.subscribe with non-grouped obj Checks trigger handlers 2`] = ` 96 | Object { 97 | "unrecognized": Object { 98 | "nameTest1": [MockFunction], 99 | "nameTest2": [MockFunction], 100 | "nameTest3": [MockFunction], 101 | }, 102 | } 103 | `; 104 | 105 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing registry.subscribe with same ref and group name obj and new value Checks button group 1`] = ` 106 | Object { 107 | "unrecognized": Object { 108 | "gr1": Set { 109 | "nameTest1", 110 | "nameTest2", 111 | }, 112 | "gr2": Set { 113 | "nameTest3", 114 | }, 115 | }, 116 | } 117 | `; 118 | 119 | exports[`Testing subscribe effects on dataHolder/btnGroup Testing registry.subscribe with same ref and group name obj and new value Checks trigger handlers 1`] = ` 120 | Object { 121 | "unrecognized": Object { 122 | "nameTest1": [MockFunction], 123 | "nameTest2": [MockFunction], 124 | "nameTest3": [MockFunction], 125 | }, 126 | } 127 | `; 128 | -------------------------------------------------------------------------------- /packages/folo-store/test/__snapshots__/updater.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Registry updater Toggles boolean value in a group button 1`] = ` 4 | Object { 5 | "unrecognized": Object { 6 | "input1": "some value", 7 | "input2": true, 8 | "input3": false, 9 | }, 10 | } 11 | `; 12 | 13 | exports[`Registry updater Updates string value 1`] = ` 14 | Object { 15 | "unrecognized": Object { 16 | "input1": "some value", 17 | "input2": false, 18 | "input3": false, 19 | }, 20 | } 21 | `; 22 | -------------------------------------------------------------------------------- /packages/folo-store/test/subscribe.test.js: -------------------------------------------------------------------------------- 1 | import Registry from "../src"; 2 | 3 | let registry; 4 | 5 | const MUTUAL_GROUP_MANE_1 = "gr1"; 6 | 7 | const NAME_REF_1 = "nameTest1"; 8 | const INIT_VAL_1 = false; 9 | 10 | const NAME_REF_2 = "nameTest2"; 11 | const INIT_VAL_2 = true; 12 | 13 | const MUTUAL_GROUP_MANE_2 = "gr2"; 14 | 15 | const NAME_REF_3 = "nameTest3"; 16 | const INIT_VAL_3 = true; 17 | 18 | const NAME_REF_4 = "nameTest4"; 19 | const INIT_VAL_4 = "test"; 20 | 21 | const INIT_VAL_5 = "new test value"; 22 | 23 | const GENERAL_STORE_ID = "unrecognized"; 24 | 25 | const handler = jest.fn((newValue) => newValue); 26 | 27 | describe("Testing subscribe effects on dataHolder/btnGroup", () => { 28 | beforeAll(() => { 29 | registry = new Registry(); 30 | }); 31 | 32 | describe("Initializes data Obj holder with first register", () => { 33 | beforeAll(() => { 34 | const obj1 = { 35 | nameRef: NAME_REF_1, 36 | initValue: INIT_VAL_1, 37 | groupName: MUTUAL_GROUP_MANE_1, 38 | }; 39 | 40 | registry.subscribe(obj1, handler); 41 | }); 42 | 43 | it("Checks dataHolder", () => { 44 | expect(registry.dataHolder).toMatchObject({ 45 | [GENERAL_STORE_ID]: { [NAME_REF_1]: INIT_VAL_1 }, 46 | }); 47 | }); 48 | 49 | it("Checks button group", () => { 50 | expect(registry.btnGroup).toMatchSnapshot(); 51 | }); 52 | 53 | it("Checks trigger handlers", () => { 54 | expect(registry.triggers[GENERAL_STORE_ID][NAME_REF_1]).toBeDefined(); 55 | }); 56 | }); 57 | 58 | describe("Pushing a second obj with same group name", () => { 59 | beforeAll(() => { 60 | const obj2 = { 61 | nameRef: NAME_REF_2, 62 | initValue: INIT_VAL_2, 63 | groupName: MUTUAL_GROUP_MANE_1, 64 | }; 65 | 66 | registry.subscribe(obj2, handler); 67 | }); 68 | 69 | it("Checks dataHolder", () => { 70 | expect(registry.dataHolder).toMatchObject({ 71 | [GENERAL_STORE_ID]: { 72 | [NAME_REF_1]: INIT_VAL_1, 73 | [NAME_REF_2]: INIT_VAL_2, 74 | }, 75 | }); 76 | }); 77 | 78 | it("Checks button group", () => { 79 | expect(registry.btnGroup).toMatchSnapshot(); 80 | }); 81 | 82 | it("Checks trigger handlers", () => { 83 | expect(registry.triggers).toMatchSnapshot(); 84 | }); 85 | }); 86 | 87 | describe("Testing 2 branches of button group", () => { 88 | beforeAll(() => { 89 | const obj3 = { 90 | nameRef: NAME_REF_3, 91 | initValue: INIT_VAL_3, 92 | groupName: MUTUAL_GROUP_MANE_2, 93 | }; 94 | 95 | registry.subscribe(obj3, handler); 96 | }); 97 | 98 | it("Checks dataHolder", () => { 99 | expect(registry.dataHolder).toMatchObject({ 100 | [GENERAL_STORE_ID]: { 101 | [NAME_REF_1]: INIT_VAL_1, 102 | [NAME_REF_2]: INIT_VAL_2, 103 | [NAME_REF_3]: INIT_VAL_3, 104 | }, 105 | }); 106 | }); 107 | 108 | it("Checks button group", () => { 109 | expect(registry.btnGroup).toMatchSnapshot(); 110 | }); 111 | 112 | it("Checks trigger handlers", () => { 113 | expect(registry.triggers).toMatchSnapshot(); 114 | }); 115 | }); 116 | 117 | describe("Testing registry.subscribe with non-grouped obj", () => { 118 | beforeAll(() => { 119 | const obj4 = { 120 | nameRef: NAME_REF_4, 121 | initValue: INIT_VAL_4, 122 | groupName: null, 123 | }; 124 | 125 | registry.subscribe(obj4); 126 | }); 127 | 128 | it("Checks dataHolder", () => { 129 | expect(registry.dataHolder).toMatchObject({ 130 | [GENERAL_STORE_ID]: { 131 | [NAME_REF_1]: INIT_VAL_1, 132 | [NAME_REF_2]: INIT_VAL_2, 133 | [NAME_REF_3]: INIT_VAL_3, 134 | [NAME_REF_4]: INIT_VAL_4, 135 | }, 136 | }); 137 | }); 138 | 139 | it("Checks button group", () => { 140 | expect(registry.btnGroup).toMatchSnapshot(); 141 | }); 142 | 143 | it("Checks trigger handlers", () => { 144 | expect(registry.triggers).toMatchSnapshot(); 145 | }); 146 | }); 147 | 148 | describe("Testing registry.subscribe with non-grouped obj", () => { 149 | beforeAll(() => { 150 | const obj4 = { 151 | nameRef: NAME_REF_4, 152 | initValue: INIT_VAL_4, 153 | groupName: null, 154 | }; 155 | 156 | registry.subscribe(obj4, handler); 157 | }); 158 | 159 | it("Checks dataHolder", () => { 160 | expect(registry.dataHolder).toMatchObject({ 161 | [GENERAL_STORE_ID]: { 162 | [NAME_REF_1]: INIT_VAL_1, 163 | [NAME_REF_2]: INIT_VAL_2, 164 | [NAME_REF_3]: INIT_VAL_3, 165 | [NAME_REF_4]: INIT_VAL_4, 166 | }, 167 | }); 168 | }); 169 | 170 | it("Checks button group", () => { 171 | expect(registry.btnGroup).toMatchSnapshot(); 172 | }); 173 | 174 | it("Checks trigger handlers", () => { 175 | expect(registry.triggers).toMatchSnapshot(); 176 | }); 177 | }); 178 | 179 | describe("Testing registry.subscribe with same ref and group name obj and new value", () => { 180 | beforeAll(() => { 181 | const obj5 = { 182 | nameRef: NAME_REF_3, 183 | initValue: INIT_VAL_5, 184 | groupName: MUTUAL_GROUP_MANE_2, 185 | }; 186 | 187 | registry.subscribe(obj5, handler); 188 | }); 189 | 190 | it("Checks dataHolder", () => { 191 | expect(registry.dataHolder).toMatchObject({ 192 | [GENERAL_STORE_ID]: { 193 | [NAME_REF_1]: INIT_VAL_1, 194 | [NAME_REF_2]: INIT_VAL_2, 195 | [NAME_REF_3]: INIT_VAL_5, 196 | [NAME_REF_4]: INIT_VAL_4, 197 | }, 198 | }); 199 | }); 200 | 201 | it("Checks button group", () => { 202 | expect(registry.btnGroup).toMatchSnapshot(); 203 | }); 204 | 205 | it("Checks trigger handlers", () => { 206 | expect(registry.triggers).toMatchSnapshot(); 207 | }); 208 | }); 209 | }); 210 | -------------------------------------------------------------------------------- /packages/folo-store/test/updater.test.js: -------------------------------------------------------------------------------- 1 | import Registry from "../src"; 2 | 3 | let registry; 4 | 5 | const NAME_REF_1 = "input1"; 6 | 7 | const NAME_REF_2 = "input2"; 8 | const NAME_REF_3 = "input3"; 9 | 10 | const BTN_GROUP = "group1"; 11 | 12 | const btnGroup = new Set(); 13 | btnGroup.add(BTN_GROUP); 14 | btnGroup.add(BTN_GROUP); 15 | btnGroup.group1 = new Set(); 16 | btnGroup[BTN_GROUP].add(NAME_REF_2); 17 | btnGroup[BTN_GROUP].add(NAME_REF_3); 18 | 19 | const GENERAL_STORE_ID = "unrecognized"; 20 | 21 | const TEXT_NEW_VALUE = "some value"; 22 | 23 | const updater1 = jest.fn((newValue) => newValue); 24 | const updater2 = jest.fn((newValue) => newValue); 25 | 26 | describe("Registry updater", () => { 27 | beforeAll(() => { 28 | registry = new Registry(); 29 | 30 | const input1 = { 31 | nameRef: NAME_REF_1, 32 | initValue: "", 33 | groupName: null, 34 | }; 35 | registry.subscribe(input1); 36 | 37 | const input2 = { 38 | nameRef: NAME_REF_2, 39 | initValue: false, 40 | groupName: BTN_GROUP, 41 | }; 42 | registry.subscribe(input2, updater1); 43 | 44 | const input3 = { 45 | nameRef: NAME_REF_3, 46 | initValue: false, 47 | groupName: BTN_GROUP, 48 | }; 49 | registry.subscribe(input3, updater2); 50 | }); 51 | 52 | it("Updates string value", () => { 53 | registry.updater({ 54 | nameRef: NAME_REF_1, 55 | newValue: TEXT_NEW_VALUE, 56 | }); 57 | 58 | expect(registry.dataHolder).toStrictEqual({ 59 | [GENERAL_STORE_ID]: { 60 | [NAME_REF_1]: TEXT_NEW_VALUE, 61 | [NAME_REF_2]: false, 62 | [NAME_REF_3]: false, 63 | }, 64 | }); 65 | 66 | expect(registry.dataHolder).toMatchSnapshot(); 67 | }); 68 | 69 | it("Toggles boolean value in a group button", () => { 70 | registry.updater({ 71 | nameRef: NAME_REF_2, 72 | newValue: true, 73 | groupName: BTN_GROUP, 74 | }); 75 | 76 | expect(registry.dataHolder).toStrictEqual({ 77 | [GENERAL_STORE_ID]: { 78 | [NAME_REF_1]: TEXT_NEW_VALUE, 79 | [NAME_REF_2]: true, 80 | [NAME_REF_3]: false, 81 | }, 82 | }); 83 | 84 | expect(updater1).toHaveBeenCalledTimes(0); 85 | expect(updater2).toHaveBeenCalledWith(false); 86 | 87 | expect(registry.dataHolder).toMatchSnapshot(); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /packages/folo-store/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src/**/*"], 4 | 5 | "compilerOptions": { 6 | "outDir": "./lib" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/folo-utils/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/folo-utils/README.md: -------------------------------------------------------------------------------- 1 | # @folo/utils 2 | 3 | > utils used in folo packages 4 | 5 |
6 | 7 | 8 | [![NPM Version](https://img.shields.io/npm/v/@folo/utils.svg)](https://www.npmjs.com/package/@folo/utils) 9 | [![NPM Download](https://img.shields.io/npm/dt/@folo/utils.svg)](https://www.npmjs.com/package/@folo/utils) 10 | [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/react.svg)](https://www.npmjs.com/package/@folo/utils) 11 | [![npm bundle size (gzip)](https://img.shields.io/bundlephobia/minzip/react.svg)](https://www.npmjs.com/package/@folo/utils) 12 | [![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/jalal246/folo/blob/master/LICENSE) 13 | [![CircleCI](https://circleci.com/gh/jalal246/folo/tree/master.svg?style=svg)](https://circleci.com/gh/jalal246/folo/tree/master) 14 | [![Codecov](https://img.shields.io/codecov/c/github/jalal246/folo.svg)](https://codecov.io/gh/jalal246/folo) 15 | 16 | 17 | ## Installation 18 | 19 | ``` 20 | npm install @folo/utils 21 | ``` 22 | 23 | ## License 24 | 25 | This project is licensed under the [MIT License](https://github.com/jalal246/folo/blob/master/LICENSE) 26 | -------------------------------------------------------------------------------- /packages/folo-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@folo/utils", 3 | "version": "0.1.5", 4 | "source": "src/index.js", 5 | "main": "dist/foloUtils.min.cjs.js", 6 | "umd:main": "dist/foloUtils.min.umd.js", 7 | "author": "Jalal Maskoun ", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "jest", 11 | "build": "builderz --output=foloUtils" 12 | }, 13 | "repository": "https://github.com/jalal246/folo/tree/master/packages/folo-utils", 14 | "files": [ 15 | "src", 16 | "dist", 17 | "LICENSE" 18 | ], 19 | "keywords": [ 20 | "@folo", 21 | "@folo/auto-position", 22 | "@folo/forms", 23 | "@folo/layout", 24 | "@folo/store", 25 | "@folo/utils", 26 | "@folo/values", 27 | "@folo/withcontext", 28 | "dflex", 29 | "react", 30 | "react-component", 31 | "css-grid", 32 | "css-layout", 33 | "form-builder-api", 34 | "dynamic-forms" 35 | ], 36 | "publishConfig": { 37 | "registry": "https://registry.npmjs.org/", 38 | "access": "public" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/folo-utils/src/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | export { keyGenerator } from "./utils"; 3 | -------------------------------------------------------------------------------- /packages/folo-utils/src/utils.js: -------------------------------------------------------------------------------- 1 | let lastId = 0; 2 | 3 | /** 4 | * generate unique ID for key 5 | * @param {String} prefix the prefix for the id 6 | * @return {String} the unique ID 7 | */ 8 | // eslint-disable-next-line 9 | export function keyGenerator(prefix = "folo") { 10 | lastId += 1; 11 | return prefix + lastId; 12 | } 13 | -------------------------------------------------------------------------------- /packages/folo-values/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint-config-folo-react"], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/folo-values/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/folo-values/README.md: -------------------------------------------------------------------------------- 1 | # 📋 @folo/values 2 | 3 | > A from store returns input values with zero config 4 | 5 | 6 | 7 | ![live example](https://raw.githubusercontent.com/jalal246/folo/master/packages/folo-values/foloValues-demo.gif) 8 | 9 | 10 | [![NPM Version](https://img.shields.io/npm/v/@folo/values.svg)](https://www.npmjs.com/package/@folo/values) 11 | [![NPM Download](https://img.shields.io/npm/dt/@folo/values.svg)](https://www.npmjs.com/package/@folo/values) 12 | [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/react.svg)](https://www.npmjs.com/package/@folo/values) 13 | [![npm bundle size (gzip)](https://img.shields.io/bundlephobia/minzip/react.svg)](https://www.npmjs.com/package/@folo/values) 14 | [![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/jalal246/folo/blob/master/packages/folo-values/LICENSE) 15 | [![CI](https://img.shields.io/github/workflow/status/jalal246/folo/CI)](https://github.com/jalal246/folo/tree/master) 16 | [![Codecov](https://img.shields.io/codecov/c/github/jalal246/folo.svg)](https://codecov.io/gh/jalal246/folo) 17 | 18 | 19 | ## Installation 20 | 21 | ```sh 22 | npm install @folo/values 23 | ``` 24 | 25 | - ☑️ A simple, lightweight package, comes with two components connected to global 26 | JavaScript store. That's it. No complexity, no unnecessary code. 27 | 28 | - ☑️ Instead of implementing your own store, `@folo/values` can do it for you with a 29 | store that holds inputs and knows exactly when to trigger connected components 30 | to re-render. 31 | 32 | - ☑️ Doesn't require using Redux/Mobx/Context. While these technologies are 33 | amazing it always comes with a cost. That's why every update that happens in `Folo` 34 | happens locally. The store is just the Maestro. 35 | 36 | - ☑️ You can add multiple forms connect them to the store or create branches 37 | yourself. It's all about `StoreID` my friends. 38 | 39 | - ☑️ Friendly code. What you do for a form written in JS, Can do it here. No 40 | external API. No external functionality. `onSubmit`, `onChange` are still here and 41 | not going anywhere. 42 | 43 | - ☑️ It's well tested code, with nearly 100% of code coverage 🥳 44 | 45 | ## Usage 46 | 47 | ```js 48 | import { Form, Field } from "@folo/values"; 49 | 50 | const MyComponent = ({ onSubmit }) => ( 51 |
52 | 56 | 60 | 68 | 69 |
70 | ); 71 | // submit function will return: (event, {myName: "" myPass: "", alphabet:""}) 72 | ``` 73 | 74 | ## Available Components 75 | 76 | ```js 77 | import { Form, Field } from "@folo/values"; 78 | ``` 79 | 80 | All components accept custom props + children which is required in all except `Field` 81 | 82 | ### Form 83 | 84 | | property | type | description | default | 85 | | ----------- | ------------------ | ---------------------------------------------------------------- | -------------- | 86 | | `component` | HTMLElement/string | custom render-component | `form` | 87 | | `onSubmit` | function | submit function returns values in all cells (event, {...values}) | `() {}` | 88 | | `storeID` | string | unique id shared with form and fields | `unrecognized` | 89 | 90 | ### Field 91 | 92 | Essential to register values in the store, returns its value when submit. 93 | Accepts all events handlers. 94 | 95 | | property | type | description | default | 96 | | ----------- | ------------------ | ---------------------------------------- | ------------------- | 97 | | `component` | HTMLElement/string | custom render-component | `div` | 98 | | `storeID` | string | unique id shared with form and fields | `unrecognized` | 99 | | `valueKey` | string | key used to store value in values object | `id` or `timestamp` | 100 | | `value` | string | Initial value if type is not a button | `""` | 101 | | `checked` | Boolean | Initial value if type is a button | `false` | 102 | | `type` | string | Input type | `text` | 103 | | `groupName` | string | only for button toggle group | `null` | 104 | 105 | ## Examples 106 | 107 | You can clone all the examples used in these packages 108 | [here](https://github.com/jalal246/folo/tree/master/packages/folo-values/examples). 109 | With an example for [Simple 110 | Form](https://jalal246.github.io/folo/?path=/story/forms-forms-with-submit--simple-form) 111 | Or a [Custom 112 | Form](https://jalal246.github.io/folo/?path=/story/forms-forms-with-submit--custom-components) 113 | built with custom components. It includes All [Available 114 | fields](https://jalal246.github.io/folo/?path=/story/forms-available-fields--default-input) 115 | and examples that show you how to [handle toggle button groups](https://jalal246.github.io/folo/?path=/story/forms-toggle-groups--group-toggle-no-init-value). 116 | 117 | ## Test 118 | 119 | ```sh 120 | yarn test folo-values 121 | ``` 122 | 123 | ## Contribution 😇 124 | 125 | If you have ideas or issues don't hesitate. You are always welcome. 126 | 127 | ## License 128 | 129 | This project is licensed under the [MIT License](https://github.com/jalal246/folo/blob/master/packages/folo-values/LICENSE) 130 | -------------------------------------------------------------------------------- /packages/folo-values/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [["@babel/preset-env"], ["@babel/preset-react"]], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/folo-values/examples/AddressForm.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-wrap-multilines */ 2 | /* eslint-disable import/no-extraneous-dependencies */ 3 | 4 | import React from "react"; 5 | 6 | import Grid from "@material-ui/core/Grid"; 7 | import Typography from "@material-ui/core/Typography"; 8 | import TextField from "@material-ui/core/TextField"; 9 | import FormControlLabel from "@material-ui/core/FormControlLabel"; 10 | import Checkbox from "@material-ui/core/Checkbox"; 11 | import Button from "@material-ui/core/Button"; 12 | 13 | import Field from "../src/components/Field"; 14 | import Form from "../src/components/Form"; 15 | 16 | const AddressForm = ({ onSubmit }) => ( 17 |
24 |
33 | 34 | Shipping address 35 | 36 |
37 | 38 | 39 | 48 | 49 | 50 | 59 | 60 | 61 | 70 | 71 | 72 | 81 | 82 | 83 | 92 | 93 | 94 | 102 | 103 | 104 | 113 | 114 | 115 | 124 | 125 | 126 | 137 | } 138 | label="Use this address for payment details" 139 | /> 140 | 141 | 142 | 143 | 146 | 147 |
148 |
149 |
150 | ); 151 | 152 | export default AddressForm; 153 | -------------------------------------------------------------------------------- /packages/folo-values/examples/BasicForm.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/label-has-associated-control */ 2 | import React from "react"; 3 | 4 | import Field from "../src/components/Field"; 5 | import Form from "../src/components/Form"; 6 | 7 | const styleForm = { 8 | display: "flex", 9 | flexDirection: "column", 10 | background: "#d4e2d4", 11 | width: "40%", 12 | margin: "10px", 13 | padding: "17px", 14 | }; 15 | 16 | const styleLabel = { 17 | marginTop: "20px", 18 | fontSize: "18px", 19 | }; 20 | 21 | const styleText = { 22 | paddingRight: "10px", 23 | }; 24 | 25 | const BasicForm = ({ onSubmit }) => ( 26 |
27 | 36 | 45 | 55 | 63 | 66 |
67 | ); 68 | 69 | export default BasicForm; 70 | -------------------------------------------------------------------------------- /packages/folo-values/examples/GroupToggle.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Field from "../src/components/Field"; 4 | import Form from "../src/components/Form"; 5 | 6 | export const GroupToggleNoInitValue = () => ( 7 |
8 | 14 | 20 | 26 | 27 | ); 28 | 29 | export const GroupToggleWithInitValue = () => ( 30 |
31 | 38 | 44 | 50 | 51 | ); 52 | 53 | export const GroupToggleDifferentGroupName = () => ( 54 |
55 | 62 | 68 | 75 | 81 | 82 | ); 83 | -------------------------------------------------------------------------------- /packages/folo-values/foloValues-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jalal246/folo/f9d5577f0e9e86bbbf5461a22041f583222d986b/packages/folo-values/foloValues-demo.gif -------------------------------------------------------------------------------- /packages/folo-values/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@folo/values", 3 | "version": "1.0.3", 4 | "source": "src/index.js", 5 | "main": "dist/foloValues.min.cjs.js", 6 | "umd:main": "dist/foloValues.min.umd.js", 7 | "author": "Jalal Maskoun ", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "jest", 11 | "build": "builderz --output=foloValues" 12 | }, 13 | "homepage": "https://github.com/jalal246/folo/tree/master/packages/folo-values", 14 | "repository": "https://github.com/jalal246/folo", 15 | "bundledDependencies": [ 16 | "@folo/store" 17 | ], 18 | "peerDependencies": { 19 | "react": ">=16" 20 | }, 21 | "devDependencies": { 22 | "@material-ui/core": "^4.11.2", 23 | "react": "^17.0.1" 24 | }, 25 | "files": [ 26 | "src", 27 | "dist", 28 | "LICENSE" 29 | ], 30 | "keywords": [ 31 | "@folo", 32 | "@folo/auto-position", 33 | "@folo/forms", 34 | "@folo/layout", 35 | "@folo/store", 36 | "@folo/utils", 37 | "@folo/values", 38 | "@folo/withcontext", 39 | "@dflex", 40 | "react", 41 | "react-component", 42 | "form-builder-api", 43 | "dynamic-forms", 44 | "store", 45 | "json", 46 | "forms", 47 | "storage" 48 | ], 49 | "publishConfig": { 50 | "registry": "https://registry.npmjs.org/", 51 | "access": "public" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/folo-values/src/components/Core.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import registry from "../valuesStore"; 4 | 5 | const BLUR = "blur"; 6 | 7 | /** 8 | * manage value updates for all cell types 9 | * all controlled 10 | */ 11 | const Core = ({ 12 | coreComponent: Component, 13 | initValue, 14 | nameRef, 15 | groupName, 16 | valueRef, 17 | isInput, 18 | storeID, 19 | onBlur: onBlurProps, 20 | onChange: onChangeProps, 21 | children, 22 | ...rest 23 | }) => { 24 | const [localValue, setValue] = React.useState(initValue); 25 | 26 | React.useEffect(() => { 27 | registry.subscribe( 28 | { 29 | nameRef, 30 | initValue, 31 | groupName, 32 | storeID, 33 | }, 34 | setValue 35 | ); 36 | }, [initValue]); 37 | 38 | function eventHandler(e) { 39 | const { 40 | target: { [valueRef]: newValue }, 41 | type, 42 | } = e; 43 | 44 | if (type === BLUR && typeof onBlurProps === "function") { 45 | // trigger onBlur coming form props 46 | onBlurProps(e); 47 | } else { 48 | // update local value while the change is happening 49 | // don't notify the global store yet 50 | setValue(newValue); 51 | 52 | if (typeof onChangeProps === "function") { 53 | onChangeProps(e); 54 | } 55 | } 56 | 57 | if (!isInput || type === BLUR) { 58 | // inform the store with the new changes we have here 59 | // only when bur or change happens in non-input element 60 | registry.updater({ 61 | nameRef, 62 | newValue, 63 | groupName, 64 | storeID, 65 | }); 66 | } 67 | } 68 | 69 | return ( 70 | 76 | {children} 77 | 78 | ); 79 | }; 80 | 81 | export default Core; 82 | -------------------------------------------------------------------------------- /packages/folo-values/src/components/Field.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Core from "./Core"; 4 | 5 | import cellRecognizer from "../utils/cellRecognizer"; 6 | import { TEXT } from "../constants"; 7 | 8 | const Field = ({ 9 | component, 10 | valueKey, 11 | id, 12 | value = "", 13 | checked = false, 14 | type = TEXT, 15 | groupName = null, 16 | storeID, 17 | children, 18 | ...rest 19 | }) => { 20 | const { 21 | valueRef, 22 | isInput, 23 | initValue, 24 | RecommendedComponent, 25 | } = cellRecognizer({ type, checked, value }); 26 | 27 | const nameRef = valueKey || id || `${new Date().getTime()}`; 28 | 29 | return ( 30 | 42 | {children} 43 | 44 | ); 45 | }; 46 | 47 | export default Field; 48 | -------------------------------------------------------------------------------- /packages/folo-values/src/components/Form.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import registry from "../valuesStore"; 4 | 5 | const Form = ({ 6 | component: FormComponent = "form", 7 | onSubmit: onSubmitProps, 8 | storeID, 9 | children, 10 | ...rest 11 | }) => { 12 | React.useEffect( 13 | () => 14 | function cleanup() { 15 | registry.clear(storeID); 16 | } 17 | ); 18 | 19 | function onSubmit(e) { 20 | e.preventDefault(); 21 | 22 | if (typeof onSubmitProps === "function") { 23 | onSubmitProps(e, registry.getDataByStoreID(storeID)); 24 | } 25 | } 26 | 27 | return ( 28 | 29 | {children} 30 | 31 | ); 32 | }; 33 | 34 | export default Form; 35 | -------------------------------------------------------------------------------- /packages/folo-values/src/constants.js: -------------------------------------------------------------------------------- 1 | export const VALUE = "value"; 2 | export const CHECKED = "checked"; 3 | 4 | export const TEXT = "text"; 5 | 6 | export const INPUT = "input"; 7 | export const SELECT = "select"; 8 | export const LIST = "list"; 9 | export const CHECKBOX = "checkbox"; 10 | export const RADIO = "radio"; 11 | export const BTN = "button"; 12 | -------------------------------------------------------------------------------- /packages/folo-values/src/index.js: -------------------------------------------------------------------------------- 1 | import Field from "./components/Field"; 2 | import Form from "./components/Form"; 3 | 4 | export { Field, Form }; 5 | -------------------------------------------------------------------------------- /packages/folo-values/src/utils/cellRecognizer.js: -------------------------------------------------------------------------------- 1 | import { 2 | VALUE, 3 | CHECKED, 4 | SELECT, 5 | LIST, 6 | CHECKBOX, 7 | RADIO, 8 | INPUT, 9 | } from "../constants"; 10 | 11 | /** 12 | * Gets the cell type 13 | * returns booleans type flag. 14 | * 15 | * @param {string} type 16 | * @param {boolean} checked 17 | * @param {string} value 18 | * @return {{isInput:boolean, valueRef: string, initValue: string||boolean, RecommendedComponent: string }} 19 | */ 20 | function cellRecognizer({ type, checked, value }) { 21 | // only true when cell is button 22 | let isInput = false; 23 | 24 | // input or select 25 | let RecommendedComponent = INPUT; 26 | 27 | // value ref to the element: value or checked; depends on the type 28 | let valueRef = VALUE; 29 | 30 | // is it boolean or string; depends on the type 31 | let initValue = value; 32 | 33 | if (type === SELECT || type === LIST) { 34 | RecommendedComponent = SELECT; 35 | } else if (type === CHECKBOX || type === RADIO) { 36 | valueRef = CHECKED; 37 | initValue = checked; 38 | } else { 39 | isInput = true; 40 | } 41 | 42 | return { 43 | isInput, 44 | valueRef, 45 | initValue, 46 | RecommendedComponent, 47 | }; 48 | } 49 | 50 | export default cellRecognizer; 51 | -------------------------------------------------------------------------------- /packages/folo-values/src/valuesStore.js: -------------------------------------------------------------------------------- 1 | import Registry from "@folo/store/src"; 2 | 3 | export default (function init() { 4 | const registry = new Registry(); 5 | 6 | return registry; 7 | })(); 8 | -------------------------------------------------------------------------------- /packages/folo-values/stories/Field.stories.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from "react"; 3 | import TextField from "@material-ui/core/TextField"; 4 | 5 | import Field from "../src/components/Field"; 6 | 7 | export default { 8 | title: "Forms/Available Fields", 9 | component: Field, 10 | argTypes: { 11 | onChange: { 12 | action: "onChange", 13 | }, 14 | onBlur: { 15 | action: "onBlur", 16 | }, 17 | }, 18 | }; 19 | 20 | const Template = (args) => ; 21 | 22 | export const DefaultInput = Template.bind({}); 23 | 24 | export const TextInput = Template.bind({}); 25 | TextInput.args = { 26 | type: "text", 27 | value: "Initial Value", 28 | }; 29 | 30 | export const EmailInputWithCustomComponent = Template.bind({}); 31 | EmailInputWithCustomComponent.args = { 32 | type: "email", 33 | component: TextField, 34 | label: "Name", 35 | margin: "normal", 36 | }; 37 | 38 | export const PasswordInputWithHandlers = Template.bind({}); 39 | PasswordInputWithHandlers.args = { 40 | type: "password", 41 | // onBlur:{action("onBlur")} 42 | }; 43 | 44 | export const RadioButtonInput = Template.bind({}); 45 | RadioButtonInput.args = { 46 | type: "radio", 47 | groupName: "choices", 48 | }; 49 | 50 | export const CheckboxInput = Template.bind({}); 51 | CheckboxInput.args = { 52 | type: "checkbox", 53 | }; 54 | 55 | export const ColorInput = Template.bind({}); 56 | ColorInput.args = { 57 | type: "color", 58 | }; 59 | 60 | export const DateInput = Template.bind({}); 61 | DateInput.args = { 62 | type: "date", 63 | }; 64 | 65 | export const SelectOpts = Template.bind({}); 66 | SelectOpts.args = { 67 | type: "select", 68 | children: ( 69 | <> 70 | 71 | 72 | 73 | 74 | ), 75 | }; 76 | -------------------------------------------------------------------------------- /packages/folo-values/stories/FormsSubmit.stories.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/label-has-associated-control */ 2 | import React from "react"; 3 | 4 | import Form from "../src/components/Form"; 5 | 6 | import BasicForm from "../examples/BasicForm"; 7 | import AddressForm from "../examples/AddressForm"; 8 | 9 | export default { 10 | title: "Forms/Forms with Submit", 11 | component: Form, 12 | argTypes: { 13 | onSubmit: { 14 | action: "onSubmit", 15 | }, 16 | }, 17 | }; 18 | 19 | export const SimpleForm = (args) => ; 20 | 21 | export const CustomComponents = (args) => ; 22 | -------------------------------------------------------------------------------- /packages/folo-values/stories/FormsToggle.stories.js: -------------------------------------------------------------------------------- 1 | import Form from "../src/components/Form"; 2 | 3 | import { 4 | GroupToggleNoInitValue, 5 | GroupToggleWithInitValue, 6 | GroupToggleDifferentGroupName, 7 | } from "../examples/GroupToggle"; 8 | 9 | export default { 10 | title: "Forms/Toggle groups", 11 | component: Form, 12 | onSubmit: { 13 | action: "onSubmit", 14 | }, 15 | }; 16 | 17 | export { GroupToggleNoInitValue }; 18 | 19 | export { GroupToggleWithInitValue }; 20 | 21 | export { GroupToggleDifferentGroupName }; 22 | -------------------------------------------------------------------------------- /packages/folo-values/test/Field.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, fireEvent, screen, cleanup } from "@testing-library/react"; 3 | 4 | import Field from "../src/components/Field"; 5 | import Form from "../src/components/Form"; 6 | 7 | const TEST_TEXT_1 = "default"; 8 | const TEST_BTN_1 = "btn1"; 9 | const TEST_BTN_2 = "btn2"; 10 | const TEST_FORM = "form"; 11 | 12 | const GROUP_NAME = "group1"; 13 | 14 | const NEW_VAL = "hello!"; 15 | 16 | const onSubmitMock = jest.fn((e, value) => value); 17 | 18 | const App = () => ( 19 |
20 | 21 | 27 | 34 | 35 | ); 36 | 37 | let txt; 38 | let btn1; 39 | let btn2; 40 | let form; 41 | 42 | describe("Testing Fields & Form", () => { 43 | beforeEach(() => { 44 | render(); 45 | 46 | txt = screen.getByTestId(TEST_TEXT_1); 47 | 48 | btn1 = screen.getByTestId(TEST_BTN_1); 49 | btn2 = screen.getByTestId(TEST_BTN_2); 50 | 51 | form = screen.getByTestId(TEST_FORM); 52 | }); 53 | 54 | afterAll(() => { 55 | cleanup(); 56 | }); 57 | 58 | describe("Testing submit event on form for initial value", () => { 59 | it("returns context value when submit", () => { 60 | fireEvent.submit(form, {}); 61 | 62 | // to 63 | expect(onSubmitMock).toHaveReturnedWith( 64 | expect.objectContaining({ 65 | [TEST_TEXT_1]: "", 66 | [TEST_BTN_1]: false, 67 | [TEST_BTN_2]: true, 68 | }) 69 | ); 70 | }); 71 | }); 72 | 73 | describe("Testing toggle group in buttons", () => { 74 | it("Gets initial values", () => { 75 | expect(btn1.checked).toBe(false); 76 | }); 77 | 78 | it("Sets one btn to false doesn't toggle the other one which is false", () => { 79 | fireEvent.blur(btn2, { target: { checked: false } }); 80 | 81 | expect(btn1.checked).toBe(false); 82 | expect(btn2.checked).toBe(false); 83 | }); 84 | 85 | it("Sets one btn to true doesn't toggle the other one which is false", () => { 86 | fireEvent.blur(btn2, { target: { checked: true } }); 87 | 88 | expect(btn1.checked).toBe(false); 89 | expect(btn2.checked).toBe(true); 90 | }); 91 | 92 | it("Sets one btn to true, toggles the other to false", () => { 93 | fireEvent.blur(btn1, { target: { checked: true } }); 94 | expect(btn1.checked).toBe(true); 95 | expect(btn2.checked).toBe(false); 96 | }); 97 | 98 | describe("Testing text input", () => { 99 | it("Gets initial values", () => { 100 | expect(txt.value).toBe(""); 101 | }); 102 | 103 | it("Sets new value to text input", () => { 104 | fireEvent.change(txt, { target: { value: NEW_VAL } }); 105 | 106 | expect(txt.value).toBe(NEW_VAL); 107 | }); 108 | 109 | it("sets the same value to text input", () => { 110 | fireEvent.change(txt, { target: { value: NEW_VAL } }); 111 | 112 | expect(txt.value).toBe(NEW_VAL); 113 | }); 114 | }); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /packages/folo-values/test/cellRecognizer.test.js: -------------------------------------------------------------------------------- 1 | import cellRecognizer from "../src/utils/cellRecognizer"; 2 | 3 | import { 4 | VALUE, 5 | CHECKED, 6 | SELECT, 7 | LIST, 8 | CHECKBOX, 9 | RADIO, 10 | INPUT, 11 | } from "../src/constants"; 12 | 13 | const MY_VAL = "HELLO WORLD!"; 14 | 15 | describe("cellRecognizer function", () => { 16 | it("Testing type=INPUT", () => { 17 | expect( 18 | cellRecognizer({ type: INPUT, checked: false, value: MY_VAL }) 19 | ).toMatchObject({ 20 | valueRef: VALUE, 21 | isInput: true, 22 | initValue: MY_VAL, 23 | RecommendedComponent: INPUT, 24 | }); 25 | }); 26 | 27 | it("Testing type=CHECKBOX or RADIO", () => { 28 | const RESULT_CHECKBOX = cellRecognizer({ 29 | type: CHECKBOX, 30 | checked: true, 31 | value: MY_VAL, 32 | }); 33 | 34 | const RESULT_RADIO = cellRecognizer({ 35 | type: RADIO, 36 | checked: true, 37 | value: MY_VAL, 38 | }); 39 | 40 | const expected = { 41 | valueRef: CHECKED, 42 | isInput: false, 43 | initValue: true, 44 | RecommendedComponent: INPUT, 45 | }; 46 | 47 | expect(RESULT_CHECKBOX).toMatchObject(expected); 48 | expect(RESULT_RADIO).toMatchObject(expected); 49 | }); 50 | 51 | it("Testing type=SELECT or LIST", () => { 52 | const RESULT_SELECT = cellRecognizer({ 53 | type: SELECT, 54 | checked: true, 55 | value: MY_VAL, 56 | }); 57 | 58 | const RESULT_LIST = cellRecognizer({ 59 | type: LIST, 60 | checked: true, 61 | value: MY_VAL, 62 | }); 63 | 64 | const expected = { 65 | valueRef: VALUE, 66 | isInput: false, 67 | initValue: MY_VAL, 68 | RecommendedComponent: SELECT, 69 | }; 70 | 71 | expect(RESULT_SELECT).toMatchObject(expected); 72 | expect(RESULT_LIST).toMatchObject(expected); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /packages/folo-values/test/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom"; 6 | -------------------------------------------------------------------------------- /packages/folo-withcontext/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Jalal Maskoun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/folo-withcontext/README.md: -------------------------------------------------------------------------------- 1 | # @folo/withcontext 2 | 3 | > micro HOC compose component accepts custom context values as props 4 | 5 |
6 | 7 | 8 | [![NPM Version](https://img.shields.io/npm/v/@folo/withcontext.svg)](https://www.npmjs.com/package/@folo/withcontext) 9 | [![NPM Download](https://img.shields.io/npm/dt/@folo/withcontext.svg)](https://www.npmjs.com/package/@folo/withcontext) 10 | [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/react.svg)](https://www.npmjs.com/package/@folo/withcontext) 11 | [![npm bundle size (gzip)](https://img.shields.io/bundlephobia/minzip/react.svg)](https://www.npmjs.com/package/@folo/withcontext) 12 | [![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/jalal246/folo/blob/master/LICENSE) 13 | [![CircleCI](https://circleci.com/gh/jalal246/folo/tree/master.svg?style=svg)](https://circleci.com/gh/jalal246/folo/tree/master) 14 | [![Codecov](https://img.shields.io/codecov/c/github/jalal246/folo.svg)](https://codecov.io/gh/jalal246/folo) 15 | 16 | 17 | ## Installation 18 | 19 | ``` 20 | npm install @folo/withcontext 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```js 26 | import withcontext from "@folo/withcontext"; 27 | 28 | const ComponentWithContext = withcontext({ 29 | Component: MyComponent, 30 | Consumer, 31 | contextProps: ["prop1", "prop4"], // with no contextProps provided, it accepts all context props 32 | }); 33 | ``` 34 | 35 | You can compose all context props by not passing `contextProps` 36 | 37 | ```js 38 | const ComponentWithAllContextProps = withcontext({ 39 | Component: MyComponent, 40 | Consumer, 41 | }); 42 | ``` 43 | 44 | ## License 45 | 46 | This project is licensed under the [MIT License](https://github.com/jalal246/folo/blob/master/LICENSE) 47 | -------------------------------------------------------------------------------- /packages/folo-withcontext/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@folo/withcontext", 3 | "version": "0.1.5", 4 | "description": "micro HOC compose component accepts custom context values as props", 5 | "main": "dist/foloWithcontext.cjs.js", 6 | "umd:main": "dist/foloWithcontext.min.umd.js", 7 | "module": "dist/foloWithcontext.esm.js", 8 | "scripts": {}, 9 | "repository": "https://github.com/jalal246/folo/tree/master/packages/folo-withcontext", 10 | "author": "Jalal Maskoun ", 11 | "license": "MIT", 12 | "bugs": { 13 | "url": "https://github.com/jalal246/folo/issues" 14 | }, 15 | "homepage": "https://github.com/jalal246/folo", 16 | "files": [ 17 | "src", 18 | "dist", 19 | "README.md" 20 | ], 21 | "keywords": [ 22 | "@folo", 23 | "@folo/forms", 24 | "@folo/withcontext", 25 | "@folo/values", 26 | "@folo/layout", 27 | "@folo/utils", 28 | "react", 29 | "react-component", 30 | "utility" 31 | ], 32 | "peerDependencies": { 33 | "react": "^16.4.1" 34 | }, 35 | "publishConfig": { 36 | "access": "public" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/folo-withcontext/src/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./withcontext"; 2 | -------------------------------------------------------------------------------- /packages/folo-withcontext/src/withcontext.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | /** 4 | * HOC component 5 | * Connect component to props 6 | * 7 | * @param {Component} targeted_component 8 | * @param {Component} consumer_context 9 | * @param {Array} contextProps contains props required from consumer 10 | * @return {Component} - new component connected to context props 11 | */ 12 | export default function ({ Component, Consumer, contextProps = [] }) { 13 | return function ComponentWithContext(props) { 14 | return ( 15 | 16 | {(context) => { 17 | let cn = contextProps.length > 0 ? {} : context; 18 | /** 19 | * if contextProps length is zero, pass all context props 20 | * otherwise extract the required props 21 | */ 22 | if (contextProps.length > 0) { 23 | contextProps.forEach((prop) => { 24 | cn[prop] = context[prop]; 25 | }); 26 | } 27 | return React.createElement(Component, Object.assign({}, props, cn)); 28 | }} 29 | 30 | ); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /packages/folo-withcontext/test/withContext.test.jsx: -------------------------------------------------------------------------------- 1 | import React, { createContext } from "react"; 2 | import { render, cleanup } from "react-testing-library"; 3 | 4 | import withcontext from "../src/withcontext"; 5 | 6 | const orginalProps = { 7 | type: "text" 8 | }; 9 | 10 | const MyComponent = ({ 11 | type = orginalProps.type, 12 | /* form context? */ disabled, 13 | /* form context? */ id, 14 | /* form context */ value, 15 | /* form context */ placeholder 16 | }) => ( 17 | {}} 21 | value={value} 22 | placeholder={placeholder} 23 | disabled={disabled} 24 | id={id} 25 | /> 26 | ); 27 | 28 | const contextProps = { 29 | value: "test", 30 | placeholder: "Username", 31 | id: "unique", 32 | disabled: true 33 | }; 34 | 35 | const { Consumer, Provider } = createContext({}); 36 | 37 | const CNProvider = ({ children }) => ( 38 | {children} 39 | ); 40 | 41 | let MyComponentWIithContextConsumer; 42 | 43 | const App = () => ( 44 | 45 | 46 | 47 | ); 48 | 49 | afterEach(cleanup); 50 | 51 | describe("withcontext", () => { 52 | describe("custom context props", () => { 53 | MyComponentWIithContextConsumer = withcontext({ 54 | Component: MyComponent, 55 | Consumer, 56 | contextProps: ["value", "placeholder"] 57 | }); 58 | 59 | const { getByTestId } = render(); 60 | 61 | const { value, placeholder, type, disabled } = getByTestId( 62 | "withcontextTest" 63 | ); 64 | 65 | it("returns component props bind with context values", () => { 66 | expect(value).toBe(contextProps.value); 67 | 68 | expect(placeholder).toBe(contextProps.placeholder); 69 | }); 70 | 71 | it("retruns all context props when contextProps is not defined", () => { 72 | expect(disabled).toBe(false); 73 | }); 74 | 75 | it("orginal component props still exist", () => { 76 | expect(type).toBe(orginalProps.type); 77 | }); 78 | }); 79 | 80 | describe("all context props", () => { 81 | it("does not have all context props, just the required", () => { 82 | MyComponentWIithContextConsumer = withcontext({ 83 | Component: MyComponent, 84 | Consumer 85 | }); 86 | 87 | const { getByTestId } = render(); 88 | 89 | const { disabled, id } = getByTestId("withcontextTest"); 90 | 91 | expect(disabled).toBe(true); 92 | 93 | expect(id).toBe(contextProps.id); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /scripts/babel-preset-folo-dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-preset-folo-dev", 3 | "main": "src/index", 4 | "version": "0.1.2", 5 | "private": true, 6 | "author": "Jalal Maskoun ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "@babel/core": "^7.1.6", 10 | "@babel/plugin-proposal-class-properties": "^7.3.0", 11 | "@babel/plugin-proposal-export-default-from": "^7.0.0", 12 | "@babel/plugin-proposal-object-rest-spread": "^7.3.2", 13 | "@babel/plugin-transform-runtime": "^7.1.0", 14 | "@babel/preset-env": "^7.3.1", 15 | "@babel/preset-react": "^7.0.0", 16 | "@babel/runtime": "^7.3.1", 17 | "babel-plugin-minify-dead-code-elimination": "^0.5.0", 18 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scripts/babel-preset-folo-dev/src/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | BUILD_FORMAT = process.env.BUILD_FORMAT, 3 | BABEL_ENV = process.env.BABEL_ENV || process.env.NODE_ENV || "development" 4 | }) => { 5 | const preset = { 6 | presets: [ 7 | [ 8 | require.resolve("@babel/preset-env"), 9 | { 10 | modules: BABEL_ENV === "test" ? "cjs" : false, 11 | loose: true 12 | } 13 | ], 14 | 15 | [require.resolve("@babel/preset-react")] 16 | ], 17 | 18 | plugins: [ 19 | [ 20 | // By default, this plugin uses Babel's extends helper which polyfills Object.assign. 21 | // Enabling useBuiltIns option will use Object.assign directly. 22 | require.resolve("@babel/plugin-proposal-object-rest-spread"), 23 | { 24 | useBuiltIns: true 25 | } 26 | ], 27 | 28 | // Compile export default to ES2015 29 | [require.resolve("@babel/plugin-proposal-export-default-from")], 30 | 31 | // This plugin transforms static class properties as well as properties declared with the property initializer syntax 32 | [ 33 | require.resolve("@babel/plugin-proposal-class-properties"), 34 | { loose: true } 35 | ] 36 | ] 37 | }; 38 | 39 | if (BABEL_ENV === "production") { 40 | preset.plugins.push.apply(preset.plugins, [ 41 | [ 42 | require.resolve("@babel/plugin-transform-runtime"), 43 | { 44 | useESModules: BUILD_FORMAT !== "cjs" 45 | } 46 | ], 47 | 48 | [ 49 | require.resolve("babel-plugin-transform-react-remove-prop-types"), 50 | { 51 | mode: "remove", 52 | removeImport: true 53 | } 54 | ], 55 | [ 56 | // remove inused code 57 | require.resolve("babel-plugin-minify-dead-code-elimination") 58 | ] 59 | ]); 60 | } 61 | 62 | // solving this issue: https://github.com/webpack/webpack/issues/4039 63 | if (BABEL_ENV === "storybook") { 64 | preset.plugins.push.apply(preset.plugins, [ 65 | [require.resolve("@babel/plugin-transform-modules-commonjs")] 66 | ]); 67 | } 68 | return preset; 69 | }; 70 | -------------------------------------------------------------------------------- /scripts/babel-preset-folo-dev/src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./babel.config.js"); 2 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo-react/config.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["airbnb", "prettier", "plugin:react/recommended"], 3 | parserOptions: { 4 | ecmaFeatures: { 5 | jsx: true, 6 | }, 7 | ecmaVersion: 2018, 8 | sourceType: "module", 9 | }, 10 | plugins: ["react"], 11 | rules: { 12 | "linebreak-style": 0, 13 | "react/jsx-filename-extension": 0, 14 | "react/prop-types": 0, 15 | "comma-dangle": 0, 16 | "react/jsx-props-no-spreading": 0, 17 | "import/no-unresolved": ["error", { ignore: ["^react$"] }], 18 | "import/no-extraneous-dependencies": "off", 19 | }, 20 | settings: { 21 | "import/resolver": { 22 | node: { 23 | extensions: [".js", ".jsx", ".ts", ".tsx"], 24 | }, 25 | }, 26 | }, 27 | overrides: [ 28 | { 29 | files: ["packages/**/stories/*.stories.js"], 30 | rules: { 31 | "jsx-a11y/label-has-associated-control": "off", 32 | "jsx-a11y/label-has-for": "off", 33 | }, 34 | }, 35 | { 36 | files: ["packages/**/test/*.test.js"], 37 | rules: { 38 | "import/no-extraneous-dependencies": "off", 39 | }, 40 | }, 41 | ], 42 | }; 43 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo-react/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./config.eslintrc.js"); 2 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-folo-react", 3 | "version": "0.1.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "eslint-config-airbnb": "^18.1.0", 8 | "eslint-config-prettier": "^8.3.0", 9 | "eslint-plugin-jsx-a11y": "^6.2.3", 10 | "eslint-plugin-react": "^7.19.0", 11 | "eslint-plugin-react-hooks": "^2.5.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo-ts/config.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | "jest/globals": true, 6 | }, 7 | extends: ["airbnb-base", "prettier"], 8 | parser: "@typescript-eslint/parser", 9 | parserOptions: { 10 | ecmaVersion: 12, 11 | sourceType: "module", 12 | }, 13 | plugins: ["@typescript-eslint", "tree-shaking", "jest"], 14 | rules: { 15 | "import/extensions": [ 16 | "error", 17 | "ignorePackages", 18 | { 19 | js: "never", 20 | jsx: "never", 21 | ts: "never", 22 | tsx: "never", 23 | }, 24 | ], 25 | }, 26 | settings: { 27 | "import/resolver": { 28 | node: { 29 | extensions: [".js", ".jsx", ".ts", ".tsx"], 30 | }, 31 | }, 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo-ts/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./config.eslintrc.js"); 2 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-folo-ts", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@typescript-eslint/eslint-plugin": "^4.11.0", 8 | "@typescript-eslint/parser": "^4.11.0", 9 | "eslint": "^7.16.0", 10 | "eslint-config-airbnb-base": "^14.2.1", 11 | "eslint-config-prettier": "^8.3.0", 12 | "eslint-plugin-import": "^2.22.1", 13 | "eslint-plugin-jest": "^24.1.3", 14 | "eslint-plugin-tree-shaking": "^1.8.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-folo", 3 | "main": "src/index.js", 4 | "version": "0.1.1", 5 | "private": true, 6 | "author": "Jalal Maskoun ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "babel-eslint": "^10.1.0", 10 | "eslint": "^7.16.0", 11 | "eslint-config-airbnb-base": "^14.1.0", 12 | "eslint-config-prettier": "^8.3.0", 13 | "eslint-plugin-import": "^2.20.1", 14 | "eslint-plugin-jest": "^24.1.3", 15 | "eslint-plugin-prettier": "^3.1.3", 16 | "eslint-plugin-tree-shaking": "^1.8.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo/src/config.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "babel-eslint", 3 | env: { 4 | browser: true, 5 | es6: true, 6 | "jest/globals": true, 7 | }, 8 | extends: ["airbnb-base", "prettier"], 9 | parserOptions: { 10 | ecmaVersion: 11, 11 | sourceType: "module", 12 | }, 13 | rules: {}, 14 | plugins: ["tree-shaking", "jest"], 15 | }; 16 | -------------------------------------------------------------------------------- /scripts/eslint-config-folo/src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./config.eslintrc.js"); 2 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 8 | "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | // "outDir": "./", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | 44 | /* Module Resolution Options */ 45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 47 | // "paths": {} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | // "typeRoots": [], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 65 | 66 | /* Advanced Options */ 67 | "skipLibCheck": true /* Skip type checking of declaration files. */, 68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 69 | } 70 | } 71 | --------------------------------------------------------------------------------