From dfe40772e3afcdae528479032f78c4b734cb7fce Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Mon, 10 Nov 2025 18:18:52 +0530 Subject: [PATCH 1/9] Paragraphs simple edit module. --- ...form_display.node.landing_page.default.yml | 11 +- config/sync/core.extension.yml | 1 + .../css/paragraphs_simple_edit.claro.css | 14 ++ .../paragraphs_simple_edit.info.yml | 8 + .../paragraphs_simple_edit.libraries.yml | 5 + .../paragraphs_simple_edit.routing.yml | 8 + .../ParagraphsSimpleEditController.php | 89 +++++++ .../ParagraphsSimpleEditDefaultWidget.php | 217 ++++++++++++++++++ 8 files changed, 348 insertions(+), 5 deletions(-) create mode 100755 web/modules/custom/paragraphs_simple_edit/css/paragraphs_simple_edit.claro.css create mode 100755 web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml create mode 100755 web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.libraries.yml create mode 100755 web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml create mode 100755 web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php create mode 100755 web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php diff --git a/config/sync/core.entity_form_display.node.landing_page.default.yml b/config/sync/core.entity_form_display.node.landing_page.default.yml index 443f61432..cfb915a35 100644 --- a/config/sync/core.entity_form_display.node.landing_page.default.yml +++ b/config/sync/core.entity_form_display.node.landing_page.default.yml @@ -11,7 +11,7 @@ dependencies: module: - content_moderation - metatag - - paragraphs + - paragraphs_simple_edit - path id: node.landing_page.default targetEntityType: node @@ -40,7 +40,7 @@ content: use_details: true third_party_settings: { } field_paragraphs: - type: paragraphs + type: paragraphs_simple_edit_default weight: 10 region: content settings: @@ -49,14 +49,15 @@ content: edit_mode: closed closed_mode: summary autocollapse: none - closed_mode_threshold: 0 + closed_mode_threshold: '0' add_mode: dropdown form_display_mode: default default_paragraph_type: _none features: - add_above: '0' - collapse_edit_all: collapse_edit_all duplicate: duplicate + collapse_edit_all: collapse_edit_all + add_above: 0 + convert: 0 third_party_settings: { } langcode: type: language_select diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index c1968f6f3..2c1922e9e 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -69,6 +69,7 @@ module: page_cache: 0 pantheon_advanced_page_cache: 0 paragraphs_edit: 0 + paragraphs_simple_edit: 0 password_policy_blacklist: 0 password_policy_character_types: 0 password_policy_length: 0 diff --git a/web/modules/custom/paragraphs_simple_edit/css/paragraphs_simple_edit.claro.css b/web/modules/custom/paragraphs_simple_edit/css/paragraphs_simple_edit.claro.css new file mode 100755 index 000000000..b5ea4c75a --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/css/paragraphs_simple_edit.claro.css @@ -0,0 +1,14 @@ +/* Claro theme automatically adds the .button--small class, which introduces + extra margin and padding. These overrides remove the unwanted spacing + applied to the link wrapper. */ +.paragraph-simple-edit--add-button.dropbutton.button--small { + margin: 0; + padding: 0; +} + +/* Claro theme applies certain admin styles (e.g., accordion, tabs) based on + class names. These overrides ensure paragraph widgets display correctly + regardless of their type. */ +.paragraph-simple-edit--add-button li { + box-shadow: none; +} diff --git a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml new file mode 100755 index 000000000..223f3bb1f --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml @@ -0,0 +1,8 @@ +name: 'Paragraphs Simple Edit' +type: module +description: 'Provides a paragraphs field widget that allows editing, adding, and deleting paragraphs on dedicated pages.' +package: Paragraphs +core_version_requirement: ^10 || ^11 +dependencies: + - drupal:field + - paragraphs:paragraphs diff --git a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.libraries.yml b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.libraries.yml new file mode 100755 index 000000000..d0716d506 --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.libraries.yml @@ -0,0 +1,5 @@ +widget.claro: + version: 1.x + css: + theme: + css/paragraphs_simple_edit.claro.css: {} diff --git a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml new file mode 100755 index 000000000..be3e0e04b --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml @@ -0,0 +1,8 @@ +paragraphs_simple_edit.add_form: + path: '/paragraphs-simple-edit/{root_parent_type}/{root_parent}/paragraph/{bundle}/add' + defaults: + _controller: '\Drupal\paragraphs_simple_edit\Controller\ParagraphsSimpleEditController::add' + requirements: + root_parent: \d+ + options: + _admin_route: TRUE diff --git a/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php b/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php new file mode 100755 index 000000000..3e34daecd --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php @@ -0,0 +1,89 @@ +entityTypeManager->getStorage($host_entity_type); + // if (!$entity_storage) { + // throw new NotFoundHttpException("Unknown host entity type: $host_entity_type"); + // } + + // $host = $entity_storage->load($host_entity); + // if (!$host) { + // throw new NotFoundHttpException("Host entity not found: $host_entity_type $host_entity"); + // } + + // // Ensure the current user can view the host (basic protection). + // if (!$host->access('view')) { + // throw new NotFoundHttpException("Host entity not accessible."); + // } + + // Create a paragraph entity of the requested bundle (unsaved). + //$paragraph_storage = $this->entityTypeManager->getStorage('paragraph'); + // if (!$paragraph_storage) { + // // Paragraph module not enabled or entity type missing. + // throw new NotFoundHttpException("Paragraph entity type is not available on this site."); + // } + + //$paragraph = $paragraph_storage->create(['type' => $paragraph_type]); + + // Access check: can current user create this paragraph bundle? + // if (!$paragraph->access('create')) { + // // Use access denied rather than 404 if you prefer. + // return $this->accessDenied(); + // } + + // OPTIONAL / HELPFUL: If you want the paragraph form to know which host it + // will be attached to (so you can attach in the save path), you can add + // these as form state values or as query parameters. Here we inject them + // as #attached form build info (a small, non-invasive approach): + // + // The form can read these values in a hook_form_alter or in a custom form + // submit handler. + // $paragraph->parent_type = $host_entity_type; + // $paragraph->parent_id = $host_entity; + + // Render and return the paragraph entity add form. + // Use the standard entity form builder. We present the default form mode. + //$form = $this->entityFormBuilder->getForm($paragraph, 'add'); + + // Attach the host info as a hidden form element so submit handlers can + // pick it up easily. We must alter the built form structure here. + // Only add if form is an array and not cached markup. + // if (is_array($form)) { + // $form['#cache']['contexts'][] = 'user'; // conservative cache context + // // Add hidden values for downstream submit handlers. + // $form['mymodule_host_info'] = [ + // '#type' => 'value', + // '#value' => [ + // 'host_entity_type' => $host_entity_type, + // 'host_entity_id' => $host_entity, + // ], + // ]; + // } + + $form['test'] = [ + '#markup' => 'test', + ]; + + return $form; + } + +} diff --git a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php new file mode 100755 index 000000000..f4f49b58f --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php @@ -0,0 +1,217 @@ +entityTypeManager = $entity_type_manager; + $this->themeManager = $theme_manager; + $this->redirectDestination = $redirect_destination; + parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings, $entity_field_manager); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $plugin_id, + $plugin_definition, + $configuration['field_definition'], + $configuration['settings'], + $configuration['third_party_settings'], + $container->get('entity_field.manager'), + $container->get('entity_type.manager'), + $container->get('theme.manager'), + $container->get('redirect.destination') + ); + } + + /** + * {@inheritdoc} + */ + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + $element = parent::formElement($items, $delta, $element, $form, $form_state); + + $paragraph = $items[$delta]->entity; + if (!$paragraph || $paragraph->isNew()) { + return $element; + } + + $host = $items->getEntity(); + + if (!$host->id()) { + // Edit and Delete links require saved paragraph entities with IDs. + // For new host entities, paragraphs are created on form submission. + return $element; + } + + $destination = $this->redirectDestination->getAsArray(); + + $edit_url = Url::fromRoute('paragraphs_edit.edit_form', [ + 'root_parent_type' => $host->getEntityTypeId(), + 'root_parent' => $host->id(), + 'paragraph' => $paragraph->id(), + ], + [ + 'query' => $destination, + ]); + + $delete_url = Url::fromRoute('paragraphs_edit.delete_form', [ + 'root_parent_type' => $host->getEntityTypeId(), + 'root_parent' => $host->id(), + 'paragraph' => $paragraph->id(), + ], + [ + 'query' => $destination, + ]); + + $element['top']['actions']['actions'] = [ + '#type' => 'dropbutton', + '#dropbutton_type' => 'extrasmall', + '#links' => [ + 'edit' => [ + 'title' => $this->t('Edit'), + 'url' => $edit_url, + ], + 'delete' => [ + 'title' => $this->t('Delete'), + 'url' => $delete_url, + ], + ], + '#weight' => 10, + ]; + + return $element; + } + + /** + * {@inheritdoc} + */ + public function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { + $f = get_entity_add_route('paragraph', 'accordion'); + ksm($f); + $elements = parent::formMultipleElements($items, $form, $form_state); + + $field_name = $this->fieldDefinition->getName(); + + if (!isset($elements['add_more'])) { + return $elements; + } + + $bundle_fields = $this->entityFieldManager + ->getFieldDefinitions($items->getEntity()->getEntityTypeId(), $items->getEntity()->bundle()); + + if (!isset($bundle_fields[$field_name])) { + return $elements; + } + + $field_config = $bundle_fields[$field_name]; + $handler_settings = $field_config->getSetting('handler_settings'); + $target_bundles = $handler_settings['target_bundles'] ?? []; + + if (empty($target_bundles)) { + $paragraph_types = $this->entityTypeManager + ->getStorage('paragraphs_type') + ->loadMultiple(); + $target_bundles = array_keys($paragraph_types); + } + + $add_links = []; + foreach ($target_bundles as $bundle) { + $paragraph_type = $this->entityTypeManager + ->getStorage('paragraphs_type') + ->load($bundle); + + if (!$paragraph_type) { + continue; + } + + $add_links[$bundle] = [ + 'title' => $this->t('Add @type', ['@type' => $paragraph_type->label()]), + 'url' => Url::fromRoute(''), + ]; + } + + if (empty($add_links)) { + return $elements; + } + + $elements['add_more'] = [ + '#type' => 'dropbutton', + '#dropbutton_type' => 'extrasmall', + '#links' => $add_links, + '#attributes' => [ + 'class' => ['paragraph-simple-edit--add-button'] + ], + ]; + + // Add css for claro theme to fix styling for add button. + if ($this->themeManager->getActiveTheme()->getName() == 'claro') { + $elements['add_more']['#attached']['library'][] = 'paragraphs_simple_edit/widget.claro'; + } + + return $elements; + } + +} From 91c120ee6f1ee46496db4e2866d1dc1e1c08017c Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Mon, 10 Nov 2025 18:20:09 +0530 Subject: [PATCH 2/9] Removed debug. --- .../Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php index f4f49b58f..6dc60676f 100755 --- a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php +++ b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php @@ -149,8 +149,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen * {@inheritdoc} */ public function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { - $f = get_entity_add_route('paragraph', 'accordion'); - ksm($f); $elements = parent::formMultipleElements($items, $form, $form_state); $field_name = $this->fieldDefinition->getName(); From 344713bf90faae8693f8b85647897dd9614c3ccf Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Tue, 11 Nov 2025 11:34:27 +0530 Subject: [PATCH 3/9] Paragraph add form. --- .../paragraphs_simple_edit.routing.yml | 13 ++- .../ParagraphsSimpleEditController.php | 100 +++++++----------- 2 files changed, 50 insertions(+), 63 deletions(-) diff --git a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml index be3e0e04b..813abf5b8 100755 --- a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml +++ b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml @@ -1,8 +1,15 @@ -paragraphs_simple_edit.add_form: +paragraphs_simple_edit.add: path: '/paragraphs-simple-edit/{root_parent_type}/{root_parent}/paragraph/{bundle}/add' defaults: - _controller: '\Drupal\paragraphs_simple_edit\Controller\ParagraphsSimpleEditController::add' + _controller: '\Drupal\paragraphs_simple_edit\Controller\ParagraphsSimpleEditController::addParagraph' + _title_callback: '\Drupal\paragraphs_simple_edit\Controller\ParagraphsSimpleEditController::addParagraphTitle' requirements: - root_parent: \d+ + _entity_create_access: 'paragraph:{bundle}' + root_parent_type: '[a-z_]+' + root_parent: '\d+' + bundle: '[a-z0-9_]+' options: _admin_route: TRUE + parameters: + root_parent: + type: entity:{root_parent_type} diff --git a/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php b/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php index 3e34daecd..3c0adec69 100755 --- a/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php +++ b/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php @@ -3,11 +3,10 @@ namespace Drupal\paragraphs_simple_edit\Controller; use Drupal\Core\Controller\ControllerBase; -use Drupal\Core\Session\AccountInterface; -use Drupal\Core\Access\AccessResult; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\paragraphs\ParagraphsTypeInterface; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpFoundation\Request; /** * Controller for showing a Paragraph add form targeted to a host entity. @@ -15,73 +14,54 @@ class ParagraphsSimpleEditController extends ControllerBase { /** - * Show paragraph add form. + * Title callback. */ - public function add($root_parent_type, $root_parent, $bundle, Request $request) { - - // Load host entity storage and the host entity itself. - // $entity_storage = $this->entityTypeManager->getStorage($host_entity_type); - // if (!$entity_storage) { - // throw new NotFoundHttpException("Unknown host entity type: $host_entity_type"); - // } - - // $host = $entity_storage->load($host_entity); - // if (!$host) { - // throw new NotFoundHttpException("Host entity not found: $host_entity_type $host_entity"); - // } + public function addParagraphTitle(string $root_parent_type, ContentEntityInterface $root_parent, string $bundle) { + $paragraph_type = $this->entityTypeManager()->getStorage('paragraphs_type')->load($bundle); + if ($paragraph_type instanceof ParagraphsTypeInterface) { + return $this->t('Add new @bundle', [ + '@bundle' => $paragraph_type->label(), + ]); + } + return $this->t('Add new paragraph'); + } - // // Ensure the current user can view the host (basic protection). - // if (!$host->access('view')) { - // throw new NotFoundHttpException("Host entity not accessible."); - // } + /** + * Show paragraph add form. + */ + public function addParagraph(string $root_parent_type, ContentEntityInterface $root_parent, string $bundle) { + // Load parent entity storage & the parent entity itself. + $entity_storage = $this->entityTypeManager()->getStorage($root_parent_type); + if (!$entity_storage) { + throw new NotFoundHttpException("Unknown parent entity type: $root_parent_type"); + } + + // Ensure the current user can view the parent (basic protection). + if (!$root_parent->access('view')) { + throw new AccessDeniedHttpException("Parent entity not accessible."); + } + + $paragraph_type = $this->entityTypeManager()->getStorage('paragraphs_type')->load($bundle); + if (!($paragraph_type instanceof ParagraphsTypeInterface)) { + throw new NotFoundHttpException("Paragraph bundle not found: $bundle"); + } // Create a paragraph entity of the requested bundle (unsaved). - //$paragraph_storage = $this->entityTypeManager->getStorage('paragraph'); - // if (!$paragraph_storage) { - // // Paragraph module not enabled or entity type missing. - // throw new NotFoundHttpException("Paragraph entity type is not available on this site."); - // } + $paragraph_storage = $this->entityTypeManager()->getStorage('paragraph'); - //$paragraph = $paragraph_storage->create(['type' => $paragraph_type]); + $paragraph = $paragraph_storage->create(['type' => $bundle]); // Access check: can current user create this paragraph bundle? - // if (!$paragraph->access('create')) { - // // Use access denied rather than 404 if you prefer. - // return $this->accessDenied(); - // } + if (!$paragraph->access('create')) { + throw new AccessDeniedHttpException("Access denied while creating paragraph of type $bundle."); + } - // OPTIONAL / HELPFUL: If you want the paragraph form to know which host it - // will be attached to (so you can attach in the save path), you can add - // these as form state values or as query parameters. Here we inject them - // as #attached form build info (a small, non-invasive approach): - // - // The form can read these values in a hook_form_alter or in a custom form - // submit handler. - // $paragraph->parent_type = $host_entity_type; - // $paragraph->parent_id = $host_entity; + $paragraph->set('parent_type', $root_parent_type); + $paragraph->set('parent_id', $root_parent); // Render and return the paragraph entity add form. // Use the standard entity form builder. We present the default form mode. - //$form = $this->entityFormBuilder->getForm($paragraph, 'add'); - - // Attach the host info as a hidden form element so submit handlers can - // pick it up easily. We must alter the built form structure here. - // Only add if form is an array and not cached markup. - // if (is_array($form)) { - // $form['#cache']['contexts'][] = 'user'; // conservative cache context - // // Add hidden values for downstream submit handlers. - // $form['mymodule_host_info'] = [ - // '#type' => 'value', - // '#value' => [ - // 'host_entity_type' => $host_entity_type, - // 'host_entity_id' => $host_entity, - // ], - // ]; - // } - - $form['test'] = [ - '#markup' => 'test', - ]; + $form = $this->entityFormBuilder()->getForm($paragraph, 'default'); return $form; } From 3f1293c255b6eba849afd8509a97fcfbb42cc998 Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Tue, 11 Nov 2025 13:02:13 +0530 Subject: [PATCH 4/9] Use route from paragraphs_modal_add. --- composer.json | 1 + composer.lock | 47 ++++++++++++- .../paragraphs_simple_edit.info.yml | 3 +- .../paragraphs_simple_edit.routing.yml | 15 ---- .../ParagraphsSimpleEditController.php | 69 ------------------- .../ParagraphsSimpleEditDefaultWidget.php | 19 ++++- 6 files changed, 66 insertions(+), 88 deletions(-) delete mode 100755 web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml delete mode 100755 web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php diff --git a/composer.json b/composer.json index 5959cd8fc..ef2ac2039 100644 --- a/composer.json +++ b/composer.json @@ -64,6 +64,7 @@ "drupal/pantheon_advanced_page_cache": "^2.2", "drupal/paragraphs": "^1.17", "drupal/paragraphs_edit": "^3.0", + "drupal/paragraphs_modal_add": "^1.0", "drupal/password_policy": "^4.0", "drupal/pathauto": "^1.12", "drupal/permissions_filter": "^1.3", diff --git a/composer.lock b/composer.lock index 54e0080c5..bdd089664 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ae38a3555fbcbcf247f26284a80b5560", + "content-hash": "34441132215979ad800d651e2cf98bc4", "packages": [ { "name": "asm89/stack-cors", @@ -4676,6 +4676,51 @@ "source": "https://git.drupalcode.org/project/paragraphs_edit" } }, + { + "name": "drupal/paragraphs_modal_add", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/paragraphs_modal_add.git", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/paragraphs_modal_add-1.0.0.zip", + "reference": "1.0.0", + "shasum": "7f4819c4168a85fae204956691afd9526acced8b" + }, + "require": { + "drupal/core": "^10 | ^11", + "drupal/paragraphs": "*" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "1.0.0", + "datestamp": "1744336309", + "security-coverage": { + "status": "not-covered", + "message": "Project has not opted into security advisory coverage!" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "ruby232", + "homepage": "https://www.drupal.org/user/2604126" + } + ], + "description": "Allows users to add paragraphs.", + "homepage": "https://www.drupal.org/project/paragraphs_modal_add", + "support": { + "source": "https://git.drupalcode.org/project/paragraphs_modal_add" + } + }, { "name": "drupal/password_policy", "version": "4.0.3", diff --git a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml index 223f3bb1f..76c5e8ecb 100755 --- a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml +++ b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml @@ -4,5 +4,4 @@ description: 'Provides a paragraphs field widget that allows editing, adding, an package: Paragraphs core_version_requirement: ^10 || ^11 dependencies: - - drupal:field - - paragraphs:paragraphs + - paragraphs_modal_add:paragraphs_modal_add diff --git a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml deleted file mode 100755 index 813abf5b8..000000000 --- a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.routing.yml +++ /dev/null @@ -1,15 +0,0 @@ -paragraphs_simple_edit.add: - path: '/paragraphs-simple-edit/{root_parent_type}/{root_parent}/paragraph/{bundle}/add' - defaults: - _controller: '\Drupal\paragraphs_simple_edit\Controller\ParagraphsSimpleEditController::addParagraph' - _title_callback: '\Drupal\paragraphs_simple_edit\Controller\ParagraphsSimpleEditController::addParagraphTitle' - requirements: - _entity_create_access: 'paragraph:{bundle}' - root_parent_type: '[a-z_]+' - root_parent: '\d+' - bundle: '[a-z0-9_]+' - options: - _admin_route: TRUE - parameters: - root_parent: - type: entity:{root_parent_type} diff --git a/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php b/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php deleted file mode 100755 index 3c0adec69..000000000 --- a/web/modules/custom/paragraphs_simple_edit/src/Controller/ParagraphsSimpleEditController.php +++ /dev/null @@ -1,69 +0,0 @@ -entityTypeManager()->getStorage('paragraphs_type')->load($bundle); - if ($paragraph_type instanceof ParagraphsTypeInterface) { - return $this->t('Add new @bundle', [ - '@bundle' => $paragraph_type->label(), - ]); - } - return $this->t('Add new paragraph'); - } - - /** - * Show paragraph add form. - */ - public function addParagraph(string $root_parent_type, ContentEntityInterface $root_parent, string $bundle) { - // Load parent entity storage & the parent entity itself. - $entity_storage = $this->entityTypeManager()->getStorage($root_parent_type); - if (!$entity_storage) { - throw new NotFoundHttpException("Unknown parent entity type: $root_parent_type"); - } - - // Ensure the current user can view the parent (basic protection). - if (!$root_parent->access('view')) { - throw new AccessDeniedHttpException("Parent entity not accessible."); - } - - $paragraph_type = $this->entityTypeManager()->getStorage('paragraphs_type')->load($bundle); - if (!($paragraph_type instanceof ParagraphsTypeInterface)) { - throw new NotFoundHttpException("Paragraph bundle not found: $bundle"); - } - - // Create a paragraph entity of the requested bundle (unsaved). - $paragraph_storage = $this->entityTypeManager()->getStorage('paragraph'); - - $paragraph = $paragraph_storage->create(['type' => $bundle]); - - // Access check: can current user create this paragraph bundle? - if (!$paragraph->access('create')) { - throw new AccessDeniedHttpException("Access denied while creating paragraph of type $bundle."); - } - - $paragraph->set('parent_type', $root_parent_type); - $paragraph->set('parent_id', $root_parent); - - // Render and return the paragraph entity add form. - // Use the standard entity form builder. We present the default form mode. - $form = $this->entityFormBuilder()->getForm($paragraph, 'default'); - - return $form; - } - -} diff --git a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php index 6dc60676f..737015a61 100755 --- a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php +++ b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php @@ -157,6 +157,13 @@ public function formMultipleElements(FieldItemListInterface $items, array &$form return $elements; } + $host = $items->getEntity(); + + if (!$host->id()) { + // Add links require host entity ids. + return $elements; + } + $bundle_fields = $this->entityFieldManager ->getFieldDefinitions($items->getEntity()->getEntityTypeId(), $items->getEntity()->bundle()); @@ -175,6 +182,8 @@ public function formMultipleElements(FieldItemListInterface $items, array &$form $target_bundles = array_keys($paragraph_types); } + $destination = $this->redirectDestination->getAsArray(); + $add_links = []; foreach ($target_bundles as $bundle) { $paragraph_type = $this->entityTypeManager @@ -187,7 +196,15 @@ public function formMultipleElements(FieldItemListInterface $items, array &$form $add_links[$bundle] = [ 'title' => $this->t('Add @type', ['@type' => $paragraph_type->label()]), - 'url' => Url::fromRoute(''), + 'url' => Url::fromRoute('paragraphs_modal_add.add_form', [ + 'root_parent_type' => $host->getEntityTypeId(), + 'root_parent' => $host->id(), + 'parent_field_name' => $field_name, + 'paragraphs_type' => $bundle, + ], + [ + 'query' => $destination, + ]), ]; } From 9e10dcc5bf4020aabede50a60ad00d50f9c1d49a Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Tue, 11 Nov 2025 15:26:59 +0530 Subject: [PATCH 5/9] Settings & other changes. --- .../ParagraphsSimpleEditDefaultWidget.php | 95 ++++++++++++++++++- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php index 737015a61..5a401b751 100755 --- a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php +++ b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php @@ -24,7 +24,7 @@ description: new TranslatableMarkup('The paragraphs form that allows editing, adding, and deleting paragraphs on dedicated pages.'), field_types: ['entity_reference_revisions'] )] -class ParagraphsSimpleEditDefaultWidget extends ParagraphsWidget { +final class ParagraphsSimpleEditDefaultWidget extends ParagraphsWidget { /** * The entity type manager. @@ -37,10 +37,19 @@ class ParagraphsSimpleEditDefaultWidget extends ParagraphsWidget { protected ThemeManagerInterface $themeManager; /** - * The redirect destination + * The redirect destination. */ protected RedirectDestinationInterface $redirectDestination; + /** + * The settings that are no longer necessary. + */ + protected static array $unusedSettings = [ + 'autocollapse' => 'none', + 'closed_mode_threshold' => 0, + 'add_mode' => 'dropdown', + ]; + /** * Constructs a ParagraphsSimpleEditDefaultWidget object. * @@ -87,13 +96,85 @@ public static function create(ContainerInterface $container, array $configuratio ); } + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + $defaults = parent::defaultSettings(); + // Set default values for unused settings. We are not removing these + // to avoid any problems when parent widget is being used. + foreach (static::$unusedSettings as $element_key => $element_value) { + $defaults[$element_key] = $element_value; + } + return $defaults; + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $elements = parent::settingsForm($form, $form_state); + // Instead of removing these elements, we are just hiding those + // to avoid any problems when parent widget is being used. + foreach (static::$unusedSettings as $element_key => $element_value) { + $elements[$element_key]['#access'] = FALSE; + } + + // Form display mode is only useful when edit_mode is open. + if (!empty($elements['form_display_mode'])) { + $elements['form_display_mode']['#states'] = [ + 'visible' => [ + 'select[name="fields[' . $this->fieldDefinition->getName() . '][settings_edit_form][settings][edit_mode]"]' => ['value' => 'open'], + ], + ]; + } + return $elements; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = []; + $summary[] = $this->t('Title: @title', ['@title' => $this->getSetting('title')]); + $summary[] = $this->t('Plural title: @title_plural', [ + '@title_plural' => $this->getSetting('title_plural'), + ]); + + $edit_mode = $this->getSettingOptions('edit_mode')[$this->getSetting('edit_mode')]; + $closed_mode = $this->getSettingOptions('closed_mode')[$this->getSetting('closed_mode')]; + + $summary[] = $this->t('Edit mode: @edit_mode', ['@edit_mode' => $edit_mode]); + $summary[] = $this->t('Closed mode: @closed_mode', ['@closed_mode' => $closed_mode]); + + if ($this->getSetting('edit_mode') == 'open') { + $summary[] = $this->t('Form display mode: @form_display_mode', [ + '@form_display_mode' => $this->getSetting('form_display_mode'), + ]); + } + + if ($this->getDefaultParagraphTypeLabelName() !== NULL) { + $summary[] = $this->t('Default paragraph type: @default_paragraph_type', [ + '@default_paragraph_type' => $this->getDefaultParagraphTypeLabelName(), + ]); + } + $features_labels = array_intersect_key($this->getSettingOptions('features'), array_filter($this->getSetting('features'))); + if (!empty($features_labels)) { + $summary[] = $this->t('Features: @features', ['@features' => implode(', ', $features_labels)]); + } + + return $summary; + } + /** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $element = parent::formElement($items, $delta, $element, $form, $form_state); - $paragraph = $items[$delta]->entity; + /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $item */ + $item = $items[$delta]; + $paragraph = $item->entity; if (!$paragraph || $paragraph->isNew()) { return $element; } @@ -106,6 +187,12 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen return $element; } + // Links shouldn't be there when edit mode is set to open. + $edit_mode = $this->getSetting('edit_mode'); + if ($edit_mode == 'open') { + return $element; + } + $destination = $this->redirectDestination->getAsArray(); $edit_url = Url::fromRoute('paragraphs_edit.edit_form', [ @@ -217,7 +304,7 @@ public function formMultipleElements(FieldItemListInterface $items, array &$form '#dropbutton_type' => 'extrasmall', '#links' => $add_links, '#attributes' => [ - 'class' => ['paragraph-simple-edit--add-button'] + 'class' => ['paragraph-simple-edit--add-button'], ], ]; From f004250b999861e89eaf74d2633cc8a01cf86c7a Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Tue, 11 Nov 2025 17:54:33 +0530 Subject: [PATCH 6/9] Added test. --- config/sync/core.extension.yml | 1 + .../paragraphs_simple_edit.info.yml | 1 + .../ParagraphsSimpleEditDefaultWidget.php | 3 + .../ParagraphsSimpleEditLinksTest.php | 66 +++++++++++++++++++ .../ParagraphsSimpleEditTestBase.php | 26 ++++++++ 5 files changed, 97 insertions(+) create mode 100644 web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php create mode 100644 web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index 2c1922e9e..85699f84b 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -69,6 +69,7 @@ module: page_cache: 0 pantheon_advanced_page_cache: 0 paragraphs_edit: 0 + paragraphs_modal_add: 0 paragraphs_simple_edit: 0 password_policy_blacklist: 0 password_policy_character_types: 0 diff --git a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml index 76c5e8ecb..adcce6789 100755 --- a/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml +++ b/web/modules/custom/paragraphs_simple_edit/paragraphs_simple_edit.info.yml @@ -4,4 +4,5 @@ description: 'Provides a paragraphs field widget that allows editing, adding, an package: Paragraphs core_version_requirement: ^10 || ^11 dependencies: + - paragraphs_edit:paragraphs_edit - paragraphs_modal_add:paragraphs_modal_add diff --git a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php index 5a401b751..f4198dea2 100755 --- a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php +++ b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php @@ -290,6 +290,9 @@ public function formMultipleElements(FieldItemListInterface $items, array &$form 'paragraphs_type' => $bundle, ], [ + 'attributes' => [ + 'class' => ['paragraphs-simple-edit-add-link'], + ], 'query' => $destination, ]), ]; diff --git a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php new file mode 100644 index 000000000..f972ab23b --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php @@ -0,0 +1,66 @@ +loginAsAdmin(); + // Add two Paragraph types. + $this->addParagraphsType('btext'); + $this->addParagraphsType('dtext'); + + $content_type = 'paragraphed_test'; + $paragraph_field_name = 'paragraphs'; + + $this->addParagraphedContentType($content_type, $paragraph_field_name); + $node = $this->createNode(['type' => $content_type]); + // Enter to the field config since the weight is set through the form. + $this->drupalGet('admin/structure/types/manage/paragraphed_test/fields/node.' . $content_type . '.' . $paragraph_field_name); + $this->submitForm([], 'Save settings'); + + $settings = [ + 'edit_mode' => 'closed', + ]; + $this->setSimpleEditWidget($content_type, $paragraph_field_name, $settings); + + $this->assertAddLinks(['Add btext', 'Add dtext'], $node); + + $this->addParagraphsType('atext'); + $this->assertAddLinks(['Add btext', 'Add dtext', 'Add atext'], $node); + + $this->setParagraphsTypeWeight($content_type, 'dtext', 2, $paragraph_field_name); + $this->assertAddLinks(['Add dtext', 'Add btext', 'Add atext'], $node); + + $this->setAllowedParagraphsTypes($content_type, ['dtext', 'atext'], TRUE, $paragraph_field_name); + $this->assertAddLinks(['Add dtext', 'Add atext'], $node); + + $this->setParagraphsTypeWeight($content_type, 'atext', 1, $paragraph_field_name); + $this->assertAddLinks(['Add atext', 'Add dtext'], $node); + + $this->setAllowedParagraphsTypes($content_type, ['atext', 'dtext', 'btext'], TRUE, $paragraph_field_name); + $this->assertAddLinks(['Add atext', 'Add dtext', 'Add btext'], $node); + } + + /** + * Asserts order and quantity of add links. + */ + protected function assertAddLinks($options, $node) { + $this->drupalGet('node/' . $node->id() . '/edit'); + $links = $this->xpath('//a[@class="paragraphs-simple-edit-add-link"]'); + // Check if the buttons are in the same order as the given array. + foreach ($links as $key => $link) { + $this->assertEquals($link->getValue(), $options[$key]); + } + $this->assertEquals(count($links), count($options), 'The amount of drop down links matches with the given array'); + } + +} diff --git a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php new file mode 100644 index 000000000..7425f54c3 --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php @@ -0,0 +1,26 @@ +setComponent($paragraphs_field, [ + 'type' => 'paragraphs_simple_edit_default', + 'settings' => $settings, + ]); + $form_display->save(); + } + +} From 98ae9142f141dd0e46ec9e5c52574751325474a3 Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Wed, 12 Nov 2025 13:22:48 +0530 Subject: [PATCH 7/9] 783 : Working tests with other required changes. --- .../schema/paragraphs_simple_edit.schema.yml | 36 +++++++++++++++++++ .../ParagraphsSimpleEditDefaultWidget.php | 23 ++++++------ .../ParagraphsSimpleEditLinksTest.php | 36 ++++++++++++++----- .../ParagraphsSimpleEditTestBase.php | 34 ++++++++++++++++-- 4 files changed, 105 insertions(+), 24 deletions(-) create mode 100755 web/modules/custom/paragraphs_simple_edit/config/schema/paragraphs_simple_edit.schema.yml diff --git a/web/modules/custom/paragraphs_simple_edit/config/schema/paragraphs_simple_edit.schema.yml b/web/modules/custom/paragraphs_simple_edit/config/schema/paragraphs_simple_edit.schema.yml new file mode 100755 index 000000000..643cae82f --- /dev/null +++ b/web/modules/custom/paragraphs_simple_edit/config/schema/paragraphs_simple_edit.schema.yml @@ -0,0 +1,36 @@ +field.widget.settings.paragraphs_simple_edit_default: + type: mapping + label: 'Paragraphs simple edit widget settings' + mapping: + title: + type: string + label: 'Title' + title_plural: + type: string + label: 'Title plural' + edit_mode: + type: string + label: 'Edit mode' + closed_mode: + type: string + label: 'Closed mode' + autocollapse: + type: string + label: 'Autocollapse' + closed_mode_threshold: + type: integer + label: 'Closed mode threshold' + add_mode: + type: string + label: 'Add mode' + form_display_mode: + type: string + label: 'Form display mode' + default_paragraph_type: + type: string + label: 'Default paragraph type' + features: + type: sequence + label: 'Features' + sequence: + type: string diff --git a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php index f4198dea2..7d32b7e34 100755 --- a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php +++ b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php @@ -202,6 +202,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ], [ 'query' => $destination, + 'attributes' => [ + 'class' => ['paragraphs-simple-edit-edit-link'], + ], ]); $delete_url = Url::fromRoute('paragraphs_edit.delete_form', [ @@ -211,6 +214,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ], [ 'query' => $destination, + 'attributes' => [ + 'class' => ['paragraphs-simple-edit-delete-link'], + ], ]); $element['top']['actions']['actions'] = [ @@ -252,31 +258,22 @@ public function formMultipleElements(FieldItemListInterface $items, array &$form } $bundle_fields = $this->entityFieldManager - ->getFieldDefinitions($items->getEntity()->getEntityTypeId(), $items->getEntity()->bundle()); + ->getFieldDefinitions($host->getEntityTypeId(), $host->bundle()); if (!isset($bundle_fields[$field_name])) { return $elements; } - $field_config = $bundle_fields[$field_name]; - $handler_settings = $field_config->getSetting('handler_settings'); - $target_bundles = $handler_settings['target_bundles'] ?? []; - - if (empty($target_bundles)) { - $paragraph_types = $this->entityTypeManager - ->getStorage('paragraphs_type') - ->loadMultiple(); - $target_bundles = array_keys($paragraph_types); - } + $target_bundles = $this->getAccessibleOptions(); $destination = $this->redirectDestination->getAsArray(); $add_links = []; - foreach ($target_bundles as $bundle) { + foreach ($target_bundles as $bundle => $bundle_label) { $paragraph_type = $this->entityTypeManager ->getStorage('paragraphs_type') ->load($bundle); - + // Additional check to make sure paragraph type exists. if (!$paragraph_type) { continue; } diff --git a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php index f972ab23b..e584e19b1 100644 --- a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php +++ b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php @@ -2,7 +2,7 @@ namespace Drupal\Tests\paragraphs_simple_edit\Functional\WidgetSimpleEdit; -use Drupal\paragraphs\Entity\ParagraphsType; +use Drupal\Core\Entity\ContentEntityInterface; /** * Tests paragraphs simple edit links. @@ -10,9 +10,9 @@ class ParagraphsSimpleEditLinksTest extends ParagraphsSimpleEditTestBase { /** - * Tests the add dropdown links. + * Tests the dropdown links. */ - public function testDropDownAddLinks() { + public function testDropDownLinks() { $this->loginAsAdmin(); // Add two Paragraph types. $this->addParagraphsType('btext'); @@ -27,9 +27,7 @@ public function testDropDownAddLinks() { $this->drupalGet('admin/structure/types/manage/paragraphed_test/fields/node.' . $content_type . '.' . $paragraph_field_name); $this->submitForm([], 'Save settings'); - $settings = [ - 'edit_mode' => 'closed', - ]; + $settings = ['edit_mode' => 'closed']; $this->setSimpleEditWidget($content_type, $paragraph_field_name, $settings); $this->assertAddLinks(['Add btext', 'Add dtext'], $node); @@ -48,19 +46,39 @@ public function testDropDownAddLinks() { $this->setAllowedParagraphsTypes($content_type, ['atext', 'dtext', 'btext'], TRUE, $paragraph_field_name); $this->assertAddLinks(['Add atext', 'Add dtext', 'Add btext'], $node); + + // Create paragraphs & add to the node. + foreach (['atext', 'btext'] as $type) { + $this->addParagraphToEntity($type, $node, $paragraph_field_name); + } + $this->assertActionLinks(2, $node); + + $this->addParagraphToEntity('dtext', $node, $paragraph_field_name); + $this->assertActionLinks(3, $node); } /** * Asserts order and quantity of add links. */ - protected function assertAddLinks($options, $node) { + protected function assertAddLinks(array $expected_links, ContentEntityInterface $node) { $this->drupalGet('node/' . $node->id() . '/edit'); $links = $this->xpath('//a[@class="paragraphs-simple-edit-add-link"]'); // Check if the buttons are in the same order as the given array. foreach ($links as $key => $link) { - $this->assertEquals($link->getValue(), $options[$key]); + $this->assertEquals($link->getText(), $expected_links[$key]); } - $this->assertEquals(count($links), count($options), 'The amount of drop down links matches with the given array'); + $this->assertEquals(count($links), count($expected_links), 'The amount of drop down links matches with the given array'); + } + + /** + * Asserts quantity of edit/delete links. + */ + protected function assertActionLinks(int $count, ContentEntityInterface $node) { + $this->drupalGet('node/' . $node->id() . '/edit'); + $edit_links = $this->xpath('//a[@class="paragraphs-simple-edit-edit-link"]'); + $this->assertEquals(count($edit_links), $count, 'The amount of edit links matches with the given count'); + $delete_links = $this->xpath('//a[@class="paragraphs-simple-edit-delete-link"]'); + $this->assertEquals(count($delete_links), $count, 'The amount of delete links matches with the given count'); } } diff --git a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php index 7425f54c3..b70e5d90e 100644 --- a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php +++ b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php @@ -2,7 +2,9 @@ namespace Drupal\Tests\paragraphs_simple_edit\Functional\WidgetSimpleEdit; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\paragraphs\Entity\Paragraph; use Drupal\Tests\paragraphs\Functional\WidgetStable\ParagraphsTestBase; /** @@ -10,13 +12,41 @@ */ abstract class ParagraphsSimpleEditTestBase extends ParagraphsTestBase { + /** + * Modules to enable. + * + * @var array + */ + protected static $modules = [ + 'node', + 'paragraphs', + 'field', + 'field_ui', + 'block', + 'paragraphs_test', + 'paragraphs_simple_edit', + ]; + + /** + * Create & add paragraph to the entity. + */ + protected function addParagraphToEntity(string $type, ContentEntityInterface $entity, string $paragraph_field_name) { + $paragraph = Paragraph::create([ + 'type' => $type, + ]); + $paragraph->setParentEntity($entity, $paragraph_field_name); + $paragraph->save(); + // Save the entity as well. + $entity->{$paragraph_field_name}->appendItem($paragraph); + $entity->save(); + } /** * Sets the Paragraphs widget to simple edit. */ - protected function setSimpleEditWidget($content_type, $paragraphs_field, $settings = []) { + protected function setSimpleEditWidget(string $content_type, string $paragraph_field_name, array $settings = []) { $form_display = EntityFormDisplay::load('node.' . $content_type . '.default') - ->setComponent($paragraphs_field, [ + ->setComponent($paragraph_field_name, [ 'type' => 'paragraphs_simple_edit_default', 'settings' => $settings, ]); From a92063b03a1269cb3e0eebac84b9cf531eff10d5 Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Thu, 13 Nov 2025 16:22:49 +0530 Subject: [PATCH 8/9] Added info text for new entities. --- .../ParagraphsSimpleEditDefaultWidget.php | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php index 7d32b7e34..b20f53efe 100755 --- a/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php +++ b/web/modules/custom/paragraphs_simple_edit/src/Plugin/Field/FieldWidget/ParagraphsSimpleEditDefaultWidget.php @@ -251,14 +251,31 @@ public function formMultipleElements(FieldItemListInterface $items, array &$form } $host = $items->getEntity(); + $bundle = $host->bundle(); if (!$host->id()) { - // Add links require host entity ids. + $host_bundle_label = NULL; + $bundle_entity_type = $host->getEntityType()->getBundleEntityType(); + if ($bundle_entity_type) { + $host_bundle = $this->entityTypeManager->getStorage($bundle_entity_type)->load($bundle); + $host_bundle_label = $host_bundle ? $host_bundle->label() : $bundle; + } + else { + // Entity type does not have bundles. + $host_bundle_label = $host->getEntityType()->getLabel(); + } + // For new entities, we will just add a text. + $elements['add_more'] = [ + '#markup' => $this->t('Save the @bundle first to add new @title_plural.', [ + '@bundle' => $host_bundle_label, + '@title_plural' => $this->getSetting('title_plural'), + ]), + ]; return $elements; } $bundle_fields = $this->entityFieldManager - ->getFieldDefinitions($host->getEntityTypeId(), $host->bundle()); + ->getFieldDefinitions($host->getEntityTypeId(), $bundle); if (!isset($bundle_fields[$field_name])) { return $elements; From dc6d61160885294e6985546da629b4405dbddadf Mon Sep 17 00:00:00 2001 From: Tanmay Khedekar Date: Thu, 13 Nov 2025 18:18:08 +0530 Subject: [PATCH 9/9] Added more tests. --- .../ParagraphsSimpleEditLinksTest.php | 86 ++++++++++++++++++- .../ParagraphsSimpleEditTestBase.php | 13 ++- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php index e584e19b1..9c92cae76 100644 --- a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php +++ b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditLinksTest.php @@ -22,13 +22,15 @@ public function testDropDownLinks() { $paragraph_field_name = 'paragraphs'; $this->addParagraphedContentType($content_type, $paragraph_field_name); - $node = $this->createNode(['type' => $content_type]); + // Enter to the field config since the weight is set through the form. - $this->drupalGet('admin/structure/types/manage/paragraphed_test/fields/node.' . $content_type . '.' . $paragraph_field_name); + $this->drupalGet('admin/structure/types/manage/' . $content_type . '/fields/node.' . $content_type . '.' . $paragraph_field_name); $this->submitForm([], 'Save settings'); $settings = ['edit_mode' => 'closed']; - $this->setSimpleEditWidget($content_type, $paragraph_field_name, $settings); + $this->setSimpleEditWidget('node', $content_type, $paragraph_field_name, $settings); + + $node = $this->createNode(['type' => $content_type]); $this->assertAddLinks(['Add btext', 'Add dtext'], $node); @@ -57,6 +59,84 @@ public function testDropDownLinks() { $this->assertActionLinks(3, $node); } + /** + * Tests the widget empty text for node. + */ + public function testWidgetEmptyTextForNode() { + $this->loginAsAdmin(); + // Add two Paragraph types. + $this->addParagraphsType('btext'); + $this->addParagraphsType('dtext'); + + $content_type = 'paragraphed_test'; + $paragraph_field_name = 'paragraphs'; + + $this->addParagraphedContentType($content_type, $paragraph_field_name); + + // Enter to the field config since the weight is set through the form. + $this->drupalGet('admin/structure/types/manage/' . $content_type . '/fields/node.' . $content_type . '.' . $paragraph_field_name); + $this->submitForm([], 'Save settings'); + + $settings = ['edit_mode' => 'closed']; + $this->setSimpleEditWidget('node', $content_type, $paragraph_field_name, $settings); + + // Go to node add page. + $this->drupalGet('node/add/' . $content_type); + + $this->assertSession()->pageTextContains('Save the ' . $content_type . ' first to add new Paragraphs.'); + + // Change paragraphs title text. + $settings = [ + 'title' => 'Item', + 'title_plural' => 'Items', + ]; + $this->setParagraphsWidgetSettings($content_type, $paragraph_field_name, $settings); + + // Go to node add page. + $this->drupalGet('node/add/' . $content_type); + + $this->assertSession()->pageTextContains('Save the ' . $content_type . ' first to add new Items.'); + } + + /** + * Tests the widget empty text for user. + */ + public function testWidgetEmptyTextForUser() { + $this->loginAsAdmin(); + // Add two Paragraph types. + $this->addParagraphsType('btext'); + $this->addParagraphsType('dtext'); + + $paragraph_field_name = 'paragraphs'; + + // Add paragraphs field to user. + $this->addParagraphsField('user', $paragraph_field_name, 'user'); + + // Enter to the field config since the weight is set through the form. + $this->drupalGet('admin/config/people/accounts/fields/user.user.' . $paragraph_field_name); + $this->submitForm([], 'Save settings'); + + $settings = ['edit_mode' => 'closed']; + $this->setSimpleEditWidget('user', 'user', $paragraph_field_name, $settings); + + // Go to user add page. + $this->drupalGet('admin/people/create'); + + $this->assertSession()->pageTextContains('Save the User first to add new Paragraphs.'); + + // Change paragraphs title text. + $settings = [ + 'title' => 'Item', + 'title_plural' => 'Items', + ]; + $this->setParagraphsWidgetSettings('user', $paragraph_field_name, $settings, NULL, 'user'); + + // Go to user add page. + $this->drupalGet('admin/people/create'); + + $this->assertSession()->pageTextContains('Save the User first to add new Items.'); + } + /** * Asserts order and quantity of add links. */ diff --git a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php index b70e5d90e..3eb72cf17 100644 --- a/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php +++ b/web/modules/custom/paragraphs_simple_edit/tests/src/Functional/WidgetSimpleEdit/ParagraphsSimpleEditTestBase.php @@ -27,6 +27,15 @@ abstract class ParagraphsSimpleEditTestBase extends ParagraphsTestBase { 'paragraphs_simple_edit', ]; + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + $this->admin_permissions[] = 'administer user fields'; + $this->admin_permissions[] = 'administer users'; + } + /** * Create & add paragraph to the entity. */ @@ -44,8 +53,8 @@ protected function addParagraphToEntity(string $type, ContentEntityInterface $en /** * Sets the Paragraphs widget to simple edit. */ - protected function setSimpleEditWidget(string $content_type, string $paragraph_field_name, array $settings = []) { - $form_display = EntityFormDisplay::load('node.' . $content_type . '.default') + protected function setSimpleEditWidget(string $entity_type, string $bundle, string $paragraph_field_name, array $settings = []) { + $form_display = EntityFormDisplay::load($entity_type . '.' . $bundle . '.default') ->setComponent($paragraph_field_name, [ 'type' => 'paragraphs_simple_edit_default', 'settings' => $settings,