planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

373
node_modules/decap-cms-widget-list/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,373 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.2.2](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.2.1...decap-cms-widget-list@3.2.2) (2024-08-13)
### Reverts
- Revert "Update dependencies (#7264)" ([22d483a](https://github.com/decaporg/decap-cms/commit/22d483a5b0c654071ae05735ac4f49abdc13d38c)), closes [#7264](https://github.com/decaporg/decap-cms/issues/7264)
## [3.2.1](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.2.0...decap-cms-widget-list@3.2.1) (2024-08-13)
**Note:** Version bump only for package decap-cms-widget-list
# [3.2.0](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.1.1...decap-cms-widget-list@3.2.0) (2024-08-07)
**Note:** Version bump only for package decap-cms-widget-list
## [3.1.1](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.1.0-beta.1...decap-cms-widget-list@3.1.1) (2024-03-21)
**Note:** Version bump only for package decap-cms-widget-list
# [3.1.0](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.1.0-beta.1...decap-cms-widget-list@3.1.0) (2024-02-01)
**Note:** Version bump only for package decap-cms-widget-list
# [3.1.0-beta.1](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.1.0-beta.0...decap-cms-widget-list@3.1.0-beta.1) (2024-01-31)
**Note:** Version bump only for package decap-cms-widget-list
# [3.1.0-beta.0](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.1.0...decap-cms-widget-list@3.1.0-beta.0) (2023-10-20)
### Reverts
- Revert "chore(release): publish" ([b89fc89](https://github.com/decaporg/decap-cms/commit/b89fc894dfbb5f4136b2e5427fd25a29378a58c6))
## [3.0.4](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.0.3...decap-cms-widget-list@3.0.4) (2023-10-13)
**Note:** Version bump only for package decap-cms-widget-list
## [3.0.3](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.0.2...decap-cms-widget-list@3.0.3) (2023-10-10)
**Note:** Version bump only for package decap-cms-widget-list
## [3.0.2](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.0.1...decap-cms-widget-list@3.0.2) (2023-09-06)
**Note:** Version bump only for package decap-cms-widget-list
## [3.0.1](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@3.0.0...decap-cms-widget-list@3.0.1) (2023-08-25)
### Bug Fixes
- update peer dependencies ([#6886](https://github.com/decaporg/decap-cms/issues/6886)) ([e580ce5](https://github.com/decaporg/decap-cms/commit/e580ce52ce5f80fa040e8fbcab7fed0744f4f695))
# [3.0.0](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@2.11.0...decap-cms-widget-list@3.0.0) (2023-08-18)
**Note:** Version bump only for package decap-cms-widget-list
# [2.11.0](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@2.11.0-beta.0...decap-cms-widget-list@2.11.0) (2023-08-18)
**Note:** Version bump only for package decap-cms-widget-list
# 2.11.0-beta.0 (2023-08-18)
### Features
- rename packages ([#6863](https://github.com/decaporg/decap-cms/issues/6863)) ([d515e7b](https://github.com/decaporg/decap-cms/commit/d515e7bd33216a775d96887b08c4f7b1962941bb))
## [2.10.2-beta.0](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@2.10.1...decap-cms-widget-list@2.10.2-beta.0) (2023-07-27)
**Note:** Version bump only for package decap-cms-widget-list
## [2.10.1](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@2.10.0...decap-cms-widget-list@2.10.1) (2021-08-04)
### Bug Fixes
- **widget-list:** add missing translations in widget list top bar ([#5471](https://github.com/decaporg/decap-cms/issues/5471)) ([#5679](https://github.com/decaporg/decap-cms/issues/5679)) ([db560cc](https://github.com/decaporg/decap-cms/commit/db560cc082fcc0a9842919e28f715e44a6e4625a))
# [2.10.0](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@2.9.3...decap-cms-widget-list@2.10.0) (2021-07-14)
### Features
- **list:** Add heading for list widgets ([#5544](https://github.com/decaporg/decap-cms/issues/5544)) ([d60df87](https://github.com/decaporg/decap-cms/commit/d60df8786d7ea01af94c86a6e889654ce20e53ca))
## [2.9.3](https://github.com/decaporg/decap-cms/compare/decap-cms-widget-list@2.9.2...decap-cms-widget-list@2.9.3) (2021-07-07)
### Bug Fixes
- **list-control:** better value validation ([#5592](https://github.com/decaporg/decap-cms/issues/5592)) ([fb0f825](https://github.com/decaporg/decap-cms/commit/fb0f8259eae60c27a3a35c3db834c8311131d328))
## [2.9.2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.9.1...decap-cms-widget-list@2.9.2) (2021-06-01)
**Note:** Version bump only for package decap-cms-widget-list
## [2.9.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.9.0...decap-cms-widget-list@2.9.1) (2021-05-12)
### Bug Fixes
- **deps:** update dependency react-sortable-hoc to v2 ([#5371](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/5371)) ([b5dabc2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/b5dabc212953348bee83d41c36ca1f949c87b6b5))
# [2.9.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.8.5...decap-cms-widget-list@2.9.0) (2021-05-04)
### Features
- added react 17 as peer dependency in packages ([#5316](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/5316)) ([9e42380](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/9e423805707321396eec137f5b732a5b07a0dd3f))
## [2.8.5](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.8.4...decap-cms-widget-list@2.8.5) (2021-04-06)
### Bug Fixes
- allow any default list as default value for list widgets ([#5030](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/5030)) ([83c2354](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/83c235423e76023fe10f167c8f9087cba7a4c922))
## [2.8.4](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.8.3...decap-cms-widget-list@2.8.4) (2021-02-25)
**Note:** Version bump only for package decap-cms-widget-list
## [2.8.3](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.8.2...decap-cms-widget-list@2.8.3) (2021-02-23)
**Note:** Version bump only for package decap-cms-widget-list
## [2.8.2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.8.1...decap-cms-widget-list@2.8.2) (2021-02-10)
**Note:** Version bump only for package decap-cms-widget-list
## [2.8.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.8.0...decap-cms-widget-list@2.8.1) (2020-11-26)
### Bug Fixes
- list markdown widgets ([#4492](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/4492)) ([4789d20](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/4789d205386f589c3556df32700df25ad078904f))
# [2.8.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.7.0...decap-cms-widget-list@2.8.0) (2020-10-25)
### Bug Fixes
- **widget-list:** don't split an empty string ([#4464](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/4464)) ([f3a8eac](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/f3a8eac816710dbc639ad40b06742fa90885e613))
### Features
- add add_to_top option to list widget ([#4465](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/4465)) ([4c962f9](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/4c962f9149722074652c8939486650a4ddb1d7b3))
# [2.7.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.6.6...decap-cms-widget-list@2.7.0) (2020-10-20)
### Features
- **widget-list:** add min max configuration ([#4394](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/4394)) ([5fdfe40](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/5fdfe40dd29e9e22c9ae7d6219bc057f7ea7280b))
## [2.6.6](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.6.5...decap-cms-widget-list@2.6.6) (2020-09-20)
**Note:** Version bump only for package decap-cms-widget-list
## [2.6.5](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.6.4...decap-cms-widget-list@2.6.5) (2020-09-15)
**Note:** Version bump only for package decap-cms-widget-list
## 2.6.4 (2020-09-08)
### Reverts
- Revert "chore(release): publish" ([828bb16](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/828bb16415b8c22a34caa19c50c38b24ffe9ceae))
## 2.6.3 (2020-08-20)
### Reverts
- Revert "chore(release): publish" ([8262487](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/82624879ccbcb16610090041db28f00714d924c8))
## 2.6.2 (2020-07-27)
### Reverts
- Revert "chore(release): publish" ([118d50a](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/118d50a7a70295f25073e564b5161aa2b9883056))
## [2.6.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.6.0...decap-cms-widget-list@2.6.1) (2020-07-16)
### Bug Fixes
- **prop-types:** check for react components via PropTypes.elementType ([#4025](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/4025)) ([d3831b1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/d3831b1ed44fcff51a63f6645a5aa68332467dab))
# [2.6.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.5.1...decap-cms-widget-list@2.6.0) (2020-06-18)
### Features
- add widgets schema validation ([#3841](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3841)) ([2b46608](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/2b46608f86d22c8ad34f75e396be7c34462d9e99))
## [2.5.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.5.0...decap-cms-widget-list@2.5.1) (2020-06-01)
### Bug Fixes
- Error UI improvements for nested lists/objects ([#3726](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3726)) ([3978578](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/397857855b2c8514c2f7ce83756af6b6698abc3d))
# [2.5.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.4.6...decap-cms-widget-list@2.5.0) (2020-05-19)
### Features
- **decap-cms-widget-list:** allow 'summary' field ([#3616](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3616)) ([7cc4c89](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/7cc4c89539ddbd410ec7a1767c66f04e8d711cc5))
- **widget-list:** add hiding list content with minimize_collapsed option ([#3607](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3607)) ([4dd58c5](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/4dd58c5dcb22f4c0456a36d6c38883b54d8a7c4d))
## [2.4.6](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.4.4...decap-cms-widget-list@2.4.6) (2020-05-04)
**Note:** Version bump only for package decap-cms-widget-list
## [2.4.4](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.4.3...decap-cms-widget-list@2.4.4) (2020-04-20)
### Bug Fixes
- list widget item collapse toggle ([#3623](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3623)) ([3a666e2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/3a666e26b507f16767d0dafb82cd8a030424eec3))
- list widget validation after sort ([#3611](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3611)) ([3d0856e](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/3d0856ea884f5acdceb23ea09e39fb67b0c902a9))
## [2.4.3](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.4.2...decap-cms-widget-list@2.4.3) (2020-02-14)
### Bug Fixes
- remove empty list item ([#3245](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3245)) ([f915bf3](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/f915bf375fc8faa9b4ed5b3684861dfbe462a032))
## [2.4.2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.4.1...decap-cms-widget-list@2.4.2) (2020-02-13)
### Bug Fixes
- change getAsset to not return a promise ([#3232](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/3232)) ([ab685e8](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/ab685e85943d1ac48142f157683bc2126fd6af16))
## [2.4.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.4.0...decap-cms-widget-list@2.4.1) (2020-01-07)
### Bug Fixes
- cleanup nested widget validation ([#2991](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2991)) ([e4ba4d9](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/e4ba4d9d749c864e594c10e0bb31b0b8c4e6e60b))
# [2.4.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.4.0-beta.0...decap-cms-widget-list@2.4.0) (2019-12-18)
**Note:** Version bump only for package decap-cms-widget-list
# [2.4.0-beta.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.5-beta.1...decap-cms-widget-list@2.4.0-beta.0) (2019-12-16)
### Features
- Code Widget + Markdown Widget Internal Overhaul ([#2828](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2828)) ([18c579d](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/18c579d0e9f0ff71ed8c52f5c66f2309259af054))
## [2.3.5-beta.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.5-beta.0...decap-cms-widget-list@2.3.5-beta.1) (2019-11-07)
### Bug Fixes
- **widget-list:** when single field value is object widget ([#2387](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2387)) ([90748ff](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/90748ff4fe25f47dfc6aabd46cfc02df079bc126))
## [2.3.5-beta.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.4...decap-cms-widget-list@2.3.5-beta.0) (2019-09-04)
### Bug Fixes
- pass List instead of array to onChange ([#2611](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2611)) ([a801636](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/a801636))
## [2.3.4](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.3...decap-cms-widget-list@2.3.4) (2019-07-24)
**Note:** Version bump only for package decap-cms-widget-list
## [2.3.3](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.2...decap-cms-widget-list@2.3.3) (2019-07-11)
### Bug Fixes
- **widget-list:** honor default values for widgets in lists ([#2395](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2395)) ([83bd5d5](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/83bd5d5))
## [2.3.2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.2-beta.0...decap-cms-widget-list@2.3.2) (2019-04-10)
**Note:** Version bump only for package decap-cms-widget-list
## [2.3.2-beta.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.1...decap-cms-widget-list@2.3.2-beta.0) (2019-04-05)
**Note:** Version bump only for package decap-cms-widget-list
## [2.3.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.1-beta.2...decap-cms-widget-list@2.3.1) (2019-03-29)
**Note:** Version bump only for package decap-cms-widget-list
## [2.3.1-beta.2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.1-beta.1...decap-cms-widget-list@2.3.1-beta.2) (2019-03-28)
**Note:** Version bump only for package decap-cms-widget-list
## [2.3.1-beta.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.1-beta.0...decap-cms-widget-list@2.3.1-beta.1) (2019-03-26)
### Bug Fixes
- export on decap-cms and maps on esm ([#2244](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2244)) ([6ffd13b](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/6ffd13b))
## [2.3.1-beta.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.3.0...decap-cms-widget-list@2.3.1-beta.0) (2019-03-25)
### Bug Fixes
- update peer dep versions ([#2234](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2234)) ([7987091](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/7987091))
# [2.3.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.2.0...decap-cms-widget-list@2.3.0) (2019-03-22)
### Features
- add ES module builds ([#2215](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2215)) ([d142b32](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/d142b32))
# [2.2.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.2.0-beta.0...decap-cms-widget-list@2.2.0) (2019-03-22)
**Note:** Version bump only for package decap-cms-widget-list
# [2.2.0-beta.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.1.2-beta.0...decap-cms-widget-list@2.2.0-beta.0) (2019-03-21)
### Features
- provide usable UMD builds for all packages ([#2141](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2141)) ([82cc794](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/82cc794))
## [2.1.2-beta.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.1.1...decap-cms-widget-list@2.1.2-beta.0) (2019-03-15)
### Bug Fixes
- **deps:** update dependency react-sortable-hoc to v1 ([#2198](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2198)) ([b5180e9](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/b5180e9))
### Features
- upgrade to Emotion 10 ([#2166](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2166)) ([ccef446](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/ccef446))
## [2.1.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.1.0...decap-cms-widget-list@2.1.1) (2019-02-08)
### Bug Fixes
- **decap-cms-core:** fix fields metadata for objects and lists ([#2011](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/2011)) ([2d1d1c1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/2d1d1c1))
- **decap-cms-core:** validate nested fields ([#1873](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/1873)) ([627e600](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/627e600))
# [2.1.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.0.7...decap-cms-widget-list@2.1.0) (2018-12-27)
### Features
- **decap-cms-widget-list:** add variable type definitions to list widget ([#1857](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/1857)) ([8ddc168](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/8ddc168))
## [2.0.7](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.0.6...decap-cms-widget-list@2.0.7) (2018-11-12)
### Bug Fixes
- **widget-list:** fix list item deletion ([#1815](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/1815)) ([cd2036f](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/cd2036f))
<a name="2.0.6"></a>
## [2.0.6](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.0.5...decap-cms-widget-list@2.0.6) (2018-08-27)
**Note:** Version bump only for package decap-cms-widget-list
<a name="2.0.5"></a>
## [2.0.5](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.0.4...decap-cms-widget-list@2.0.5) (2018-08-24)
### Bug Fixes
- **list-widget:** fix single field usage in list widget ([#1395](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/issues/1395)) ([06d3650](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/commit/06d3650))
<a name="2.0.4"></a>
## [2.0.4](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.0.3...decap-cms-widget-list@2.0.4) (2018-08-07)
**Note:** Version bump only for package decap-cms-widget-list
<a name="2.0.3"></a>
## [2.0.3](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.0.2...decap-cms-widget-list@2.0.3) (2018-08-01)
**Note:** Version bump only for package decap-cms-widget-list
<a name="2.0.2"></a>
## [2.0.2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list/compare/decap-cms-widget-list@2.0.1...decap-cms-widget-list@2.0.2) (2018-07-28)
**Note:** Version bump only for package decap-cms-widget-list
<a name="2.0.1"></a>
## 2.0.1 (2018-07-26)
<a name="2.0.0"></a>
# 2.0.0 (2018-07-26)
**Note:** Version bump only for package decap-cms-widget-list

22
node_modules/decap-cms-widget-list/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2016 Netlify <decap@p-m.si>
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

9
node_modules/decap-cms-widget-list/README.md generated vendored Normal file
View File

@@ -0,0 +1,9 @@
# Docs coming soon!
Decap CMS was converted from a single npm package to a "monorepo" of over 20 packages.
We haven't created a README for this package yet, but you can:
1. Check out the [main readme](https://github.com/decaporg/decap-cms/#readme) or the [documentation
site](https://www.decapcms.org) for more info.
2. Reach out to the [community chat](https://decapcms.org/chat/) if you need help.
3. Help out and [write the readme yourself](https://github.com/decaporg/decap-cms/edit/main/packages/decap-cms-widget-list/README.md)!

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

30
node_modules/decap-cms-widget-list/dist/esm/index.js generated vendored Normal file
View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.DecapCmsWidgetList = void 0;
var _decapCmsWidgetObject = _interopRequireDefault(require("decap-cms-widget-object"));
var _ListControl = _interopRequireDefault(require("./ListControl"));
var _schema = _interopRequireDefault(require("./schema"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const previewComponent = _decapCmsWidgetObject.default.previewComponent;
function Widget(opts = {}) {
return _objectSpread({
name: 'list',
controlComponent: _ListControl.default,
previewComponent,
schema: _schema.default
}, opts);
}
const DecapCmsWidgetList = exports.DecapCmsWidgetList = {
Widget,
controlComponent: _ListControl.default,
previewComponent
};
var _default = exports.default = DecapCmsWidgetList;

34
node_modules/decap-cms-widget-list/dist/esm/schema.js generated vendored Normal file
View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _default = exports.default = {
properties: {
allow_add: {
type: 'boolean'
},
collapsed: {
type: 'boolean'
},
summary: {
type: 'string'
},
minimize_collapsed: {
type: 'boolean'
},
label_singular: {
type: 'string'
},
i18n: {
type: 'boolean'
},
min: {
type: 'number'
},
max: {
type: 'number'
}
}
};

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TYPE_KEY = exports.TYPES_KEY = exports.DEFAULT_TYPE_KEY = void 0;
exports.getErrorMessageForTypedFieldAndValue = getErrorMessageForTypedFieldAndValue;
exports.getTypedFieldForValue = getTypedFieldForValue;
exports.resolveFieldKeyType = resolveFieldKeyType;
exports.resolveFunctionForTypedField = resolveFunctionForTypedField;
const TYPES_KEY = exports.TYPES_KEY = 'types';
const TYPE_KEY = exports.TYPE_KEY = 'typeKey';
const DEFAULT_TYPE_KEY = exports.DEFAULT_TYPE_KEY = 'type';
function getTypedFieldForValue(field, value) {
const typeKey = resolveFieldKeyType(field);
const types = field.get(TYPES_KEY);
const valueType = value.get(typeKey);
return types.find(type => type.get('name') === valueType);
}
function resolveFunctionForTypedField(field) {
const typeKey = resolveFieldKeyType(field);
const types = field.get(TYPES_KEY);
return value => {
const valueType = value.get(typeKey);
return types.find(type => type.get('name') === valueType);
};
}
function resolveFieldKeyType(field) {
return field.get(TYPE_KEY, DEFAULT_TYPE_KEY);
}
function getErrorMessageForTypedFieldAndValue(field, value) {
const keyType = resolveFieldKeyType(field);
const type = value.get(keyType);
let errorMessage;
if (!type) {
errorMessage = `Error: item has no '${keyType}' property`;
} else {
errorMessage = `Error: item has illegal '${keyType}' property: '${type}'`;
}
return errorMessage;
}

41
node_modules/decap-cms-widget-list/package.json generated vendored Normal file
View File

@@ -0,0 +1,41 @@
{
"name": "decap-cms-widget-list",
"description": "Widget for editing lists in Decap CMS.",
"version": "3.2.2",
"homepage": "https://www.decapcms.org/docs/widgets/#list",
"repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list",
"bugs": "https://github.com/decaporg/decap-cms/issues",
"module": "dist/esm/index.js",
"main": "dist/decap-cms-widget-list.js",
"license": "MIT",
"keywords": [
"decap-cms",
"widget",
"list",
"object"
],
"sideEffects": false,
"scripts": {
"develop": "npm run build:esm -- --watch",
"build": "cross-env NODE_ENV=production webpack",
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward"
},
"dependencies": {
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2"
},
"peerDependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"decap-cms-lib-widgets": "^3.0.0",
"decap-cms-ui-default": "^3.0.0",
"decap-cms-widget-object": "^3.0.0",
"immutable": "^3.7.6",
"lodash": "^4.17.11",
"prop-types": "^15.7.2",
"react": "^18.2.0",
"react-immutable-proptypes": "^2.1.0"
},
"gitHead": "64d91b8bb3d0a93dd36c53800cdac4ba2e435000"
}

751
node_modules/decap-cms-widget-list/src/ListControl.js generated vendored Normal file
View File

@@ -0,0 +1,751 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from '@emotion/styled';
import { css, ClassNames } from '@emotion/react';
import { List, Map, fromJS } from 'immutable';
import { partial, isEmpty, uniqueId } from 'lodash';
import { v4 as uuid } from 'uuid';
import DecapCmsWidgetObject from 'decap-cms-widget-object';
import {
DndContext,
MouseSensor,
TouchSensor,
closestCenter,
useSensor,
useSensors,
} from '@dnd-kit/core';
import { SortableContext, useSortable } from '@dnd-kit/sortable';
import { restrictToParentElement } from '@dnd-kit/modifiers';
import { CSS } from '@dnd-kit/utilities';
import {
ListItemTopBar,
ObjectWidgetTopBar,
colors,
lengths,
FieldLabel,
} from 'decap-cms-ui-default';
import { stringTemplate, validations } from 'decap-cms-lib-widgets';
import {
TYPES_KEY,
getTypedFieldForValue,
resolveFieldKeyType,
getErrorMessageForTypedFieldAndValue,
} from './typedListHelpers';
const ObjectControl = DecapCmsWidgetObject.controlComponent;
const ListItem = styled.div();
const StyledListItemTopBar = styled(ListItemTopBar)`
background-color: ${colors.textFieldBorder};
`;
const NestedObjectLabel = styled.div`
display: ${props => (props.collapsed ? 'block' : 'none')};
border-top: 0;
color: ${props => (props.error ? colors.errorText : 'inherit')};
background-color: ${colors.textFieldBorder};
padding: 13px;
border-radius: 0 0 ${lengths.borderRadius} ${lengths.borderRadius};
`;
const styleStrings = {
collapsedObjectControl: `
display: none;
`,
objectWidgetTopBarContainer: `
padding: ${lengths.objectWidgetTopBarContainerPadding};
`,
};
const styles = {
listControlItem: css`
margin-top: 18px;
&:first-of-type {
margin-top: 26px;
}
`,
listControlItemCollapsed: css`
padding-bottom: 0;
`,
};
function SortableList({ items, children, onSortEnd, keys }) {
const activationConstraint = { distance: 4 };
const sensors = useSensors(
useSensor(MouseSensor, { activationConstraint }),
useSensor(TouchSensor, { activationConstraint }),
);
function handleSortEnd({ active, over }) {
onSortEnd({
oldIndex: keys.indexOf(active.id),
newIndex: keys.indexOf(over.id),
});
}
return (
<div>
<DndContext
modifiers={[restrictToParentElement]}
collisionDetection={closestCenter}
sensors={sensors}
onDragEnd={handleSortEnd}
>
<SortableContext items={items}>{children}</SortableContext>
</DndContext>
</div>
);
}
function SortableListItem(props) {
const { setNodeRef, transform, transition } = useSortable({
id: props.id,
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
const { collapsed } = props;
return (
<ListItem
sortable
ref={setNodeRef}
style={style}
css={[styles.listControlItem, collapsed && styles.listControlItemCollapsed]}
>
{props.children}
</ListItem>
);
}
function DragHandle({ children, id }) {
const { attributes, listeners } = useSortable({
id,
});
return (
<div {...attributes} {...listeners}>
{children}
</div>
);
}
const valueTypes = {
SINGLE: 'SINGLE',
MULTIPLE: 'MULTIPLE',
MIXED: 'MIXED',
};
function handleSummary(summary, entry, label, item) {
const data = stringTemplate.addFileTemplateFields(
entry.get('path'),
item.set('fields.label', label),
);
return stringTemplate.compileStringTemplate(summary, null, '', data);
}
function validateItem(field, item) {
if (!Map.isMap(item)) {
console.warn(
`'${field.get('name')}' field item value value should be a map but is a '${typeof item}'`,
);
return false;
}
return true;
}
function LabelComponent({ field, isActive, hasErrors, uniqueFieldId, isFieldOptional, t }) {
const label = `${field.get('label', field.get('name'))}`;
return (
<FieldLabel isActive={isActive} hasErrors={hasErrors} htmlFor={uniqueFieldId}>
{label} {`${isFieldOptional ? ` (${t('editor.editorControl.field.optional')})` : ''}`}
</FieldLabel>
);
}
export default class ListControl extends React.Component {
validations = [];
static propTypes = {
metadata: ImmutablePropTypes.map,
onChange: PropTypes.func.isRequired,
onChangeObject: PropTypes.func.isRequired,
onValidateObject: PropTypes.func.isRequired,
validate: PropTypes.func.isRequired,
value: ImmutablePropTypes.list,
field: PropTypes.object,
forID: PropTypes.string,
controlRef: PropTypes.func,
mediaPaths: ImmutablePropTypes.map.isRequired,
getAsset: PropTypes.func.isRequired,
onOpenMediaLibrary: PropTypes.func.isRequired,
onAddAsset: PropTypes.func.isRequired,
onRemoveInsertedMedia: PropTypes.func.isRequired,
classNameWrapper: PropTypes.string.isRequired,
setActiveStyle: PropTypes.func.isRequired,
setInactiveStyle: PropTypes.func.isRequired,
editorControl: PropTypes.elementType.isRequired,
resolveWidget: PropTypes.func.isRequired,
clearFieldErrors: PropTypes.func.isRequired,
fieldsErrors: ImmutablePropTypes.map.isRequired,
entry: ImmutablePropTypes.map.isRequired,
t: PropTypes.func,
};
static defaultProps = {
value: List(),
parentIds: [],
};
constructor(props) {
super(props);
const { field, value } = props;
const listCollapsed = field.get('collapsed', true);
const itemsCollapsed = (value && Array(value.size).fill(listCollapsed)) || [];
const keys = (value && Array.from({ length: value.size }, () => uuid())) || [];
this.state = {
listCollapsed,
itemsCollapsed,
value: this.valueToString(value),
keys,
};
}
valueToString = value => {
let stringValue;
if (List.isList(value) || Array.isArray(value)) {
stringValue = value.join(',');
} else {
console.warn(
`Expected List value to be an array but received '${value}' with type of '${typeof value}'. Please check the value provided to the '${this.props.field.get(
'name',
)}' field`,
);
stringValue = String(value);
}
return stringValue.replace(/,([^\s]|$)/g, ', $1');
};
getValueType = () => {
const { field } = this.props;
if (field.get('fields')) {
return valueTypes.MULTIPLE;
} else if (field.get('field')) {
return valueTypes.SINGLE;
} else if (field.get(TYPES_KEY)) {
return valueTypes.MIXED;
} else {
return null;
}
};
uniqueFieldId = uniqueId(`${this.props.field.get('name')}-field-`);
/**
* Always update so that each nested widget has the option to update. This is
* required because ControlHOC provides a default `shouldComponentUpdate`
* which only updates if the value changes, but every widget must be allowed
* to override this.
*/
shouldComponentUpdate() {
return true;
}
handleChange = e => {
const { onChange } = this.props;
const oldValue = this.state.value;
const newValue = e.target.value.trim();
const listValue = newValue ? newValue.split(',') : [];
if (newValue.match(/,$/) && oldValue.match(/, $/)) {
listValue.pop();
}
const parsedValue = this.valueToString(listValue);
this.setState({ value: parsedValue });
onChange(List(listValue.map(val => val.trim())));
};
handleFocus = () => {
this.props.setActiveStyle();
};
handleBlur = e => {
const listValue = e.target.value
.split(',')
.map(el => el.trim())
.filter(el => el);
this.setState({ value: this.valueToString(listValue) });
this.props.setInactiveStyle();
};
handleAdd = e => {
e.preventDefault();
const { field } = this.props;
const parsedValue =
this.getValueType() === valueTypes.SINGLE
? this.singleDefault()
: fromJS(this.multipleDefault(field.get('fields')));
this.addItem(parsedValue);
};
singleDefault = () => {
return this.props.field.getIn(['field', 'default'], null);
};
multipleDefault = fields => {
return this.getFieldsDefault(fields);
};
handleAddType = (type, typeKey) => {
const parsedValue = fromJS(this.mixedDefault(typeKey, type));
this.addItem(parsedValue);
};
mixedDefault = (typeKey, type) => {
const selectedType = this.props.field.get(TYPES_KEY).find(f => f.get('name') === type);
const fields = selectedType.get('fields') || [selectedType.get('field')];
return this.getFieldsDefault(fields, { [typeKey]: type });
};
getFieldsDefault = (fields, initialValue = {}) => {
return fields.reduce((acc, item) => {
const subfields = item.get('field') || item.get('fields');
const object = item.get('widget') == 'object';
const name = item.get('name');
const defaultValue = item.get('default', null);
if (List.isList(subfields) && object) {
const subDefaultValue = this.getFieldsDefault(subfields);
!isEmpty(subDefaultValue) && (acc[name] = subDefaultValue);
return acc;
}
if (Map.isMap(subfields) && object) {
const subDefaultValue = this.getFieldsDefault([subfields]);
!isEmpty(subDefaultValue) && (acc[name] = subDefaultValue);
return acc;
}
if (defaultValue !== null) {
acc[name] = defaultValue;
}
return acc;
}, initialValue);
};
addItem = parsedValue => {
const { value, onChange, field } = this.props;
const addToTop = field.get('add_to_top', false);
const itemKey = uuid();
this.setState({
itemsCollapsed: addToTop
? [false, ...this.state.itemsCollapsed]
: [...this.state.itemsCollapsed, false],
keys: addToTop ? [itemKey, ...this.state.keys] : [...this.state.keys, itemKey],
});
const listValue = value || List();
if (addToTop) {
onChange(listValue.unshift(parsedValue));
} else {
onChange(listValue.push(parsedValue));
}
};
processControlRef = ref => {
if (!ref) return;
const {
validate,
props: { validationKey: key },
} = ref;
this.validations.push({ key, validate });
};
validate = () => {
if (this.getValueType()) {
this.validations.forEach(item => {
item.validate();
});
} else {
this.props.validate();
}
this.props.onValidateObject(this.props.forID, this.validateSize());
};
validateSize = () => {
const { field, value, t } = this.props;
const min = field.get('min');
const max = field.get('max');
const required = field.get('required', true);
if (!required && !value?.size) {
return [];
}
const error = validations.validateMinMax(
t,
field.get('label', field.get('name')),
value,
min,
max,
);
return error ? [error] : [];
};
/**
* In case the `onChangeObject` function is frozen by a child widget implementation,
* e.g. when debounced, always get the latest object value instead of using
* `this.props.value` directly.
*/
getObjectValue = idx => this.props.value.get(idx) || Map();
handleChangeFor(index) {
return (f, newValue, newMetadata) => {
const { value, metadata, onChange, field } = this.props;
const collectionName = field.get('name');
const listFieldObjectWidget = field.getIn(['field', 'widget']) === 'object';
const withNameKey =
this.getValueType() !== valueTypes.SINGLE ||
(this.getValueType() === valueTypes.SINGLE && listFieldObjectWidget);
const newObjectValue = withNameKey
? this.getObjectValue(index).set(f.get('name'), newValue)
: newValue;
const parsedMetadata = {
[collectionName]: Object.assign(metadata ? metadata.toJS() : {}, newMetadata || {}),
};
onChange(value.set(index, newObjectValue), parsedMetadata);
};
}
handleRemove = (index, event) => {
event.preventDefault();
const { itemsCollapsed } = this.state;
const { value, metadata, onChange, field, clearFieldErrors } = this.props;
const collectionName = field.get('name');
const isSingleField = this.getValueType() === valueTypes.SINGLE;
const metadataRemovePath = isSingleField ? value.get(index) : value.get(index).valueSeq();
const parsedMetadata =
metadata && !metadata.isEmpty()
? { [collectionName]: metadata.removeIn(metadataRemovePath) }
: metadata;
itemsCollapsed.splice(index, 1);
// clear validations
this.validations = [];
this.setState({
itemsCollapsed: [...itemsCollapsed],
keys: Array.from({ length: value.size - 1 }, () => uuid()),
});
onChange(value.remove(index), parsedMetadata);
clearFieldErrors();
};
handleItemCollapseToggle = (index, event) => {
event.preventDefault();
const { itemsCollapsed } = this.state;
const newItemsCollapsed = itemsCollapsed.map((collapsed, itemIndex) => {
if (index === itemIndex) {
return !collapsed;
}
return collapsed;
});
this.setState({
itemsCollapsed: newItemsCollapsed,
});
};
handleCollapseAllToggle = e => {
e.preventDefault();
const { value, field } = this.props;
const { itemsCollapsed, listCollapsed } = this.state;
const minimizeCollapsedItems = field.get('minimize_collapsed', false);
const listCollapsedByDefault = field.get('collapsed', true);
const allItemsCollapsed = itemsCollapsed.every(val => val === true);
if (minimizeCollapsedItems) {
let updatedItemsCollapsed = itemsCollapsed;
// Only allow collapsing all items in this mode but not opening all at once
if (!listCollapsed || !listCollapsedByDefault) {
updatedItemsCollapsed = Array(value.size).fill(!listCollapsed);
}
this.setState({ listCollapsed: !listCollapsed, itemsCollapsed: updatedItemsCollapsed });
} else {
this.setState({ itemsCollapsed: Array(value.size).fill(!allItemsCollapsed) });
}
};
objectLabel(item) {
const { field, entry } = this.props;
const valueType = this.getValueType();
switch (valueType) {
case valueTypes.MIXED: {
if (!validateItem(field, item)) {
return;
}
const itemType = getTypedFieldForValue(field, item);
const label = itemType.get('label', itemType.get('name'));
// each type can have its own summary, but default to the list summary if exists
const summary = itemType.get('summary', field.get('summary'));
const labelReturn = summary ? handleSummary(summary, entry, label, item) : label;
return labelReturn;
}
case valueTypes.SINGLE: {
const singleField = field.get('field');
const label = singleField.get('label', singleField.get('name'));
const summary = field.get('summary');
const data = fromJS({ [singleField.get('name')]: item });
const labelReturn = summary ? handleSummary(summary, entry, label, data) : label;
return labelReturn;
}
case valueTypes.MULTIPLE: {
if (!validateItem(field, item)) {
return;
}
const multiFields = field.get('fields');
const labelField = multiFields && multiFields.first();
const value = item.get(labelField.get('name'));
const summary = field.get('summary');
const labelReturn = summary ? handleSummary(summary, entry, value, item) : value;
return (labelReturn || `No ${labelField.get('name')}`).toString();
}
}
return '';
}
onSortEnd = ({ oldIndex, newIndex }) => {
const { value, clearFieldErrors } = this.props;
const { itemsCollapsed, keys } = this.state;
// Update value
const item = value.get(oldIndex);
const newValue = value.delete(oldIndex).insert(newIndex, item);
this.props.onChange(newValue);
// Update collapsing
const collapsed = itemsCollapsed[oldIndex];
itemsCollapsed.splice(oldIndex, 1);
const updatedItemsCollapsed = [...itemsCollapsed];
updatedItemsCollapsed.splice(newIndex, 0, collapsed);
// Reset item to ensure updated state
const updatedKeys = keys.map((key, keyIndex) => {
if (keyIndex === oldIndex || keyIndex === newIndex) {
return uuid();
}
return key;
});
this.setState({ itemsCollapsed: updatedItemsCollapsed, keys: updatedKeys });
//clear error fields and remove old validations
clearFieldErrors();
this.validations = this.validations.filter(item => updatedKeys.includes(item.key));
};
hasError = index => {
const { fieldsErrors } = this.props;
if (fieldsErrors && fieldsErrors.size > 0) {
return Object.values(fieldsErrors.toJS()).some(arr =>
arr.some(err => err.parentIds && err.parentIds.includes(this.state.keys[index])),
);
}
};
// eslint-disable-next-line react/display-name
renderItem = (item, index) => {
const {
classNameWrapper,
editorControl,
onValidateObject,
metadata,
clearFieldErrors,
fieldsErrors,
controlRef,
resolveWidget,
parentIds,
forID,
t,
} = this.props;
const { itemsCollapsed, keys } = this.state;
const collapsed = itemsCollapsed[index];
const key = keys[index];
let field = this.props.field;
const hasError = this.hasError(index);
const isVariableTypesList = this.getValueType() === valueTypes.MIXED;
if (isVariableTypesList) {
field = getTypedFieldForValue(field, item);
if (!field) {
return this.renderErroneousTypedItem(index, item);
}
}
return (
<SortableListItem
css={[styles.listControlItem, collapsed && styles.listControlItemCollapsed]}
index={index}
key={key}
id={key}
keys={keys}
>
{isVariableTypesList && (
<LabelComponent
field={field}
isActive={false}
hasErrors={hasError}
uniqueFieldId={this.uniqueFieldId}
isFieldOptional={field.get('required') === false}
t={t}
/>
)}
<StyledListItemTopBar
collapsed={collapsed}
onCollapseToggle={partial(this.handleItemCollapseToggle, index)}
dragHandle={DragHandle}
id={key}
onRemove={partial(this.handleRemove, index)}
data-testid={`styled-list-item-top-bar-${key}`}
/>
<NestedObjectLabel collapsed={collapsed} error={hasError}>
{this.objectLabel(item)}
</NestedObjectLabel>
<ClassNames>
{({ css, cx }) => (
<ObjectControl
classNameWrapper={cx(classNameWrapper, {
[css`
${styleStrings.collapsedObjectControl};
`]: collapsed,
})}
value={item}
field={field}
onChangeObject={this.handleChangeFor(index)}
editorControl={editorControl}
resolveWidget={resolveWidget}
metadata={metadata}
forList
onValidateObject={onValidateObject}
clearFieldErrors={clearFieldErrors}
fieldsErrors={fieldsErrors}
ref={this.processControlRef}
controlRef={controlRef}
validationKey={key}
collapsed={collapsed}
data-testid={`object-control-${key}`}
hasError={hasError}
parentIds={[...parentIds, forID, key]}
/>
)}
</ClassNames>
</SortableListItem>
);
};
renderErroneousTypedItem(index, item) {
const field = this.props.field;
const errorMessage = getErrorMessageForTypedFieldAndValue(field, item);
const key = `item-${index}`;
return (
<SortableListItem
css={[styles.listControlItem, styles.listControlItemCollapsed]}
index={index}
key={key}
>
<StyledListItemTopBar
onCollapseToggle={null}
onRemove={partial(this.handleRemove, index, key)}
dragHandle={DragHandle}
id={key}
/>
<NestedObjectLabel collapsed={true} error={true}>
{errorMessage}
</NestedObjectLabel>
</SortableListItem>
);
}
renderListControl() {
const { value, forID, field, classNameWrapper, t } = this.props;
const { itemsCollapsed, listCollapsed, keys } = this.state;
const items = value || List();
const label = field.get('label', field.get('name'));
const labelSingular = field.get('label_singular') || field.get('label', field.get('name'));
const listLabel = items.size === 1 ? labelSingular.toLowerCase() : label.toLowerCase();
const minimizeCollapsedItems = field.get('minimize_collapsed', false);
const allItemsCollapsed = itemsCollapsed.every(val => val === true);
const selfCollapsed = allItemsCollapsed && (listCollapsed || !minimizeCollapsedItems);
const itemsArray = keys.map(key => ({ id: key }));
return (
<ClassNames>
{({ cx, css }) => (
<div
id={forID}
className={cx(
classNameWrapper,
css`
${styleStrings.objectWidgetTopBarContainer}
`,
)}
>
<ObjectWidgetTopBar
allowAdd={field.get('allow_add', true)}
onAdd={this.handleAdd}
types={field.get(TYPES_KEY, null)}
onAddType={type => this.handleAddType(type, resolveFieldKeyType(field))}
heading={`${items.size} ${listLabel}`}
label={labelSingular.toLowerCase()}
onCollapseToggle={this.handleCollapseAllToggle}
collapsed={selfCollapsed}
t={t}
/>
{(!selfCollapsed || !minimizeCollapsedItems) && (
<SortableList items={itemsArray} keys={keys} onSortEnd={this.onSortEnd}>
{items.map(this.renderItem)}
</SortableList>
)}
</div>
)}
</ClassNames>
);
}
renderInput() {
const { forID, classNameWrapper } = this.props;
const { value } = this.state;
return (
<input
type="text"
id={forID}
value={value}
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
className={classNameWrapper}
/>
);
}
render() {
if (this.getValueType() !== null) {
return this.renderListControl();
} else {
return this.renderInput();
}
}
}

View File

@@ -0,0 +1,792 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import { fromJS } from 'immutable';
import ListControl from '../ListControl';
jest.mock('decap-cms-widget-object', () => {
const React = require('react');
class MockObjectControl extends React.Component {
render() {
return <mock-object-control {...this.props}>{this.props.children}</mock-object-control>;
}
}
return {
controlComponent: MockObjectControl,
};
});
jest.mock('decap-cms-ui-default', () => {
const actual = jest.requireActual('decap-cms-ui-default');
function ListItemTopBar(props) {
return (
<mock-list-item-top-bar {...props} onClick={props.onCollapseToggle}>
<button onClick={props.onRemove}>Remove</button>
{props.children}
</mock-list-item-top-bar>
);
}
return {
...actual,
ListItemTopBar,
};
});
jest.mock('uuid');
describe('ListControl', () => {
const props = {
onChange: jest.fn(),
onChangeObject: jest.fn(),
onValidateObject: jest.fn(),
validate: jest.fn(),
mediaPaths: fromJS({}),
getAsset: jest.fn(),
onOpenMediaLibrary: jest.fn(),
onAddAsset: jest.fn(),
onRemoveInsertedMedia: jest.fn(),
classNameWrapper: 'classNameWrapper',
setActiveStyle: jest.fn(),
setInactiveStyle: jest.fn(),
editorControl: jest.fn(),
resolveWidget: jest.fn(),
clearFieldErrors: jest.fn(),
fieldsErrors: fromJS({}),
entry: fromJS({
path: 'posts/index.md',
}),
forID: 'forID',
t: key => key,
};
beforeEach(() => {
jest.clearAllMocks();
const uuid = require('uuid');
let id = 0;
uuid.v4.mockImplementation(() => {
return id++;
});
});
it('should render empty list', () => {
const field = fromJS({ name: 'list', label: 'List' });
const { asFragment } = render(<ListControl {...props} field={field} />);
expect(asFragment()).toMatchSnapshot();
});
it('should render list with string array', () => {
const field = fromJS({ name: 'list', label: 'List' });
const { asFragment } = render(
<ListControl {...props} field={field} value={fromJS(['item 1', 'item 2'])} />,
);
expect(asFragment()).toMatchSnapshot();
});
it('should render list with nested object', () => {
const field = fromJS({
name: 'list',
label: 'List',
field: {
name: 'object',
widget: 'object',
label: 'Object',
fields: [{ name: 'title', widget: 'string', label: 'Title' }],
},
});
const { asFragment, getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ object: { title: 'item 1' } }, { object: { title: 'item 2' } }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'true');
expect(asFragment()).toMatchSnapshot();
});
it('should render list with nested object with collapse = false', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
field: {
name: 'object',
widget: 'object',
label: 'Object',
fields: [{ name: 'title', widget: 'string', label: 'Title' }],
},
});
const { asFragment, getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ object: { title: 'item 1' } }, { object: { title: 'item 2' } }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'false');
expect(asFragment()).toMatchSnapshot();
});
it('should collapse all items on top bar collapse click', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
field: {
name: 'object',
widget: 'object',
label: 'Object',
fields: [{ name: 'title', widget: 'string', label: 'Title' }],
},
});
const { getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ object: { title: 'item 1' } }, { object: { title: 'item 2' } }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'false');
fireEvent.click(getByTestId('expand-button'));
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'true');
});
it('should collapse a single item on collapse item click', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
field: {
name: 'object',
widget: 'object',
label: 'Object',
fields: [{ name: 'title', widget: 'string', label: 'Title' }],
},
});
const { getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ object: { title: 'item 1' } }, { object: { title: 'item 2' } }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'false');
fireEvent.click(getByTestId('styled-list-item-top-bar-0'));
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'false');
});
it('should expand all items on top bar expand click', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
field: {
name: 'object',
widget: 'object',
label: 'Object',
fields: [{ name: 'title', widget: 'string', label: 'Title' }],
},
});
const { getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ object: { title: 'item 1' } }, { object: { title: 'item 2' } }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'true');
fireEvent.click(getByTestId('expand-button'));
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'false');
});
it('should expand a single item on expand item click', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
field: {
name: 'object',
widget: 'object',
label: 'Object',
fields: [{ name: 'title', widget: 'string', label: 'Title' }],
},
});
const { getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ object: { title: 'item 1' } }, { object: { title: 'item 2' } }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'true');
fireEvent.click(getByTestId('styled-list-item-top-bar-0'));
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'true');
});
it('should use widget name when no summary or label are configured for mixed types', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
types: [
{
name: 'type_1_object',
widget: 'object',
fields: [
{ label: 'First Name', name: 'first_name', widget: 'string' },
{ label: 'Last Name', name: 'last_name', widget: 'string' },
],
},
],
});
const { getAllByText } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ first_name: 'hello', last_name: 'world', type: 'type_1_object' }])}
/>,
);
expect(getAllByText('type_1_object')[1]).toBeInTheDocument();
});
it('should use label when no summary is configured for mixed types', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
types: [
{
label: 'Type 1 Object',
name: 'type_1_object',
widget: 'object',
fields: [
{ label: 'First Name', name: 'first_name', widget: 'string' },
{ label: 'Last Name', name: 'last_name', widget: 'string' },
],
},
],
});
const { getAllByText } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ first_name: 'hello', last_name: 'world', type: 'type_1_object' }])}
/>,
);
expect(getAllByText('Type 1 Object')[1]).toBeInTheDocument();
});
it('should use summary when configured for mixed types', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
types: [
{
label: 'Type 1 Object',
name: 'type_1_object',
summary: '{{first_name}} - {{last_name}} - {{filename}}.{{extension}}',
widget: 'object',
fields: [
{ label: 'First Name', name: 'first_name', widget: 'string' },
{ label: 'Last Name', name: 'last_name', widget: 'string' },
],
},
],
});
const { getByText } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ first_name: 'hello', last_name: 'world', type: 'type_1_object' }])}
/>,
);
expect(getByText('hello - world - index.md')).toBeInTheDocument();
});
it('should use widget name when no summary or label are configured for a single field', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
field: { name: 'name', widget: 'string' },
});
const { getByText } = render(<ListControl {...props} field={field} value={fromJS(['Name'])} />);
expect(getByText('name')).toBeInTheDocument();
});
it('should use label when no summary is configured for a single field', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
field: { name: 'name', widget: 'string', label: 'Name' },
});
const { getByText } = render(<ListControl {...props} field={field} value={fromJS(['Name'])} />);
expect(getByText('Name')).toBeInTheDocument();
});
it('should use summary when configured for a single field', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
summary: 'Name - {{fields.name}}',
field: { name: 'name', widget: 'string', label: 'Name' },
});
const { getByText } = render(<ListControl {...props} field={field} value={fromJS(['Name'])} />);
expect(getByText('Name - Name')).toBeInTheDocument();
});
it('should use first field value when no summary or label are configured for multiple fields', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
fields: [
{ name: 'first_name', widget: 'string', label: 'First Name' },
{ name: 'last_name', widget: 'string', label: 'Last Name' },
],
});
const { getByText } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ first_name: 'hello', last_name: 'world' }])}
/>,
);
expect(getByText('hello')).toBeInTheDocument();
});
it('should show `No <field>` when value is missing from first field for multiple fields', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
fields: [
{ name: 'first_name', widget: 'string', label: 'First Name' },
{ name: 'last_name', widget: 'string', label: 'Last Name' },
],
});
const { getByText } = render(
<ListControl {...props} field={field} value={fromJS([{ last_name: 'world' }])} />,
);
expect(getByText('No first_name')).toBeInTheDocument();
});
it('should use summary when configured for multiple fields', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: true,
summary: '{{first_name}} - {{last_name}} - {{filename}}.{{extension}}',
fields: [
{ name: 'first_name', widget: 'string', label: 'First Name' },
{ name: 'last_name', widget: 'string', label: 'Last Name' },
],
});
const { getByText } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ first_name: 'hello', last_name: 'world' }])}
/>,
);
expect(getByText('hello - world - index.md')).toBeInTheDocument();
});
it('should render list with fields with default collapse ("true") and minimize_collapsed ("false")', () => {
const field = fromJS({
name: 'list',
label: 'List',
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const { asFragment, getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ string: 'item 1' }, { string: 'item 2' }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'true');
expect(asFragment()).toMatchSnapshot();
});
it('should render list with fields with collapse = "false" and default minimize_collapsed ("false")', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const { asFragment, getByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ string: 'item 1' }, { string: 'item 2' }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'false');
expect(asFragment()).toMatchSnapshot();
});
it('should render list with fields with default collapse ("true") and minimize_collapsed = "true"', () => {
const field = fromJS({
name: 'list',
label: 'List',
minimize_collapsed: true,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const { asFragment, getByTestId, queryByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ string: 'item 1' }, { string: 'item 2' }])}
/>,
);
expect(queryByTestId('styled-list-item-top-bar-0')).toBeNull();
expect(queryByTestId('styled-list-item-top-bar-1')).toBeNull();
expect(queryByTestId('object-control-0')).toBeNull();
expect(queryByTestId('object-control-1')).toBeNull();
expect(asFragment()).toMatchSnapshot();
fireEvent.click(getByTestId('expand-button'));
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'true');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'true');
});
it('should render list with fields with collapse = "false" and default minimize_collapsed = "true"', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const { asFragment, getByTestId, queryByTestId } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ string: 'item 1' }, { string: 'item 2' }])}
/>,
);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('styled-list-item-top-bar-1')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-1')).toHaveAttribute('collapsed', 'false');
expect(asFragment()).toMatchSnapshot();
fireEvent.click(getByTestId('expand-button'));
expect(queryByTestId('styled-list-item-top-bar-0')).toBeNull();
expect(queryByTestId('styled-list-item-top-bar-1')).toBeNull();
expect(queryByTestId('object-control-0')).toBeNull();
expect(queryByTestId('object-control-1')).toBeNull();
});
it('should add to list when add button is clicked', () => {
const field = fromJS({
name: 'list',
label: 'List',
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const { asFragment, getByText, queryByTestId, rerender, getByTestId } = render(
<ListControl {...props} field={field} value={fromJS([])} />,
);
expect(queryByTestId('object-control-0')).toBeNull();
fireEvent.click(getByText('editor.editorWidgets.list.add'));
expect(props.onChange).toHaveBeenCalledTimes(1);
expect(props.onChange).toHaveBeenCalledWith(fromJS([{}]));
rerender(<ListControl {...props} field={field} value={fromJS([{}])} />);
expect(getByTestId('styled-list-item-top-bar-0')).toHaveAttribute('collapsed', 'false');
expect(getByTestId('object-control-0')).toHaveAttribute('collapsed', 'false');
expect(asFragment()).toMatchSnapshot();
});
it('should remove from list when remove button is clicked', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const { asFragment, getAllByText, rerender } = render(
<ListControl
{...props}
field={field}
value={fromJS([{ string: 'item 1' }, { string: 'item 2' }])}
/>,
);
expect(asFragment()).toMatchSnapshot();
let mock;
try {
mock = jest.spyOn(console, 'error').mockImplementation(() => undefined);
const items = getAllByText('Remove');
fireEvent.click(items[0]);
expect(props.onChange).toHaveBeenCalledTimes(1);
expect(props.onChange).toHaveBeenCalledWith(fromJS([{ string: 'item 2' }]), undefined);
rerender(<ListControl {...props} field={field} value={fromJS([{ string: 'item 2' }])} />);
expect(asFragment()).toMatchSnapshot();
} finally {
mock.mockRestore();
}
});
it('should give validation error if below min elements', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
required: true,
min: 2,
max: 3,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const listControl = new ListControl({
...props,
field,
value: fromJS([{ string: 'item 1' }]),
});
listControl.validate();
expect(props.onValidateObject).toHaveBeenCalledWith('forID', [
{
message: 'editor.editorControlPane.widget.rangeCount',
type: 'RANGE',
},
]);
});
it('should give min validation error if below min elements', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
required: true,
min: 2,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const listControl = new ListControl({
...props,
field,
value: fromJS([{ string: 'item 1' }]),
});
listControl.validate();
expect(props.onValidateObject).toHaveBeenCalledWith('forID', [
{
message: 'editor.editorControlPane.widget.rangeMin',
type: 'RANGE',
},
]);
});
it('should give validation error if above max elements', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
required: true,
min: 2,
max: 3,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const listControl = new ListControl({
...props,
field,
value: fromJS([
{ string: 'item 1' },
{ string: 'item 2' },
{ string: 'item 3' },
{ string: 'item 4' },
]),
});
listControl.validate();
expect(props.onValidateObject).toHaveBeenCalledWith('forID', [
{
message: 'editor.editorControlPane.widget.rangeCount',
type: 'RANGE',
},
]);
});
it('should give max validation error if above max elements', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
required: true,
max: 3,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const listControl = new ListControl({
...props,
field,
value: fromJS([
{ string: 'item 1' },
{ string: 'item 2' },
{ string: 'item 3' },
{ string: 'item 4' },
]),
});
listControl.validate();
expect(props.onValidateObject).toHaveBeenCalledWith('forID', [
{
message: 'editor.editorControlPane.widget.rangeMax',
type: 'RANGE',
},
]);
});
it('should give no validation error if between min and max elements', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
required: true,
min: 2,
max: 3,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const listControl = new ListControl({
...props,
field,
value: fromJS([{ string: 'item 1' }, { string: 'item 2' }, { string: 'item 3' }]),
});
listControl.validate();
expect(props.onValidateObject).toHaveBeenCalledWith('forID', []);
});
it('should give no validation error if no elements and optional', () => {
const field = fromJS({
name: 'list',
label: 'List',
collapsed: false,
minimize_collapsed: true,
required: false,
min: 2,
max: 3,
fields: [{ label: 'String', name: 'string', widget: 'string' }],
});
const listControl = new ListControl({
...props,
field,
value: fromJS([]),
});
listControl.validate();
expect(props.onValidateObject).toHaveBeenCalledWith('forID', []);
});
});

File diff suppressed because it is too large Load Diff

19
node_modules/decap-cms-widget-list/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
import DecapCmsWidgetObject from 'decap-cms-widget-object';
import controlComponent from './ListControl';
import schema from './schema';
const previewComponent = DecapCmsWidgetObject.previewComponent;
function Widget(opts = {}) {
return {
name: 'list',
controlComponent,
previewComponent,
schema,
...opts,
};
}
export const DecapCmsWidgetList = { Widget, controlComponent, previewComponent };
export default DecapCmsWidgetList;

12
node_modules/decap-cms-widget-list/src/schema.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export default {
properties: {
allow_add: { type: 'boolean' },
collapsed: { type: 'boolean' },
summary: { type: 'string' },
minimize_collapsed: { type: 'boolean' },
label_singular: { type: 'string' },
i18n: { type: 'boolean' },
min: { type: 'number' },
max: { type: 'number' },
},
};

View File

@@ -0,0 +1,35 @@
export const TYPES_KEY = 'types';
export const TYPE_KEY = 'typeKey';
export const DEFAULT_TYPE_KEY = 'type';
export function getTypedFieldForValue(field, value) {
const typeKey = resolveFieldKeyType(field);
const types = field.get(TYPES_KEY);
const valueType = value.get(typeKey);
return types.find(type => type.get('name') === valueType);
}
export function resolveFunctionForTypedField(field) {
const typeKey = resolveFieldKeyType(field);
const types = field.get(TYPES_KEY);
return value => {
const valueType = value.get(typeKey);
return types.find(type => type.get('name') === valueType);
};
}
export function resolveFieldKeyType(field) {
return field.get(TYPE_KEY, DEFAULT_TYPE_KEY);
}
export function getErrorMessageForTypedFieldAndValue(field, value) {
const keyType = resolveFieldKeyType(field);
const type = value.get(keyType);
let errorMessage;
if (!type) {
errorMessage = `Error: item has no '${keyType}' property`;
} else {
errorMessage = `Error: item has illegal '${keyType}' property: '${type}'`;
}
return errorMessage;
}

3
node_modules/decap-cms-widget-list/webpack.config.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
const { getConfig } = require('../../scripts/webpack.js');
module.exports = getConfig();