diff --git a/config/schema/plugin.schema.yml b/config/schema/plugin.schema.yml
index cff8247..3cd443f 100644
--- a/config/schema/plugin.schema.yml
+++ b/config/schema/plugin.schema.yml
@@ -41,3 +41,8 @@ plugin.plugin_configuration.*:
       label: Plugin configuration
       type: string
   type: config_object
+
+# The "plugin_id" Views filter plugin configuration schema.
+views.filter.plugin_id:
+  type: views.filter.in_operator
+  label: 'Plugin ID'
diff --git a/modules/plugin_test_helper/config/optional/views.view.plugin_svpbf.yml b/modules/plugin_test_helper/config/optional/views.view.plugin_svpbf.yml
new file mode 100644
index 0000000..63eb831
--- /dev/null
+++ b/modules/plugin_test_helper/config/optional/views.view.plugin_svpbf.yml
@@ -0,0 +1,185 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - plugin
+    - plugin_test_helper
+id: plugin_svpbf
+label: 'Single-value plugin base field'
+module: views
+description: ''
+tag: ''
+base_table: plugin_svpbf
+base_field: id
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: none
+        options: {  }
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: mini
+        options:
+          items_per_page: 10
+          offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: ‹‹
+            next: ››
+      style:
+        type: default
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          uses_fields: false
+      row:
+        type: fields
+        options:
+          inline: {  }
+          separator: ''
+          hide_empty: false
+          default_field_elements: true
+      fields:
+        rendered_entity:
+          table: plugin_svpbf
+          field: rendered_entity
+          id: rendered_entity
+          entity_type: null
+          entity_field: null
+          plugin_id: rendered_entity
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ''
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          view_mode: default
+      filters:
+        plugin__plugin_id_filter:
+          id: plugin__plugin_id_filter
+          table: plugin_svpbf
+          field: plugin__plugin_id_filter
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: in
+          value: {  }
+          group: 1
+          exposed: true
+          expose:
+            operator_id: plugin__plugin_id_filter_op
+            label: Plugin
+            description: ''
+            use_operator: false
+            operator: plugin__plugin_id_filter_op
+            identifier: plugin__plugin_id_filter
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              anonymous: anonymous
+              authenticated: authenticated
+              administrator: '0'
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_type: plugin_svpsbf
+          plugin_id: plugin_id
+      sorts: {  }
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments: {  }
+      display_extenders: {  }
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+      tags: {  }
diff --git a/modules/plugin_test_helper/src/Entity/SingleValuePluginBaseField.php b/modules/plugin_test_helper/src/Entity/SingleValuePluginBaseField.php
new file mode 100644
index 0000000..7c15d05
--- /dev/null
+++ b/modules/plugin_test_helper/src/Entity/SingleValuePluginBaseField.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\plugin_test_helper\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+
+/**
+ * Defines an entity with a single-value plugin base field.
+ *
+ * @ContentEntityType(
+ *   base_table = "plugin_svpbf",
+ *   handlers = {
+ *     "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
+ *     "views_data" = "Drupal\views\EntityViewsData"
+ *   },
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *   },
+ *   id = "plugin_svpbf",
+ *   label = @Translation("Single-value value plugin base field")
+ * )
+ */
+class SingleValuePluginBaseField extends ContentEntityBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields = parent::baseFieldDefinitions($entity_type);
+    $fields['plugin'] = BaseFieldDefinition::create('plugin:plugin_test_helper_mock')
+      ->setLabel(t('Plugin'))
+      ->setDisplayOptions('view', array(
+        'type' => 'plugin_label',
+        'weight' => 0,
+      ))
+      ->setDisplayConfigurable('view', TRUE);
+
+    return $fields;
+  }
+
+}
diff --git a/plugin.module b/plugin.module
index fc8387a..a6f0231 100644
--- a/plugin.module
+++ b/plugin.module
@@ -5,6 +5,10 @@
  * Contains hook implementations.
  */
 
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\FieldStorageConfigInterface;
 use Drupal\plugin\Plugin\Field\FieldType\PluginCollectionItemInterface;
 
 function plugin_field_info_alter(array &$field_type_definitions) {
@@ -33,6 +37,109 @@ function plugin_field_widget_info_alter(array &$field_widget_definitions) {
 }
 
 /**
+ * Implements hook_views_data_alter().
+ */
+function plugin_views_data_alter(array &$data) {
+  /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */
+  $entity_field_manager = \Drupal::service('entity_field.manager');
+
+  // We need to work with entity type database table mappings, which are
+  // available per entity type.
+  foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type) {
+    // Skip non-fieldable entity types.
+    if (!$entity_type->isSubclassOf(FieldableEntityInterface::class)) {
+      continue;
+    }
+
+    $field_definitions = $entity_field_manager->getBaseFieldDefinitions($entity_type->id());
+    $entity_storage = \Drupal::entityTypeManager()->getStorage($entity_type->id());
+
+    // We cannot alter Views data if we cannot map fields to tables.
+    if (!($entity_storage instanceof SqlEntityStorageInterface)) {
+      continue;
+    }
+    $table_mapping = $entity_storage->getTableMapping($field_definitions);
+    if (!$table_mapping) {
+      continue;
+    }
+
+    // Loop through all of this entity type's stored fields.
+    foreach ($table_mapping->getTableNames() as $table_name) {
+      foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
+        $field_definition = $field_definitions[$field_name];
+
+        // Skip this field if it's of the wrong type.
+        if (strpos($field_definition->getType(), 'plugin:') !== 0) {
+          continue;
+        }
+
+        $plugin_id_column_name = $table_mapping->getFieldColumnName($field_definition->getFieldStorageDefinition(), 'plugin_id');
+        _plugin_views_data_alter_filter_plugin_id($data, $field_definition->getFieldStorageDefinition(), $table_name, $plugin_id_column_name);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_views_data_alter().
+ */
+function plugin_field_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) {
+  // Add the "plugin_id" filter to all configurable "plugin" fields.
+  if (strpos($field_storage->getType(), 'plugin:') === 0) {
+    $table_name = $field_storage->getTargetEntityTypeId() . '__' . $field_storage->getName();
+    $plugin_id_column_name = $field_storage->getName() . '_plugin_id';
+
+    // Skip if there is no Views data for this field.
+    if (!isset($data[$table_name][$plugin_id_column_name])) {
+      return;
+    }
+
+    _plugin_views_data_alter_filter_plugin_id($data, $field_storage, $table_name, $plugin_id_column_name);
+  }
+}
+
+/**
+ * Alters Views data for the "plugin_id" Views filter plugin.
+ *
+ * @param array[] $data
+ *   An array of Views data.
+ * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
+ *   The storage definition of the field to alter the data for.
+ * @param $table_name
+ *   The name of the table to alter the data for.
+ * @param $plugin_id_column_name
+ *   The name of the original plugin ID column this method adds data to.
+ */
+function _plugin_views_data_alter_filter_plugin_id(array &$data, FieldStorageDefinitionInterface $field_storage_definition, $table_name, $plugin_id_column_name) {
+  if (strpos($field_storage_definition->getType(), 'plugin:') !== 0) {
+    return;
+  }
+
+  if (!isset($data[$table_name][$plugin_id_column_name])) {
+    return;
+  }
+
+  $plugin_id_filter_column_name = $plugin_id_column_name . '_filter';
+  $data[$table_name][$plugin_id_filter_column_name] = [
+    'title' => t('@label (plugin ID filter)', [
+      '@label' => $field_storage_definition->getLabel(),
+    ]),
+    'group' => isset($data[$table_name][$plugin_id_column_name]['group']) ? $data[$table_name][$plugin_id_column_name]['group'] : NULL,
+    'help' => isset($data[$table_name][$plugin_id_column_name]['help']) ? $data[$table_name][$plugin_id_column_name]['help'] : NULL,
+    'filter' => [
+      'field' => $plugin_id_column_name,
+      'table' => $table_name,
+      'id' => 'plugin_id',
+      'additional fields' => [],
+      'field_name' => $field_storage_definition->getName(),
+      'entity_type' => $field_storage_definition->getTargetEntityTypeId(),
+      'plugin_type_id' => substr($field_storage_definition->getType(), 7),
+      'allow empty' => TRUE,
+    ],
+  ];
+}
+
+/**
  * Gets the IDs of plugin item collection field types.
  *
  * @return string[]
diff --git a/src/Plugin/views/filter/PluginId.php b/src/Plugin/views/filter/PluginId.php
new file mode 100644
index 0000000..25cfefd
--- /dev/null
+++ b/src/Plugin/views/filter/PluginId.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace Drupal\plugin\Plugin\views\filter;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\plugin\PluginDefinition\PluginLabelDefinitionInterface;
+use Drupal\plugin\PluginType\PluginTypeInterface;
+use Drupal\views\Plugin\views\filter\InOperator;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a Views filter for plugin IDs.
+ *
+ * @ingroup views_filter_handlers
+ *
+ * @ViewsFilter("plugin_id")
+ */
+final class PluginId extends InOperator implements ContainerFactoryPluginInterface {
+
+  /**
+   * The plugin type.
+   *
+   * @var \Drupal\plugin\PluginType\PluginTypeInterface
+   */
+  protected $pluginType;
+
+  /**
+   * Constructs a new instance.
+   *
+   * @param mixed[] $configuration
+   *   The plugin configuration.
+   * @param string $plugin_id
+   *   The plugin ID.
+   * @param mixed[] $plugin_definition
+   *   The plugin definition.
+   * @param \Drupal\plugin\PluginType\PluginTypeInterface $plugin_type
+   *   The plugin type.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, PluginTypeInterface $plugin_type) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->pluginType = $plugin_type;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    /** @var \Drupal\plugin\PluginType\PluginTypeManagerInterface $plugin_type_manager */
+    $plugin_type_manager = $container->get('plugin.plugin_type_manager');
+
+    return new static($configuration, $plugin_id, $plugin_definition, $plugin_type_manager->getPluginType($configuration['plugin_type_id']));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValueOptions() {
+    if (!is_null($this->valueOptions)) {
+      return $this->valueOptions;
+    }
+
+    $this->valueOptions = array_reduce($this->pluginType->getPluginManager()->getDefinitions(), function(array $value_options, $plugin_definition) {
+      $plugin_definition = $this->pluginType->ensureTypedPluginDefinition($plugin_definition);
+      $value_options[$plugin_definition->getId()] = $plugin_definition instanceof PluginLabelDefinitionInterface ? $plugin_definition->getLabel() : $plugin_definition->getId();
+      return $value_options;
+    }, []);
+    natcasesort($this->valueOptions);
+
+    return $this->valueOptions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function valueForm(&$form, FormStateInterface $form_state) {
+    parent::valueForm($form, $form_state);
+    // Apply cacheability metadata, because the parent class does not.
+    $this->getCacheableMetadata()->applyTo($form);
+
+    return $form;
+  }
+
+  /**
+   * Gets this instance's cacheable metadata.
+   *
+   * @return \Drupal\Core\Cache\CacheableMetadata
+   */
+  protected function getCacheableMetadata() {
+    $cacheable_metadata = new CacheableMetadata();
+    $cacheable_metadata->addCacheableDependency($this->pluginType->getPluginManager());
+    $cacheable_metadata->addCacheTags(parent::getCacheTags());
+    $cacheable_metadata->addCacheContexts(parent::getCacheContexts());
+    $cacheable_metadata->mergeCacheMaxAge(parent::getCacheMaxAge());
+
+    return $cacheable_metadata;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return $this->getCacheableMetadata()->getCacheTags();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    return $this->getCacheableMetadata()->getCacheContexts();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    return $this->getCacheableMetadata()->getCacheMaxAge();
+  }
+
+}
diff --git a/tests/src/Kernel/ViewsFilterPluginIdSingleValueBaseFieldTest.php b/tests/src/Kernel/ViewsFilterPluginIdSingleValueBaseFieldTest.php
new file mode 100644
index 0000000..c07b5f7
--- /dev/null
+++ b/tests/src/Kernel/ViewsFilterPluginIdSingleValueBaseFieldTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\Tests\plugin\Functional\Plugin\views\filter;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the single-value base field integration of the "plugin_id" Views filter.
+ *
+ * @group Plugin
+ */
+class ViewsFilterPluginIdSingleValueBaseFieldTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['plugin', 'plugin_test_helper', 'system', 'views'];
+
+  /**
+   * Tests the integration.
+   */
+  public function testIntegration() {
+    $entity = \Drupal::entityTypeManager()->getStorage('plugin_svpbf')->create();
+    $entity->save();
+
+    // The filter class itself is tested using unit tests. Here we just assert
+    // that a view using this filter does not break.
+    /** @var \Drupal\views\ViewEntityInterface $view */
+    $view = \Drupal::entityTypeManager()->getStorage('view')->load('plugin_svpbf');
+    $view->getExecutable()->execute();
+    $this->assertCount(1, $view->getExecutable()->result);
+  }
+
+}
diff --git a/tests/src/Unit/Plugin/views/filter/PluginIdTest.php b/tests/src/Unit/Plugin/views/filter/PluginIdTest.php
new file mode 100644
index 0000000..8fbdf93
--- /dev/null
+++ b/tests/src/Unit/Plugin/views/filter/PluginIdTest.php
@@ -0,0 +1,208 @@
+<?php
+
+namespace Drupal\Tests\plugin\Unit\Plugin\views\filter;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\plugin\Plugin\views\filter\PluginId;
+use Drupal\plugin\PluginDefinition\PluginDefinitionInterface;
+use Drupal\plugin\PluginDefinition\PluginLabelDefinitionInterface;
+use Drupal\plugin\PluginType\PluginTypeInterface;
+use Drupal\plugin\PluginType\PluginTypeManagerInterface;
+use Drupal\Tests\UnitTestCase;
+use Prophecy\Argument;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @coversDefaultClass \Drupal\plugin\Plugin\views\filter\PluginId
+ *
+ * @group Plugin
+ */
+class PluginIdTest extends UnitTestCase {
+
+  /**
+   * The plugin type.
+   *
+   * @var \Drupal\plugin\PluginType\PluginTypeInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $pluginType;
+
+  /**
+   * The system under test.
+   *
+   * @var \Drupal\plugin\Plugin\views\filter\PluginId
+   */
+  protected $sut;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $plugin_type_id = 'foo_bar';
+    $plugin_id = '';
+    $plugin_definition = [
+      'id' => $plugin_id,
+    ];
+    $configuration = [
+      'plugin_type_id' => $plugin_type_id,
+    ];
+
+    $this->pluginType = $this->prophesize(PluginTypeInterface::class);
+
+    $this->sut = new PluginId($configuration, $plugin_id, $plugin_definition, $this->pluginType->reveal());
+  }
+
+  /**
+   * @covers ::create
+   * @covers ::__construct
+   */
+  public function testCreate() {
+    $plugin_type_id = 'foo_bar';
+    $plugin_id = '';
+    $plugin_definition = [
+      'id' => $plugin_id,
+    ];
+    $configuration = [
+      'plugin_type_id' => $plugin_type_id,
+    ];
+
+    $plugin_type_manager = $this->prophesize(PluginTypeManagerInterface::class);
+    $plugin_type_manager->getPluginType($plugin_type_id)->wilLReturn($this->pluginType->reveal());
+
+    $container = $this->prophesize(ContainerInterface::class);
+    $container->get('plugin.plugin_type_manager')->willReturn($plugin_type_manager->reveal());
+
+    $this->sut = PluginId::create($container->reveal(), $configuration, $plugin_id, $plugin_definition);
+    $this->assertInstanceOf(PluginId::class, $this->sut);
+  }
+
+  /**
+   * @covers ::getCacheContexts
+   * @covers ::getCacheableMetadata
+   */
+  public function testCacheContexts() {
+    $plugin_manager_cache_contexts = ['dog', 'ball'];
+
+    $plugin_manager = $this->prophesize(CacheableDependencyPluginManagerInterface::class);
+    $plugin_manager->getCacheContexts()->willReturn($plugin_manager_cache_contexts);
+    $plugin_manager->getCacheTags()->willReturn([]);
+    $plugin_manager->getCacheMaxAge()->willReturn(0);
+
+    $this->pluginType->getPluginManager()->willReturn($plugin_manager->reveal());
+
+    // Temporarily disable asserts, because Cache::mergeContexts() calls
+    // \Drupal::service(). See https://www.drupal.org/node/2720947.
+    assert_options(ASSERT_ACTIVE, FALSE);
+    $cache_contexts = $this->sut->getCacheContexts();
+    $this->assertInternalType('array', $cache_contexts);
+    foreach ($plugin_manager_cache_contexts as $plugin_manager_cache_context) {
+      $this->assertTrue(in_array($plugin_manager_cache_context, $cache_contexts));
+    }
+    assert_options(ASSERT_ACTIVE, TRUE);
+  }
+
+  /**
+   * @covers ::getCacheTags
+   * @covers ::getCacheableMetadata
+   */
+  public function testCacheTags() {
+    $plugin_manager_cache_tags = ['bar', 'foo'];
+
+    $plugin_manager = $this->prophesize(CacheableDependencyPluginManagerInterface::class);
+    $plugin_manager->getCacheContexts()->willReturn([]);
+    $plugin_manager->getCacheTags()->willReturn($plugin_manager_cache_tags);
+    $plugin_manager->getCacheMaxAge()->willReturn(0);
+
+    $this->pluginType->getPluginManager()->willReturn($plugin_manager->reveal());
+
+    $this->assertArraySubset($plugin_manager_cache_tags, $this->sut->getCacheTags());
+  }
+
+  /**
+   * @covers ::getCacheMaxAge
+   * @covers ::getCacheableMetadata
+   *
+   * @dataProvider provideCacheMaxAge
+   */
+  public function testCacheMaxAge($expected, $plugin_manager_max_age) {
+    $plugin_manager = $this->prophesize(CacheableDependencyPluginManagerInterface::class);
+    $plugin_manager->getCacheContexts()->willReturn([]);
+    $plugin_manager->getCacheTags()->willReturn([]);
+    $plugin_manager->getCacheMaxAge()->willReturn($plugin_manager_max_age);
+
+    $this->pluginType->getPluginManager()->willReturn($plugin_manager->reveal());
+
+    $this->assertSame($expected, $this->sut->getCacheMaxAge());
+  }
+
+  /**
+   * Provides data to self::testCacheMaxAge().
+   */
+  public function provideCacheMaxAge() {
+    $data = [];
+
+    $data['plugin-manager-permanent'] = [Cache::PERMANENT, Cache::PERMANENT];
+    $data['plugin-manager-never'] = [0, 0];
+    $data['plugin-manager-limited'] = [7, 7];
+
+    return $data;
+  }
+
+  /**
+   * @covers ::getValueOptions
+   * @covers ::getCacheableMetadata
+   */
+  public function testGetValueOptions() {
+    $plugin_label_1 = 'Foo';
+    $plugin_id_1 = 'aaa_foo';
+    $plugin_id_2 = 'baz';
+    $plugin_id_3 = 'qux';
+    $plugin_label_4 = 'Bar';
+    $plugin_id_4 = 'zzz_bar';
+
+    // Values must be sorted naturally.
+    $expected = [
+      $plugin_id_4 => $plugin_label_4,
+      $plugin_id_2 => $plugin_id_2,
+      $plugin_id_1 => $plugin_label_1,
+      $plugin_id_3 => $plugin_id_3,
+    ];
+
+    $plugin_definition_1 = $this->prophesize(PluginLabelDefinitionInterface::class);
+    $plugin_definition_1->getId()->willReturn($plugin_id_1);
+    $plugin_definition_1->getLabel()->willReturn($plugin_label_1);
+    $plugin_definition_2 = $this->prophesize(PluginDefinitionInterface::class);
+    $plugin_definition_2->getId()->willReturn($plugin_id_2);
+    $plugin_definition_3 = $this->prophesize(PluginDefinitionInterface::class);
+    $plugin_definition_3->getId()->willReturn($plugin_id_3);
+    $plugin_definition_4 = $this->prophesize(PluginLabelDefinitionInterface::class);
+    $plugin_definition_4->getId()->willReturn($plugin_id_4);
+    $plugin_definition_4->getLabel()->willReturn($plugin_label_4);
+
+    $plugin_definitions = [
+      $plugin_id_1 => $plugin_definition_1,
+      $plugin_id_2 => $plugin_definition_2,
+      $plugin_id_3 => $plugin_definition_3,
+      $plugin_id_4 => $plugin_definition_4,
+    ];
+
+    $plugin_manager = $this->prophesize(PluginManagerInterface::class);
+    $plugin_manager->getDefinitions()->willReturn($plugin_definitions);
+
+    $this->pluginType->ensureTypedPluginDefinition(Argument::any())->willReturnArgument();
+    $this->pluginType->getPluginManager()->willReturn($plugin_manager);
+
+    $this->assertSame($expected, $this->sut->getValueOptions());
+  }
+
+
+}
+
+/**
+ * Defines a plugin manager which is also a cacheable dependency.
+ */
+interface CacheableDependencyPluginManagerInterface extends PluginManagerInterface, CacheableDependencyInterface {
+}
