diff --git a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php index 719869c..658481e 100644 --- a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php +++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php @@ -530,6 +530,7 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor return [ '#theme_wrappers' => ['container'], '#attributes' => ['class' => $classes], + '#prefix' => '
' . $this->getCardinalityMessage($entities) . '
', 'items' => array_map( function (ContentEntityInterface $entity, $row_id) use ($field_widget_display, $details_id, $field_parents, $replace_button_access) { $display = $field_widget_display->view($entity); @@ -614,6 +615,42 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor ]; } + /** + * Generates a message informing the user how many more items they can choose. + * + * @param array|int $selected + * The current selections, or how many items are selected. + * + * @return string + * A message informing the user who many more items they can select. + */ + protected function getCardinalityMessage($selected) { + $message = NULL; + + $storage = $this->fieldDefinition->getFieldStorageDefinition(); + $cardinality = $storage->getCardinality(); + $target_type = $storage->getSetting('target_type'); + $target_type = $this->entityTypeManager->getDefinition($target_type); + + if (is_array($selected)) { + $selected = count($selected); + } + + if ($cardinality === 1 && $selected === 0) { + $message = t('You can select one @entity_type.', [ + '@entity_type' => $target_type->getSingularLabel(), + ]); + } + elseif ($cardinality >= $selected) { + $message = t('You can select up to @maximum @entity_type (@remaining left).', [ + '@maximum' => $cardinality, + '@entity_type' => $target_type->getPluralLabel(), + '@remaining' => $cardinality - $selected, + ]); + } + return (string) $message; + } + /** * Gets data that should persist across Entity Browser renders. * diff --git a/src/Plugin/Field/FieldWidget/FileBrowserWidget.php b/src/Plugin/Field/FieldWidget/FileBrowserWidget.php index d883f15..cde6471 100644 --- a/src/Plugin/Field/FieldWidget/FileBrowserWidget.php +++ b/src/Plugin/Field/FieldWidget/FileBrowserWidget.php @@ -217,6 +217,7 @@ class FileBrowserWidget extends EntityReferenceBrowserWidget { $current = [ '#type' => 'table', '#empty' => $this->t('No files yet'), + '#prefix' => '' . $this->getCardinalityMessage($entities) . '
', '#attributes' => ['class' => ['entities-list']], '#tabledrag' => [ [ diff --git a/tests/src/FunctionalJavascript/EntityBrowserTest.php b/tests/src/FunctionalJavascript/EntityBrowserTest.php index 857d2c1..2f0f8c3 100644 --- a/tests/src/FunctionalJavascript/EntityBrowserTest.php +++ b/tests/src/FunctionalJavascript/EntityBrowserTest.php @@ -50,6 +50,72 @@ class EntityBrowserTest extends EntityBrowserJavascriptTestBase { $this->assertSession()->linkExists('Select entities'); } + /** + * Tests the field widget with a single-cardinality field. + */ + public function testSingleCardinalityField() { + $this->container->get('entity_type.manager') + ->getStorage('field_storage_config') + ->load('node.field_reference') + ->setCardinality(1) + ->save(); + + // Create a file. + $image = $this->createFile('llama'); + + $this->drupalGet('node/add/article'); + + $this->assertSession()->linkExists('Select entities'); + $this->assertSession()->pageTextContains('You can select one file.'); + $this->getSession()->getPage()->clickLink('Select entities'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_test_entity_browser_file'); + + $this->getSession()->getPage()->checkField('entity_browser_select[file:' . $image->id() . ']'); + $this->getSession()->getPage()->pressButton('Select entities'); + + // Switch back to the main page. + $this->getSession()->switchToIFrame(); + $this->waitForAjaxToFinish(); + // A selection has been made, so the message is no longer necessary. + $this->assertSession()->pageTextNotContains('You can select one file.'); + } + + /** + * Tests the field widget with a multi-cardinality field. + */ + public function testMultiCardinalityField() { + $this->container->get('entity_type.manager') + ->getStorage('field_storage_config') + ->load('node.field_reference') + ->setCardinality(3) + ->save(); + + // Create a few files to choose. + $images = []; + array_push($images, $this->createFile('llama')); + array_push($images, $this->createFile('sloth')); + array_push($images, $this->createFile('puppy')); + + $this->drupalGet('node/add/article'); + + $this->assertSession()->linkExists('Select entities'); + $this->assertSession()->pageTextContains('You can select up to 3 file entities (3 left).'); + $this->getSession()->getPage()->clickLink('Select entities'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_test_entity_browser_file'); + + $this->getSession()->getPage()->checkField('entity_browser_select[file:' . $images[0]->id() . ']'); + $this->getSession()->getPage()->checkField('entity_browser_select[file:' . $images[1]->id() . ']'); + $this->getSession()->getPage()->pressButton('Select entities'); + + // Switch back to the main page. + $this->getSession()->switchToIFrame(); + $this->waitForAjaxToFinish(); + // Selections have been made, so the message should be different. + $this->assertSession()->pageTextContains('You can select up to 3 file entities (1 left).'); + } + /** * Tests tabs widget selector. */