Compare commits

...

171 Commits
2.3.0 ... 2

Author SHA1 Message Date
github-actions 5f1d896cb7 Merge branch '2.11' into 2 2023-10-11 12:06:12 +00:00
Sabina Talipova 3f69c0d9f9
Merge pull request #262 from josephlewisnz/fix/trigger-has-changes
FIX Added the state changed checks and bubble up event
2023-10-09 15:42:28 +13:00
josephlewisnz 9257956255 fixed reference issue now that we've changed ref from createRef() 2023-10-05 16:02:59 +13:00
josephlewisnz f7af10a837 build 2023-10-05 16:02:59 +13:00
josephlewisnz c931056689 FIX Added the state changed checks and bubble up event - pending build 2023-10-05 16:02:59 +13:00
Guy Sartorelli f32c37cdd9
Merge branch '2.11' into 2 2023-04-27 13:43:39 +12:00
Guy Sartorelli 635d13c6bb
Merge branch '2.10' into 2.11 2023-04-26 13:48:56 +12:00
Guy Sartorelli 1b9f5a9388
Merge pull request #233 from w-huber/2
add performReadonlyTransformation
2023-04-26 13:47:27 +12:00
Guy Sartorelli 783ab4ef2b
Merge branch '2.11' into 2 2023-04-26 12:45:41 +12:00
Guy Sartorelli 158bc006a5
FIX Format value to string for readonly transformation
Co-authored-by: Werner Huber <w.huber@byro13.de>
2023-04-17 13:51:10 +12:00
Guy Sartorelli ef178ce534
MNT Revert erroneous dependency changes (#236) 2023-03-28 17:00:38 +13:00
Maxime Rainville d53ba1a9f8
Merge pull request #235 from creative-commoners/pulls/2/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:19:27 +13:00
Steve Boyd 9db4a2e55b MNT Use gha-dispatch-ci 2023-03-21 13:42:10 +13:00
Guy Sartorelli 9013377996
MNT Update development dependencies 2023-03-10 16:32:16 +13:00
Guy Sartorelli 41c412970d
MNT Update release dependencies 2023-03-10 16:32:13 +13:00
Guy Sartorelli d8be307ced
MNT Update development dependencies 2023-03-10 12:21:28 +13:00
Guy Sartorelli 42cf651cef
Merge pull request #225 from purplespider/2
Remove unnecessary react-select scss import that breaks other CMS styles
2023-02-23 09:29:23 +13:00
James Cocker 777e2e9e59
Remove unnecessary react-select scss import that breaks other CMS styles 2022-12-22 10:13:39 +00:00
Sabina Talipova b5e7981aac
Merge pull request #222 from creative-commoners/pulls/2/stop-using-depr
API Stop using deprecated API
2022-12-05 16:41:12 +13:00
dependabot[bot] d2a3a74e76
Merge pull request #223 from silverstripe/dependabot/npm_and_yarn/decode-uri-component-0.2.2 2022-12-04 08:27:25 +00:00
dependabot[bot] e7c618992c
Bump decode-uri-component from 0.2.0 to 0.2.2
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-03 15:33:32 +00:00
Steve Boyd dc932bd72d API Stop using deprecated API 2022-11-24 17:39:57 +13:00
Guy Sartorelli 8822b0f0b5
Merge pull request #221 from creative-commoners/pulls/2/depr-messages
API Update deprecations
2022-11-21 09:57:33 +13:00
Steve Boyd 442d9d8f32 API Update deprecations 2022-11-16 11:55:46 +13:00
Michal Kleiner 90f02b5602
Merge pull request #220 from thewebmen/fix/untrack-hidden-inputs 2022-11-08 10:24:44 +13:00
Rens 39759209d7 FIX Ignore hidden inputs for tracking unsaved changes 2022-11-07 15:07:20 +01:00
Steve Boyd 3d6a77fff1 Merge branch '2.9' into 2 2022-08-02 19:00:56 +12:00
Steve Boyd 8a4d2220af Merge branch '2.8' into 2.9 2022-08-02 19:00:53 +12:00
Guy Sartorelli ab52014523
Merge pull request #217 from creative-commoners/pulls/2.8/standardise-modules
MNT Standardise modules
2022-08-02 16:10:33 +12:00
Steve Boyd d45abda0dc MNT Standardise modules 2022-08-01 16:23:29 +12:00
Guy Sartorelli 86f2d63205
Merge pull request #216 from creative-commoners/pulls/2/update-js
MNT Use up­date-js action
2022-07-29 17:06:01 +12:00
Steve Boyd 1beccb2d96 MNT Use update-js action 2022-07-29 13:08:19 +12:00
Guy Sartorelli ab1e5b2ec0
Merge pull request #214 from bummzack/fix/has-one
fix: Deal with DataObjects or Int values
2022-07-29 10:35:37 +12:00
Roman Schmid 3f1d65e88e docs: Update readme with an example for `has_one` relations. 2022-07-28 10:48:33 +02:00
Roman Schmid 8da9f83cde fix: Fall back to the `ID` if no title is given 2022-07-26 10:47:13 +02:00
Roman Schmid 560a1398ab fix: Do not use `getField` to allow for field fallback values 2022-07-25 16:49:23 +02:00
Guy Sartorelli eed4afb710 Merge branch '2.9' into 2 2022-07-11 11:39:16 +12:00
Guy Sartorelli 0590385268 Merge branch '2.8' into 2.9 2022-07-11 11:39:04 +12:00
Guy Sartorelli 152ce93c51
Merge pull request #213 from creative-commoners/pulls/2.8/module-standards
MNT Use GitHub Actions CI
2022-07-05 16:32:31 +12:00
Steve Boyd 8d29af6158 MNT Use GitHub Actions CI 2022-07-05 16:10:46 +12:00
Roman Schmid e34d6e4898 fix: Use the title-field for has_one DataObject relations 2022-07-04 16:10:02 +02:00
Roman Schmid bc7bf5265b fix: Deal with DataObjects or Int values, so that the correct value is shown with has_one 2022-06-28 09:29:32 +02:00
Will Rossiter 527c5b2224
FIX Selected tag is not shown when isMutliple is false (#201)
* doc: add note about `setTitleField` (Fixes #153)

* NEW Add support for saving tag value into has_one components

FIX Value not shown if isMultiple is false (#195)

* fix: don't default to ID column as pgsql throws an error if comparing string value
2022-06-07 13:00:12 +12:00
Steve Boyd ee7c422855
Merge pull request #207 from silverstripe/pulls/2/update-js-1648772584
DEP Update JS dependencies
2022-05-31 11:01:54 +12:00
dependabot[bot] 8172abe158
Merge pull request #210 from silverstripe/dependabot/npm_and_yarn/async-2.6.4 2022-05-03 09:27:40 +00:00
dependabot[bot] 7a19b709f2
Bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-28 21:08:54 +00:00
Guy Sartorelli d486842a69
Merge pull request #209 from creative-commoners/pulls/2/php81
ENH PHP 8.1 compatibility
2022-04-26 17:57:37 +12:00
Steve Boyd 1b7dba124a ENH PHP 8.1 compatibility 2022-04-13 13:50:19 +12:00
Daniel Kliemsch 0a04e35825
ENH Make the write call of StringTagField::saveInto method configurable (#208)
* Make the immediate write call of StringTagField::saveInto method configurable
* Set immediate_write_enabled option in the class instead of config; add deprecation notice
* Add tests for the immediate write option
2022-04-04 20:27:49 +12:00
github-actions 1a4dfc5904 DEP Update JS dependencies 2022-04-01 00:23:04 +00:00
Maxime Rainville 8477911890
Merge pull request #205 from creative-commoners/pulls/2/update-js-deps
MNT Add update JS deps workflow
2022-03-23 14:17:06 +13:00
Steve Boyd a71f57d0fa MNT Add update JS deps workflow 2022-03-22 12:10:24 +13:00
dependabot[bot] 162f629db8
Merge pull request #204 from silverstripe/dependabot/npm_and_yarn/url-parse-1.5.10 2022-02-28 11:51:09 +00:00
dependabot[bot] 6de73bad08
Bump url-parse from 1.5.7 to 1.5.10
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.7 to 1.5.10.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.7...1.5.10)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-28 04:44:34 +00:00
dependabot[bot] a74e00568f
Merge pull request #203 from silverstripe/dependabot/npm_and_yarn/url-parse-1.5.7 2022-02-21 23:22:17 +00:00
Maxime Rainville e00d4f7c59
Merge pull request #202 from creative-commoners/pulls/2/php74
DEP Set PHP 7.4 as the minimum version
2022-02-18 22:09:31 +13:00
dependabot[bot] d7539bebb9
Bump url-parse from 1.5.3 to 1.5.7
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-18 06:19:36 +00:00
Steve Boyd 206740a9aa DEP Set PHP 7.4 as the minimum version 2022-02-10 17:39:53 +13:00
Will Rossiter de93200d7d
Merge pull request #199 from micschk/patch-1
Adding `$schemaComponent` to make react render correct component
2021-12-17 06:45:31 +13:00
Michael van Schaik 07868abfb5
Adding `$schemaComponent` to make react render correct component
Fixes #198
2021-11-24 15:41:10 +01:00
Maxime Rainville 187e66401e
Merge pull request #197 from creative-commoners/pulls/2/js
DEP Update JS dependencies
2021-11-24 17:21:14 +13:00
Steve Boyd adfe0f994e DEP Update JS dependencies 2021-11-22 16:51:14 +13:00
Steve Boyd cf9be969e6 Merge branch '2.7' into 2 2021-11-18 17:48:52 +13:00
Maxime Rainville aad83ac30d
Merge pull request #196 from creative-commoners/pulls/2/remove-js-prefix
MNT Remove js prefix from require statements
2021-11-16 10:49:57 +13:00
Steve Boyd d11abb5638 MNT Remove js prefix from require statements 2021-11-02 21:56:45 +13:00
Maxime Rainville 3f595cf09a
Merge pull request #194 from creative-commoners/pulls/2/sapphire-test-nine
API phpunit 9 support
2021-11-01 22:33:09 +13:00
Steve Boyd 5eae80abb3 API phpunit 9 support 2021-10-27 18:15:33 +13:00
Joseph Madden b81546d555 FIX Use existing source datalist if available before creating a new one (closes #171)
Fix for #171 - Issue when utilizing multiple of same parent class

Co-authored-by: Joe Madden <joe.madden@moosylvania.com>
2021-10-15 13:57:33 +13:00
Joseph Madden 55e03731bf
FIX Use existing source datalist if available before creating a new one
Fix for #171 - Issue when utilizing multiple of same parent class

Co-authored-by: Joe Madden <joe.madden@moosylvania.com>
2021-10-15 13:38:53 +13:00
Maxime Rainville 43ccb16def Merge branch '2.6' into 2 2021-08-26 17:00:02 +12:00
dependabot[bot] 5fe280f172
Merge pull request #189 from silverstripe/dependabot/npm_and_yarn/https-proxy-agent-2.2.4 2021-08-08 21:48:14 +00:00
dependabot[bot] 1792dcdfbd
Merge pull request #188 from silverstripe/dependabot/npm_and_yarn/ws-5.2.3 2021-08-08 21:44:44 +00:00
dependabot[bot] 72563dba40
Merge pull request #190 from silverstripe/dependabot/npm_and_yarn/ini-1.3.8 2021-08-08 15:57:37 +00:00
dependabot[bot] 28ab283760
Merge pull request #172 from silverstripe/dependabot/npm_and_yarn/npm-user-validate-1.0.1 2021-08-05 06:31:11 +00:00
dependabot[bot] 068df7e190
Bump ini from 1.3.5 to 1.3.8
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

---
updated-dependencies:
- dependency-name: ini
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-05 06:30:26 +00:00
dependabot[bot] f996bbab90
Bump ws from 5.2.2 to 5.2.3
Bumps [ws](https://github.com/websockets/ws) from 5.2.2 to 5.2.3.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/5.2.2...5.2.3)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-05 06:29:42 +00:00
dependabot[bot] 07dcc82580
Merge pull request #178 from silverstripe/dependabot/npm_and_yarn/elliptic-6.5.4 2021-08-05 06:29:29 +00:00
dependabot[bot] 96147c0216
Merge pull request #181 from silverstripe/dependabot/npm_and_yarn/handlebars-4.7.7 2021-08-05 06:28:04 +00:00
dependabot[bot] 605ab465c5
Merge pull request #180 from silverstripe/dependabot/npm_and_yarn/ua-parser-js-0.7.28 2021-08-05 06:28:01 +00:00
dependabot[bot] abf0823789
Merge pull request #182 from silverstripe/dependabot/npm_and_yarn/url-parse-1.5.1 2021-08-05 06:26:21 +00:00
dependabot[bot] aa7e3167ab
Merge pull request #183 from silverstripe/dependabot/npm_and_yarn/lodash-4.17.21 2021-08-05 06:26:12 +00:00
dependabot[bot] 1273c728e5
Bump https-proxy-agent from 2.2.2 to 2.2.4
Bumps [https-proxy-agent](https://github.com/TooTallNate/node-https-proxy-agent) from 2.2.2 to 2.2.4.
- [Release notes](https://github.com/TooTallNate/node-https-proxy-agent/releases)
- [Commits](https://github.com/TooTallNate/node-https-proxy-agent/compare/2.2.2...2.2.4)

---
updated-dependencies:
- dependency-name: https-proxy-agent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-05 06:24:26 +00:00
dependabot[bot] 8f192925de
Merge pull request #184 from silverstripe/dependabot/npm_and_yarn/hosted-git-info-2.8.9 2021-08-05 06:23:45 +00:00
Loz Calver 88c069c3ae Fix missing CSS class for StringTagField initialisation (closes #148) 2021-07-27 10:49:02 +12:00
Steve Boyd 511b1365e0
Merge pull request #187 from kinglozzer/148-part-2
Fix missing CSS class for StringTagField initialisation (closes #148)
2021-07-27 10:45:01 +12:00
Loz Calver debcd12b62 Fix missing CSS class for StringTagField initialisation (closes #148) 2021-07-26 09:49:05 +01:00
Steve Boyd 0089ce7ed1 Merge branch '2.6' into 2 2021-07-07 14:07:16 +12:00
Daniel Hensby 67985f550b
Merge pull request #185 from kinglozzer/148-string-tagfield
Fix StringTagField initialisation (fixes #148)
2021-06-21 15:48:58 +01:00
Loz Calver 325de9e94d Fix StringTagField initialisation (fixes #148) 2021-06-18 12:02:05 +01:00
Steve Boyd 4200a95b95 Merge branch '2.6' into 2 2021-05-21 14:28:25 +12:00
dependabot[bot] 5e0174a560
Merge pull request #179 from silverstripe/dependabot/npm_and_yarn/y18n-3.2.2 2021-05-14 10:18:55 +00:00
dependabot[bot] bb59a75622
Bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-10 15:44:09 +00:00
dependabot[bot] 7b289cca43
Bump lodash from 4.17.20 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-09 09:42:44 +00:00
dependabot[bot] f685681fb3
Bump url-parse from 1.4.7 to 1.5.1
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.1.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-08 07:06:17 +00:00
dependabot[bot] 476e657971
Bump handlebars from 4.5.3 to 4.7.7
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.5.3 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.5.3...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-07 16:44:15 +00:00
dependabot[bot] 281e237ead
Bump ua-parser-js from 0.7.20 to 0.7.28
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.20 to 0.7.28.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.20...0.7.28)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-07 01:56:17 +00:00
Maxime Rainville a677c63694 MNT Remove obsolete branch-alias 2021-05-05 11:17:40 +12:00
dependabot[bot] 6406f215c8
Bump y18n from 3.2.1 to 3.2.2
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-30 18:45:17 +00:00
dependabot[bot] b11b434cf5
Bump elliptic from 6.5.3 to 6.5.4
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-09 09:34:32 +00:00
Steve Boyd dfaeb71336 Merge branch '2.5' into 2 2021-01-27 14:34:00 +13:00
Steve Boyd 46c07c98d1
Merge pull request #177 from creative-commoners/pulls/2.5/node-10
MNT Update to Node 10
2021-01-27 14:32:17 +13:00
Garion Herman 605e52edbb MNT Update to Node 10 2021-01-27 11:47:38 +13:00
Steve Boyd 0a1a370d65
Update build status badge 2021-01-21 16:42:50 +13:00
Garion Herman 1f542c21e2
Merge pull request #170 from silverstripe/dependabot/npm_and_yarn/node-sass-4.14.1
Bump node-sass from 4.12.0 to 4.14.1
2020-11-17 10:43:14 +13:00
Steve Boyd eef72506e1 Merge branch '2.4' into 2 2020-11-11 17:45:54 +13:00
Serge Latyntsev e5ba6caf74
Merge pull request #174 from creative-commoners/pulls/2.4/shared-config
MNT Use shared travis config, use sminnee/phpunit
2020-11-11 11:41:16 +13:00
Steve Boyd c06430068c MNT Use shared travis config, use sminnee/phpunit 2020-11-10 15:37:53 +13:00
Maxime Rainville ccca22c6fa Merge branch '2.4' into 2 2020-10-22 13:41:47 +13:00
dependabot[bot] 3427bd4ef7
Bump npm-user-validate from 1.0.0 to 1.0.1
Bumps [npm-user-validate](https://github.com/npm/npm-user-validate) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/npm/npm-user-validate/releases)
- [Commits](https://github.com/npm/npm-user-validate/compare/v1.0.0...v1.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-16 19:22:29 +00:00
dependabot[bot] c7c37787c4
Bump node-sass from 4.12.0 to 4.14.1
Bumps [node-sass](https://github.com/sass/node-sass) from 4.12.0 to 4.14.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.12.0...v4.14.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-04 00:04:56 +00:00
dependabot[bot] 5c5a8f605a
Merge pull request #168 from silverstripe/dependabot/npm_and_yarn/elliptic-6.5.3 2020-09-04 00:02:41 +00:00
Sam Minnée c50f461a78
Merge pull request #165 from open-sausages/pulls/fix-list-values
FIX Preselect based on SS_List values
2020-08-12 13:32:50 +12:00
dependabot[bot] 2af34223aa
Bump elliptic from 6.5.0 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.0 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.0...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-30 20:43:55 +00:00
dependabot[bot] ea1b4c26ab
Merge pull request #167 from silverstripe/dependabot/npm_and_yarn/npm-6.14.7 2020-07-22 10:37:50 +00:00
dependabot[bot] 7a43f3af36
Bump npm from 6.13.4 to 6.14.7
Bumps [npm](https://github.com/npm/cli) from 6.13.4 to 6.14.7.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v6.13.4...v6.14.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-21 20:26:46 +00:00
dependabot[bot] d1c6d35bd1
Merge pull request #164 from silverstripe/dependabot/npm_and_yarn/npm-registry-fetch-4.0.5 2020-07-21 20:25:01 +00:00
dependabot[bot] 69f61a5f88
Merge pull request #166 from silverstripe/dependabot/npm_and_yarn/lodash-4.17.19 2020-07-21 20:24:55 +00:00
dependabot[bot] 4c7174d9b7
Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-17 11:06:37 +00:00
Ingo Schommer 70d80920d7 FIX Preselect based on SS_List values 2020-07-16 17:45:04 +12:00
Ingo Schommer 2c0c712b59 Remove cms dependance in unit tests 2020-07-16 09:35:15 +12:00
dependabot[bot] b1a278303c
Bump npm-registry-fetch from 4.0.2 to 4.0.5
Bumps [npm-registry-fetch](https://github.com/npm/registry-fetch) from 4.0.2 to 4.0.5.
- [Release notes](https://github.com/npm/registry-fetch/releases)
- [Changelog](https://github.com/npm/npm-registry-fetch/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/registry-fetch/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-07 21:36:31 +00:00
Guy Marriott 66aa1af72a
Merge pull request #162 from tractorcow/pulls/fix-react 2020-07-06 16:55:48 -07:00
Damian Mooyman 748ef1a8f0
Actually save the values 2020-07-06 14:36:17 +12:00
Damian Mooyman 2790d7504a
PHP 5 compatibility 🙄 2020-07-06 14:33:09 +12:00
Damian Mooyman 5decfa39e1
Merge remote-tracking branch 'origin/2.4' into pulls/fix-react 2020-07-06 14:18:12 +12:00
Damian Mooyman a61612ec4b
BUG Resolve issue with react select values not being saved
Fixes #150
2020-07-06 14:17:08 +12:00
dependabot[bot] 28812fcae1
Merge pull request #159 from silverstripe/dependabot/npm_and_yarn/websocket-extensions-0.1.4 2020-06-30 23:12:53 +00:00
Steve Boyd c6a387f5f5 Use travis node version 10 2020-06-18 14:27:54 +12:00
Steve Boyd 2830b221f1 Merge branch '2.4' into 2 2020-06-18 13:38:00 +12:00
Daniel Hensby 3f6af1ab17
Merge pull request #160 from sminnee/fix-readonly-titlefield
FIX: Fix readonly transformation of field with setTitleField()
2020-06-17 09:04:35 +01:00
Sam Minnee d11b9474ed FIX: Fix readonly transformation of field with setTitleField()
If the title field was customised, this metadata was lost during a
readonly transformation. This can cause fatal errors in read-only views
such as history browsing.
2020-06-17 16:40:57 +12:00
Maxime Rainville 9f11be6a04 Merge branch '2.4' into 2 2020-06-12 14:26:28 +12:00
dependabot[bot] ee7f71b753
Bump websocket-extensions from 0.1.3 to 0.1.4
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-07 02:05:58 +00:00
Guy Marriott 3b6ec92e66
Merge pull request #158 from christopherdarling/patch-1 2020-05-06 12:28:42 -07:00
Christopher Darling c538a231b7
FIX $record->hasMethod() should be called before getting the relation 2020-05-06 19:45:41 +01:00
Robbie Averill 1e453c0e0f
Merge pull request #157 from silverstripe/dependabot/npm_and_yarn/jquery-3.5.0
Bump jquery from 3.4.1 to 3.5.0
2020-05-04 16:53:22 -07:00
dependabot[bot] 9e66d61fa4
Bump jquery from 3.4.1 to 3.5.0
Bumps [jquery](https://github.com/jquery/jquery) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/jquery/jquery/releases)
- [Commits](https://github.com/jquery/jquery/compare/3.4.1...3.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-30 07:14:51 +00:00
Garion Herman eaaf467d28 Merge branch '2.4' into 2 2020-02-13 16:18:01 +13:00
Garion Herman 01e7f457a6 Merge branch '2.3' into 2.4 2020-02-13 15:42:44 +13:00
Garion Herman 2620218fe4 Merge branch '2.2' into 2.3 2020-02-13 15:28:42 +13:00
Garion Herman b7a2b3b1d7 Merge branch '2.1' into 2.2 2020-02-13 15:18:32 +13:00
Guy Marriott 09df859091
Merge pull request #155 from silverstripe/dependabot/npm_and_yarn/handlebars-4.5.3
Bump handlebars from 4.1.2 to 4.5.3
2020-01-02 11:36:26 -08:00
dependabot[bot] 54cbb3e14a
Bump handlebars from 4.1.2 to 4.5.3
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.2 to 4.5.3.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-28 17:45:15 +00:00
Robbie Averill fd66043d1f
Merge pull request #154 from silverstripe/dependabot/npm_and_yarn/npm-6.13.4
Bump npm from 6.10.2 to 6.13.4
2019-12-13 13:44:10 -08:00
dependabot[bot] 410c35d9a2
Bump npm from 6.10.2 to 6.13.4
Bumps [npm](https://github.com/npm/cli) from 6.10.2 to 6.13.4.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v6.10.2...v6.13.4)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-13 19:12:49 +00:00
Robbie Averill f67cd979f2 Merge branch '2.4' 2019-08-15 09:57:07 +12:00
Robbie Averill ec0d65904d Remove obsolete branch alias 2019-08-15 09:56:39 +12:00
Robbie Averill 58edb12fa3
Merge pull request #120 from wilr/fixes/107-tagfield-in-admin
Setup TagField to work within AssetAdmin (Fixes #107)
2019-07-25 09:03:18 +02:00
Robbie Averill 804c6bac2f
Run automated phpcs linting 2019-07-25 12:27:29 +12:00
Guy Marriott bd84139b96
Code clean-up 2019-07-25 12:23:04 +12:00
Guy Marriott 61d3d04ed6
FIX Removing potentially breaking lower-case change (and fix tests) 2019-07-25 12:13:52 +12:00
Guy Marriott a1c57f664d
FIX redux-form can now control tag fields
This also fixes an issue where tags would be created and tags could not have the same name as the ID of an existing tag
And the react-select module is updated to the (current) latest 1.x version
And the tests are now working again after updating jest
2019-07-25 12:13:52 +12:00
Guy Marriott e97a82f0b7
FIX Ensure tagfield is compatible with both React and Entwine contexts 2019-07-25 12:13:50 +12:00
Will Rossiter 068e4c6a26
Setup TagField to work within AssetAdmin (Fixes #107) 2019-07-25 12:13:44 +12:00
Ingo Schommer bc7404005b
Add screenshot to readme 2019-07-04 09:11:50 +12:00
Ingo Schommer 461d467e3f
Added screenshot 2019-07-04 09:11:11 +12:00
Ingo Schommer ec8ca5bf2a
DOCS Note about CMS usage
Fixes https://github.com/silverstripe/silverstripe-tagfield/issues/145
2019-07-04 09:09:48 +12:00
Robbie Averill b5ca874591
Merge pull request #142 from SilverStripers/fix/source-list-queries
fixed the issue with filters being taken off from rendering values
2019-04-29 15:55:58 +12:00
Nivanka Fonseka 6276ad2aee php cs fixes 2019-04-29 06:37:31 +05:30
Nivanka Fonseka ead0fbac6b fixed the issue with filters being taken off from rendering values 2019-04-17 11:19:42 +05:30
Robbie Averill 5bf4b2959a Merge branch '2.3' 2019-04-15 15:50:16 +12:00
Robbie Averill 968a4f585e Merge branch '2.0' into 2.1 2019-02-19 11:15:39 +07:00
Guy Marriott dac02faa66
Merge pull request #137 from creative-commoners/pulls/2.0/scalars
FIX StringTagField now works with SS-2018-021/CVE-2019-5715 by serialising arrays before write
2019-02-19 17:14:23 +13:00
Robbie Averill 3ff72be24c FIX StringTagField now works with SS-2018-021/CVE-2019-5715 by serialising arrays before write 2019-02-19 11:09:02 +07:00
Robbie Averill 4da004f3e2
Merge pull request #117 from littlegiant/fix-lazy-tags
Fix lazy tags
2018-09-17 23:25:59 +02:00
Damian Mooyman 77bad9b943
Use getShouldLazyLoad() accesser 2018-09-18 09:12:44 +12:00
Damian Mooyman ce8ba85182
Revert default argument change 2018-09-17 18:24:03 +12:00
Damian Mooyman 690f0cc793
BUG Fix for $source left null
BUG Fix missing imports
Semver compatible setSource()
2018-09-17 18:18:40 +12:00
Simon Gow 024e648e5a Added getSource() function to populate source inline with the api.
Source isn't populated to the parent because it's transformed directly
to an array. Instead we're lazy loading the source when it's requested.
2018-09-13 11:18:55 +12:00
Simon Gow fa754f3fd3
TagField lazy load shouldnt render Options
TagField should only render options if the lazy load value isn't set. Options from here come from subsequent POSTs, and rendering the options for every request significantly slows down both the form generation and render.
2018-09-07 17:28:05 +12:00
Simon Gow 3797049a31
Resolve Performance issues with TagField
TagField hydrates the entire result set because a parent class calls `toArray()` on the $source. 
This fix changes the source to an empty array, so no such manipulation can be made.
2018-09-06 18:16:06 +12:00
32 changed files with 7506 additions and 2571 deletions

View File

@ -13,6 +13,7 @@ trim_trailing_whitespace = true
[{*.yml,package.json,*.js,*.scss}]
indent_size = 2
indent_style = space
quote_type = single
# The indent size used in the package.json file cannot be changed:
# https://github.com/npm/npm/pull/3180#issuecomment-16336516

11
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
name: CI
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Dispatch CI
on:
# At 12:00 PM UTC, only on Saturday and Sunday
schedule:
- cron: '0 12 * * 6,0'
jobs:
dispatch-ci:
name: Dispatch CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Dispatch CI
uses: silverstripe/gha-dispatch-ci@v1

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
schedule:
- cron: '50 10 4 * *'
jobs:
keepalive:
name: Keepalive
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Keepalive
uses: silverstripe/gha-keepalive@v1

17
.github/workflows/update-js.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Update JS
on:
workflow_dispatch:
# Run on a schedule of once per quarter
schedule:
- cron: '0 0 1 */3 *'
jobs:
update-js:
name: Update JS
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Update JS
uses: silverstripe/gha-update-js@v1

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
10

View File

@ -1,12 +0,0 @@
checks:
php: true
build:
nodes:
analysis:
tests:
override: [php-scrutinizer-run]
filter:
paths: ["src/*", "tests/*"]

4
.ss-storybook.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
src: 'client/src',
fileMatcher: /(\/bundle\.scss|[A-Za-z]-story\.jsx?)$/,
};

View File

@ -1,43 +0,0 @@
language: php
dist: trusty
env:
global:
- COMPOSER_ROOT_VERSION=2.0.x-dev
- TRAVIS_NODE_VERSION="6"
matrix:
include:
- php: 5.6
env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
- php: 7.0
env: DB=PGSQL PHPUNIT_TEST=1
- php: 7.1
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
- php: 7.1
env: DB=MYSQL NPM_TEST=1
before_script:
- phpenv rehash
- phpenv config-rm xdebug.ini
- composer validate
- composer require --no-update silverstripe/recipe-cms:4.3.x-dev silverstripe/admin:1.3.x-dev
- if [[ $DB == PGSQL ]]; then composer require --no-update silverstripe/postgresql:2.1.x-dev; fi
- composer install --prefer-source --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
# Install NPM dependencies
- if [[ $NPM_TEST ]]; then nvm install $TRAVIS_NODE_VERSION && nvm use $TRAVIS_NODE_VERSION && npm install -g yarn && yarn install --network-concurrency 1 && (cd vendor/silverstripe/admin && yarn install --network-concurrency 1) && yarn run build; fi
script:
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs src/ tests/ *.php; fi
- if [[ $NPM_TEST ]]; then git diff-files --quiet -w --relative=client; fi
- if [[ $NPM_TEST ]]; then git diff --name-status --relative=client; fi
- if [[ $NPM_TEST ]]; then yarn run test; fi
- if [[ $NPM_TEST ]]; then yarn run lint; fi
after_success:
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi

166
README.md Normal file
View File

@ -0,0 +1,166 @@
# Tag Field
[![CI](https://github.com/silverstripe/silverstripe-tagfield/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-tagfield/actions/workflows/ci.yml)
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
Custom tag input field, for SilverStripe.
## Overview
![Screenshot](docs/en/screenshot.png)
Allows storing tags as a relationship, or comma-delimited strings.
Supports autocompletion with lazy-loading.
Note: The field is optimised for usage in the Silverstripe CMS UI.
The form field class itself can be used outside of the CMS,
but you'll need to build your own frontend to interpret the raw field data (`data-schema` attribute).
## Requirements
* Silverstripe 4.0
## Installing
```sh
$ composer require silverstripe/tagfield
```
## Using
### Relational Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $many_many = [
'BlogTags' => BlogTag::class
];
}
```
```php
use SilverStripe\ORM\DataObject;
class BlogTag extends DataObject
{
private static $db = [
'Title' => 'Varchar(200)',
];
private static $belongs_many_many = [
'BlogPosts' => BlogPost::class
];
}
```
```php
$field = TagField::create(
'BlogTags',
'Blog Tags',
BlogTag::get(),
$this->BlogTags()
)
->setShouldLazyLoad(true) // tags should be lazy loaded
->setCanCreate(true); // new tag DataObjects can be created
```
**Note:** This assumes you have imported the namespaces class, e.g. use
SilverStripe\TagField\TagField;
#### Has-One Relations
You can also use the TagField to select values for `has_one` relations.
Let's assume, that a `BlogPost` *has one* `BlogCategory`.
```php
class BlogCategory extends DataObject
{
private static $db = [
'Title' => 'Varchar(200)',
];
}
```
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $has_one = [
'BlogCategory' => BlogCategory::class
];
}
```
```php
$field = TagField::create(
'BlogCategoryID',
$this->fieldLabel('BlogCategory'),
BlogCategory::get()
)
->setIsMultiple(false)
->setCanCreate(true);
```
**Note:** We're using the `ID` suffix for the field-name (eg. `BlogCategoryID` instead of `BlogCategory`) and
only allow one value by setting `->setIsMultiple(false)`
### String Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $db = [
'Tags' => 'Text',
];
}
```
```php
$field = StringTagField::create(
'Tags',
'Tags',
['one', 'two'],
explode(',', $this->Tags)
);
$field->setShouldLazyLoad(true); // tags should be lazy loaded
```
You can find more in-depth documentation in [docs/en](docs/en/introduction.md).
## Using TagField with silverstripe-taxonomy
TagField assumes a `Title` field on objects. For classes without a `Title` field
use `setTitleField` to modify accordingly.
```php
$field = TagField::create(
'Tags',
'Blog Tags',
TaxonomyTerm::get(),
)
->setTitleField('Name');
```
## Versioning
This library follows [Semver](http://semver.org). According to Semver, you will
be able to upgrade to any minor or patch version of this library without any
breaking changes to the public API. Semver also requires that we clearly define
the public API for this library.
All methods, with `public` visibility, are part of the public API. All other
methods are not part of the public API. Where possible, we'll try to keep
`protected` methods backwards-compatible in minor/patch versions, but if you're
overriding methods then please test your work before upgrading.
## Reporting Issues
Please [create an
issue](http://github.com/silverstripe/silverstripe-tagfield/issues) for any bugs
you've found, or features you're missing.

5
_config/admin.yml Normal file
View File

@ -0,0 +1,5 @@
SilverStripe\Admin\LeftAndMain:
extra_requirements_css:
- 'silverstripe/tagfield:client/dist/styles/bundle.css'
extra_requirements_javascript:
- 'silverstripe/tagfield:client/dist/js/bundle.js'

View File

@ -1 +1 @@
!function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(t,e,n){"use strict";var o=n("./client/src/boot/registerComponents.js"),r=function(t){return t&&t.__esModule?t:{default:t}}(o);window.document.addEventListener("DOMContentLoaded",function(){(0,r.default)()})},"./client/src/boot/registerComponents.js":function(t,e,n){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var r=n(0),s=o(r),i=n("./client/src/components/TagField.js"),a=o(i);e.default=function(){s.default.component.registerMany({TagField:a.default})}},"./client/src/bundles/bundle.js":function(t,e,n){"use strict";n("./client/src/legacy/entwine/TagField.js"),n("./client/src/boot/index.js")},"./client/src/components/TagField.js":function(t,e,n){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}function r(t,e){var n={};for(var o in t)e.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(t,o)&&(n[o]=t[o]);return n}function s(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function u(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var h=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t},l=function(){function t(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(e,n,o){return n&&t(e.prototype,n),o&&t(e,o),e}}(),c=n(1),f=o(c),p=n(4),d=o(p),m=n(2),y=o(m),v=n("./node_modules/url/url.js"),b=o(v),g=n("./node_modules/debounce-promise/dist/index.js"),j=o(g),O=n("./node_modules/prop-types/index.js"),x=o(O),w=function(t){function e(t){i(this,e);var n=a(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t));return n.state={value:t.value},n.onChange=n.onChange.bind(n),n.getOptions=n.getOptions.bind(n),n.fetchOptions=(0,j.default)(n.fetchOptions,500),n}return u(e,t),l(e,[{key:"onChange",value:function(t){this.setState({value:t}),"function"==typeof this.props.onChange&&this.props.onChange(t)}},{key:"getOptions",value:function(t){var e=this.props,n=e.lazyLoad,o=e.options;return n?t?this.fetchOptions(t):Promise.resolve({options:[]}):Promise.resolve({options:o})}},{key:"fetchOptions",value:function(t){var e=this.props,n=e.optionUrl,o=e.labelKey,r=e.valueKey,i=b.default.parse(n,!0);return i.query.term=t,(0,y.default)(b.default.format(i),{credentials:"same-origin"}).then(function(t){return t.json()}).then(function(t){return{options:t.items.map(function(t){var e;return e={},s(e,o,t.id),s(e,r,t.text),e})}})}},{key:"render",value:function(){var t=this.props,e=t.lazyLoad,n=t.options,o=t.creatable,s=r(t,["lazyLoad","options","creatable"]),i=e?{loadOptions:this.getOptions}:{options:n},a=d.default;return e&&o?a=d.default.AsyncCreatable:e?a=d.default.Async:o&&(a=d.default.Creatable),s.value=this.state.value,f.default.createElement(a,h({},s,{onChange:this.onChange,inputProps:{className:"no-change-track"}},i))}}]),e}(c.Component);w.propTypes={name:x.default.string.isRequired,labelKey:x.default.string.isRequired,valueKey:x.default.string.isRequired,lazyLoad:x.default.bool.isRequired,creatable:x.default.bool.isRequired,multi:x.default.bool.isRequired,disabled:x.default.bool,options:x.default.arrayOf(x.default.object),optionUrl:x.default.string,value:x.default.any,onChange:x.default.func,onBlur:x.default.func},w.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1},e.default=w},"./client/src/legacy/entwine/TagField.js":function(t,e,n){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}var r=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t},s=n(1),i=o(s),a=n(3),u=o(a),h=n(0);window.jQuery.entwine("ss",function(t){t(".js-injector-boot .ss-tag-field").entwine({onmatch:function(){var t=this,e=this.closest(".cms-content").attr("id"),n=e?{context:e}:{},o=(0,h.loadComponent)("TagField",n),s=r({},this.data("schema"),{onBlur:function(){t.parents(".cms-edit-form:first").trigger("change")}});u.default.render(i.default.createElement(o,s),this[0])},onunmatch:function(){u.default.unmountComponentAtNode(this[0])}})})},"./node_modules/debounce-promise/dist/index.js":function(t,e,n){"use strict";function o(t){return"function"==typeof t?t():t}function r(){var t={};return t.promise=new Promise(function(e,n){t.resolve=e,t.reject=n}),t}var s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};t.exports=function(t){function e(){var e=u;clearTimeout(h),Promise.resolve(i.accumulate?t.call(this,l):t.apply(this,l[l.length-1])).then(e.resolve,e.reject),l=[],u=null}var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=void 0,u=void 0,h=void 0,l=[];return function(){var c=o(n),f=(new Date).getTime(),p=!a||f-a>c;a=f;for(var d=arguments.length,m=Array(d),y=0;y<d;y++)m[y]=arguments[y];if(p&&i.leading)return i.accumulate?Promise.resolve(t.call(this,[m])).then(function(t){return t[0]}):Promise.resolve(t.call.apply(t,[this].concat(m)));if(u?clearTimeout(h):u=r(),l.push(m),h=setTimeout(e.bind(this),c),i.accumulate){var v=function(){var t=l.length-1;return{v:u.promise.then(function(e){return e[t]})}}();if("object"===(void 0===v?"undefined":s(v)))return v.v}return u.promise}}},"./node_modules/prop-types/factoryWithThrowingShims.js":function(t,e,n){"use strict";function o(){}function r(){}var s=n("./node_modules/prop-types/lib/ReactPropTypesSecret.js");r.resetWarningCache=o,t.exports=function(){function t(t,e,n,o,r,i){if(i!==s){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function e(){return t}t.isRequired=t;var n={array:t,bool:t,func:t,number:t,object:t,string:t,symbol:t,any:t,arrayOf:e,element:t,elementType:t,instanceOf:e,node:t,objectOf:e,oneOf:e,oneOfType:e,shape:e,exact:e,checkPropTypes:r,resetWarningCache:o};return n.PropTypes=n,n}},"./node_modules/prop-types/index.js":function(t,e,n){t.exports=n("./node_modules/prop-types/factoryWithThrowingShims.js")()},"./node_modules/prop-types/lib/ReactPropTypesSecret.js":function(t,e,n){"use strict";t.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},"./node_modules/punycode/punycode.js":function(t,e,n){(function(t,o){var r;!function(o){function s(t){throw new RangeError(R[t])}function i(t,e){for(var n=t.length,o=[];n--;)o[n]=e(t[n]);return o}function a(t,e){var n=t.split("@"),o="";return n.length>1&&(o=n[0]+"@",t=n[1]),t=t.replace(A,"."),o+i(t.split("."),e).join(".")}function u(t){for(var e,n,o=[],r=0,s=t.length;r<s;)e=t.charCodeAt(r++),e>=55296&&e<=56319&&r<s?(n=t.charCodeAt(r++),56320==(64512&n)?o.push(((1023&e)<<10)+(1023&n)+65536):(o.push(e),r--)):o.push(e);return o}function h(t){return i(t,function(t){var e="";return t>65535&&(t-=65536,e+=k(t>>>10&1023|55296),t=56320|1023&t),e+=k(t)}).join("")}function l(t){return t-48<10?t-22:t-65<26?t-65:t-97<26?t-97:g}function c(t,e){return t+22+75*(t<26)-((0!=e)<<5)}function f(t,e,n){var o=0;for(t=n?S(t/w):t>>1,t+=S(t/e);t>I*O>>1;o+=g)t=S(t/I);return S(o+(I+1)*t/(t+x))}function p(t){var e,n,o,r,i,a,u,c,p,d,m=[],y=t.length,v=0,x=C,w=_;for(n=t.lastIndexOf(P),n<0&&(n=0),o=0;o<n;++o)t.charCodeAt(o)>=128&&s("not-basic"),m.push(t.charCodeAt(o));for(r=n>0?n+1:0;r<y;){for(i=v,a=1,u=g;r>=y&&s("invalid-input"),c=l(t.charCodeAt(r++)),(c>=g||c>S((b-v)/a))&&s("overflow"),v+=c*a,p=u<=w?j:u>=w+O?O:u-w,!(c<p);u+=g)d=g-p,a>S(b/d)&&s("overflow"),a*=d;e=m.length+1,w=f(v-i,e,0==i),S(v/e)>b-x&&s("overflow"),x+=S(v/e),v%=e,m.splice(v++,0,x)}return h(m)}function d(t){var e,n,o,r,i,a,h,l,p,d,m,y,v,x,w,q=[];for(t=u(t),y=t.length,e=C,n=0,i=_,a=0;a<y;++a)(m=t[a])<128&&q.push(k(m));for(o=r=q.length,r&&q.push(P);o<y;){for(h=b,a=0;a<y;++a)(m=t[a])>=e&&m<h&&(h=m);for(v=o+1,h-e>S((b-n)/v)&&s("overflow"),n+=(h-e)*v,e=h,a=0;a<y;++a)if(m=t[a],m<e&&++n>b&&s("overflow"),m==e){for(l=n,p=g;d=p<=i?j:p>=i+O?O:p-i,!(l<d);p+=g)w=l-d,x=g-d,q.push(k(c(d+w%x,0))),l=S(w/x);q.push(k(c(l,0))),i=f(n,v,o==r),n=0,++o}++n,++e}return q.join("")}function m(t){return a(t,function(t){return q.test(t)?p(t.slice(4).toLowerCase()):t})}function y(t){return a(t,function(t){return T.test(t)?"xn--"+d(t):t})}var v,b=("object"==typeof e&&e&&e.nodeType,"object"==typeof t&&t&&t.nodeType,2147483647),g=36,j=1,O=26,x=38,w=700,_=72,C=128,P="-",q=/^xn--/,T=/[^\x20-\x7E]/,A=/[\x2E\u3002\uFF0E\uFF61]/g,R={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},I=g-j,S=Math.floor,k=String.fromCharCode;v={version:"1.4.1",ucs2:{decode:u,encode:h},decode:p,encode:d,toASCII:y,toUnicode:m},void 0!==(r=function(){return v}.call(e,n,e,t))&&(t.exports=r)}()}).call(e,n("./node_modules/webpack/buildin/module.js")(t),n("./node_modules/webpack/buildin/global.js"))},"./node_modules/querystring-es3/decode.js":function(t,e,n){"use strict";function o(t,e){return Object.prototype.hasOwnProperty.call(t,e)}t.exports=function(t,e,n,s){e=e||"&",n=n||"=";var i={};if("string"!=typeof t||0===t.length)return i;var a=/\+/g;t=t.split(e);var u=1e3;s&&"number"==typeof s.maxKeys&&(u=s.maxKeys);var h=t.length;u>0&&h>u&&(h=u);for(var l=0;l<h;++l){var c,f,p,d,m=t[l].replace(a,"%20"),y=m.indexOf(n);y>=0?(c=m.substr(0,y),f=m.substr(y+1)):(c=m,f=""),p=decodeURIComponent(c),d=decodeURIComponent(f),o(i,p)?r(i[p])?i[p].push(d):i[p]=[i[p],d]:i[p]=d}return i};var r=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)}},"./node_modules/querystring-es3/encode.js":function(t,e,n){"use strict";function o(t,e){if(t.map)return t.map(e);for(var n=[],o=0;o<t.length;o++)n.push(e(t[o],o));return n}var r=function(t){switch(typeof t){case"string":return t;case"boolean":return t?"true":"false";case"number":return isFinite(t)?t:"";default:return""}};t.exports=function(t,e,n,a){return e=e||"&",n=n||"=",null===t&&(t=void 0),"object"==typeof t?o(i(t),function(i){var a=encodeURIComponent(r(i))+n;return s(t[i])?o(t[i],function(t){return a+encodeURIComponent(r(t))}).join(e):a+encodeURIComponent(r(t[i]))}).join(e):a?encodeURIComponent(r(a))+n+encodeURIComponent(r(t)):""};var s=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},i=Object.keys||function(t){var e=[];for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&e.push(n);return e}},"./node_modules/querystring-es3/index.js":function(t,e,n){"use strict";e.decode=e.parse=n("./node_modules/querystring-es3/decode.js"),e.encode=e.stringify=n("./node_modules/querystring-es3/encode.js")},"./node_modules/url/url.js":function(t,e,n){"use strict";function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function r(t,e,n){if(t&&h.isObject(t)&&t instanceof o)return t;var r=new o;return r.parse(t,e,n),r}function s(t){return h.isString(t)&&(t=r(t)),t instanceof o?t.format():o.prototype.format.call(t)}function i(t,e){return r(t,!1,!0).resolve(e)}function a(t,e){return t?r(t,!1,!0).resolveObject(e):e}var u=n("./node_modules/punycode/punycode.js"),h=n("./node_modules/url/util.js");e.parse=r,e.resolve=i,e.resolveObject=a,e.format=s,e.Url=o;var l=/^([a-z0-9.+-]+:)/i,c=/:[0-9]*$/,f=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,p=["<",">",'"',"`"," ","\r","\n","\t"],d=["{","}","|","\\","^","`"].concat(p),m=["'"].concat(d),y=["%","/","?",";","#"].concat(m),v=["/","?","#"],b=/^[+a-z0-9A-Z_-]{0,63}$/,g=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,j={javascript:!0,"javascript:":!0},O={javascript:!0,"javascript:":!0},x={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},w=n("./node_modules/querystring-es3/index.js");o.prototype.parse=function(t,e,n){if(!h.isString(t))throw new TypeError("Parameter 'url' must be a string, not "+typeof t);var o=t.indexOf("?"),r=-1!==o&&o<t.indexOf("#")?"?":"#",s=t.split(r),i=/\\/g;s[0]=s[0].replace(i,"/"),t=s.join(r);var a=t;if(a=a.trim(),!n&&1===t.split("#").length){var c=f.exec(a);if(c)return this.path=a,this.href=a,this.pathname=c[1],c[2]?(this.search=c[2],this.query=e?w.parse(this.search.substr(1)):this.search.substr(1)):e&&(this.search="",this.query={}),this}var p=l.exec(a);if(p){p=p[0];var d=p.toLowerCase();this.protocol=d,a=a.substr(p.length)}if(n||p||a.match(/^\/\/[^@\/]+@[^@\/]+/)){var _="//"===a.substr(0,2);!_||p&&O[p]||(a=a.substr(2),this.slashes=!0)}if(!O[p]&&(_||p&&!x[p])){for(var C=-1,P=0;P<v.length;P++){var q=a.indexOf(v[P]);-1!==q&&(-1===C||q<C)&&(C=q)}var T,A;A=-1===C?a.lastIndexOf("@"):a.lastIndexOf("@",C),-1!==A&&(T=a.slice(0,A),a=a.slice(A+1),this.auth=decodeURIComponent(T)),C=-1;for(var P=0;P<y.length;P++){var q=a.indexOf(y[P]);-1!==q&&(-1===C||q<C)&&(C=q)}-1===C&&(C=a.length),this.host=a.slice(0,C),a=a.slice(C),this.parseHost(),this.hostname=this.hostname||"";var R="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!R)for(var I=this.hostname.split(/\./),P=0,S=I.length;P<S;P++){var k=I[P];if(k&&!k.match(b)){for(var U="",E=0,F=k.length;E<F;E++)k.charCodeAt(E)>127?U+="x":U+=k[E];if(!U.match(b)){var L=I.slice(0,P),M=I.slice(P+1),N=k.match(g);N&&(L.push(N[1]),M.unshift(N[2])),M.length&&(a="/"+M.join(".")+a),this.hostname=L.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),R||(this.hostname=u.toASCII(this.hostname));var K=this.port?":"+this.port:"",z=this.hostname||"";this.host=z+K,this.href+=this.host,R&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!j[d])for(var P=0,S=m.length;P<S;P++){var D=m[P];if(-1!==a.indexOf(D)){var W=encodeURIComponent(D);W===D&&(W=escape(D)),a=a.split(D).join(W)}}var $=a.indexOf("#");-1!==$&&(this.hash=a.substr($),a=a.slice(0,$));var B=a.indexOf("?");if(-1!==B?(this.search=a.substr(B),this.query=a.substr(B+1),e&&(this.query=w.parse(this.query)),a=a.slice(0,B)):e&&(this.search="",this.query={}),a&&(this.pathname=a),x[d]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var K=this.pathname||"",H=this.search||"";this.path=K+H}return this.href=this.format(),this},o.prototype.format=function(){var t=this.auth||"";t&&(t=encodeURIComponent(t),t=t.replace(/%3A/i,":"),t+="@");var e=this.protocol||"",n=this.pathname||"",o=this.hash||"",r=!1,s="";this.host?r=t+this.host:this.hostname&&(r=t+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(r+=":"+this.port)),this.query&&h.isObject(this.query)&&Object.keys(this.query).length&&(s=w.stringify(this.query));var i=this.search||s&&"?"+s||"";return e&&":"!==e.substr(-1)&&(e+=":"),this.slashes||(!e||x[e])&&!1!==r?(r="//"+(r||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):r||(r=""),o&&"#"!==o.charAt(0)&&(o="#"+o),i&&"?"!==i.charAt(0)&&(i="?"+i),n=n.replace(/[?#]/g,function(t){return encodeURIComponent(t)}),i=i.replace("#","%23"),e+r+n+i+o},o.prototype.resolve=function(t){return this.resolveObject(r(t,!1,!0)).format()},o.prototype.resolveObject=function(t){if(h.isString(t)){var e=new o;e.parse(t,!1,!0),t=e}for(var n=new o,r=Object.keys(this),s=0;s<r.length;s++){var i=r[s];n[i]=this[i]}if(n.hash=t.hash,""===t.href)return n.href=n.format(),n;if(t.slashes&&!t.protocol){for(var a=Object.keys(t),u=0;u<a.length;u++){var l=a[u];"protocol"!==l&&(n[l]=t[l])}return x[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n}if(t.protocol&&t.protocol!==n.protocol){if(!x[t.protocol]){for(var c=Object.keys(t),f=0;f<c.length;f++){var p=c[f];n[p]=t[p]}return n.href=n.format(),n}if(n.protocol=t.protocol,t.host||O[t.protocol])n.pathname=t.pathname;else{for(var d=(t.pathname||"").split("/");d.length&&!(t.host=d.shift()););t.host||(t.host=""),t.hostname||(t.hostname=""),""!==d[0]&&d.unshift(""),d.length<2&&d.unshift(""),n.pathname=d.join("/")}if(n.search=t.search,n.query=t.query,n.host=t.host||"",n.auth=t.auth,n.hostname=t.hostname||t.host,n.port=t.port,n.pathname||n.search){var m=n.pathname||"",y=n.search||"";n.path=m+y}return n.slashes=n.slashes||t.slashes,n.href=n.format(),n}var v=n.pathname&&"/"===n.pathname.charAt(0),b=t.host||t.pathname&&"/"===t.pathname.charAt(0),g=b||v||n.host&&t.pathname,j=g,w=n.pathname&&n.pathname.split("/")||[],d=t.pathname&&t.pathname.split("/")||[],_=n.protocol&&!x[n.protocol];if(_&&(n.hostname="",n.port=null,n.host&&(""===w[0]?w[0]=n.host:w.unshift(n.host)),n.host="",t.protocol&&(t.hostname=null,t.port=null,t.host&&(""===d[0]?d[0]=t.host:d.unshift(t.host)),t.host=null),g=g&&(""===d[0]||""===w[0])),b)n.host=t.host||""===t.host?t.host:n.host,n.hostname=t.hostname||""===t.hostname?t.hostname:n.hostname,n.search=t.search,n.query=t.query,w=d;else if(d.length)w||(w=[]),w.pop(),w=w.concat(d),n.search=t.search,n.query=t.query;else if(!h.isNullOrUndefined(t.search)){if(_){n.hostname=n.host=w.shift();var C=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");C&&(n.auth=C.shift(),n.host=n.hostname=C.shift())}return n.search=t.search,n.query=t.query,h.isNull(n.pathname)&&h.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!w.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var P=w.slice(-1)[0],q=(n.host||t.host||w.length>1)&&("."===P||".."===P)||""===P,T=0,A=w.length;A>=0;A--)P=w[A],"."===P?w.splice(A,1):".."===P?(w.splice(A,1),T++):T&&(w.splice(A,1),T--);if(!g&&!j)for(;T--;T)w.unshift("..");!g||""===w[0]||w[0]&&"/"===w[0].charAt(0)||w.unshift(""),q&&"/"!==w.join("/").substr(-1)&&w.push("");var R=""===w[0]||w[0]&&"/"===w[0].charAt(0);if(_){n.hostname=n.host=R?"":w.length?w.shift():"";var C=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");C&&(n.auth=C.shift(),n.host=n.hostname=C.shift())}return g=g||n.host&&w.length,g&&!R&&w.unshift(""),w.length?n.pathname=w.join("/"):(n.pathname=null,n.path=null),h.isNull(n.pathname)&&h.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=t.auth||n.auth,n.slashes=n.slashes||t.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var t=this.host,e=c.exec(t);e&&(e=e[0],":"!==e&&(this.port=e.substr(1)),t=t.substr(0,t.length-e.length)),t&&(this.hostname=t)}},"./node_modules/url/util.js":function(t,e,n){"use strict";t.exports={isString:function(t){return"string"==typeof t},isObject:function(t){return"object"==typeof t&&null!==t},isNull:function(t){return null===t},isNullOrUndefined:function(t){return null==t}}},"./node_modules/webpack/buildin/global.js":function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(n=window)}t.exports=n},"./node_modules/webpack/buildin/module.js":function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},0:function(t,e){t.exports=Injector},1:function(t,e){t.exports=React},2:function(t,e){t.exports=IsomorphicFetch},3:function(t,e){t.exports=ReactDom},4:function(t,e){t.exports=ReactSelect}});
!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(e,t,n){"use strict";var o=n("./client/src/boot/registerComponents.js"),r=function(e){return e&&e.__esModule?e:{default:e}}(o);window.document.addEventListener("DOMContentLoaded",function(){(0,r.default)()})},"./client/src/boot/registerComponents.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),s=o(r),i=n("./client/src/components/TagField.js"),a=o(i);t.default=function(){s.default.component.registerMany({TagField:a.default})}},"./client/src/bundles/bundle.js":function(e,t,n){"use strict";n("./client/src/legacy/entwine/TagField.js"),n("./client/src/boot/index.js")},"./client/src/components/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n={};for(var o in e)t.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(n[o]=e[o]);return n}function s(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.Component=void 0;var l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},c=function(){function e(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,n,o){return n&&e(t.prototype,n),o&&e(t,o),t}}(),f=n(1),p=o(f),d=n(5),m=o(d),v=n(3),y=o(v),b=n(2),g=o(b),j=n("./node_modules/url/url.js"),O=o(j),w=n("./node_modules/debounce-promise/dist/index.js"),C=o(w),x=n("./node_modules/prop-types/index.js"),_=o(x),S=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.selectComponentRef=null,n.setSelectComponentRef=function(e){n.selectComponentRef=e},n.state={initalState:e.value?e.value:[],hasChanges:!1},n.isControlled()||(n.state=h({},n.state,{value:e.value})),n.handleChange=n.handleChange.bind(n),n.handleOnBlur=n.handleOnBlur.bind(n),n.getOptions=n.getOptions.bind(n),n.fetchOptions=(0,C.default)(n.fetchOptions,500),n}return u(t,e),c(t,[{key:"componentDidUpdate",value:function(e,t){if(t.hasChanges!==this.state.hasChanges){var n=this.selectComponentRef.select.wrapper,o=new Event("change",{bubbles:!0});n.dispatchEvent(o)}}},{key:"getOptions",value:function(e){var t=this.props,n=t.lazyLoad,o=t.options;return n?e?this.fetchOptions(e):Promise.resolve({options:[]}):Promise.resolve({options:o})}},{key:"handleChange",value:function(e){if(this.setState({hasChanges:!1}),JSON.stringify(this.state.initalState)!==JSON.stringify(e)&&this.setState({hasChanges:!0}),this.isControlled())return void this.props.onChange(e);this.setState({value:e})}},{key:"isControlled",value:function(){return"function"==typeof this.props.onChange}},{key:"handleOnBlur",value:function(){}},{key:"fetchOptions",value:function(e){var t=this.props,n=t.optionUrl,o=t.labelKey,r=t.valueKey,i=O.default.parse(n,!0);return i.query.term=e,(0,y.default)(O.default.format(i),{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return{options:e.items.map(function(e){var t;return t={},s(t,o,e.Title),s(t,r,e.Value),s(t,"Selected",e.Selected),t})}})}},{key:"render",value:function(){var e=this.props,t=e.lazyLoad,n=e.options,o=e.creatable,s=r(e,["lazyLoad","options","creatable"]),i=t?{loadOptions:this.getOptions}:{options:n},a=m.default;if(t&&o?a=m.default.AsyncCreatable:t?a=m.default.Async:o&&(a=m.default.Creatable),this.isControlled()||(s.value=this.state.value),!s.multi&&s.value&&Object.keys(s.value).length>0){var u=s.value[Object.keys(s.value)[0]];"object"===(void 0===u?"undefined":l(u))&&(s.value=u)}var c=this.state.hasChanges?"":"no-change-track";return p.default.createElement(a,h({},s,{onChange:this.handleChange,onBlur:this.handleOnBlur},i,{className:c,ref:this.setSelectComponentRef}))}}]),t}(f.Component);S.propTypes={name:_.default.string.isRequired,labelKey:_.default.string.isRequired,valueKey:_.default.string.isRequired,lazyLoad:_.default.bool,creatable:_.default.bool,multi:_.default.bool,disabled:_.default.bool,options:_.default.arrayOf(_.default.object),optionUrl:_.default.string,value:_.default.any,onChange:_.default.func,onBlur:_.default.func},S.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1,lazyLoad:!1,creatable:!1,multi:!1},t.Component=S,t.default=(0,g.default)(S)},"./client/src/legacy/entwine/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s=n(1),i=o(s),a=n(4),u=o(a),l=n(0);window.jQuery.entwine("ss",function(e){e(".js-injector-boot .ss-tag-field.entwine").entwine({onmatch:function(){var e=this,t=this.closest(".cms-content").attr("id"),n=t?{context:t}:{},o=(0,l.loadComponent)("TagField",n),s=r({},this.data("schema"),{onBlur:function(){e.parents(".cms-edit-form:first").trigger("change")}});u.default.render(i.default.createElement(o,r({noHolder:!0},s)),this[0])},onunmatch:function(){u.default.unmountComponentAtNode(this[0])}}),e(".cms-edit-form").entwine({getChangeTrackerOptions:function(){var t=void 0===this.entwineData("ChangeTrackerOptions"),n=this._super();return t&&(n=e.extend({},n),n.ignoreFieldSelector+=", .ss-tag-field .no-change-track :input",this.setChangeTrackerOptions(n)),n}})})},"./node_modules/debounce-promise/dist/index.js":function(e,t,n){"use strict";function o(e){return"function"==typeof e?e():e}function r(){var e={};return e.promise=new Promise(function(t,n){e.resolve=t,e.reject=n}),e}e.exports=function(e){function t(){var t=a;clearTimeout(u),Promise.resolve(s.accumulate?e.call(this,l):e.apply(this,l[l.length-1])).then(t.resolve,t.reject),l=[],a=null}var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=void 0,a=void 0,u=void 0,l=[];return function(){var h=o(n),c=(new Date).getTime(),f=!i||c-i>h;i=c;for(var p=arguments.length,d=Array(p),m=0;m<p;m++)d[m]=arguments[m];if(f&&s.leading)return s.accumulate?Promise.resolve(e.call(this,[d])).then(function(e){return e[0]}):Promise.resolve(e.call.apply(e,[this].concat(d)));if(a?clearTimeout(u):a=r(),l.push(d),u=setTimeout(t.bind(this),h),s.accumulate){var v=l.length-1;return a.promise.then(function(e){return e[v]})}return a.promise}}},"./node_modules/node-libs-browser/node_modules/punycode/punycode.js":function(e,t,n){(function(e,o){var r;!function(o){function s(e){throw new RangeError(R[e])}function i(e,t){for(var n=e.length,o=[];n--;)o[n]=t(e[n]);return o}function a(e,t){var n=e.split("@"),o="";return n.length>1&&(o=n[0]+"@",e=n[1]),e=e.replace(k,"."),o+i(e.split("."),t).join(".")}function u(e){for(var t,n,o=[],r=0,s=e.length;r<s;)t=e.charCodeAt(r++),t>=55296&&t<=56319&&r<s?(n=e.charCodeAt(r++),56320==(64512&n)?o.push(((1023&t)<<10)+(1023&n)+65536):(o.push(t),r--)):o.push(t);return o}function l(e){return i(e,function(e){var t="";return e>65535&&(e-=65536,t+=I(e>>>10&1023|55296),e=56320|1023&e),t+=I(e)}).join("")}function h(e){return e-48<10?e-22:e-65<26?e-65:e-97<26?e-97:g}function c(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function f(e,t,n){var o=0;for(e=n?A(e/C):e>>1,e+=A(e/t);e>q*O>>1;o+=g)e=A(e/q);return A(o+(q+1)*e/(e+w))}function p(e){var t,n,o,r,i,a,u,c,p,d,m=[],v=e.length,y=0,w=_,C=x;for(n=e.lastIndexOf(S),n<0&&(n=0),o=0;o<n;++o)e.charCodeAt(o)>=128&&s("not-basic"),m.push(e.charCodeAt(o));for(r=n>0?n+1:0;r<v;){for(i=y,a=1,u=g;r>=v&&s("invalid-input"),c=h(e.charCodeAt(r++)),(c>=g||c>A((b-y)/a))&&s("overflow"),y+=c*a,p=u<=C?j:u>=C+O?O:u-C,!(c<p);u+=g)d=g-p,a>A(b/d)&&s("overflow"),a*=d;t=m.length+1,C=f(y-i,t,0==i),A(y/t)>b-w&&s("overflow"),w+=A(y/t),y%=t,m.splice(y++,0,w)}return l(m)}function d(e){var t,n,o,r,i,a,l,h,p,d,m,v,y,w,C,T=[];for(e=u(e),v=e.length,t=_,n=0,i=x,a=0;a<v;++a)(m=e[a])<128&&T.push(I(m));for(o=r=T.length,r&&T.push(S);o<v;){for(l=b,a=0;a<v;++a)(m=e[a])>=t&&m<l&&(l=m);for(y=o+1,l-t>A((b-n)/y)&&s("overflow"),n+=(l-t)*y,t=l,a=0;a<v;++a)if(m=e[a],m<t&&++n>b&&s("overflow"),m==t){for(h=n,p=g;d=p<=i?j:p>=i+O?O:p-i,!(h<d);p+=g)C=h-d,w=g-d,T.push(I(c(d+C%w,0))),h=A(C/w);T.push(I(c(h,0))),i=f(n,y,o==r),n=0,++o}++n,++t}return T.join("")}function m(e){return a(e,function(e){return T.test(e)?p(e.slice(4).toLowerCase()):e})}function v(e){return a(e,function(e){return P.test(e)?"xn--"+d(e):e})}var y,b=("object"==typeof t&&t&&t.nodeType,"object"==typeof e&&e&&e.nodeType,2147483647),g=36,j=1,O=26,w=38,C=700,x=72,_=128,S="-",T=/^xn--/,P=/[^\x20-\x7E]/,k=/[\x2E\u3002\uFF0E\uFF61]/g,R={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},q=g-j,A=Math.floor,I=String.fromCharCode;y={version:"1.4.1",ucs2:{decode:u,encode:l},decode:p,encode:d,toASCII:v,toUnicode:m},void 0!==(r=function(){return y}.call(t,n,t,e))&&(e.exports=r)}()}).call(t,n("./node_modules/webpack/buildin/module.js")(e),n("./node_modules/webpack/buildin/global.js"))},"./node_modules/prop-types/factoryWithThrowingShims.js":function(e,t,n){"use strict";function o(){}function r(){}var s=n("./node_modules/prop-types/lib/ReactPropTypesSecret.js");r.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,r,i){if(i!==s){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:o};return n.PropTypes=n,n}},"./node_modules/prop-types/index.js":function(e,t,n){e.exports=n("./node_modules/prop-types/factoryWithThrowingShims.js")()},"./node_modules/prop-types/lib/ReactPropTypesSecret.js":function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},"./node_modules/querystring-es3/decode.js":function(e,t,n){"use strict";function o(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,s){t=t||"&",n=n||"=";var i={};if("string"!=typeof e||0===e.length)return i;var a=/\+/g;e=e.split(t);var u=1e3;s&&"number"==typeof s.maxKeys&&(u=s.maxKeys);var l=e.length;u>0&&l>u&&(l=u);for(var h=0;h<l;++h){var c,f,p,d,m=e[h].replace(a,"%20"),v=m.indexOf(n);v>=0?(c=m.substr(0,v),f=m.substr(v+1)):(c=m,f=""),p=decodeURIComponent(c),d=decodeURIComponent(f),o(i,p)?r(i[p])?i[p].push(d):i[p]=[i[p],d]:i[p]=d}return i};var r=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},"./node_modules/querystring-es3/encode.js":function(e,t,n){"use strict";function o(e,t){if(e.map)return e.map(t);for(var n=[],o=0;o<e.length;o++)n.push(t(e[o],o));return n}var r=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,a){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?o(i(e),function(i){var a=encodeURIComponent(r(i))+n;return s(e[i])?o(e[i],function(e){return a+encodeURIComponent(r(e))}).join(t):a+encodeURIComponent(r(e[i]))}).join(t):a?encodeURIComponent(r(a))+n+encodeURIComponent(r(e)):""};var s=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},"./node_modules/querystring-es3/index.js":function(e,t,n){"use strict";t.decode=t.parse=n("./node_modules/querystring-es3/decode.js"),t.encode=t.stringify=n("./node_modules/querystring-es3/encode.js")},"./node_modules/url/url.js":function(e,t,n){"use strict";function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function r(e,t,n){if(e&&l.isObject(e)&&e instanceof o)return e;var r=new o;return r.parse(e,t,n),r}function s(e){return l.isString(e)&&(e=r(e)),e instanceof o?e.format():o.prototype.format.call(e)}function i(e,t){return r(e,!1,!0).resolve(t)}function a(e,t){return e?r(e,!1,!0).resolveObject(t):t}var u=n("./node_modules/node-libs-browser/node_modules/punycode/punycode.js"),l=n("./node_modules/url/util.js");t.parse=r,t.resolve=i,t.resolveObject=a,t.format=s,t.Url=o;var h=/^([a-z0-9.+-]+:)/i,c=/:[0-9]*$/,f=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,p=["<",">",'"',"`"," ","\r","\n","\t"],d=["{","}","|","\\","^","`"].concat(p),m=["'"].concat(d),v=["%","/","?",";","#"].concat(m),y=["/","?","#"],b=/^[+a-z0-9A-Z_-]{0,63}$/,g=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,j={javascript:!0,"javascript:":!0},O={javascript:!0,"javascript:":!0},w={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},C=n("./node_modules/querystring-es3/index.js");o.prototype.parse=function(e,t,n){if(!l.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),r=-1!==o&&o<e.indexOf("#")?"?":"#",s=e.split(r),i=/\\/g;s[0]=s[0].replace(i,"/"),e=s.join(r);var a=e;if(a=a.trim(),!n&&1===e.split("#").length){var c=f.exec(a);if(c)return this.path=a,this.href=a,this.pathname=c[1],c[2]?(this.search=c[2],this.query=t?C.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var p=h.exec(a);if(p){p=p[0];var d=p.toLowerCase();this.protocol=d,a=a.substr(p.length)}if(n||p||a.match(/^\/\/[^@\/]+@[^@\/]+/)){var x="//"===a.substr(0,2);!x||p&&O[p]||(a=a.substr(2),this.slashes=!0)}if(!O[p]&&(x||p&&!w[p])){for(var _=-1,S=0;S<y.length;S++){var T=a.indexOf(y[S]);-1!==T&&(-1===_||T<_)&&(_=T)}var P,k;k=-1===_?a.lastIndexOf("@"):a.lastIndexOf("@",_),-1!==k&&(P=a.slice(0,k),a=a.slice(k+1),this.auth=decodeURIComponent(P)),_=-1;for(var S=0;S<v.length;S++){var T=a.indexOf(v[S]);-1!==T&&(-1===_||T<_)&&(_=T)}-1===_&&(_=a.length),this.host=a.slice(0,_),a=a.slice(_),this.parseHost(),this.hostname=this.hostname||"";var R="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!R)for(var q=this.hostname.split(/\./),S=0,A=q.length;S<A;S++){var I=q[S];if(I&&!I.match(b)){for(var U="",E=0,F=I.length;E<F;E++)I.charCodeAt(E)>127?U+="x":U+=I[E];if(!U.match(b)){var L=q.slice(0,S),N=q.slice(S+1),M=I.match(g);M&&(L.push(M[1]),N.unshift(M[2])),N.length&&(a="/"+N.join(".")+a),this.hostname=L.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),R||(this.hostname=u.toASCII(this.hostname));var z=this.port?":"+this.port:"",B=this.hostname||"";this.host=B+z,this.href+=this.host,R&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!j[d])for(var S=0,A=m.length;S<A;S++){var K=m[S];if(-1!==a.indexOf(K)){var D=encodeURIComponent(K);D===K&&(D=escape(K)),a=a.split(K).join(D)}}var H=a.indexOf("#");-1!==H&&(this.hash=a.substr(H),a=a.slice(0,H));var W=a.indexOf("?");if(-1!==W?(this.search=a.substr(W),this.query=a.substr(W+1),t&&(this.query=C.parse(this.query)),a=a.slice(0,W)):t&&(this.search="",this.query={}),a&&(this.pathname=a),w[d]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var z=this.pathname||"",$=this.search||"";this.path=z+$}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",o=this.hash||"",r=!1,s="";this.host?r=e+this.host:this.hostname&&(r=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(r+=":"+this.port)),this.query&&l.isObject(this.query)&&Object.keys(this.query).length&&(s=C.stringify(this.query));var i=this.search||s&&"?"+s||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||w[t])&&!1!==r?(r="//"+(r||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):r||(r=""),o&&"#"!==o.charAt(0)&&(o="#"+o),i&&"?"!==i.charAt(0)&&(i="?"+i),n=n.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),i=i.replace("#","%23"),t+r+n+i+o},o.prototype.resolve=function(e){return this.resolveObject(r(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if(l.isString(e)){var t=new o;t.parse(e,!1,!0),e=t}for(var n=new o,r=Object.keys(this),s=0;s<r.length;s++){var i=r[s];n[i]=this[i]}if(n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol){for(var a=Object.keys(e),u=0;u<a.length;u++){var h=a[u];"protocol"!==h&&(n[h]=e[h])}return w[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n}if(e.protocol&&e.protocol!==n.protocol){if(!w[e.protocol]){for(var c=Object.keys(e),f=0;f<c.length;f++){var p=c[f];n[p]=e[p]}return n.href=n.format(),n}if(n.protocol=e.protocol,e.host||O[e.protocol])n.pathname=e.pathname;else{for(var d=(e.pathname||"").split("/");d.length&&!(e.host=d.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==d[0]&&d.unshift(""),d.length<2&&d.unshift(""),n.pathname=d.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var m=n.pathname||"",v=n.search||"";n.path=m+v}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var y=n.pathname&&"/"===n.pathname.charAt(0),b=e.host||e.pathname&&"/"===e.pathname.charAt(0),g=b||y||n.host&&e.pathname,j=g,C=n.pathname&&n.pathname.split("/")||[],d=e.pathname&&e.pathname.split("/")||[],x=n.protocol&&!w[n.protocol];if(x&&(n.hostname="",n.port=null,n.host&&(""===C[0]?C[0]=n.host:C.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===d[0]?d[0]=e.host:d.unshift(e.host)),e.host=null),g=g&&(""===d[0]||""===C[0])),b)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,C=d;else if(d.length)C||(C=[]),C.pop(),C=C.concat(d),n.search=e.search,n.query=e.query;else if(!l.isNullOrUndefined(e.search)){if(x){n.hostname=n.host=C.shift();var _=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");_&&(n.auth=_.shift(),n.host=n.hostname=_.shift())}return n.search=e.search,n.query=e.query,l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!C.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var S=C.slice(-1)[0],T=(n.host||e.host||C.length>1)&&("."===S||".."===S)||""===S,P=0,k=C.length;k>=0;k--)S=C[k],"."===S?C.splice(k,1):".."===S?(C.splice(k,1),P++):P&&(C.splice(k,1),P--);if(!g&&!j)for(;P--;P)C.unshift("..");!g||""===C[0]||C[0]&&"/"===C[0].charAt(0)||C.unshift(""),T&&"/"!==C.join("/").substr(-1)&&C.push("");var R=""===C[0]||C[0]&&"/"===C[0].charAt(0);if(x){n.hostname=n.host=R?"":C.length?C.shift():"";var _=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");_&&(n.auth=_.shift(),n.host=n.hostname=_.shift())}return g=g||n.host&&C.length,g&&!R&&C.unshift(""),C.length?n.pathname=C.join("/"):(n.pathname=null,n.path=null),l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var e=this.host,t=c.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},"./node_modules/url/util.js":function(e,t,n){"use strict";e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},"./node_modules/webpack/buildin/global.js":function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},"./node_modules/webpack/buildin/module.js":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},0:function(e,t){e.exports=Injector},1:function(e,t){e.exports=React},2:function(e,t){e.exports=FieldHolder},3:function(e,t){e.exports=IsomorphicFetch},4:function(e,t){e.exports=ReactDom},5:function(e,t){e.exports=ReactSelect}});

View File

@ -1 +1 @@
.Select{position:relative}.Select input::-webkit-contacts-auto-fill-button,.Select input::-webkit-credentials-auto-fill-button{display:none!important}.Select input::-ms-clear,.Select input::-ms-reveal{display:none!important}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none;opacity:.35}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#b3b3b3 #ccc #d9d9d9}.Select.is-open>.Select-control .Select-arrow{top:-2px;border-color:transparent transparent #999;border-width:0 5px 5px}.Select.is-searchable.is-focused:not(.is-open)>.Select-control,.Select.is-searchable.is-open>.Select-control{cursor:text}.Select.is-focused>.Select-control{background:#fff}.Select.is-focused:not(.is-open)>.Select-control{border-color:#007eff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 0 3px rgba(0,126,255,.1);background:#fff}.Select.has-value.is-clearable.Select--single>.Select-control .Select-value{padding-right:42px}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label,.Select.has-value.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover{color:#007eff;outline:none;text-decoration:underline}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus{background:#fff}.Select.has-value.is-pseudo-focused .Select-input{opacity:0}.Select.is-open .Select-arrow,.Select .Select-arrow-zone:hover>.Select-arrow{border-top-color:#666}.Select.Select--rtl{direction:rtl;text-align:right}.Select-control{background-color:#fff;border-color:#d9d9d9 #ccc #b3b3b3;border-radius:4px;border:1px solid #ccc;color:#333;cursor:default;display:table;border-spacing:0;border-collapse:separate;height:36px;outline:none;overflow:hidden;position:relative;width:100%}.Select-control:hover{box-shadow:0 1px 0 rgba(0,0,0,.06)}.Select-control .Select-input:focus{outline:none;background:#fff}.Select--single>.Select-control .Select-value,.Select-placeholder{bottom:0;color:#aaa;left:0;line-height:34px;padding-left:10px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none transparent;border:0 none;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:none;line-height:17px;padding:8px 0 12px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.has-value.is-pseudo-focused .Select-input{opacity:0}.Select-control:not(.is-searchable)>.Select-input{outline:none}.Select-loading-zone{cursor:pointer;display:table-cell;text-align:center}.Select-loading,.Select-loading-zone{position:relative;vertical-align:middle;width:16px}.Select-loading{-webkit-animation:Select-animation-spin .4s infinite linear;-o-animation:Select-animation-spin .4s infinite linear;animation:Select-animation-spin .4s infinite linear;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn .2s;-o-animation:Select-animation-fadeIn .2s;animation:Select-animation-fadeIn .2s;color:#999;cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:17px}.Select-clear-zone:hover{color:#d0021b}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select-arrow-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:25px;padding-right:5px}.Select--rtl .Select-arrow-zone{padding-right:0;padding-left:5px}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0;position:relative}.Select-control>:last-child{padding-right:5px}.Select--multi .Select-multi-value-wrapper{display:inline-block}.Select .Select-aria-only{position:absolute;display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0,0,0,0);overflow:hidden;float:left}@-webkit-keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;box-shadow:0 1px 0 rgba(0,0,0,.06);box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;left:0;top:100%;width:100%;z-index:1;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option{box-sizing:border-box;background-color:#fff;color:#666;cursor:pointer;display:block;padding:8px 10px}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;background-color:rgba(0,126,255,.04);color:#333}.Select-option.is-focused{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{box-sizing:border-box;color:#999;cursor:default;display:block;padding:8px 10px}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.Select--rtl .Select-input{margin-left:0;margin-right:10px}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);border-radius:2px;border:1px solid #c2e0ff;border:1px solid rgba(0,126,255,.24);color:#007eff;display:inline-block;font-size:.9em;line-height:1.4;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#007eff;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c2e0ff;border-right:1px solid rgba(0,126,255,.24);padding:1px 5px 3px}.Select--multi .Select-value-icon:focus,.Select--multi .Select-value-icon:hover{background-color:#d8eafd;background-color:rgba(0,113,230,.08);color:#0071e6}.Select--multi .Select-value-icon:active{background-color:#c2e0ff;background-color:rgba(0,126,255,.24)}.Select--multi.Select--rtl .Select-value{margin-left:0;margin-right:5px}.Select--multi.Select--rtl .Select-value-icon{border-right:none;border-left:1px solid #c2e0ff;border-left:1px solid rgba(0,126,255,.24)}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:active,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:hover{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}.ss-tag-field .Select--multi .Select-value{margin-top:3px}
.ss-tag-field .Select--multi .Select-value{margin-top:3px}

View File

@ -1,2 +1,2 @@
require('legacy/entwine/TagField.js');
require('legacy/entwine/TagField');
require('boot');

View File

@ -1,34 +1,54 @@
import React, { Component } from 'react';
import Select from 'react-select';
import fetch from 'isomorphic-fetch';
import fieldHolder from 'components/FieldHolder/FieldHolder';
import url from 'url';
import debounce from 'debounce-promise';
import PropTypes from 'prop-types';
class TagField extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value,
this.selectComponentRef = null;
this.setSelectComponentRef = ref => {
this.selectComponentRef = ref;
};
this.onChange = this.onChange.bind(this);
this.state = {
initalState: props.value ? props.value : [],
hasChanges: false,
};
if (!this.isControlled()) {
this.state = {
...this.state,
value: props.value,
};
}
this.handleChange = this.handleChange.bind(this);
this.handleOnBlur = this.handleOnBlur.bind(this);
this.getOptions = this.getOptions.bind(this);
this.fetchOptions = debounce(this.fetchOptions, 500);
}
onChange(value) {
this.setState({
value
});
if (typeof this.props.onChange === 'function') {
this.props.onChange(value);
componentDidUpdate(previousProps, previousState) {
if (previousState.hasChanges !== this.state.hasChanges) {
const element = this.selectComponentRef.select.wrapper;
const event = new Event('change', { bubbles: true });
element.dispatchEvent(event);
}
}
/**
* Get the options that should be shown to the user for this tagfield, optionally filtering by the
* given string input
*
* @param {string} input
* @return {Promise<Array<Object>>|Promise<{options: Array<Object>}>}
*/
getOptions(input) {
const { lazyLoad, options } = this.props;
@ -43,6 +63,57 @@ class TagField extends Component {
return this.fetchOptions(input);
}
/**
* Handle a change, either calling the change handler provided (if controlled) or updating
* internal state of this component
*
* @param {string} value
*/
handleChange(value) {
this.setState({
hasChanges: false
});
if (JSON.stringify(this.state.initalState) !== JSON.stringify(value)) {
this.setState({
hasChanges: true
});
}
if (this.isControlled()) {
this.props.onChange(value);
return;
}
this.setState({
value,
});
}
/**
* Determine if this input should be "controlled" or not. Controlled inputs should rely on their
* value coming from props and a change handler provided to update the state stored elsewhere.
* This is specifically the case for use with `redux-form`.
*
* @return {boolean}
*/
isControlled() {
return typeof this.props.onChange === 'function';
}
/**
* Required to prevent TagField being cleared on blur
*
* @link https://github.com/JedWatson/react-select/issues/805
*/
handleOnBlur() {}
/**
* Initiate a request to fetch options, optionally using the given string as a filter.
*
* @param {string} input
* @return {Promise<{options: Array<Object>}>}
*/
fetchOptions(input) {
const { optionUrl, labelKey, valueKey } = this.props;
const fetchURL = url.parse(optionUrl, true);
@ -51,20 +122,17 @@ class TagField extends Component {
return fetch(url.format(fetchURL), { credentials: 'same-origin' })
.then((response) => response.json())
.then((json) => ({
options: json.items.map(item => ({
[labelKey]: item.id,
[valueKey]: item.text,
}))
options: json.items.map((item) => ({
[labelKey]: item.Title,
[valueKey]: item.Value,
Selected: item.Selected,
})),
}));
}
render() {
const {
lazyLoad,
options,
creatable,
...passThroughAttributes
} = this.props;
const { lazyLoad, options, creatable, ...passThroughAttributes } =
this.props;
const optionAttributes = lazyLoad
? { loadOptions: this.getOptions }
@ -79,14 +147,36 @@ class TagField extends Component {
SelectComponent = Select.Creatable;
}
passThroughAttributes.value = this.state.value;
// Update the value to passthrough with the kept state provided this component is not
// "controlled"
if (!this.isControlled()) {
passThroughAttributes.value = this.state.value;
}
// if this is a single select then we just need the first value
if (!passThroughAttributes.multi && passThroughAttributes.value) {
if (Object.keys(passThroughAttributes.value).length > 0) {
const value =
passThroughAttributes.value[
Object.keys(passThroughAttributes.value)[0]
];
if (typeof value === 'object') {
passThroughAttributes.value = value;
}
}
}
const changedClassName = this.state.hasChanges ? '' : 'no-change-track';
return (
<SelectComponent
{...passThroughAttributes}
onChange={this.onChange}
inputProps={{ className: 'no-change-track' }}
onChange={this.handleChange}
onBlur={this.handleOnBlur}
{...optionAttributes}
className={changedClassName}
ref={this.setSelectComponentRef}
/>
);
}
@ -96,9 +186,9 @@ TagField.propTypes = {
name: PropTypes.string.isRequired,
labelKey: PropTypes.string.isRequired,
valueKey: PropTypes.string.isRequired,
lazyLoad: PropTypes.bool.isRequired,
creatable: PropTypes.bool.isRequired,
multi: PropTypes.bool.isRequired,
lazyLoad: PropTypes.bool,
creatable: PropTypes.bool,
multi: PropTypes.bool,
disabled: PropTypes.bool,
options: PropTypes.arrayOf(PropTypes.object),
optionUrl: PropTypes.string,
@ -110,7 +200,12 @@ TagField.propTypes = {
TagField.defaultProps = {
labelKey: 'Title',
valueKey: 'Value',
disabled: false
disabled: false,
lazyLoad: false,
creatable: false,
multi: false,
};
export default TagField;
export { TagField as Component };
export default fieldHolder(TagField);

View File

@ -1,5 +1,3 @@
@import "~react-select/dist/react-select.css";
.ss-tag-field .Select--multi .Select-value {
margin-top: 3px;
}

View File

@ -0,0 +1,36 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { Component as TagField } from '../TagField';
storiesOf('TagField/TagField', module)
.addDecorator(storyFn => (
<div style={{ width: '250px' }} className="ss-tag-field">
{storyFn()}
</div>
))
.add('Simple Example', () => (
<TagField
name="test"
options={[
{ Title: 'One', Value: 1 },
{ Title: 'Two', Value: 2 },
{ Title: 'Three', Value: 3 },
{ Title: 'Four', Value: 4 },
{ Title: 'Five', Value: 5 },
]}
/>
))
.add('Multiple Selection', () => (
<TagField
name="test"
multi
options={[
{ Title: 'One', Value: 1 },
{ Title: 'Two', Value: 2 },
{ Title: 'Three', Value: 3 },
{ Title: 'Four', Value: 4 },
{ Title: 'Five', Value: 5 },
]}
/>
))
;

View File

@ -6,7 +6,7 @@ jest.mock('isomorphic-fetch');
import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-15.4';
import TagField from '../TagField';
import { Component as TagField } from '../TagField';
import Select from 'react-select';
import fetch from 'isomorphic-fetch';

View File

@ -4,7 +4,7 @@ import ReactDOM from 'react-dom';
import { loadComponent } from 'lib/Injector';
window.jQuery.entwine('ss', ($) => {
$('.js-injector-boot .ss-tag-field').entwine({
$('.js-injector-boot .ss-tag-field.entwine').entwine({
onmatch() {
const cmsContent = this.closest('.cms-content').attr('id');
const context = (cmsContent)
@ -20,6 +20,7 @@ window.jQuery.entwine('ss', ($) => {
ReactDOM.render(
<TagField
noHolder
{...dataSchema}
/>,
this[0]
@ -30,4 +31,26 @@ window.jQuery.entwine('ss', ($) => {
ReactDOM.unmountComponentAtNode(this[0]);
}
});
$('.cms-edit-form').entwine({
getChangeTrackerOptions() {
// Figure out if we're still returning the default value
const isDefault = (this.entwineData('ChangeTrackerOptions') === undefined);
// Get the current options
let opts = this._super();
if (isDefault) {
// If it is the default then...
// clone the object (so we don't modify the original),
opts = $.extend({}, opts);
// modify it,
opts.ignoreFieldSelector += ', .ss-tag-field .no-change-track :input';
// then set the clone as the value on this element
// (so next call to this method gets this same clone)
this.setChangeTrackerOptions(opts);
}
return opts;
}
});
});

View File

@ -19,11 +19,12 @@
"issues": "http://github.com/silverstripe/silverstripe-tagfield/issues"
},
"require": {
"silverstripe/framework": "^4.3",
"php": "^7.4 || ^8.0",
"silverstripe/framework": "^4.10",
"silverstripe/versioned": "^1.3"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.0"
},
"autoload": {
@ -39,4 +40,4 @@
},
"minimum-stability": "dev",
"prefer-stable": true
}
}

BIN
docs/en/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -68,3 +68,17 @@ class BlogPost extends DataObject
In the above code example, the options available (whether lazy loaded or not) would be "one" and "two", and the
user would be able to create new options. Whichever tags are chosen would be stored in the BlogPost's `Tags` field
as a comma-delimited string.
## Using TagField with silverstripe-taxonomy
TagField assumes a `Title` field on objects. For classes without a `Title` field
use `setTitleField` to modify accordingly.
```php
$field = TagField::create(
'Tags',
'Blog Tags',
TaxonomyTerm::get(),
)
->setTitleField('Name');
```

View File

@ -57,14 +57,15 @@
]
},
"devDependencies": {
"@silverstripe/eslint-config": "^0.0.4",
"@silverstripe/webpack-config": "^0.8.0",
"babel-jest": "^20.0.3",
"@silverstripe/eslint-config": "^0.0.6",
"@silverstripe/webpack-config": "^0.10.0",
"@storybook/react": "^3.2.19",
"babel-jest": "^23.6.0",
"copy-webpack-plugin": "^4.2.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15.4": "^1.0.5",
"html-loader": "^0.5.1",
"jest-cli": "^19.0.2",
"jest-cli": "^23.6.0",
"react-addons-test-utils": "15.3.1",
"webpack": "^2"
},
@ -73,11 +74,11 @@
"classnames": "^2.2.5",
"debounce-promise": "^3.1.0",
"isomorphic-fetch": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"jquery": "^3.1.1",
"jquery": "^3.5.0",
"prop-types": "^15.7.2",
"react": "15.3.1",
"react-dom": "15.3.1",
"react-select": "^1.0.0-rc.5",
"react-select": "^1.2.1",
"url": "^0.11.0"
}
}

View File

@ -2,6 +2,9 @@
<ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>src</file>
<file>tests</file>
<!-- base rules are PSR-2 -->
<rule ref="PSR2" >
<!-- Current exclusions -->

View File

@ -1,7 +1,10 @@
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
<testsuite name="Default">
<directory>tests/</directory>
</testsuite>
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Default">
<directory>tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/</directory>

View File

@ -1,98 +0,0 @@
# Tag Field
Custom tag input field, for SilverStripe.
[![Build Status](https://travis-ci.org/silverstripe/silverstripe-tagfield.svg?branch=master)](https://travis-ci.org/silverstripe/silverstripe-tagfield)
[![Code Quality](http://img.shields.io/scrutinizer/g/silverstripe/silverstripe-tagfield.svg?style=flat)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-tagfield)
[![Code coverage](https://codecov.io/gh/silverstripe/silverstripe-tagfield/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-tagfield)
[![Version](http://img.shields.io/packagist/v/silverstripe/tagfield.svg?style=flat)](https://packagist.org/packages/silverstripe/tagfield)
[![License](http://img.shields.io/packagist/l/silverstripe/tagfield.svg?style=flat)](license.md)
[![SilverStripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
## Requirements
* SilverStripe 4.0
## Installing
```sh
$ composer require silverstripe/tagfield
```
## Using
### Relational Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $many_many = [
'BlogTags' => BlogTag::class
];
}
```
```php
use SilverStripe\ORM\DataObject;
class BlogTag extends DataObject
{
private static $db = [
'Title' => 'Varchar(200)',
];
private static $belongs_many_many = [
'BlogPosts' => BlogPost::class
];
}
```
```php
$field = TagField::create(
'BlogTags',
'Blog Tags',
BlogTag::get(),
$this->BlogTags()
)
->setShouldLazyLoad(true) // tags should be lazy loaded
->setCanCreate(true); // new tag DataObjects can be created
```
**Note:** This assumes you have imported the namespaces class, e.g. use SilverStripe\TagField\TagField;
### String Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $db = [
'Tags' => 'Text',
];
}
```
```php
$field = StringTagField::create(
'Tags',
'Tags',
['one', 'two'],
explode(',', $this->Tags)
);
$field->setShouldLazyLoad(true); // tags should be lazy loaded
```
You can find more in-depth documentation in [docs/en](docs/en/introduction.md).
## Versioning
This library follows [Semver](http://semver.org). According to Semver, you will be able to upgrade to any minor or patch version of this library without any breaking changes to the public API. Semver also requires that we clearly define the public API for this library.
All methods, with `public` visibility, are part of the public API. All other methods are not part of the public API. Where possible, we'll try to keep `protected` methods backwards-compatible in minor/patch versions, but if you're overriding methods then please test your work before upgrading.
## Reporting Issues
Please [create an issue](http://github.com/silverstripe/silverstripe-tagfield/issues) for any bugs you've found, or features you're missing.

View File

@ -6,6 +6,7 @@ use Iterator;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\Validator;
use SilverStripe\ORM\ArrayList;
@ -33,6 +34,13 @@ class StringTagField extends DropdownField
'suggest',
];
/**
* @var bool Triggers a write call within the saveInto function if enabled
*
* @deprecated 3.0.0 Will be removed without equivalent functionality to replace it
*/
private static $immediate_write_enabled = true;
/**
* @var bool
*/
@ -58,6 +66,9 @@ class StringTagField extends DropdownField
*/
protected $isMultiple = true;
/** @skipUpgrade */
protected $schemaComponent = 'TagField';
/**
* @return bool
*/
@ -144,7 +155,7 @@ class StringTagField extends DropdownField
public function Field($properties = [])
{
$this->addExtraClass('ss-tag-field');
$this->addExtraClass('ss-tag-field entwine');
return $this
->customise($properties)
@ -244,12 +255,12 @@ class StringTagField extends DropdownField
public function setValue($value, $source = null)
{
if (is_string($value)) {
$value = explode(',', $value);
$value = explode(',', $value ?? '');
}
if ($source instanceof DataObject) {
$name = $this->getName();
$value = explode(',', $source->$name);
$value = explode(',', $source->$name ?? '');
}
if ($source instanceof SS_List) {
@ -260,7 +271,7 @@ class StringTagField extends DropdownField
$value = [];
}
return parent::setValue(array_filter($value));
return parent::setValue(array_filter($value ?? []));
}
public function saveInto(DataObjectInterface $record)
@ -270,7 +281,13 @@ class StringTagField extends DropdownField
$name = $this->getName();
$record->$name = $this->dataValue();
$record->write();
$immediate_write_enabled = Deprecation::withNoReplacement(function () {
return self::config()->get('immediate_write_enabled');
});
if ($immediate_write_enabled) {
$record->write();
}
}
/**
@ -302,6 +319,49 @@ class StringTagField extends DropdownField
return $response;
}
/**
* Get or create tag with the given value
*
* @param string $term
* @return DataObject|bool
*/
protected function getOrCreateTag($term)
{
// Check if existing record can be found
$source = $this->getSourceList();
if (!$source) {
return false;
}
$titleField = $this->getTitleField();
$record = $source
->filter($titleField, $term)
->first();
if ($record) {
return $record;
}
// Create new instance if not yet saved
if ($this->getCanCreate()) {
$dataClass = $source->dataClass();
$record = Injector::inst()->create($dataClass);
if (is_array($term)) {
$term = $term['Value'];
}
$record->{$titleField} = $term;
$record->write();
if ($source instanceof SS_List) {
$source->add($record);
}
return $record;
}
return false;
}
/**
* Returns array of arrays representing tags that partially match the given search term
*
@ -315,7 +375,7 @@ class StringTagField extends DropdownField
/** @var ArrayData $tag */
$tagValue = $tag->Value;
// Map into a distinct list (prevent duplicates)
if (stripos($tagValue, $term) !== false && !array_key_exists($tagValue, $items)) {
if (stripos($tagValue ?? '', $term ?? '') !== false && !array_key_exists($tagValue, $items ?? [])) {
$items[$tagValue] = [
'id' => $tag->Title,
'text' => $tag->Value,
@ -323,7 +383,7 @@ class StringTagField extends DropdownField
}
}
// @todo do we actually need lazy loading limits for StringTagField?
return array_slice(array_values($items), 0, $this->getLazyLoadItemLimit());
return array_slice(array_values($items ?? []), 0, $this->getLazyLoadItemLimit());
}
/**
@ -356,4 +416,11 @@ class StringTagField extends DropdownField
return $this;
}
public function performReadonlyTransformation()
{
$field = parent::performReadonlyTransformation();
$field->setValue(implode(', ', $this->Value()));
return $field;
}
}

View File

@ -2,19 +2,21 @@
namespace SilverStripe\TagField;
use Exception;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\MultiSelectField;
use SilverStripe\Forms\Validator;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\FieldType\DBMultiEnum;
use SilverStripe\ORM\Relation;
use SilverStripe\ORM\SS_List;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
/**
* Provides a tagging interface, storing links between tag DataObjects and a parent DataObject.
@ -22,7 +24,7 @@ use SilverStripe\View\Requirements;
* @package forms
* @subpackage fields
*/
class TagField extends DropdownField
class TagField extends MultiSelectField
{
/**
* @var array
@ -61,18 +63,22 @@ class TagField extends DropdownField
*/
protected $isMultiple = true;
/** @skipUpgrade */
protected $schemaComponent = 'TagField';
/**
* @param string $name
* @param string $title
* @param null|DataList $source
* @param null|DataList|array $source
* @param null|DataList $value
* @param string $titleField
*/
public function __construct($name, $title = '', $source = [], $value = null, $titleField = 'Title')
{
$this->setSourceList($source);
$this->setTitleField($titleField);
parent::__construct($name, $title, $source, $value);
$this->addExtraClass('ss-tag-field');
}
/**
@ -176,7 +182,9 @@ class TagField extends DropdownField
}
/**
* Get the DataList source. The 4.x upgrade for SelectField::setSource starts to convert this to an array
* Get the DataList source. The 4.x upgrade for SelectField::setSource starts to convert this to an array.
* If empty use getSource() for array version
*
* @return DataList
*/
public function getSourceList()
@ -186,7 +194,8 @@ class TagField extends DropdownField
/**
* Set the model class name for tags
* @param DataList $className
*
* @param DataList $sourceList
* @return self
*/
public function setSourceList($sourceList)
@ -200,7 +209,7 @@ class TagField extends DropdownField
*/
public function Field($properties = [])
{
$this->addExtraClass('ss-tag-field');
$this->addExtraClass('entwine');
return $this->customise($properties)->renderWith(self::class);
}
@ -212,43 +221,34 @@ class TagField extends DropdownField
*/
public function getSchemaDataDefaults()
{
$options = $this->getOptions(true);
$name = $this->getName();
if ($this->getIsMultiple() && strpos($name, '[') === false) {
$name .= '[]';
}
$schema = array_merge(
parent::getSchemaDataDefaults(),
[
'name' => $this->getName() . '[]',
'name' => $name,
'lazyLoad' => $this->getShouldLazyLoad(),
'creatable' => $this->getCanCreate(),
'multi' => $this->getIsMultiple(),
'value' => $this->Value(),
'value' => $options->count() ? $options->toNestedArray() : null,
'disabled' => $this->isDisabled() || $this->isReadonly(),
]
);
if (!$this->getShouldLazyLoad()) {
$schema['options'] = array_values($this->getOptions()->toNestedArray());
$schema['options'] = array_values($this->getOptions()->toNestedArray() ?? []);
} else {
if ($this->Value()) {
$schema['value'] = $this->getOptions(true)->toNestedArray();
}
$schema['optionUrl'] = $this->getSuggestURL();
}
return $schema;
}
/**
* When not used in a React form factory context, this adds the schema data to SilverStripe template
* rendered attributes lists
*
* @return array
*/
public function getAttributes()
{
$attributes = parent::getAttributes();
$attributes['data-schema'] = json_encode($this->getSchemaData());
return $attributes;
}
/**
* @return string
*/
@ -258,128 +258,240 @@ class TagField extends DropdownField
}
/**
* @param bool $onlySelected Only return options that are selected
* @return ArrayList
*/
protected function getOptions($onlySelected = false)
{
$options = ArrayList::create();
$source = $this->getSourceList();
// No source means we have no options
if (!$source) {
$source = ArrayList::create();
return ArrayList::create();
}
$dataClass = $source->dataClass();
$titleField = $this->getTitleField();
$values = $this->Value();
if ($values) {
if (is_array($values)) {
$values = DataList::create($dataClass)->filter($titleField, $values);
$values = $this->getValueArray();
// If we have no values and we only want selected options we can bail here
if (empty($values) && $onlySelected) {
return ArrayList::create();
}
$titleField = $this->getTitleField();
// Convert an array of values into a datalist of options
if (!$values instanceof SS_List) {
if (is_array($values) && !empty($values)) {
// if values is an array of Ids then we should look up via
// ID.
if (array_filter($values, 'is_int')) {
$queryField = 'ID';
} else {
$queryField = $titleField;
}
if (is_a($source, DataList::class)) {
$values = $source->filterAny([
$queryField => $values
]);
} else {
$values = DataList::create($dataClass)
->filterAny([
$queryField => $values
]);
}
} else {
$values = ArrayList::create();
}
}
// Prep a function to parse a dataobject into an option
$addOption = function (DataObject $item) use ($options, $values, $titleField) {
$option = $item->$titleField;
$options->push(ArrayData::create([
'Title' => $option,
'Value' => $option,
'Selected' => (bool) $values->find($titleField, $option)
]));
};
// Only parse the values if we only want the selected items in the values list (this is for lazy-loading)
if ($onlySelected) {
$source = $values;
$values->each($addOption);
return $options;
}
return $source instanceof DataList ? $this->formatOptions($source) : ArrayList::create();
}
/**
* @param DataList $source
* @return ArrayList
*/
protected function formatOptions(DataList $source)
{
$options = ArrayList::create();
$titleField = $this->getTitleField();
foreach ($source as $object) {
$options->push(
ArrayData::create([
'Title' => $object->$titleField,
'Value' => $object->Title,
])
);
}
$source->each($addOption);
return $options;
}
/**
* {@inheritdoc}
* Gets the source array if required
*
* Note: this is expensive for a SS_List
*
* @return array
*/
public function setValue($value, $source = null)
public function getSource()
{
if ($source instanceof DataObject) {
$name = $this->getName();
if ($source->hasMethod($name)) {
$value = $source->$name()->column($this->getTitleField());
}
} elseif ($value instanceof SS_List) {
$value = $value->column($this->getTitleField());
if (is_null($this->source)) {
$this->source = $this->getListMap($this->getSourceList());
}
if (!is_array($value)) {
return parent::setValue($value);
}
return parent::setValue(array_filter($value));
return $this->source;
}
/**
* Intercept DataList source
*
* @param mixed $source
* @return $this
*/
public function setSource($source)
{
// When setting a datalist force internal list to null
if ($source instanceof DataList) {
$this->source = null;
$this->setSourceList($source);
} else {
parent::setSource($source);
}
return $this;
}
/**
* @param DataObject|DataObjectInterface $record DataObject to save data into
* @throws Exception
*/
public function getAttributes()
{
$name = $this->getName();
if ($this->getIsMultiple() && strpos($name, '[') === false) {
$name .= '[]';
}
return array_merge(
parent::getAttributes(),
[
'name' => $name,
'style' => 'width: 100%',
'data-schema' => json_encode($this->getSchemaData()),
]
);
}
protected function getListValues($values): array
{
if (empty($values)) {
return [];
}
if (is_array($values)) {
return $values;
}
if ($values instanceof SS_List) {
return $values->column($this->getTitleField());
}
if ($values instanceof DataObject && $values->exists()) {
return [$values->{$this->getTitleField()} ?? $values->ID];
}
if (is_int($values)) {
return [$values];
}
return [trim((string) $values)];
}
/**
* {@inheritdoc}
*/
public function saveInto(DataObjectInterface $record)
{
parent::saveInto($record);
$name = $this->getName();
$titleField = $this->getTitleField();
$values = $this->Value();
$relation = $record->$name();
$values = $this->getValueArray();
$ids = [];
if (!$values) {
$values = [];
}
if (empty($record) || empty($titleField)) {
if (empty($record)) {
return;
}
if (!$record->hasMethod($name)) {
throw new Exception(
sprintf("%s does not have a %s method", get_class($record), $name)
);
}
/** @var Relation $relation */
$relation = $record->hasMethod($name) ? $record->$name() : null;
foreach ($values as $key => $value) {
// Get or create record
$record = $this->getOrCreateTag($value);
if ($record) {
$ids[] = $record->ID;
$values[$key] = $record->Title;
$tag = $this->getOrCreateTag($value);
if ($tag) {
$ids[] = $tag->ID;
$values[$key] = $tag->Title;
}
}
$relation->setByIDList(array_filter($ids));
if ($relation instanceof Relation) {
// Save ids into relation
$relation->setByIDList(array_filter($ids ?? []));
} elseif ($record->hasField($name)) {
if ($this->getIsMultiple()) {
if ($record->obj($name) instanceof DBMultiEnum) {
// Save dataValue into field... a CSV for DBMultiEnum
$record->$name = $this->csvEncode(array_filter(array_values($values)));
} else {
// ... JSON-encoded string for other fields
$record->$name = $this->stringEncode(array_filter(array_values($values)));
}
} else {
if (isset($tag) && $tag->ID) {
$record->$name = $tag->ID;
} else {
$record->$name = null;
}
}
}
}
/**
* Get or create tag with the given value
*
* @param string $term
* @param string $value
*
* @return DataObject|bool
*/
protected function getOrCreateTag($term)
protected function getOrCreateTag($value)
{
if (is_array($value)) {
$value = $value['Value'] ?? '';
}
// Check if existing record can be found
$source = $this->getSourceList();
$titleField = $this->getTitleField();
if (!$source) {
return false;
}
$record = $source
->filter($titleField, $term)
->filter($titleField, $value)
->first();
if ($record) {
return $record;
}
@ -388,8 +500,13 @@ class TagField extends DropdownField
if ($this->getCanCreate()) {
$dataClass = $source->dataClass();
$record = Injector::inst()->create($dataClass);
$record->{$titleField} = $term;
$record->{$titleField} = $value;
$record->write();
if ($source instanceof SS_List) {
$source->add($record);
}
return $record;
}
@ -422,6 +539,9 @@ class TagField extends DropdownField
protected function getTags($term)
{
$source = $this->getSourceList();
if (!$source) {
return [];
}
$titleField = $this->getTitleField();
@ -433,14 +553,15 @@ class TagField extends DropdownField
// Map into a distinct list
$items = [];
$titleField = $this->getTitleField();
foreach ($query->map('ID', $titleField) as $id => $title) {
foreach ($query->map('ID', $titleField)->values() as $title) {
$items[$title] = [
'id' => $title,
'text' => $title,
'Title' => $title,
'Value' => $title,
];
}
return array_values($items);
return array_values($items ?? []);
}
/**
@ -465,6 +586,8 @@ class TagField extends DropdownField
/** @var ReadonlyTagField $copy */
$copy = $this->castedCopy(ReadonlyTagField::class);
$copy->setSourceList($this->getSourceList());
$copy->setTitleField($this->getTitleField());
return $copy;
}
@ -477,4 +600,29 @@ class TagField extends DropdownField
{
return '';
}
public function getSchemaStateDefaults()
{
$data = parent::getSchemaStateDefaults();
// Add options to 'data'
$data['lazyLoad'] = $this->getShouldLazyLoad();
$data['multi'] = $this->getIsMultiple();
$data['optionUrl'] = $this->getSuggestURL();
$data['creatable'] = $this->getCanCreate();
$options = $this->getOptions(true);
$data['value'] = $options->count() ? $options->toNestedArray() : null;
return $data;
}
public function getSchemaDataType(): string
{
if ($this->getIsMultiple()) {
return self::SCHEMA_DATA_TYPE_MULTISELECT;
}
return self::SCHEMA_DATA_TYPE_SINGLESELECT;
}
}

View File

@ -2,17 +2,14 @@
namespace SilverStripe\TagField\Tests;
use PHPUnit_Framework_TestCase;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\TagField\StringTagField;
use SilverStripe\TagField\Tests\Stub\StringTagFieldTestBlogPost;
use SilverStripe\Dev\Deprecation;
/**
* @mixin PHPUnit_Framework_TestCase
*/
class StringTagFieldTest extends SapphireTest
{
/**
@ -65,6 +62,44 @@ class StringTagFieldTest extends SapphireTest
$this->assertEquals('Tag1,Tag2', $record->Tags);
}
public function testImmediateWriteEnabled()
{
if (Deprecation::isEnabled()) {
$this->markTestSkipped('Test calls deprecated code');
}
$record = $this->getNewStringTagFieldTestBlogPost('BlogPost1');
$record->write();
StringTagField::config()->set('immediate_write_enabled', true);
$field = new StringTagField('Tags');
$field->setValue(['Tag1', 'Tag2']);
$field->saveInto($record);
$this->assertEquals('Tag1,Tag2', StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
}
public function testImmediateWriteDisabled()
{
if (Deprecation::isEnabled()) {
$this->markTestSkipped('Test calls deprecated code');
}
$record = $this->getNewStringTagFieldTestBlogPost('BlogPost1');
$record->write();
StringTagField::config()->set('immediate_write_enabled', false);
$field = new StringTagField('Tags');
$field->setValue(['Tag1', 'Tag2']);
$field->saveInto($record);
$this->assertNull(StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
$record->write();
$this->assertEquals('Tag1,Tag2', StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
}
public function testItSuggestsTags()
{
$field = new StringTagField('SomeField', 'Some field', ['Tag1', 'Tag2'], []);
@ -133,7 +168,7 @@ class StringTagFieldTest extends SapphireTest
$schema = $field->getSchemaDataDefaults();
$this->assertTrue($schema['lazyLoad']);
$this->assertTrue($schema['creatable']);
$this->assertContains('suggest', $schema['optionUrl']);
$this->assertStringContainsString('suggest', $schema['optionUrl']);
}
public function testSchemaIsAddedToAttributes()
@ -143,6 +178,28 @@ class StringTagFieldTest extends SapphireTest
$this->assertNotEmpty($attributes['data-schema']);
}
public function testPerformReadonlyTransformation()
{
$field = new StringTagField('Tags');
$field->setSource(['Test1', 'Test2', 'Test3']);
// Ensure a single value can be rendered
$field->setValue(['Test2']);
$field_readonly = $field->performReadonlyTransformation();
$this->assertEquals('Test2', $field_readonly->Value());
// Ensure multiple valid values are rendered
$field->setValue(['Test1', 'Test2']);
$field_readonly = $field->performReadonlyTransformation();
$this->assertEquals('Test1, Test2', $field_readonly->Value());
// Ensure an value not in the source array is still rendered
// (because e.g. in history view it must have been a valid value when it was set)
$field->setValue(['Test', 'Test1']);
$field_readonly = $field->performReadonlyTransformation();
$this->assertEquals('Test, Test1', $field_readonly->Value());
}
/**
* @param array $parameters
* @return HTTPRequest

View File

@ -18,4 +18,8 @@ class TagFieldTestBlogPost extends DataObject implements TestOnly
private static $many_many = [
'Tags' => TagFieldTestBlogTag::class
];
private static $has_one = [
'PrimaryTag' => TagFieldTestBlogTag::class
];
}

View File

@ -2,7 +2,6 @@
namespace SilverStripe\TagField\Tests;
use PHPUnit_Framework_TestCase;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
@ -15,9 +14,6 @@ use SilverStripe\TagField\Tests\Stub\TagFieldTestBlogPost;
use SilverStripe\TagField\Tests\Stub\TagFieldTestBlogTag;
use SilverStripe\TagField\Tests\Stub\TagFieldTestController;
/**
* @mixin PHPUnit_Framework_TestCase
*/
class TagFieldTest extends SapphireTest
{
/**
@ -40,12 +36,40 @@ class TagFieldTest extends SapphireTest
$field->setValue(['Tag3', 'Tag4']);
$field->saveInto($record);
$record->write();
$this->compareExpectedAndActualTags(
['Tag3', 'Tag4'],
$record
);
}
public function testItSavesToHasOne()
{
$record = $this->getNewTagFieldTestBlogPost('BlogPost1');
$tag = new TagFieldTestBlogTag();
$tag->Title = 'Foobar';
$tag->write();
$field = new TagField('PrimaryTagID', '', new DataList(TagFieldTestBlogTag::class));
$field->setIsMultiple(false);
$field->setValue('Foobar');
$field->saveInto($record);
$record->write();
$this->assertEquals($tag->ID, $record->PrimaryTagID, 'The tag is saved to a has_one');
$tag = new TagFieldTestBlogTag();
$tag->Title = 'Foobarbaz';
$tag->write();
$field->setValue(['Foobarbaz']);
$field->saveInto($record);
$record->write();
$this->assertEquals($tag->ID, $record->PrimaryTagID, 'The tag is saved to a has_one');
}
/**
* @param string $name
*
@ -76,7 +100,7 @@ class TagFieldTest extends SapphireTest
*/
protected function compareTagLists(array $expected, DataList $actualSource)
{
$actual = array_values($actualSource->map('ID', 'Title')->toArray());
$actual = array_values($actualSource->map('ID', 'Title')->toArray() ?? []);
sort($expected);
sort($actual);
@ -132,6 +156,30 @@ class TagFieldTest extends SapphireTest
);
}
public function testSavesReactTags()
{
$record = $this->getNewTagFieldTestBlogPost('BlogPost1');
$record->write();
$field = new TagField('Tags', '', new DataList(TagFieldTestBlogTag::class));
$field->setValue([
[
'Title' => 'Tag1',
'Value' => 'Tag1',
],
[
'Title' => 'Tag2',
'Value' => 'Tag2',
],
]);
$field->saveInto($record);
$this->compareExpectedAndActualTags(
['Tag1', 'Tag2'],
$record
);
}
/**
* Ensure that {@see TagField::saveInto} respects existing tags
*/
@ -181,7 +229,7 @@ class TagFieldTest extends SapphireTest
$request = $this->getNewRequest(['term' => 'Tag']);
$this->assertEquals(
'{"items":[{"id":"Tag1","text":"Tag1"}]}',
'{"items":[{"Title":"Tag1","Value":"Tag1"}]}',
$field->suggest($request)->getBody()
);
@ -191,7 +239,7 @@ class TagFieldTest extends SapphireTest
$request = $this->getNewRequest(['term' => '222']);
$this->assertEquals(
'{"items":[{"id":"222","text":"222"}]}',
'{"items":[{"Title":"222","Value":"222"}]}',
$field->suggest($request)->getBody()
);
@ -201,7 +249,7 @@ class TagFieldTest extends SapphireTest
$request = $this->getNewRequest(['term' => 'TAG1']);
$this->assertEquals(
'{"items":[{"id":"Tag1","text":"Tag1"}]}',
'{"items":[{"Title":"Tag1","Value":"Tag1"}]}',
$field->suggest($request)->getBody()
);
@ -230,7 +278,7 @@ class TagFieldTest extends SapphireTest
$request = $this->getNewRequest(['term' => 'Tag']);
$this->assertEquals(
'{"items":[{"id":"Tag1","text":"Tag1"}]}',
'{"items":[{"Title":"Tag1","Value":"Tag1"}]}',
$field->suggest($request)->getBody()
);
@ -240,7 +288,7 @@ class TagFieldTest extends SapphireTest
$request = $this->getNewRequest(['term' => 'Tag1']);
$this->assertEquals(
'{"items":[{"id":"Tag1","text":"Tag1"}]}',
'{"items":[{"Title":"Tag1","Value":"Tag1"}]}',
$field->suggest($request)->getBody()
);
@ -287,7 +335,7 @@ class TagFieldTest extends SapphireTest
$this->objFromFixture(TagFieldTestBlogPost::class, 'BlogPost2')
);
$ids = TagFieldTestBlogTag::get()->column('Title');
$ids = TagFieldTestBlogTag::get()->column('ID');
$this->assertEquals($field->Value(), $ids);
}
@ -337,7 +385,9 @@ class TagFieldTest extends SapphireTest
$tag = TagFieldTestBlogTag::get()->first();
$this->assertNotEmpty($tag);
$this->assertEquals('New Tag', $tag->Title);
$record = TagFieldTestBlogPost::get()->byID($record->ID);
$this->assertEquals(
$tag->ID,
$record->Tags()->first()->ID
@ -352,6 +402,31 @@ class TagFieldTest extends SapphireTest
$field = new TagField('Tags', '', TagFieldTestBlogTag::get());
$readOnlyField = $field->performReadonlyTransformation();
$this->assertInstanceOf(ReadonlyTagField::class, $readOnlyField);
// Custom title field
$field = new TagField('Tags', '', TagFieldTestBlogTag::get());
$field->setTitleField('Name');
$readOnlyField = $field->performReadonlyTransformation();
$this->assertEquals('Name', $readOnlyField->getTitleField());
}
public function testItDisplaysWithSelectedValuesFromDataList()
{
$source = TagFieldTestBlogTag::get();
$selectedTag = $source->First();
$unselectedTag = $source->Last();
$value = $source->filter('ID', $selectedTag->ID); // arbitrary subset
$field = new TagField('TestField', null, $source, $value);
// Not the cleanest way to assert this, but getOptions() is protected
$schema = $field->getSchemaDataDefaults();
$this->assertTrue(
$this->getFromOptionsByTitle($schema['options'], $selectedTag->Title)['Selected']
);
$this->assertFalse(
$this->getFromOptionsByTitle($schema['options'], $unselectedTag->Title)['Selected']
);
}
public function testGetSchemaDataDefaults()
@ -369,8 +444,16 @@ class TagFieldTest extends SapphireTest
$this->assertFalse($schema['lazyLoad']);
$this->assertFalse($schema['creatable']);
$this->assertEquals([
['Title' => 'Tag1', 'Value' => 'Tag1'],
['Title' => '222', 'Value' => '222'],
['Title' => 'Tag1', 'Value' => 'Tag1', 'Selected' => false],
['Title' => '222', 'Value' => '222', 'Selected' => false],
], $schema['options']);
$field->setValue(['222']);
$schema = $field->getSchemaDataDefaults();
$this->assertEquals([
['Title' => 'Tag1', 'Value' => 'Tag1', 'Selected' => false],
['Title' => '222', 'Value' => '222', 'Selected' => true],
], $schema['options']);
$field
@ -380,7 +463,7 @@ class TagFieldTest extends SapphireTest
$schema = $field->getSchemaDataDefaults();
$this->assertTrue($schema['lazyLoad']);
$this->assertTrue($schema['creatable']);
$this->assertContains('suggest', $schema['optionUrl']);
$this->assertStringContainsString('suggest', $schema['optionUrl']);
}
public function testSchemaIsAddedToAttributes()
@ -389,4 +472,20 @@ class TagFieldTest extends SapphireTest
$attributes = $field->getAttributes();
$this->assertNotEmpty($attributes['data-schema']);
}
/**
* @param array $options
* @param string $title
* @return array|null
*/
protected function getFromOptionsByTitle(array $options, $title)
{
foreach ($options as $option) {
if ($option['Title'] == $title) {
return $option;
}
}
return null;
}
}

8795
yarn.lock

File diff suppressed because it is too large Load Diff