diff --git a/frontend/src/metabase-lib/parameters/constants.ts b/frontend/src/metabase-lib/parameters/constants.ts
index 015625060d909f36e643fb2a2d5ba7cf677a806e..5c05d0270daa68ff815f6977f7fa225c3aeb70e5 100644
--- a/frontend/src/metabase-lib/parameters/constants.ts
+++ b/frontend/src/metabase-lib/parameters/constants.ts
@@ -148,11 +148,6 @@ export const LOCATION_OPTIONS = [
   },
 ];
 
-export const CUSTOM_SOURCE_PARAMETER_TYPES: Record<string, string[]> = {
-  string: ["="],
-  location: ["="],
-};
-
 export const TYPE_SUPPORTS_LINKED_FILTERS = [
   "string",
   "category",
diff --git a/frontend/src/metabase-lib/parameters/utils/parameter-source.ts b/frontend/src/metabase-lib/parameters/utils/parameter-source.ts
index 3017e4871472d8e3b71b7dc108ee283f1203d20f..b4d621c866182cb297eefec9aed07b65af7960d2 100644
--- a/frontend/src/metabase-lib/parameters/utils/parameter-source.ts
+++ b/frontend/src/metabase-lib/parameters/utils/parameter-source.ts
@@ -6,8 +6,9 @@ import {
   ValuesSourceConfig,
   ValuesSourceType,
 } from "metabase-types/api";
-import { getFields } from "metabase-lib/parameters/utils/parameter-fields";
 import Field from "metabase-lib/metadata/Field";
+import { getFields } from "./parameter-fields";
+import { getParameterSubType, getParameterType } from "./parameter-type";
 
 export const getQueryType = (parameter: Parameter): ValuesQueryType => {
   return parameter.values_query_type ?? "list";
@@ -21,6 +22,21 @@ export const getSourceConfig = (parameter: Parameter): ValuesSourceConfig => {
   return parameter.values_source_config ?? {};
 };
 
+export const canUseCustomSource = (parameter: Parameter) => {
+  const type = getParameterType(parameter);
+  const subType = getParameterSubType(parameter);
+
+  switch (type) {
+    case "string":
+    case "location":
+      return subType === "=";
+    case "category":
+      return true;
+    default:
+      return false;
+  }
+};
+
 export const isValidSourceConfig = (
   sourceType: ValuesSourceType,
   { card_id, value_field, values }: ValuesSourceConfig,
diff --git a/frontend/src/metabase-lib/parameters/utils/template-tags.ts b/frontend/src/metabase-lib/parameters/utils/template-tags.ts
index 60a687e3dd8d7c243b2d7cc182161e298264c18c..fba053ad9307aaacf4b340b3a47de45c1dd66b70 100644
--- a/frontend/src/metabase-lib/parameters/utils/template-tags.ts
+++ b/frontend/src/metabase-lib/parameters/utils/template-tags.ts
@@ -38,6 +38,9 @@ export function getTemplateTagParameter(tag: TemplateTag): ParameterWithTarget {
     name: tag["display-name"],
     slug: tag.name,
     default: tag.default,
+    values_query_type: tag.values_query_type,
+    values_source_type: tag.values_source_type,
+    values_source_config: tag.values_source_config,
   };
 }
 
diff --git a/frontend/src/metabase-types/types/Query.ts b/frontend/src/metabase-types/types/Query.ts
index 6938fdb58e94200cad4afc4046a161d639f4bd7e..ee5ab520049848be77c1e3d7c46f4714b5417259 100644
--- a/frontend/src/metabase-types/types/Query.ts
+++ b/frontend/src/metabase-types/types/Query.ts
@@ -3,6 +3,11 @@
  * @deprecated use existing types from, or add to metabase-types/api/*
  */
 
+import {
+  ValuesQueryType,
+  ValuesSourceConfig,
+  ValuesSourceType,
+} from "metabase-types/api";
 import { DatetimeUnit } from "metabase-types/api/query";
 import { TableId } from "./Table";
 import { FieldId, BaseType } from "./Field";
@@ -60,6 +65,11 @@ export type TemplateTag = {
   // Snippet specific
   "snippet-id"?: number;
   "snippet-name"?: string;
+
+  // Values source
+  values_query_type?: ValuesQueryType;
+  values_source_type?: ValuesSourceType;
+  values_source_config?: ValuesSourceConfig;
 };
 
 export type TemplateTags = { [key: TemplateTagName]: TemplateTag };
diff --git a/frontend/src/metabase/parameters/components/ParameterLinkedFilters/ParameterLinkedFilters.tsx b/frontend/src/metabase/parameters/components/ParameterLinkedFilters/ParameterLinkedFilters.tsx
index 11e4991f971483e799b885bd14fce2fed6a75f81..1af186f8be14f1c2a81b14423ea32300f5e8b281 100644
--- a/frontend/src/metabase/parameters/components/ParameterLinkedFilters/ParameterLinkedFilters.tsx
+++ b/frontend/src/metabase/parameters/components/ParameterLinkedFilters/ParameterLinkedFilters.tsx
@@ -4,8 +4,13 @@ import Toggle from "metabase/core/components/Toggle";
 import Fields from "metabase/entities/fields";
 import Tables from "metabase/entities/tables";
 import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
-import { Field, FieldId, ParameterId, Table } from "metabase-types/api";
-import { UiParameter } from "metabase-lib/parameters/types";
+import {
+  Field,
+  FieldId,
+  Parameter,
+  ParameterId,
+  Table,
+} from "metabase-types/api";
 import { usableAsLinkedFilter } from "../../utils/linked-filters";
 import useFilterFields from "./use-filter-fields";
 import {
@@ -25,8 +30,8 @@ import {
 } from "./ParameterLinkedFilters.styled";
 
 export interface ParameterLinkedFiltersProps {
-  parameter: UiParameter;
-  otherParameters: UiParameter[];
+  parameter: Parameter;
+  otherParameters: Parameter[];
   onChangeFilteringParameters: (filteringParameters: ParameterId[]) => void;
   onShowAddParameterPopover: () => void;
 }
@@ -50,7 +55,7 @@ const ParameterLinkedFilters = ({
   );
 
   const handleFilterChange = useCallback(
-    (otherParameter: UiParameter, isFiltered: boolean) => {
+    (otherParameter: Parameter, isFiltered: boolean) => {
       const newParameters = isFiltered
         ? filteringParameters.concat(otherParameter.id)
         : filteringParameters.filter(id => id !== otherParameter.id);
@@ -61,7 +66,7 @@ const ParameterLinkedFilters = ({
   );
 
   const handleExpandedChange = useCallback(
-    (otherParameter: UiParameter, isExpanded: boolean) => {
+    (otherParameter: Parameter, isExpanded: boolean) => {
       setExpandedParameterId(isExpanded ? otherParameter.id : undefined);
     },
     [],
@@ -111,12 +116,12 @@ const ParameterLinkedFilters = ({
 };
 
 interface LinkedParameterProps {
-  parameter: UiParameter;
-  otherParameter: UiParameter;
+  parameter: Parameter;
+  otherParameter: Parameter;
   isFiltered: boolean;
   isExpanded: boolean;
-  onFilterChange: (otherParameter: UiParameter, isFiltered: boolean) => void;
-  onExpandedChange: (otherParameter: UiParameter, isExpanded: boolean) => void;
+  onFilterChange: (otherParameter: Parameter, isFiltered: boolean) => void;
+  onExpandedChange: (otherParameter: Parameter, isExpanded: boolean) => void;
 }
 
 const LinkedParameter = ({
@@ -157,8 +162,8 @@ const LinkedParameter = ({
 };
 
 interface LinkedFieldListProps {
-  parameter: UiParameter;
-  otherParameter: UiParameter;
+  parameter: Parameter;
+  otherParameter: Parameter;
 }
 
 const LinkedFieldList = ({
diff --git a/frontend/src/metabase/parameters/components/ParameterLinkedFilters/use-filter-fields.ts b/frontend/src/metabase/parameters/components/ParameterLinkedFilters/use-filter-fields.ts
index 12d4161d58afa3d2759e59e29a4096107d34ae8c..031d7110f788eb9673d4aaa95f48d2150148751a 100644
--- a/frontend/src/metabase/parameters/components/ParameterLinkedFilters/use-filter-fields.ts
+++ b/frontend/src/metabase/parameters/components/ParameterLinkedFilters/use-filter-fields.ts
@@ -2,8 +2,8 @@ import { useCallback, useState } from "react";
 import { t } from "ttag";
 import { DashboardApi } from "metabase/services";
 import { useOnMount } from "metabase/hooks/use-on-mount";
-import { FieldId } from "metabase-types/api";
-import { UiParameter } from "metabase-lib/parameters/types";
+import { FieldId, Parameter } from "metabase-types/api";
+import { getFields } from "metabase-lib/parameters/utils/parameter-fields";
 
 export interface UseFilterFieldsState {
   data?: FieldId[][];
@@ -12,14 +12,14 @@ export interface UseFilterFieldsState {
 }
 
 const useFilterFields = (
-  parameter: UiParameter,
-  otherParameter: UiParameter,
+  parameter: Parameter,
+  otherParameter: Parameter,
 ): UseFilterFieldsState => {
   const [state, setState] = useState<UseFilterFieldsState>({ loading: false });
 
   const handleLoad = useCallback(async () => {
-    const filtered = getParameterFieldIds(parameter);
-    const filtering = getParameterFieldIds(otherParameter);
+    const filtered = getFields(parameter).map(field => field.id);
+    const filtering = getFields(otherParameter).map(field => field.id);
 
     if (!filtered.length || !filtered.length) {
       const errorParameter = !filtered.length ? parameter : otherParameter;
@@ -40,18 +40,10 @@ const useFilterFields = (
   return state;
 };
 
-const getParameterError = ({ name }: UiParameter) => {
+const getParameterError = ({ name }: Parameter) => {
   return t`To view this, ${name} must be connected to at least one field.`;
 };
 
-const getParameterFieldIds = (parameter: UiParameter) => {
-  if ("fields" in parameter) {
-    return parameter.fields.map(field => field.id);
-  } else {
-    return [];
-  }
-};
-
 const getParameterMapping = (data: Record<FieldId, FieldId[]>) => {
   return Object.entries(data).flatMap(([filteredId, filteringIds]) =>
     filteringIds.map(filteringId => [filteringId, parseInt(filteredId, 10)]),
diff --git a/frontend/src/metabase/parameters/components/ParameterSettings/ParameterSettings.tsx b/frontend/src/metabase/parameters/components/ParameterSettings/ParameterSettings.tsx
index e89a395635e1d404fffae9628569a6d38449494a..52e5d64a1c303d1adab9380eadd57afecbf493d5 100644
--- a/frontend/src/metabase/parameters/components/ParameterSettings/ParameterSettings.tsx
+++ b/frontend/src/metabase/parameters/components/ParameterSettings/ParameterSettings.tsx
@@ -3,16 +3,14 @@ import { t } from "ttag";
 import InputBlurChange from "metabase/components/InputBlurChange";
 import Radio from "metabase/core/components/Radio";
 import {
+  Parameter,
   ValuesQueryType,
   ValuesSourceConfig,
   ValuesSourceType,
 } from "metabase-types/api";
-import { UiParameter } from "metabase-lib/parameters/types";
+import { canUseCustomSource } from "metabase-lib/parameters/utils/parameter-source";
 import { getIsMultiSelect } from "../../utils/dashboards";
-import {
-  canUseCustomSource,
-  isSingleOrMultiSelectable,
-} from "../../utils/parameter-type";
+import { isSingleOrMultiSelectable } from "../../utils/parameter-type";
 import ValuesSourceSettings from "../ValuesSourceSettings";
 import {
   SettingLabel,
@@ -28,7 +26,7 @@ const MULTI_SELECT_OPTIONS = [
 ];
 
 export interface ParameterSettingsProps {
-  parameter: UiParameter;
+  parameter: Parameter;
   onChangeName: (name: string) => void;
   onChangeDefaultValue: (value: unknown) => void;
   onChangeIsMultiSelect: (isMultiSelect: boolean) => void;
diff --git a/frontend/src/metabase/parameters/components/ParameterSidebar/ParameterSidebar.tsx b/frontend/src/metabase/parameters/components/ParameterSidebar/ParameterSidebar.tsx
index fee77458a80ed200a53aeb3f19474be1af9f40fa..0a331660a240e5406b288560df11b02df7bf99f7 100644
--- a/frontend/src/metabase/parameters/components/ParameterSidebar/ParameterSidebar.tsx
+++ b/frontend/src/metabase/parameters/components/ParameterSidebar/ParameterSidebar.tsx
@@ -3,20 +3,20 @@ import { t } from "ttag";
 import Radio from "metabase/core/components/Radio";
 import Sidebar from "metabase/dashboard/components/Sidebar";
 import {
+  Parameter,
   ParameterId,
   ValuesQueryType,
   ValuesSourceConfig,
   ValuesSourceType,
 } from "metabase-types/api";
-import { UiParameter } from "metabase-lib/parameters/types";
 import { canUseLinkedFilters } from "../../utils/linked-filters";
 import ParameterSettings from "../ParameterSettings";
 import ParameterLinkedFilters from "../ParameterLinkedFilters";
 import { SidebarBody, SidebarHeader } from "./ParameterSidebar.styled";
 
 export interface ParameterSidebarProps {
-  parameter: UiParameter;
-  otherParameters: UiParameter[];
+  parameter: Parameter;
+  otherParameters: Parameter[];
   onChangeName: (parameterId: ParameterId, name: string) => void;
   onChangeDefaultValue: (parameterId: ParameterId, value: unknown) => void;
   onChangeIsMultiSelect: (
@@ -151,7 +151,7 @@ const ParameterSidebar = ({
   );
 };
 
-const getTabs = (parameter: UiParameter) => {
+const getTabs = (parameter: Parameter) => {
   const tabs = [{ value: "settings", name: t`Settings`, icon: "gear" }];
 
   if (canUseLinkedFilters(parameter)) {
diff --git a/frontend/src/metabase/parameters/components/ValuesSourceModal/ValuesSourceModal.tsx b/frontend/src/metabase/parameters/components/ValuesSourceModal/ValuesSourceModal.tsx
index 789201aac25444395ebe6651ccda25eecc8fd7e3..88f221f2e9d79b2d9c29a8ff0d97b2ae77766b82 100644
--- a/frontend/src/metabase/parameters/components/ValuesSourceModal/ValuesSourceModal.tsx
+++ b/frontend/src/metabase/parameters/components/ValuesSourceModal/ValuesSourceModal.tsx
@@ -1,19 +1,22 @@
 import React, { useCallback, useMemo, useState } from "react";
-import { ValuesSourceConfig, ValuesSourceType } from "metabase-types/api";
+import {
+  Parameter,
+  ValuesSourceConfig,
+  ValuesSourceType,
+} from "metabase-types/api";
 import { getNonVirtualFields } from "metabase-lib/parameters/utils/parameter-fields";
 import {
   getSourceConfig,
   getSourceConfigForType,
   getSourceType,
 } from "metabase-lib/parameters/utils/parameter-source";
-import { UiParameter } from "metabase-lib/parameters/types";
 import ValuesSourceTypeModal from "./ValuesSourceTypeModal";
 import ValuesSourceCardModal from "./ValuesSourceCardModal";
 
 type ModalStep = "main" | "card";
 
 interface ModalProps {
-  parameter: UiParameter;
+  parameter: Parameter;
   onSubmit: (
     sourceType: ValuesSourceType,
     sourceConfig: ValuesSourceConfig,
diff --git a/frontend/src/metabase/parameters/components/ValuesSourceSettings/ValuesSourceSettings.tsx b/frontend/src/metabase/parameters/components/ValuesSourceSettings/ValuesSourceSettings.tsx
index 9d9a1cbcd2385cdcc8ad6b579cbf04bf88f3e954..800ca78cd9cfcefbc51fd1a9ada6ed7ffe2cf1f7 100644
--- a/frontend/src/metabase/parameters/components/ValuesSourceSettings/ValuesSourceSettings.tsx
+++ b/frontend/src/metabase/parameters/components/ValuesSourceSettings/ValuesSourceSettings.tsx
@@ -3,12 +3,12 @@ import { t } from "ttag";
 import Radio from "metabase/core/components/Radio/Radio";
 import Modal from "metabase/components/Modal";
 import {
+  Parameter,
   ValuesQueryType,
   ValuesSourceConfig,
   ValuesSourceType,
 } from "metabase-types/api";
 import { getQueryType } from "metabase-lib/parameters/utils/parameter-source";
-import { UiParameter } from "metabase-lib/parameters/types";
 import ValuesSourceModal from "../ValuesSourceModal";
 import {
   RadioLabelButton,
@@ -17,7 +17,7 @@ import {
 } from "./ValuesSourceSettings.styled";
 
 export interface ValuesSourceSettingsProps {
-  parameter: UiParameter;
+  parameter: Parameter;
   onChangeQueryType: (queryType: ValuesQueryType) => void;
   onChangeSourceType: (sourceType: ValuesSourceType) => void;
   onChangeSourceConfig: (sourceConfig: ValuesSourceConfig) => void;
diff --git a/frontend/src/metabase/parameters/utils/parameter-type.ts b/frontend/src/metabase/parameters/utils/parameter-type.ts
index 715a8e58094c07a01c60e7323c0dac272a789ad1..9d1642edf21bae3d104e2a1044b6563675910540 100644
--- a/frontend/src/metabase/parameters/utils/parameter-type.ts
+++ b/frontend/src/metabase/parameters/utils/parameter-type.ts
@@ -1,11 +1,8 @@
 import { Parameter } from "metabase-types/api";
+import { SINGLE_OR_MULTI_SELECTABLE_TYPES } from "metabase-lib/parameters/constants";
 import {
-  CUSTOM_SOURCE_PARAMETER_TYPES,
-  SINGLE_OR_MULTI_SELECTABLE_TYPES,
-} from "metabase-lib/parameters/constants";
-import {
-  getParameterType,
   getParameterSubType,
+  getParameterType,
 } from "metabase-lib/parameters/utils/parameter-type";
 
 export function isSingleOrMultiSelectable(parameter: Parameter): boolean {
@@ -20,13 +17,3 @@ export function isSingleOrMultiSelectable(parameter: Parameter): boolean {
   }
   return SINGLE_OR_MULTI_SELECTABLE_TYPES[type].includes(subType);
 }
-
-export const canUseCustomSource = (parameter: Parameter) => {
-  const type = getParameterType(parameter);
-  const subType = getParameterSubType(parameter);
-
-  return (
-    CUSTOM_SOURCE_PARAMETER_TYPES[type] != null &&
-    CUSTOM_SOURCE_PARAMETER_TYPES[type].includes(subType)
-  );
-};
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx b/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx
index af657d894a3baab1c86b548efd342c69b81f8203..1bc62abffc2d6e940dfeba1f1c201626acbe0d7a 100644
--- a/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx
@@ -11,6 +11,7 @@ import Toggle from "metabase/core/components/Toggle";
 import InputBlurChange from "metabase/components/InputBlurChange";
 import Select, { Option } from "metabase/core/components/Select";
 
+import ValuesSourceSettings from "metabase/parameters/components/ValuesSourceSettings";
 import { getParameterOptionsForField } from "metabase/parameters/utils/template-tag-options";
 
 import { fetchField } from "metabase/redux/metadata";
@@ -18,6 +19,8 @@ import { getMetadata } from "metabase/selectors/metadata";
 import { SchemaTableAndFieldDataSelector } from "metabase/query_builder/components/DataSelector";
 import MetabaseSettings from "metabase/lib/settings";
 
+import { canUseCustomSource } from "metabase-lib/parameters/utils/parameter-source";
+
 import {
   ErrorSpan,
   TagName,
@@ -274,6 +277,24 @@ export class TagEditorParam extends Component {
           />
         </InputContainer>
 
+        {parameter && canUseCustomSource(parameter) && (
+          <InputContainer>
+            <ContainerLabel>{t`How should users filter on this variable?`}</ContainerLabel>
+            <ValuesSourceSettings
+              parameter={parameter}
+              onChangeQueryType={value =>
+                this.setParameterAttribute("values_query_type", value)
+              }
+              onChangeSourceType={value =>
+                this.setParameterAttribute("values_source_type", value)
+              }
+              onChangeSourceConfig={value =>
+                this.setParameterAttribute("values_source_config", value)
+              }
+            />
+          </InputContainer>
+        )}
+
         {((tag.type !== "dimension" && tag.required) ||
           tag.type === "dimension" ||
           tag["widget-type"]) && (
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.unit.spec.jsx b/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.unit.spec.jsx
index 4312dc04fc29fe764d3399e41e6e6180d8551970..6589aa6665f21de6ff02548e2e7d3aaa1ab2b8b2 100644
--- a/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.unit.spec.jsx
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.unit.spec.jsx
@@ -23,6 +23,7 @@ jest.mock("metabase/query_builder/components/DataSelector", () => ({
 }));
 
 jest.mock("metabase/entities/schemas", () => ({
+  load: () => children => children,
   Loader: ({ children }) => children(),
 }));
 
diff --git a/frontend/test/__support__/e2e/helpers/e2e-filter-helpers.js b/frontend/test/__support__/e2e/helpers/e2e-filter-helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..b26faa9c381561228144ab3cfcf273efb0121a79
--- /dev/null
+++ b/frontend/test/__support__/e2e/helpers/e2e-filter-helpers.js
@@ -0,0 +1,43 @@
+import {
+  modal,
+  popover,
+} from "__support__/e2e/helpers/e2e-ui-elements-helpers";
+
+export function setFilterQuestionSource({ question, field }) {
+  cy.findByText("Dropdown list").click();
+  cy.findByText("Edit").click();
+
+  modal().within(() => {
+    cy.findByText("From another model or question").click();
+    cy.findByText("Pick a model or question…").click();
+  });
+
+  modal().within(() => {
+    cy.findByPlaceholderText(/Search for a question/).type(question);
+    cy.findByText(question).click();
+    cy.button("Done").click();
+  });
+
+  modal().within(() => {
+    cy.findByText("Pick a column…").click();
+  });
+
+  popover().within(() => {
+    cy.findByText(field).click();
+  });
+
+  modal().within(() => {
+    cy.button("Done").click();
+  });
+}
+
+export function setFilterListSource({ values }) {
+  cy.findByText("Dropdown list").click();
+  cy.findByText("Edit").click();
+
+  modal().within(() => {
+    cy.findByText("Custom list").click();
+    cy.findByRole("textbox").clear().type(values.join("\n"));
+    cy.button("Done").click();
+  });
+}
diff --git a/frontend/test/__support__/e2e/helpers/e2e-misc-helpers.js b/frontend/test/__support__/e2e/helpers/e2e-misc-helpers.js
index c5cfc6d8fa1d7066da6488b332a7130ab62a2e7f..f7101dc03f7c47a2dcbb04aea182ff63efd235f3 100644
--- a/frontend/test/__support__/e2e/helpers/e2e-misc-helpers.js
+++ b/frontend/test/__support__/e2e/helpers/e2e-misc-helpers.js
@@ -1,3 +1,5 @@
+import { modal } from "__support__/e2e/helpers/e2e-ui-elements-helpers";
+
 // Find a text field by label text, type it in, then blur the field.
 // Commonly used in our Admin section as we auto-save settings.
 export function typeAndBlurUsingLabel(label, value) {
@@ -218,3 +220,26 @@ export function interceptIfNotPreviouslyDefined({ method, url, alias } = {}) {
     cy.intercept(method, url).as(alias);
   }
 }
+
+export function saveQuestion(
+  name,
+  { wrapId = false, idAlias = "questionId" } = {},
+) {
+  cy.intercept("POST", "/api/card").as("saveQuestion");
+  cy.findByText("Save").click();
+
+  modal().within(() => {
+    cy.findByLabelText("Name").type(name);
+    cy.button("Save").click();
+  });
+
+  cy.wait("@saveQuestion").then(({ response: { body } }) => {
+    if (wrapId) {
+      cy.wrap(body.id).as(idAlias);
+    }
+  });
+
+  modal().within(() => {
+    cy.button("Not now").click();
+  });
+}
diff --git a/frontend/test/__support__/e2e/helpers/index.js b/frontend/test/__support__/e2e/helpers/index.js
index 8ae57a12d63fcba5cb2bc97de4d20ac0e5697236..ebd82cb8f4f419bdadcb3381cdb80444f1fb3755 100644
--- a/frontend/test/__support__/e2e/helpers/index.js
+++ b/frontend/test/__support__/e2e/helpers/index.js
@@ -6,6 +6,7 @@ export * from "./e2e-database-metadata-helpers";
 export * from "./e2e-qa-databases-helpers";
 export * from "./e2e-ad-hoc-question-helpers";
 export * from "./e2e-enterprise-helpers";
+export * from "./e2e-filter-helpers";
 export * from "./e2e-mock-app-settings-helpers";
 export * from "./e2e-notebook-helpers";
 export * from "./e2e-cloud-helpers";
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-source.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-source.cy.spec.js
index 111eb4cca9065b97e4fa9314269d2ac7c43af17e..fee8d801e44da9b6b34086a7099557d07f58da7a 100644
--- a/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-source.cy.spec.js
+++ b/frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-source.cy.spec.js
@@ -1,6 +1,5 @@
 import {
   editDashboard,
-  modal,
   popover,
   restore,
   saveDashboard,
@@ -8,6 +7,8 @@ import {
   visitDashboard,
   openQuestionActions,
   visitQuestion,
+  setFilterQuestionSource,
+  setFilterListSource,
 } from "__support__/e2e/helpers";
 import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database";
 
@@ -22,7 +23,7 @@ const dashboardQuestionDetails = {
 };
 
 const structuredQuestionDetails = {
-  name: "Categories",
+  name: "GUI source",
   query: {
     "source-table": PRODUCTS_ID,
     aggregation: [["count"]],
@@ -32,7 +33,7 @@ const structuredQuestionDetails = {
 };
 
 const nativeQuestionDetails = {
-  name: "Categories",
+  name: "SQL source",
   native: {
     query: "select distinct CATEGORY from PRODUCTS order by CATEGORY limit 2",
   },
@@ -47,7 +48,7 @@ describe("scenarios > dashboard > filters", () => {
   });
 
   it("should be able to use a structured question source", () => {
-    cy.createQuestion(structuredQuestionDetails);
+    cy.createQuestion(structuredQuestionDetails, { wrapId: true });
     cy.createQuestionAndDashboard({
       questionDetails: dashboardQuestionDetails,
     }).then(({ body: { dashboard_id } }) => {
@@ -57,14 +58,16 @@ describe("scenarios > dashboard > filters", () => {
     editDashboard();
     setFilter("Text or Category", "Is");
     mapFilterToQuestion();
-    editDropdown();
-    setupStructuredQuestionSource();
+    setFilterQuestionSource({ question: "GUI source", field: "Category" });
     saveDashboard();
     filterDashboard();
+
+    cy.get("@questionId").then(visitQuestion);
+    archiveQuestion();
   });
 
   it("should be able to use a native question source", () => {
-    cy.createNativeQuestion(nativeQuestionDetails);
+    cy.createNativeQuestion(nativeQuestionDetails, { wrapId: true });
     cy.createQuestionAndDashboard({
       questionDetails: dashboardQuestionDetails,
     }).then(({ body: { dashboard_id } }) => {
@@ -74,10 +77,12 @@ describe("scenarios > dashboard > filters", () => {
     editDashboard();
     setFilter("Text or Category", "Is");
     mapFilterToQuestion();
-    editDropdown();
-    setupNativeQuestionSource();
+    setFilterQuestionSource({ question: "SQL source", field: "CATEGORY" });
     saveDashboard();
     filterDashboard();
+
+    cy.get("@questionId").then(visitQuestion);
+    archiveQuestion();
   });
 
   it("should be able to use a static list source", () => {
@@ -90,113 +95,12 @@ describe("scenarios > dashboard > filters", () => {
     editDashboard();
     setFilter("Text or Category", "Is");
     mapFilterToQuestion();
-    editDropdown();
-    setupCustomList();
+    setFilterListSource({ values: ["Doohickey", "Gadget"] });
     saveDashboard();
     filterDashboard();
   });
-
-  it("should result in a warning being shown when archiving a question it uses", () => {
-    cy.intercept("POST", "/api/dashboard/**/query").as("getCardQuery");
-
-    cy.createQuestion(structuredQuestionDetails, {
-      wrapId: true,
-      idAlias: "structuredQuestionId",
-    });
-    cy.createQuestionAndDashboard({
-      questionDetails: dashboardQuestionDetails,
-    }).then(({ body: { dashboard_id } }) => {
-      visitDashboard(dashboard_id);
-    });
-
-    editDashboard();
-    setFilter("Text or Category", "Is");
-    mapFilterToQuestion();
-    editDropdown();
-    setupStructuredQuestionSource();
-    saveDashboard();
-
-    cy.intercept("GET", "/api/collection/root/items**").as("getItems");
-
-    cy.get("@structuredQuestionId").then(question_id => {
-      visitQuestion(question_id);
-      openQuestionActions();
-      cy.findByTestId("archive-button").click();
-      modal().within(() => {
-        cy.findByText(
-          "This question will be removed from any dashboards or pulses using it. It will also be removed from the filter that uses it to populate values.",
-        );
-      });
-    });
-  });
 });
 
-const editDropdown = () => {
-  cy.findByText("Dropdown list").click();
-  cy.findByText("Edit").click();
-};
-
-const setupStructuredQuestionSource = () => {
-  modal().within(() => {
-    cy.findByText("From another model or question").click();
-    cy.findByText("Pick a model or question…").click();
-  });
-
-  modal().within(() => {
-    cy.findByPlaceholderText(/Search for a question/).type("Categories");
-    cy.findByText("Categories").click();
-    cy.button("Done").click();
-  });
-
-  modal().within(() => {
-    cy.findByText("Pick a column…").click();
-  });
-
-  popover().within(() => {
-    cy.findByText("Category").click();
-  });
-
-  modal().within(() => {
-    cy.wait("@dataset");
-    cy.findByDisplayValue(/Gadget/).should("be.visible");
-    cy.button("Done").click();
-  });
-};
-
-const setupNativeQuestionSource = () => {
-  modal().within(() => {
-    cy.findByText("From another model or question").click();
-    cy.findByText("Pick a model or question…").click();
-  });
-
-  modal().within(() => {
-    cy.findByText("Categories").click();
-    cy.button("Done").click();
-  });
-
-  modal().within(() => {
-    cy.findByText("Pick a column…").click();
-  });
-
-  popover().within(() => {
-    cy.findByText("CATEGORY").click();
-  });
-
-  modal().within(() => {
-    cy.wait("@dataset");
-    cy.findByDisplayValue(/Gadget/).should("be.visible");
-    cy.button("Done").click();
-  });
-};
-
-const setupCustomList = () => {
-  modal().within(() => {
-    cy.findByText("Custom list").click();
-    cy.findByRole("textbox").clear().type("Doohickey\nGadget");
-    cy.button("Done").click();
-  });
-};
-
 const mapFilterToQuestion = () => {
   cy.findByText("Select…").click();
   popover().within(() => cy.findByText("Category").click());
@@ -217,3 +121,11 @@ const filterDashboard = () => {
     cy.wait("@getCardQuery");
   });
 };
+
+const archiveQuestion = () => {
+  openQuestionActions();
+  cy.findByTestId("archive-button").click();
+  cy.findByText(
+    "This question will be removed from any dashboards or pulses using it. It will also be removed from the filter that uses it to populate values.",
+  );
+};
diff --git a/frontend/test/metabase/scenarios/native-filters/sql-filters-source.cy.spec.js b/frontend/test/metabase/scenarios/native-filters/sql-filters-source.cy.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..f8dac2d660820843e6b24b7d3c1809a5ef7ff4a0
--- /dev/null
+++ b/frontend/test/metabase/scenarios/native-filters/sql-filters-source.cy.spec.js
@@ -0,0 +1,57 @@
+import {
+  openNativeEditor,
+  restore,
+  setFilterQuestionSource,
+  saveQuestion,
+} from "__support__/e2e/helpers";
+import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database";
+import * as SQLFilter from "./helpers/e2e-sql-filter-helpers";
+import * as FieldFilter from "./helpers/e2e-field-filter-helpers";
+
+const { PRODUCTS_ID, PRODUCTS } = SAMPLE_DATABASE;
+
+const structuredQuestionDetails = {
+  name: "GUI source",
+  query: {
+    "source-table": PRODUCTS_ID,
+    aggregation: [["count"]],
+    breakout: [["field", PRODUCTS.CATEGORY, null]],
+    filter: ["!=", ["field", PRODUCTS.CATEGORY, null], "Gizmo"],
+  },
+};
+
+const nativeQuestionDetails = {
+  name: "SQL source",
+  native: {
+    query: "select distinct CATEGORY from PRODUCTS order by CATEGORY limit 2",
+  },
+};
+
+describe("scenarios > filters > sql filters > values source", () => {
+  beforeEach(() => {
+    restore();
+    cy.signInAsAdmin();
+  });
+
+  it("should be able to use a custom source for a text filter", () => {
+    cy.createQuestion(structuredQuestionDetails);
+    openNativeEditor();
+
+    SQLFilter.enterParameterizedQuery("SELECT * FROM products WHERE {{f}}");
+    SQLFilter.openTypePickerFromDefaultFilterType();
+    setFilterQuestionSource({ question: "GUI source", field: "Category" });
+    saveQuestion("SQL filter");
+  });
+
+  it("should be able to use a custom source for a field filter", () => {
+    cy.createNativeQuestion(nativeQuestionDetails);
+    openNativeEditor();
+
+    SQLFilter.enterParameterizedQuery("SELECT * FROM products WHERE {{f}}");
+    SQLFilter.openTypePickerFromDefaultFilterType();
+    SQLFilter.chooseType("Field Filter");
+    FieldFilter.mapTo({ table: "Products", field: "Category" });
+    setFilterQuestionSource({ question: "SQL source", field: "CATEGORY" });
+    saveQuestion("SQL filter");
+  });
+});