diff --git a/CHANGELOG.md b/CHANGELOG.md index ef4f2cb437..bff3154f9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,129 @@ should change the heading of the (upcoming) version to include a major version b --> +# 5.19.3 + +## @rjsf/antd + +- SelectWidget now displays an empty option when appropriate, fixing [#4197](https://github.com/rjsf-team/react-jsonschema-form/issues/4197) + +## @rjsf/chakra-ui + +- SelectWidget now displays an empty option when appropriate, fixing [#4197](https://github.com/rjsf-team/react-jsonschema-form/issues/4197) + +## @rjsf/fluentui-rc + +- SelectWidget now displays an empty option when appropriate, fixing [#4197](https://github.com/rjsf-team/react-jsonschema-form/issues/4197) + +## @rjsf/material-ui + +- SelectWidget now displays an empty option when appropriate, fixing [#4197](https://github.com/rjsf-team/react-jsonschema-form/issues/4197) + +## @rjsf/mui + +- SelectWidget now displays an empty option when appropriate, fixing [#4197](https://github.com/rjsf-team/react-jsonschema-form/issues/4197) + +## @rjsf/semantic-ui + +- SelectWidget now displays an empty option when appropriate, fixing [#4197](https://github.com/rjsf-team/react-jsonschema-form/issues/4197) + +# 5.19.2 + +## @rjsf/core + +- Removed `.only` on tests that was accidentally added in `5.19.0` + +# 5.19.1 + +## Dev / docs / playground + +- Bumped the peer dependencies to `5.19.x` due to use of new API in `5.19.0` + +# 5.19.0 + +## @rjsf/antd + +- Updated `AltDateWidget` to use the new `dateRangeOptions()` function in `utils` to support relative Years and reversing the order of the Year choices + +## @rjsf/chakra-ui + +- Updated `AltDateWidget` to use the new `dateRangeOptions()` function in `utils` to support relative Years and reversing the order of the Year choices + +## @rjsf/core + +- Fixed case where `readOnly` from a JSON Schema was not applied in SchemaField ([#4236](https://github.com/rjsf-team/react-jsonschema-form/issues/4236)) +- Updated `AltDateWidget` to use the new `dateRangeOptions()` function in `utils` to support relative Years and reversing the order of the Year choices + +## @rjsf/utils + +- Added a new `dateRangeOptions()` function to implement relative Years in (via negative ranges) and reversing the order of the Year choices + +## Dev / docs / playground + +- Added documentation for the new `dateRangeOptions()` function as well as showing examples of using relative Years and reversed Year ordering + +# 5.18.6 + +## @rjsf/antd + +- Fix disabled property of options in CheckboxesWidget and RadioWidget ([#4216](https://github.com/rjsf-team/react-jsonschema-form/pull/4216)) + +## @rjsf/core + +- Fixed `omitExtraData` not working in `onSubmit` and `validateForm`; fixing [#4187](https://github.com/rjsf-team/react-jsonschema-form/issues/4187), [#4165](https://github.com/rjsf-team/react-jsonschema-form/issues/4165) and [#4109](https://github.com/rjsf-team/react-jsonschema-form/issues/4109) + +## @rjsf/utils + +- Fix IdSchema and PathSchema types ([#4196](https://github.com/rjsf-team/react-jsonschema-form/pull/4196)) + +## @rjsf/validator-ajv6 + +- Fix IdSchema and PathSchema types ([#4196](https://github.com/rjsf-team/react-jsonschema-form/pull/4196)) + +## @rjsf/validator-ajv8 + +- Fix IdSchema and PathSchema types ([#4196](https://github.com/rjsf-team/react-jsonschema-form/pull/4196)) + # 5.18.5 +## @rjsf/antd + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers + +## @rjsf/bootstrap4 + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers + +## @rjsf/chakra-ui + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers + ## @rjsf/core - Fix case where NumberField would not properly reset the field when using programmatic form reset (#4202)[https://github.com/rjsf-team/react-jsonschema-form/issues/4202] +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers +- Fix field disable or readonly property can't cover globalOptions corresponding property (#4212)[https://github.com/rjsf-team/react-jsonschema-form/pull/4212] +- Added support for `default` values in `additionalProperties` in [#4199](https://github.com/rjsf-team/react-jsonschema-form/issues/4199), fixing [#3195](https://github.com/rjsf-team/react-jsonschema-form/issues/3915) + +## @rjsf/fluent-ui + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers + +## @rjsf/fluentui-rc + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers + +## @rjsf/material-ui + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers + +## @rjsf/mui + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers + +## @rjsf/semantic-ui + +- Updated widgets to handle undefined `target` in `onFocus` and `onBlur` handlers ## @rjsf/validator-ajv6 @@ -61,6 +179,7 @@ should change the heading of the (upcoming) version to include a major version b # 5.18.0 ## @rjsf/antd + - Fix issue where the theme provided by the ConfigProvider under antd v5 wasn't respected thereby rendering the form items unusable under dark themes [#4129](https://github.com/rjsf-team/react-jsonschema-form/issues/4129) ## @rjsf/core @@ -99,7 +218,7 @@ should change the heading of the (upcoming) version to include a major version b ## Dev / docs / playground -- [#4080](https://github.com/rjsf-team/react-jsonschema-form/issues/4080) - Moved the `base64` encoder/decoder object to the Playground package. +- [#4080](https://github.com/rjsf-team/react-jsonschema-form/issues/4080) - Moved the `base64` encoder/decoder object to the Playground package. - Added test configuration and script to the Playground. # 5.17.0 diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index b2606d29e9..d9836abdd3 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,7 @@ If your PR is non-trivial and you'd like to schedule a synchronous review, pleas - [ ] **I'm updating documentation** - [ ] I've [checked the rendering](https://rjsf-team.github.io/react-jsonschema-form/docs/contributing) of the Markdown text I've added - [ ] **I'm adding or updating code** - - [ ] I've added and/or updated tests. I've run `npm run test:update` to update snapshots, if needed. + - [ ] I've added and/or updated tests. I've run `npx nx run-many --target=build --exclude=@rjsf/docs && npm run test:update` to update snapshots, if needed. - [ ] I've updated [docs](https://rjsf-team.github.io/react-jsonschema-form/docs) if needed - [ ] I've updated the [changelog](https://github.com/rjsf-team/react-jsonschema-form/blob/main/CHANGELOG.md) with a description of the PR - [ ] **I'm adding a new feature** diff --git a/lerna.json b/lerna.json index 285213ce50..bbeb1d215d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "5.18.4", + "version": "5.19.3", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": true, "useWorkspaces": true diff --git a/package-lock.json b/package-lock.json index 6ffc9b4d03..5e137ba7ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "cross-env": "^7.0.3", + "esbuild": "^0.18.20", "eslint": "^8.56.0", "eslint-config-prettier": "^8.10.0", "eslint-plugin-import": "^2.29.1", @@ -33830,7 +33831,7 @@ }, "packages/antd": { "name": "@rjsf/antd", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "classnames": "^2.5.1", @@ -33848,10 +33849,10 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@rollup/plugin-replace": "^5.0.5", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", @@ -33862,7 +33863,6 @@ "atob": "^2.1.2", "babel-jest": "^29.7.0", "dayjs": "^1.11.10", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -33878,8 +33878,8 @@ }, "peerDependencies": { "@ant-design/icons": "^4.0.0 || ^5.0.0", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "antd": "^4.24.0 || ^5.8.5", "dayjs": "^1.8.0", "react": "^16.14.0 || >=17" @@ -34527,7 +34527,7 @@ }, "packages/bootstrap-4": { "name": "@rjsf/bootstrap-4", - "version": "5.18.4", + "version": "5.19.3", "license": "MIT", "dependencies": { "@react-icons/all-files": "^4.1.0" @@ -34539,16 +34539,15 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -34564,15 +34563,15 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17", "react-bootstrap": "^1.6.5" } }, "packages/chakra-ui": { "name": "@rjsf/chakra-ui", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "react-select": "^5.8.0" @@ -34591,17 +34590,16 @@ "@emotion/jest": "^11.11.0", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", "chakra-react-select": "^3.3.9", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "framer-motion": "^5.6.0", "jest": "^29.7.0", @@ -34621,8 +34619,8 @@ "@chakra-ui/icons": ">=1.1.1", "@chakra-ui/react": ">=1.7.3", "@chakra-ui/system": ">=1.12.1", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "chakra-react-select": ">=3.3.8", "framer-motion": ">=5.6.0", "react": "^16.14.0 || >=17" @@ -34671,7 +34669,7 @@ }, "packages/core": { "name": "@rjsf/core", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21", @@ -34687,10 +34685,10 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv6": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv6": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", "@types/react": "^18.2.58", @@ -34700,7 +34698,6 @@ "atob": "^2.1.2", "babel-jest": "^29.7.0", "chai": "^3.5.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "html": "^1.0.0", "jest": "^29.7.0", @@ -34720,7 +34717,7 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.18.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17" } }, @@ -34748,7 +34745,7 @@ }, "packages/docs": { "name": "@rjsf/docs", - "version": "5.18.4", + "version": "5.19.3", "dependencies": { "@docusaurus/core": "^2.4.0", "@docusaurus/preset-classic": "^2.4.3", @@ -34790,7 +34787,7 @@ }, "packages/fluent-ui": { "name": "@rjsf/fluent-ui", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21", @@ -34804,17 +34801,16 @@ "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@fluentui/react": "^8.115.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -34830,14 +34826,14 @@ }, "peerDependencies": { "@fluentui/react": ">= 7", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17" } }, "packages/fluentui-rc": { "name": "@rjsf/fluentui-rc", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "@fluentui/react-components": "^9.46.3", @@ -34851,16 +34847,15 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -34875,13 +34870,13 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.18.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17" } }, "packages/material-ui": { "name": "@rjsf/material-ui", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "devDependencies": { "@babel/core": "^7.23.9", @@ -34892,16 +34887,15 @@ "@babel/preset-typescript": "^7.23.3", "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.11.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -34918,14 +34912,14 @@ "peerDependencies": { "@material-ui/core": "^4.12.3", "@material-ui/icons": "^4.11.2", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17" } }, "packages/mui": { "name": "@rjsf/mui", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "devDependencies": { "@babel/core": "^7.23.9", @@ -34940,16 +34934,15 @@ "@mui/base": "5.0.0-beta.28", "@mui/icons-material": "5.15.2", "@mui/material": "5.15.2", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -34969,14 +34962,14 @@ "@emotion/styled": "^11.6.0", "@mui/icons-material": "^5.2.0", "@mui/material": "^5.2.2", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "react": ">=17" } }, "packages/playground": { "name": "@rjsf/playground", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "@ant-design/icons": "^4.8.1", @@ -34987,18 +34980,18 @@ "@material-ui/core": "^4.12.4", "@mui/base": "5.0.0-beta.28", "@mui/material": "5.15.2", - "@rjsf/antd": "^5.18.4", - "@rjsf/bootstrap-4": "^5.18.4", - "@rjsf/chakra-ui": "^5.18.4", - "@rjsf/core": "^5.18.4", - "@rjsf/fluent-ui": "^5.18.4", - "@rjsf/fluentui-rc": "^5.18.4", - "@rjsf/material-ui": "^5.18.4", - "@rjsf/mui": "^5.18.4", - "@rjsf/semantic-ui": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv6": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/antd": "^5.19.3", + "@rjsf/bootstrap-4": "^5.19.3", + "@rjsf/chakra-ui": "^5.19.3", + "@rjsf/core": "^5.19.3", + "@rjsf/fluent-ui": "^5.19.3", + "@rjsf/fluentui-rc": "^5.19.3", + "@rjsf/material-ui": "^5.19.3", + "@rjsf/mui": "^5.19.3", + "@rjsf/semantic-ui": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv6": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "ajv-i18n": "^4.2.0", @@ -35044,7 +35037,6 @@ "@types/react-frame-component": "^4.1.6", "@vitejs/plugin-react": "^4.2.1", "cross-env": "^7.0.3", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "gh-pages": "^5.0.0", "html": "^1.0.0", @@ -35090,7 +35082,7 @@ }, "packages/semantic-ui": { "name": "@rjsf/semantic-ui", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "semantic-ui-css": "^2.5.0" @@ -35102,10 +35094,10 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", "@types/react": "^18.2.58", @@ -35113,7 +35105,6 @@ "@types/react-test-renderer": "^18.0.7", "atob": "^2.1.2", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -35130,20 +35121,20 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17", "semantic-ui-react": "^1.3.1 || ^2.1.3" } }, "packages/snapshot-tests": { "name": "@rjsf/snapshot-tests", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { - "@rjsf/core": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4" + "@rjsf/core": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3" }, "devDependencies": { "@types/jest": "^29.5.12", @@ -35164,7 +35155,7 @@ }, "packages/utils": { "name": "@rjsf/utils", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "json-schema-merge-allof": "^0.8.1", @@ -35189,7 +35180,6 @@ "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", "deep-freeze-es6": "^1.4.1", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -35208,7 +35198,7 @@ }, "packages/validator-ajv6": { "name": "@rjsf/validator-ajv6", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "ajv": "^6.12.6", @@ -35222,12 +35212,11 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/utils": "^5.18.4", + "@rjsf/utils": "^5.19.3", "@types/jest": "^29.5.12", "@types/json-schema": "^7.0.15", "@types/lodash": "^4.14.202", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -35239,12 +35228,12 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.18.x" + "@rjsf/utils": "^5.19.x" } }, "packages/validator-ajv8": { "name": "@rjsf/validator-ajv8", - "version": "5.18.4", + "version": "5.19.3", "license": "Apache-2.0", "dependencies": { "ajv": "^8.12.0", @@ -35259,12 +35248,11 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/utils": "^5.18.4", + "@rjsf/utils": "^5.19.3", "@types/jest": "^29.5.12", "@types/json-schema": "^7.0.15", "@types/lodash": "^4.14.202", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -35276,7 +35264,7 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.18.x" + "@rjsf/utils": "^5.19.x" } }, "packages/validator-ajv8/node_modules/ajv": { diff --git a/package.json b/package.json index ab2216c78b..0639401064 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "lint": "eslint './packages/**/*.{ts,tsx}' --quiet", "cs-check": "lerna run cs-check", "cs-format": "lerna run cs-format", + "clean-build": "rimraf packages/*/tsconfig.tsbuildinfo && rimraf packages/*/lib && rimraf packages/*/dist && npm run build", "build": "lerna run --stream build", "build-serial": "lerna run --concurrency 1 --stream build", "start": "echo 'use \"npm run build\" from main directory and then \"npm start\" in the playground package'", @@ -43,6 +44,7 @@ "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "cross-env": "^7.0.3", + "esbuild": "^0.18.20", "eslint": "^8.56.0", "eslint-config-prettier": "^8.10.0", "eslint-plugin-import": "^2.29.1", diff --git a/packages/antd/package.json b/packages/antd/package.json index c0045d2857..135fb6bfb4 100644 --- a/packages/antd/package.json +++ b/packages/antd/package.json @@ -1,6 +1,6 @@ { "name": "@rjsf/antd", - "version": "5.18.4", + "version": "5.19.3", "description": "Ant Design theme, fields and widgets for react-jsonschema-form", "main": "dist/index.js", "module": "lib/index.js", @@ -34,8 +34,8 @@ }, "peerDependencies": { "@ant-design/icons": "^4.0.0 || ^5.0.0", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "antd": "^4.24.0 || ^5.8.5", "dayjs": "^1.8.0", "react": "^16.14.0 || >=17" @@ -56,10 +56,10 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@rollup/plugin-replace": "^5.0.5", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", @@ -70,7 +70,6 @@ "atob": "^2.1.2", "babel-jest": "^29.7.0", "dayjs": "^1.11.10", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", diff --git a/packages/antd/src/templates/BaseInputTemplate/index.tsx b/packages/antd/src/templates/BaseInputTemplate/index.tsx index 8a18584793..71e7c86690 100644 --- a/packages/antd/src/templates/BaseInputTemplate/index.tsx +++ b/packages/antd/src/templates/BaseInputTemplate/index.tsx @@ -50,9 +50,9 @@ export default function BaseInputTemplate< ? onChangeOverride : ({ target }: ChangeEvent) => onChange(target.value === '' ? options.emptyValue : target.value); - const handleBlur = ({ target }: FocusEvent) => onBlur(id, target.value); + const handleBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); - const handleFocus = ({ target }: FocusEvent) => onFocus(id, target.value); + const handleFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); const input = inputProps.type === 'number' || inputProps.type === 'integer' ? ( diff --git a/packages/antd/src/templates/WrapIfAdditionalTemplate/index.tsx b/packages/antd/src/templates/WrapIfAdditionalTemplate/index.tsx index dc299205bb..3bce1e41de 100644 --- a/packages/antd/src/templates/WrapIfAdditionalTemplate/index.tsx +++ b/packages/antd/src/templates/WrapIfAdditionalTemplate/index.tsx @@ -65,7 +65,7 @@ export default function WrapIfAdditionalTemplate< ); } - const handleBlur = ({ target }: FocusEvent) => onKeyChange(target.value); + const handleBlur = ({ target }: FocusEvent) => onKeyChange(target && target.value); // The `block` prop is not part of the `IconButtonProps` defined in the template, so put it into the uiSchema instead const uiOptions = uiSchema ? uiSchema[UI_OPTIONS_KEY] : {}; diff --git a/packages/antd/src/widgets/AltDateWidget/index.tsx b/packages/antd/src/widgets/AltDateWidget/index.tsx index 1b01260370..2e0dc5d261 100644 --- a/packages/antd/src/widgets/AltDateWidget/index.tsx +++ b/packages/antd/src/widgets/AltDateWidget/index.tsx @@ -2,8 +2,8 @@ import { MouseEvent, useEffect, useState } from 'react'; import { Row, Col, Button } from 'antd'; import { ariaDescribedByIds, + dateRangeOptions, getDateElementProps, - pad, parseDateString, toDateString, DateObject, @@ -25,14 +25,6 @@ type DateElementProps { - const options = []; - for (let i = start; i <= stop; i++) { - options.push({ value: i, label: pad(i, 2) }); - } - return options; -}; - const readyForChange = (state: DateObject) => { return Object.values(state).every((value) => value !== -1); }; @@ -107,7 +99,7 @@ export default function AltDateWidget< onChange={(elemValue) => elemProps.select(elemProps.type as keyof DateObject, elemValue)} onFocus={elemProps.onFocus} options={{ - enumOptions: rangeOptions(elemProps.range[0], elemProps.range[1]), + enumOptions: dateRangeOptions(elemProps.range[0], elemProps.range[1]), }} placeholder={elemProps.type} readonly={elemProps.readonly} diff --git a/packages/antd/src/widgets/CheckboxWidget/index.tsx b/packages/antd/src/widgets/CheckboxWidget/index.tsx index fb08a5c094..9c8cf228c8 100644 --- a/packages/antd/src/widgets/CheckboxWidget/index.tsx +++ b/packages/antd/src/widgets/CheckboxWidget/index.tsx @@ -25,9 +25,9 @@ export default function CheckboxWidget< const handleChange: NonNullable = ({ target }) => onChange(target.checked); - const handleBlur = ({ target }: FocusEvent) => onBlur(id, target.checked); + const handleBlur = ({ target }: FocusEvent) => onBlur(id, target && target.checked); - const handleFocus = ({ target }: FocusEvent) => onFocus(id, target.checked); + const handleFocus = ({ target }: FocusEvent) => onFocus(id, target && target.checked); // Antd's typescript definitions do not contain the following props that are actually necessary and, if provided, // they are used, so hacking them in via by spreading `extraProps` on the component to avoid typescript errors diff --git a/packages/antd/src/widgets/CheckboxesWidget/index.tsx b/packages/antd/src/widgets/CheckboxesWidget/index.tsx index 6fd6f8e8b8..1d6b8b0c45 100644 --- a/packages/antd/src/widgets/CheckboxesWidget/index.tsx +++ b/packages/antd/src/widgets/CheckboxesWidget/index.tsx @@ -61,7 +61,7 @@ export default function CheckboxesWidget< id={optionId(id, i)} name={id} autoFocus={i === 0 ? autofocus : false} - disabled={Array.isArray(enumDisabled) && enumDisabled.indexOf(value) !== -1} + disabled={Array.isArray(enumDisabled) && enumDisabled.indexOf(option.value) !== -1} value={String(i)} > {option.label} diff --git a/packages/antd/src/widgets/RadioWidget/index.tsx b/packages/antd/src/widgets/RadioWidget/index.tsx index 64ab3f4f85..ea59a9b56b 100644 --- a/packages/antd/src/widgets/RadioWidget/index.tsx +++ b/packages/antd/src/widgets/RadioWidget/index.tsx @@ -37,10 +37,10 @@ export default function RadioWidget(nextValue, enumOptions, emptyValue)); const handleBlur = ({ target }: FocusEvent) => - onBlur(id, enumOptionsValueForIndex(target.value, enumOptions, emptyValue)); + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); const handleFocus = ({ target }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(target.value, enumOptions, emptyValue)); + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); const selectedIndexes = enumOptionsIndexForValue(value, enumOptions) as string; @@ -61,7 +61,7 @@ export default function RadioWidget diff --git a/packages/antd/src/widgets/SelectWidget/index.tsx b/packages/antd/src/widgets/SelectWidget/index.tsx index 2f6937b773..645e0ebb4e 100644 --- a/packages/antd/src/widgets/SelectWidget/index.tsx +++ b/packages/antd/src/widgets/SelectWidget/index.tsx @@ -10,6 +10,8 @@ import { WidgetProps, } from '@rjsf/utils'; import isString from 'lodash/isString'; +import { DefaultOptionType } from 'antd/es/select'; +import { useMemo } from 'react'; const SELECT_STYLE = { width: '100%', @@ -37,6 +39,7 @@ export default function SelectWidget< placeholder, readonly, value, + schema, }: WidgetProps) { const { readonlyAsDisabled = true } = formContext as GenericObjectType; @@ -65,6 +68,26 @@ export default function SelectWidget< const extraProps = { name: id, }; + + const showPlaceholderOption = !multiple && schema.default === undefined; + + const selectOptions: DefaultOptionType[] | undefined = useMemo(() => { + if (Array.isArray(enumOptions)) { + const options: DefaultOptionType[] = enumOptions.map(({ value: optionValue, label: optionLabel }, index) => ({ + disabled: Array.isArray(enumDisabled) && enumDisabled.indexOf(optionValue) !== -1, + key: String(index), + value: String(index), + label: optionLabel, + })); + + if (showPlaceholderOption) { + options.unshift({ value: '', label: placeholder || '' }); + } + return options; + } + return undefined; + }, [enumDisabled, enumOptions, placeholder, showPlaceholderOption]); + return ( + + + + foo + + +
+ + + +
+
+ + +
+
+ + +
+
+ + + + + + + + + +`; + exports[`single fields select field multiple choice formData 1`] = `
`; +exports[`single fields select field single choice enumDisabled using radio widget 1`] = ` + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ +
+`; + exports[`single fields select field single choice formData 1`] = `
=17", "react-bootstrap": "^1.6.5" }, @@ -49,16 +49,15 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", diff --git a/packages/bootstrap-4/src/BaseInputTemplate/BaseInputTemplate.tsx b/packages/bootstrap-4/src/BaseInputTemplate/BaseInputTemplate.tsx index 6a1789ec80..289eb4755d 100644 --- a/packages/bootstrap-4/src/BaseInputTemplate/BaseInputTemplate.tsx +++ b/packages/bootstrap-4/src/BaseInputTemplate/BaseInputTemplate.tsx @@ -39,8 +39,8 @@ export default function BaseInputTemplate< }; const _onChange = ({ target: { value } }: ChangeEvent) => onChange(value === '' ? options.emptyValue : value); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); // const classNames = [rawErrors.length > 0 ? "is-invalid" : "", type === 'file' ? 'custom-file-label': ""] return ( diff --git a/packages/bootstrap-4/src/CheckboxWidget/CheckboxWidget.tsx b/packages/bootstrap-4/src/CheckboxWidget/CheckboxWidget.tsx index a9185a2bdc..f7ab4747be 100644 --- a/packages/bootstrap-4/src/CheckboxWidget/CheckboxWidget.tsx +++ b/packages/bootstrap-4/src/CheckboxWidget/CheckboxWidget.tsx @@ -44,8 +44,8 @@ export default function CheckboxWidget< ); const _onChange = ({ target: { checked } }: FocusEvent) => onChange(checked); - const _onBlur = ({ target: { checked } }: FocusEvent) => onBlur(id, checked); - const _onFocus = ({ target: { checked } }: FocusEvent) => onFocus(id, checked); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.checked); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.checked); const description = options.description || schema.description; return ( diff --git a/packages/bootstrap-4/src/CheckboxesWidget/CheckboxesWidget.tsx b/packages/bootstrap-4/src/CheckboxesWidget/CheckboxesWidget.tsx index 61e1176b10..226e759184 100644 --- a/packages/bootstrap-4/src/CheckboxesWidget/CheckboxesWidget.tsx +++ b/packages/bootstrap-4/src/CheckboxesWidget/CheckboxesWidget.tsx @@ -31,10 +31,10 @@ export default function CheckboxesWidget< } }; - const _onBlur = ({ target: { value } }: FocusEvent) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); - const _onFocus = ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onBlur = ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); + const _onFocus = ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); return ( diff --git a/packages/bootstrap-4/src/RadioWidget/RadioWidget.tsx b/packages/bootstrap-4/src/RadioWidget/RadioWidget.tsx index f1606f0580..5c5dd2f987 100644 --- a/packages/bootstrap-4/src/RadioWidget/RadioWidget.tsx +++ b/packages/bootstrap-4/src/RadioWidget/RadioWidget.tsx @@ -26,10 +26,10 @@ export default function RadioWidget) => onChange(enumOptionsValueForIndex(value, enumOptions, emptyValue)); - const _onBlur = ({ target: { value } }: FocusEvent) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); - const _onFocus = ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onBlur = ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); + const _onFocus = ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); const inline = Boolean(options && options.inline); diff --git a/packages/bootstrap-4/src/SelectWidget/SelectWidget.tsx b/packages/bootstrap-4/src/SelectWidget/SelectWidget.tsx index 8a92cee3f5..b3e0aa320d 100644 --- a/packages/bootstrap-4/src/SelectWidget/SelectWidget.tsx +++ b/packages/bootstrap-4/src/SelectWidget/SelectWidget.tsx @@ -45,6 +45,7 @@ export default function SelectWidget< } } const selectedIndexes = enumOptionsIndexForValue(value, enumOptions, multiple); + const showPlaceholderOption = !multiple && schema.default === undefined; return ( (id)} > - {!multiple && schema.default === undefined && } + {showPlaceholderOption && } {(enumOptions as any).map(({ value, label }: any, i: number) => { const disabled: any = Array.isArray(enumDisabled) && (enumDisabled as any).indexOf(value) != -1; return ( diff --git a/packages/bootstrap-4/src/TextareaWidget/TextareaWidget.tsx b/packages/bootstrap-4/src/TextareaWidget/TextareaWidget.tsx index 1d1c1efcb2..f2509d5358 100644 --- a/packages/bootstrap-4/src/TextareaWidget/TextareaWidget.tsx +++ b/packages/bootstrap-4/src/TextareaWidget/TextareaWidget.tsx @@ -30,8 +30,8 @@ export default function TextareaWidget< }: CustomWidgetProps) { const _onChange = ({ target: { value } }: ChangeEvent) => onChange(value === '' ? options.emptyValue : value); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); return ( diff --git a/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap b/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap index 01a04792cc..960d182f83 100644 --- a/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap +++ b/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap @@ -1303,6 +1303,140 @@ exports[`single fields select field multiple choice enumDisabled 1`] = ` `; +exports[`single fields select field multiple choice enumDisabled using checkboxes 1`] = ` +
+
+
+
+
+
+ +
+
+`; + exports[`single fields select field multiple choice formData 1`] = `
`; +exports[`single fields select field single choice enumDisabled using radio widget 1`] = ` + +
+
+
+
+
+ +
+
+`; + exports[`single fields select field single choice formData 1`] = `
=1.1.1", "@chakra-ui/react": ">=1.7.3", "@chakra-ui/system": ">=1.12.1", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "chakra-react-select": ">=3.3.8", "framer-motion": ">=5.6.0", "react": "^16.14.0 || >=17" @@ -68,17 +68,16 @@ "@emotion/jest": "^11.11.0", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", "chakra-react-select": "^3.3.9", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "framer-motion": "^5.6.0", "jest": "^29.7.0", diff --git a/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx b/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx index c6f258936c..b5f89a1f5d 100644 --- a/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx +++ b/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx @@ -1,11 +1,11 @@ import { MouseEvent, useEffect, useState } from 'react'; import { ariaDescribedByIds, + dateRangeOptions, DateElementFormat, DateObject, FormContextType, getDateElementProps, - pad, parseDateString, RJSFSchema, StrictRJSFSchema, @@ -15,14 +15,6 @@ import { } from '@rjsf/utils'; import { Box, Button } from '@chakra-ui/react'; -const rangeOptions = (start: number, stop: number) => { - const options = []; - for (let i = start; i <= stop; i++) { - options.push({ value: i, label: pad(i, 2) }); - } - return options; -}; - function DateElement( props: WidgetProps ) { @@ -35,7 +27,7 @@ function DateElement) => props.select(props.type, elemValue)} options={{ - enumOptions: rangeOptions(props.range[0], props.range[1]), + enumOptions: dateRangeOptions(props.range[0], props.range[1]), }} placeholder={props.type} schema={{ type: 'integer' } as S} diff --git a/packages/chakra-ui/src/BaseInputTemplate/BaseInputTemplate.tsx b/packages/chakra-ui/src/BaseInputTemplate/BaseInputTemplate.tsx index ff5847c595..db04dc1521 100644 --- a/packages/chakra-ui/src/BaseInputTemplate/BaseInputTemplate.tsx +++ b/packages/chakra-ui/src/BaseInputTemplate/BaseInputTemplate.tsx @@ -42,8 +42,8 @@ export default function BaseInputTemplate< const _onChange = ({ target: { value } }: ChangeEvent) => onChange(value === '' ? options.emptyValue : value); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); return ( ) => onChange(checked); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); return ( diff --git a/packages/chakra-ui/src/CheckboxesWidget/CheckboxesWidget.tsx b/packages/chakra-ui/src/CheckboxesWidget/CheckboxesWidget.tsx index 51dd9b2108..e4fbf14fa3 100644 --- a/packages/chakra-ui/src/CheckboxesWidget/CheckboxesWidget.tsx +++ b/packages/chakra-ui/src/CheckboxesWidget/CheckboxesWidget.tsx @@ -38,10 +38,10 @@ export default function CheckboxesWidget< const chakraProps = getChakra({ uiSchema }); const checkboxesValues = Array.isArray(value) ? value : [value]; - const _onBlur = ({ target: { value } }: FocusEvent) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); - const _onFocus = ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onBlur = ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); + const _onFocus = ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); const row = options ? options.inline : false; const selectedIndexes = enumOptionsIndexForValue(value, enumOptions, true) as string[]; diff --git a/packages/chakra-ui/src/RangeWidget/RangeWidget.tsx b/packages/chakra-ui/src/RangeWidget/RangeWidget.tsx index 42d6b8ceed..a30c5c9cd4 100644 --- a/packages/chakra-ui/src/RangeWidget/RangeWidget.tsx +++ b/packages/chakra-ui/src/RangeWidget/RangeWidget.tsx @@ -30,8 +30,8 @@ export default function RangeWidget(schema) }; const _onChange = (value: undefined | number) => onChange(value === undefined ? options.emptyValue : value); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); return ( diff --git a/packages/chakra-ui/src/SelectWidget/SelectWidget.tsx b/packages/chakra-ui/src/SelectWidget/SelectWidget.tsx index 3d347796e5..347a677578 100644 --- a/packages/chakra-ui/src/SelectWidget/SelectWidget.tsx +++ b/packages/chakra-ui/src/SelectWidget/SelectWidget.tsx @@ -1,4 +1,4 @@ -import { FocusEvent } from 'react'; +import { FocusEvent, useMemo } from 'react'; import { FormControl, FormLabel } from '@chakra-ui/react'; import { ariaDescribedByIds, @@ -34,6 +34,7 @@ export default function SelectWidget(e.value, enumOptions, emptyValue)); }; - const _onBlur = ({ target: { value } }: FocusEvent) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onBlur = ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); - const _onFocus = ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onFocus = ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); - const _valueLabelMap: any = {}; - const displayEnumOptions: OptionsOrGroups = Array.isArray(enumOptions) - ? enumOptions.map((option: EnumOptionsType, index: number) => { + const showPlaceholderOption = !multiple && schema.default === undefined; + const { valueLabelMap, displayEnumOptions } = useMemo((): { + valueLabelMap: Record; + displayEnumOptions: OptionsOrGroups; + } => { + const valueLabelMap: Record = {}; + let displayEnumOptions: OptionsOrGroups = []; + if (Array.isArray(enumOptions)) { + displayEnumOptions = enumOptions.map((option: EnumOptionsType, index: number) => { const { value, label } = option; - _valueLabelMap[index] = label || String(value); + valueLabelMap[index] = label || String(value); return { label, value: String(index), isDisabled: Array.isArray(enumDisabled) && enumDisabled.indexOf(value) !== -1, }; - }) - : []; + }); + if (showPlaceholderOption) { + (displayEnumOptions as any[]).unshift({ value: '', label: placeholder || '' }); + } + } + return { valueLabelMap: valueLabelMap, displayEnumOptions: displayEnumOptions }; + }, [enumDisabled, enumOptions, placeholder, showPlaceholderOption]); const isMultiple = typeof multiple !== 'undefined' && multiple !== false && Boolean(enumOptions); const selectedIndex = enumOptionsIndexForValue(value, enumOptions, isMultiple); const formValue: any = isMultiple ? ((selectedIndex as string[]) || []).map((i: string) => { return { - label: _valueLabelMap[i], + label: valueLabelMap[i], value: i, }; }) : { - label: _valueLabelMap[selectedIndex as string] || '', + label: valueLabelMap[selectedIndex as string] || '', selectedIndex, }; diff --git a/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx b/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx index ca6f065223..c8f215a0b0 100644 --- a/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx +++ b/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx @@ -35,8 +35,8 @@ export default function TextareaWidget< const _onChange = ({ target: { value } }: ChangeEvent) => onChange(value === '' ? options.emptyValue : value); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); return ( onChange(value); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); return ( `; +exports[`single fields select field multiple choice enumDisabled using checkboxes 1`] = ` +.emotion-1 { + margin-bottom: 1px; +} + +.emotion-2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; +} + +.emotion-2>*:not(style)~*:not(style) { + margin-top: 0.5rem; + -webkit-margin-end: 0px; + margin-inline-end: 0px; + margin-bottom: 0px; + -webkit-margin-start: 0px; + margin-inline-start: 0px; +} + +.emotion-3 { + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + vertical-align: top; + position: relative; +} + +.emotion-4 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + vertical-align: top; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.emotion-5 { + -webkit-margin-start: 0.5rem; + margin-inline-start: 0.5rem; +} + +.emotion-19 { + margin-top: 3px; +} + +.emotion-20 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-appearance: none; + -moz-appearance: none; + -ms-appearance: none; + appearance: none; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + position: relative; + white-space: nowrap; + vertical-align: middle; + outline: 2px solid transparent; + outline-offset: 2px; + width: auto; +} + + +
+
+
+
+ + + + +
+
+
+
+
+ +
+ +`; + exports[`single fields select field multiple choice formData 1`] = ` .emotion-1 { margin-bottom: 1px; @@ -4390,6 +4749,246 @@ exports[`single fields select field single choice enumDisabled 1`] = ` `; +exports[`single fields select field single choice enumDisabled using radio widget 1`] = ` +.emotion-1 { + margin-bottom: 1px; +} + +.emotion-3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; +} + +.emotion-3>*:not(style)~*:not(style) { + margin-top: 0.5rem; + -webkit-margin-end: 0px; + margin-inline-end: 0px; + margin-bottom: 0px; + -webkit-margin-start: 0px; + margin-inline-start: 0px; +} + +.emotion-4 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + vertical-align: top; + cursor: pointer; +} + +.emotion-5 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.emotion-6 { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-margin-start: 0.5rem; + margin-inline-start: 0.5rem; +} + +.emotion-10 { + margin-top: 3px; +} + +.emotion-11 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-appearance: none; + -moz-appearance: none; + -ms-appearance: none; + appearance: none; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + position: relative; + white-space: nowrap; + vertical-align: middle; + outline: 2px solid transparent; + outline-offset: 2px; + width: auto; +} + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ +
+
+`; + exports[`single fields select field single choice formData 1`] = ` .emotion-1 { margin-bottom: 1px; diff --git a/packages/core/package.json b/packages/core/package.json index 849b239c76..485bcdfdac 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@rjsf/core", - "version": "5.18.4", + "version": "5.19.3", "description": "A simple React component capable of building HTML forms out of a JSON schema.", "scripts": { "build:ts": "tsc -b", @@ -37,7 +37,7 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.18.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17" }, "dependencies": { @@ -54,10 +54,10 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv6": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv6": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", "@types/react": "^18.2.58", @@ -67,7 +67,6 @@ "atob": "^2.1.2", "babel-jest": "^29.7.0", "chai": "^3.5.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "html": "^1.0.0", "jest": "^29.7.0", diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx index 0d24515fab..94abe15937 100644 --- a/packages/core/src/components/Form.tsx +++ b/packages/core/src/components/Form.tsx @@ -577,9 +577,23 @@ export default class Form< return getAllPaths(pathSchema); }; + /** Returns the `formData` after filtering to remove any extra data not in a form field + * + * @param formData - The data for the `Form` + * @returns The `formData` after omitting extra data + */ + omitExtraData = (formData?: T): T | undefined => { + const { schema, schemaUtils } = this.state; + const retrievedSchema = schemaUtils.retrieveSchema(schema, formData); + const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', formData); + const fieldNames = this.getFieldNames(pathSchema, formData); + const newFormData = this.getUsedFormData(formData, fieldNames); + return newFormData; + }; + /** Function to handle changes made to a field in the `Form`. This handler receives an entirely new copy of the * `formData` along with a new `ErrorSchema`. It will first update the `formData` with any missing default fields and - * then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filterer to remove any extra data not + * then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filtered to remove any extra data not * in a form field. Then, the resulting formData will be validated if required. The state will be updated with the new * updated (potentially filtered) `formData`, any errors that resulted from validation. Finally the `onChange` * callback will be called if specified with the updated state. @@ -603,12 +617,7 @@ export default class Form< let _retrievedSchema: S | undefined; if (omitExtraData === true && liveOmit === true) { - _retrievedSchema = schemaUtils.retrieveSchema(schema, formData); - const pathSchema = schemaUtils.toPathSchema(_retrievedSchema, '', formData); - - const fieldNames = this.getFieldNames(pathSchema, formData); - - newFormData = this.getUsedFormData(formData, fieldNames); + newFormData = this.omitExtraData(formData); state = { formData: newFormData, }; @@ -715,18 +724,12 @@ export default class Form< event.persist(); const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props; let { formData: newFormData } = this.state; - const { schema, schemaUtils } = this.state; if (omitExtraData === true) { - const retrievedSchema = schemaUtils.retrieveSchema(schema, newFormData); - const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', newFormData); - - const fieldNames = this.getFieldNames(pathSchema, newFormData); - - newFormData = this.getUsedFormData(newFormData, fieldNames); + newFormData = this.omitExtraData(newFormData); } - if (noValidate || this.validateForm()) { + if (noValidate || this.validateFormWithFormData(newFormData)) { // There are no errors generated through schema validation. // Check for user provided errors and update state accordingly. const errorSchema = extraErrors || {}; @@ -817,14 +820,15 @@ export default class Form< } } - /** Programmatically validate the form. If `onError` is provided, then it will be called with the list of errors the - * same way as would happen on form submission. + /** Validates the form using the given `formData`. For use on form submission or on programmatic validation. + * If `onError` is provided, then it will be called with the list of errors. * + * @param formData - The form data to validate * @returns - True if the form is valid, false otherwise. */ - validateForm() { + validateFormWithFormData = (formData?: T): boolean => { const { extraErrors, extraErrorsBlockSubmit, focusOnFirstError, onError } = this.props; - const { formData, errors: prevErrors } = this.state; + const { errors: prevErrors } = this.state; const schemaValidation = this.validate(formData); let errors = schemaValidation.errors; let errorSchema = schemaValidation.errorSchema; @@ -868,6 +872,21 @@ export default class Form< }); } return !hasError; + }; + + /** Programmatically validate the form. If `omitExtraData` is true, the `formData` will first be filtered to remove + * any extra data not in a form field. If `onError` is provided, then it will be called with the list of errors the + * same way as would happen on form submission. + * + * @returns - True if the form is valid, false otherwise. + */ + validateForm() { + const { omitExtraData } = this.props; + let { formData: newFormData } = this.state; + if (omitExtraData === true) { + newFormData = this.omitExtraData(newFormData); + } + return this.validateFormWithFormData(newFormData); } /** Renders the `Form` fields inside the
| `tagName` or `_internalFormWrapper`, rendering any errors if @@ -890,8 +909,8 @@ export default class Form< acceptcharset, acceptCharset, noHtml5Validate = false, - disabled = false, - readonly = false, + disabled, + readonly, formContext, showErrorList = 'top', _internalFormWrapper, diff --git a/packages/core/src/components/fields/ObjectField.tsx b/packages/core/src/components/fields/ObjectField.tsx index dfde7fe1c3..eb4e1d4d99 100644 --- a/packages/core/src/components/fields/ObjectField.tsx +++ b/packages/core/src/components/fields/ObjectField.tsx @@ -236,8 +236,8 @@ class ObjectField(schema, uiOptions, idSchema, registry); - const disabled = Boolean(props.disabled || uiOptions.disabled); - const readonly = Boolean(props.readonly || uiOptions.readonly || props.schema.readOnly || schema.readOnly); + const disabled = Boolean(uiOptions.disabled ?? props.disabled); + const readonly = Boolean(uiOptions.readonly ?? (props.readonly || props.schema.readOnly || schema.readOnly)); const uiSchemaHideError = uiOptions.hideError; // Set hideError to the value provided in the uiSchema, otherwise stick with the prop to propagate to children const hideError = uiSchemaHideError === undefined ? props.hideError : Boolean(uiSchemaHideError); - const autofocus = Boolean(props.autofocus || uiOptions.autofocus); + const autofocus = Boolean(uiOptions.autofocus ?? props.autofocus); if (Object.keys(schema).length === 0) { return null; } diff --git a/packages/core/src/components/templates/BaseInputTemplate.tsx b/packages/core/src/components/templates/BaseInputTemplate.tsx index e2bbb2583a..1204ccfc17 100644 --- a/packages/core/src/components/templates/BaseInputTemplate.tsx +++ b/packages/core/src/components/templates/BaseInputTemplate.tsx @@ -65,9 +65,12 @@ export default function BaseInputTemplate< ({ target: { value } }: ChangeEvent) => onChange(value === '' ? options.emptyValue : value), [onChange, options] ); - const _onBlur = useCallback(({ target: { value } }: FocusEvent) => onBlur(id, value), [onBlur, id]); + const _onBlur = useCallback( + ({ target }: FocusEvent) => onBlur(id, target && target.value), + [onBlur, id] + ); const _onFocus = useCallback( - ({ target: { value } }: FocusEvent) => onFocus(id, value), + ({ target }: FocusEvent) => onFocus(id, target && target.value), [onFocus, id] ); diff --git a/packages/core/src/components/templates/WrapIfAdditionalTemplate.tsx b/packages/core/src/components/templates/WrapIfAdditionalTemplate.tsx index e6e55224c9..de41c1135f 100644 --- a/packages/core/src/components/templates/WrapIfAdditionalTemplate.tsx +++ b/packages/core/src/components/templates/WrapIfAdditionalTemplate.tsx @@ -58,7 +58,7 @@ export default function WrapIfAdditionalTemplate< className='form-control' type='text' id={`${id}-key`} - onBlur={(event) => onKeyChange(event.target.value)} + onBlur={({ target }) => onKeyChange(target && target.value)} defaultValue={label} /> diff --git a/packages/core/src/components/widgets/AltDateWidget.tsx b/packages/core/src/components/widgets/AltDateWidget.tsx index 899a368c54..a6f9016df8 100644 --- a/packages/core/src/components/widgets/AltDateWidget.tsx +++ b/packages/core/src/components/widgets/AltDateWidget.tsx @@ -1,9 +1,9 @@ import { MouseEvent, useCallback, useEffect, useReducer, useState } from 'react'; import { ariaDescribedByIds, + dateRangeOptions, parseDateString, toDateString, - pad, DateObject, type DateElementFormat, FormContextType, @@ -14,14 +14,6 @@ import { getDateElementProps, } from '@rjsf/utils'; -function rangeOptions(start: number, stop: number) { - const options = []; - for (let i = start; i <= stop; i++) { - options.push({ value: i, label: pad(i, 2) }); - } - return options; -} - function readyForChange(state: DateObject) { return Object.values(state).every((value) => value !== -1); } @@ -58,7 +50,7 @@ function DateElement(range[0], range[1]) }} placeholder={type} value={value} disabled={disabled} diff --git a/packages/core/src/components/widgets/CheckboxesWidget.tsx b/packages/core/src/components/widgets/CheckboxesWidget.tsx index 165e0a2a41..8d07696752 100644 --- a/packages/core/src/components/widgets/CheckboxesWidget.tsx +++ b/packages/core/src/components/widgets/CheckboxesWidget.tsx @@ -31,14 +31,14 @@ function CheckboxesWidget) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)), + ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)), [onBlur, id] ); const handleFocus = useCallback( - ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)), + ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)), [onFocus, id] ); return ( diff --git a/packages/core/src/components/widgets/RadioWidget.tsx b/packages/core/src/components/widgets/RadioWidget.tsx index 4619866306..85edddc2ae 100644 --- a/packages/core/src/components/widgets/RadioWidget.tsx +++ b/packages/core/src/components/widgets/RadioWidget.tsx @@ -30,14 +30,14 @@ function RadioWidget) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)), + ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)), [onBlur, id] ); const handleFocus = useCallback( - ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)), + ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)), [onFocus, id] ); diff --git a/packages/core/src/components/widgets/SelectWidget.tsx b/packages/core/src/components/widgets/SelectWidget.tsx index 2faae6051b..1bebfa371a 100644 --- a/packages/core/src/components/widgets/SelectWidget.tsx +++ b/packages/core/src/components/widgets/SelectWidget.tsx @@ -47,7 +47,7 @@ function SelectWidget(newValue, enumOptions, optEmptyVal)); }, - [onFocus, id, schema, multiple, options] + [onFocus, id, schema, multiple, enumOptions, optEmptyVal] ); const handleBlur = useCallback( @@ -55,7 +55,7 @@ function SelectWidget(newValue, enumOptions, optEmptyVal)); }, - [onBlur, id, schema, multiple, options] + [onBlur, id, schema, multiple, enumOptions, optEmptyVal] ); const handleChange = useCallback( @@ -63,10 +63,11 @@ function SelectWidget(newValue, enumOptions, optEmptyVal)); }, - [onChange, schema, multiple, options] + [onChange, schema, multiple, enumOptions, optEmptyVal] ); const selectedIndexes = enumOptionsIndexForValue(value, enumOptions, multiple); + const showPlaceholderOption = !multiple && schema.default === undefined; return ( + + foo + + + + +
+ +
+
+ +
+
+ +
+ + +
+ +
+ +`; + exports[`single fields select field multiple choice formData 1`] = `
`; +exports[`single fields select field single choice enumDisabled using radio widget 1`] = ` + +
+
+
+ +
+
+ +
+
+
+
+ +
+
+`; + exports[`single fields select field single choice formData 1`] = `
{ }); describe('ObjectField', () => { - let node; - - beforeEach(() => { + it('should mark as readonly an ObjectField', () => { const schema = { type: 'object', properties: { @@ -2523,13 +2521,41 @@ describe('uiSchema', () => { const uiSchema = {}; let rendered = createFormComponent({ schema, uiSchema }); - node = rendered.node; - }); + const node = rendered.node; - it('should mark as readonly an ObjectField', () => { const disabled = [].map.call(node.querySelectorAll('[type=text]'), (node) => node.hasAttribute('readonly')); expect(disabled).eql([true, true]); }); + + it('should not mark as readonly even if globalOptions set readonly', () => { + const schema = { + type: 'object', + properties: { + foo: { + type: 'string', + }, + bar: { + type: 'string', + }, + }, + readOnly: true, + }; + + const uiSchema = { + 'ui:globalOptions': { + readonly: true, + }, + foo: { + 'ui:readonly': false, + }, + }; + + let rendered = createFormComponent({ schema, uiSchema }); + const node = rendered.node; + + const disabled = [].map.call(node.querySelectorAll('[type=text]'), (node) => node.hasAttribute('readonly')); + expect(disabled).eql([false, true]); + }); }); }); diff --git a/packages/docs/docs/api-reference/utility-functions.md b/packages/docs/docs/api-reference/utility-functions.md index fdee1aa0f6..c1b61a4b88 100644 --- a/packages/docs/docs/api-reference/utility-functions.md +++ b/packages/docs/docs/api-reference/utility-functions.md @@ -110,6 +110,25 @@ of that Blob if provided in the URL. If no name is provided, then the name falls - { blob: Blob, name: string }: An object containing a Blob and its name, extracted from the URI +### dateRangeOptions<S extends StrictRJSFSchema = RJSFSchema>() + +Returns a list of options for a date range between `start` and `stop`. +If the start date is greater than the end date, then the date range is reversed. +If `start` and `stop` are negative numbers (or zero), then they will be treated as relative to the current year. + +#### Parameters + +- start: number - The starting point of the date range +- stop: number - The ending point of the date range + +#### Returns + +- EnumOptionsType<S>[]: The list of EnumOptionsType for the date range between `start` and `stop` + +#### Throws + +- Error when `start` and `stop` aren't both <= 0 or > 0 + ### deepEquals() Implements a deep equals using the `lodash.isEqualWith` function, that provides a customized comparator that assumes all functions are equivalent. diff --git a/packages/docs/docs/usage/widgets.md b/packages/docs/docs/usage/widgets.md index 451578f416..63bf42518d 100644 --- a/packages/docs/docs/usage/widgets.md +++ b/packages/docs/docs/usage/widgets.md @@ -90,7 +90,9 @@ Please note that, even though they are standardized, `datetime-local`, `date` an ![](https://i.imgur.com/VF5tY60.png) -You can customize the list of years displayed in the `year` dropdown by providing a `yearsRange` property to `ui:options` in your uiSchema. It's also possible to remove the `Now` and `Clear` buttons with the `hideNowButton` and `hideClearButton` options. +You can customize the list of years displayed in the `year` dropdown by providing a `yearsRange` property to `ui:options` in your uiSchema. +The range can be descending by specifying the larger value first. +It's also possible to remove the `Now` and `Clear` buttons with the `hideNowButton` and `hideClearButton` options. You can also, customize the order in which date input fields are displayed by providing `format` property to `ui:options` in your uiSchema, available values are `YMD`(default), `MDY` and `DMY`. @@ -116,6 +118,22 @@ const uiSchema: UiSchema = { render(, document.getElementById('app')); ``` +You can also specify negative values which will be treated relative to the current year, so if it is 2020 and the range is set as follows. + +``` + yearsRange: [-120, -18], +``` + +Years from 1900-2002 will be shown. You can also specify the dates with the higher date first to display dates in reverse order. + +``` + yearsRange: [2030, 1980], + ... + yearsRange: [-18, -120], +``` + +Years from 2030-1980 and 2002-1900, respectively will be shown. + ## For `number` and `integer` fields - `updown`: an `input[type=number]` updown selector; diff --git a/packages/docs/package.json b/packages/docs/package.json index 55c38e7f20..0f71d24add 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "@rjsf/docs", - "version": "5.18.4", + "version": "5.19.3", "private": true, "scripts": { "docusaurus": "docusaurus", diff --git a/packages/fluent-ui/package.json b/packages/fluent-ui/package.json index 4c264cb67e..2528e3a579 100644 --- a/packages/fluent-ui/package.json +++ b/packages/fluent-ui/package.json @@ -1,6 +1,6 @@ { "name": "@rjsf/fluent-ui", - "version": "5.18.4", + "version": "5.19.3", "main": "dist/index.js", "module": "lib/index.js", "typings": "lib/index.d.ts", @@ -34,8 +34,8 @@ }, "peerDependencies": { "@fluentui/react": ">= 7", - "@rjsf/core": "^5.18.x", - "@rjsf/utils": "^5.18.x", + "@rjsf/core": "^5.19.x", + "@rjsf/utils": "^5.19.x", "react": "^16.14.0 || >=17" }, "devDependencies": { @@ -46,17 +46,16 @@ "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@fluentui/react": "^8.115.3", - "@rjsf/core": "^5.18.4", - "@rjsf/snapshot-tests": "^5.18.4", - "@rjsf/utils": "^5.18.4", - "@rjsf/validator-ajv8": "^5.18.4", + "@rjsf/core": "^5.19.3", + "@rjsf/snapshot-tests": "^5.19.3", + "@rjsf/utils": "^5.19.3", + "@rjsf/validator-ajv8": "^5.19.3", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-test-renderer": "^18.0.7", "babel-jest": "^29.7.0", - "esbuild": "^0.18.20", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", diff --git a/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx b/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx index d77b6de380..b1ea232f53 100644 --- a/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx +++ b/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx @@ -80,8 +80,8 @@ export default function BaseInputTemplate< const inputProps = getInputProps(schema, type, options); const _onChange = ({ target: { value } }: ChangeEvent) => onChange(value === '' ? options.emptyValue : value); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); const uiProps = _pick((options.props as object) || {}, allowedProps); diff --git a/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx b/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx index 732c7bf2bb..8b3ff8b056 100644 --- a/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx +++ b/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx @@ -72,8 +72,8 @@ export default function CheckboxWidget< [onChange] ); - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); const uiProps = _pick((options.props as object) || {}, allowedProps); const description = options.description ?? schema.description; diff --git a/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx b/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx index e52666971d..5ddbb7dc8b 100644 --- a/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx +++ b/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx @@ -48,11 +48,11 @@ export default function CheckboxesWidget< } }; - const _onBlur = ({ target: { value } }: FocusEvent) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onBlur = ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); - const _onFocus = ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onFocus = ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); const uiProps = _pick((options.props as object) || {}, allowedProps); diff --git a/packages/fluent-ui/src/DateWidget/DateWidget.tsx b/packages/fluent-ui/src/DateWidget/DateWidget.tsx index 4bf85ffed5..6ef3b18f4e 100644 --- a/packages/fluent-ui/src/DateWidget/DateWidget.tsx +++ b/packages/fluent-ui/src/DateWidget/DateWidget.tsx @@ -104,8 +104,8 @@ export default function DateWidget) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); const uiProps = _pick((options.props as object) || {}, allowedProps); return ( diff --git a/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx b/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx index 65b387cfba..8ebc4a9a34 100644 --- a/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx +++ b/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx @@ -48,10 +48,10 @@ export default function RadioWidget) => - onBlur(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); - const _onFocus = ({ target: { value } }: FocusEvent) => - onFocus(id, enumOptionsValueForIndex(value, enumOptions, emptyValue)); + const _onBlur = ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); + const _onFocus = ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)); const newOptions = Array.isArray(enumOptions) ? enumOptions.map((option, index) => ({ diff --git a/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx b/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx index 35507c55bc..f929787437 100644 --- a/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx +++ b/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx @@ -107,8 +107,8 @@ export default function UpDownWidget< } }; - const _onBlur = ({ target: { value } }: FocusEvent) => onBlur(id, value); - const _onFocus = ({ target: { value } }: FocusEvent) => onFocus(id, value); + const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value); + const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value); const uiProps = _pick((options.props as object) || {}, allowedProps); diff --git a/packages/fluent-ui/test/__snapshots__/Form.test.tsx.snap b/packages/fluent-ui/test/__snapshots__/Form.test.tsx.snap index 5d1a39229f..6965aad758 100644 --- a/packages/fluent-ui/test/__snapshots__/Form.test.tsx.snap +++ b/packages/fluent-ui/test/__snapshots__/Form.test.tsx.snap @@ -15,13 +15,13 @@ exports[`single fields checkbox field 1`] = ` } >