Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: systopia/drupal-json_forms
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.2.0
Choose a base ref
...
head repository: systopia/drupal-json_forms
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Loading
Showing with 2,686 additions and 488 deletions.
  1. +42 −0 README.md
  2. +12 −14 composer.json
  3. +44 −0 js/disable-buttons-on-ajax.js
  4. +0 −52 js/submit.js
  5. +49 −0 js/vertical-tabs.js
  6. +3 −1 json_forms.info.yml
  7. +9 −4 json_forms.libraries.yml
  8. +23 −0 json_forms.module
  9. +2 −2 modules/json_forms_example/src/Form/JsonFormsExampleForm.php
  10. +3 −0 phpcs.xml.dist
  11. +0 −4 phpstan.neon.dist
  12. +39 −9 src/Form/AbstractJsonFormsForm.php
  13. +2 −1 src/Form/ConcreteFormArrayFactoryInterface.php
  14. +32 −9 src/Form/Control/ArrayArrayFactory.php
  15. +45 −8 src/Form/Control/Callbacks/ArrayCallbacks.php
  16. +51 −0 src/Form/Control/Callbacks/DateValueCallback.php
  17. +45 −0 src/Form/Control/Callbacks/EmailValidateCallback.php
  18. +40 −0 src/Form/Control/Callbacks/HtmlCallbacks.php
  19. +76 −0 src/Form/Control/Callbacks/OptionValueCallbacks.php
  20. +4 −94 src/Form/Control/Callbacks/RecalculateCallback.php
  21. +22 −51 src/Form/Control/Callbacks/SelectCallbacks.php
  22. +59 −0 src/Form/Control/Callbacks/StringValueCallback.php
  23. +45 −0 src/Form/Control/Callbacks/UrlValidateCallback.php
  24. +143 −0 src/Form/Control/Callbacks/Util/RecalculateCallbackUtil.php
  25. +46 −0 src/Form/Control/Callbacks/ValueElementValueCallback.php
  26. +12 −2 src/Form/Control/CheckboxArrayFactory.php
  27. +5 −3 src/Form/Control/CheckboxesArrayFactory.php
  28. +4 −1 src/Form/Control/DateArrayFactory.php
  29. +2 −1 src/Form/Control/DatetimeArrayFactory.php
  30. +4 −1 src/Form/Control/EmailArrayFactory.php
  31. +12 −3 src/Form/Control/HiddenArrayFactory.php
  32. +89 −0 src/Form/Control/HtmlArrayFactory.php
  33. +2 −1 src/Form/Control/NumberArrayFactory.php
  34. +5 −32 src/Form/Control/ObjectArrayFactory.php
  35. +23 −11 src/Form/Control/RadiosArrayFactory.php
  36. +65 −19 src/Form/Control/Rule/StatesArrayFactory.php
  37. +3 −1 src/Form/Control/Rule/StatesArrayFactoryInterface.php
  38. +76 −22 src/Form/Control/Rule/StatesBuilder.php
  39. +12 −3 src/Form/Control/SelectArrayFactory.php
  40. +5 −2 src/Form/Control/StringArrayFactory.php
  41. +2 −1 src/Form/Control/SubmitButtonArrayFactory.php
  42. +4 −1 src/Form/Control/UrlArrayFactory.php
  43. +17 −6 src/Form/Control/Util/BasicFormPropertiesFactory.php
  44. +65 −7 src/Form/Control/Util/{OptionsBuilder.php → OptionsUtil.php}
  45. +82 −0 src/Form/Control/ValueArrayFactory.php
  46. +6 −0 src/Form/FormArrayFactory.php
  47. +15 −0 src/Form/Layout/AbstractLayoutArrayFactory.php
  48. +65 −0 src/Form/Layout/TableArrayFactory.php
  49. +37 −6 src/Form/Layout/TableRowArrayFactory.php
  50. +7 −5 src/Form/Markup/HtmlMarkupArrayFactory.php
  51. +50 −0 src/Form/Util/DescriptionDisplayUtil.php
  52. +6 −1 src/Form/Util/FactoryRegistrator.php
  53. +1 −1 src/Form/Util/FieldNameUtil.php
  54. +83 −0 src/Form/Util/FormValidationUtil.php
  55. +16 −11 src/Form/Util/JsonConverter.php
  56. +8 −2 src/Form/Validation/FormValidationMapper.php
  57. +11 −10 src/Form/Validation/FormValidator.php
  58. +11 −10 src/Form/Validation/FormValidatorInterface.php
  59. +11 −10 src/Form/Validation/OpisValidatorFactory.php
  60. +11 −10 src/Form/Validation/ValidationResult.php
  61. +52 −6 src/JsonForms/Definition/Control/ControlDefinition.php
  62. +22 −0 src/JsonForms/Definition/Control/ObjectControlDefinition.php
  63. +91 −0 src/JsonForms/Definition/Custom/CustomDefinition.php
  64. +14 −7 src/JsonForms/Definition/{Markup → Custom}/MarkupDefinition.php
  65. +22 −7 src/JsonForms/Definition/DefinitionFactory.php
  66. +14 −2 src/JsonForms/Definition/DefinitionInterface.php
  67. +38 −0 src/JsonForms/Definition/Layout/ArrayLayoutDefinition.php
  68. +54 −8 src/JsonForms/Definition/Layout/LayoutDefinition.php
  69. +70 −0 src/JsonForms/Definition/Layout/ObjectLayoutDefinition.php
  70. +18 −1 src/JsonForms/ScopePointer.php
  71. +52 −6 tests/src/Unit/Form/Control/CheckboxArrayFactoryTest.php
  72. +4 −4 tests/src/Unit/Form/Control/HiddenArrayFactoryTest.php
  73. +27 −7 tests/src/Unit/Form/Markup/HtmlMarkupArrayFactoryTest.php
  74. +457 −7 tests/src/Unit/Form/Rule/StatesArrayFactoryTest.php
  75. +1 −1 tests/src/Unit/Form/Util/FieldNameUtilTest.php
  76. +46 −0 translations/de.po
  77. +41 −0 translations/json_forms.pot
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# JSON Forms for Drupal

JSON Forms for Drupal is an implementation of the [JSON Forms](
https://jsonforms.io/) specification for Drupal.

## Additional features and keywords

In this implementation there are some custom features and keywords not
specified in standard JSON Forms. (TODO: Not all additional possibilities are
described here, yet.)

### Description display

The Keyword `descriptionDisplay` in Control options allows to specify the
display of the description. Possible options:

* `after`
* `before`
* `invisible`
* `tooltip`

The first three options are standard options available for the
`#description_display` in Drupal.

The option `tooltip` leads to an additional CSS class on the description
element: `json-forms-description-tooltip`. This can be used to process it
with another module to display the description as tooltip.

With the module [Form Tips](https://www.drupal.org/project/formtips) it can
be achieved with this CSS selector:

```css
:not(.json-forms-description-tooltip)
```

## Limitations

Some things cannot be done with (standard) Drupal forms, e.g.
[Rules](https://jsonforms.io/docs/uischema/rules/) cannot completely be mapped
to [conditional form fields](https://www.drupal.org/docs/drupal-apis/form-api/conditional-form-fields).

TODO: Describe all limitations.
26 changes: 12 additions & 14 deletions composer.json
Original file line number Diff line number Diff line change
@@ -9,6 +9,11 @@
"email": "info@systopia.de"
}
],
"extra": {
"branch-alias": {
"dev-main": "0.3-dev"
}
},
"autoload": {
"psr-4": {
"Drupal\\json_forms\\": "src/"
@@ -24,27 +29,20 @@
"config": {
"sort-packages": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
"dealerdirect/phpcodesniffer-composer-installer": true,
"php-http/discovery": false,
"phpstan/extension-installer": false,
"tbachert/spi": false
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/systopia/opis-json-schema-ext"
},
{
"type": "vcs",
"url": "https://github.com/systopia/expression-language-ext"
}
],
"require": {
"php": "^7.4 || ^8",
"systopia/expression-language-ext": "~0.1",
"systopia/opis-json-schema-ext": "~0.2"
},
"require-dev": {
"drupal/core": "^9.1.6",
"drupal/core-dev": "^9.1.6"
"drupal/core": "^9.5 || ^10",
"drupal/core-dev": "^9.5 || ^10"
},
"scripts": {
"composer-phpstan": [
@@ -60,7 +58,7 @@
"@php vendor/bin/phpcbf"
],
"phpstan": [
"@php tools/phpstan/vendor/bin/phpstan"
"@php tools/phpstan/vendor/bin/phpstan -v"
],
"phpunit": [
"@php vendor/bin/phpunit --coverage-text"
44 changes: 44 additions & 0 deletions js/disable-buttons-on-ajax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2024 SYSTOPIA GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* Adding/removing entries to an array before a previous AJAX call has been
* finished might lead to an inconsistent state. Thus, buttons are disabled
* during AJAX calls. Additionally, form submit is not possible during AJAX
* calls. Fields that initiate an AJAX calls are disabled until the call is
* finished and would be missing in the submitted data.
*/
(function ($) {

$(document).on('ajaxStart', () => {
const buttons = document.querySelectorAll('input[type="submit"]:enabled, input[type="button"]:enabled');
buttons.forEach((button) => {
button.disabled = true;
button.setAttribute('data-ajax-disabled', 'true');
});
});

$(document).on('ajaxStop', () => {
const buttons = document.querySelectorAll('input[data-ajax-disabled="true"]');
buttons.forEach((button) => {
button.disabled = false;
button.removeAttribute('data-ajax-disabled');
});

});

})(jQuery);
52 changes: 0 additions & 52 deletions js/submit.js

This file was deleted.

49 changes: 49 additions & 0 deletions js/vertical-tabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2024 SYSTOPIA GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* Prevent switch of vertical tab on AJAX call.
*
* By default, Drupal switches to the last vertical tab that contains a
* validation error on AJAX success. This is prevented by removing the CSS class
* "error" during "ajaxStart" event and adding it back in "ajaxStop" event. The
* approach to prevent the click on the link is not possible because event
* handlers are executed in the order of attachment. (Ordering of Drupal
* behaviors is not supported.)
*
* @see https://git.drupalcode.org/project/drupal/-/blob/10.3.6/core/misc/vertical-tabs.js?ref_type=tags#L132
* @see https://git.drupalcode.org/project/drupal/-/blob/10.3.6/core/misc/ajax.js?ref_type=tags#L1135
* @see https://www.drupal.org/project/drupal/issues/2474019
*/
(function ($) {

let errorElements;

$(document).on('ajaxStart', () => {
errorElements = document.querySelectorAll('details .form-item .error');
errorElements.forEach((item) => {
item.classList.remove('error');
});
});

$(document).on('ajaxStop', () => {
errorElements.forEach((item) => {
item.classList.add('error');
});
});

})(jQuery);
4 changes: 3 additions & 1 deletion json_forms.info.yml
Original file line number Diff line number Diff line change
@@ -5,4 +5,6 @@ package: JSONForms
core_version_requirement: ^9.5 || ^10
php: 7.4
hidden: true
version: 0.2.0
version: 0.5.7-dev
'interface translation project': json_forms
'interface translation server pattern': modules/custom/%project/translations/%language.po
13 changes: 9 additions & 4 deletions json_forms.libraries.yml
Original file line number Diff line number Diff line change
@@ -9,11 +9,16 @@ confirm:
- core/jquery
- core/once

submit:
disable_buttons_on_ajax:
version: 0.1.1
js:
js/disable-buttons-on-ajax.js: {}
dependencies:
- core/jquery

vertical_tabs:
version: 0.1.0
js:
js/submit.js: {}
js/vertical-tabs.js: {}
dependencies:
- core/drupal
- core/jquery
- core/once
23 changes: 23 additions & 0 deletions json_forms.module
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
<?php
declare(strict_types=1);

use Drupal\Core\Template\Attribute;

function _json_forms_add_tooltip_css_class(array &$variables): void {
if ('tooltip' === ($variables['element']['#_json_forms_description_display'] ?? NULL)) {
/** @var \Drupal\Core\Template\Attribute|null $descriptionAttributes */
$descriptionAttributes = $variables['description']['attributes'] ?? NULL;
if (NULL !== $descriptionAttributes) {
// Add the CSS class "json-forms-description-tooltip" to the description
// element. This can be used to process the element with another module.
$descriptionAttributes->merge(new Attribute(['class' => ['json-forms-description-tooltip']]));
}
}
}

function json_forms_preprocess_form_element(array &$variables): void {
_json_forms_add_tooltip_css_class($variables);
}

function json_forms_preprocess_fieldset(array &$variables): void {
_json_forms_add_tooltip_css_class($variables);
}
4 changes: 2 additions & 2 deletions modules/json_forms_example/src/Form/JsonFormsExampleForm.php
Original file line number Diff line number Diff line change
@@ -51,8 +51,8 @@ public function getFormId(): string {
public function buildForm(
array $form,
FormStateInterface $form_state,
\stdClass $jsonSchema = NULL,
\stdClass $uiSchema = NULL,
?\stdClass $jsonSchema = NULL,
?\stdClass $uiSchema = NULL,
int $flags = 0
): array {
Assertion::notNull($jsonSchema);
3 changes: 3 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@
<config name="ignore_warnings_on_exit" value="true"/>

<rule ref="vendor/drupal/coder/coder_sniffer/Drupal">
<!-- Keep PHP 7.4 compatibility -->
<exclude name="Drupal.Functions.MultiLineFunctionDeclaration.MissingTrailingComma"/>

<!-- Don't enforce phpdoc type hint because it (might) only duplicate a PHP type hint -->
<exclude name="Drupal.Commenting.FunctionComment.MissingParamComment"/>
<exclude name="Drupal.Commenting.FunctionComment.ParamMissingDefinition"/>
4 changes: 0 additions & 4 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -25,14 +25,10 @@ parameters:
# Wrong phpdoc type hint in Drupal
- '/^Parameter #1 \$key of method Drupal\\Core\\Form\\FormStateInterface::hasTemporaryValue\(\) expects string, array<int\|string> given.$/'
- '#^Method Drupal\\Core\\Form\\FormBuilderInterface::getForm\(\) invoked with \d+ parameters, 1 required.$#'
- '/^Parameter #1 \$form \(array<int\|string, mixed>\) of method [^\s]+::(build|validate|submit)Form\(\) should be contravariant with parameter \$form \(array\) of method Drupal\\Core\\Form\\(FormInterface|FormBase)::(build|validate|submit)Form\(\)$/'
-
message: '#^Variable property access on mixed.$#'
paths:
- */src/JsonForms/Definition/Control/ControlDefinition.php
- */src/JsonForms/Definition/Layout/LayoutDefinition.php
-
message: '#^Argument of an invalid type stdClass supplied for foreach, only iterables are supported.$#'
path: */src/Form/Control/ObjectArrayFactory.php
- '#^Method Drupal\\json_forms\\JsonForms\\Definition\\Control\\[^\s]+ControlDefinition::[^\s]+\(\) should return [^\s]+\|null but returns mixed.$#'
tmpDir: .phpstan
Loading