Loading core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php +34 −0 Original line number Diff line number Diff line Loading @@ -286,6 +286,22 @@ public function form(FieldItemListInterface $items, array &$form, FormStateInter return parent::form($items, $form, $form_state, $get_delta); } /** * {@inheritdoc} */ public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) { parent::extractFormValues($items, $form, $form_state); // Update reference to 'items' stored during add or remove to take into // account changes to values like 'weight' etc. // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::addItems // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::removeItem $field_name = $this->fieldDefinition->getName(); $field_state = static::getWidgetState($form['#parents'], $field_name, $form_state); $field_state['items'] = $items->getValue(); static::setWidgetState($form['#parents'], $field_name, $form_state, $field_state); } /** * {@inheritdoc} */ Loading Loading @@ -744,7 +760,15 @@ public static function updateWidget(array $form, FormStateInterface $form_state) * The form state. */ public static function removeItem(array $form, FormStateInterface $form_state) { // During the form rebuild, formElement() will create field item widget // elements using re-indexed deltas, so clear out FormState::$input to // avoid a mismatch between old and new deltas. The rebuilt elements will // have #default_value set appropriately for the current state of the field, // so nothing is lost in doing this. // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::extractFormValues $triggering_element = $form_state->getTriggeringElement(); $parents = array_slice($triggering_element['#parents'], 0, -2); NestedArray::setValue($form_state->getUserInput(), $parents, NULL); // Get the parents required to find the top-level widget element. if (count($triggering_element['#array_parents']) < 4) { Loading Loading @@ -844,7 +868,17 @@ public static function validateItems(array $form, FormStateInterface $form_state * The form state. */ public static function addItems(array $form, FormStateInterface $form_state) { // During the form rebuild, formElement() will create field item widget // elements using re-indexed deltas, so clear out FormState::$input to // avoid a mismatch between old and new deltas. The rebuilt elements will // have #default_value set appropriately for the current state of the field, // so nothing is lost in doing this. // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::extractFormValues $button = $form_state->getTriggeringElement(); $parents = array_slice($button['#parents'], 0, -1); $parents[] = 'selection'; NestedArray::setValue($form_state->getUserInput(), $parents, NULL); $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1)); $field_state = static::getFieldState($element, $form_state); Loading core/modules/media_library/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php +86 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\Tests\media_library\FunctionalJavascript; use Drupal\field\Entity\FieldConfig; use Drupal\FunctionalJavascriptTests\SortableTestTrait; /** * Tests the Media library entity reference widget. Loading @@ -11,6 +12,8 @@ */ class EntityReferenceWidgetTest extends MediaLibraryTestBase { use SortableTestTrait; /** * {@inheritdoc} */ Loading Loading @@ -256,6 +259,7 @@ public function testWidget() { $this->openMediaLibraryForField('field_twin_media'); $page->checkField('Select Dog'); $this->pressInsertSelected('Added one media item.'); $this->waitForElementsCount('css', '.field--name-field-twin-media [data-media-library-item-delta]', 2); // Assert that we can toggle the visibility of the weight inputs when the // field contains more than one item. $wrapper = $assert_session->elementExists('css', '.field--name-field-twin-media'); Loading Loading @@ -485,4 +489,86 @@ public function testRequiredMediaField() { $this->assertSession()->pageTextContains('Basic page My page has been created.'); } /** * Tests that changed order is maintained after removing a selection. */ public function testRemoveAfterReordering(): void { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $this->drupalGet('node/add/basic_page'); $page->fillField('Title', 'My page'); $this->openMediaLibraryForField('field_unlimited_media'); $page->checkField('Select Dog'); $page->checkField('Select Cat'); $page->checkField('Select Bear'); // Order: Dog - Cat - Bear. $this->pressInsertSelected('Added 3 media items.'); // Move first item (Dog) to the end. // Order: Cat - Bear - Dog. $this->sortableAfter('[data-media-library-item-delta="0"]', '[data-media-library-item-delta="2"]', '.js-media-library-selection'); $wrapper = $assert_session->elementExists('css', '.field--name-field-unlimited-media'); // Remove second item (Bear). // Order: Cat - Dog. $wrapper->find('css', "[aria-label='Remove Bear']")->press(); $this->waitForText('Bear has been removed.'); $page->pressButton('Save'); $assert_session->elementTextContains('css', '.field--name-field-unlimited-media > .field__items > .field__item:last-child', 'Dog'); } /** * Tests that order is correct after re-order and adding another item. */ public function testAddAfterReordering(): void { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $this->drupalGet('node/add/basic_page'); $page->fillField('Title', 'My page'); $this->openMediaLibraryForField('field_unlimited_media'); $page->checkField('Select Dog'); $page->checkField('Select Cat'); // Order: Dog - Cat. $this->pressInsertSelected('Added 2 media items.'); // Change positions. // Order: Cat - Dog. $this->sortableAfter('[data-media-library-item-delta="0"]', '[data-media-library-item-delta="1"]', '.js-media-library-selection'); $this->openMediaLibraryForField('field_unlimited_media'); $this->selectMediaItem(2); // Order: Cat - Dog - Bear. $this->pressInsertSelected('Added one media item.'); $page->pressButton('Save'); $assert_session->elementTextContains('css', '.field--name-field-unlimited-media > .field__items > .field__item:first-child', 'Cat'); $assert_session->elementTextContains('css', '.field--name-field-unlimited-media > .field__items > .field__item:last-child', 'Bear'); } /** * {@inheritdoc} */ protected function sortableUpdate($item, $from, $to = NULL) { // See core/modules/media_library/js/media_library.widget.es6.js. $script = <<<JS (function ($) { var selection = document.querySelectorAll('.js-media-library-selection'); selection.forEach(function (widget) { $(widget).children().each(function (index, child) { $(child).find('.js-media-library-item-weight').val(index); }); }); })(jQuery) JS; $this->getSession()->executeScript($script); } } Loading
core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php +34 −0 Original line number Diff line number Diff line Loading @@ -286,6 +286,22 @@ public function form(FieldItemListInterface $items, array &$form, FormStateInter return parent::form($items, $form, $form_state, $get_delta); } /** * {@inheritdoc} */ public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) { parent::extractFormValues($items, $form, $form_state); // Update reference to 'items' stored during add or remove to take into // account changes to values like 'weight' etc. // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::addItems // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::removeItem $field_name = $this->fieldDefinition->getName(); $field_state = static::getWidgetState($form['#parents'], $field_name, $form_state); $field_state['items'] = $items->getValue(); static::setWidgetState($form['#parents'], $field_name, $form_state, $field_state); } /** * {@inheritdoc} */ Loading Loading @@ -744,7 +760,15 @@ public static function updateWidget(array $form, FormStateInterface $form_state) * The form state. */ public static function removeItem(array $form, FormStateInterface $form_state) { // During the form rebuild, formElement() will create field item widget // elements using re-indexed deltas, so clear out FormState::$input to // avoid a mismatch between old and new deltas. The rebuilt elements will // have #default_value set appropriately for the current state of the field, // so nothing is lost in doing this. // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::extractFormValues $triggering_element = $form_state->getTriggeringElement(); $parents = array_slice($triggering_element['#parents'], 0, -2); NestedArray::setValue($form_state->getUserInput(), $parents, NULL); // Get the parents required to find the top-level widget element. if (count($triggering_element['#array_parents']) < 4) { Loading Loading @@ -844,7 +868,17 @@ public static function validateItems(array $form, FormStateInterface $form_state * The form state. */ public static function addItems(array $form, FormStateInterface $form_state) { // During the form rebuild, formElement() will create field item widget // elements using re-indexed deltas, so clear out FormState::$input to // avoid a mismatch between old and new deltas. The rebuilt elements will // have #default_value set appropriately for the current state of the field, // so nothing is lost in doing this. // @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::extractFormValues $button = $form_state->getTriggeringElement(); $parents = array_slice($button['#parents'], 0, -1); $parents[] = 'selection'; NestedArray::setValue($form_state->getUserInput(), $parents, NULL); $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1)); $field_state = static::getFieldState($element, $form_state); Loading
core/modules/media_library/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php +86 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\Tests\media_library\FunctionalJavascript; use Drupal\field\Entity\FieldConfig; use Drupal\FunctionalJavascriptTests\SortableTestTrait; /** * Tests the Media library entity reference widget. Loading @@ -11,6 +12,8 @@ */ class EntityReferenceWidgetTest extends MediaLibraryTestBase { use SortableTestTrait; /** * {@inheritdoc} */ Loading Loading @@ -256,6 +259,7 @@ public function testWidget() { $this->openMediaLibraryForField('field_twin_media'); $page->checkField('Select Dog'); $this->pressInsertSelected('Added one media item.'); $this->waitForElementsCount('css', '.field--name-field-twin-media [data-media-library-item-delta]', 2); // Assert that we can toggle the visibility of the weight inputs when the // field contains more than one item. $wrapper = $assert_session->elementExists('css', '.field--name-field-twin-media'); Loading Loading @@ -485,4 +489,86 @@ public function testRequiredMediaField() { $this->assertSession()->pageTextContains('Basic page My page has been created.'); } /** * Tests that changed order is maintained after removing a selection. */ public function testRemoveAfterReordering(): void { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $this->drupalGet('node/add/basic_page'); $page->fillField('Title', 'My page'); $this->openMediaLibraryForField('field_unlimited_media'); $page->checkField('Select Dog'); $page->checkField('Select Cat'); $page->checkField('Select Bear'); // Order: Dog - Cat - Bear. $this->pressInsertSelected('Added 3 media items.'); // Move first item (Dog) to the end. // Order: Cat - Bear - Dog. $this->sortableAfter('[data-media-library-item-delta="0"]', '[data-media-library-item-delta="2"]', '.js-media-library-selection'); $wrapper = $assert_session->elementExists('css', '.field--name-field-unlimited-media'); // Remove second item (Bear). // Order: Cat - Dog. $wrapper->find('css', "[aria-label='Remove Bear']")->press(); $this->waitForText('Bear has been removed.'); $page->pressButton('Save'); $assert_session->elementTextContains('css', '.field--name-field-unlimited-media > .field__items > .field__item:last-child', 'Dog'); } /** * Tests that order is correct after re-order and adding another item. */ public function testAddAfterReordering(): void { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $this->drupalGet('node/add/basic_page'); $page->fillField('Title', 'My page'); $this->openMediaLibraryForField('field_unlimited_media'); $page->checkField('Select Dog'); $page->checkField('Select Cat'); // Order: Dog - Cat. $this->pressInsertSelected('Added 2 media items.'); // Change positions. // Order: Cat - Dog. $this->sortableAfter('[data-media-library-item-delta="0"]', '[data-media-library-item-delta="1"]', '.js-media-library-selection'); $this->openMediaLibraryForField('field_unlimited_media'); $this->selectMediaItem(2); // Order: Cat - Dog - Bear. $this->pressInsertSelected('Added one media item.'); $page->pressButton('Save'); $assert_session->elementTextContains('css', '.field--name-field-unlimited-media > .field__items > .field__item:first-child', 'Cat'); $assert_session->elementTextContains('css', '.field--name-field-unlimited-media > .field__items > .field__item:last-child', 'Bear'); } /** * {@inheritdoc} */ protected function sortableUpdate($item, $from, $to = NULL) { // See core/modules/media_library/js/media_library.widget.es6.js. $script = <<<JS (function ($) { var selection = document.querySelectorAll('.js-media-library-selection'); selection.forEach(function (widget) { $(widget).children().each(function (index, child) { $(child).find('.js-media-library-item-weight').val(index); }); }); })(jQuery) JS; $this->getSession()->executeScript($script); } }