diff --git a/composer.json b/composer.json
index 32dac173e4581715e23a006c76a84fcd2d1198ad..b425dbbb80aa9a83d33b1b3fc2d782280b9967fb 100644
--- a/composer.json
+++ b/composer.json
@@ -132,7 +132,7 @@
         "drupal/image_popup": "1.1",
         "drupal/inline_entity_form": "1.0-rc1",
         "drupal/libraries": "^3.0@alpha",
-        "drupal/link_attributes": "1.0",
+        "drupal/link_attributes": "^1.0",
         "drupal/linkit": "5.0-beta10",
         "drupal/magnific_popup": "1.3",
         "drupal/mathjax": "^2.7",
diff --git a/composer.lock b/composer.lock
index 93e3412a9a32f524379167a4369c9d5a115afb9f..3efcced53ccac12228c45763ce949ba00613c19e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -5320,20 +5320,20 @@
         },
         {
             "name": "drupal/link_attributes",
-            "version": "1.0.0",
+            "version": "1.10.0",
             "source": {
                 "type": "git",
                 "url": "https://git.drupalcode.org/project/link_attributes.git",
-                "reference": "8.x-1.0"
+                "reference": "8.x-1.10"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://ftp.drupal.org/files/projects/link_attributes-8.x-1.0.zip",
-                "reference": "8.x-1.0",
-                "shasum": "8a5851467f17c01b4dcb6e446f00cd7abe116475"
+                "url": "https://ftp.drupal.org/files/projects/link_attributes-8.x-1.10.zip",
+                "reference": "8.x-1.10",
+                "shasum": "3df450b83a05911575aaf9b234a3b43833d3c1c2"
             },
             "require": {
-                "drupal/core": "*"
+                "drupal/core": "^8 || ^9"
             },
             "type": "drupal-module",
             "extra": {
@@ -5341,8 +5341,8 @@
                     "dev-1.x": "1.x-dev"
                 },
                 "drupal": {
-                    "version": "8.x-1.0",
-                    "datestamp": "1500846243",
+                    "version": "8.x-1.10",
+                    "datestamp": "1580259496",
                     "security-coverage": {
                         "status": "covered",
                         "message": "Covered by Drupal's security advisory policy"
@@ -5362,7 +5362,7 @@
             "description": "Provides a widget to allow settings of link attributes for menu links.",
             "homepage": "https://www.drupal.org/project/link_attributes",
             "support": {
-                "source": "http://cgit.drupalcode.org/link_attributes"
+                "source": "https://git.drupalcode.org/project/link_attributes"
             }
         },
         {
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index e5803b8f435b1f9af2c66846fa36699bd65f144d..740dd72003ab75f1b0f03470d541732d058802b6 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -5484,21 +5484,21 @@
     },
     {
         "name": "drupal/link_attributes",
-        "version": "1.0.0",
-        "version_normalized": "1.0.0.0",
+        "version": "1.10.0",
+        "version_normalized": "1.10.0.0",
         "source": {
             "type": "git",
             "url": "https://git.drupalcode.org/project/link_attributes.git",
-            "reference": "8.x-1.0"
+            "reference": "8.x-1.10"
         },
         "dist": {
             "type": "zip",
-            "url": "https://ftp.drupal.org/files/projects/link_attributes-8.x-1.0.zip",
-            "reference": "8.x-1.0",
-            "shasum": "8a5851467f17c01b4dcb6e446f00cd7abe116475"
+            "url": "https://ftp.drupal.org/files/projects/link_attributes-8.x-1.10.zip",
+            "reference": "8.x-1.10",
+            "shasum": "3df450b83a05911575aaf9b234a3b43833d3c1c2"
         },
         "require": {
-            "drupal/core": "*"
+            "drupal/core": "^8 || ^9"
         },
         "type": "drupal-module",
         "extra": {
@@ -5506,8 +5506,8 @@
                 "dev-1.x": "1.x-dev"
             },
             "drupal": {
-                "version": "8.x-1.0",
-                "datestamp": "1500846243",
+                "version": "8.x-1.10",
+                "datestamp": "1580259496",
                 "security-coverage": {
                     "status": "covered",
                     "message": "Covered by Drupal's security advisory policy"
@@ -5528,7 +5528,7 @@
         "description": "Provides a widget to allow settings of link attributes for menu links.",
         "homepage": "https://www.drupal.org/project/link_attributes",
         "support": {
-            "source": "http://cgit.drupalcode.org/link_attributes"
+            "source": "https://git.drupalcode.org/project/link_attributes"
         }
     },
     {
diff --git a/web/modules/link_attributes/README.md b/web/modules/link_attributes/README.md
index 734f823de49f024aa68211f14a5b1216c0885121..64067f2a463f815c76334838e7d2fb15bf2a18a5 100644
--- a/web/modules/link_attributes/README.md
+++ b/web/modules/link_attributes/README.md
@@ -1,9 +1,57 @@
-## Link attributes
+CONTENTS OF THIS FILE
+---------------------
 
-Provides an alternate widget for the link field that allows setting attributes.
+ * Introduction
+ * Requirements
+ * Installation
+ * Configuration
+ * Maintainers
 
-By default adds class, rel and target attributes to menu link content entitites.
 
-## Known issues
+INTRODUCTION
+------------
 
-You need [this core patch](https://www.drupal.org/files/issues/2760557-link-options.pass_.patch).
+The link attributes module provides a widget that allows users to add
+attributes to links. It overtakes the core default widget for menu link
+content entities, allowing you to set attributes on menu links.
+
+ * For a full description of the module, visit the project page:
+   https://www.drupal.org/project/link_attributes
+
+
+REQUIREMENTS
+------------
+
+This module requires no modules outside of Drupal core.
+
+
+INSTALLATION
+------------
+
+ * Install as you would normally install a contributed Drupal module. Visit:
+   https://www.drupal.org/documentation/install/modules-themes/modules-8 for
+   further information.
+
+
+CONFIGURATION
+-------------
+
+The module has no menu or modifiable settings. There is no configuration. Once
+enabled, the module will add class, rel and target attributes to menu link
+content entities by default.
+
+In order to use this functionality, follow the following steps:
+
+ * Enable the module like normal
+ * It will immediately take effect on *menu link content* entities.
+ * For other link fields, edit the widget using 'Manage form display'
+   and select the 'Link (with attributes)' widget
+
+You can also follow along with this [screencast](https://vimeo.com/233507094) to
+configure the field settings.
+
+
+MAINTAINERS
+-----------
+
+ * Lee Rowlands (larowlan) - https://www.drupal.org/u/larowlan
diff --git a/web/modules/link_attributes/link_attributes.api.php b/web/modules/link_attributes/link_attributes.api.php
new file mode 100644
index 0000000000000000000000000000000000000000..783d616df2192c6b0925f5e85a4905045b38a531
--- /dev/null
+++ b/web/modules/link_attributes/link_attributes.api.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Hooks related to the Link Attributes module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Modify the definitions of link attribute plugins.
+ *
+ * @param array[] $plugins
+ *   Link attribute plugin definitions.
+ */
+function hook_link_attributes_plugin_alter(array &$plugins) {
+  // Set a default value for the target attribute.
+  $plugins['target']['default_value'] = '_blank';
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/web/modules/link_attributes/link_attributes.info.yml b/web/modules/link_attributes/link_attributes.info.yml
index d0309d7860bd02dba885506ef973a0d3cc390850..4609f004dd3eb629a7ef740cab2e70381f22ff29 100644
--- a/web/modules/link_attributes/link_attributes.info.yml
+++ b/web/modules/link_attributes/link_attributes.info.yml
@@ -1,13 +1,13 @@
 name: Link attributes
 type: module
 description: Provides a widget to allow settings of link attributes for menu links.
-# core: 8.x
+core: 8.x
+core_version_requirement: ^8 || ^9
 dependencies:
   - drupal:link
-  - drupal:menu_link_content
+  - drupal:system (>=8.4)
 
-# Information added by Drupal.org packaging script on 2016-12-16
-version: '8.x-1.0'
-core: '8.x'
+# Information added by Drupal.org packaging script on 2020-01-29
+version: '8.x-1.10'
 project: 'link_attributes'
-datestamp: 1481849585
+datestamp: 1580259499
diff --git a/web/modules/link_attributes/link_attributes.link_attributes.yml b/web/modules/link_attributes/link_attributes.link_attributes.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0ebe2e7cf1e31a9ced4f6422f63ff32a9ca95b05
--- /dev/null
+++ b/web/modules/link_attributes/link_attributes.link_attributes.yml
@@ -0,0 +1,21 @@
+id:
+  title: ID
+name:
+  title: Name
+target:
+  title: Target
+  type: select
+  empty_value: ''
+  options:
+    _self: Same window (_self)
+    _blank: New window (_blank)
+rel:
+  title: Rel
+class:
+  title: Class
+accesskey:
+  title: Accesskey
+aria-label:
+  title: ARIA Label
+title:
+  title: Title
diff --git a/web/modules/link_attributes/link_attributes.module b/web/modules/link_attributes/link_attributes.module
index c98ff917c23b7340a66f4c8442f8a57f183813e7..69acd9ca238b57403b608cd1e224ea602ae9532b 100644
--- a/web/modules/link_attributes/link_attributes.module
+++ b/web/modules/link_attributes/link_attributes.module
@@ -4,6 +4,7 @@
  * @file
  * Contains main module functions.
  */
+
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 
diff --git a/web/modules/link_attributes/link_attributes.services.yml b/web/modules/link_attributes/link_attributes.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8a2e34c48d2f71515e9f62234f3e32b6bc886165
--- /dev/null
+++ b/web/modules/link_attributes/link_attributes.services.yml
@@ -0,0 +1,4 @@
+services:
+  plugin.manager.link_attributes:
+    class: Drupal\link_attributes\LinkAttributesManager
+    arguments: ['@module_handler', '@cache.discovery']
diff --git a/web/modules/link_attributes/src/LinkAttributesManager.php b/web/modules/link_attributes/src/LinkAttributesManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..9fbe801348c85a2476de346a8a7cca4a91b26edf
--- /dev/null
+++ b/web/modules/link_attributes/src/LinkAttributesManager.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\link_attributes;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\Discovery\YamlDiscovery;
+
+/**
+ * Provides the link_attributes plugin manager.
+ */
+class LinkAttributesManager extends DefaultPluginManager implements PluginManagerInterface {
+
+  /**
+   * Provides default values for all link_attributes plugins.
+   *
+   * @var array
+   */
+  protected $defaults = [
+    'title' => '',
+    'type' => '',
+    'description' => '',
+  ];
+
+  /**
+   * Constructs a LinkAttributesManager object.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend) {
+    $this->alterInfo('link_attributes_plugin');
+    $this->moduleHandler = $module_handler;
+    $this->setCacheBackend($cache_backend, 'link_attributes', ['link_attributes']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDiscovery() {
+    if (!isset($this->discovery)) {
+      $this->discovery = new YamlDiscovery('link_attributes', $this->moduleHandler->getModuleDirectories());
+      $this->discovery->addTranslatableProperty('title');
+      $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
+    }
+    return $this->discovery;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    parent::processDefinition($definition, $plugin_id);
+
+    // Make sure each plugin definition had at least a field type.
+    if (empty($definition['type'])) {
+      $definition['type'] = 'textfield';
+    }
+  }
+
+}
diff --git a/web/modules/link_attributes/src/Plugin/Field/FieldWidget/LinkWithAttributesWidget.php b/web/modules/link_attributes/src/Plugin/Field/FieldWidget/LinkWithAttributesWidget.php
index 3f628a5af36d930baa067e5995faf9d374903482..e298e3d4b7c11c3299d930b146dfd04da0b18c6b 100644
--- a/web/modules/link_attributes/src/Plugin/Field/FieldWidget/LinkWithAttributesWidget.php
+++ b/web/modules/link_attributes/src/Plugin/Field/FieldWidget/LinkWithAttributesWidget.php
@@ -2,9 +2,13 @@
 
 namespace Drupal\link_attributes\Plugin\Field\FieldWidget;
 
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\link\Plugin\Field\FieldWidget\LinkWidget;
+use Drupal\link_attributes\LinkAttributesManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Plugin implementation of the 'link' widget.
@@ -17,13 +21,55 @@
  *   }
  * )
  */
-class LinkWithAttributesWidget extends LinkWidget {
+class LinkWithAttributesWidget extends LinkWidget implements ContainerFactoryPluginInterface {
+
+  /**
+   * The link attributes manager.
+   *
+   * @var \Drupal\link_attributes\LinkAttributesManager
+   */
+  protected $linkAttributesManager;
+
+  /**
+   * Constructs a LinkWithAttributesWidget object.
+   *
+   * @param string $plugin_id
+   *   The plugin_id for the widget.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The definition of the field to which the widget is associated.
+   * @param array $settings
+   *   The widget settings.
+   * @param array $third_party_settings
+   *   Any third party settings.
+   * @param \Drupal\link_attributes\LinkAttributesManager $link_attributes_manager
+   *   The link attributes manager.
+   */
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, LinkAttributesManager $link_attributes_manager) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
+    $this->linkAttributesManager = $link_attributes_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('plugin.manager.link_attributes')
+    );
+  }
 
   /**
    * {@inheritdoc}
    */
   public static function defaultSettings() {
-    return array(
+    return [
       'placeholder_url' => '',
       'placeholder_title' => '',
       'enabled_attributes' => [
@@ -34,7 +80,7 @@ public static function defaultSettings() {
         'class' => TRUE,
         'accesskey' => FALSE,
       ],
-    ) + parent::defaultSettings();
+    ] + parent::defaultSettings();
   }
 
   /**
@@ -44,7 +90,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     $element = parent::formElement($items, $delta, $element, $form, $form_state);
     // Add each of the enabled attributes.
     // @todo move this to plugins that nominate form and label.
-
     $item = $items[$delta];
 
     $options = $item->get('options')->getValue();
@@ -55,13 +100,23 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       '#tree' => TRUE,
       '#open' => count($attributes),
     ];
+    $plugin_definitions = $this->linkAttributesManager->getDefinitions();
     foreach (array_keys(array_filter($this->getSetting('enabled_attributes'))) as $attribute) {
-      $element['options']['attributes'][$attribute] = [
-        '#type' => 'textfield',
-        '#title' => $attribute,
-        '#description' => $this->t('Enter value for the @attribute attribute', ['@attribute' => $attribute]),
-        '#default_value' => isset($attributes[$attribute]) ? $attributes[$attribute] : '',
-      ];
+      if (isset($plugin_definitions[$attribute])) {
+        foreach ($plugin_definitions[$attribute] as $property => $value) {
+          $element['options']['attributes'][$attribute]['#' . $property] = $value;
+        }
+
+        // Set the default value, in case of a class that is stored as array,
+        // convert it back to a string.
+        $default_value = isset($attributes[$attribute]) ? $attributes[$attribute] : NULL;
+        if ($attribute === 'class' && is_array($default_value)) {
+          $default_value = implode(' ', $default_value);
+        }
+        if (isset($default_value)) {
+          $element['options']['attributes'][$attribute]['#default_value'] = $default_value;
+        }
+      }
     }
     return $element;
   }
@@ -71,18 +126,41 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
    */
   public function settingsForm(array $form, FormStateInterface $form_state) {
     $element = parent::settingsForm($form, $form_state);
-    $options = ['id', 'name', 'target', 'rel', 'class', 'accesskey'];
+    $options = array_map(function ($plugin_definition) {
+      return $plugin_definition['title'];
+    }, $this->linkAttributesManager->getDefinitions());
     $selected = array_keys(array_filter($this->getSetting('enabled_attributes')));
     $element['enabled_attributes'] = [
       '#type' => 'checkboxes',
       '#title' => $this->t('Enabled attributes'),
-      '#options' => array_combine($options, $options),
+      '#options' => $options,
       '#default_value' => array_combine($selected, $selected),
       '#description' => $this->t('Select the attributes to allow the user to edit.'),
     ];
     return $element;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    // Convert a class string to an array so that it can be merged reliable.
+    foreach ($values as $delta => $value) {
+      if (isset($value['options']['attributes']['class']) && is_string($value['options']['attributes']['class'])) {
+        $values[$delta]['options']['attributes']['class'] = explode(' ', $value['options']['attributes']['class']);
+      }
+    }
+
+    return array_map(function (array $value) {
+      if (isset($value['options']['attributes'])) {
+        $value['options']['attributes'] = array_filter($value['options']['attributes'], function ($attribute) {
+          return $attribute !== "";
+        });
+      }
+      return $value;
+    }, $values);
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -90,7 +168,7 @@ public function settingsSummary() {
     $summary = parent::settingsSummary();
     $enabled_attributes = array_filter($this->getSetting('enabled_attributes'));
     if ($enabled_attributes) {
-      $summary[] = $this->t('With attributes: @attributes', array('@attributes' => implode(', ', array_keys($enabled_attributes))));
+      $summary[] = $this->t('With attributes: @attributes', ['@attributes' => implode(', ', array_keys($enabled_attributes))]);
     }
     return $summary;
   }
diff --git a/web/modules/link_attributes/tests/modules/link_attributes_test_alterinfo/link_attributes_test_alterinfo.info.yml b/web/modules/link_attributes/tests/modules/link_attributes_test_alterinfo/link_attributes_test_alterinfo.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a449282d9f69e82229349b396f5fc5e78b891c8d
--- /dev/null
+++ b/web/modules/link_attributes/tests/modules/link_attributes_test_alterinfo/link_attributes_test_alterinfo.info.yml
@@ -0,0 +1,12 @@
+name: 'Link attributes alterInfo Test'
+description: 'Implements hook_link_attributes_plugin_alter'
+type: module
+core: 8.x
+core_version_requirement: ^8 || ^9
+dependencies:
+  - link_attributes:link_attributes
+
+# Information added by Drupal.org packaging script on 2020-01-29
+version: '8.x-1.10'
+project: 'link_attributes'
+datestamp: 1580259499
diff --git a/web/modules/link_attributes/tests/modules/link_attributes_test_alterinfo/link_attributes_test_alterinfo.module b/web/modules/link_attributes/tests/modules/link_attributes_test_alterinfo/link_attributes_test_alterinfo.module
new file mode 100644
index 0000000000000000000000000000000000000000..aff63f3ad952fa27a25a8bafe7344b02cb1369e0
--- /dev/null
+++ b/web/modules/link_attributes/tests/modules/link_attributes_test_alterinfo/link_attributes_test_alterinfo.module
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Link attributes test module.
+ */
+
+/**
+ * Implements hook_link_attributes_plugin_alter().
+ */
+function link_attributes_test_alterinfo_link_attributes_plugin_alter(array &$definitions) {
+  // Alter only if our state flag is set.
+  if (\Drupal::state()->get('link_attributes_test_alterinfo.hook_link_attributes_plugin_alter')) {
+    $definitions['class']['title'] = t('Link style');
+    $definitions['class']['description'] = t('Select how the link should be displayed.');
+    $definitions['class']['type'] = 'select';
+    $definitions['class']['options'] = [
+      'link' => 'Link',
+      'button' => 'Button',
+    ];
+    $definitions['class']['default_value'] = 'button';
+    $definitions['target']['default_value'] = '_blank';
+  }
+}
diff --git a/web/modules/link_attributes/tests/src/Functional/LinkAttributesFieldTest.php b/web/modules/link_attributes/tests/src/Functional/LinkAttributesFieldTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1a9301f8a4f709d3e231bd123727b309c07f72fe
--- /dev/null
+++ b/web/modules/link_attributes/tests/src/Functional/LinkAttributesFieldTest.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace Drupal\Tests\link_attributes\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
+
+/**
+ * Tests link attributes functionality.
+ *
+ * @group link_attributes
+ */
+class LinkAttributesFieldTest extends BrowserTestBase {
+
+  use FieldUiTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'node',
+    'link_attributes',
+    'field_ui',
+    'block',
+    'link_attributes_test_alterinfo',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * A user that can edit content types.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->adminUser = $this->drupalCreateUser([
+      'administer content types',
+      'administer node fields',
+      'administer node display',
+    ]);
+    $this->drupalLogin($this->adminUser);
+    // Breadcrumb is required for FieldUiTestTrait::fieldUIAddNewField.
+    $this->drupalPlaceBlock('system_breadcrumb_block');
+    \Drupal::state()->set('link_attributes_test_alterinfo.hook_link_attributes_plugin_alter', TRUE);
+  }
+
+  /**
+   * Tests the display of attributes in the widget.
+   */
+  public function testWidget() {
+    // Add a content type.
+    $type = $this->drupalCreateContentType();
+    $type_path = 'admin/structure/types/manage/' . $type->id();
+    $add_path = 'node/add/' . $type->id();
+
+    // Add a link field to the newly-created type.
+    $label = $this->randomMachineName();
+    $field_name = mb_strtolower($label);
+    $storage_settings = ['cardinality' => 'number', 'cardinality_number' => 2];
+    $this->fieldUIAddNewField($type_path, $field_name, $label, 'link', $storage_settings);
+
+    // Manually clear cache on the tester side.
+    \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
+
+    // Change the link widget and enable some attributes.
+    \Drupal::entityTypeManager()
+      ->getStorage('entity_form_display')
+      ->load('node.' . $type->id() . '.default')
+      ->setComponent('field_' . $field_name, [
+        'type' => 'link_attributes',
+        'settings' => [
+          'enabled_attributes' => [
+            'rel' => TRUE,
+            'class' => TRUE,
+            'target' => TRUE,
+          ],
+        ],
+      ])
+      ->save();
+
+    // Check if the link field have the attributes displayed on node add page.
+    $this->drupalGet($add_path);
+    $web_assert = $this->assertSession();
+    // Link attributes.
+    $web_assert->elementExists('css', '.field--widget-link-attributes');
+
+    // Rel attribute.
+    $attribute_rel = 'field_' . $field_name . '[0][options][attributes][rel]';
+    $web_assert->fieldExists($attribute_rel);
+
+    // Class attribute.
+    $attribute_class = 'field_' . $field_name . '[0][options][attributes][class]';
+    $web_assert->fieldExists($attribute_class);
+
+    // Target attribute.
+    $attribute_target = 'field_' . $field_name . '[0][options][attributes][target]';
+    $web_assert->fieldExists($attribute_target);
+    $web_assert->fieldValueEquals($attribute_target, '_blank');
+
+    \Drupal::state()->set('link_attributes_test_alterinfo.hook_link_attributes_plugin_alter', FALSE);
+    \Drupal::service('plugin.manager.link_attributes')->clearCachedDefinitions();
+    // Create a node.
+    $edit = [
+      'title[0][value]' => 'A multi field link test',
+      'field_' . $field_name . '[0][title]' => 'Link One',
+      'field_' . $field_name . '[0][uri]' => '<front>',
+      'field_' . $field_name . '[0][options][attributes][class]' => 'class-one class-two',
+      'field_' . $field_name . '[1][title]' => 'Link Two',
+      'field_' . $field_name . '[1][uri]' => '<front>',
+      'field_' . $field_name . '[1][options][attributes][class]' => 'class-three class-four',
+    ];
+    $this->drupalPostForm($add_path, $edit, t('Save'));
+    $node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
+
+    // Load the field values.
+    $field_values = $node->get('field_' . $field_name)->getValue();
+
+    $expected_link_one = [
+      'class-one',
+      'class-two',
+    ];
+    $this->assertEquals($expected_link_one, $field_values[0]['options']['attributes']['class']);
+
+    $expected_link_two = [
+      'class-three',
+      'class-four',
+    ];
+    $this->assertEquals($expected_link_two, $field_values[1]['options']['attributes']['class']);
+  }
+
+  /**
+   * Tests saving a node without any attributes enabled in the widget settings.
+   */
+  public function testWidgetWithoutAttributes() {
+    // Add a content type.
+    $type = $this->drupalCreateContentType();
+    $type_path = 'admin/structure/types/manage/' . $type->id();
+    $add_path = 'node/add/' . $type->id();
+
+    // Add a link field to the newly-created type.
+    $label = $this->randomMachineName();
+    $field_name = mb_strtolower($label);
+    $storage_settings = ['cardinality' => 'number', 'cardinality_number' => 2];
+    $this->fieldUIAddNewField($type_path, $field_name, $label, 'link', $storage_settings);
+
+    // Manually clear cache on the tester side.
+    \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
+
+    \Drupal::entityTypeManager()
+      ->getStorage('entity_form_display')
+      ->load('node.' . $type->id() . '.default')
+      ->setComponent('field_' . $field_name, [
+        'type' => 'link_attributes',
+        'settings' => [
+          'enabled_attributes' => [],
+        ],
+      ])
+      ->save();
+
+    $this->drupalGet($add_path);
+    $web_assert = $this->assertSession();
+    // Link attributes.
+    $web_assert->elementExists('css', '.field--widget-link-attributes');
+    // Create a node.
+    $edit = [
+      'title[0][value]' => 'A multi field link test',
+      'field_' . $field_name . '[0][title]' => 'Link One',
+      'field_' . $field_name . '[0][uri]' => '<front>',
+    ];
+    $this->drupalPostForm($add_path, $edit, t('Save'));
+    $node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
+    $this->drupalGet($node->toUrl()->toString());
+    $web_assert->linkExists('Link One');
+  }
+
+}
diff --git a/web/modules/link_attributes/tests/src/Functional/LinkAttributesMenuTest.php b/web/modules/link_attributes/tests/src/Functional/LinkAttributesMenuTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f5b599ad916f9dae53e2ae4da45cd08e9876020
--- /dev/null
+++ b/web/modules/link_attributes/tests/src/Functional/LinkAttributesMenuTest.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Drupal\Tests\link_attributes\Functional;
+
+use Drupal\menu_link_content\Entity\MenuLinkContent;
+use Drupal\Tests\block\Traits\BlockCreationTrait;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests link attributes functionality.
+ *
+ * @group link_attributes
+ */
+class LinkAttributesMenuTest extends BrowserTestBase {
+
+  use BlockCreationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'link_attributes',
+    'menu_ui',
+    'menu_link_content',
+    'block',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->placeBlock('system_menu_block:footer');
+  }
+
+  /**
+   * Test attributes.
+   */
+  public function testMenuLinkAdmin() {
+    // Login as a super-admin.
+    $this->drupalLogin($this->drupalCreateUser(array_keys(\Drupal::service('user.permissions')->getPermissions())));
+
+    $this->drupalGet('admin/structure/menu/manage/footer/add');
+    $this->submitForm([
+      'title[0][value]' => 'A menu link',
+      'link[0][uri]' => '<front>',
+      // This is enough to check the fields are there.
+      'link[0][options][attributes][target]' => '_blank',
+      'link[0][options][attributes][class]' => 'menu__link--really_special menu__link--another-class',
+    ], t('Save'));
+    $this->drupalGet('user');
+    $page = $this->getSession()->getPage();
+    // The link should exist and contain the required attributes.
+    $link = $page->findLink('A menu link');
+    $this->assertNotNull($link);
+    $this->assertEquals('_blank', $link->getAttribute('target'));
+    $this->assertEquals('menu__link--really_special menu__link--another-class', $link->getAttribute('class'));
+    // No rel attribute was added, so none should be present.
+    $this->assertFalse($link->hasAttribute('rel'));
+
+    // Load the menu link, make sure that the classes were stored as an array.
+    $id = \Drupal::entityQuery('menu_link_content')
+      ->condition('title', 'A menu link')
+      ->execute();
+
+    /** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link */
+    $menu_link = MenuLinkContent::load(reset($id));
+
+    $expected = [
+      'menu__link--really_special',
+      'menu__link--another-class',
+    ];
+    $this->assertEquals($expected, $menu_link->getUrlObject()->getOption('attributes')['class']);
+
+    // Edit the link, make sure the default value for class is set correctly.
+    $this->drupalGet($menu_link->toUrl('edit-form'));
+    $this->assertSession()->fieldValueEquals('link[0][options][attributes][class]', 'menu__link--really_special menu__link--another-class');
+
+    // Add another link to assert that the target can be empty.
+    $this->drupalGet('admin/structure/menu/manage/footer/add');
+    $this->submitForm([
+      'title[0][value]' => 'No target menu link',
+      'link[0][uri]' => '<front>',
+      'link[0][options][attributes][target]' => '',
+      'link[0][options][attributes][rel]' => 'author',
+    ], t('Save'));
+    $this->drupalGet('user');
+    $page = $this->getSession()->getPage();
+    // The link should exist and contain the set rel attribute.
+    $link = $page->findLink('No target menu link');
+    $this->assertNotNull($link);
+    $this->assertEquals('author', $link->getAttribute('rel'));
+    // No class or target was specified, these shouldn't be rendered.
+    $this->assertFalse($link->hasAttribute('target'));
+    $this->assertFalse($link->hasAttribute('class'));
+  }
+
+}
diff --git a/web/modules/link_attributes/tests/src/Functional/LinkAttributesTest.php b/web/modules/link_attributes/tests/src/Functional/LinkAttributesTest.php
deleted file mode 100644
index f6be709a6f50a6dc968646ef7f72b614cbe3cd32..0000000000000000000000000000000000000000
--- a/web/modules/link_attributes/tests/src/Functional/LinkAttributesTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-namespace Drupal\Tests\link_attributes\Functional;
-
-use Drupal\simpletest\BlockCreationTrait;
-use Drupal\Tests\BrowserTestBase;
-
-/**
- * Tests link attributes functionality.
- *
- * @group link_attributes
- */
-class LinkAttributesTest extends BrowserTestBase {
-
-  use BlockCreationTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = [
-    'link',
-    'link_attributes',
-    'menu_ui',
-    'menu_link_content',
-    'system',
-    'block',
-  ];
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp() {
-    parent::setUp();
-    $this->placeBlock('system_menu_block:footer');
-  }
-
-  /**
-   * Test attributes.
-   */
-  public function testMenuLinkAdmin() {
-    // Login as a super-admin.
-    $this->drupalLogin($this->drupalCreateUser(array_keys(\Drupal::service('user.permissions')->getPermissions())));
-
-    $this->drupalGet('admin/structure/menu/manage/footer/add');
-    $this->submitForm([
-      'title[0][value]' => 'A menu link',
-      'link[0][uri]' => '<front>',
-      // This is enough to check the fields are there.
-      'link[0][options][attributes][target]' => '_blank',
-      'link[0][options][attributes][class]' => 'menu__link--really_special',
-    ], t('Save'));
-    $this->drupalGet('user');
-    $page = $this->getSession()->getPage();
-    // The link should exist and contain the required attributes.
-    $link = $page->findLink('A menu link');
-    $this->assertNotNull($link);
-    $this->assertEquals('_blank', $link->getAttribute('target'));
-    $this->assertEquals('menu__link--really_special', $link->getAttribute('class'));
-  }
-
-}
diff --git a/web/modules/link_attributes/tests/src/Kernel/InfoAlterTest.php b/web/modules/link_attributes/tests/src/Kernel/InfoAlterTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f49bd9e0ab104494c69c256ff40659b502afd19e
--- /dev/null
+++ b/web/modules/link_attributes/tests/src/Kernel/InfoAlterTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\Tests\link_attributes\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests link_attributes_plugin alterInfo.
+ *
+ * @group link_attributes
+ */
+class InfoAlterTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'system',
+    'link_attributes',
+    'link_attributes_test_alterinfo',
+  ];
+
+  /**
+   * Tests that plugin definition is changed with alterInfo.
+   *
+   * Tests that info data is changed after a module that implements
+   * hook_link_attributes_plugin_alter() is enabled.
+   */
+  public function testLinkAttributesManagerInfoAlter() {
+    /** @var \Drupal\link_attributes\LinkAttributesManager $linkAttributesManager */
+    $linkAttributesManager = $this->container->get('plugin.manager.link_attributes');
+    $definition = $linkAttributesManager->getDefinitions();
+    $this->assertTrue($definition['class']['type'] == 'textfield', 'Without altering the plugin definition the class attribute is a textfield.');
+
+    // Set our flag to alter the plugin definition in
+    // link_attributes_test_alterinfo module.
+    \Drupal::state()->set('link_attributes_test_alterinfo.hook_link_attributes_plugin_alter', TRUE);
+    $linkAttributesManager->clearCachedDefinitions();
+    $definition = $linkAttributesManager->getDefinitions();
+    $this->assertTrue($definition['class']['type'] == 'select', 'After altering the plugin definition the class attribute is a select.');
+    $this->assertTrue(isset($definition['class']['options']['button']), 'After altering the plugin definition the class attribute has a "button" option.');
+  }
+
+}