From 9a840f92c5a46561fedba33f15a8ac237861779d Mon Sep 17 00:00:00 2001
From: "Mahatthana (Kelvin) Nomsawadi" <me@bboykelvin.dev>
Date: Mon, 15 Jan 2024 16:29:35 +0700
Subject: [PATCH] Add show metabase links toggle admin setting (#37076)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add show metabase links toggle admin setting

* Hide Metabase links

* Fix type error

* Add selector tests

* Hide a link in HomeHelpCard

* Make test names more consistent with existing test suites

* Add tests to `NewModelOptions`

* Prepare `VisualizationError` for testing

* Add tests to `VisualizationError`

* Hide link and add tests to `PreviewQueryModal`

* Extract components to make it easier to test

* Move the new component into its own folder

* Hide Metabase link in `ActionDashcardSettings`

* Prepare `EmptyFormPlaceholder` for testing

* Hide Metabase link in `EmptyFormPlaceholder`

* Extract Metabase link from `FormCreator` for testing

* Hide Metabase link in `FormCreator`

* Extract link from `DashCardCardParameterMapper` to prepare for testing

* Hide Metabase link in `DashCardParameterMapper`

* Clean up the test setup function

* Hide Metabase link in `ExpressionEditorHelpText`

* Hide Metabase link in `TagEditorHelp`

* Hide Metabase links in `MetricList`

* Hide Metabase link in `SegmentList`

* Prepare `CaveatMessage` for testing

* Hide Metabase link in `CaveatMessage`

* Hide Metabase link in `CustomMapFooter`

* Hide Metabase link in `ImpossibleToCreateModelModal`

* Hide Metabase link in `ExpressionWidget`

* Hide Metabase link in `ExpressionWidget`

* Add BE tests

* Update sample database description

* Fix TypeScript type-check

* Fix failed unit tests

* Fix wrong test names

* Add missing translations

* Make tests looks like tests from other components

* Update enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleWidget.tsx

Co-authored-by: Nicolò Pretto <info@npretto.com>

* Simplify the change

* Fix weird import directory

* Fix wrapped translation copy

* Fix test name

* Move comment next to the component

* Fix test names

* Review: Make code easier to review

* Fix wrong document link

* Update src/metabase/public_settings.clj

Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>

* Update src/metabase/public_settings.clj

Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>

* Address review: BE changes

---------

Co-authored-by: Nicolò Pretto <info@npretto.com>
Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>
---
 .../metabase-enterprise/settings/selectors.ts |   4 +
 .../MetabaseLinksToggleDescription.tsx        |  27 +++
 .../MetabaseLinksToggleWidget.tsx             |  28 +++
 .../MetabaseLinksToggleWidget/index.ts        |   2 +
 .../metabase-enterprise/whitelabel/index.js   |  12 ++
 .../src/metabase-types/api/mocks/settings.ts  |   1 +
 frontend/src/metabase-types/api/settings.ts   |   1 +
 .../ActionDashcardSettings.styled.tsx         |  14 --
 .../ActionViz/ActionDashcardSettings.tsx      |  14 +-
 .../ExplainerText/ExplainerText.styled.tsx    |  18 ++
 .../ActionViz/ExplainerText/ExplainerText.tsx |  25 +++
 .../ActionViz/ExplainerText/index.ts          |   1 +
 .../ExplainerText/tests/common.unit.spec.ts   |  26 +++
 .../tests/enterprise.unit.spec.ts             |  31 +++
 .../ExplainerText/tests/premium.unit.spec.ts  |  35 ++++
 .../ActionViz/ExplainerText/tests/setup.tsx   |  32 ++++
 .../Description/Description.styled.tsx        |   7 +
 .../FormCreator/Description/Description.tsx   |  28 +++
 .../FormCreator/Description/index.ts          |   1 +
 .../Description/test/common.unit.spec.ts      |  26 +++
 .../Description/test/enterprise.unit.spec.ts  |  34 ++++
 .../Description/test/premium.unit.spec.ts     |  35 ++++
 .../FormCreator/Description/test/setup.tsx    |  32 ++++
 .../FormCreator/EmptyFormPlaceholder.tsx      |  39 ----
 .../EmptyFormPlaceholder.styled.tsx           |  62 ++++++
 .../EmptyFormPlaceholder.tsx                  |  46 +++++
 .../FormCreator/EmptyFormPlaceholder/index.ts |   1 +
 .../tests/common.unit.spec.ts                 |  20 ++
 .../tests/enterprise.unit.spec.ts             |  28 +++
 .../tests/premium.unit.spec.ts                |  31 +++
 .../EmptyFormPlaceholder/tests/setup.tsx      |  33 ++++
 .../FormCreator/FormCreator.styled.tsx        |  64 +------
 .../ActionCreator/FormCreator/FormCreator.tsx |  18 +-
 .../FormCreator/FormCreator.unit.spec.tsx     |   4 +-
 .../{ErrorMessage.jsx => ErrorMessage.tsx}    |  32 ++--
 .../components/ErrorMessage/index.jsx         |   2 +-
 .../DashCardCardParameterMapper.jsx           |  19 +-
 .../DashCardCardParameterMapper.styled.tsx    |  27 ---
 .../DashCardCardParameterMapper.unit.spec.jsx |   5 +-
 .../DisabledNativeCardHelpText.styled.tsx     |  31 +++
 .../DisabledNativeCardHelpText.tsx            |  38 ++++
 .../DisabledNativeCardHelpText/index.ts       |   1 +
 .../tests/common.unit.spec.ts                 |  26 +++
 .../tests/enterprise.unit.spec.ts             |  31 +++
 .../tests/premium.unit.spec.ts                |  35 ++++
 .../tests/setup.tsx                           |  38 ++++
 .../components/HomeHelpCard/HomeHelpCard.tsx  |  13 +-
 .../HomeHelpCard/HomeHelpCard.unit.spec.tsx   |  13 --
 .../HomeHelpCard/tests/common.unit.spec.tsx   |  19 ++
 .../tests/enterprise.unit.spec.tsx            |  24 +++
 .../HomeHelpCard/tests/premium.unit.spec.tsx  |  28 +++
 .../components/HomeHelpCard/tests/setup.tsx   |  35 ++++
 .../NewModelOptions/NewModelOptions.tsx       |  19 +-
 .../tests/common.unit.spec.tsx                |  39 ++++
 .../tests/enterprise.unit.spec.tsx            |  44 +++++
 .../tests/premium.unit.spec.tsx               |  51 +++++
 .../NewModelOptions/tests/setup.tsx           |  42 ++++
 frontend/src/metabase/plugins/index.ts        |   1 +
 .../ImpossibleToCreateModelModal.tsx          |  31 ++-
 .../tests/common.unit.spec.ts                 |  44 +++++
 .../tests/enterprise.unit.spec.ts             |  49 +++++
 .../tests/premium.unit.spec.ts                |  51 +++++
 .../tests/setup.tsx                           |  34 ++++
 .../query_builder/components/QueryModals.tsx  |   2 +-
 .../components/QueryVisualization.jsx         |   2 +-
 .../components/VisualizationError.jsx         | 181 ------------------
 .../VisualizationError.styled.tsx             |   0
 .../VisualizationError/VisualizationError.tsx | 129 +++++++++++++
 .../components/VisualizationError/index.ts    |   1 +
 .../tests/common.unit.spec.ts                 |  68 +++++++
 .../tests/enterprise.unit.spec.ts             |  73 +++++++
 .../tests/premium.unit.spec.ts                |  77 ++++++++
 .../VisualizationError/tests/setup.tsx        |  62 ++++++
 .../tests/utils.unit.spec.ts}                 |  83 +-------
 .../components/VisualizationError/utils.jsx   |  45 +++++
 .../components/VisualizationResult.jsx        |   2 +-
 .../ExpressionEditorHelpText.stories.tsx      |   2 +-
 .../ExpressionEditorHelpText.styled.tsx       |   0
 .../ExpressionEditorHelpText.tsx              |  32 ++--
 .../ExpressionEditorHelpText/index.ts         |   1 +
 .../tests/common.unit.spec.tsx}               |  47 +++--
 .../tests/enterprise.unit.spec.tsx            |  40 ++++
 .../tests/premium.unit.spec.tsx               |  44 +++++
 .../ExpressionEditorHelpText/tests/setup.tsx  |  50 +++++
 .../ExpressionEditorTextfield.tsx             |   2 +-
 .../expressions/ExpressionWidget.tsx          |  20 +-
 .../ExpressionWidgetInfo.tsx                  |  37 ++++
 .../expressions/ExpressionWidgetInfo/index.ts |   1 +
 .../tests/common.unit.spec.ts                 |  39 ++++
 .../tests/enterprise.unit.spec.ts             |  44 +++++
 .../tests/premium.unit.spec.ts                |  45 +++++
 .../ExpressionWidgetInfo/tests/setup.tsx      |  32 ++++
 .../{ => TagEditorHelp}/TagEditorHelp.tsx     |  28 +--
 .../template_tags/TagEditorHelp/index.ts      |   1 +
 .../TagEditorHelp/tests/common.unit.spec.ts   |  16 ++
 .../tests/enterprise.unit.spec.ts             |  24 +++
 .../TagEditorHelp/tests/premium.unit.spec.ts  |  27 +++
 .../TagEditorHelp/tests/setup.tsx             |  46 +++++
 .../PreviewQueryModal/PreviewQueryModal.tsx   |  35 ++--
 .../view/PreviewQueryModal/index.ts           |   3 +-
 .../tests/common.unit.spec.tsx                |  22 +++
 .../tests/enterprise.unit.spec.tsx            |  27 +++
 .../tests/premium.unit.spec.tsx               |  31 +++
 .../view/PreviewQueryModal/tests/setup.tsx    |  55 ++++++
 .../metabase/reference/metrics/MetricList.jsx |  93 ---------
 .../metrics/MetricList/MetricList.tsx         |  81 ++++++++
 .../reference/metrics/MetricList/index.ts     |   1 +
 .../MetricList/tests/common.unit.spec.ts      |  53 +++++
 .../MetricList/tests/enterprise.unit.spec.ts  |  61 ++++++
 .../MetricList/tests/premium.unit.spec.ts     |  62 ++++++
 .../metrics/MetricList/tests/setup.tsx        |  38 ++++
 .../reference/metrics/MetricListContainer.jsx |   2 +-
 .../reference/segments/SegmentList.jsx        |  93 ---------
 .../segments/SegmentList/SegmentList.tsx      |  81 ++++++++
 .../reference/segments/SegmentList/index.ts   |   1 +
 .../SegmentList/tests/common.unit.spec.ts     |  47 +++++
 .../SegmentList/tests/enterprise.unit.spec.ts |  55 ++++++
 .../SegmentList/tests/premium.unit.spec.ts    |  56 ++++++
 .../segments/SegmentList/tests/setup.tsx      |  38 ++++
 .../segments/SegmentListContainer.jsx         |   2 +-
 .../metabase/selectors/whitelabel/index.ts    |   4 +
 .../whitelabel/tests/common.unit.spec.ts      |  15 ++
 .../whitelabel/tests/enterprise.unit.spec.ts  |  15 ++
 .../whitelabel/tests/premium.unit.spec.ts     |  17 +-
 .../selectors/whitelabel/tests/setup.tsx      |   3 +
 .../AddEditSidebar/AddEditEmailSidebar.jsx    |   2 +-
 .../AddEditSidebar/AddEditSlackSidebar.jsx    |   2 +-
 .../AddEditSidebar/CaveatMessage.jsx          |  24 ---
 .../CaveatMessage/CaveatMessage.jsx           |  28 +++
 .../CaveatMessage.styled.tsx                  |   0
 .../AddEditSidebar/CaveatMessage/index.ts     |   1 +
 .../CaveatMessage/tests/common.unit.spec.ts   |  26 +++
 .../tests/enterprise.unit.spec.ts             |  31 +++
 .../CaveatMessage/tests/premium.unit.spec.ts  |  35 ++++
 .../CaveatMessage/tests/setup.tsx             |  32 ++++
 .../overlays/Popover/use-popover.ts           |   2 +-
 .../src/metabase/visualizations/register.js   |   4 +-
 .../Map/CustomMapFooter/CustomMapFooter.tsx   |  42 ++++
 .../Map/CustomMapFooter/index.ts              |   1 +
 .../CustomMapFooter/tests/common.unit.spec.ts |  60 ++++++
 .../tests/enterprise.unit.spec.ts             |  65 +++++++
 .../tests/premium.unit.spec.ts                |  64 +++++++
 .../Map/CustomMapFooter/tests/setup.tsx       |  42 ++++
 .../visualizations/{ => Map}/Map.jsx          |  42 +---
 .../visualizations/{ => Map}/Maps.styled.tsx  |   0
 .../visualizations/Map/index.ts               |   1 +
 frontend/src/types/global.d.ts                |   7 +
 resources/sample-database.db.mv.db            | Bin 3477504 -> 3497984 bytes
 src/metabase/public_settings.clj              |   9 +-
 test/metabase/public_settings_test.clj        |  58 ++++--
 150 files changed, 3602 insertions(+), 872 deletions(-)
 create mode 100644 enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx
 create mode 100644 enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleWidget.tsx
 create mode 100644 enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/index.ts
 create mode 100644 frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.styled.tsx
 create mode 100644 frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.tsx
 create mode 100644 frontend/src/metabase/actions/components/ActionViz/ExplainerText/index.ts
 create mode 100644 frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/setup.tsx
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.styled.tsx
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.tsx
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/index.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/setup.tsx
 delete mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder.tsx
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.styled.tsx
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.tsx
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/index.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/setup.tsx
 rename frontend/src/metabase/components/ErrorMessage/{ErrorMessage.jsx => ErrorMessage.tsx} (56%)
 create mode 100644 frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.styled.tsx
 create mode 100644 frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.tsx
 create mode 100644 frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/index.ts
 create mode 100644 frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/setup.tsx
 delete mode 100644 frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/components/HomeHelpCard/tests/common.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/components/HomeHelpCard/tests/enterprise.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/components/HomeHelpCard/tests/premium.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/components/HomeHelpCard/tests/setup.tsx
 create mode 100644 frontend/src/metabase/models/containers/NewModelOptions/tests/common.unit.spec.tsx
 create mode 100644 frontend/src/metabase/models/containers/NewModelOptions/tests/enterprise.unit.spec.tsx
 create mode 100644 frontend/src/metabase/models/containers/NewModelOptions/tests/premium.unit.spec.tsx
 create mode 100644 frontend/src/metabase/models/containers/NewModelOptions/tests/setup.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/setup.tsx
 delete mode 100644 frontend/src/metabase/query_builder/components/VisualizationError.jsx
 rename frontend/src/metabase/query_builder/components/{ => VisualizationError}/VisualizationError.styled.tsx (100%)
 create mode 100644 frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/VisualizationError/index.ts
 create mode 100644 frontend/src/metabase/query_builder/components/VisualizationError/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/VisualizationError/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/VisualizationError/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/VisualizationError/tests/setup.tsx
 rename frontend/src/metabase/query_builder/components/{VisualizationError.unit.spec.tsx => VisualizationError/tests/utils.unit.spec.ts} (51%)
 create mode 100644 frontend/src/metabase/query_builder/components/VisualizationError/utils.jsx
 rename frontend/src/metabase/query_builder/components/expressions/{ => ExpressionEditorHelpText}/ExpressionEditorHelpText.stories.tsx (93%)
 rename frontend/src/metabase/query_builder/components/expressions/{ => ExpressionEditorHelpText}/ExpressionEditorHelpText.styled.tsx (100%)
 rename frontend/src/metabase/query_builder/components/expressions/{ => ExpressionEditorHelpText}/ExpressionEditorHelpText.tsx (82%)
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/index.ts
 rename frontend/src/metabase/query_builder/components/expressions/{ExpressionEditorHelpText.unit.spec.tsx => ExpressionEditorHelpText/tests/common.unit.spec.tsx} (70%)
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/enterprise.unit.spec.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/premium.unit.spec.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/setup.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/ExpressionWidgetInfo.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/index.ts
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/setup.tsx
 rename frontend/src/metabase/query_builder/components/template_tags/{ => TagEditorHelp}/TagEditorHelp.tsx (92%)
 create mode 100644 frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/index.ts
 create mode 100644 frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/setup.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/common.unit.spec.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/enterprise.unit.spec.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/premium.unit.spec.tsx
 create mode 100644 frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/setup.tsx
 delete mode 100644 frontend/src/metabase/reference/metrics/MetricList.jsx
 create mode 100644 frontend/src/metabase/reference/metrics/MetricList/MetricList.tsx
 create mode 100644 frontend/src/metabase/reference/metrics/MetricList/index.ts
 create mode 100644 frontend/src/metabase/reference/metrics/MetricList/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/reference/metrics/MetricList/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/reference/metrics/MetricList/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/reference/metrics/MetricList/tests/setup.tsx
 delete mode 100644 frontend/src/metabase/reference/segments/SegmentList.jsx
 create mode 100644 frontend/src/metabase/reference/segments/SegmentList/SegmentList.tsx
 create mode 100644 frontend/src/metabase/reference/segments/SegmentList/index.ts
 create mode 100644 frontend/src/metabase/reference/segments/SegmentList/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/reference/segments/SegmentList/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/reference/segments/SegmentList/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/reference/segments/SegmentList/tests/setup.tsx
 delete mode 100644 frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage.jsx
 create mode 100644 frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/CaveatMessage.jsx
 rename frontend/src/metabase/sharing/components/AddEditSidebar/{ => CaveatMessage}/CaveatMessage.styled.tsx (100%)
 create mode 100644 frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/index.ts
 create mode 100644 frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/setup.tsx
 create mode 100644 frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/CustomMapFooter.tsx
 create mode 100644 frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/index.ts
 create mode 100644 frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/common.unit.spec.ts
 create mode 100644 frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/enterprise.unit.spec.ts
 create mode 100644 frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/premium.unit.spec.ts
 create mode 100644 frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/setup.tsx
 rename frontend/src/metabase/visualizations/visualizations/{ => Map}/Map.jsx (90%)
 rename frontend/src/metabase/visualizations/visualizations/{ => Map}/Maps.styled.tsx (100%)
 create mode 100644 frontend/src/metabase/visualizations/visualizations/Map/index.ts

diff --git a/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts b/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts
index a8d316413d1..523466cdbc7 100644
--- a/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts
+++ b/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts
@@ -25,3 +25,7 @@ export const getIsWhiteLabeling = (state: EnterpriseState) =>
 export function getApplicationName(state: EnterpriseState) {
   return getSetting(state, "application-name");
 }
+
+export function getShowMetabaseLinks(state: EnterpriseState) {
+  return getSetting(state, "show-metabase-links");
+}
diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx
new file mode 100644
index 00000000000..c982516bbb4
--- /dev/null
+++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx
@@ -0,0 +1,27 @@
+import { t, jt } from "ttag";
+import { Anchor, Popover, Stack, Text } from "metabase/ui";
+
+export function MetabaseLinksToggleDescription() {
+  return jt`Control the visibility of links to Metabase documentation and Metabase references in your instance. ${(
+    <Popover key="popover" position="top-start">
+      <Popover.Target>
+        <Anchor style={{ userSelect: "none", cursor: "pointer" }}>
+          {t`Learn more`}
+        </Anchor>
+      </Popover.Target>
+      <Popover.Dropdown>
+        <Stack p="md" spacing="sm" maw={420}>
+          <Text size="sm">
+            {t`This affects all links in the product experience (outside of the admin panel) that point to Metabase.com URLs.`}
+          </Text>
+          <Text size="sm">
+            {t`When hidden, your users will lose the ability to troubleshoot and learn how to use features such as the Query and SQL Editors, among others.`}
+          </Text>
+          <Text size="sm">
+            {t`You might also want to customize the Application Name setting.`}
+          </Text>
+        </Stack>
+      </Popover.Dropdown>
+    </Popover>
+  )}`;
+}
diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleWidget.tsx b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleWidget.tsx
new file mode 100644
index 00000000000..2334c66c403
--- /dev/null
+++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleWidget.tsx
@@ -0,0 +1,28 @@
+import { t } from "ttag";
+
+import { Switch } from "metabase/ui";
+
+interface MetabaseLinksToggleSetting {
+  value: boolean | null;
+  default: boolean;
+}
+
+interface MetabaseLinksToggleWidgetProps {
+  setting: MetabaseLinksToggleSetting;
+  onChange: (value: boolean) => void;
+}
+
+export function MetabaseLinksToggleWidget({
+  setting,
+  onChange,
+}: MetabaseLinksToggleWidgetProps) {
+  return (
+    <Switch
+      checked={setting.value ?? setting.default}
+      onChange={e => onChange(e.target.checked)}
+      labelPosition="left"
+      label={t`Show links and references`}
+      size="sm"
+    />
+  );
+}
diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/index.ts b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/index.ts
new file mode 100644
index 00000000000..3f0d19f43c1
--- /dev/null
+++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/index.ts
@@ -0,0 +1,2 @@
+export { MetabaseLinksToggleWidget } from "./MetabaseLinksToggleWidget";
+export { MetabaseLinksToggleDescription } from "./MetabaseLinksToggleDescription";
diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js b/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js
index a74dc901775..8c3cb865fe5 100644
--- a/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js
+++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js
@@ -12,6 +12,7 @@ import {
   getApplicationName,
   getIsWhiteLabeling,
   getLoadingMessage,
+  getShowMetabaseLinks,
 } from "metabase-enterprise/settings/selectors";
 import MetabaseSettings from "metabase/lib/settings";
 
@@ -27,6 +28,10 @@ import LogoIcon from "./components/LogoIcon";
 import { updateColors } from "./lib/whitelabel";
 import { getLoadingMessageOptions } from "./lib/loading-message";
 import { HelpLinkSettings } from "./components/HelpLinkSettings";
+import {
+  MetabaseLinksToggleDescription,
+  MetabaseLinksToggleWidget,
+} from "./components/MetabaseLinksToggleWidget";
 
 if (hasPremiumFeature("whitelabel")) {
   PLUGIN_LANDING_PAGE.push(() => MetabaseSettings.get("landing-page"));
@@ -108,6 +113,12 @@ if (hasPremiumFeature("whitelabel")) {
           widget: LighthouseToggleWidget,
           defaultValue: true,
         },
+        {
+          key: "show-metabase-links",
+          display_name: t`Documentation and references`,
+          description: <MetabaseLinksToggleDescription />,
+          widget: MetabaseLinksToggleWidget,
+        },
       ],
     },
     ...sections,
@@ -125,4 +136,5 @@ if (hasPremiumFeature("whitelabel")) {
   PLUGIN_SELECTORS.getLoadingMessage = getLoadingMessage;
   PLUGIN_SELECTORS.getIsWhiteLabeling = getIsWhiteLabeling;
   PLUGIN_SELECTORS.getApplicationName = getApplicationName;
+  PLUGIN_SELECTORS.getShowMetabaseLinks = getShowMetabaseLinks;
 }
diff --git a/frontend/src/metabase-types/api/mocks/settings.ts b/frontend/src/metabase-types/api/mocks/settings.ts
index 10f42f32acc..ecd5b75012c 100644
--- a/frontend/src/metabase-types/api/mocks/settings.ts
+++ b/frontend/src/metabase-types/api/mocks/settings.ts
@@ -200,6 +200,7 @@ export const createMockSettings = (
   "show-homepage-pin-message": false,
   "show-homepage-xrays": false,
   "show-lighthouse-illustration": true,
+  "show-metabase-links": true,
   "show-metabot": true,
   "site-locale": "en",
   "site-url": "http://localhost:3000",
diff --git a/frontend/src/metabase-types/api/settings.ts b/frontend/src/metabase-types/api/settings.ts
index 1f1b394fc34..a358cf69002 100644
--- a/frontend/src/metabase-types/api/settings.ts
+++ b/frontend/src/metabase-types/api/settings.ts
@@ -258,6 +258,7 @@ export interface Settings {
   "show-homepage-pin-message": boolean;
   "show-homepage-xrays": boolean;
   "show-lighthouse-illustration": boolean;
+  "show-metabase-links": boolean;
   "show-metabot": boolean;
   "site-locale": string;
   "site-uuid": string;
diff --git a/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.styled.tsx b/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.styled.tsx
index 2630b02d1e7..02244169ff0 100644
--- a/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.styled.tsx
+++ b/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.styled.tsx
@@ -3,8 +3,6 @@ import styled from "@emotion/styled";
 import { space } from "metabase/styled-components/theme";
 import { color } from "metabase/lib/colors";
 
-import ExternalLink from "metabase/core/components/ExternalLink";
-
 export const ActionSettingsWrapper = styled.div`
   display: flex;
   height: 80vh;
@@ -81,15 +79,3 @@ export const ModalActions = styled.div`
   padding: 1rem;
   border-top: 1px solid ${color("border")};
 `;
-
-export const ExplainerText = styled.p`
-  margin-left: ${space(3)};
-  margin-right: ${space(3)};
-
-  color: ${color("text-medium")};
-`;
-
-export const BrandLinkWithLeftMargin = styled(ExternalLink)`
-  margin-left: ${space(1)};
-  color: ${color("brand")};
-`;
diff --git a/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.tsx b/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.tsx
index ff394f45447..8629dba0306 100644
--- a/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.tsx
+++ b/frontend/src/metabase/actions/components/ActionViz/ActionDashcardSettings.tsx
@@ -5,8 +5,6 @@ import { useMemo } from "react";
 import Button from "metabase/core/components/Button";
 import EmptyState from "metabase/components/EmptyState";
 
-import MetabaseSettings from "metabase/lib/settings";
-
 import { ConnectedActionPicker } from "metabase/actions/containers/ActionPicker";
 import { setActionForDashcard } from "metabase/dashboard/actions";
 
@@ -27,14 +25,13 @@ import {
   ActionSettingsLeft,
   ActionSettingsRight,
   ModalActions,
-  ExplainerText,
-  BrandLinkWithLeftMargin,
 } from "./ActionDashcardSettings.styled";
 import {
   getParameterDefaultValue,
   isParameterHidden,
   isParameterRequired,
 } from "./utils";
+import { ExplainerText } from "./ExplainerText";
 
 const mapDispatchToProps = {
   setActionForDashcard,
@@ -101,14 +98,7 @@ export function ActionDashcardSettings({
                 <ActionSettingsHeader>
                   {t`Where should the values for '${action.name}' come from?`}
                 </ActionSettingsHeader>
-                <ExplainerText>
-                  {t`You can either ask users to enter values, or use the value of a dashboard filter.`}
-                  <BrandLinkWithLeftMargin
-                    href={MetabaseSettings.docsUrl("dashboards/actions")}
-                  >
-                    {t`Learn more.`}
-                  </BrandLinkWithLeftMargin>
-                </ExplainerText>
+                <ExplainerText />
               </>
             )}
             <ParameterMapperContainer>
diff --git a/frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.styled.tsx b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.styled.tsx
new file mode 100644
index 00000000000..663bb9c702a
--- /dev/null
+++ b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.styled.tsx
@@ -0,0 +1,18 @@
+import styled from "@emotion/styled";
+
+import { space } from "metabase/styled-components/theme";
+import { color } from "metabase/lib/colors";
+
+import ExternalLink from "metabase/core/components/ExternalLink";
+
+export const ExplainerTextContainer = styled.p`
+  margin-left: ${space(3)};
+  margin-right: ${space(3)};
+
+  color: ${color("text-medium")};
+`;
+
+export const BrandLinkWithLeftMargin = styled(ExternalLink)`
+  margin-left: ${space(1)};
+  color: ${color("brand")};
+`;
diff --git a/frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.tsx b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.tsx
new file mode 100644
index 00000000000..39084ad8419
--- /dev/null
+++ b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/ExplainerText.tsx
@@ -0,0 +1,25 @@
+import { t } from "ttag";
+import MetabaseSettings from "metabase/lib/settings";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+
+import {
+  ExplainerTextContainer,
+  BrandLinkWithLeftMargin,
+} from "./ExplainerText.styled";
+
+export function ExplainerText() {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+  return (
+    <ExplainerTextContainer>
+      {t`You can either ask users to enter values, or use the value of a dashboard filter.`}
+      {showMetabaseLinks && (
+        <BrandLinkWithLeftMargin
+          href={MetabaseSettings.docsUrl("dashboards/actions")}
+        >
+          {t`Learn more.`}
+        </BrandLinkWithLeftMargin>
+      )}
+    </ExplainerTextContainer>
+  );
+}
diff --git a/frontend/src/metabase/actions/components/ActionViz/ExplainerText/index.ts b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/index.ts
new file mode 100644
index 00000000000..b2ac86a0300
--- /dev/null
+++ b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/index.ts
@@ -0,0 +1 @@
+export { ExplainerText } from "./ExplainerText";
diff --git a/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/common.unit.spec.ts b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..df5c131bb25
--- /dev/null
+++ b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/common.unit.spec.ts
@@ -0,0 +1,26 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("ExplainerText (OSS)", () => {
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "You can either ask users to enter values, or use the value of a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "You can either ask users to enter values, or use the value of a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/enterprise.unit.spec.ts b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..15ad40b1ceb
--- /dev/null
+++ b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/enterprise.unit.spec.ts
@@ -0,0 +1,31 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("ExplainerText (EE without token)", () => {
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "You can either ask users to enter values, or use the value of a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "You can either ask users to enter values, or use the value of a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/premium.unit.spec.ts b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..49997db8083
--- /dev/null
+++ b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/premium.unit.spec.ts
@@ -0,0 +1,35 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("ExplainerText (EE with token)", () => {
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "You can either ask users to enter values, or use the value of a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+
+  it("should not render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "You can either ask users to enter values, or use the value of a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.queryByText("Learn more.")).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/setup.tsx b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/setup.tsx
new file mode 100644
index 00000000000..999b37c32bb
--- /dev/null
+++ b/frontend/src/metabase/actions/components/ActionViz/ExplainerText/tests/setup.tsx
@@ -0,0 +1,32 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { ExplainerText } from "../ExplainerText";
+
+export interface SetupOpts {
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<ExplainerText />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.styled.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.styled.tsx
new file mode 100644
index 00000000000..c36bd51112a
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.styled.tsx
@@ -0,0 +1,7 @@
+import styled from "@emotion/styled";
+
+import { color } from "metabase/lib/colors";
+
+export const InfoText = styled.div`
+  color: ${color("text-medium")};
+`;
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.tsx
new file mode 100644
index 00000000000..bd284eb8464
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/Description.tsx
@@ -0,0 +1,28 @@
+import { jt, t } from "ttag";
+import ExternalLink from "metabase/core/components/ExternalLink";
+import { useSelector } from "metabase/lib/redux";
+import { getDocsUrl } from "metabase/selectors/settings";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { InfoText } from "./Description.styled";
+
+export function Description() {
+  const docsLink = useSelector(state =>
+    getDocsUrl(state, { page: "actions/custom" }),
+  );
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
+  return (
+    <InfoText>
+      {jt`Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter.`}
+      {showMetabaseLinks && (
+        <>
+          {" "}
+          <ExternalLink
+            key="learn-more"
+            href={docsLink}
+          >{t`Learn more`}</ExternalLink>
+        </>
+      )}
+    </InfoText>
+  );
+}
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/index.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/index.ts
new file mode 100644
index 00000000000..48db91ad85a
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/index.ts
@@ -0,0 +1 @@
+export { Description } from "./Description";
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/common.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/common.unit.spec.ts
new file mode 100644
index 00000000000..6220e4cef6b
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/common.unit.spec.ts
@@ -0,0 +1,26 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("FormCerator > Description (OSS)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/enterprise.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..1de6c1b2f79
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/enterprise.unit.spec.ts
@@ -0,0 +1,34 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  return baseSetup({
+    hasEnterprisePlugins: true,
+    ...opts,
+  });
+}
+
+describe("FormCerator > Description (EE without token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/premium.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/premium.unit.spec.ts
new file mode 100644
index 00000000000..4e6a549dd15
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/premium.unit.spec.ts
@@ -0,0 +1,35 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  return baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("FormCerator > Description (EE with token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more")).toBeInTheDocument();
+  });
+
+  it("should not show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.queryByText("Learn more")).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/setup.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/setup.tsx
new file mode 100644
index 00000000000..300a959e806
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/Description/test/setup.tsx
@@ -0,0 +1,32 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { Description } from "../Description";
+
+export interface SetupOpts {
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<Description />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder.tsx
deleted file mode 100644
index 4b005b8a985..00000000000
--- a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { t } from "ttag";
-
-import { Icon } from "metabase/ui";
-
-import MetabaseSettings from "metabase/lib/settings";
-
-import {
-  EmptyFormPlaceholderWrapper,
-  ExplainerTitle,
-  ExplainerText,
-  ExplainerList,
-  ExplainerLink,
-  IconContainer,
-  TopRightIcon,
-} from "./FormCreator.styled";
-
-export const EmptyFormPlaceholder = () => (
-  <EmptyFormPlaceholderWrapper>
-    <IconContainer>
-      <Icon name="sql" size={62} />
-      <TopRightIcon name="insight" size={24} />
-    </IconContainer>
-    <ExplainerTitle>{t`Build custom forms and business logic.`}</ExplainerTitle>
-    <ExplainerText>
-      {t`Actions let you write parameterized SQL that writes back to your database. Actions can be attached to buttons on dashboards to create custom workflows. You can even publicly share the parameterized forms they generate to collect data.`}
-    </ExplainerText>
-    <ExplainerText>
-      {t`Here are a few ideas for what you can do with actions`}
-      <ExplainerList>
-        <li>{t`Create a customer feedback form and embed it on your website.`}</li>
-        <li>{t`Mark the customer you’re viewing in a dashboard as a VIP.`}</li>
-        <li>{t`Let team members remove redundant data.`}</li>
-      </ExplainerList>
-    </ExplainerText>
-    <ExplainerLink
-      href={MetabaseSettings.docsUrl("actions/custom")}
-    >{t`See an example`}</ExplainerLink>
-  </EmptyFormPlaceholderWrapper>
-);
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.styled.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.styled.tsx
new file mode 100644
index 00000000000..203cca7f3a8
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.styled.tsx
@@ -0,0 +1,62 @@
+import styled from "@emotion/styled";
+import { Icon } from "metabase/ui";
+import ExternalLink from "metabase/core/components/ExternalLink";
+
+import { color, lighten } from "metabase/lib/colors";
+import { space } from "metabase/styled-components/theme";
+
+export const EmptyFormPlaceholderWrapper = styled.div`
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  height: 100%;
+  padding: 3rem;
+`;
+
+export const ExplainerTitle = styled.h3`
+  margin-bottom: ${space(1)};
+`;
+
+export const ExplainerText = styled.div`
+  font-weight: 400;
+  line-height: 1.5rem;
+  color: ${color("text-medium")};
+  margin: ${space(1)} 0 0 0;
+`;
+
+export const ExplainerList = styled.ul`
+  list-style-type: disc;
+  margin-left: 1.5rem;
+
+  li {
+    font-weight: 400;
+    line-height: 24px;
+    color: ${color("text-medium")};
+    margin: 0;
+  }
+`;
+
+export const ExplainerLink = styled(ExternalLink)`
+  font-weight: 700;
+  margin-top: ${space(2)};
+
+  color: ${color("brand")};
+
+  &:hover {
+    color: ${lighten("brand", 0.1)};
+  }
+`;
+
+export const IconContainer = styled.div`
+  display: inline-block;
+  padding: 1.25rem;
+  position: relative;
+  color: ${color("brand")};
+  align-self: center;
+`;
+
+export const TopRightIcon = styled(Icon)`
+  position: absolute;
+  top: 0;
+  right: 0;
+`;
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.tsx
new file mode 100644
index 00000000000..e612e52c193
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/EmptyFormPlaceholder.tsx
@@ -0,0 +1,46 @@
+import { t } from "ttag";
+
+import { Icon } from "metabase/ui";
+import MetabaseSettings from "metabase/lib/settings";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { useSelector } from "metabase/lib/redux";
+
+import {
+  EmptyFormPlaceholderWrapper,
+  ExplainerTitle,
+  ExplainerText,
+  ExplainerList,
+  ExplainerLink,
+  IconContainer,
+  TopRightIcon,
+} from "./EmptyFormPlaceholder.styled";
+
+export const EmptyFormPlaceholder = () => {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
+  return (
+    <EmptyFormPlaceholderWrapper>
+      <IconContainer>
+        <Icon name="sql" size={62} />
+        <TopRightIcon name="insight" size={24} />
+      </IconContainer>
+      <ExplainerTitle>{t`Build custom forms and business logic.`}</ExplainerTitle>
+      <ExplainerText>
+        {t`Actions let you write parameterized SQL that writes back to your database. Actions can be attached to buttons on dashboards to create custom workflows. You can even publicly share the parameterized forms they generate to collect data.`}
+      </ExplainerText>
+      <ExplainerText>
+        {t`Here are a few ideas for what you can do with actions`}
+        <ExplainerList>
+          <li>{t`Create a customer feedback form and embed it on your website.`}</li>
+          <li>{t`Mark the customer you’re viewing in a dashboard as a VIP.`}</li>
+          <li>{t`Let team members remove redundant data.`}</li>
+        </ExplainerList>
+      </ExplainerText>
+      {showMetabaseLinks && (
+        <ExplainerLink
+          href={MetabaseSettings.docsUrl("actions/custom")}
+        >{t`See an example`}</ExplainerLink>
+      )}
+    </EmptyFormPlaceholderWrapper>
+  );
+};
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/index.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/index.ts
new file mode 100644
index 00000000000..e8454899544
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/index.ts
@@ -0,0 +1 @@
+export { EmptyFormPlaceholder } from "./EmptyFormPlaceholder";
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/common.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..020d21316c7
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/common.unit.spec.ts
@@ -0,0 +1,20 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("EmptyFormPlaceholder (OSS)", () => {
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+    expect(
+      screen.getByText("Build custom forms and business logic."),
+    ).toBeInTheDocument();
+    expect(screen.getByText("See an example")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+    expect(
+      screen.getByText("Build custom forms and business logic."),
+    ).toBeInTheDocument();
+    expect(screen.getByText("See an example")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/enterprise.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..412afc57015
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/enterprise.unit.spec.ts
@@ -0,0 +1,28 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    ...opts,
+  });
+}
+
+describe("EmptyFormPlaceholder (EE without token)", () => {
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+    expect(
+      screen.getByText("Build custom forms and business logic."),
+    ).toBeInTheDocument();
+    expect(screen.getByText("See an example")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+    expect(
+      screen.getByText("Build custom forms and business logic."),
+    ).toBeInTheDocument();
+    expect(screen.getByText("See an example")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/premium.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..b886f6f8f71
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/premium.unit.spec.ts
@@ -0,0 +1,31 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: {
+      whitelabel: true,
+    },
+    ...opts,
+  });
+}
+
+describe("EmptyFormPlaceholder (EE with token)", () => {
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+    expect(
+      screen.getByText("Build custom forms and business logic."),
+    ).toBeInTheDocument();
+    expect(screen.getByText("See an example")).toBeInTheDocument();
+  });
+
+  it("should not render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+    expect(
+      screen.getByText("Build custom forms and business logic."),
+    ).toBeInTheDocument();
+    expect(screen.queryByText("See an example")).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/setup.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/setup.tsx
new file mode 100644
index 00000000000..4fbdcdf3819
--- /dev/null
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/EmptyFormPlaceholder/tests/setup.tsx
@@ -0,0 +1,33 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { EmptyFormPlaceholder } from "../EmptyFormPlaceholder";
+
+export interface SetupOpts {
+  applicationName?: string;
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<EmptyFormPlaceholder />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.styled.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.styled.tsx
index de0fdcbad2d..7241217f967 100644
--- a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.styled.tsx
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.styled.tsx
@@ -1,8 +1,6 @@
 import styled from "@emotion/styled";
-import { Icon } from "metabase/ui";
-import ExternalLink from "metabase/core/components/ExternalLink";
 
-import { color, lighten, alpha } from "metabase/lib/colors";
+import { color, alpha } from "metabase/lib/colors";
 import { space } from "metabase/styled-components/theme";
 
 export const FormContainer = styled.div`
@@ -18,72 +16,12 @@ export const FormFieldEditorDragContainer = styled.div`
   margin-bottom: ${space(1)};
 `;
 
-export const InfoText = styled.div`
-  color: ${color("text-medium")};
-`;
-
 export const FieldSettingsButtonsContainer = styled.div`
   display: flex;
   align-items: center;
   gap: ${space(1)};
 `;
 
-export const EmptyFormPlaceholderWrapper = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  height: 100%;
-  padding: 3rem;
-`;
-
-export const ExplainerTitle = styled.h3`
-  margin-bottom: ${space(1)};
-`;
-
-export const ExplainerText = styled.div`
-  font-weight: 400;
-  line-height: 1.5rem;
-  color: ${color("text-medium")};
-  margin: ${space(1)} 0 0 0;
-`;
-
-export const ExplainerList = styled.ul`
-  list-style-type: disc;
-  margin-left: 1.5rem;
-
-  li {
-    font-weight: 400;
-    line-height: 24px;
-    color: ${color("text-medium")};
-    margin: 0;
-  }
-`;
-
-export const ExplainerLink = styled(ExternalLink)`
-  font-weight: 700;
-  margin-top: ${space(2)};
-
-  color: ${color("brand")};
-
-  &:hover {
-    color: ${lighten("brand", 0.1)};
-  }
-`;
-
-export const IconContainer = styled.div`
-  display: inline-block;
-  padding: 1.25rem;
-  position: relative;
-  color: ${color("brand")};
-  align-self: center;
-`;
-
-export const TopRightIcon = styled(Icon)`
-  position: absolute;
-  top: 0;
-  right: 0;
-`;
-
 export const WarningBanner = styled.div`
   padding: ${space(2)};
   border: 1px solid ${color("warning")};
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx
index 8e6498b2847..04da8e5ff69 100644
--- a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx
@@ -1,5 +1,5 @@
 import { useEffect, useCallback, useMemo, useState } from "react";
-import { jt, t } from "ttag";
+import { t } from "ttag";
 import _ from "underscore";
 import { Droppable, Draggable } from "react-beautiful-dnd";
 
@@ -10,11 +10,8 @@ import type {
 } from "react-beautiful-dnd";
 
 import { DragDropContext } from "metabase/core/components/DragDropContext";
-import ExternalLink from "metabase/core/components/ExternalLink";
 import { Form, FormProvider } from "metabase/forms";
 
-import MetabaseSettings from "metabase/lib/settings";
-
 import SidebarContent from "metabase/query_builder/components/SidebarContent";
 
 import type {
@@ -37,9 +34,9 @@ import FormFieldEditor from "./FormFieldEditor";
 import {
   FormContainer,
   FormFieldEditorDragContainer,
-  InfoText,
   WarningBanner,
 } from "./FormCreator.styled";
+import { Description } from "./Description";
 
 // FormEditor's can't be submitted as it serves as a form preview
 const ON_SUBMIT_NOOP = _.noop;
@@ -142,13 +139,6 @@ export function FormCreator({
 
   const fieldSettings = formSettings.fields || {};
 
-  const docsLink = (
-    <ExternalLink
-      key="learn-more"
-      href={MetabaseSettings.docsUrl("actions/custom")}
-    >{t`Learn more`}</ExternalLink>
-  );
-
   const showWarning = form.fields.some(field => {
     const settings = fieldSettings[field.name];
 
@@ -172,9 +162,7 @@ export function FormCreator({
   return (
     <SidebarContent title={t`Action parameters`}>
       <FormContainer>
-        <InfoText>
-          {jt`Configure your parameters' types and properties here. The values for these parameters can come from user input, or from a dashboard filter. ${docsLink}`}
-        </InfoText>
+        <Description />
         {showWarning && (
           <WarningBanner>
             <b>{t`Heads up.`}</b>{" "}
diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.unit.spec.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.unit.spec.tsx
index 137565f3049..d5cc5871d1f 100644
--- a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.unit.spec.tsx
+++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.unit.spec.tsx
@@ -1,6 +1,6 @@
-import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 
+import { renderWithProviders, screen } from "__support__/ui";
 import type {
   ActionFormSettings,
   FieldSettings,
@@ -50,7 +50,7 @@ type SetupOpts = {
 const setup = ({ parameters, formSettings, actionType }: SetupOpts) => {
   const onChange = jest.fn();
 
-  render(
+  renderWithProviders(
     <FormCreator
       parameters={parameters}
       formSettings={formSettings}
diff --git a/frontend/src/metabase/components/ErrorMessage/ErrorMessage.jsx b/frontend/src/metabase/components/ErrorMessage/ErrorMessage.tsx
similarity index 56%
rename from frontend/src/metabase/components/ErrorMessage/ErrorMessage.jsx
rename to frontend/src/metabase/components/ErrorMessage/ErrorMessage.tsx
index 33336be3b33..f7b692e1138 100644
--- a/frontend/src/metabase/components/ErrorMessage/ErrorMessage.jsx
+++ b/frontend/src/metabase/components/ErrorMessage/ErrorMessage.tsx
@@ -1,12 +1,22 @@
-/* eslint "react/prop-types": "warn" */
-
-import PropTypes from "prop-types";
-
 import cx from "classnames";
+import type { ReactNode } from "react";
 
-// NOTE: currently relies on .QueryError CSS selectors residing in query_builder.css
+interface ErrorMessageProps {
+  title: string;
+  type: string;
+  message: string;
+  action: ReactNode;
+  className?: string;
+}
 
-const ErrorMessage = ({ title, type, message, action, className }) => {
+// NOTE: currently relies on .QueryError CSS selectors residing in query_builder.css
+export const ErrorMessage = ({
+  title,
+  type,
+  message,
+  action,
+  className,
+}: ErrorMessageProps) => {
   return (
     <div className={cx(className, "QueryError flex align-center")}>
       <div className={`QueryError-image QueryError-image--${type}`} />
@@ -18,13 +28,3 @@ const ErrorMessage = ({ title, type, message, action, className }) => {
     </div>
   );
 };
-
-ErrorMessage.propTypes = {
-  title: PropTypes.string.isRequired,
-  type: PropTypes.string.isRequired,
-  message: PropTypes.string.isRequired,
-  action: PropTypes.node,
-  className: PropTypes.string,
-};
-
-export default ErrorMessage;
diff --git a/frontend/src/metabase/components/ErrorMessage/index.jsx b/frontend/src/metabase/components/ErrorMessage/index.jsx
index 3b2ac0c7fbc..f93a084d069 100644
--- a/frontend/src/metabase/components/ErrorMessage/index.jsx
+++ b/frontend/src/metabase/components/ErrorMessage/index.jsx
@@ -1 +1 @@
-export { default } from "./ErrorMessage";
+export { ErrorMessage } from "./ErrorMessage";
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.jsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.jsx
index 0aed2582077..95c89060863 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.jsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.jsx
@@ -13,12 +13,10 @@ import { Icon } from "metabase/ui";
 import Tooltip from "metabase/core/components/Tooltip";
 import TippyPopover from "metabase/components/Popover/TippyPopover";
 
-import MetabaseSettings from "metabase/lib/settings";
 import { getMetadata } from "metabase/selectors/metadata";
 
 import ParameterTargetList from "metabase/parameters/components/ParameterTargetList";
 import {
-  getNativeDashCardEmptyMappingText,
   isNativeDashCard,
   isVirtualDashCard,
   getVirtualCardType,
@@ -50,11 +48,8 @@ import {
   ChevrondownIcon,
   KeyIcon,
   Warning,
-  NativeCardDefault,
-  NativeCardIcon,
-  NativeCardText,
-  NativeCardLink,
 } from "./DashCardCardParameterMapper.styled";
+import { DisabledNativeCardHelpText } from "./DisabledNativeCardHelpText";
 
 function formatSelected({ name, sectionName }) {
   if (sectionName == null) {
@@ -234,17 +229,7 @@ export function DashCardCardParameterMapper({
           </TextCardDefault>
         )
       ) : isNative && isDisabled ? (
-        <NativeCardDefault>
-          <NativeCardIcon name="info" />
-          <NativeCardText>
-            {getNativeDashCardEmptyMappingText(editingParameter)}
-          </NativeCardText>
-          <NativeCardLink
-            href={MetabaseSettings.docsUrl(
-              "questions/native-editor/sql-parameters",
-            )}
-          >{t`Learn how`}</NativeCardLink>
-        </NativeCardDefault>
+        <DisabledNativeCardHelpText parameter={editingParameter} />
       ) : (
         <>
           {headerContent && (
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.styled.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.styled.tsx
index db01b29a164..3bc54f9a961 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.styled.tsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.styled.tsx
@@ -5,7 +5,6 @@ import { space } from "metabase/styled-components/theme";
 import { alpha, color } from "metabase/lib/colors";
 import { Icon } from "metabase/ui";
 import Button from "metabase/core/components/Button";
-import ExternalLink from "metabase/core/components/ExternalLink";
 
 export const Container = styled.div<{ isSmall: boolean }>`
   margin: ${({ isSmall }) => (isSmall ? 0 : space(1))} 0;
@@ -25,32 +24,6 @@ export const TextCardDefault = styled.div`
   line-height: 1.5rem;
 `;
 
-export const NativeCardDefault = styled.div`
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-`;
-
-export const NativeCardIcon = styled(Icon)`
-  color: ${color("text-medium")};
-  margin-bottom: 0.5rem;
-  width: 1.25rem;
-  height: 1.25rem;
-`;
-
-export const NativeCardText = styled.div`
-  color: ${color("text-dark")};
-  max-width: 15rem;
-  text-align: center;
-  line-height: 1.5rem;
-`;
-
-export const NativeCardLink = styled(ExternalLink)`
-  color: ${color("brand")};
-  font-weight: bold;
-  margin-top: 0.5rem;
-`;
-
 export const CardLabel = styled.div`
   font-size: 0.83em;
   margin-bottom: ${space(1)};
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.unit.spec.jsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.unit.spec.jsx
index b7b3c3a1b6a..04c3191dc53 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.unit.spec.jsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DashCardCardParameterMapper.unit.spec.jsx
@@ -1,5 +1,4 @@
-import { render, screen } from "@testing-library/react";
-import { getIcon } from "__support__/ui";
+import { getIcon, renderWithProviders, screen } from "__support__/ui";
 
 import {
   createMockCard,
@@ -33,7 +32,7 @@ const state = createMockState({
 const metadata = getMetadata(state); // metabase-lib Metadata instance
 
 const setup = options => {
-  render(
+  renderWithProviders(
     <DashCardCardParameterMapper
       card={createMockCard()}
       dashcard={createMockDashboardCard()}
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.styled.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.styled.tsx
new file mode 100644
index 00000000000..9d0ddc81c3c
--- /dev/null
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.styled.tsx
@@ -0,0 +1,31 @@
+import styled from "@emotion/styled";
+
+import { color } from "metabase/lib/colors";
+import { Icon } from "metabase/ui";
+import ExternalLink from "metabase/core/components/ExternalLink";
+
+export const NativeCardDefault = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+`;
+
+export const NativeCardIcon = styled(Icon)`
+  color: ${color("text-medium")};
+  margin-bottom: 0.5rem;
+  width: 1.25rem;
+  height: 1.25rem;
+`;
+
+export const NativeCardText = styled.div`
+  color: ${color("text-dark")};
+  max-width: 15rem;
+  text-align: center;
+  line-height: 1.5rem;
+`;
+
+export const NativeCardLink = styled(ExternalLink)`
+  color: ${color("brand")};
+  font-weight: bold;
+  margin-top: 0.5rem;
+`;
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.tsx
new file mode 100644
index 00000000000..aca82b39723
--- /dev/null
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/DisabledNativeCardHelpText.tsx
@@ -0,0 +1,38 @@
+import { t } from "ttag";
+import MetabaseSettings from "metabase/lib/settings";
+import { getNativeDashCardEmptyMappingText } from "metabase/dashboard/utils";
+import type { Parameter } from "metabase-types/api";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import {
+  NativeCardDefault,
+  NativeCardIcon,
+  NativeCardText,
+  NativeCardLink,
+} from "./DisabledNativeCardHelpText.styled";
+
+interface DisabledNativeCardHelpTextProps {
+  parameter: Parameter;
+}
+
+export function DisabledNativeCardHelpText({
+  parameter,
+}: DisabledNativeCardHelpTextProps) {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
+  return (
+    <NativeCardDefault>
+      <NativeCardIcon name="info" />
+      <NativeCardText>
+        {getNativeDashCardEmptyMappingText(parameter)}
+      </NativeCardText>
+      {showMetabaseLinks && (
+        <NativeCardLink
+          href={MetabaseSettings.docsUrl(
+            "questions/native-editor/sql-parameters",
+          )}
+        >{t`Learn how`}</NativeCardLink>
+      )}
+    </NativeCardDefault>
+  );
+}
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/index.ts b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/index.ts
new file mode 100644
index 00000000000..44e97432838
--- /dev/null
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/index.ts
@@ -0,0 +1 @@
+export { DisabledNativeCardHelpText } from "./DisabledNativeCardHelpText";
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/common.unit.spec.ts b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..496a76b29a9
--- /dev/null
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/common.unit.spec.ts
@@ -0,0 +1,26 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("DashCardParameterMapper > DisabledNativeCardHelpText (OSS)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Add a string variable to this question to connect it to a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn how")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Add a string variable to this question to connect it to a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn how")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/enterprise.unit.spec.ts b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..ad053393d35
--- /dev/null
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/enterprise.unit.spec.ts
@@ -0,0 +1,31 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("DashCardParameterMapper > DisabledNativeCardHelpText (EE without token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Add a string variable to this question to connect it to a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn how")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Add a string variable to this question to connect it to a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn how")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/premium.unit.spec.ts b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..9c3ef03470e
--- /dev/null
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/premium.unit.spec.ts
@@ -0,0 +1,35 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("DashCardParameterMapper > DisabledNativeCardHelpText (EE with token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Add a string variable to this question to connect it to a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn how")).toBeInTheDocument();
+  });
+
+  it("should not show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Add a string variable to this question to connect it to a dashboard filter.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.queryByText("Learn how")).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/setup.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/setup.tsx
new file mode 100644
index 00000000000..6f41c541aab
--- /dev/null
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardParameterMapper/DisabledNativeCardHelpText/tests/setup.tsx
@@ -0,0 +1,38 @@
+import type { TokenFeatures } from "metabase-types/api";
+import {
+  createMockParameter,
+  createMockTokenFeatures,
+} from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { DisabledNativeCardHelpText } from "../DisabledNativeCardHelpText";
+
+export interface SetupOpts {
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(
+    <DisabledNativeCardHelpText parameter={createMockParameter()} />,
+    { storeInitialState: state },
+  );
+};
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
index 4b57cc91257..bdd836481d4 100644
--- a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
+++ b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
@@ -2,12 +2,21 @@ import { t } from "ttag";
 import MetabaseSettings from "metabase/lib/settings";
 import { useUniqueId } from "metabase/hooks/use-unique-id";
 import { useSelector } from "metabase/lib/redux";
-import { getApplicationName } from "metabase/selectors/whitelabel";
+import {
+  getApplicationName,
+  getShowMetabaseLinks,
+} from "metabase/selectors/whitelabel";
 import { CardIcon, CardRoot, CardTitle } from "./HomeHelpCard.styled";
 
-export const HomeHelpCard = (): JSX.Element => {
+export const HomeHelpCard = (): JSX.Element | null => {
   const cardTitleId = useUniqueId();
   const applicationName = useSelector(getApplicationName);
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
+  if (!showMetabaseLinks) {
+    return null;
+  }
+
   return (
     <CardRoot href={MetabaseSettings.learnUrl()} aria-labelledby={cardTitleId}>
       <CardIcon name="reference" />
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
deleted file mode 100644
index 42ee56e4aef..00000000000
--- a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { renderWithProviders, screen } from "__support__/ui";
-import { HomeHelpCard } from "./HomeHelpCard";
-
-const setup = () => {
-  renderWithProviders(<HomeHelpCard />);
-};
-
-describe("HomeHelpCard", () => {
-  it("should render correctly", () => {
-    setup();
-    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
-  });
-});
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/tests/common.unit.spec.tsx b/frontend/src/metabase/home/components/HomeHelpCard/tests/common.unit.spec.tsx
new file mode 100644
index 00000000000..75f25880301
--- /dev/null
+++ b/frontend/src/metabase/home/components/HomeHelpCard/tests/common.unit.spec.tsx
@@ -0,0 +1,19 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("HomeHelpCard (OSS)", () => {
+  it("should show Metabase despite customizing the application name", () => {
+    setup({ applicationName: "My app analytics" });
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/tests/enterprise.unit.spec.tsx b/frontend/src/metabase/home/components/HomeHelpCard/tests/enterprise.unit.spec.tsx
new file mode 100644
index 00000000000..c98e7cb4ae3
--- /dev/null
+++ b/frontend/src/metabase/home/components/HomeHelpCard/tests/enterprise.unit.spec.tsx
@@ -0,0 +1,24 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts = {}) {
+  return baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("HomeHelpCard (EE without token)", () => {
+  it("should show Metabase despite customizing the application name", () => {
+    setup({ applicationName: "My app analytics" });
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/tests/premium.unit.spec.tsx b/frontend/src/metabase/home/components/HomeHelpCard/tests/premium.unit.spec.tsx
new file mode 100644
index 00000000000..d1d4ff9de74
--- /dev/null
+++ b/frontend/src/metabase/home/components/HomeHelpCard/tests/premium.unit.spec.tsx
@@ -0,0 +1,28 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts = {}) {
+  return baseSetup({
+    hasEnterprisePlugins: true,
+    ...opts,
+    tokenFeatures: { whitelabel: true },
+  });
+}
+
+describe("HomeHelpCard (EE with token)", () => {
+  it("should show the customized application name", () => {
+    setup({ applicationName: "My app analytics" });
+    expect(screen.getByText("My app analytics tips")).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+
+  it("should not render help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+    expect(screen.queryByText("Metabase tips")).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/tests/setup.tsx b/frontend/src/metabase/home/components/HomeHelpCard/tests/setup.tsx
new file mode 100644
index 00000000000..6c44b7d61f1
--- /dev/null
+++ b/frontend/src/metabase/home/components/HomeHelpCard/tests/setup.tsx
@@ -0,0 +1,35 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { HomeHelpCard } from "../HomeHelpCard";
+
+export interface SetupOpts {
+  applicationName?: string;
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  applicationName = "Metabase",
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "application-name": applicationName,
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<HomeHelpCard />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx b/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx
index 0df4de70644..354674be3e7 100644
--- a/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx
+++ b/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx
@@ -12,6 +12,7 @@ import { getHasDataAccess, getHasNativeWrite } from "metabase/selectors/data";
 
 import { useSelector } from "metabase/lib/redux";
 import { NoDatabasesEmptyState } from "metabase/reference/databases/NoDatabasesEmptyState";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
 import type Database from "metabase-lib/metadata/Database";
 import {
   OptionsGridItem,
@@ -38,6 +39,8 @@ const NewModelOptions = (props: NewModelOptionsProps) => {
     props.location.query.collectionId as string,
   );
 
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
   if (!hasDataAccess && !hasNativeWrite) {
     return (
       <div className="full-height flex align-center justify-center">
@@ -89,13 +92,15 @@ const NewModelOptions = (props: NewModelOptionsProps) => {
         )}
       </Grid>
 
-      <EducationalButton
-        target="_blank"
-        href={EDUCATIONAL_LINK}
-        className="mt4"
-      >
-        {t`What's a model?`}
-      </EducationalButton>
+      {showMetabaseLinks && (
+        <EducationalButton
+          target="_blank"
+          href={EDUCATIONAL_LINK}
+          className="mt4"
+        >
+          {t`What's a model?`}
+        </EducationalButton>
+      )}
     </OptionsRoot>
   );
 };
diff --git a/frontend/src/metabase/models/containers/NewModelOptions/tests/common.unit.spec.tsx b/frontend/src/metabase/models/containers/NewModelOptions/tests/common.unit.spec.tsx
new file mode 100644
index 00000000000..35ab0754a89
--- /dev/null
+++ b/frontend/src/metabase/models/containers/NewModelOptions/tests/common.unit.spec.tsx
@@ -0,0 +1,39 @@
+import { screen } from "__support__/ui";
+import { createMockDatabase } from "metabase-types/api/mocks";
+
+import { setup } from "./setup";
+
+describe("NewModelOptions (OSS)", () => {
+  it("should render no data access notice when instance have no database access", async () => {
+    setup({ databases: [] });
+
+    expect(
+      await screen.findByText("Metabase is no fun without any data"),
+    ).toBeInTheDocument();
+  });
+
+  describe("has data access", () => {
+    it("should render options for creating a model", async () => {
+      setup({ databases: [createMockDatabase()] });
+
+      expect(
+        await screen.findByText("Use the notebook editor"),
+      ).toBeInTheDocument();
+      expect(await screen.findByText("Use a native query")).toBeInTheDocument();
+    });
+
+    describe("whitelabel feature", () => {
+      it("should render help link when `show-metabase-links: false`", async () => {
+        setup({ databases: [createMockDatabase()], showMetabaseLinks: false });
+
+        expect(await screen.findByText("What's a model?")).toBeInTheDocument();
+      });
+
+      it("should render help link when `show-metabase-links: true`", async () => {
+        setup({ databases: [createMockDatabase()], showMetabaseLinks: true });
+
+        expect(await screen.findByText("What's a model?")).toBeInTheDocument();
+      });
+    });
+  });
+});
diff --git a/frontend/src/metabase/models/containers/NewModelOptions/tests/enterprise.unit.spec.tsx b/frontend/src/metabase/models/containers/NewModelOptions/tests/enterprise.unit.spec.tsx
new file mode 100644
index 00000000000..98c25aad05a
--- /dev/null
+++ b/frontend/src/metabase/models/containers/NewModelOptions/tests/enterprise.unit.spec.tsx
@@ -0,0 +1,44 @@
+import { screen } from "__support__/ui";
+import { createMockDatabase } from "metabase-types/api/mocks";
+
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("NewModelOptions (EE without token)", () => {
+  it("should render no data access notice when instance have no database access", async () => {
+    setup({ databases: [] });
+
+    expect(
+      await screen.findByText("Metabase is no fun without any data"),
+    ).toBeInTheDocument();
+  });
+
+  describe("has data access", () => {
+    it("should render options for creating a model", async () => {
+      setup({ databases: [createMockDatabase()] });
+
+      expect(
+        await screen.findByText("Use the notebook editor"),
+      ).toBeInTheDocument();
+      expect(await screen.findByText("Use a native query")).toBeInTheDocument();
+    });
+
+    describe("whitelabel feature", () => {
+      it("should render help link when `show-metabase-links: false`", async () => {
+        setup({ databases: [createMockDatabase()], showMetabaseLinks: false });
+
+        expect(await screen.findByText("What's a model?")).toBeInTheDocument();
+      });
+
+      it("should render help link when `show-metabase-links: true`", async () => {
+        setup({ databases: [createMockDatabase()], showMetabaseLinks: true });
+
+        expect(await screen.findByText("What's a model?")).toBeInTheDocument();
+      });
+    });
+  });
+});
diff --git a/frontend/src/metabase/models/containers/NewModelOptions/tests/premium.unit.spec.tsx b/frontend/src/metabase/models/containers/NewModelOptions/tests/premium.unit.spec.tsx
new file mode 100644
index 00000000000..86a759a40f4
--- /dev/null
+++ b/frontend/src/metabase/models/containers/NewModelOptions/tests/premium.unit.spec.tsx
@@ -0,0 +1,51 @@
+import { screen } from "__support__/ui";
+import { createMockDatabase } from "metabase-types/api/mocks";
+
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("NewModelOptions (EE with token)", () => {
+  it("should render no data access notice when instance have no database access", async () => {
+    setup({ databases: [] });
+
+    expect(
+      await screen.findByText("Metabase is no fun without any data"),
+    ).toBeInTheDocument();
+  });
+
+  describe("has data access", () => {
+    it("should render options for creating a model", async () => {
+      setup({ databases: [createMockDatabase()] });
+
+      expect(
+        await screen.findByText("Use the notebook editor"),
+      ).toBeInTheDocument();
+      expect(await screen.findByText("Use a native query")).toBeInTheDocument();
+    });
+
+    describe("whitelabel feature", () => {
+      it("should not render help link when `show-metabase-links: false`", async () => {
+        setup({ databases: [createMockDatabase()], showMetabaseLinks: false });
+
+        expect(
+          await screen.findByText("Use the notebook editor"),
+        ).toBeInTheDocument();
+        expect(screen.queryByText("What's a model?")).not.toBeInTheDocument();
+      });
+
+      it("should render help link when `show-metabase-links: true`", async () => {
+        setup({ databases: [createMockDatabase()], showMetabaseLinks: true });
+
+        expect(await screen.findByText("What's a model?")).toBeInTheDocument();
+      });
+    });
+  });
+});
diff --git a/frontend/src/metabase/models/containers/NewModelOptions/tests/setup.tsx b/frontend/src/metabase/models/containers/NewModelOptions/tests/setup.tsx
new file mode 100644
index 00000000000..7163269e002
--- /dev/null
+++ b/frontend/src/metabase/models/containers/NewModelOptions/tests/setup.tsx
@@ -0,0 +1,42 @@
+import { Route } from "react-router";
+import type { Database, TokenFeatures } from "metabase-types/api";
+import { setupDatabasesEndpoints } from "__support__/server-mocks";
+import { renderWithProviders } from "__support__/ui";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { createMockState } from "metabase-types/store/mocks";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { mockSettings } from "__support__/settings";
+
+import NewModelOptions from "../NewModelOptions";
+
+export interface SetupOpts {
+  databases: Database[];
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export function setup({
+  databases = [],
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts) {
+  setupDatabasesEndpoints(databases);
+
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<Route path="*" component={NewModelOptions}></Route>, {
+    withRouter: true,
+    storeInitialState: state,
+  });
+}
diff --git a/frontend/src/metabase/plugins/index.ts b/frontend/src/metabase/plugins/index.ts
index 40c1a2dab42..70337e44511 100644
--- a/frontend/src/metabase/plugins/index.ts
+++ b/frontend/src/metabase/plugins/index.ts
@@ -129,6 +129,7 @@ export const PLUGIN_SELECTORS = {
   getLoadingMessage: (_state: State) => t`Doing science...`,
   getIsWhiteLabeling: (_state: State) => false,
   getApplicationName: (_state: State) => "Metabase",
+  getShowMetabaseLinks: (_state: State) => true,
 };
 
 export const PLUGIN_FORM_WIDGETS: Record<string, ComponentType<any>> = {};
diff --git a/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/ImpossibleToCreateModelModal.tsx b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/ImpossibleToCreateModelModal.tsx
index c89d83ffdc7..66158f81e42 100644
--- a/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/ImpossibleToCreateModelModal.tsx
+++ b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/ImpossibleToCreateModelModal.tsx
@@ -4,19 +4,28 @@ import Button from "metabase/core/components/Button";
 import ExternalLink from "metabase/core/components/ExternalLink";
 import ModalContent from "metabase/components/ModalContent";
 
-import MetabaseSettings from "metabase/lib/settings";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { getDocsUrl } from "metabase/selectors/settings";
 
 type Props = {
   onClose: () => void;
 };
 
 function SQLSnippetsDocLink() {
-  const href = MetabaseSettings.docsUrl("questions/native-editor/sql-snippets");
+  const href = useSelector(state =>
+    getDocsUrl(state, { page: "questions/native-editor/sql-snippets" }),
+  );
   return <ExternalLink href={href}>{t`SQL snippets`}</ExternalLink>;
 }
 
 function ReferencingQuestionsDocLink() {
-  const href = MetabaseSettings.docsUrl("questions/native-editor/sql-snippets");
+  const href = useSelector(state =>
+    getDocsUrl(state, {
+      page: "questions/native-editor/referencing-saved-questions-in-queries",
+      anchor: "referencing-models-and-saved-questions",
+    }),
+  );
   return (
     <ExternalLink
       href={href}
@@ -25,16 +34,22 @@ function ReferencingQuestionsDocLink() {
 }
 
 export function ImpossibleToCreateModelModal({ onClose }: Props) {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
   return (
     <ModalContent
       title={t`Variables in models aren't supported yet`}
       onClose={onClose}
     >
-      <p className="text-paragraph">{jt`To solve this, just remove the variables in this question and try again. (It's okay to use ${(
-        <SQLSnippetsDocLink key="link-1" />
-      )} or ${(
-        <ReferencingQuestionsDocLink key="link-2" />
-      )} in your query.)`}</p>
+      <p className="text-paragraph">
+        {showMetabaseLinks
+          ? jt`To solve this, just remove the variables in this question and try again. (It's okay to use ${(
+              <SQLSnippetsDocLink key="link-1" />
+            )} or ${(
+              <ReferencingQuestionsDocLink key="link-2" />
+            )} in your query.)`
+          : t`To solve this, just remove the variables in this question and try again. (It's okay to use SQL snippets or reference the results of another saved question in your query.)`}
+      </p>
       <div className="flex justify-center py1">
         <Button primary onClick={onClose}>{t`Okay`}</Button>
       </div>
diff --git a/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/common.unit.spec.ts b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..5909563b389
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/common.unit.spec.ts
@@ -0,0 +1,44 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("ImpossibleToCreateModelModal (OSS)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    const sqlSnippetLink = screen.getByRole("link", { name: "SQL snippets" });
+    expect(sqlSnippetLink).toBeInTheDocument();
+    expect(sqlSnippetLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/sql-snippets.html",
+    );
+
+    const referenceLink = screen.getByRole("link", {
+      name: "reference the results of another saved question",
+    });
+    expect(referenceLink).toBeInTheDocument();
+    expect(referenceLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/referencing-saved-questions-in-queries.html#referencing-models-and-saved-questions",
+    );
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    const sqlSnippetLink = screen.getByRole("link", { name: "SQL snippets" });
+    expect(sqlSnippetLink).toBeInTheDocument();
+    expect(sqlSnippetLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/sql-snippets.html",
+    );
+
+    const referenceLink = screen.getByRole("link", {
+      name: "reference the results of another saved question",
+    });
+    expect(referenceLink).toBeInTheDocument();
+    expect(referenceLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/referencing-saved-questions-in-queries.html#referencing-models-and-saved-questions",
+    );
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/enterprise.unit.spec.ts b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..6d3a29ed1db
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/enterprise.unit.spec.ts
@@ -0,0 +1,49 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("ImpossibleToCreateModelModal (EE without token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    const sqlSnippetLink = screen.getByRole("link", { name: "SQL snippets" });
+    expect(sqlSnippetLink).toBeInTheDocument();
+    expect(sqlSnippetLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/sql-snippets.html",
+    );
+
+    const referenceLink = screen.getByRole("link", {
+      name: "reference the results of another saved question",
+    });
+    expect(referenceLink).toBeInTheDocument();
+    expect(referenceLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/referencing-saved-questions-in-queries.html#referencing-models-and-saved-questions",
+    );
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    const sqlSnippetLink = screen.getByRole("link", { name: "SQL snippets" });
+    expect(sqlSnippetLink).toBeInTheDocument();
+    expect(sqlSnippetLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/sql-snippets.html",
+    );
+
+    const referenceLink = screen.getByRole("link", {
+      name: "reference the results of another saved question",
+    });
+    expect(referenceLink).toBeInTheDocument();
+    expect(referenceLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/referencing-saved-questions-in-queries.html#referencing-models-and-saved-questions",
+    );
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/premium.unit.spec.ts b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..39c1ca13aae
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/premium.unit.spec.ts
@@ -0,0 +1,51 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("ImpossibleToCreateModelModal (EE with token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    const sqlSnippetLink = screen.getByRole("link", { name: "SQL snippets" });
+    expect(sqlSnippetLink).toBeInTheDocument();
+    expect(sqlSnippetLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/sql-snippets.html",
+    );
+
+    const referenceLink = screen.getByRole("link", {
+      name: "reference the results of another saved question",
+    });
+    expect(referenceLink).toBeInTheDocument();
+    expect(referenceLink).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/native-editor/referencing-saved-questions-in-queries.html#referencing-models-and-saved-questions",
+    );
+  });
+
+  it("should not show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "To solve this, just remove the variables in this question and try again. (It's okay to use SQL snippets or reference the results of another saved question in your query.)",
+      ),
+    ).toBeInTheDocument();
+    expect(
+      screen.queryByRole("link", { name: "SQL snippets" }),
+    ).not.toBeInTheDocument();
+    expect(
+      screen.queryByRole("link", {
+        name: "reference the results of another saved question",
+      }),
+    ).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/setup.tsx b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/setup.tsx
new file mode 100644
index 00000000000..0973727205e
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/ImpossibleToCreateModelModal/tests/setup.tsx
@@ -0,0 +1,34 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { ImpossibleToCreateModelModal } from "../ImpossibleToCreateModelModal";
+
+export interface SetupOpts {
+  showMetabaseLinks: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<ImpossibleToCreateModelModal onClose={jest.fn()} />, {
+    storeInitialState: state,
+  });
+};
diff --git a/frontend/src/metabase/query_builder/components/QueryModals.tsx b/frontend/src/metabase/query_builder/components/QueryModals.tsx
index 796d1785b8e..8242db135c6 100644
--- a/frontend/src/metabase/query_builder/components/QueryModals.tsx
+++ b/frontend/src/metabase/query_builder/components/QueryModals.tsx
@@ -27,7 +27,7 @@ import { FilterModal } from "metabase/querying";
 import NewEventModal from "metabase/timelines/questions/containers/NewEventModal";
 import EditEventModal from "metabase/timelines/questions/containers/EditEventModal";
 import MoveEventModal from "metabase/timelines/questions/containers/MoveEventModal";
-import PreviewQueryModal from "metabase/query_builder/components/view/PreviewQueryModal";
+import { PreviewQueryModal } from "metabase/query_builder/components/view/PreviewQueryModal";
 import ConvertQueryModal from "metabase/query_builder/components/view/ConvertQueryModal";
 import QuestionMoveToast from "metabase/questions/components/QuestionMoveToast";
 import type { Alert, Card, Collection, User } from "metabase-types/api";
diff --git a/frontend/src/metabase/query_builder/components/QueryVisualization.jsx b/frontend/src/metabase/query_builder/components/QueryVisualization.jsx
index de075050e92..437b5b4b986 100644
--- a/frontend/src/metabase/query_builder/components/QueryVisualization.jsx
+++ b/frontend/src/metabase/query_builder/components/QueryVisualization.jsx
@@ -6,7 +6,7 @@ import cx from "classnames";
 import LoadingSpinner from "metabase/components/LoadingSpinner";
 
 import { HARD_ROW_LIMIT } from "metabase-lib/queries/utils";
-import VisualizationError from "./VisualizationError";
+import { VisualizationError } from "./VisualizationError";
 import VisualizationResult from "./VisualizationResult";
 import Warnings from "./Warnings";
 import RunButtonWithTooltip from "./RunButtonWithTooltip";
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError.jsx b/frontend/src/metabase/query_builder/components/VisualizationError.jsx
deleted file mode 100644
index f7f2740c925..00000000000
--- a/frontend/src/metabase/query_builder/components/VisualizationError.jsx
+++ /dev/null
@@ -1,181 +0,0 @@
-/* eslint "react/prop-types": "warn" */
-
-import { Component } from "react";
-import PropTypes from "prop-types";
-import { t } from "ttag";
-import { getIn } from "icepick";
-import cx from "classnames";
-
-import MetabaseSettings from "metabase/lib/settings";
-import { getEngineNativeType } from "metabase/lib/engine";
-import ErrorMessage from "metabase/components/ErrorMessage";
-import ErrorDetails from "metabase/components/ErrorDetails/ErrorDetails";
-import { VISUALIZATION_SLOW_TIMEOUT } from "../constants";
-import {
-  QueryError,
-  QueryErrorHeader,
-  QueryErrorIcon,
-  QueryErrorTitle,
-  QueryErrorLink,
-  QueryErrorMessage,
-  QueryErrorContent,
-} from "./VisualizationError.styled";
-
-const EmailAdmin = () => {
-  const adminEmail = MetabaseSettings.adminEmail();
-  return (
-    adminEmail && (
-      <span className="QueryError-adminEmail">
-        <a className="no-decoration" href={`mailto:${adminEmail}`}>
-          {adminEmail}
-        </a>
-      </span>
-    )
-  );
-};
-
-export function adjustPositions(error, origSql) {
-  /* Positions in error messages are borked coming in for Postgres errors.
-   * Previously, you would see "blahblahblah bombed out, Position: 119" in a 10-character invalid query.
-   * This is because MB shoves in 'remarks' into the original query and we get the exception from the query with remarks.
-   * This function adjusts the value of the positions in the exception message to account for this.
-   * This is done in mildly scary kludge here in frontend after everything,
-   * because the alternative of doing it in backend
-   * is an absolutely terrifying kludge involving messing with exceptions.
-   */
-  let adjustmentLength = 0;
-
-  // redshift remarks use c-style multiline comments...
-  const multiLineBeginPos = origSql.search("/\\*");
-  const multiLineEndPos = origSql.search("\\*/");
-  // if multiLineBeginPos is 0 then we know it's a redshift remark
-  if (multiLineBeginPos === 0 && multiLineEndPos !== -1) {
-    adjustmentLength += multiLineEndPos + 2; // 2 for */ in itself
-  }
-
-  const chompedSql = origSql.substr(adjustmentLength);
-  // there also seem to be cases where remarks don't get in...
-  const commentPos = chompedSql.search("--");
-  const newLinePos = chompedSql.search("\n");
-  // 5 is a heuristic: this indicates that this is almost certainly an initial remark comment
-  if (commentPos !== -1 && commentPos < 5) {
-    // There will be a \n after the redshift comment,
-    // which is why there needs to be a 2 added
-    adjustmentLength += newLinePos + 2;
-  }
-
-  return error.replace(/Position: (\d+)/, function (_, p1) {
-    return "Position: " + (parseInt(p1) - adjustmentLength);
-  });
-}
-
-export function stripRemarks(error) {
-  /* SQL snippets in error messages are borked coming in for errors in many DBs.
-   * You're expecting something with just your sql in the message,
-   * but the whole error contains these remarks that MB added in. Confusing!
-   */
-  return error.replace(
-    /-- Metabase:: userID: \d+ queryType: native queryHash: \w+\n/,
-    "",
-  );
-}
-
-class VisualizationError extends Component {
-  constructor(props) {
-    super(props);
-    this.state = {
-      showError: false,
-    };
-  }
-  static propTypes = {
-    via: PropTypes.object.isRequired,
-    question: PropTypes.object.isRequired,
-    duration: PropTypes.number.isRequired,
-    error: PropTypes.any.isRequired,
-    className: PropTypes.string,
-  };
-
-  render() {
-    const { via, question, duration, error, className } = this.props;
-
-    if (error && typeof error.status === "number") {
-      // Assume if the request took more than 15 seconds it was due to a timeout
-      // Some platforms like Heroku return a 503 for numerous types of errors so we can't use the status code to distinguish between timeouts and other failures.
-      if (duration > VISUALIZATION_SLOW_TIMEOUT) {
-        return (
-          <ErrorMessage
-            className={className}
-            type="timeout"
-            title={t`Your question took too long`}
-            message={t`We didn't get an answer back from your database in time, so we had to stop. You can try again in a minute, or if the problem persists, you can email an admin to let them know.`}
-            action={<EmailAdmin />}
-          />
-        );
-      } else {
-        return (
-          <ErrorMessage
-            className={className}
-            type="serverError"
-            title={t`We're experiencing server issues`}
-            message={t`Try refreshing the page after waiting a minute or two. If the problem persists we'd recommend you contact an admin.`}
-            action={<EmailAdmin />}
-          />
-        );
-      }
-    } else if (error instanceof Error) {
-      return (
-        <div className={cx(className, "QueryError2 flex justify-center")}>
-          <div className="QueryError-image QueryError-image--queryError mr4" />
-          <div className="QueryError2-details">
-            <h1 className="text-bold">{t`There was a problem with this visualization`}</h1>
-            <ErrorDetails className="pt2" details={error} />
-          </div>
-        </div>
-      );
-    } else if (question?.isNative()) {
-      // always show errors for native queries
-      let processedError = error;
-      const origSql = getIn(via, [(via || "").length - 1, "ex-data", "sql"]);
-      if (typeof error === "string" && typeof origSql === "string") {
-        processedError = adjustPositions(error, origSql);
-      }
-      if (typeof error === "string") {
-        processedError = stripRemarks(processedError);
-      }
-      const database = question.database();
-      const isSql = database && getEngineNativeType(database.engine) === "sql";
-
-      return (
-        <QueryError className={className}>
-          <QueryErrorContent>
-            <QueryErrorHeader>
-              <QueryErrorIcon name="warning" />
-              <QueryErrorTitle>{t`An error occurred in your query`}</QueryErrorTitle>
-            </QueryErrorHeader>
-            <QueryErrorMessage>{processedError}</QueryErrorMessage>
-            {isSql && (
-              <QueryErrorLink
-                href={MetabaseSettings.learnUrl("debugging-sql/sql-syntax")}
-              >
-                {t`Learn how to debug SQL errors`}
-              </QueryErrorLink>
-            )}
-          </QueryErrorContent>
-        </QueryError>
-      );
-    } else {
-      return (
-        <div className={cx(className, "QueryError2 flex justify-center")}>
-          <div className="QueryError-image QueryError-image--queryError mr4" />
-          <div className="QueryError2-details">
-            <h1 className="text-bold">{t`There was a problem with your question`}</h1>
-            <p className="QueryError-messageText">{t`Most of the time this is caused by an invalid selection or bad input value. Double check your inputs and retry your query.`}</p>
-            <ErrorDetails className="pt2" details={error} />
-          </div>
-        </div>
-      );
-    }
-  }
-}
-
-export default VisualizationError;
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError.styled.tsx b/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.styled.tsx
similarity index 100%
rename from frontend/src/metabase/query_builder/components/VisualizationError.styled.tsx
rename to frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.styled.tsx
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx b/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx
new file mode 100644
index 00000000000..e9a0fb8e4c2
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx
@@ -0,0 +1,129 @@
+import { t } from "ttag";
+import { getIn } from "icepick";
+import cx from "classnames";
+
+import MetabaseSettings from "metabase/lib/settings";
+import { getEngineNativeType } from "metabase/lib/engine";
+import { ErrorMessage } from "metabase/components/ErrorMessage";
+import ErrorDetails from "metabase/components/ErrorDetails/ErrorDetails";
+import { isNotNull } from "metabase/lib/types";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { useSelector } from "metabase/lib/redux";
+import type Question from "metabase-lib/Question";
+import { VISUALIZATION_SLOW_TIMEOUT } from "../../constants";
+import {
+  QueryError,
+  QueryErrorHeader,
+  QueryErrorIcon,
+  QueryErrorTitle,
+  QueryErrorLink,
+  QueryErrorMessage,
+  QueryErrorContent,
+} from "./VisualizationError.styled";
+import { adjustPositions, stripRemarks } from "./utils";
+
+function EmailAdmin(): JSX.Element | null {
+  const hasAdminEmail = isNotNull(MetabaseSettings.adminEmail());
+  return hasAdminEmail ? (
+    <span className="QueryError-adminEmail">
+      <a className="no-decoration" href={`mailto:${hasAdminEmail}`}>
+        {hasAdminEmail}
+      </a>
+    </span>
+  ) : null;
+}
+
+interface VisualizationErrorProps {
+  via: Record<string, any>[];
+  question: Question;
+  duration: number;
+  error: any;
+  className?: string;
+}
+
+export function VisualizationError({
+  via,
+  question,
+  duration,
+  error,
+  className,
+}: VisualizationErrorProps) {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+  if (error && typeof error.status === "number") {
+    // Assume if the request took more than 15 seconds it was due to a timeout
+    // Some platforms like Heroku return a 503 for numerous types of errors so we can't use the status code to distinguish between timeouts and other failures.
+    if (duration > VISUALIZATION_SLOW_TIMEOUT) {
+      return (
+        <ErrorMessage
+          className={className}
+          type="timeout"
+          title={t`Your question took too long`}
+          message={t`We didn't get an answer back from your database in time, so we had to stop. You can try again in a minute, or if the problem persists, you can email an admin to let them know.`}
+          action={<EmailAdmin />}
+        />
+      );
+    } else {
+      return (
+        <ErrorMessage
+          className={className}
+          type="serverError"
+          title={t`We're experiencing server issues`}
+          message={t`Try refreshing the page after waiting a minute or two. If the problem persists we'd recommend you contact an admin.`}
+          action={<EmailAdmin />}
+        />
+      );
+    }
+  } else if (error instanceof Error) {
+    return (
+      <div className={cx(className, "QueryError2 flex justify-center")}>
+        <div className="QueryError-image QueryError-image--queryError mr4" />
+        <div className="QueryError2-details">
+          <h1 className="text-bold">{t`There was a problem with this visualization`}</h1>
+          <ErrorDetails className="pt2" details={error} />
+        </div>
+      </div>
+    );
+  } else if (question?.isNative()) {
+    // always show errors for native queries
+    let processedError = error;
+    const origSql = getIn(via, [(via || "").length - 1, "ex-data", "sql"]);
+    if (typeof error === "string" && typeof origSql === "string") {
+      processedError = adjustPositions(error, origSql);
+    }
+    if (typeof error === "string") {
+      processedError = stripRemarks(processedError);
+    }
+    const database = question.database();
+    const isSql = database && getEngineNativeType(database.engine) === "sql";
+
+    return (
+      <QueryError className={className}>
+        <QueryErrorContent>
+          <QueryErrorHeader>
+            <QueryErrorIcon name="warning" />
+            <QueryErrorTitle>{t`An error occurred in your query`}</QueryErrorTitle>
+          </QueryErrorHeader>
+          <QueryErrorMessage>{processedError}</QueryErrorMessage>
+          {isSql && showMetabaseLinks && (
+            <QueryErrorLink
+              href={MetabaseSettings.learnUrl("debugging-sql/sql-syntax")}
+            >
+              {t`Learn how to debug SQL errors`}
+            </QueryErrorLink>
+          )}
+        </QueryErrorContent>
+      </QueryError>
+    );
+  } else {
+    return (
+      <div className={cx(className, "QueryError2 flex justify-center")}>
+        <div className="QueryError-image QueryError-image--queryError mr4" />
+        <div className="QueryError2-details">
+          <h1 className="text-bold">{t`There was a problem with your question`}</h1>
+          <p className="QueryError-messageText">{t`Most of the time this is caused by an invalid selection or bad input value. Double check your inputs and retry your query.`}</p>
+          <ErrorDetails className="pt2" details={error} />
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/index.ts b/frontend/src/metabase/query_builder/components/VisualizationError/index.ts
new file mode 100644
index 00000000000..078f966ebfe
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/index.ts
@@ -0,0 +1 @@
+export { VisualizationError } from "./VisualizationError";
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/tests/common.unit.spec.ts b/frontend/src/metabase/query_builder/components/VisualizationError/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..0f235ea0fd4
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/tests/common.unit.spec.ts
@@ -0,0 +1,68 @@
+import { createMockCard, createMockDatabase } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("VisualizationError (OSS)", () => {
+  describe("SQL databases", () => {
+    const database = createMockDatabase({
+      engine: "postgres",
+    });
+
+    it("should show a SQL help link when `show-metabase-links: true`", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "SELECT * FROM ORDERS",
+          },
+        },
+      });
+      setup({ database, card, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText("Learn how to debug SQL errors"),
+      ).toBeInTheDocument();
+    });
+
+    it("should show a SQL help link when `show-metabase-links: false`", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "SELECT * FROM ORDERS",
+          },
+        },
+      });
+      setup({ database, card, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText("Learn how to debug SQL errors"),
+      ).toBeInTheDocument();
+    });
+  });
+
+  describe("NoSQL databases", () => {
+    const database = createMockDatabase({
+      engine: "mongo",
+    });
+
+    it("should not show a SQL help link", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "[]",
+          },
+        },
+      });
+      setup({ database, card });
+
+      expect(
+        screen.queryByText("Learn how to debug SQL errors"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/tests/enterprise.unit.spec.ts b/frontend/src/metabase/query_builder/components/VisualizationError/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..e9ced54352a
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/tests/enterprise.unit.spec.ts
@@ -0,0 +1,73 @@
+import { createMockCard, createMockDatabase } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("VisualizationError (EE without token)", () => {
+  describe("SQL databases", () => {
+    const database = createMockDatabase({
+      engine: "postgres",
+    });
+
+    it("should show a SQL help link when `show-metabase-links: true`", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "SELECT * FROM ORDERS",
+          },
+        },
+      });
+      setup({ database, card, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText("Learn how to debug SQL errors"),
+      ).toBeInTheDocument();
+    });
+
+    it("should show a SQL help link when `show-metabase-links: false`", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "SELECT * FROM ORDERS",
+          },
+        },
+      });
+      setup({ database, card, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText("Learn how to debug SQL errors"),
+      ).toBeInTheDocument();
+    });
+  });
+
+  describe("NoSQL databases", () => {
+    const database = createMockDatabase({
+      engine: "mongo",
+    });
+
+    it("should not show a SQL help link", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "[]",
+          },
+        },
+      });
+      setup({ database, card });
+
+      expect(
+        screen.queryByText("Learn how to debug SQL errors"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/tests/premium.unit.spec.ts b/frontend/src/metabase/query_builder/components/VisualizationError/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..78a593f0972
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/tests/premium.unit.spec.ts
@@ -0,0 +1,77 @@
+import { createMockCard, createMockDatabase } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("VisualizationError (EE with token)", () => {
+  describe("SQL databases", () => {
+    const database = createMockDatabase({
+      engine: "postgres",
+    });
+
+    it("should show a SQL help link when `show-metabase-links: true`", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "SELECT * FROM ORDERS",
+          },
+        },
+      });
+      setup({ database, card, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText("Learn how to debug SQL errors"),
+      ).toBeInTheDocument();
+    });
+
+    it("should not show a SQL help link when `show-metabase-links: false`", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "SELECT * FROM ORDERS",
+          },
+        },
+      });
+      setup({ database, card, showMetabaseLinks: false });
+
+      expect(
+        screen.queryByText("Learn how to debug SQL errors"),
+      ).not.toBeInTheDocument();
+    });
+  });
+
+  describe("NoSQL databases", () => {
+    const database = createMockDatabase({
+      engine: "mongo",
+    });
+
+    it("should not show a SQL help link", () => {
+      const card = createMockCard({
+        dataset_query: {
+          database: database.id,
+          type: "native",
+          native: {
+            query: "[]",
+          },
+        },
+      });
+      setup({ database, card });
+
+      expect(
+        screen.queryByText("Learn how to debug SQL errors"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/tests/setup.tsx b/frontend/src/metabase/query_builder/components/VisualizationError/tests/setup.tsx
new file mode 100644
index 00000000000..e965764308e
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/tests/setup.tsx
@@ -0,0 +1,62 @@
+import type { Card, Database, TokenFeatures } from "metabase-types/api";
+import {
+  createMockCard,
+  createMockDatabase,
+  createMockTokenFeatures,
+} from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { createMockEntitiesState } from "__support__/store";
+import { renderWithProviders } from "__support__/ui";
+import { mockSettings } from "__support__/settings";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { checkNotNull } from "metabase/lib/types";
+import { createMockMetadata } from "__support__/metadata";
+
+import { VisualizationError } from "../VisualizationError";
+
+export interface SetupOpts {
+  database?: Database;
+  card?: Card;
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  database = createMockDatabase(),
+  card = createMockCard(),
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts) => {
+  const state = createMockState({
+    entities: createMockEntitiesState({
+      databases: [database],
+      questions: [card],
+    }),
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  const metadata = createMockMetadata({
+    questions: [card],
+    databases: [database],
+  });
+  const question = checkNotNull(metadata.question(card.id));
+
+  renderWithProviders(
+    <VisualizationError
+      question={question}
+      duration={0}
+      error="An error occurred"
+      via={[]}
+    />,
+    { storeInitialState: state },
+  );
+};
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError.unit.spec.tsx b/frontend/src/metabase/query_builder/components/VisualizationError/tests/utils.unit.spec.ts
similarity index 51%
rename from frontend/src/metabase/query_builder/components/VisualizationError.unit.spec.tsx
rename to frontend/src/metabase/query_builder/components/VisualizationError/tests/utils.unit.spec.ts
index 5007f06da3c..ef5b4ce0d65 100644
--- a/frontend/src/metabase/query_builder/components/VisualizationError.unit.spec.tsx
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/tests/utils.unit.spec.ts
@@ -1,84 +1,4 @@
-import { getMetadata } from "metabase/selectors/metadata";
-import type { Card, Database } from "metabase-types/api";
-import { createMockCard, createMockDatabase } from "metabase-types/api/mocks";
-import { createMockState } from "metabase-types/store/mocks";
-import { createMockEntitiesState } from "__support__/store";
-import { render, screen } from "__support__/ui";
-import VisualizationError, {
-  adjustPositions,
-  stripRemarks,
-} from "./VisualizationError";
-
-interface SetupOpts {
-  database?: Database;
-  card?: Card;
-}
-
-const setup = ({
-  database = createMockDatabase(),
-  card = createMockCard(),
-}: SetupOpts) => {
-  const state = createMockState({
-    entities: createMockEntitiesState({
-      databases: [database],
-      questions: [card],
-    }),
-  });
-
-  const metadata = getMetadata(state);
-  const question = metadata.question(card.id);
-
-  render(
-    <VisualizationError
-      question={question}
-      duration={0}
-      error="An error occurred"
-      via={{}}
-    />,
-  );
-};
-
-describe("VisualizationError", () => {
-  it("should show a help link for sql databases", () => {
-    const database = createMockDatabase({
-      engine: "postgres",
-    });
-    const card = createMockCard({
-      dataset_query: {
-        database: database.id,
-        type: "native",
-        native: {
-          query: "SELECT * FROM ORDERS",
-        },
-      },
-    });
-    setup({ database, card });
-
-    expect(
-      screen.getByText("Learn how to debug SQL errors"),
-    ).toBeInTheDocument();
-  });
-
-  it("should not show a help link for a nosql databases", () => {
-    const database = createMockDatabase({
-      engine: "mongo",
-    });
-    const card = createMockCard({
-      dataset_query: {
-        database: database.id,
-        type: "native",
-        native: {
-          query: "[]",
-        },
-      },
-    });
-    setup({ database, card });
-
-    expect(
-      screen.queryByText("Learn how to debug SQL errors"),
-    ).not.toBeInTheDocument();
-  });
-});
+import { adjustPositions, stripRemarks } from "../utils";
 
 describe("adjustPositions", () => {
   const remarkedQuery =
@@ -117,6 +37,7 @@ describe("adjustPositions", () => {
     );
   });
 });
+
 describe("stripRemarks", () => {
   const errorH2Unstripped = `
   Syntax error in SQL statement " FWEFWEF[*] "; expected "FROM, {"; SQL statement: -- Metabase:: userID: 1 queryType: native queryHash: 9863b8284f269ce8763ad59b04cec26407a1dd74eebeb16cffdf1ef3e23b325a\nfwefwef [42001-197]`;
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/utils.jsx b/frontend/src/metabase/query_builder/components/VisualizationError/utils.jsx
new file mode 100644
index 00000000000..5d763b2ada6
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/utils.jsx
@@ -0,0 +1,45 @@
+export function adjustPositions(error, origSql) {
+  /* Positions in error messages are borked coming in for Postgres errors.
+   * Previously, you would see "blahblahblah bombed out, Position: 119" in a 10-character invalid query.
+   * This is because MB shoves in 'remarks' into the original query and we get the exception from the query with remarks.
+   * This function adjusts the value of the positions in the exception message to account for this.
+   * This is done in mildly scary kludge here in frontend after everything,
+   * because the alternative of doing it in backend
+   * is an absolutely terrifying kludge involving messing with exceptions.
+   */
+  let adjustmentLength = 0;
+
+  // redshift remarks use c-style multiline comments...
+  const multiLineBeginPos = origSql.search("/\\*");
+  const multiLineEndPos = origSql.search("\\*/");
+  // if multiLineBeginPos is 0 then we know it's a redshift remark
+  if (multiLineBeginPos === 0 && multiLineEndPos !== -1) {
+    adjustmentLength += multiLineEndPos + 2; // 2 for */ in itself
+  }
+
+  const chompedSql = origSql.substr(adjustmentLength);
+  // there also seem to be cases where remarks don't get in...
+  const commentPos = chompedSql.search("--");
+  const newLinePos = chompedSql.search("\n");
+  // 5 is a heuristic: this indicates that this is almost certainly an initial remark comment
+  if (commentPos !== -1 && commentPos < 5) {
+    // There will be a \n after the redshift comment,
+    // which is why there needs to be a 2 added
+    adjustmentLength += newLinePos + 2;
+  }
+
+  return error.replace(/Position: (\d+)/, function (_, p1) {
+    return "Position: " + (parseInt(p1) - adjustmentLength);
+  });
+}
+
+export function stripRemarks(error) {
+  /* SQL snippets in error messages are borked coming in for errors in many DBs.
+   * You're expecting something with just your sql in the message,
+   * but the whole error contains these remarks that MB added in. Confusing!
+   */
+  return error.replace(
+    /-- Metabase:: userID: \d+ queryType: native queryHash: \w+\n/,
+    "",
+  );
+}
diff --git a/frontend/src/metabase/query_builder/components/VisualizationResult.jsx b/frontend/src/metabase/query_builder/components/VisualizationResult.jsx
index bf4d497857e..0c12c581f3e 100644
--- a/frontend/src/metabase/query_builder/components/VisualizationResult.jsx
+++ b/frontend/src/metabase/query_builder/components/VisualizationResult.jsx
@@ -4,7 +4,7 @@ import { t, jt } from "ttag";
 import cx from "classnames";
 import _ from "underscore";
 
-import ErrorMessage from "metabase/components/ErrorMessage";
+import { ErrorMessage } from "metabase/components/ErrorMessage";
 import Visualization from "metabase/visualizations/components/Visualization";
 import { CreateAlertModalContent } from "metabase/query_builder/components/AlertModals";
 import Modal from "metabase/components/Modal";
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.stories.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.stories.tsx
similarity index 93%
rename from frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.stories.tsx
rename to frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.stories.tsx
index e5e117be379..1ccd5d960de 100644
--- a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.stories.tsx
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.stories.tsx
@@ -5,7 +5,7 @@ import { createMockDatabase } from "metabase-types/api/mocks";
 import { createMockMetadata } from "__support__/metadata";
 import { getHelpText } from "metabase-lib/expressions/helper-text-strings";
 import type { ExpressionEditorHelpTextProps } from "./ExpressionEditorHelpText";
-import ExpressionEditorHelpText from "./ExpressionEditorHelpText";
+import { ExpressionEditorHelpText } from "./ExpressionEditorHelpText";
 
 export default {
   title: "Query Builder/ExpressionEditorHelpText",
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.styled.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.styled.tsx
similarity index 100%
rename from frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.styled.tsx
rename to frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.styled.tsx
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.tsx
similarity index 82%
rename from frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.tsx
rename to frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.tsx
index 31a9e09a306..66bc9c135de 100644
--- a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.tsx
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/ExpressionEditorHelpText.tsx
@@ -2,6 +2,8 @@ import * as React from "react";
 import { t } from "ttag";
 import TippyPopover from "metabase/components/Popover/TippyPopover";
 import MetabaseSettings from "metabase/lib/settings";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
 import type { HelpText } from "metabase-lib/expressions/types";
 import { getHelpDocsUrl } from "metabase-lib/expressions/helper-text-strings";
 import {
@@ -23,11 +25,12 @@ export interface ExpressionEditorHelpTextProps {
   target: React.RefObject<HTMLElement>;
 }
 
-const ExpressionEditorHelpText = ({
+export const ExpressionEditorHelpText = ({
   helpText,
   width,
   target,
 }: ExpressionEditorHelpTextProps) => {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
   if (!helpText) {
     return null;
   }
@@ -95,24 +98,23 @@ const ExpressionEditorHelpText = ({
             <ExampleCode data-ignore-outside-clicks>
               {helpText.example}
             </ExampleCode>
-            <DocumentationLink
-              href={MetabaseSettings.docsUrl(getHelpDocsUrl(helpText))}
-              target="_blank"
-              data-ignore-outside-clicks
-            >
-              <LearnMoreIcon
-                name="reference"
-                size={12}
+            {showMetabaseLinks && (
+              <DocumentationLink
+                href={MetabaseSettings.docsUrl(getHelpDocsUrl(helpText))}
+                target="_blank"
                 data-ignore-outside-clicks
-              />
-              {t`Learn more`}
-            </DocumentationLink>
+              >
+                <LearnMoreIcon
+                  name="reference"
+                  size={12}
+                  data-ignore-outside-clicks
+                />
+                {t`Learn more`}
+              </DocumentationLink>
+            )}
           </Container>
         </>
       }
     />
   );
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default ExpressionEditorHelpText;
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/index.ts b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/index.ts
new file mode 100644
index 00000000000..6ad52d8f448
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/index.ts
@@ -0,0 +1 @@
+export { ExpressionEditorHelpText } from "./ExpressionEditorHelpText";
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.unit.spec.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/common.unit.spec.tsx
similarity index 70%
rename from frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.unit.spec.tsx
rename to frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/common.unit.spec.tsx
index 8cb8556b5b4..ec5127829fb 100644
--- a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.unit.spec.tsx
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/common.unit.spec.tsx
@@ -1,4 +1,4 @@
-import { render, screen, within } from "@testing-library/react";
+import { screen, within } from "@testing-library/react";
 import { checkNotNull } from "metabase/lib/types";
 import { createMockMetadata } from "__support__/metadata";
 import { getBrokenUpTextMatcher } from "__support__/ui";
@@ -7,10 +7,9 @@ import {
   SAMPLE_DB_ID,
 } from "metabase-types/api/mocks/presets";
 import { getHelpText } from "metabase-lib/expressions/helper-text-strings";
-import type { ExpressionEditorHelpTextProps } from "./ExpressionEditorHelpText";
-import ExpressionEditorHelpText from "./ExpressionEditorHelpText";
+import { setup } from "./setup";
 
-describe("ExpressionEditorHelpText", () => {
+describe("ExpressionEditorHelpText (OSS)", () => {
   const metadata = createMockMetadata({ databases: [createSampleDatabase()] });
   const database = checkNotNull(metadata.database(SAMPLE_DB_ID));
 
@@ -59,9 +58,8 @@ describe("ExpressionEditorHelpText", () => {
   });
 
   it("should render function arguments", async () => {
-    const {
-      props: { helpText },
-    } = await setup({ helpText: getHelpText("concat", database, "UTC") });
+    const helpText = getHelpText("concat", database, "UTC");
+    await setup({ helpText });
 
     const argumentsBlock = screen.getByTestId(
       "expression-helper-popover-arguments",
@@ -72,24 +70,25 @@ describe("ExpressionEditorHelpText", () => {
       expect(within(argumentsBlock).getByText(description)).toBeInTheDocument();
     });
   });
-});
-
-async function setup(additionalProps?: Partial<ExpressionEditorHelpTextProps>) {
-  const target = { current: null };
 
-  const props: ExpressionEditorHelpTextProps = {
-    helpText: additionalProps?.helpText,
-    width: 397,
-    target,
-    ...additionalProps,
-  };
+  describe("Metabase links", () => {
+    const helpText = getHelpText("concat", database, "UTC");
+    it("should show a help link when `show-metabase-links: true`", async () => {
+      await setup({ helpText, showMetabaseLinks: true });
 
-  render(<ExpressionEditorHelpText {...props} />);
+      expect(
+        screen.getByRole("img", { name: "reference icon" }),
+      ).toBeInTheDocument();
+      expect(screen.getByText("Learn more")).toBeInTheDocument();
+    });
 
-  // have to wait for TippyPopover to render content
-  expect(
-    await screen.findByTestId("expression-helper-popover"),
-  ).toBeInTheDocument();
+    it("should show a help link when `show-metabase-links: false`", async () => {
+      await setup({ helpText, showMetabaseLinks: false });
 
-  return { props };
-}
+      expect(
+        screen.getByRole("img", { name: "reference icon" }),
+      ).toBeInTheDocument();
+      expect(screen.getByText("Learn more")).toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/enterprise.unit.spec.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/enterprise.unit.spec.tsx
new file mode 100644
index 00000000000..788437b233f
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/enterprise.unit.spec.tsx
@@ -0,0 +1,40 @@
+import { screen } from "@testing-library/react";
+import { checkNotNull } from "metabase/lib/types";
+import { createMockMetadata } from "__support__/metadata";
+import {
+  createSampleDatabase,
+  SAMPLE_DB_ID,
+} from "metabase-types/api/mocks/presets";
+import { getHelpText } from "metabase-lib/expressions/helper-text-strings";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+async function setup(opts: SetupOpts) {
+  await baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("ExpressionEditorHelpText (EE without token)", () => {
+  const metadata = createMockMetadata({ databases: [createSampleDatabase()] });
+  const database = checkNotNull(metadata.database(SAMPLE_DB_ID));
+
+  describe("Metabase links", () => {
+    const helpText = getHelpText("concat", database, "UTC");
+    it("should show a help link when `show-metabase-links: true`", async () => {
+      await setup({ helpText, showMetabaseLinks: true });
+
+      expect(
+        screen.getByRole("img", { name: "reference icon" }),
+      ).toBeInTheDocument();
+      expect(screen.getByText("Learn more")).toBeInTheDocument();
+    });
+
+    it("should show a help link when `show-metabase-links: false`", async () => {
+      await setup({ helpText, showMetabaseLinks: false });
+
+      expect(
+        screen.getByRole("img", { name: "reference icon" }),
+      ).toBeInTheDocument();
+      expect(screen.getByText("Learn more")).toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/premium.unit.spec.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/premium.unit.spec.tsx
new file mode 100644
index 00000000000..baf36974a47
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/premium.unit.spec.tsx
@@ -0,0 +1,44 @@
+import { screen } from "@testing-library/react";
+import { checkNotNull } from "metabase/lib/types";
+import { createMockMetadata } from "__support__/metadata";
+import {
+  createSampleDatabase,
+  SAMPLE_DB_ID,
+} from "metabase-types/api/mocks/presets";
+import { getHelpText } from "metabase-lib/expressions/helper-text-strings";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+async function setup(opts: SetupOpts) {
+  await baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("ExpressionEditorHelpText (EE with token)", () => {
+  const metadata = createMockMetadata({ databases: [createSampleDatabase()] });
+  const database = checkNotNull(metadata.database(SAMPLE_DB_ID));
+
+  describe("Metabase links", () => {
+    const helpText = getHelpText("concat", database, "UTC");
+    it("should show a help link when `show-metabase-links: true`", async () => {
+      await setup({ helpText, showMetabaseLinks: true });
+
+      expect(
+        screen.getByRole("img", { name: "reference icon" }),
+      ).toBeInTheDocument();
+      expect(screen.getByText("Learn more")).toBeInTheDocument();
+    });
+
+    it("should not show a help link when `show-metabase-links: false`", async () => {
+      await setup({ helpText, showMetabaseLinks: false });
+
+      expect(
+        screen.queryByRole("img", { name: "reference icon" }),
+      ).not.toBeInTheDocument();
+      expect(screen.queryByText("Learn more")).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/setup.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/setup.tsx
new file mode 100644
index 00000000000..bd33d3acaaf
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText/tests/setup.tsx
@@ -0,0 +1,50 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { renderWithProviders, screen } from "__support__/ui";
+import { createMockState } from "metabase-types/store/mocks";
+import { mockSettings } from "__support__/settings";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { ExpressionEditorHelpText } from "../ExpressionEditorHelpText";
+import type { ExpressionEditorHelpTextProps } from "../ExpressionEditorHelpText";
+
+export interface SetupOpts {
+  helpText?: ExpressionEditorHelpTextProps["helpText"];
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export async function setup({
+  helpText,
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts) {
+  const target = { current: null };
+
+  const props: ExpressionEditorHelpTextProps = {
+    helpText,
+    width: 397,
+    target,
+  };
+
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<ExpressionEditorHelpText {...props} />, {
+    storeInitialState: state,
+  });
+
+  // have to wait for TippyPopover to render content
+  expect(
+    await screen.findByTestId("expression-helper-popover"),
+  ).toBeInTheDocument();
+}
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield/ExpressionEditorTextfield.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield/ExpressionEditorTextfield.tsx
index 0d20065fda9..d59e306323a 100644
--- a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield/ExpressionEditorTextfield.tsx
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield/ExpressionEditorTextfield.tsx
@@ -26,7 +26,7 @@ import type {
 } from "metabase-lib/expressions/types";
 
 import type Metadata from "metabase-lib/metadata/Metadata";
-import ExpressionEditorHelpText from "../ExpressionEditorHelpText";
+import { ExpressionEditorHelpText } from "../ExpressionEditorHelpText";
 import ExpressionEditorSuggestions from "../ExpressionEditorSuggestions";
 import ExpressionMode from "../ExpressionMode";
 import {
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.tsx
index f8778f93cc0..c5c5e4d9e64 100644
--- a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.tsx
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.tsx
@@ -4,7 +4,6 @@ import { t } from "ttag";
 import { isNotNull } from "metabase/lib/types";
 import Button from "metabase/core/components/Button";
 import Input from "metabase/core/components/Input/Input";
-import Tooltip from "metabase/core/components/Tooltip";
 import MetabaseSettings from "metabase/lib/settings";
 import type { Expression } from "metabase-types/api";
 import type * as Lib from "metabase-lib";
@@ -18,12 +17,11 @@ import {
   FieldLabel,
   FieldWrapper,
   Footer,
-  InfoLink,
   RemoveLink,
-  StyledFieldTitleIcon,
 } from "./ExpressionWidget.styled";
+import { ExpressionWidgetInfo } from "./ExpressionWidgetInfo";
 
-const EXPRESSIONS_DOCUMENTATION_URL = MetabaseSettings.docsUrl(
+export const EXPRESSIONS_DOCUMENTATION_URL = MetabaseSettings.docsUrl(
   "questions/query-builder/expressions",
 );
 
@@ -131,19 +129,7 @@ export const ExpressionWidget = <Clause extends object = Lib.ExpressionClause>(
       <ExpressionFieldWrapper>
         <FieldLabel htmlFor="expression-content">
           {t`Expression`}
-          <Tooltip
-            tooltip={t`You can reference columns here in functions or equations, like: floor([Price] - [Discount]). Click for documentation.`}
-            placement="right"
-            maxWidth={332}
-          >
-            <InfoLink
-              target="_blank"
-              href={EXPRESSIONS_DOCUMENTATION_URL}
-              aria-label={t`Open expressions documentation`}
-            >
-              <StyledFieldTitleIcon name="info" />
-            </InfoLink>
-          </Tooltip>
+          <ExpressionWidgetInfo />
         </FieldLabel>
         <div ref={helpTextTargetRef}>
           <ExpressionEditorTextfield
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/ExpressionWidgetInfo.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/ExpressionWidgetInfo.tsx
new file mode 100644
index 00000000000..c4a30e3e0d7
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/ExpressionWidgetInfo.tsx
@@ -0,0 +1,37 @@
+import { t } from "ttag";
+import Tooltip from "metabase/core/components/Tooltip";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { Box } from "metabase/ui";
+import { InfoLink, StyledFieldTitleIcon } from "../ExpressionWidget.styled";
+import { EXPRESSIONS_DOCUMENTATION_URL } from "../ExpressionWidget";
+
+export function ExpressionWidgetInfo() {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
+  return showMetabaseLinks ? (
+    <Tooltip
+      tooltip={t`You can reference columns here in functions or equations, like: floor([Price] - [Discount]). Click for documentation.`}
+      placement="right"
+      maxWidth={332}
+    >
+      <InfoLink
+        target="_blank"
+        href={EXPRESSIONS_DOCUMENTATION_URL}
+        aria-label={t`Open expressions documentation`}
+      >
+        <StyledFieldTitleIcon name="info" />
+      </InfoLink>
+    </Tooltip>
+  ) : (
+    <Tooltip
+      tooltip={t`You can reference columns here in functions or equations, like: floor([Price] - [Discount]).`}
+      placement="right"
+      maxWidth={332}
+    >
+      <Box ml="0.25rem">
+        <StyledFieldTitleIcon name="info" />
+      </Box>
+    </Tooltip>
+  );
+}
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/index.ts b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/index.ts
new file mode 100644
index 00000000000..d833d913eb4
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/index.ts
@@ -0,0 +1 @@
+export { ExpressionWidgetInfo } from "./ExpressionWidgetInfo";
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/common.unit.spec.ts b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..b6d482963b0
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/common.unit.spec.ts
@@ -0,0 +1,39 @@
+import userEvent from "@testing-library/user-event";
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("ExpressionWidgetInfo (OSS)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByRole("link", { name: "Open expressions documentation" }),
+    ).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/query-builder/expressions.html",
+    );
+    userEvent.hover(screen.getByLabelText("info icon"));
+    expect(
+      screen.getByText(
+        "You can reference columns here in functions or equations, like: floor([Price] - [Discount]). Click for documentation.",
+      ),
+    ).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByRole("link", { name: "Open expressions documentation" }),
+    ).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/query-builder/expressions.html",
+    );
+    userEvent.hover(screen.getByLabelText("info icon"));
+    expect(
+      screen.getByText(
+        "You can reference columns here in functions or equations, like: floor([Price] - [Discount]). Click for documentation.",
+      ),
+    ).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/enterprise.unit.spec.ts b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..f79024f3e27
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/enterprise.unit.spec.ts
@@ -0,0 +1,44 @@
+import userEvent from "@testing-library/user-event";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("ExpressionWidgetInfo (EE without token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByRole("link", { name: "Open expressions documentation" }),
+    ).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/query-builder/expressions.html",
+    );
+    userEvent.hover(screen.getByLabelText("info icon"));
+    expect(
+      screen.getByText(
+        "You can reference columns here in functions or equations, like: floor([Price] - [Discount]). Click for documentation.",
+      ),
+    ).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByRole("link", { name: "Open expressions documentation" }),
+    ).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/query-builder/expressions.html",
+    );
+    userEvent.hover(screen.getByLabelText("info icon"));
+    expect(
+      screen.getByText(
+        "You can reference columns here in functions or equations, like: floor([Price] - [Discount]). Click for documentation.",
+      ),
+    ).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/premium.unit.spec.ts b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..940e6736bfe
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/premium.unit.spec.ts
@@ -0,0 +1,45 @@
+import userEvent from "@testing-library/user-event";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("ExpressionWidgetInfo (EE with token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByRole("link", { name: "Open expressions documentation" }),
+    ).toHaveProperty(
+      "href",
+      "https://www.metabase.com/docs/latest/questions/query-builder/expressions.html",
+    );
+    userEvent.hover(screen.getByLabelText("info icon"));
+    expect(
+      screen.getByText(
+        "You can reference columns here in functions or equations, like: floor([Price] - [Discount]). Click for documentation.",
+      ),
+    ).toBeInTheDocument();
+  });
+
+  it("should not show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.queryByRole("link", { name: "Open expressions documentation" }),
+    ).not.toBeInTheDocument();
+    userEvent.hover(screen.getByLabelText("info icon"));
+    expect(
+      screen.getByText(
+        "You can reference columns here in functions or equations, like: floor([Price] - [Discount]).",
+      ),
+    ).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/setup.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/setup.tsx
new file mode 100644
index 00000000000..32e1712554c
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidgetInfo/tests/setup.tsx
@@ -0,0 +1,32 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { ExpressionWidgetInfo } from "../ExpressionWidgetInfo";
+
+export interface SetupOpts {
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<ExpressionWidgetInfo />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp.tsx b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/TagEditorHelp.tsx
similarity index 92%
rename from frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp.tsx
rename to frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/TagEditorHelp.tsx
index f72e380c9f5..e8cf41526a9 100644
--- a/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp.tsx
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/TagEditorHelp.tsx
@@ -5,6 +5,8 @@ import ExternalLink from "metabase/core/components/ExternalLink";
 import MetabaseSettings from "metabase/lib/settings";
 import { uuid } from "metabase/lib/utils";
 import type { DatabaseId, NativeDatasetQuery } from "metabase-types/api";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
 import type Database from "metabase-lib/metadata/Database";
 
 const SQL_EXAMPLES: Record<string, NativeDatasetQuery> = {
@@ -234,6 +236,8 @@ export const TagEditorHelp = ({
   const examples = engine === "mongo" ? MONGO_EXAMPLES : SQL_EXAMPLES;
   const datasetId = engine === "mongo" ? database?.id : sampleDatabaseId;
 
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
   let setQueryWithDatasetId;
 
   if (datasetId != null) {
@@ -262,7 +266,7 @@ export const TagEditorHelp = ({
       <h4 className="pt2">{t`Variables`}</h4>
       <p>
         {jt`${(
-          <Code>{"{{variable_name}}"}</Code>
+          <Code key="code">{"{{variable_name}}"}</Code>
         )} creates a variable in this query template called "variable_name". Variables can be given types in the side panel, which changes their behavior. All variable types other than "Field Filter" will automatically cause a filter widget to be placed on this question; with Field Filters, this is optional. When this filter widget is filled in, that value replaces the variable in the query template.`}
       </p>
       <TagExample
@@ -285,7 +289,7 @@ export const TagEditorHelp = ({
       <h4 className="pt2">{t`Optional Clauses`}</h4>
       <p>
         {jt`Brackets around a ${(
-          <Code>{"[[{{variable}}]]"}</Code>
+          <Code key="code">{"[[{{variable}}]]"}</Code>
         )} create an optional clause in the template. If "variable" is set, then the entire clause is placed into the template. If not, then the entire clause is ignored.`}
       </p>
       <TagExample
@@ -307,15 +311,17 @@ export const TagEditorHelp = ({
         setDatasetQuery={setQueryWithDatasetId}
       />
 
-      <p className="pt2 link">
-        <ExternalLink
-          href={MetabaseSettings.docsUrl(
-            "questions/native-editor/sql-parameters",
-          )}
-          target="_blank"
-          data-metabase-event="QueryBuilder;Template Tag Documentation Click"
-        >{t`Read the full documentation`}</ExternalLink>
-      </p>
+      {showMetabaseLinks && (
+        <p className="pt2 link">
+          <ExternalLink
+            href={MetabaseSettings.docsUrl(
+              "questions/native-editor/sql-parameters",
+            )}
+            target="_blank"
+            data-metabase-event="QueryBuilder;Template Tag Documentation Click"
+          >{t`Read the full documentation`}</ExternalLink>
+        </p>
+      )}
     </div>
   );
 };
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/index.ts b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/index.ts
new file mode 100644
index 00000000000..47b75d54308
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/index.ts
@@ -0,0 +1 @@
+export { TagEditorHelp } from "./TagEditorHelp";
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/common.unit.spec.ts b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..99ea0fa90ea
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/common.unit.spec.ts
@@ -0,0 +1,16 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("TagEditorHelp (OSS)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(screen.getByText("Read the full documentation")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(screen.getByText("Read the full documentation")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/enterprise.unit.spec.ts b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..52f71e6a434
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/enterprise.unit.spec.ts
@@ -0,0 +1,24 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    ...opts,
+  });
+}
+
+describe("TagEditorHelp (EE without token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(screen.getByText("Read the full documentation")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(screen.getByText("Read the full documentation")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/premium.unit.spec.ts b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..e9bd3b03fdc
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/premium.unit.spec.ts
@@ -0,0 +1,27 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("TagEditorHelp (EE with token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(screen.getByText("Read the full documentation")).toBeInTheDocument();
+  });
+
+  it("should not show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.queryByText("Read the full documentation"),
+    ).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/setup.tsx b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/setup.tsx
new file mode 100644
index 00000000000..0237eabb98d
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorHelp/tests/setup.tsx
@@ -0,0 +1,46 @@
+import type { TokenFeatures } from "metabase-types/api";
+import {
+  createMockDatabase,
+  createMockTokenFeatures,
+} from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import Database from "metabase-lib/metadata/Database";
+import { TagEditorHelp } from "../TagEditorHelp";
+
+export interface SetupOpts {
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(
+    <TagEditorHelp
+      database={new Database({ ...createMockDatabase(), tables: [] })}
+      sampleDatabaseId={99}
+      setDatasetQuery={jest.fn()}
+      switchToSettings={jest.fn()}
+    />,
+    {
+      storeInitialState: state,
+    },
+  );
+};
diff --git a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/PreviewQueryModal.tsx b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/PreviewQueryModal.tsx
index a6e81978549..42662d8740a 100644
--- a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/PreviewQueryModal.tsx
+++ b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/PreviewQueryModal.tsx
@@ -1,31 +1,32 @@
-import { connect } from "react-redux";
 import { t } from "ttag";
+import { useCallback } from "react";
 import MetabaseSettings from "metabase/lib/settings";
 import {
   getNativeQueryFn,
   getQuestion,
 } from "metabase/query_builder/selectors";
-import type { NativeQueryForm } from "metabase-types/api";
-import type { State } from "metabase-types/store";
-import type Question from "metabase-lib/Question";
+import { checkNotNull } from "metabase/lib/types";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
 import NativeQueryModal, { useNativeQuery } from "../NativeQueryModal";
 import { ModalExternalLink } from "./PreviewQueryModal.styled";
 
 interface PreviewQueryModalProps {
-  question: Question;
-  onLoadQuery: ({ pretty }: { pretty?: boolean }) => Promise<NativeQueryForm>;
   onClose?: () => void;
 }
 
-const PreviewQueryModal = ({
-  question,
-  onLoadQuery,
+export const PreviewQueryModal = ({
   onClose,
 }: PreviewQueryModalProps): JSX.Element => {
-  const { query, error, isLoading } = useNativeQuery(question, () =>
-    onLoadQuery({ pretty: false }),
+  const question = checkNotNull(useSelector(getQuestion));
+  const onLoadQuery = useSelector(getNativeQueryFn);
+  const handleLoadQuery = useCallback(
+    () => onLoadQuery({ pretty: false }),
+    [onLoadQuery],
   );
+  const { query, error, isLoading } = useNativeQuery(question, handleLoadQuery);
   const learnUrl = MetabaseSettings.learnUrl("debugging-sql/sql-syntax");
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
 
   return (
     <NativeQueryModal
@@ -35,7 +36,7 @@ const PreviewQueryModal = ({
       isLoading={isLoading}
       onClose={onClose}
     >
-      {error && (
+      {error && showMetabaseLinks && (
         <ModalExternalLink href={learnUrl}>
           {t`Learn how to debug SQL errors`}
         </ModalExternalLink>
@@ -43,13 +44,3 @@ const PreviewQueryModal = ({
     </NativeQueryModal>
   );
 };
-
-const mapStateToProps = (state: State) => ({
-  // FIXME: remove the non-null assertion operator
-  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-  question: getQuestion(state)!,
-  onLoadQuery: getNativeQueryFn(state),
-});
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default connect(mapStateToProps)(PreviewQueryModal);
diff --git a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/index.ts b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/index.ts
index e2d9200b81d..54756d74cec 100644
--- a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/index.ts
+++ b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./PreviewQueryModal";
+export { PreviewQueryModal } from "./PreviewQueryModal";
diff --git a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/common.unit.spec.tsx b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/common.unit.spec.tsx
new file mode 100644
index 00000000000..b84032b4275
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/common.unit.spec.tsx
@@ -0,0 +1,22 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("PreviewQueryModal (OSS)", () => {
+  it("should render help link when `show-metabase-links: true", async () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(await screen.findByText("Query preview")).toBeInTheDocument();
+    expect(
+      screen.getByText("Learn how to debug SQL errors"),
+    ).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false", async () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(await screen.findByText("Query preview")).toBeInTheDocument();
+    expect(
+      screen.getByText("Learn how to debug SQL errors"),
+    ).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/enterprise.unit.spec.tsx b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/enterprise.unit.spec.tsx
new file mode 100644
index 00000000000..09cf0e31bd4
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/enterprise.unit.spec.tsx
@@ -0,0 +1,27 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("PreviewQueryModal (EE without token)", () => {
+  it("should render help link when `show-metabase-links: true", async () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(await screen.findByText("Query preview")).toBeInTheDocument();
+    expect(
+      screen.getByText("Learn how to debug SQL errors"),
+    ).toBeInTheDocument();
+  });
+
+  it("should render help link when `show-metabase-links: false", async () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(await screen.findByText("Query preview")).toBeInTheDocument();
+    expect(
+      screen.getByText("Learn how to debug SQL errors"),
+    ).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/premium.unit.spec.tsx b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/premium.unit.spec.tsx
new file mode 100644
index 00000000000..7a9bd3877c3
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/premium.unit.spec.tsx
@@ -0,0 +1,31 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("PreviewQueryModal (EE with token)", () => {
+  it("should render help link when `show-metabase-links: true", async () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(await screen.findByText("Query preview")).toBeInTheDocument();
+    expect(
+      screen.getByText("Learn how to debug SQL errors"),
+    ).toBeInTheDocument();
+  });
+
+  it("should not render help link when `show-metabase-links: false", async () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(await screen.findByText("Query preview")).toBeInTheDocument();
+    expect(
+      screen.queryByText("Learn how to debug SQL errors"),
+    ).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/setup.tsx b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/setup.tsx
new file mode 100644
index 00000000000..e2f77939b62
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/PreviewQueryModal/tests/setup.tsx
@@ -0,0 +1,55 @@
+import fetchMock from "fetch-mock";
+import type { TokenFeatures } from "metabase-types/api";
+import {
+  createMockCard,
+  createMockDatabase,
+  createMockTokenFeatures,
+} from "metabase-types/api/mocks";
+import {
+  createMockQueryBuilderState,
+  createMockState,
+} from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { createMockEntitiesState } from "__support__/store";
+import { renderWithProviders } from "__support__/ui";
+
+import { PreviewQueryModal } from "..";
+
+export interface SetupOpts {
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const card = createMockCard();
+  const state = createMockState({
+    qb: createMockQueryBuilderState({ card }),
+    entities: createMockEntitiesState({
+      databases: [createMockDatabase()],
+      questions: [card],
+    }),
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  fetchMock.post("path:/api/dataset/native", {
+    status: 500,
+    body: {
+      message: 'Cannot run the query: missing required parameters: #{"value"}',
+    },
+  });
+
+  renderWithProviders(<PreviewQueryModal />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/reference/metrics/MetricList.jsx b/frontend/src/metabase/reference/metrics/MetricList.jsx
deleted file mode 100644
index 6097af58dd4..00000000000
--- a/frontend/src/metabase/reference/metrics/MetricList.jsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/* eslint "react/prop-types": "warn" */
-import { Component } from "react";
-import PropTypes from "prop-types";
-import { connect } from "react-redux";
-import { t } from "ttag";
-
-import S from "metabase/components/List/List.css";
-
-import List from "metabase/components/List";
-import ListItem from "metabase/components/ListItem";
-import AdminAwareEmptyState from "metabase/components/AdminAwareEmptyState";
-
-import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
-
-import MetabaseSettings from "metabase/lib/settings";
-import * as metadataActions from "metabase/redux/metadata";
-import ReferenceHeader from "../components/ReferenceHeader";
-
-import { getMetrics, getError, getLoading } from "../selectors";
-
-const emptyStateData = {
-  title: t`Metrics are the official numbers that your team cares about`,
-  adminMessage: t`Defining common metrics for your team makes it even easier to ask questions`,
-  message: t`Metrics will appear here once your admins have created some`,
-  image: "app/assets/img/metrics-list",
-  adminAction: t`Learn how to create metrics`,
-  adminLink: MetabaseSettings.docsUrl(
-    "data-modeling/segments-and-metrics",
-    "creating-a-metric",
-  ),
-};
-
-const mapStateToProps = (state, props) => ({
-  entities: getMetrics(state, props),
-  loading: getLoading(state, props),
-  loadingError: getError(state, props),
-});
-
-const mapDispatchToProps = {
-  ...metadataActions,
-};
-
-class MetricList extends Component {
-  static propTypes = {
-    style: PropTypes.object.isRequired,
-    entities: PropTypes.object.isRequired,
-    loading: PropTypes.bool,
-    loadingError: PropTypes.object,
-  };
-
-  render() {
-    const { entities, style, loadingError, loading } = this.props;
-
-    return (
-      <div style={style} className="full">
-        <ReferenceHeader name={t`Metrics`} />
-        <LoadingAndErrorWrapper
-          loading={!loadingError && loading}
-          error={loadingError}
-        >
-          {() =>
-            Object.keys(entities).length > 0 ? (
-              <div className="wrapper wrapper--trim">
-                <List>
-                  {Object.values(entities).map(
-                    entity =>
-                      entity &&
-                      entity.id &&
-                      entity.name && (
-                        <ListItem
-                          key={entity.id}
-                          name={entity.display_name || entity.name}
-                          description={entity.description}
-                          url={`/reference/metrics/${entity.id}`}
-                          icon="ruler"
-                        />
-                      ),
-                  )}
-                </List>
-              </div>
-            ) : (
-              <div className={S.empty}>
-                <AdminAwareEmptyState {...emptyStateData} />
-              </div>
-            )
-          }
-        </LoadingAndErrorWrapper>
-      </div>
-    );
-  }
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MetricList);
diff --git a/frontend/src/metabase/reference/metrics/MetricList/MetricList.tsx b/frontend/src/metabase/reference/metrics/MetricList/MetricList.tsx
new file mode 100644
index 00000000000..81b30f71a11
--- /dev/null
+++ b/frontend/src/metabase/reference/metrics/MetricList/MetricList.tsx
@@ -0,0 +1,81 @@
+import type { CSSProperties } from "react";
+import { t } from "ttag";
+
+import S from "metabase/components/List/List.css";
+
+import List from "metabase/components/List";
+import ListItem from "metabase/components/ListItem";
+import AdminAwareEmptyState from "metabase/components/AdminAwareEmptyState";
+
+import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
+
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { getDocsUrl } from "metabase/selectors/settings";
+import ReferenceHeader from "../../components/ReferenceHeader";
+
+import { getMetrics, getError, getLoading } from "../../selectors";
+
+const emptyStateData = {
+  title: t`Metrics are the official numbers that your team cares about`,
+  adminMessage: t`Defining common metrics for your team makes it even easier to ask questions`,
+  message: t`Metrics will appear here once your admins have created some`,
+  image: "app/assets/img/metrics-list",
+  adminAction: t`Learn how to create metrics`,
+};
+
+interface MetricListProps {
+  style?: CSSProperties;
+}
+
+export function MetricList({ style }: MetricListProps) {
+  const entities = useSelector(getMetrics);
+  const loading = useSelector(getLoading);
+  const loadingError = useSelector(getError);
+  const adminLink = useSelector(state =>
+    getDocsUrl(state, {
+      page: "data-modeling/segments-and-metrics",
+      anchor: "creating-a-metric",
+    }),
+  );
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+  return (
+    <div style={style} className="full">
+      <ReferenceHeader name={t`Metrics`} />
+      <LoadingAndErrorWrapper
+        loading={!loadingError && loading}
+        error={loadingError}
+      >
+        {() =>
+          Object.keys(entities).length > 0 ? (
+            <div className="wrapper wrapper--trim">
+              <List>
+                {Object.values(entities).map(
+                  entity =>
+                    entity &&
+                    entity.id &&
+                    entity.name && (
+                      <ListItem
+                        key={entity.id}
+                        name={entity.name}
+                        description={entity.description}
+                        url={`/reference/metrics/${entity.id}`}
+                        icon="ruler"
+                      />
+                    ),
+                )}
+              </List>
+            </div>
+          ) : (
+            <div className={S.empty}>
+              <AdminAwareEmptyState
+                {...emptyStateData}
+                adminLink={showMetabaseLinks ? adminLink : undefined}
+              />
+            </div>
+          )
+        }
+      </LoadingAndErrorWrapper>
+    </div>
+  );
+}
diff --git a/frontend/src/metabase/reference/metrics/MetricList/index.ts b/frontend/src/metabase/reference/metrics/MetricList/index.ts
new file mode 100644
index 00000000000..b21b9b6715a
--- /dev/null
+++ b/frontend/src/metabase/reference/metrics/MetricList/index.ts
@@ -0,0 +1 @@
+export { MetricList } from "./MetricList";
diff --git a/frontend/src/metabase/reference/metrics/MetricList/tests/common.unit.spec.ts b/frontend/src/metabase/reference/metrics/MetricList/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..e786b27b799
--- /dev/null
+++ b/frontend/src/metabase/reference/metrics/MetricList/tests/common.unit.spec.ts
@@ -0,0 +1,53 @@
+import { createMockUser } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("MetricList (OSS)", () => {
+  describe("Admins", () => {
+    const adminUser = createMockUser({
+      is_superuser: true,
+    });
+
+    it("should render help link when `show-metabase-links: true`", () => {
+      setup({ user: adminUser, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create metrics"),
+      ).toBeInTheDocument();
+    });
+
+    it("should render help link when `show-metabase-links: false`", () => {
+      setup({ user: adminUser, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create metrics"),
+      ).toBeInTheDocument();
+    });
+  });
+
+  describe("Non-admins", () => {
+    const user = createMockUser();
+    it("should not render help link", () => {
+      setup({ user });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create metrics"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/reference/metrics/MetricList/tests/enterprise.unit.spec.ts b/frontend/src/metabase/reference/metrics/MetricList/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..e733e1c0966
--- /dev/null
+++ b/frontend/src/metabase/reference/metrics/MetricList/tests/enterprise.unit.spec.ts
@@ -0,0 +1,61 @@
+import { createMockUser } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    ...opts,
+  });
+}
+
+describe("MetricList (EE without token)", () => {
+  describe("Admins", () => {
+    const adminUser = createMockUser({
+      is_superuser: true,
+    });
+
+    it("should render help link when `show-metabase-links: true`", () => {
+      setup({ user: adminUser, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create metrics"),
+      ).toBeInTheDocument();
+    });
+
+    it("should render help link when `show-metabase-links: false`", () => {
+      setup({ user: adminUser, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create metrics"),
+      ).toBeInTheDocument();
+    });
+  });
+
+  describe("Non-admins", () => {
+    const user = createMockUser();
+    it("should not render help link", () => {
+      setup({ user });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create metrics"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/reference/metrics/MetricList/tests/premium.unit.spec.ts b/frontend/src/metabase/reference/metrics/MetricList/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..39df9e08796
--- /dev/null
+++ b/frontend/src/metabase/reference/metrics/MetricList/tests/premium.unit.spec.ts
@@ -0,0 +1,62 @@
+import { createMockUser } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("MetricList (EE with token)", () => {
+  describe("Admins", () => {
+    const adminUser = createMockUser({
+      is_superuser: true,
+    });
+
+    it("should render help link when `show-metabase-links: true`", () => {
+      setup({ user: adminUser, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create metrics"),
+      ).toBeInTheDocument();
+    });
+
+    it("should not render help link when `show-metabase-links: false`", () => {
+      setup({ user: adminUser, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create metrics"),
+      ).not.toBeInTheDocument();
+    });
+  });
+
+  describe("Non-admins", () => {
+    const user = createMockUser();
+    it("should not render help link", () => {
+      setup({ user });
+
+      expect(
+        screen.getByText(
+          "Metrics are the official numbers that your team cares about",
+        ),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create metrics"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/reference/metrics/MetricList/tests/setup.tsx b/frontend/src/metabase/reference/metrics/MetricList/tests/setup.tsx
new file mode 100644
index 00000000000..2bd4736d57d
--- /dev/null
+++ b/frontend/src/metabase/reference/metrics/MetricList/tests/setup.tsx
@@ -0,0 +1,38 @@
+import type { TokenFeatures, User } from "metabase-types/api";
+import {
+  createMockTokenFeatures,
+  createMockUser,
+} from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { MetricList } from "../MetricList";
+
+export interface SetupOpts {
+  user: User;
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  user,
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts) => {
+  const state = createMockState({
+    currentUser: createMockUser(user),
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<MetricList />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/reference/metrics/MetricListContainer.jsx b/frontend/src/metabase/reference/metrics/MetricListContainer.jsx
index 0dadc595080..b2f6cdb711c 100644
--- a/frontend/src/metabase/reference/metrics/MetricListContainer.jsx
+++ b/frontend/src/metabase/reference/metrics/MetricListContainer.jsx
@@ -5,7 +5,7 @@ import { connect } from "react-redux";
 
 import BaseSidebar from "metabase/reference/guide/BaseSidebar";
 import SidebarLayout from "metabase/components/SidebarLayout";
-import MetricList from "metabase/reference/metrics/MetricList";
+import { MetricList } from "metabase/reference/metrics/MetricList";
 
 import * as metadataActions from "metabase/redux/metadata";
 import * as actions from "metabase/reference/reference";
diff --git a/frontend/src/metabase/reference/segments/SegmentList.jsx b/frontend/src/metabase/reference/segments/SegmentList.jsx
deleted file mode 100644
index 6ab37213572..00000000000
--- a/frontend/src/metabase/reference/segments/SegmentList.jsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/* eslint "react/prop-types": "warn" */
-import { Component } from "react";
-import PropTypes from "prop-types";
-import { connect } from "react-redux";
-import { t } from "ttag";
-import MetabaseSettings from "metabase/lib/settings";
-
-import S from "metabase/components/List/List.css";
-
-import List from "metabase/components/List";
-import ListItem from "metabase/components/ListItem";
-import AdminAwareEmptyState from "metabase/components/AdminAwareEmptyState";
-
-import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
-
-import * as metadataActions from "metabase/redux/metadata";
-import ReferenceHeader from "../components/ReferenceHeader";
-
-import { getSegments, getError, getLoading } from "../selectors";
-
-const emptyStateData = {
-  title: t`Segments are interesting subsets of tables`,
-  adminMessage: t`Defining common segments for your team makes it even easier to ask questions`,
-  message: t`Segments will appear here once your admins have created some`,
-  image: "app/assets/img/segments-list",
-  adminAction: t`Learn how to create segments`,
-  adminLink: MetabaseSettings.docsUrl(
-    "data-modeling/segments-and-metrics",
-    "creating-a-segment",
-  ),
-};
-
-const mapStateToProps = (state, props) => ({
-  entities: getSegments(state, props),
-  loading: getLoading(state, props),
-  loadingError: getError(state, props),
-});
-
-const mapDispatchToProps = {
-  ...metadataActions,
-};
-
-class SegmentList extends Component {
-  static propTypes = {
-    style: PropTypes.object.isRequired,
-    entities: PropTypes.object.isRequired,
-    loading: PropTypes.bool,
-    loadingError: PropTypes.object,
-  };
-
-  render() {
-    const { entities, style, loadingError, loading } = this.props;
-
-    return (
-      <div style={style} className="full">
-        <ReferenceHeader name={t`Segments`} />
-        <LoadingAndErrorWrapper
-          loading={!loadingError && loading}
-          error={loadingError}
-        >
-          {() =>
-            Object.keys(entities).length > 0 ? (
-              <div className="wrapper wrapper--trim">
-                <List>
-                  {Object.values(entities).map(
-                    entity =>
-                      entity &&
-                      entity.id &&
-                      entity.name && (
-                        <ListItem
-                          key={entity.id}
-                          name={entity.display_name || entity.name}
-                          description={entity.description}
-                          url={`/reference/segments/${entity.id}`}
-                          icon="segment"
-                        />
-                      ),
-                  )}
-                </List>
-              </div>
-            ) : (
-              <div className={S.empty}>
-                <AdminAwareEmptyState {...emptyStateData} />
-              </div>
-            )
-          }
-        </LoadingAndErrorWrapper>
-      </div>
-    );
-  }
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(SegmentList);
diff --git a/frontend/src/metabase/reference/segments/SegmentList/SegmentList.tsx b/frontend/src/metabase/reference/segments/SegmentList/SegmentList.tsx
new file mode 100644
index 00000000000..4837d57e4e4
--- /dev/null
+++ b/frontend/src/metabase/reference/segments/SegmentList/SegmentList.tsx
@@ -0,0 +1,81 @@
+import type { CSSProperties } from "react";
+import { t } from "ttag";
+
+import S from "metabase/components/List/List.css";
+
+import List from "metabase/components/List";
+import ListItem from "metabase/components/ListItem";
+import AdminAwareEmptyState from "metabase/components/AdminAwareEmptyState";
+
+import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
+
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { getDocsUrl } from "metabase/selectors/settings";
+import ReferenceHeader from "../../components/ReferenceHeader";
+
+import { getSegments, getError, getLoading } from "../../selectors";
+
+const emptyStateData = {
+  title: t`Segments are interesting subsets of tables`,
+  adminMessage: t`Defining common segments for your team makes it even easier to ask questions`,
+  message: t`Segments will appear here once your admins have created some`,
+  image: "app/assets/img/segments-list",
+  adminAction: t`Learn how to create segments`,
+};
+
+interface SegmentListProps {
+  style?: CSSProperties;
+}
+
+export function SegmentList({ style }: SegmentListProps) {
+  const entities = useSelector(getSegments);
+  const loading = useSelector(getLoading);
+  const loadingError = useSelector(getError);
+  const adminLink = useSelector(state =>
+    getDocsUrl(state, {
+      page: "data-modeling/segments-and-metrics",
+      anchor: "creating-a-segment",
+    }),
+  );
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+  return (
+    <div style={style} className="full">
+      <ReferenceHeader name={t`Segments`} />
+      <LoadingAndErrorWrapper
+        loading={!loadingError && loading}
+        error={loadingError}
+      >
+        {() =>
+          Object.keys(entities).length > 0 ? (
+            <div className="wrapper wrapper--trim">
+              <List>
+                {Object.values(entities).map(
+                  entity =>
+                    entity &&
+                    entity.id &&
+                    entity.name && (
+                      <ListItem
+                        key={entity.id}
+                        name={entity.name}
+                        description={entity.description}
+                        url={`/reference/segments/${entity.id}`}
+                        icon="segment"
+                      />
+                    ),
+                )}
+              </List>
+            </div>
+          ) : (
+            <div className={S.empty}>
+              <AdminAwareEmptyState
+                {...emptyStateData}
+                adminLink={showMetabaseLinks ? adminLink : undefined}
+              />
+            </div>
+          )
+        }
+      </LoadingAndErrorWrapper>
+    </div>
+  );
+}
diff --git a/frontend/src/metabase/reference/segments/SegmentList/index.ts b/frontend/src/metabase/reference/segments/SegmentList/index.ts
new file mode 100644
index 00000000000..20289d9e93b
--- /dev/null
+++ b/frontend/src/metabase/reference/segments/SegmentList/index.ts
@@ -0,0 +1 @@
+export { SegmentList } from "./SegmentList";
diff --git a/frontend/src/metabase/reference/segments/SegmentList/tests/common.unit.spec.ts b/frontend/src/metabase/reference/segments/SegmentList/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..4b10dc07b99
--- /dev/null
+++ b/frontend/src/metabase/reference/segments/SegmentList/tests/common.unit.spec.ts
@@ -0,0 +1,47 @@
+import { createMockUser } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("SegmentList (OSS)", () => {
+  describe("Admins", () => {
+    const adminUser = createMockUser({
+      is_superuser: true,
+    });
+
+    it("should render help link when `show-metabase-links: true`", () => {
+      setup({ user: adminUser, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create segments"),
+      ).toBeInTheDocument();
+    });
+
+    it("should render help link when `show-metabase-links: false`", () => {
+      setup({ user: adminUser, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create segments"),
+      ).toBeInTheDocument();
+    });
+  });
+
+  describe("Non-admins", () => {
+    const user = createMockUser();
+    it("should not render help link", () => {
+      setup({ user });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create segments"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/reference/segments/SegmentList/tests/enterprise.unit.spec.ts b/frontend/src/metabase/reference/segments/SegmentList/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..7363bfe9754
--- /dev/null
+++ b/frontend/src/metabase/reference/segments/SegmentList/tests/enterprise.unit.spec.ts
@@ -0,0 +1,55 @@
+import { createMockUser } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    ...opts,
+  });
+}
+
+describe("SegmentList (EE without token)", () => {
+  describe("Admins", () => {
+    const adminUser = createMockUser({
+      is_superuser: true,
+    });
+
+    it("should render help link when `show-metabase-links: true`", () => {
+      setup({ user: adminUser, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create segments"),
+      ).toBeInTheDocument();
+    });
+
+    it("should render help link when `show-metabase-links: false`", () => {
+      setup({ user: adminUser, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create segments"),
+      ).toBeInTheDocument();
+    });
+  });
+
+  describe("Non-admins", () => {
+    const user = createMockUser();
+    it("should not render help link", () => {
+      setup({ user });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create segments"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/reference/segments/SegmentList/tests/premium.unit.spec.ts b/frontend/src/metabase/reference/segments/SegmentList/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..056e3cb212d
--- /dev/null
+++ b/frontend/src/metabase/reference/segments/SegmentList/tests/premium.unit.spec.ts
@@ -0,0 +1,56 @@
+import { createMockUser } from "metabase-types/api/mocks";
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("SegmentList (EE with token)", () => {
+  describe("Admins", () => {
+    const adminUser = createMockUser({
+      is_superuser: true,
+    });
+
+    it("should render help link when `show-metabase-links: true`", () => {
+      setup({ user: adminUser, showMetabaseLinks: true });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.getByText("Learn how to create segments"),
+      ).toBeInTheDocument();
+    });
+
+    it("should not render help link when `show-metabase-links: false`", () => {
+      setup({ user: adminUser, showMetabaseLinks: false });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create segments"),
+      ).not.toBeInTheDocument();
+    });
+  });
+
+  describe("Non-admins", () => {
+    const user = createMockUser();
+    it("should not render help link", () => {
+      setup({ user });
+
+      expect(
+        screen.getByText("Segments are interesting subsets of tables"),
+      ).toBeInTheDocument();
+      expect(
+        screen.queryByText("Learn how to create segments"),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/reference/segments/SegmentList/tests/setup.tsx b/frontend/src/metabase/reference/segments/SegmentList/tests/setup.tsx
new file mode 100644
index 00000000000..e24638ea71b
--- /dev/null
+++ b/frontend/src/metabase/reference/segments/SegmentList/tests/setup.tsx
@@ -0,0 +1,38 @@
+import type { TokenFeatures, User } from "metabase-types/api";
+import {
+  createMockTokenFeatures,
+  createMockUser,
+} from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { SegmentList } from "../SegmentList";
+
+export interface SetupOpts {
+  user: User;
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  user,
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts) => {
+  const state = createMockState({
+    currentUser: createMockUser(user),
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<SegmentList />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/reference/segments/SegmentListContainer.jsx b/frontend/src/metabase/reference/segments/SegmentListContainer.jsx
index 47fb171140a..bbf8a376fbf 100644
--- a/frontend/src/metabase/reference/segments/SegmentListContainer.jsx
+++ b/frontend/src/metabase/reference/segments/SegmentListContainer.jsx
@@ -5,7 +5,7 @@ import { connect } from "react-redux";
 
 import BaseSidebar from "metabase/reference/guide/BaseSidebar";
 import SidebarLayout from "metabase/components/SidebarLayout";
-import SegmentList from "metabase/reference/segments/SegmentList";
+import { SegmentList } from "metabase/reference/segments/SegmentList";
 
 import * as metadataActions from "metabase/redux/metadata";
 import * as actions from "metabase/reference/reference";
diff --git a/frontend/src/metabase/selectors/whitelabel/index.ts b/frontend/src/metabase/selectors/whitelabel/index.ts
index 8314df567c0..111715e6006 100644
--- a/frontend/src/metabase/selectors/whitelabel/index.ts
+++ b/frontend/src/metabase/selectors/whitelabel/index.ts
@@ -16,3 +16,7 @@ export function getApplicationName(state: State) {
 export function getCanWhitelabel(state: State) {
   return PLUGIN_SELECTORS.canWhitelabel(state);
 }
+
+export function getShowMetabaseLinks(state: State) {
+  return PLUGIN_SELECTORS.getShowMetabaseLinks(state);
+}
diff --git a/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts b/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts
index 4b71c2697df..66128307fb9 100644
--- a/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts
+++ b/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts
@@ -1,6 +1,7 @@
 import {
   getApplicationName,
   getIsWhiteLabeling,
+  getShowMetabaseLinks,
   getWhiteLabeledLoadingMessage,
   getCanWhitelabel,
 } from "..";
@@ -61,3 +62,17 @@ describe("getCanWhitelabel (OSS)", () => {
     expect(getCanWhitelabel(getState())).toBe(false);
   });
 });
+
+describe("getShowMetabaseLinks (OSS)", () => {
+  it("should return true when show-metabase-links is true", () => {
+    const { getState } = setup({ showMetabaseLinks: true });
+
+    expect(getShowMetabaseLinks(getState())).toBe(true);
+  });
+
+  it("should return true when show-metabase-links is false", () => {
+    const { getState } = setup({ showMetabaseLinks: false });
+
+    expect(getShowMetabaseLinks(getState())).toBe(true);
+  });
+});
diff --git a/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts b/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts
index 27ac486bc45..44c7cc39bf1 100644
--- a/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts
+++ b/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts
@@ -2,6 +2,7 @@ import {
   getApplicationName,
   getCanWhitelabel,
   getIsWhiteLabeling,
+  getShowMetabaseLinks,
   getWhiteLabeledLoadingMessage,
 } from "..";
 import type { SetupOpts } from "./setup";
@@ -66,3 +67,17 @@ describe("getCanWhitelabel (EE without token)", () => {
     expect(getCanWhitelabel(getState())).toBe(false);
   });
 });
+
+describe("getShowMetabaseLinks (EE without token)", () => {
+  it("should return true when show-metabase-links is true", () => {
+    const { getState } = setup({ showMetabaseLinks: true });
+
+    expect(getShowMetabaseLinks(getState())).toBe(true);
+  });
+
+  it("should return true when show-metabase-links is false", () => {
+    const { getState } = setup({ showMetabaseLinks: false });
+
+    expect(getShowMetabaseLinks(getState())).toBe(true);
+  });
+});
diff --git a/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts b/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts
index f688b9d82f5..cc843ede3b8 100644
--- a/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts
+++ b/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts
@@ -2,6 +2,7 @@ import {
   getApplicationName,
   getCanWhitelabel,
   getIsWhiteLabeling,
+  getShowMetabaseLinks,
   getWhiteLabeledLoadingMessage,
 } from "..";
 import type { SetupOpts } from "./setup";
@@ -65,10 +66,24 @@ describe("getApplicationName (EE with token)", () => {
   });
 });
 
-describe("getCanWhitelabel (EE without token)", () => {
+describe("getCanWhitelabel (EE with token)", () => {
   it("should return true", () => {
     const { getState } = setup();
 
     expect(getCanWhitelabel(getState())).toBe(true);
   });
 });
+
+describe("getShowMetabaseLinks (EE with token)", () => {
+  it("should return true when show-metabase-links is true", () => {
+    const { getState } = setup({ showMetabaseLinks: true });
+
+    expect(getShowMetabaseLinks(getState())).toBe(true);
+  });
+
+  it("should return false when show-metabase-links is false", () => {
+    const { getState } = setup({ showMetabaseLinks: false });
+
+    expect(getShowMetabaseLinks(getState())).toBe(false);
+  });
+});
diff --git a/frontend/src/metabase/selectors/whitelabel/tests/setup.tsx b/frontend/src/metabase/selectors/whitelabel/tests/setup.tsx
index 2bf0ad55d24..68e12ba125b 100644
--- a/frontend/src/metabase/selectors/whitelabel/tests/setup.tsx
+++ b/frontend/src/metabase/selectors/whitelabel/tests/setup.tsx
@@ -10,6 +10,7 @@ import type { LoadingMessage, TokenFeatures } from "metabase-types/api";
 export interface SetupOpts {
   loadingMessage?: LoadingMessage;
   applicationName?: string;
+  showMetabaseLinks?: boolean;
   tokenFeatures?: Partial<TokenFeatures>;
   hasEnterprisePlugins?: boolean;
 }
@@ -17,6 +18,7 @@ export interface SetupOpts {
 export function setup({
   loadingMessage = "doing-science",
   applicationName = "Metabase",
+  showMetabaseLinks = true,
   tokenFeatures = {},
   hasEnterprisePlugins = false,
 }: SetupOpts = {}) {
@@ -25,6 +27,7 @@ export function setup({
       "loading-message": loadingMessage,
       "application-name": applicationName,
       "token-features": createMockTokenFeatures(tokenFeatures),
+      "show-metabase-links": showMetabaseLinks,
     }),
   });
 
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx
index ea5417ad4a3..42207690951 100644
--- a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx
@@ -14,7 +14,7 @@ import RecipientPicker from "metabase/pulse/components/RecipientPicker";
 import SendTestPulse from "metabase/components/SendTestPulse";
 import DeleteSubscriptionAction from "./DeleteSubscriptionAction";
 import DefaultParametersSection from "./DefaultParametersSection";
-import CaveatMessage from "./CaveatMessage";
+import { CaveatMessage } from "./CaveatMessage";
 import Heading from "./Heading";
 import { CHANNEL_NOUN_PLURAL } from "./constants";
 
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx
index 06733084ed7..890ec5e4576 100644
--- a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx
@@ -12,7 +12,7 @@ import { dashboardPulseIsValid } from "metabase/lib/pulse";
 
 import { PLUGIN_DASHBOARD_SUBSCRIPTION_PARAMETERS_SECTION_OVERRIDE } from "metabase/plugins";
 import SlackChannelField from "../SlackChannelField";
-import CaveatMessage from "./CaveatMessage";
+import { CaveatMessage } from "./CaveatMessage";
 import Heading from "./Heading";
 import DeleteSubscriptionAction from "./DeleteSubscriptionAction";
 import DefaultParametersSection from "./DefaultParametersSection";
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage.jsx b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage.jsx
deleted file mode 100644
index f36874a08bf..00000000000
--- a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { t } from "ttag";
-
-import MetabaseSettings from "metabase/lib/settings";
-import ExternalLink from "metabase/core/components/ExternalLink";
-import { CaveatText } from "./CaveatMessage.styled";
-
-function CaveatMessage() {
-  return (
-    <CaveatText>
-      {t`Recipients will see this data just as you see it, regardless of their permissions.`}
-      &nbsp;
-      <ExternalLink
-        className="link"
-        target="_blank"
-        href={MetabaseSettings.docsUrl("dashboards/subscriptions")}
-      >
-        {t`Learn more`}
-      </ExternalLink>
-      .
-    </CaveatText>
-  );
-}
-
-export default CaveatMessage;
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/CaveatMessage.jsx b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/CaveatMessage.jsx
new file mode 100644
index 00000000000..67cbee4c4fa
--- /dev/null
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/CaveatMessage.jsx
@@ -0,0 +1,28 @@
+import { t } from "ttag";
+
+import MetabaseSettings from "metabase/lib/settings";
+import ExternalLink from "metabase/core/components/ExternalLink";
+import { useSelector } from "metabase/lib/redux";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { CaveatText } from "./CaveatMessage.styled";
+
+export function CaveatMessage() {
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+  return (
+    <CaveatText>
+      {t`Recipients will see this data just as you see it, regardless of their permissions.`}
+      {showMetabaseLinks && (
+        <>
+          &nbsp;
+          <ExternalLink
+            className="link"
+            target="_blank"
+            href={MetabaseSettings.docsUrl("dashboards/subscriptions")}
+          >
+            {t`Learn more.`}
+          </ExternalLink>
+        </>
+      )}
+    </CaveatText>
+  );
+}
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage.styled.tsx b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/CaveatMessage.styled.tsx
similarity index 100%
rename from frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage.styled.tsx
rename to frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/CaveatMessage.styled.tsx
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/index.ts b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/index.ts
new file mode 100644
index 00000000000..ea3addb607a
--- /dev/null
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/index.ts
@@ -0,0 +1 @@
+export { CaveatMessage } from "./CaveatMessage";
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/common.unit.spec.ts b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..8d5ee152f2b
--- /dev/null
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/common.unit.spec.ts
@@ -0,0 +1,26 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("CaveatMessage (OSS)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Recipients will see this data just as you see it, regardless of their permissions.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Recipients will see this data just as you see it, regardless of their permissions.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/enterprise.unit.spec.ts b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..c1ca8b8cf71
--- /dev/null
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/enterprise.unit.spec.ts
@@ -0,0 +1,31 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("CaveatMessage (EE without token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Recipients will see this data just as you see it, regardless of their permissions.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+
+  it("should show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Recipients will see this data just as you see it, regardless of their permissions.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/premium.unit.spec.ts b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..4302784110f
--- /dev/null
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/premium.unit.spec.ts
@@ -0,0 +1,35 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("CaveatMessage (EE with token)", () => {
+  it("should show a help link when `show-metabase-links: true`", () => {
+    setup({ showMetabaseLinks: true });
+
+    expect(
+      screen.getByText(
+        "Recipients will see this data just as you see it, regardless of their permissions.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.getByText("Learn more.")).toBeInTheDocument();
+  });
+
+  it("should not show a help link when `show-metabase-links: false`", () => {
+    setup({ showMetabaseLinks: false });
+
+    expect(
+      screen.getByText(
+        "Recipients will see this data just as you see it, regardless of their permissions.",
+      ),
+    ).toBeInTheDocument();
+    expect(screen.queryByText("Learn more.")).not.toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/setup.tsx b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/setup.tsx
new file mode 100644
index 00000000000..209b765b343
--- /dev/null
+++ b/frontend/src/metabase/sharing/components/AddEditSidebar/CaveatMessage/tests/setup.tsx
@@ -0,0 +1,32 @@
+import type { TokenFeatures } from "metabase-types/api";
+import { createMockTokenFeatures } from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { CaveatMessage } from "../CaveatMessage";
+
+export interface SetupOpts {
+  showMetabaseLinks?: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<CaveatMessage />, { storeInitialState: state });
+};
diff --git a/frontend/src/metabase/ui/components/overlays/Popover/use-popover.ts b/frontend/src/metabase/ui/components/overlays/Popover/use-popover.ts
index 8bc8bf3badd..6bd39ade120 100644
--- a/frontend/src/metabase/ui/components/overlays/Popover/use-popover.ts
+++ b/frontend/src/metabase/ui/components/overlays/Popover/use-popover.ts
@@ -112,7 +112,7 @@ export function usePopover(options: UsePopoverOptions) {
 
   return {
     floating,
-    controlled: options.opened !== null,
+    controlled: typeof options.opened === "boolean",
     opened: _opened,
     onClose,
     onToggle,
diff --git a/frontend/src/metabase/visualizations/register.js b/frontend/src/metabase/visualizations/register.js
index dafce70eef7..fb43b779240 100644
--- a/frontend/src/metabase/visualizations/register.js
+++ b/frontend/src/metabase/visualizations/register.js
@@ -18,7 +18,7 @@ import RowChart from "./visualizations/RowChart";
 import PieChart from "./visualizations/PieChart";
 import AreaChart from "./visualizations/AreaChart";
 import ComboChart from "./visualizations/ComboChart";
-import MapViz from "./visualizations/Map";
+import { Map } from "./visualizations/Map";
 import ScatterPlot from "./visualizations/ScatterPlot";
 import Funnel from "./visualizations/Funnel";
 import Gauge from "./visualizations/Gauge";
@@ -42,7 +42,7 @@ export default function () {
   registerVisualization(RowChart);
   registerVisualization(ScatterPlot);
   registerVisualization(PieChart);
-  registerVisualization(MapViz);
+  registerVisualization(Map);
   registerVisualization(Funnel);
   registerVisualization(ObjectDetail);
   registerVisualization(PivotTable);
diff --git a/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/CustomMapFooter.tsx b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/CustomMapFooter.tsx
new file mode 100644
index 00000000000..8d0d7e23d28
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/CustomMapFooter.tsx
@@ -0,0 +1,42 @@
+import { t } from "ttag";
+import Link from "metabase/core/components/Link";
+import ExternalLink from "metabase/core/components/ExternalLink";
+import { Icon } from "metabase/ui";
+import { getUserIsAdmin } from "metabase/selectors/user";
+import { useSelector } from "metabase/lib/redux";
+import { getDocsUrl } from "metabase/selectors/settings";
+import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
+import { CustomMapContent } from "../Maps.styled";
+
+export function CustomMapFooter() {
+  const isAdmin = useSelector(getUserIsAdmin);
+  const docsUrl = useSelector(state =>
+    getDocsUrl(state, { page: "configuring-metabase/custom-maps" }),
+  );
+  const showMetabaseLinks = useSelector(getShowMetabaseLinks);
+
+  const content = (
+    <CustomMapContent>
+      {t`Custom map`}
+      <Icon name="share" />
+    </CustomMapContent>
+  );
+
+  if (isAdmin) {
+    return (
+      <Link to="/admin/settings/maps" aria-label={t`Custom map`}>
+        {content}
+      </Link>
+    );
+  }
+
+  if (showMetabaseLinks) {
+    return (
+      <ExternalLink aria-label={t`Custom map`} href={docsUrl}>
+        {content}
+      </ExternalLink>
+    );
+  }
+
+  return null;
+}
diff --git a/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/index.ts b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/index.ts
new file mode 100644
index 00000000000..cf1f06c0ee3
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/index.ts
@@ -0,0 +1 @@
+export { CustomMapFooter } from "./CustomMapFooter";
diff --git a/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/common.unit.spec.ts b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/common.unit.spec.ts
new file mode 100644
index 00000000000..51447203042
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/common.unit.spec.ts
@@ -0,0 +1,60 @@
+import { screen } from "__support__/ui";
+import { setup } from "./setup";
+
+describe("CustomMapFooter (OSS)", () => {
+  describe("admin users", () => {
+    it("should show an admin settings link `show-metabase-links: true`", () => {
+      setup({ isAdmin: true, showMetabaseLinks: true });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "http://localhost/admin/settings/maps",
+      );
+    });
+
+    it("should show an admin settings link `show-metabase-links: false`", () => {
+      setup({ isAdmin: true, showMetabaseLinks: false });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "http://localhost/admin/settings/maps",
+      );
+    });
+  });
+
+  describe("non admin users", () => {
+    it("should show a help link when `show-metabase-links: true`", () => {
+      setup({ isAdmin: false, showMetabaseLinks: true });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "https://www.metabase.com/docs/latest/configuring-metabase/custom-maps.html",
+      );
+    });
+
+    it("should show a help link when `show-metabase-links: false`", () => {
+      setup({ isAdmin: false, showMetabaseLinks: false });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "https://www.metabase.com/docs/latest/configuring-metabase/custom-maps.html",
+      );
+    });
+  });
+});
diff --git a/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/enterprise.unit.spec.ts b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/enterprise.unit.spec.ts
new file mode 100644
index 00000000000..58b4b9143d8
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/enterprise.unit.spec.ts
@@ -0,0 +1,65 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({ hasEnterprisePlugins: true, ...opts });
+}
+
+describe("CustomMapFooter (EE without token)", () => {
+  describe("admin users", () => {
+    it("should show an admin settings link `show-metabase-links: true`", () => {
+      setup({ isAdmin: true, showMetabaseLinks: true });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "http://localhost/admin/settings/maps",
+      );
+    });
+
+    it("should show an admin settings link `show-metabase-links: false`", () => {
+      setup({ isAdmin: true, showMetabaseLinks: false });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "http://localhost/admin/settings/maps",
+      );
+    });
+  });
+
+  describe("non admin users", () => {
+    it("should show a help link when `show-metabase-links: true`", () => {
+      setup({ isAdmin: false, showMetabaseLinks: true });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "https://www.metabase.com/docs/latest/configuring-metabase/custom-maps.html",
+      );
+    });
+
+    it("should show a help link when `show-metabase-links: false`", () => {
+      setup({ isAdmin: false, showMetabaseLinks: false });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "https://www.metabase.com/docs/latest/configuring-metabase/custom-maps.html",
+      );
+    });
+  });
+});
diff --git a/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/premium.unit.spec.ts b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/premium.unit.spec.ts
new file mode 100644
index 00000000000..1fcb9794b28
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/premium.unit.spec.ts
@@ -0,0 +1,64 @@
+import { screen } from "__support__/ui";
+import { setup as baseSetup } from "./setup";
+import type { SetupOpts } from "./setup";
+
+function setup(opts: SetupOpts) {
+  baseSetup({
+    hasEnterprisePlugins: true,
+    tokenFeatures: { whitelabel: true },
+    ...opts,
+  });
+}
+
+describe("CustomMapFooter (EE with token)", () => {
+  describe("admin users", () => {
+    it("should show an admin settings link `show-metabase-links: true`", () => {
+      setup({ isAdmin: true, showMetabaseLinks: true });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "http://localhost/admin/settings/maps",
+      );
+    });
+
+    it("should show an admin settings link `show-metabase-links: false`", () => {
+      setup({ isAdmin: true, showMetabaseLinks: false });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "http://localhost/admin/settings/maps",
+      );
+    });
+  });
+
+  describe("non admin users", () => {
+    it("should show a help link when `show-metabase-links: true`", () => {
+      setup({ isAdmin: false, showMetabaseLinks: true });
+
+      const customMapOption = screen.getByRole("link", {
+        name: "Custom map",
+      });
+      expect(customMapOption).toBeInTheDocument();
+      expect(customMapOption).toHaveProperty(
+        "href",
+        "https://www.metabase.com/docs/latest/configuring-metabase/custom-maps.html",
+      );
+    });
+
+    it("should not show a help link when `show-metabase-links: false`", () => {
+      setup({ isAdmin: false, showMetabaseLinks: false });
+
+      expect(
+        screen.queryByRole("link", { name: "Custom map" }),
+      ).not.toBeInTheDocument();
+    });
+  });
+});
diff --git a/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/setup.tsx b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/setup.tsx
new file mode 100644
index 00000000000..3fbcdd06f7b
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/Map/CustomMapFooter/tests/setup.tsx
@@ -0,0 +1,42 @@
+import { Route } from "react-router";
+import type { TokenFeatures } from "metabase-types/api";
+import {
+  createMockTokenFeatures,
+  createMockUser,
+} from "metabase-types/api/mocks";
+import { createMockState } from "metabase-types/store/mocks";
+import { setupEnterprisePlugins } from "__support__/enterprise";
+import { mockSettings } from "__support__/settings";
+import { renderWithProviders } from "__support__/ui";
+import { CustomMapFooter } from "../CustomMapFooter";
+
+export interface SetupOpts {
+  isAdmin: boolean;
+  showMetabaseLinks: boolean;
+  hasEnterprisePlugins?: boolean;
+  tokenFeatures?: Partial<TokenFeatures>;
+}
+
+export const setup = ({
+  isAdmin,
+  showMetabaseLinks = true,
+  hasEnterprisePlugins,
+  tokenFeatures = {},
+}: SetupOpts) => {
+  const state = createMockState({
+    currentUser: createMockUser({ is_superuser: isAdmin }),
+    settings: mockSettings({
+      "show-metabase-links": showMetabaseLinks,
+      "token-features": createMockTokenFeatures(tokenFeatures),
+    }),
+  });
+
+  if (hasEnterprisePlugins) {
+    setupEnterprisePlugins();
+  }
+
+  renderWithProviders(<Route path="*" component={CustomMapFooter} />, {
+    storeInitialState: state,
+    withRouter: true,
+  });
+};
diff --git a/frontend/src/metabase/visualizations/visualizations/Map.jsx b/frontend/src/metabase/visualizations/visualizations/Map/Map.jsx
similarity index 90%
rename from frontend/src/metabase/visualizations/visualizations/Map.jsx
rename to frontend/src/metabase/visualizations/visualizations/Map/Map.jsx
index f56aafee57a..b0c1a7f9e3a 100644
--- a/frontend/src/metabase/visualizations/visualizations/Map.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/Map/Map.jsx
@@ -1,14 +1,9 @@
 /* eslint-disable react/prop-types */
 import { Component } from "react";
-import { connect } from "react-redux";
 import { t } from "ttag";
 import _ from "underscore";
 import { ChartSettingsError } from "metabase/visualizations/lib/errors";
 
-import Link from "metabase/core/components/Link";
-import ExternalLink from "metabase/core/components/ExternalLink";
-import { Icon } from "metabase/ui";
-
 import { isSameSeries } from "metabase/visualizations/lib/utils";
 import {
   metricSetting,
@@ -18,7 +13,6 @@ import {
 import { columnSettings } from "metabase/visualizations/lib/settings/column";
 
 import MetabaseSettings from "metabase/lib/settings";
-import { getUserIsAdmin } from "metabase/selectors/user";
 
 const PIN_MAP_TYPES = new Set(["pin", "heat", "grid"]);
 
@@ -37,15 +31,14 @@ import {
   isState,
   isCountry,
 } from "metabase-lib/types/utils/isa";
-import LeafletGridHeatMap from "../components/LeafletGridHeatMap";
-import PinMap from "../components/PinMap";
+import LeafletGridHeatMap from "../../components/LeafletGridHeatMap";
+import PinMap from "../../components/PinMap";
 import ChoroplethMap, {
   getColorplethColorScale,
-} from "../components/ChoroplethMap";
-
-import { CustomMapContent } from "./Maps.styled";
+} from "../../components/ChoroplethMap";
+import { CustomMapFooter } from "./CustomMapFooter";
 
-export default class Map extends Component {
+export class Map extends Component {
   static uiName = t`Map`;
   static identifier = "map";
   static iconName = "pinmap";
@@ -357,28 +350,3 @@ export default class Map extends Component {
     }
   }
 }
-
-const mapStateToProps = (state, props) => {
-  return {
-    isAdmin: getUserIsAdmin(state, props),
-  };
-};
-
-const CustomMapFooter = connect(mapStateToProps)(function CustomMapFooter({
-  isAdmin,
-}) {
-  const content = (
-    <CustomMapContent>
-      {t`Custom map`}
-      <Icon name="share" />
-    </CustomMapContent>
-  );
-
-  return isAdmin ? (
-    <Link to="/admin/settings/maps">{content}</Link>
-  ) : (
-    <ExternalLink href="https://www.metabase.com/docs/latest/configuring-metabase/custom-maps">
-      {content}
-    </ExternalLink>
-  );
-});
diff --git a/frontend/src/metabase/visualizations/visualizations/Maps.styled.tsx b/frontend/src/metabase/visualizations/visualizations/Map/Maps.styled.tsx
similarity index 100%
rename from frontend/src/metabase/visualizations/visualizations/Maps.styled.tsx
rename to frontend/src/metabase/visualizations/visualizations/Map/Maps.styled.tsx
diff --git a/frontend/src/metabase/visualizations/visualizations/Map/index.ts b/frontend/src/metabase/visualizations/visualizations/Map/index.ts
new file mode 100644
index 00000000000..f774b75d21c
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/Map/index.ts
@@ -0,0 +1 @@
+export { Map } from "./Map";
diff --git a/frontend/src/types/global.d.ts b/frontend/src/types/global.d.ts
index 94f716aac6e..682c997e8f6 100644
--- a/frontend/src/types/global.d.ts
+++ b/frontend/src/types/global.d.ts
@@ -10,3 +10,10 @@ declare module "*.svg" {
   // eslint-disable-next-line import/no-default-export -- deprecated usage
   export default content;
 }
+
+// This allows importing CSS from TypeScript files
+declare module "*.css" {
+  const content: any;
+  // eslint-disable-next-line import/no-default-export -- deprecated usage
+  export default content;
+}
diff --git a/resources/sample-database.db.mv.db b/resources/sample-database.db.mv.db
index f8bac250c3c4f22f3c2a35eac556c7e8105f0d50..1ac5a197074e605ff32a85554d209e76ac50780e 100644
GIT binary patch
delta 14769
zcmeG@TZkN2c0Dcas5Nq1UTN*sV<cZ&vSoRDO7-mSX-K5e%vd{E(hQ@qWNW<|->SMb
zQ=aasc2~7V?M)~Vl3J3FENg=s{IM+LK|Vsj5R$LJLOwQue1wpMB!q=HumtiGmOy?I
z$T{~`)l~QNNHbo|ngtC)x9;n_&$*}WsebswzaM+}qkkAX|ILP4F`K@%(a;P{Jl%3X
zWDP}@<%+f5^)?#D_s?JYnpSa|EVR~Hr(xABd)lch6Q6zXy`$fMIPuYQ6Q{oL=HDPO
z8H-6VCI@12FeYD#$#_f-#iSII!!enN$&r{m6O(dGo{h=Tm^>Ggug2t9OpeFoYcY8~
zCNIR~>oGYIlanzy6_e93nT*Mqn4FEti!lNCFU92Ln4F8r`Ix*ClUHMMAttZI<YG)-
zkI6S;Qi;h_Or)4xiisQ(B_?W2v_B;Je|>W0_&C!u2`QB(^FU%P+HSBqG!Gi8qRADH
zJqWn~+jNZu4WrWW{gBI0mUWp<(@N;JSVME5rznh7?z2wd`d&j<E1_>S6jT2AJ@Upg
z6NN(Q&C>rB{%Pz?q5s{(lcfJghbPZQO37fRTCdlgQk61;(wbVTP1j6n+gho{<eF_N
zPN}XamR#56Qr)ai%NEn{fil&w@xe5zriBld=F}*E&@~N49I6_ste2*%OmV7~RidV5
z8P%FnqLwbJvT6TH$#iU{))cE`$!3+BjFqe^)v8v_DA`KAuGcNS#9&-jYkJAi^_r|t
z*GrC}!}@+}Vv<D7=55;SvN;;km0p`w{LY%Ru1c-@LFjjw6h4q{3z0CJFT_RX-=G%E
z_FfrUY@5=uznOwcR~;%9(!X=tHbm0R?*5Z7erP8OBB1#fLBYYV2nvQp7KI>B>1r=z
zGo23YrLx-xH53mZ1;ZK`$^Y=8Q8L=inq6=a?bVtNgDYl#!_pjWfW=~=SQs0<HcBO)
zTI+bRSRC77@$lM1dsusT^x9~o%i!YT7#Z>wZYtM(Z!M)`7xLW_?`VZZKZpB|hPXF?
z5z7aZCjoJ90HeLeyeCXzwA(0JI-FU3VNZG=ZTG1OMXBAFpWTAbpVT4i1K+yto3|kD
z!%=#Jw$EQ}`sOloSciEQJCACd+Rc_5ETnuj=OTKtXU%h89pVDIkCaw~hu>aJ5P+U5
z{mU5q6$<dL_{|r1d*D0AUYHz*6hvaxLgC=wl>ehR_CkRVSPpM&Su~1hwDDF1t8HC$
z4bfCZQxi>HG}EG?qA^8diN+QU6OF*c1STdhF_q6TK#0@{e9U;oqlY2;5qj+Z+rJ+>
z9eET6LTWadr0Nx`!)VBo1fZc*HdwEb<|UQ;wAm1uM9r?{_tl2>soVEys##@*rU3<l
z05B+A6%Bz;GM0-1JZrVV1e2wMD~)MdrE*OXgt(r~9!Oesh!9N>0;v{eSe$`0m3mbW
z68R>2#&&`S;R^vlemN+hM=(0h;jaobV!J#7OH`LNQ*#nF5;WQnnuLucKjdsEhHX$;
zVR=0$>d?Ls`b)FLbYFyuSB%GROmo_FBi~nl{JvE2c|ECHghnF!PAE;%<pD+IH|oO{
zs;3I(+4aD_sxTpL+0#4H8DYlF_oeCc0KQ>^dIY|KeZz*5?c>RRj?TzTTuE3kyoC%}
zShkVBEaWzt_Vb)Uzs<ar4)p@a(BVBM$djQB>nHPvOk7bTbqEM+zk2i^4~$KU0LDaA
zVj?QBI=5$OwTwt5L9D9gds=m%SI_r!d7ziu7d?=4ePFWs3`YSm3LW!E+JPXDJ;Ncy
znU{u{7h4l=6HrA*Nl+#|Bb$rR{1aNmq^e|-JSGnGYWbd)a3%I>S|0oKQBHW6MX+ai
zzw6olb$?A#((XCYSB2VA3I>g!|KRuZ$>>D{(8vG`E@|k>;QTf_Qi^uHXtSbi6dC+e
zi%cupPLaWQwP<Ta3!e3&JzccTqHPszyU1W%F50l5R<vnUWYE!ztX5?8LSf>cPyNrq
z!l}X`elsB{NvkHUcBloXzU12D`2G#kys;e7+H%q+<F=Ocjijw6Z7pf*hdS#B$DU3G
zXwsTVYbC9nv@B^8ge*bG5`;_@^DIHk62z?fC*vQH6O&E%K4d%(8j9g4s$9YD62DKN
zy7uwciP8Uy-`Ags@Y+CgvKs_=<TnVa1?ODFx2$fPdRDIi_c#0U7wrqw-2;7!fA_N5
zkCj(`9-vD7&kn+s+NsRcRhem!-)xzB)u{JRUO8?=U9TH3dnQb?LS4yBUYCW*Pg8x`
z-Y1jaUo)#eR*3tH|FP)hrN=MX!;0z;4}C(WPM%I9T-}oGyw;)HKS8GSM`~T+TCdfs
zs<BT>m%nVOKgGWXquPh1zr^xqvaF0LN3d=q%c|z++Jp|p2WnQWY5s`A)G}~XwrIU7
zJNT$jxW3oLqbWy*V=FkUD!OXGFa#<^HsontOCZatHBE6?)l^o4A`pKx97`-#jjCL)
z*Z61^UKI_#z_2w#SNLPKs#rQu0o$romDX%+!a|A#ZTIlmuxgschZR+`b)XYQ6}48E
zfi`@sQ_AXM)V5?zR|*BV2q{!Ph5xP;ikD|ai`OO4hwBn(;i6>X)6YJ5>)=OZ{Iz^R
zCs&EdDqN-sr3_}U{%4nu-|YYI<>M2Mk=1}SUIWqwYCyGWRs*V525UelZdrG9BVPkj
zDcj26bWI#*j$wXG$^0bVJl6^j3qRo(KZS?m^zZ%_E`F{Q3XSFdr5{($9xnc6@zU5&
z;GL<j{J3(ep)W7ZFD_l5mn?c8E>eOcx0Alj0;}V;@eNJ>b~4uXT`vq)eP<Qg{_p;E
z<wPXq)}6MNmKW#Vnq9di%`VT+tjy1?&aC7YOum~fn4h_spE>ibY-V9DKXLY6HnFt4
zFgu^0dvSFsN)ay4tSsETmY*eCJ69G~fWsW6FRf-!Z_nSHTU^dhsw>%KB9xPFXCpZv
z5&(%3eg`B2*Ns7V?%n12+Y9q|Zb?@c=ibS~fBoLTOu$;y3tej!@6OMg>D*`uzH&F6
z7h;N19le@uOh{cFl;edA>2^uI_Ff89wDSYvFm9!@i8NlmlMdJ{%8W$G5Nwyl)SJoH
zZ!G5ISGm1?B|6*L1Akupsj%N4js5G96T*If_Jb1_PZkQ#9XkG32Yxzsv2gO#>B%!^
z`xnkm9_#<{vB^?@^%I>OzOmq0%`PD6|2A|Za8MHXO`DiK60S4izzeBOZqZh|$;hnV
zg4dm%L^I{ig3VrATprL?`Nj%tMBNbIA?=QDcdf8O;AYXa@&7Ihh%1mb$ei!5yVgdp
zLatGJjYY8Vn)_kPC)Bga9T$ee$N5NELVPydX>y7agk+Nj1WL9*edrUv+aVTQH8kP9
zd`FimzqO(QB=A($@hJK+Z4%l6O9S4$Zp&mHiOdj1BeuKdhRIO>dwu=+D34m&$u-Lx
zNGU*K9oGWbDXA&!MX%W-n24t|G@CuZ*lZH0cq2_sV)?CB*Mr?i9tqa{F6gQoM1uFw
z4S`g@LoirudGoe7Q2uo!r0E7B(BN8JQ6Y>Z1WDwk&!tI2;L8Pd1;q11LYtek7bL9W
zwro#ig~DtcKyG(o#s?t<mfwZ&S3$TfpI=>72q+ZeKoSPF`&RiH(gv{=OA@35jCyM*
zlukg_VU(=Fi@LXk*$O4HvK|42a6ty20}uyr5rs?<V@m+r!ddw;BEj1hw9$+W-~y}$
zRGSFa1iNyBc9Zsm8ChK@po8{;90xocMgsxh9s&=*=C(^V7YVXJP`<Lv?z?OgB?}TC
zbT*RV6zn&d6B1B8T6ChWKzcVy1I%#fEE`Bmhr{Q5q9h3dk;rwo<%WVW6^>0}t<w(N
zFd}<dY#bd_%Q8Z>X^*=pd~E^tM^h~5c$*zJ3_(p0MJQLEG!%x@C|rJXhJZbP049_8
zU6Z&rgYfRSKq#6MYDX|2kz2yx(HKED+*P=1K#2o=B_n%uNH>w{)akas@=yVovUohg
zD5r31VvMhW_$dj%gSqu6Q^rN%|68(s<oWO(!s2T;*L|*w9FOlNnZw<MC28QGTf&HX
z>8{(J^=+1s%Jm(vD0d>R@P%_**+~8fDNDD&fFYiUhz?Pcav}HpF8VB*I)prMTbpdU
z;kq>9#3|eFz+#Jf#DP0v2ntOSM1Cg>(3Nc0;l;y{ybq}lUd$7I;VIk`U77o5YPlRI
z^YL&2>_W@n=a3Ib=?Pf=4x)n%CP3>E+xISnpfL|ZlaEpa*||ZKGSMMsH*h@`1XEyQ
z5V|phgK_i%264#jh5#H68#lZV5a78(HvLZ1u8>W3p~J}g;NK7y?<1YA2di>^zWwOE
zvw*SUG6DKP{|w~N4PN^8;_}@5@-69+%ePjK$`g4ulmniv&a}P_codFWzb!2*-_4n^
z<u|)6U=of=_#Y0vHFP*0J*Nz^iAsPt3ez?e3~i^2g0zW&Bj`IOq--dA5EZ*0!O^&!
zaGqo?<#uW&XbD_P7`JfilE?`Vu}u~`HpX^tB_KeAhiew*z`R(~4kR*{93+UDhd&R#
zE+VcY&ZKs`=~@vl0S#s5<*QrA7~Ih}{T_q!0db}=l?2^(8%&o3A$1(Ec!0|f8ET26
zOBzn)${mm(7+q?r6bj{Fk5T9*iH6`N@bMG@dfR>wxab9KVdWgU*bE!C+yJZ+e8=W2
zwc+4cr+|bjqZ>$X&&<v)zI7Ac5^~94`8UTFJcwE?Oa&r15WFq6Qu2Y!H8|u!lfs?J
zlFNcNwU|WaskKg$b+D`r88}2;5D)R#CVr$sJ@>;T%W&5`R7<zbz0CKfY|Oxce1Imc
zAaN0=F4g>}V=11`3nzojBMFppAcTSrFth-saMK6JV;dywxR?$>TIAajS#Z!bNx;yz
z;XwSnFgXji?5qh~%!Hp8&Y)nq4S=DbMbR+ISb?b^6nbp8T?iO$+C$=#lf;En6RCcC
z;pXi{csm*tsW<C;j@!YM7q+#yK)4bH_(`n?O!I~hP5@Gjc%p-w1`&aQnb|pyDx#$2
zd*M3JWzxU}>1A}J!uz$<ZPp+RxA6o8{%3?ef>$8>5cp6y7<tiE0Be+^6}&msD=Mk$
z-DfViOJ*w`M7Rx%w~!7K+Cs_!1lS&c#VlG{&1l*dXTtL93nJ*<VW3+e0KQWNZAD!?
zkCB-7yO6Vsq>4iL%q(};pmv0GbLRRxD+{x$^N?{3xx$}DnZ>~Y0R|S}AxAym>j8i0
z^Kb&dVE|zUGMo*x2hhd3zsZkbU<gxwNPUE10vl*1O&X$mVS34Wz%1mRJ|^hv6hs9d
zn4-K~d@lmoM_>iS0mosGoMOg{Ck-0$h6hLx5f29vcR)<y!MWW|5K85jXNLItLV}8N
zGXK^v#MH7jQY!`)0%D;gz|aLN9Z0R46_6X*V5|)SAuT-Ix&Lut3dTqaEL=p$CY}n|
zX+w270zf>-17F>+3&;1{Z_LlnU7eYIb0B7gchK`0#vUIdC4WN%e*}95EA+eY`j-)z
zydz>r0$o5VjV_$|9(RJS2^lQNoxvB6zzvV>BDkV0f)Y6rX*Hwe3kf$T+m#GbCR8|M
z2*dYgNSqQc1%jGrR8|u*5$;zI1`<j+p=eVSA{hin%lDhy#5(9uY4G}>3z>OhQV^Nj
zQ1k~O@w})|IPyCu?j0<g&evt(Dt|-TD$I(4t^ULdv^&;k7qM{rv_OlukH6TzU*bFT
zCnBgXoBt<XpjB%5QeD-k?ogmrpB8AJhym>m$kOhX?X(KZHo3WN1zJ-#^IFH>af~j|
z=CuB_K%0~3{ycu!!R0CbFRWJwe){zXW8x=u`2wW)MOpe~gklVQ4x<m085J$c>Wtb@
zosrWu^0#K&zNE>yZ1(fB{HJ4zykj9b;^pI{TKlL<@;()kBT-2{qVTdR6r<}qm6G>R
zNlu|LQ-)G<?IDzse@nED<w9W^`u_=^-(&kT{RdFof?D!_--RDW=C%0PGV(z}2>fF5
c;ZZC==c~!t&fHHdZPj!X_zaL2p!v@K2dX&_asU7T

delta 284
zcmbWvJ2C?S0D$3H?`*K~CSE_*J7lv-40{5-6R<JE#4rktDx>&Y6OBTn(z}5}a1TeI
za01=me1GTTE9ks_2Jvy0R?e<&@^+TC{_5oN{yeKCNm9u#?ylQey&G4bU!rEQaM(O(
z9JNmK@cs5N>UP4XQds^6dq4u|kzVPOei@KK8Ipp8Qj}pCkx_}HBx5oz6EZ1NGA%PQ
wD|1qod0CJ}S(0V>{jbQXtVt~EvLTzYCEKziyHb%o*_Wy$Qj=8bZ#2Gt0Ph@7ivR!s

diff --git a/src/metabase/public_settings.clj b/src/metabase/public_settings.clj
index 29c54bba3b6..437e8066531 100644
--- a/src/metabase/public_settings.clj
+++ b/src/metabase/public_settings.clj
@@ -471,7 +471,6 @@
     "Keyword setting to control whitelabeling of the help link. Valid values are `:metabase`, `:hidden`, and "
     "`:custom`. If `:custom` is set, the help link will use the URL specified in the `help-link-custom-destination`, "
     "or be hidden if it is not set."))
-  :default    :default
   :type       :keyword
   :audit      :getter
   :visibility :public
@@ -507,6 +506,14 @@
                  (validate-help-url new-value-string)
                  (setting/set-value-of-type! :string :help-link-custom-destination new-value-string))))
 
+(defsetting show-metabase-links
+  (deferred-tru (str "Whether or not to display Metabase links outside admin settings."))
+  :type       :boolean
+  :default    true
+  :visibility :public
+  :audit      :getter
+  :feature    :whitelabel)
+
 (defsetting enable-password-login
   (deferred-tru "Allow logging in by email and password.")
   :visibility :public
diff --git a/test/metabase/public_settings_test.clj b/test/metabase/public_settings_test.clj
index 963c48715be..4e0ea383c14 100644
--- a/test/metabase/public_settings_test.clj
+++ b/test/metabase/public_settings_test.clj
@@ -216,30 +216,31 @@
                  (public-settings/start-of-week))))))))
 
 (deftest help-link-setting-test
-  (mt/with-premium-features #{:whitelabel}
-    (testing "When whitelabeling is enabled, help-link setting can be set to any valid value"
-      (public-settings/help-link! :metabase)
-      (is (= :metabase (public-settings/help-link)))
+  (mt/discard-setting-changes [help-link]
+    (mt/with-premium-features #{:whitelabel}
+      (testing "When whitelabeling is enabled, help-link setting can be set to any valid value"
+        (public-settings/help-link! :metabase)
+        (is (= :metabase (public-settings/help-link)))
 
-      (public-settings/help-link! :hidden)
-      (is (= :hidden (public-settings/help-link)))
+        (public-settings/help-link! :hidden)
+        (is (= :hidden (public-settings/help-link)))
 
-      (public-settings/help-link! :custom)
-      (is (= :custom (public-settings/help-link))))
+        (public-settings/help-link! :custom)
+        (is (= :custom (public-settings/help-link))))
 
-    (testing "help-link cannot be set to an invalid value"
-      (is (thrown-with-msg?
-           Exception #"Invalid help link option"
-           (public-settings/help-link! :invalid)))))
+      (testing "help-link cannot be set to an invalid value"
+        (is (thrown-with-msg?
+             Exception #"Invalid help link option"
+             (public-settings/help-link! :invalid)))))
 
-  (mt/with-premium-features #{}
-    (testing "When whitelabeling is not enabled, help-link setting cannot be set, and always returns :metabase"
-      (is (thrown-with-msg?
-           clojure.lang.ExceptionInfo
-           #"Setting help-link is not enabled because feature :whitelabel is not available"
-           (public-settings/help-link! :hidden)))
+    (mt/with-premium-features #{}
+      (testing "When whitelabeling is not enabled, help-link setting cannot be set, and always returns :metabase"
+        (is (thrown-with-msg?
+             clojure.lang.ExceptionInfo
+             #"Setting help-link is not enabled because feature :whitelabel is not available"
+             (public-settings/help-link! :hidden)))
 
-      (is (= :metabase (public-settings/help-link))))))
+        (is (= :metabase (public-settings/help-link)))))))
 
 (deftest validate-help-url-test
   (testing "validate-help-url accepts valid URLs with HTTP or HTTPS protocols"
@@ -350,3 +351,22 @@
            Exception
            #"This field must be a relative URL."
            (public-settings/landing-page! "file:///path/to/resource")))))
+
+(deftest show-metabase-links-test
+  (mt/discard-setting-changes [show-metabase-links]
+    (mt/with-premium-features #{:whitelabel}
+      (testing "When whitelabeling is enabled, show-metabase-links setting can be set to boolean"
+        (public-settings/show-metabase-links! true)
+        (is (= true (public-settings/show-metabase-links)))
+
+        (public-settings/show-metabase-links! false)
+        (is (= false (public-settings/show-metabase-links)))))
+
+    (mt/with-premium-features #{}
+      (testing "When whitelabeling is not enabled, show-metabase-links setting cannot be set, and always returns true"
+        (is (thrown-with-msg?
+            clojure.lang.ExceptionInfo
+            #"Setting show-metabase-links is not enabled because feature :whitelabel is not available"
+            (public-settings/show-metabase-links! true)))
+
+        (is (= true (public-settings/show-metabase-links)))))))
-- 
GitLab