diff --git a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
index 82088a988c39b2eb72a7c9b2dd602e06692b88da..5490a50622fe5e15094b860c3dbc9104aa3fbf74 100644
--- a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
+++ b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
@@ -129,19 +129,25 @@ class RandomDataPlugin extends ProjectBrowserSourceBase {
     );
     $filters['categories'] = new MultipleChoiceFilter($choices, [], $this->t('Categories'), NULL);
 
-    $filters['maintained'] = new BooleanFilter(
+    $filters['securityCoverage'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show actively maintained projects'),
+      $this->t('Show projects covered by a security policy'),
+      $this->t('Show all'),
+      $this->t('Security Advisory Coverage'),
       NULL,
     );
-    $filters['security_advisories'] = new BooleanFilter(
+    $filters['maintenanceStatus'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show projects covered by a security policy'),
+      $this->t('Show actively maintained projects'),
+      $this->t('Show all'),
+      $this->t('Maintenance Status'),
       NULL,
     );
-    $filters['developed'] = new BooleanFilter(
+    $filters['developmentStatus'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show projects under active development'),
+      $this->t('Show projects under active development'),
+      $this->t('Show all'),
+      $this->t('Development Status'),
       NULL,
     );
 
diff --git a/src/DevelopmentStatus.php b/src/DevelopmentStatus.php
index 2689728cb7ff3a6b17ebc3c21b8269b7f091c8b6..577db57d078d24711a5bfc32a82b08b2bcb2bc86 100644
--- a/src/DevelopmentStatus.php
+++ b/src/DevelopmentStatus.php
@@ -11,8 +11,8 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
  */
 enum DevelopmentStatus: string {
 
-  case Active = 'active';
-  case All = 'all';
+  case Active = '1';
+  case All = '0';
 
   /**
    * Represents this enum as a set of options.
@@ -37,7 +37,7 @@ enum DevelopmentStatus: string {
    */
   public function label(): TranslatableMarkup {
     return match ($this) {
-      self::Active => t('Active'),
+      self::Active => t('Show projects under active development'),
       self::All => t('Show all'),
     };
   }
diff --git a/src/MaintenanceStatus.php b/src/MaintenanceStatus.php
index ad50651be6d09c172dfb84d23e043d1421aac757..42cae92aa93135e981cd501e446d2c9d129ef072 100644
--- a/src/MaintenanceStatus.php
+++ b/src/MaintenanceStatus.php
@@ -11,8 +11,8 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
  */
 enum MaintenanceStatus: string {
 
-  case Maintained = 'maintained';
-  case All = 'all';
+  case Maintained = '1';
+  case All = '0';
 
   /**
    * Represents this enum as a set of options.
@@ -37,7 +37,7 @@ enum MaintenanceStatus: string {
    */
   public function label(): TranslatableMarkup {
     return match ($this) {
-      self::Maintained => t('Maintained'),
+      self::Maintained => t('Show actively maintained projects'),
       self::All => t('Show all'),
     };
   }
diff --git a/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php b/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
index 7b2d60648ccd3c465a601a2274232e3406304685..1bd974d45844848353a7de74447d8bb7d06d0db5 100644
--- a/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
+++ b/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
@@ -263,19 +263,25 @@ class DrupalDotOrgJsonApi extends ProjectBrowserSourceBase {
     );
     $filters['categories'] = new MultipleChoiceFilter($choices, [], $this->t('Categories'), NULL);
 
-    $filters['maintained'] = new BooleanFilter(
+    $filters['securityCoverage'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show actively maintained projects'),
+      $this->t('Show projects covered by a security policy'),
+      $this->t('Show all'),
+      $this->t('Security Advisory Coverage'),
       NULL,
     );
-    $filters['security_advisories'] = new BooleanFilter(
+    $filters['maintenanceStatus'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show projects covered by a security policy'),
+      $this->t('Show actively maintained projects'),
+      $this->t('Show all'),
+      $this->t('Maintenance Status'),
       NULL,
     );
-    $filters['developed'] = new BooleanFilter(
+    $filters['developmentStatus'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show projects under active development'),
+      $this->t('Show projects under active development'),
+      $this->t('Show all'),
+      $this->t('Development Status'),
       NULL,
     );
 
diff --git a/src/Plugin/ProjectBrowserSourceBase.php b/src/Plugin/ProjectBrowserSourceBase.php
index d544e7cf288f86c08562583e2d21fd134553edb6..c63cbf98444fd0434f18b9dd18e8a75900e7d15e 100644
--- a/src/Plugin/ProjectBrowserSourceBase.php
+++ b/src/Plugin/ProjectBrowserSourceBase.php
@@ -43,7 +43,7 @@ abstract class ProjectBrowserSourceBase extends PluginBase implements ProjectBro
         array_column($categories, 'id'),
         array_column($categories, 'name'),
       );
-      $filters['categories'] = new MultipleChoiceFilter($choices, [], $this->t('Categories'), NULL);
+      $filters['categories'] = new MultipleChoiceFilter($choices, [], $this->t('Categories'), $this->t('Categories'), NULL);
     }
     return $filters;
   }
diff --git a/src/ProjectBrowser/Filter/BooleanFilter.php b/src/ProjectBrowser/Filter/BooleanFilter.php
index 88738ef10bc7b5c78ea826e53780e1b49cf8dfab..447bb768d59212d8d97e87c6c0e5b6d8676139aa 100644
--- a/src/ProjectBrowser/Filter/BooleanFilter.php
+++ b/src/ProjectBrowser/Filter/BooleanFilter.php
@@ -9,7 +9,7 @@ namespace Drupal\project_browser\ProjectBrowser\Filter;
  */
 final class BooleanFilter extends FilterBase {
 
-  public function __construct(public bool $value, mixed ...$arguments) {
+  public function __construct(public bool $value, public string|\Stringable $on_label, public string|\Stringable $off_label, mixed ...$arguments) {
     parent::__construct(...$arguments);
   }
 
diff --git a/src/ProjectBrowser/Filter/FilterBase.php b/src/ProjectBrowser/Filter/FilterBase.php
index a48129bdc272c6aa6dfe58246d67b3dddde9bee9..fa7f1ce47d4e44c788cfc93ad7972978022fc5a6 100644
--- a/src/ProjectBrowser/Filter/FilterBase.php
+++ b/src/ProjectBrowser/Filter/FilterBase.php
@@ -18,12 +18,17 @@ abstract class FilterBase implements \JsonSerializable {
    * {@inheritdoc}
    */
   final public function jsonSerialize(): array {
-    return [
+    $values = [
       '_type' => match (static::class) {
         BooleanFilter::class => 'boolean',
         MultipleChoiceFilter::class => 'multiple_choice',
       },
     ] + get_object_vars($this);
+
+    return array_map(
+      fn ($value) => $value instanceof \Stringable ? (string) $value : $value,
+      $values,
+    );
   }
 
 }
diff --git a/src/SecurityStatus.php b/src/SecurityStatus.php
index 03e76db54b3b69f7b7ab570c5431f7cc154033e9..955975d82a178665621e7d555439e1db9190bb43 100644
--- a/src/SecurityStatus.php
+++ b/src/SecurityStatus.php
@@ -11,8 +11,8 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
  */
 enum SecurityStatus: string {
 
-  case Covered = 'covered';
-  case All = 'all';
+  case Covered = '1';
+  case All = '0';
 
   /**
    * Represents this enum as a set of options.
@@ -37,7 +37,7 @@ enum SecurityStatus: string {
    */
   public function label(): TranslatableMarkup {
     return match ($this) {
-      self::Covered => t('Covered by a security policy'),
+      self::Covered => t('Show projects covered by a security policy'),
       self::All => t('Show all'),
     };
   }
diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js
index 2c4efd15b88d1f41ab74193a32214586b5be2589..7ba3332fa6c923c9f9055a309636c5455ddb8626 100644
Binary files a/sveltejs/public/build/bundle.js and b/sveltejs/public/build/bundle.js differ
diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map
index d8a99df334c3b0e1fe872e79b2ac25eb3d60946a..6b38300825d14225b0170863170de2d08c7f4520 100644
Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ
diff --git a/sveltejs/src/Filter.svelte b/sveltejs/src/MultipleChoiceFilter.svelte
similarity index 100%
rename from sveltejs/src/Filter.svelte
rename to sveltejs/src/MultipleChoiceFilter.svelte
diff --git a/sveltejs/src/Search/BooleanFilter.svelte b/sveltejs/src/Search/BooleanFilter.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..82b7b83447959d2945c69fb2b3ba5d15691ed499
--- /dev/null
+++ b/sveltejs/src/Search/BooleanFilter.svelte
@@ -0,0 +1,23 @@
+<script>
+  import { filters } from '../stores';
+
+  export let name;
+  export let changeHandler;
+  export let type;
+  export let onLabel;
+  export let offLabel;
+  const { Drupal } = window;
+</script>
+
+<div class="filter-group__filter-options form-item">
+  <label for={type} class="form-item__label">{name}</label>
+  <select
+    name={type}
+    class="search__filter-select form-select form-element form-element--type-select"
+    bind:value={$filters[type]}
+    on:change={changeHandler}
+  >
+    <option value="1">{onLabel}</option>
+    <option value="0">{offLabel}</option>
+  </select>
+</div>
diff --git a/sveltejs/src/Search/FilterGroup.svelte b/sveltejs/src/Search/FilterGroup.svelte
deleted file mode 100644
index ea75622adc86915994f7a867aadd7ee0a5421363..0000000000000000000000000000000000000000
--- a/sveltejs/src/Search/FilterGroup.svelte
+++ /dev/null
@@ -1,22 +0,0 @@
-<script>
-  import { filters } from '../stores';
-
-  export let filterTitle;
-  export let filterData;
-  export let changeHandler;
-  export let filterType;
-</script>
-
-<div class="filter-group__filter-options form-item">
-  <label for={filterType} class="form-item__label">{filterTitle}</label>
-  <select
-    name={filterType}
-    class="search__filter-select form-select form-element form-element--type-select"
-    bind:value={$filters[filterType]}
-    on:change={changeHandler}
-  >
-    {#each Object.entries(filterData) as [id, label]}
-      <option value={id}>{label}</option>
-    {/each}
-  </select>
-</div>
diff --git a/sveltejs/src/Search/Search.svelte b/sveltejs/src/Search/Search.svelte
index 406daebfbbdc8baf68998cbae207909753ae1f96..447601cf6115e35c56c4fe83333debce02cc28f9 100644
--- a/sveltejs/src/Search/Search.svelte
+++ b/sveltejs/src/Search/Search.svelte
@@ -1,9 +1,9 @@
 <script>
   import { createEventDispatcher, getContext, onMount } from 'svelte';
   import FilterApplied from './FilterApplied.svelte';
-  import FilterGroup from './FilterGroup.svelte';
+  import BooleanFilter from './BooleanFilter.svelte';
   import { normalizeOptions, shallowCompare } from '../util';
-  import Filter from '../Filter.svelte';
+  import MultipleChoiceFilter from '../MultipleChoiceFilter.svelte';
   import SearchSort from './SearchSort.svelte';
   import {
     sourceFilters,
@@ -204,58 +204,22 @@
   {#if $sourceFilters.length !== 0}
     <div class="search__form-filters-container">
       <div class="search__form-filters">
-        {#if 'categories' in $sourceFilters}
-          <Filter
-            on:selectCategory={onSelectCategory}
-            bind:this={filterComponent}
-          />
-        {/if}
-        {#if 'security_advisories' in $sourceFilters}
-          <FilterGroup
-            filterTitle={Drupal.t('Security Advisory Coverage')}
-            filterData={SECURITY_OPTIONS}
-            filterType="securityCoverage"
-            changeHandler={onAdvancedFilter}
-            let:id
-            let:label
-          />
-        {/if}
-        {#if 'maintained' in $sourceFilters}
-          <FilterGroup
-            filterTitle={Drupal.t('Maintenance Status')}
-            filterData={MAINTENANCE_OPTIONS}
-            filterType="maintenanceStatus"
-            changeHandler={onAdvancedFilter}
-            let:id
-            let:label
-          >
-            <label
-              slot="label"
-              class="search__checkbox-label"
-              for={`maintenanceStatus${id}`}
-            >
-              {label}
-            </label>
-          </FilterGroup>
-        {/if}
-        {#if 'developed' in $sourceFilters}
-          <FilterGroup
-            filterTitle={Drupal.t('Development Status')}
-            filterData={DEVELOPMENT_OPTIONS}
-            filterType="developmentStatus"
-            changeHandler={onAdvancedFilter}
-            let:id
-            let:label
-          >
-            <label
-              slot="label"
-              class="search__checkbox-label"
-              for={`developmentStatus${id}`}
-            >
-              {label}
-            </label>
-          </FilterGroup>
-        {/if}
+        {#each Object.entries($sourceFilters) as [filterType, filter]}
+          {#if filter._type === 'boolean'}
+            <BooleanFilter
+              name={filter.name}
+              type={filterType}
+              onLabel={filter.on_label}
+              offLabel={filter.off_label}
+              changeHandler={onAdvancedFilter}
+            />
+          {:else if filter._type === 'multiple_choice'}
+            <MultipleChoiceFilter
+              on:selectCategory={onSelectCategory}
+              bind:this={filterComponent}
+            />
+          {/if}
+        {/each}
       </div>
       <div
         class="search__form-sort js-form-item js-form-type-select form-type--select js-form-item-type form-item--type"
diff --git a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php
index c048b4d1a8112eefc7adf6f79c7687d118c470f4..f3078c3b052d3f7f1659df1cba886264eef48345 100644
--- a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php
+++ b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php
@@ -349,19 +349,25 @@ class ProjectBrowserTestMock extends ProjectBrowserSourceBase {
     );
     $filters['categories'] = new MultipleChoiceFilter($choices, [], $this->t('Categories'), NULL);
 
-    $filters['maintained'] = new BooleanFilter(
+    $filters['securityCoverage'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show actively maintained projects'),
+      $this->t('Show projects covered by a security policy'),
+      $this->t('Show all'),
+      $this->t('Security Advisory Coverage'),
       NULL,
     );
-    $filters['security_advisories'] = new BooleanFilter(
+    $filters['maintenanceStatus'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show projects covered by a security policy'),
+      $this->t('Show actively maintained projects'),
+      $this->t('Show all'),
+      $this->t('Maintenance Status'),
       NULL,
     );
-    $filters['developed'] = new BooleanFilter(
+    $filters['developmentStatus'] = new BooleanFilter(
       TRUE,
-      $this->t('Only show projects under active development'),
+      $this->t('Show projects under active development'),
+      $this->t('Show all'),
+      $this->t('Development Status'),
       NULL,
     );
 
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
index 13166f70a850e7d23fac788ae63e76b1488fea98..43c6508da7f752ab2dba620308d7e8b9b7bbea68 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
@@ -105,8 +105,8 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
     $this->drupalGet('admin/modules/browse');
     $this->svelteInitHelper('text', 'Results');
 
-    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
-    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show projects covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show actively maintained projects', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
     $this->assertEquals('Show all', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     // Clear the security covered filter.
@@ -115,7 +115,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
 
     // Set the development status filter.
     $this->clickWithWait(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
-    $this->assertEquals('Active', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show projects under active development', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     // Clear all filters.
     $this->pressWithWait('Clear filters');
@@ -125,8 +125,8 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
 
     // Reset to recommended filters.
     $this->pressWithWait('Recommended filters');
-    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
-    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show projects covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show actively maintained projects', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
     $this->assertEquals('Show all', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
   }
 
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index bf6c9cc9dc53a442b4b53a0e012cdc8cfb829beb..098fb2c18ac233faae2b0542916161618c70f3c9 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -352,7 +352,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     ]);
 
     // Make sure the second filter applied is the security covered filter.
-    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show projects covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     // Clear the security covered filter.
     $this->clickWithWait(self::SECURITY_OPTION_SELECTOR . self::OPTION_LAST_CHILD);
@@ -380,7 +380,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->clickWithWait(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
 
     // Make sure the correct filter was applied.
-    $this->assertEquals('Active', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show projects under active development', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     $this->assertProjectsVisible([
       'Jazz',
@@ -419,7 +419,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
 
     // Click the Actively maintained filter.
     $this->clickWithWait(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
-    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show actively maintained projects', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     $this->assertProjectsVisible([
       'Jazz',
@@ -693,9 +693,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->pressWithWait('Recommended filters');
 
     // Check that the actively maintained tag is present.
-    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show actively maintained projects', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
     // Make sure the second filter applied is the security covered filter.
-    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show projects covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
     $this->assertTrue($assert_session->waitForText('9 Results'));
   }
 
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
index a3bb9edf25113176960f472245899c585fcddee4..cd3820a38b41813f18db6422859960d7eb5eb41b 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
@@ -236,7 +236,9 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     ]);
 
     // Make sure the second filter applied is the security covered filter.
-    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $option = $assert_session->optionExists('securityCoverage', '1');
+    $this->assertSame('Show projects covered by a security policy', $option->getText());
+    $this->assertTrue($option->isSelected());
 
     // Clear the security covered filter.
     $this->clickWithWait(self::SECURITY_OPTION_SELECTOR . self::OPTION_LAST_CHILD);
@@ -249,7 +251,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $this->clickWithWait(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
 
     // Make sure the correct filter was applied.
-    $this->assertEquals('Active', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show projects under active development', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
     $assert_session->waitForText('No records available');
 
     // Clear all filters.
@@ -257,7 +259,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     // Click the Actively maintained filter.
     $this->clickWithWait(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_FIRST_CHILD, '6,749 Results');
-    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show actively maintained projects', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     $this->assertProjectsVisible([
       'Chaos Tool Suite (ctools)', 'Token', 'Pathauto', 'Libraries API', 'Entity API', 'Webform', 'Metatag', 'Field Group', 'IMCE', 'CAPTCHA', 'Google Analytics', 'Redirect',
@@ -344,9 +346,11 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $this->pressWithWait('Recommended filters');
 
     // Check that the actively maintained tag is present.
-    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $maintenance_checked_option = $this->assertSession()->optionExists('maintenanceStatus', '1');
+    $this->assertTrue($maintenance_checked_option->isSelected());
+    $this->assertEquals('Show actively maintained projects', $maintenance_checked_option->getText());
     // Make sure the second filter applied is the security covered filter.
-    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $assert_session->fieldValueEquals('securityCoverage', '1');
     $this->assertTrue($assert_session->waitForText('4,523 Results'));
   }
 
@@ -467,7 +471,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     // Set the names of filters which will be defined by the test mock.
     // @see \Drupal\project_browser_test\Plugin\ProjectBrowserSource\ProjectBrowserTestMock::getFilterDefinitions()
-    $filters_to_define = ['maintained', 'security_advisories'];
+    $filters_to_define = ['maintenanceStatus', 'securityCoverage'];
     \Drupal::state()->set('filters_to_define', $filters_to_define);
 
     $this->drupalGet('admin/modules/browse');
diff --git a/tests/src/Nightwatch/Tests/keyboardTest.js b/tests/src/Nightwatch/Tests/keyboardTest.js
index 39cdedd2cc63386b60247f94f425360160103bb0..2cb89e3a99e7e2a04971379cd31a2dc9880dadd3 100644
--- a/tests/src/Nightwatch/Tests/keyboardTest.js
+++ b/tests/src/Nightwatch/Tests/keyboardTest.js
@@ -176,7 +176,7 @@ module.exports = {
           'Assert second category is selected.',
         );
 
-      // Press space to expand categories drop-down again.
+      // Press tab to navigate to next drop-down.
       browser
         .keys(browser.Keys.TAB)
         .pause(delayInMilliseconds)
diff --git a/tests/src/Unit/FiltersTest.php b/tests/src/Unit/FiltersTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..76d854a7d3b6f099ed9df77709fb951ba66b46fd
--- /dev/null
+++ b/tests/src/Unit/FiltersTest.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\project_browser\Unit;
+
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Tests\UnitTestCase;
+use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter;
+use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter;
+
+/**
+ * Tests filters that can be defined by source plugins.
+ *
+ * @group project_browser
+ * @covers \Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter
+ * @covers \Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter
+ */
+class FiltersTest extends UnitTestCase {
+
+  /**
+   * Tests filters are serialized as expected.
+   */
+  public function testFiltersSerializeAsExpected(): void {
+    $filter = new BooleanFilter(
+      TRUE,
+      new TranslatableMarkup('ON', string_translation: $this->getStringTranslationStub()),
+      new TranslatableMarkup('OFF', string_translation: $this->getStringTranslationStub()),
+      'I name the filter',
+      'tribe',
+    );
+    $serialized = $filter->jsonSerialize();
+    $this->assertSame('boolean', $serialized['_type']);
+    $this->assertSame('ON', $serialized['on_label']);
+    $this->assertSame('OFF', $serialized['off_label']);
+    $this->assertSame('I name the filter', $serialized['name']);
+    $this->assertSame('tribe', $serialized['group']);
+    $this->assertTrue($serialized['value']);
+
+    $filter = new MultipleChoiceFilter(
+      ['a' => 'Choice A', 'b' => 'Choice B'],
+      ['a'],
+      'I name thee multiple-choice',
+      'cool_stuff',
+    );
+    $serialized = $filter->jsonSerialize();
+    $this->assertSame('multiple_choice', $serialized['_type']);
+    $this->assertSame(['a'], $serialized['value']);
+    $this->assertSame(['a' => 'Choice A', 'b' => 'Choice B'], $serialized['choices']);
+    $this->assertSame('I name thee multiple-choice', $serialized['name']);
+    $this->assertSame('cool_stuff', $serialized['group']);
+  }
+
+}