diff --git a/modules/oe_whitelabel_paragraphs/oe_whitelabel_paragraphs.install b/modules/oe_whitelabel_paragraphs/oe_whitelabel_paragraphs.install
index a6ba792d2f966d74cde1b6080210a1a14f530633..0479caf550404e4163efd873c974a9d3c2182591 100644
--- a/modules/oe_whitelabel_paragraphs/oe_whitelabel_paragraphs.install
+++ b/modules/oe_whitelabel_paragraphs/oe_whitelabel_paragraphs.install
@@ -7,19 +7,52 @@
 
 declare(strict_types = 1);
 
+use Drupal\field\Entity\FieldConfig;
 use Drupal\oe_bootstrap_theme\ConfigImporter;
 
 /**
  * Implements hook_install().
  *
- * Customise fields for whitelabel paragraphs.
+ * Customizes paragraphs fields and display configuration.
+ *
+ * If oe_bootstrap_theme_paragraphs was installed in the past, this will also
+ * migrate field data and clean up fields that have been renamed.
  */
-function oe_whitelabel_paragraphs_install($is_syncing): void {
-  // If we are installing from config, we bail out.
+function oe_whitelabel_paragraphs_install(bool $is_syncing): void {
+  // Find legacy fields from oe_bootstrap_theme_paragraphs.
+  // This needs to happen at the start, to allow for early abort.
+  $field_names_by_bundle = _oe_whitelabel_paragraphs_install_get_legacy_fields_map();
+
   if ($is_syncing) {
+    // The module is being installed as part of a config import.
+    if ($field_names_by_bundle) {
+      // There is data to be migrated. This should not happen as a side effect
+      // of config-import. Instead, the installation should be enacted from an
+      // update hook.
+      throw new \Exception('This module should be installed through an update hook, not through config-import, if there is still leftover data from oe_bootstrap_theme_paragraphs to migrate.');
+    }
+    // No data needs to be migrated, but still, no configuration should be
+    // imported in hook_install() during config-import.
     return;
   }
 
+  // The module is being installed explicitly, e.g. via a hook_update_N().
+  // Configuration needs to be imported explicitly.
+  _oe_whitelabel_paragraphs_install_config();
+
+  if (!$field_names_by_bundle) {
+    // No fields to migrate and clean up - finished.
+    return;
+  }
+
+  _oe_whitelabel_paragraphs_install_migrate_field_data($field_names_by_bundle);
+  _oe_whitelabel_paragraphs_install_drop_legacy_fields($field_names_by_bundle);
+}
+
+/**
+ * Imports configuration on module install.
+ */
+function _oe_whitelabel_paragraphs_install_config(): void {
   $configs = [
     'core.entity_form_display.paragraph.oe_accordion_item.default',
     'core.entity_form_display.paragraph.oe_description_list.default',
@@ -39,3 +72,122 @@ function oe_whitelabel_paragraphs_install($is_syncing): void {
 
   ConfigImporter::importMultiple('oe_whitelabel_paragraphs', '/config/overrides/', $configs);
 }
+
+/**
+ * Gets a map of legacy fields to be migrated on install.
+ *
+ * @return string[][]
+ *   Legacy field names, indexed by paragraph type and destination field name.
+ *   Only contains entries where both the legacy field name and the destination
+ *   field name do exist.
+ *   Empty, if oe_bootstrap_theme_paragraphs was not installed in the past.
+ */
+function _oe_whitelabel_paragraphs_install_get_legacy_fields_map(): array {
+  /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */
+  $entity_field_manager = \Drupal::service('entity_field.manager');
+  $fields_map = $entity_field_manager->getFieldMap()['paragraph'] ?? [];
+
+  $field_names_by_bundle = [
+    'oe_description_list' => [
+      'oe_w_orientation' => 'oe_bt_orientation',
+    ],
+    'oe_facts_figures' => [
+      'oe_w_n_columns' => 'oe_bt_n_columns',
+    ],
+    'oe_links_block' => [
+      'oe_w_links_block_background' => 'oe_bt_links_block_background',
+      'oe_w_links_block_orientation' => 'oe_bt_links_block_orientation',
+    ],
+    'oe_social_media_follow' => [
+      'oe_w_links_block_background' => 'oe_bt_links_block_background',
+    ],
+  ];
+
+  foreach ($field_names_by_bundle as $bundle => $field_names) {
+    foreach ($field_names as $dest_field_name => $source_field_name) {
+      if (!isset($fields_map[$source_field_name]['bundles'][$bundle])) {
+        // The legacy field does not exist.
+        // Perhaps the field or the paragraph type were removed manually.
+        unset($field_names_by_bundle[$bundle][$dest_field_name]);
+        continue;
+      }
+      if (!isset($fields_map[$dest_field_name]['bundles'][$bundle])) {
+        // A destination field is missing, that should have been created
+        // earlier. This could happen if a paragraph type was removed manually,
+        // but in that case the install should have already failed earlier.
+        // Either way, this case is not supported.
+        throw new \RuntimeException("Destination field 'paragraph.$bundle.$dest_field_name' was not properly created.");
+      }
+    }
+  }
+
+  return $field_names_by_bundle;
+}
+
+/**
+ * Migrates field data from the old oe_bootstrap_theme_paragraphs module.
+ *
+ * This should happen through a batch process, e.g. via $sandbox in a
+ * hook_update_N(). Unfortunately, hook_install() does not support batch
+ * processes.
+ *
+ * @param string[][] $field_names_by_bundle
+ *   Legacy field names, indexed by paragraph type and destination field name.
+ */
+function _oe_whitelabel_paragraphs_install_migrate_field_data(array $field_names_by_bundle): void {
+  $paragraphs_storage = \Drupal::entityTypeManager()->getStorage('paragraph');
+
+  // Load all the paragraph ids.
+  $query = $paragraphs_storage->getQuery();
+  $query->allRevisions();
+  $query->condition('type', array_keys($field_names_by_bundle), 'IN');
+  $paragraph_ids = $query->execute();
+
+  foreach ($paragraph_ids as $revision_id => $paragraph_id) {
+    // Revision can't be NULL.
+    /** @var \Drupal\paragraphs\ParagraphInterface $paragraph_revision */
+    $paragraph_revision = $paragraphs_storage->loadRevision($revision_id);
+    $field_names_map = $field_names_by_bundle[$paragraph_revision->bundle()];
+    $modified = FALSE;
+    foreach ($field_names_map as $dest_field_name => $source_field_name) {
+      if ($paragraph_revision->get($source_field_name)->isEmpty()) {
+        // Source field has no data.
+        continue;
+      }
+      if (!$paragraph_revision->get($dest_field_name)->isEmpty()) {
+        // Destination already has data.
+        continue;
+      }
+      // Copy the field value.
+      // For these simple field types, magic __set() does the job.
+      $paragraph_revision->$dest_field_name = $paragraph_revision->$source_field_name;
+      // Do not unset the old field, because it might be required.
+      // Remember that the revision needs saving.
+      $modified = TRUE;
+    }
+    if (!$modified) {
+      // No saving is needed.
+      continue;
+    }
+    $paragraph_revision->setNewRevision(FALSE);
+    $paragraph_revision->save();
+  }
+}
+
+/**
+ * Removes legacy field instances from oe_bootstrap_theme_paragraphs module.
+ *
+ * @param string[][] $field_names_by_bundle
+ *   Legacy field names, indexed by paragraph type and destination field name.
+ */
+function _oe_whitelabel_paragraphs_install_drop_legacy_fields(array $field_names_by_bundle): void {
+  foreach ($field_names_by_bundle as $bundle => $legacy_field_names) {
+    foreach ($legacy_field_names as $legacy_field_name) {
+      $field_config = FieldConfig::loadByName('paragraph', $bundle, $legacy_field_name);
+      if ($field_config === NULL) {
+        throw new \RuntimeException("Legacy field 'paragraph.$bundle.$legacy_field_name' not found.");
+      }
+      $field_config->delete();
+    }
+  }
+}
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_description_list.oe_bt_orientation.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_description_list.oe_bt_orientation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..73b09a472d8fca82e66e5c1a1ccda494d88fb33c
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_description_list.oe_bt_orientation.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.paragraph.oe_bt_orientation
+    - paragraphs.paragraphs_type.oe_description_list
+  module:
+    - options
+id: paragraph.oe_description_list.oe_bt_orientation
+field_name: oe_bt_orientation
+entity_type: paragraph
+bundle: oe_description_list
+label: Orientation
+description: 'Sets the orientation (vertical|horizontal) for the paragraph.'
+required: true
+translatable: false
+default_value:
+  -
+    value: horizontal
+default_value_callback: ''
+settings: {  }
+field_type: list_string
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_facts_figures.oe_bt_n_columns.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_facts_figures.oe_bt_n_columns.yml
new file mode 100644
index 0000000000000000000000000000000000000000..eec70a14e1e037db51893287df01d018fc6b868e
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_facts_figures.oe_bt_n_columns.yml
@@ -0,0 +1,24 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.paragraph.oe_bt_n_columns
+    - paragraphs.paragraphs_type.oe_facts_figures
+id: paragraph.oe_facts_figures.oe_bt_n_columns
+field_name: oe_bt_n_columns
+entity_type: paragraph
+bundle: oe_facts_figures
+label: 'Number of columns'
+description: 'Sets the number of grid columns. Minimum number is 1 column and maximum is 3.'
+required: false
+translatable: false
+default_value:
+  -
+    value: 1
+default_value_callback: ''
+settings:
+  min: 1
+  max: 3
+  prefix: ''
+  suffix: ''
+field_type: integer
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_links_block.oe_bt_links_block_background.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_links_block.oe_bt_links_block_background.yml
new file mode 100644
index 0000000000000000000000000000000000000000..de66c69713d5ed89c8815b1dbdb3f599a21eb2cd
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_links_block.oe_bt_links_block_background.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.paragraph.oe_bt_links_block_background
+    - paragraphs.paragraphs_type.oe_links_block
+  module:
+    - options
+id: paragraph.oe_links_block.oe_bt_links_block_background
+field_name: oe_bt_links_block_background
+entity_type: paragraph
+bundle: oe_links_block
+label: Background
+description: 'Allows to select the background color of the links block.'
+required: false
+translatable: false
+default_value:
+  -
+    value: gray
+default_value_callback: ''
+settings: {  }
+field_type: list_string
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_links_block.oe_bt_links_block_orientation.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_links_block.oe_bt_links_block_orientation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..610c35d3739f035f44364e2852bebd7247e1698c
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_links_block.oe_bt_links_block_orientation.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.paragraph.oe_bt_links_block_orientation
+    - paragraphs.paragraphs_type.oe_links_block
+  module:
+    - options
+id: paragraph.oe_links_block.oe_bt_links_block_orientation
+field_name: oe_bt_links_block_orientation
+entity_type: paragraph
+bundle: oe_links_block
+label: Orientation
+description: 'Allows to select the direction of the links block.'
+required: false
+translatable: false
+default_value:
+  -
+    value: vertical
+default_value_callback: ''
+settings: {  }
+field_type: list_string
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_social_media_follow.oe_bt_links_block_background.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_social_media_follow.oe_bt_links_block_background.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8e167195da6f02c6d387c8ad0c0426a04a6123cb
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.field.paragraph.oe_social_media_follow.oe_bt_links_block_background.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.paragraph.oe_bt_links_block_background
+    - paragraphs.paragraphs_type.oe_social_media_follow
+  module:
+    - options
+id: paragraph.oe_social_media_follow.oe_bt_links_block_background
+field_name: oe_bt_links_block_background
+entity_type: paragraph
+bundle: oe_social_media_follow
+label: Background
+description: 'Allows to select the background color of the Social Media Links block.'
+required: false
+translatable: false
+default_value:
+  -
+    value: gray
+default_value_callback: ''
+settings: {  }
+field_type: list_string
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_links_block_background.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_links_block_background.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1a7dbec06489349b31b105c506d6256d6ca9d4f8
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_links_block_background.yml
@@ -0,0 +1,26 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - options
+    - paragraphs
+id: paragraph.oe_bt_links_block_background
+field_name: oe_bt_links_block_background
+entity_type: paragraph
+type: list_string
+settings:
+  allowed_values:
+    -
+      value: gray
+      label: Gray
+    -
+      value: transparent
+      label: Transparent
+  allowed_values_function: ''
+module: options
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_links_block_orientation.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_links_block_orientation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d582eb8126c744b0643c987431a6cc6dfa4cbd20
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_links_block_orientation.yml
@@ -0,0 +1,26 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - options
+    - paragraphs
+id: paragraph.oe_bt_links_block_orientation
+field_name: oe_bt_links_block_orientation
+entity_type: paragraph
+type: list_string
+settings:
+  allowed_values:
+    -
+      value: horizontal
+      label: Horizontal
+    -
+      value: vertical
+      label: Vertical
+  allowed_values_function: ''
+module: options
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_n_columns.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_n_columns.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f2a740718db02227ad37f1ae65b8a425745e32e1
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_n_columns.yml
@@ -0,0 +1,19 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - paragraphs
+id: paragraph.oe_bt_n_columns
+field_name: oe_bt_n_columns
+entity_type: paragraph
+type: integer
+settings:
+  unsigned: false
+  size: normal
+module: core
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_orientation.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_orientation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..94f290a8e681aa0e93bef75192d167d80ea24ae5
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/config/install/field.storage.paragraph.oe_bt_orientation.yml
@@ -0,0 +1,26 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - options
+    - paragraphs
+id: paragraph.oe_bt_orientation
+field_name: oe_bt_orientation
+entity_type: paragraph
+type: list_string
+settings:
+  allowed_values:
+    -
+      value: vertical
+      label: Vertical
+    -
+      value: horizontal
+      label: Horizontal
+  allowed_values_function: ''
+module: options
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/oe_whitelabel_legacy_paragraphs_test.info.yml b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/oe_whitelabel_legacy_paragraphs_test.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4cb09829c9ca4f6105cbd9ca32deef9ca7f258d5
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/modules/oe_whitelabel_legacy_paragraphs_test/oe_whitelabel_legacy_paragraphs_test.info.yml
@@ -0,0 +1,9 @@
+name: OE Whitelabel - Legacy Paragraphs Test
+type: module
+description: Provides legacy configuration as in oe_bootstrap_theme_paragraphs.
+package: Testing
+hidden: true
+dependencies:
+  - drupal:description_list_field
+  - oe_paragraphs:oe_paragraphs
+  - oe_paragraphs:oe_paragraphs_description_list
diff --git a/modules/oe_whitelabel_paragraphs/tests/src/Functional/InstallTest.php b/modules/oe_whitelabel_paragraphs/tests/src/Functional/InstallTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e6a20318f1f4f0c372696edcb19f4ec561651a8c
--- /dev/null
+++ b/modules/oe_whitelabel_paragraphs/tests/src/Functional/InstallTest.php
@@ -0,0 +1,218 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\Tests\oe_whitelabel_paragraphs\Functional;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\paragraphs\Entity\Paragraph;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests installation of oe_whitelabel_paragraphs.
+ *
+ * @see oe_whitelabel_paragraphs_install()
+ */
+class InstallTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'node',
+    'oe_whitelabel_legacy_paragraphs_test',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * Test installation with legacy fields and data present.
+   */
+  public function testInstallWithLegacyParagraphs(): void {
+    $paragraphs_data = [];
+    $paragraphs_data['oe_description_list'] = [
+      'type' => 'oe_description_list',
+      'field_oe_title' => 'Description list paragraph',
+      // This field will be renamed.
+      'oe_bt_orientation' => 'horizontal',
+      'field_oe_description_list_items' => [
+        // One item is enough for this test.
+        [
+          'term' => 'Aliquam ultricies',
+          'description' => 'Donec et leo ac velit posuere tempor mattis ac mi. Vivamus nec dictum lectus. Aliquam ultricies placerat eros, vitae ornare sem.',
+        ],
+      ],
+    ];
+    $paragraphs_data['oe_facts_figures'] = [
+      'type' => 'oe_facts_figures',
+      'field_oe_title' => 'Fact and figures block',
+      'field_oe_link' => [
+        'uri' => 'https://www.readmore.com',
+        'title' => 'Read more',
+      ],
+      // This field will be renamed.
+      'oe_bt_n_columns' => 3,
+      'field_oe_paragraphs' => [
+        'type' => 'oe_fact',
+        'field_oe_icon' => 'box-arrow-up',
+        'field_oe_title' => '1529 JIRA Ticket',
+        'field_oe_subtitle' => 'Jira Tickets',
+        'field_oe_plain_text_long' => 'Nunc condimentum sapien ut nibh finibus suscipit vitae at justo. Morbi quis odio faucibus, commodo tortor id, elementum libero.',
+      ],
+    ];
+    $paragraphs_data['oe_links_block'] = [
+      'type' => 'oe_links_block',
+      'field_oe_text' => 'More information',
+      // These fields will be renamed.
+      'oe_bt_links_block_orientation' => 'vertical',
+      'oe_bt_links_block_background' => 'gray',
+      'field_oe_links' => [
+        // One link is enough for this test.
+        [
+          'title' => 'European Commission',
+          'uri' => 'https://example.com',
+        ],
+      ],
+    ];
+    $paragraphs_data['oe_social_media_follow'] = [
+      'type' => 'oe_social_media_follow',
+      'field_oe_title' => 'Social media title',
+      'field_oe_social_media_variant' => 'horizontal',
+      // This field will be renamed.
+      'oe_bt_links_block_background' => 'gray',
+      // One link is enough for this test.
+      'field_oe_social_media_links' => [
+        [
+          'title' => 'Email',
+          'uri' => 'mailto:example@com',
+          'link_type' => 'email',
+        ],
+      ],
+      'field_oe_social_media_see_more' => [
+        'title' => 'Other social networks',
+        'uri' => 'https://europa.eu/european-union/contact/social-networks_en',
+      ],
+    ];
+
+    $revision_ids = [];
+    foreach ($paragraphs_data as $name => $paragraph_data) {
+      $paragraph = Paragraph::create($paragraph_data);
+      $paragraph->save();
+      $revision_ids[$name] = $paragraph->getRevisionId();
+      if ($name !== 'oe_links_block') {
+        // Don't create a revision for most of the paragraphs.
+        continue;
+      }
+      // Make this paragraph a revision.
+      $paragraph->setNewRevision();
+      $paragraph->oe_bt_links_block_orientation = 'horizontal';
+      $paragraph->save();
+      $revision_ids[$name . ':modified'] = $paragraph->getRevisionId();
+    }
+
+    $legacy_field_config_ids = [
+      'paragraph.oe_description_list.oe_bt_orientation',
+      'paragraph.oe_facts_figures.oe_bt_n_columns',
+      'paragraph.oe_links_block.oe_bt_links_block_background',
+      'paragraph.oe_links_block.oe_bt_links_block_orientation',
+      'paragraph.oe_social_media_follow.oe_bt_links_block_background',
+    ];
+    $legacy_field_storage_ids = [
+      'paragraph.oe_bt_links_block_background',
+      'paragraph.oe_bt_links_block_orientation',
+      'paragraph.oe_bt_n_columns',
+      'paragraph.oe_bt_orientation',
+    ];
+    $this->assertEqualsCanonicalizing(
+      $legacy_field_config_ids,
+      array_keys(FieldConfig::loadMultiple($legacy_field_config_ids)),
+    );
+    $this->assertEqualsCanonicalizing(
+      $legacy_field_storage_ids,
+      array_keys(FieldStorageConfig::loadMultiple($legacy_field_storage_ids)),
+    );
+
+    /** @var \Drupal\Core\Extension\ModuleInstallerInterface $installer */
+    $installer = \Drupal::service('module_installer');
+    $installer->install(['oe_whitelabel_paragraphs']);
+
+    $this->assertTrue(
+      \Drupal::moduleHandler()->moduleExists('oe_whitelabel_paragraphs'),
+      "Module 'oe_whitelabel_paragraphs was successfully installed.");
+
+    $this->assertEmpty(FieldConfig::loadMultiple($legacy_field_config_ids));
+    $this->assertEmpty(FieldStorageConfig::loadMultiple($legacy_field_storage_ids));
+
+    $expected_created = [
+      'oe_description_list' => [
+        'oe_w_orientation' => 'horizontal',
+      ],
+      'oe_facts_figures' => [
+        'oe_w_n_columns' => '3',
+      ],
+      'oe_links_block' => [
+        'oe_w_links_block_orientation' => 'vertical',
+        'oe_w_links_block_background' => 'gray',
+      ],
+      'oe_links_block:modified' => [
+        'oe_w_links_block_orientation' => 'horizontal',
+        'oe_w_links_block_background' => 'gray',
+      ],
+      'oe_social_media_follow' => [
+        'oe_w_links_block_background' => 'gray',
+      ],
+    ];
+
+    $expected_deleted = [
+      'oe_description_list' => [
+        'oe_bt_orientation' => TRUE,
+      ],
+      'oe_facts_figures' => [
+        'oe_bt_n_columns' => TRUE,
+      ],
+      'oe_links_block' => [
+        'oe_bt_links_block_orientation' => TRUE,
+        'oe_bt_links_block_background' => TRUE,
+      ],
+      'oe_links_block:modified' => [
+        'oe_bt_links_block_orientation' => TRUE,
+        'oe_bt_links_block_background' => TRUE,
+      ],
+      'oe_social_media_follow' => [
+        'oe_bt_links_block_background' => TRUE,
+      ],
+    ];
+
+    $storage = \Drupal::entityTypeManager()->getStorage('paragraph');
+
+    // Produce reports instead of many individual assertions. This is less
+    // simple in code, but produces more useful output on test failure.
+    $actual_updated = [];
+    $actual_deleted = [];
+    foreach ($revision_ids as $name => $revision_id) {
+      $updated_revision = $storage->loadRevision($revision_id);
+      $this->assertNotNull($updated_revision);
+      foreach ($expected_created[$name] as $field_name => $value) {
+        if (!$updated_revision->hasField($field_name)) {
+          // The expected field was not created.
+          // Omit this entry in $actual_updated, to cause a fail below.
+          continue;
+        }
+        // The expected field was created, but the value might be wrong.
+        $actual_updated[$name][$field_name] = $updated_revision->get($field_name)->value;
+      }
+      foreach ($expected_deleted[$name] as $field_name => $deleted) {
+        $actual_deleted[$name][$field_name] = !$updated_revision->hasField($field_name);
+      }
+    }
+
+    // Compare the reports to the expected values.
+    $this->assertSame($expected_created, $actual_updated);
+    $this->assertSame($expected_deleted, $actual_deleted);
+  }
+
+}