From 279b372136cca86100a874c39a844bb8116ee025 Mon Sep 17 00:00:00 2001 From: "Mahatthana (Kelvin) Nomsawadi" <me@bboykelvin.dev> Date: Tue, 13 Feb 2024 22:29:31 +0700 Subject: [PATCH] Add ESLint rule for Metabase strings (#38553) * Fix case where Metabase links are render inside template strings * Add `no-literal-metabase-strings` ESLint rule * Fix all Metabase string errors * Address review: Fix the rule The rule was checking if we have imported the selector `getApplicationName` then ignore all Metabase strings. This is different than `no-unconditional-metabase-links-render` because in that rule, when fixed the Documentation URLs are still in the file, but in this rule, when fixed, there should be no Metabase strings left in the file. * Fix errors from the new lint rule --- .eslintrc | 16 +- .../ImpersonationModalView.tsx | 9 +- .../ImpersonationWarning.tsx | 3 + .../audit_app/components/OpenInMetabase.jsx | 1 + .../UnsubscribeUserForm.jsx | 1 + .../audit_app/containers/AuditApp.jsx | 1 + .../SettingsSAMLForm/SettingsSAMLForm.jsx | 4 + .../DatabaseCacheTimeField.tsx | 1 + .../CollectionInstanceAnalyticsIcon.tsx | 1 + .../EmbeddingAppSameSiteCookieDescription.tsx | 1 + .../metabase-enterprise/embedding/index.js | 1 + .../SettingsCloudStoreLink.tsx | 2 + .../BillingInfo/BillingGoToStore.tsx | 2 + .../settings/hooks/use-license.ts | 1 + .../metabase-enterprise/settings/selectors.ts | 1 + .../HelpLinkSettings/HelpLinkSettings.tsx | 1 + .../MetabaseLinksToggleDescription.tsx | 2 + .../no-literal-metabase-strings.js | 97 +++++++++++ .../no-unconditional-metabase-links-render.js | 5 +- .../no-literal-metabase-strings.unit.spec.js | 154 ++++++++++++++++++ ...itional-metabase-links-render.unit.spec.js | 26 +++ .../expressions/helper-text-strings.ts | 1 + .../src/metabase-types/api/mocks/settings.ts | 1 + .../DatabaseCacheScheduleField.tsx | 1 + .../DatabaseEngineWarning.tsx | 2 + .../DatabaseNameField/DatabaseNameField.tsx | 1 + .../DatabaseSyncModal/DatabaseSyncModal.tsx | 3 +- .../EmbedMinimalHomepage.tsx | 1 + frontend/src/metabase/lib/api.js | 2 + frontend/src/metabase/lib/i18n.js | 1 + .../components/AdminNavbar/AdminNavbar.tsx | 1 + .../DatabasePromptBanner.tsx | 1 + .../components/ProfileLink/ProfileLink.jsx | 2 + .../WhatsNewNotification.tsx | 1 + frontend/src/metabase/plugins/index.ts | 1 + .../components/EmbedFrame/LogoBadge.tsx | 1 + .../InteractiveEmbeddingCTA.tsx | 1 + .../AppearanceSettings.tsx | 2 + .../widgets/LegaleseStep/LegaleseStep.tsx | 1 + frontend/src/metabase/redux/settings.ts | 1 + frontend/src/metabase/route-guards.jsx | 1 + .../PreferencesStep/PreferencesStep.tsx | 1 - .../setup/components/SetupHelp/SetupHelp.tsx | 1 - 43 files changed, 348 insertions(+), 10 deletions(-) create mode 100644 frontend/lint/eslint-rules/no-literal-metabase-strings.js create mode 100644 frontend/lint/tests/no-literal-metabase-strings.unit.spec.js diff --git a/.eslintrc b/.eslintrc index a8829f738b2..02c473f2a6e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -161,13 +161,23 @@ { "files": ["*.js", "*.jsx", "*.ts", "*.tsx"], "rules": { - "no-unconditional-metabase-links-render": "error" + "no-unconditional-metabase-links-render": "error", + "no-literal-metabase-strings": "error" } }, { - "files": ["*.unit.spec.*", "frontend/src/metabase/admin/**/*"], + "files": [ + "*.unit.spec.*", + "frontend/src/metabase/admin/**/*", + "frontend/src/metabase/setup/**/*", + "frontend/lint/**/*", + "*.stories.*", + "e2e/**/*", + "**/tests/*" + ], "rules": { - "no-unconditional-metabase-links-render": "off" + "no-unconditional-metabase-links-render": "off", + "no-literal-metabase-strings": "off" } }, { diff --git a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx index b4ab3900eb8..72fa81b736b 100644 --- a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx +++ b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx @@ -74,12 +74,15 @@ export const ImpersonationModalView = ({ const impersonationUsesUsers = database.engine === "redshift"; const modalTitle = impersonationUsesUsers - ? t`Map a Metabase user attribute to database users` + ? // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings + t`Map a Metabase user attribute to database users` : t`Map a user attribute to database roles`; const modalMessage = impersonationUsesUsers - ? t`When the person runs a query (including native queries), Metabase will impersonate the privileges of the database user you associate with the user attribute.` - : t`When the person runs a query (including native queries), Metabase will impersonate the privileges of the database role you associate with the user attribute.`; + ? // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings + t`When the person runs a query (including native queries), Metabase will impersonate the privileges of the database user you associate with the user attribute.` + : // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings + t`When the person runs a query (including native queries), Metabase will impersonate the privileges of the database role you associate with the user attribute.`; return ( <ImpersonationModalViewRoot> diff --git a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx index 93c4ffc375f..3380a76ea0b 100644 --- a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx +++ b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx @@ -16,8 +16,10 @@ export const ImpersonationWarning = ({ const databaseUser = database.details.user; const isRedshift = database.engine === "redshift"; + // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings const emptyText = t`Make sure the main database credential has access to everything different user groups may need access to. It's what Metabase uses to sync table information.`; + // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings const redshiftWarning = jt`You’re connecting Metabase to the ${( <BoldCode key="2" size={13}> {database.name} @@ -32,6 +34,7 @@ export const ImpersonationWarning = ({ </BoldCode> )} must be a superuser in Redshift.`; + // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings const regularWarning = jt`${( <BoldCode key="1" size={13}> {databaseUser} diff --git a/enterprise/frontend/src/metabase-enterprise/audit_app/components/OpenInMetabase.jsx b/enterprise/frontend/src/metabase-enterprise/audit_app/components/OpenInMetabase.jsx index 322daa510cb..9f376b7c61e 100644 --- a/enterprise/frontend/src/metabase-enterprise/audit_app/components/OpenInMetabase.jsx +++ b/enterprise/frontend/src/metabase-enterprise/audit_app/components/OpenInMetabase.jsx @@ -6,6 +6,7 @@ import { Icon } from "metabase/ui"; const OpenInMetabase = ({ ...props }) => ( <Link {...props} className="link flex align-center" target="_blank"> <Icon name="external" className="mr1" /> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`Open in Metabase`} </Link> ); diff --git a/enterprise/frontend/src/metabase-enterprise/audit_app/components/UnsubscribeUserForm/UnsubscribeUserForm.jsx b/enterprise/frontend/src/metabase-enterprise/audit_app/components/UnsubscribeUserForm/UnsubscribeUserForm.jsx index 38cd53f6512..c5751ff1ac4 100644 --- a/enterprise/frontend/src/metabase-enterprise/audit_app/components/UnsubscribeUserForm/UnsubscribeUserForm.jsx +++ b/enterprise/frontend/src/metabase-enterprise/audit_app/components/UnsubscribeUserForm/UnsubscribeUserForm.jsx @@ -42,6 +42,7 @@ const UnsubscribeUserForm = ({ user, onUnsubscribe, onClose }) => { {t`This will delete any dashboard subscriptions or alerts ${user.common_name} has created, and remove them as a recipient from any other subscriptions or alerts.`} </ModalMessage> <ModalMessage> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`This does not affect email distribution lists that are managed outside of Metabase.`} </ModalMessage> </ModalContent> diff --git a/enterprise/frontend/src/metabase-enterprise/audit_app/containers/AuditApp.jsx b/enterprise/frontend/src/metabase-enterprise/audit_app/containers/AuditApp.jsx index 1c362062f09..b5a16d00ddc 100644 --- a/enterprise/frontend/src/metabase-enterprise/audit_app/containers/AuditApp.jsx +++ b/enterprise/frontend/src/metabase-enterprise/audit_app/containers/AuditApp.jsx @@ -39,6 +39,7 @@ const DeprecationSection = () => { key="link" to={`/collection/${auditCollection.id}`} > + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`Metabase Analytics Collection`} </Link> )} diff --git a/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx b/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx index d9f8782cbff..2679e27c2f3 100644 --- a/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx +++ b/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx @@ -89,6 +89,7 @@ const SettingsSAMLForm = ({ elements = [], settingValues = {}, onSubmit }) => { </SAMLFormCaption> <SAMLFormSection> <h3 className="mb0">{t`Configure your identity provider (IdP)`}</h3> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <p className="mb4 mt1 text-medium">{t`Your identity provider will need the following info about Metabase.`}</p> <FormTextInput @@ -123,7 +124,9 @@ const SettingsSAMLForm = ({ elements = [], settingValues = {}, onSubmit }) => { </SAMLFormSection> <SAMLFormSection> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <h3 className="mb0">{t`Tell Metabase about your identity provider`}</h3> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <p className="mb4 mt1 text-medium">{t`Metabase will need the following info about your provider.`}</p> <Stack gap="md"> <FormTextInput @@ -173,6 +176,7 @@ const SettingsSAMLForm = ({ elements = [], settingValues = {}, onSubmit }) => { <SAMLFormSection wide> <h3 className="mb0">{t`Synchronize group membership with your SSO`}</h3> <p className="mb4 mt1 text-medium"> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`To enable this, you'll need to create mappings to tell Metabase which group(s) your users should be added to based on the SSO group they're in.`} </p> diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTimeField/DatabaseCacheTimeField.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTimeField/DatabaseCacheTimeField.tsx index 05f74e0564a..0fbdf132b91 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTimeField/DatabaseCacheTimeField.tsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTimeField/DatabaseCacheTimeField.tsx @@ -46,6 +46,7 @@ const DatabaseCacheTimeField = () => { const DatabaseCacheTimeDescription = (): JSX.Element => { return ( <div> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {jt`How long to keep question results. By default, Metabase will use the value you supply on the ${( <Link key="link" to="/admin/settings/caching"> {t`cache settings page`} diff --git a/enterprise/frontend/src/metabase-enterprise/collections/components/CollectionInstanceAnalyticsIcon.tsx b/enterprise/frontend/src/metabase-enterprise/collections/components/CollectionInstanceAnalyticsIcon.tsx index ff8a51f4635..efc3eb28e74 100644 --- a/enterprise/frontend/src/metabase-enterprise/collections/components/CollectionInstanceAnalyticsIcon.tsx +++ b/enterprise/frontend/src/metabase-enterprise/collections/components/CollectionInstanceAnalyticsIcon.tsx @@ -32,6 +32,7 @@ export function CollectionInstanceAnalyticsIcon({ <Icon {...iconProps} name={collectionType.icon} + // eslint-disable-next-line no-literal-metabase-strings -- Metabase analytics tooltip={t`This is a read-only Metabase Analytics ${collectionIconTooltipNameMap[entity]}.`} data-testid="instance-analytics-collection-marker" /> diff --git a/enterprise/frontend/src/metabase-enterprise/embedding/components/EmbeddingAppSameSiteCookieDescription/EmbeddingAppSameSiteCookieDescription.tsx b/enterprise/frontend/src/metabase-enterprise/embedding/components/EmbeddingAppSameSiteCookieDescription/EmbeddingAppSameSiteCookieDescription.tsx index 2a984dc38fa..51171d2ef98 100644 --- a/enterprise/frontend/src/metabase-enterprise/embedding/components/EmbeddingAppSameSiteCookieDescription/EmbeddingAppSameSiteCookieDescription.tsx +++ b/enterprise/frontend/src/metabase-enterprise/embedding/components/EmbeddingAppSameSiteCookieDescription/EmbeddingAppSameSiteCookieDescription.tsx @@ -30,6 +30,7 @@ export const EmbeddingAppSameSiteCookieDescription = () => { return ( <Stack spacing="sm"> {shouldDisplayNote && <AuthorizedOriginsNote />} + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <Text>{t`Determines whether or not cookies are allowed to be sent on cross-site requests. You’ll likely need to change this to None if your embedding application is hosted under a different domain than Metabase. Otherwise, leave it set to Lax, as it's more secure.`}</Text> <Text>{jt`If you set this to None, you'll have to use HTTPS (unless you're just embedding locally), or browsers will reject the request. ${( <ExternalLink key="learn-more" href={docsUrl}> diff --git a/enterprise/frontend/src/metabase-enterprise/embedding/index.js b/enterprise/frontend/src/metabase-enterprise/embedding/index.js index e262478a9be..a9909fa5f43 100644 --- a/enterprise/frontend/src/metabase-enterprise/embedding/index.js +++ b/enterprise/frontend/src/metabase-enterprise/embedding/index.js @@ -45,6 +45,7 @@ if (hasPremiumFeature("embedding")) { { value: "strict", name: t`Strict (not recommended)`, + // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings description: t`Never allows cookies to be sent on a cross-site request. Warning: this will prevent users from following external links to Metabase.`, }, { diff --git a/enterprise/frontend/src/metabase-enterprise/hosting/components/SettingsCloudStoreLink/SettingsCloudStoreLink.tsx b/enterprise/frontend/src/metabase-enterprise/hosting/components/SettingsCloudStoreLink/SettingsCloudStoreLink.tsx index 3f06f40461c..5364e7251dd 100644 --- a/enterprise/frontend/src/metabase-enterprise/hosting/components/SettingsCloudStoreLink/SettingsCloudStoreLink.tsx +++ b/enterprise/frontend/src/metabase-enterprise/hosting/components/SettingsCloudStoreLink/SettingsCloudStoreLink.tsx @@ -7,8 +7,10 @@ export const SettingsCloudStoreLink = () => { return ( <div> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <Description>{t`Manage your Cloud account, including billing preferences and technical settings about this instance in your Metabase Store account.`}</Description> <Link href={url}> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`Go to the Metabase Store`} <LinkIcon name="external" /> </Link> diff --git a/enterprise/frontend/src/metabase-enterprise/license/components/BillingInfo/BillingGoToStore.tsx b/enterprise/frontend/src/metabase-enterprise/license/components/BillingInfo/BillingGoToStore.tsx index 6d2029054f0..3f12e9f633e 100644 --- a/enterprise/frontend/src/metabase-enterprise/license/components/BillingInfo/BillingGoToStore.tsx +++ b/enterprise/frontend/src/metabase-enterprise/license/components/BillingInfo/BillingGoToStore.tsx @@ -10,8 +10,10 @@ export const BillingGoToStore = () => { return ( <> <SectionHeader>{t`Billing`}</SectionHeader> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <Text color="text-md">{t`Manage your Cloud account, including billing preferences, in your Metabase Store account.`}</Text> <StoreButtonLink href={url}> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`Go to the Metabase Store`} </StoreButtonLink> </> diff --git a/enterprise/frontend/src/metabase-enterprise/settings/hooks/use-license.ts b/enterprise/frontend/src/metabase-enterprise/settings/hooks/use-license.ts index d99bae6e7c9..598448de8fe 100644 --- a/enterprise/frontend/src/metabase-enterprise/settings/hooks/use-license.ts +++ b/enterprise/frontend/src/metabase-enterprise/settings/hooks/use-license.ts @@ -5,6 +5,7 @@ import { SettingsApi, StoreApi } from "metabase/services"; export const LICENSE_ACCEPTED_URL_HASH = "#activated"; const INVALID_TOKEN_ERROR = t`This token doesn't seem to be valid. Double-check it, then contact support if you think it should be working.`; +// eslint-disable-next-line no-literal-metabase-strings -- Metabase settings const UNABLE_TO_VALIDATE_TOKEN = t`We're having trouble validating your token. Please double-check that your instance can connect to Metabase's servers.`; export type TokenStatus = { diff --git a/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts b/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts index 523466cdbc7..6d417dd2818 100644 --- a/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts +++ b/enterprise/frontend/src/metabase-enterprise/settings/selectors.ts @@ -18,6 +18,7 @@ export const getLogoUrl = (state: EnterpriseState) => export const getLoadingMessage = (state: EnterpriseState) => LOADING_MESSAGE_BY_SETTING[getSetting(state, "loading-message")]; +// eslint-disable-next-line no-literal-metabase-strings -- This is a Metabase string we want to keep. It's used for comparison. const DEFAULT_APPLICATION_NAME = "Metabase"; export const getIsWhiteLabeling = (state: EnterpriseState) => getApplicationName(state) !== DEFAULT_APPLICATION_NAME; diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/HelpLinkSettings/HelpLinkSettings.tsx b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/HelpLinkSettings/HelpLinkSettings.tsx index c4c5ddc3705..e2f1589c05b 100644 --- a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/HelpLinkSettings/HelpLinkSettings.tsx +++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/HelpLinkSettings/HelpLinkSettings.tsx @@ -58,6 +58,7 @@ export const HelpLinkSettings = ({ <Stack> <Radio.Group value={helpLinkSetting} onChange={handleRadioChange}> <Stack> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <Radio label={t`Link to Metabase help`} value="metabase" /> <Radio label={t`Hide it`} value="hidden" /> <Radio label={t`Go to a custom destination...`} value="custom" /> diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx index c982516bbb4..3960b0b1a55 100644 --- a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx +++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/MetabaseLinksToggleWidget/MetabaseLinksToggleDescription.tsx @@ -2,6 +2,7 @@ import { t, jt } from "ttag"; import { Anchor, Popover, Stack, Text } from "metabase/ui"; export function MetabaseLinksToggleDescription() { + // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */ return jt`Control the visibility of links to Metabase documentation and Metabase references in your instance. ${( <Popover key="popover" position="top-start"> <Popover.Target> @@ -12,6 +13,7 @@ export function MetabaseLinksToggleDescription() { <Popover.Dropdown> <Stack p="md" spacing="sm" maw={420}> <Text size="sm"> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`This affects all links in the product experience (outside of the admin panel) that point to Metabase.com URLs.`} </Text> <Text size="sm"> diff --git a/frontend/lint/eslint-rules/no-literal-metabase-strings.js b/frontend/lint/eslint-rules/no-literal-metabase-strings.js new file mode 100644 index 00000000000..ef5b2ba07ce --- /dev/null +++ b/frontend/lint/eslint-rules/no-literal-metabase-strings.js @@ -0,0 +1,97 @@ +/** + * ------------------------------------------------------------------------------ + * Rule Definition + * ------------------------------------------------------------------------------ + */ + +const ADD_COMMENT_MESSAGE = + 'add comment to indicate the reason why this rule needs to be disabled.\nExample: "// eslint-disable-next-line no-literal-metabase-strings -- This string only shows for admins."'; +const ERROR_MESSAGE = + "Metabase string must not be used directly.\n\nPlease import `getApplicationName` selector from `metabase/selectors/whitelabel` and use it to render the application name.\n\nOr " + + ADD_COMMENT_MESSAGE; +const LITERAL_METABASE_STRING_REGEX = /Metabase/; + +// eslint-disable-next-line import/no-commonjs +module.exports = { + meta: { + type: "problem", + docs: { + description: + "Ensure that Metabase string literals are not used so whitelabeled names are used instead", + }, + schema: [], // no options + }, + + create(context) { + return { + Literal(node) { + if ( + typeof node.value !== "string" || + ["ExportNamedDeclaration", "ImportDeclaration"].includes( + node.parent.type, + ) + ) { + return; + } + + if (LITERAL_METABASE_STRING_REGEX.exec(node.value)) { + context.report({ + node, + message: ERROR_MESSAGE, + }); + } + }, + TemplateLiteral(node) { + const quasis = node.quasis; + quasis.forEach(quasi => { + if (LITERAL_METABASE_STRING_REGEX.exec(quasi.value.raw)) { + context.report({ + node, + message: ERROR_MESSAGE, + }); + } + }); + }, + JSXText(node) { + if (LITERAL_METABASE_STRING_REGEX.exec(node.value)) { + context.report({ + node, + message: ERROR_MESSAGE, + }); + } + }, + Program() { + const comments = context.getSourceCode().getAllComments(); + + const ESLINT_DISABLE_BLOCK_REGEX = + /eslint-disable\s+no-literal-metabase-strings/; + const ESLINT_DISABLE_LINE_REGEX = + /eslint-disable-next-line\s+no-literal-metabase-strings/; + const ALLOWED_ESLINT_DISABLE_LINE_REGEX = + /eslint-disable-next-line\s+no-literal-metabase-strings -- \w+/; + comments.forEach(comment => { + if (ESLINT_DISABLE_BLOCK_REGEX.exec(comment.value)) { + const { start, end } = comment.loc; + context.report({ + loc: { + start: { line: start.line - 1, column: start.column }, + end: { line: end.line - 1, column: end.column }, + }, + message: "Please use inline disable with comments instead.", + }); + } + + if ( + ESLINT_DISABLE_LINE_REGEX.exec(comment.value) && + !ALLOWED_ESLINT_DISABLE_LINE_REGEX.exec(comment.value) + ) { + context.report({ + loc: comment.loc, + message: `Please ${ADD_COMMENT_MESSAGE}`, + }); + } + }); + }, + }; + }, +}; diff --git a/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js b/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js index 710d1d1f4e9..bd6918011c6 100644 --- a/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js +++ b/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js @@ -172,7 +172,10 @@ module.exports = { TemplateLiteral(node) { const quasis = node.quasis; quasis.forEach(quasi => { - if (LITERAL_METABASE_URL_REGEX.exec(quasi.value.raw)) { + if ( + LITERAL_METABASE_URL_REGEX.exec(quasi.value.raw) && + !isGetShowMetabaseLinksSelectorImported + ) { context.report({ node, message: ERROR_MESSAGE, diff --git a/frontend/lint/tests/no-literal-metabase-strings.unit.spec.js b/frontend/lint/tests/no-literal-metabase-strings.unit.spec.js new file mode 100644 index 00000000000..57750f2e1b5 --- /dev/null +++ b/frontend/lint/tests/no-literal-metabase-strings.unit.spec.js @@ -0,0 +1,154 @@ +import { RuleTester } from "eslint"; +import noLiteralMetabaseString from "../eslint-rules/no-literal-metabase-strings"; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2015, + sourceType: "module", + ecmaFeatures: { jsx: true }, + }, +}); + +const VALID_CASES = [ + { + // "Metabase in import sources" + code: ` +import OpenInMetabase from "../components/OpenInMetabase";`, + }, + { + // "Metabase in reexports" + code: ` +export { MetabaseLinksToggleWidget } from "./MetabaseLinksToggleWidget";`, + }, + { + // "No Metabase string", + code: ` + const label = "some string"`, + }, + { + // "Detect disabled rule next line", + code: ` + function MyComponent() { + // eslint-disable-next-line no-literal-metabase-strings -- In admin settings + return <div>Metabase store {"interpolation"} something else</div>; + }`, + }, +]; + +const INVALID_CASES = [ + { + name: "Detect in literal strings", + code: ` + const label = "Metabase blabla"`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in literal strings", + code: ` + function MyComponent() { + return <AnotherComponent label="Hello Metabase" />; + }`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in literal strings", + code: ` + import { getApplicationName } from 'metabase/selectors/whitelabel'; + + const label = "Metabase blabla"`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in literal strings", + code: ` + import { getApplicationName } from 'metabase/selectors/whitelabel'; + + function MyComponent() { + return <AnotherComponent label="Hello Metabase" />; + }`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in template strings", + code: ` + const label = t\`Metabase blabla\``, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in template strings", + code: ` + function MyComponent() { + return <AnotherComponent label={t\`Hello Metabase\`} />; + }`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in template strings", + code: ` + import { getApplicationName } from 'metabase/selectors/whitelabel'; + + const label = t\`Metabase blabla\``, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in template strings", + code: ` + import { getApplicationName } from 'metabase/selectors/whitelabel'; + + function MyComponent() { + return <AnotherComponent label={t\`Hello Metabase\`} />; + }`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in JSX tags", + code: ` + function MyComponent() { + return <div>Metabase store {"interpolation"} something else</div>; + }`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect in JSX tags", + code: ` + import { getApplicationName } from 'metabase/selectors/whitelabel'; + + function MyComponent() { + return <div>Metabase store {"interpolation"} something else</div>; + }`, + error: /Metabase string must not be used directly./, + }, + { + name: "Detect disabled rule next line", + code: ` + function MyComponent() { + // eslint-disable-next-line no-literal-metabase-strings + return <div>Metabase store {"interpolation"} something else</div>; + }`, + error: + /Please add comment to indicate the reason why this rule needs to be disabled./, + }, + { + name: "Detect disabled rule block", + code: ` + /* eslint-disable no-literal-metabase-strings */ + function MyComponent() { + return <div>Metabase store {"interpolation"} something else</div>; + }`, + error: "Please use inline disable with comments instead.", + }, +]; + +ruleTester.run("no-literal-metabase-strings", noLiteralMetabaseString, { + valid: VALID_CASES, + invalid: INVALID_CASES.map(invalidCase => { + return { + code: invalidCase.code, + errors: [ + { + message: invalidCase.error, + }, + ], + }; + }), +}); diff --git a/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js b/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js index eb6d40e0a85..d35ac36766f 100644 --- a/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js +++ b/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js @@ -62,6 +62,14 @@ function MyComponent() { code: ` import { getShowMetabaseLinks } from "metabase/selectors/whitelabel"; +function MyComponent() { + return <a href={\`https://metabase.com/docs/latest/troubleshooting-guide/bugs.html\`}>Troubleshooting</a>; +}`, + }, + { + code: ` +import { getShowMetabaseLinks } from "metabase/selectors/whitelabel"; + function MyComponent() { return <a href="https://www.metabase.com/learn/getting-started/">Getting started</a>; }`, @@ -131,6 +139,15 @@ const docsLink = useSelector(state => code: ` function MyComponent() { return <a href="https://metabase.com/docs/latest/troubleshooting-guide/bugs.html">Troubleshooting</a>; +}`, + error: + /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/, + }, + { + name: 'Detect "metabase.com/docs" in template strings', + code: ` +function MyComponent() { + return <a href={\`https://metabase.com/docs/latest/troubleshooting-guide/bugs.html\`}>Troubleshooting</a>; }`, error: /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/, @@ -140,6 +157,15 @@ function MyComponent() { code: ` function MyComponent() { return <a href="https://www.metabase.com/learn/getting-started/">Getting started</a>; +}`, + error: + /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/, + }, + { + name: 'Detect "metabase.com/learn" in template strings', + code: ` +function MyComponent() { + return <a href={\`https://www.metabase.com/learn/getting-started/\`}>Getting started</a>; }`, error: /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/, diff --git a/frontend/src/metabase-lib/expressions/helper-text-strings.ts b/frontend/src/metabase-lib/expressions/helper-text-strings.ts index c673289abe7..e4ad505109f 100644 --- a/frontend/src/metabase-lib/expressions/helper-text-strings.ts +++ b/frontend/src/metabase-lib/expressions/helper-text-strings.ts @@ -823,6 +823,7 @@ const HELPER_TEXT_STRINGS: HelpTextConfig[] = [ { name: t`mode`, // TODO: This is the only place that's not easy to replace the application name. + // eslint-disable-next-line no-literal-metabase-strings -- Hard to replace the application name because it's not a React component description: t`Optional. The default is "ISO". - ISO: Week 1 starts on the Monday before the first Thursday of January. - US: Week 1 starts on Jan 1. All other weeks start on Sunday. diff --git a/frontend/src/metabase-types/api/mocks/settings.ts b/frontend/src/metabase-types/api/mocks/settings.ts index 23d3067bfd2..f44963c34a3 100644 --- a/frontend/src/metabase-types/api/mocks/settings.ts +++ b/frontend/src/metabase-types/api/mocks/settings.ts @@ -139,6 +139,7 @@ export const createMockSettings = ( "application-colors": {}, "application-font": "Lato", "application-font-files": [], + // eslint-disable-next-line no-literal-metabase-strings -- This is a mock "application-name": "Metabase", "available-fonts": [], "available-locales": null, diff --git a/frontend/src/metabase/databases/components/DatabaseCacheScheduleField/DatabaseCacheScheduleField.tsx b/frontend/src/metabase/databases/components/DatabaseCacheScheduleField/DatabaseCacheScheduleField.tsx index f0b08beda22..325541c7b70 100644 --- a/frontend/src/metabase/databases/components/DatabaseCacheScheduleField/DatabaseCacheScheduleField.tsx +++ b/frontend/src/metabase/databases/components/DatabaseCacheScheduleField/DatabaseCacheScheduleField.tsx @@ -85,6 +85,7 @@ const DatabaseCacheScheduleField = ({ onSelect={handleOnDemandSyncSelect} > <ScheduleOptionText> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`When a user adds a new filter to a dashboard or a SQL question, Metabase will scan the field(s) mapped to that filter in order to show the list of selectable values.`} </ScheduleOptionText> </ScheduleOption> diff --git a/frontend/src/metabase/databases/components/DatabaseEngineWarning/DatabaseEngineWarning.tsx b/frontend/src/metabase/databases/components/DatabaseEngineWarning/DatabaseEngineWarning.tsx index 7dd68e484a4..63e9d6b8e1c 100644 --- a/frontend/src/metabase/databases/components/DatabaseEngineWarning/DatabaseEngineWarning.tsx +++ b/frontend/src/metabase/databases/components/DatabaseEngineWarning/DatabaseEngineWarning.tsx @@ -104,6 +104,7 @@ const OldEngineWarning = ({ engineName, onChange }: OldEngineWarningProps) => ( const CommunityEngineWarning = () => ( <Warning icon="info"> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`This is a community-developed driver and not supported by Metabase. `} </Warning> ); @@ -131,6 +132,7 @@ const PartnerEngineWarning = ({ return ( <Warning icon="info"> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} {t`This is a partner-developed driver. Though Metabase can’t provide support for it, if you need help you can contact the fine folks at `} {contactLink} {!contactLink && (sourceName || "our partner")}. diff --git a/frontend/src/metabase/databases/components/DatabaseNameField/DatabaseNameField.tsx b/frontend/src/metabase/databases/components/DatabaseNameField/DatabaseNameField.tsx index 91b85b437d1..c80467ea736 100644 --- a/frontend/src/metabase/databases/components/DatabaseNameField/DatabaseNameField.tsx +++ b/frontend/src/metabase/databases/components/DatabaseNameField/DatabaseNameField.tsx @@ -15,6 +15,7 @@ const DatabaseNameField = ({ engine }: DatabaseNameFieldProps): JSX.Element => { title={t`Display name`} placeholder={t`Our ${name}`} rightIcon="info" + // eslint-disable-next-line no-literal-metabase-strings -- Admin settings rightIconTooltip={t`Choose what this data will be called in Metabase.`} /> ); diff --git a/frontend/src/metabase/databases/components/DatabaseSyncModal/DatabaseSyncModal.tsx b/frontend/src/metabase/databases/components/DatabaseSyncModal/DatabaseSyncModal.tsx index 81b6f3645be..9839940deea 100644 --- a/frontend/src/metabase/databases/components/DatabaseSyncModal/DatabaseSyncModal.tsx +++ b/frontend/src/metabase/databases/components/DatabaseSyncModal/DatabaseSyncModal.tsx @@ -31,7 +31,8 @@ const DatabaseSyncModal = ({ sampleUrl, onClose }: DatabaseSyncModalProps) => { ? jt`In the meantime, you can take a look at the ${( <strong key="name">{t`Sample Database`}</strong> )} if you want to get a head start. Want to explore?` - : t`Have a look around your Metabase in the meantime if you want to get a head start.`} + : // eslint-disable-next-line no-literal-metabase-strings -- Metabase settings + t`Have a look around your Metabase in the meantime if you want to get a head start.`} </ModalMessage> {sampleUrl ? ( <Link className="Button Button--primary" to={sampleUrl}> diff --git a/frontend/src/metabase/home/components/EmbedMinimalHomepage/EmbedMinimalHomepage.tsx b/frontend/src/metabase/home/components/EmbedMinimalHomepage/EmbedMinimalHomepage.tsx index e3f291950be..654c905812a 100644 --- a/frontend/src/metabase/home/components/EmbedMinimalHomepage/EmbedMinimalHomepage.tsx +++ b/frontend/src/metabase/home/components/EmbedMinimalHomepage/EmbedMinimalHomepage.tsx @@ -29,6 +29,7 @@ export const EmbedMinimalHomepage = ({ <Text fw="bold" color="text-dark" + // eslint-disable-next-line no-literal-metabase-strings -- this is only visible to admins >{t`Get started with Embedding Metabase in your app`}</Text> <Card px={40} py={32} maw={570}> <Stack spacing="lg"> diff --git a/frontend/src/metabase/lib/api.js b/frontend/src/metabase/lib/api.js index 3e0c5a9ccfb..d9b43d7d870 100644 --- a/frontend/src/metabase/lib/api.js +++ b/frontend/src/metabase/lib/api.js @@ -9,6 +9,7 @@ import { isTest } from "metabase/env"; const ONE_SECOND = 1000; const MAX_RETRIES = 10; +// eslint-disable-next-line no-literal-metabase-strings -- Not a user facing string const ANTI_CSRF_HEADER = "X-Metabase-Anti-CSRF-Token"; let ANTI_CSRF_TOKEN = null; @@ -90,6 +91,7 @@ export class Api extends EventEmitter { } if (isWithinIframe()) { + // eslint-disable-next-line no-literal-metabase-strings -- Not a user facing string headers["X-Metabase-Embedded"] = "true"; } diff --git a/frontend/src/metabase/lib/i18n.js b/frontend/src/metabase/lib/i18n.js index 78c15df379c..79b73cb9f44 100644 --- a/frontend/src/metabase/lib/i18n.js +++ b/frontend/src/metabase/lib/i18n.js @@ -22,6 +22,7 @@ export async function loadLocalization(locale) { "plural-forms": "nplurals=2; plural=(n != 1);", }, translations: { + // eslint-disable-next-line no-literal-metabase-strings -- Not a user facing string "": { Metabase: { msgid: "Metabase", msgstr: ["Metabase"] } }, }, }; diff --git a/frontend/src/metabase/nav/components/AdminNavbar/AdminNavbar.tsx b/frontend/src/metabase/nav/components/AdminNavbar/AdminNavbar.tsx index 66536c30fdc..7c4fcd8d14e 100644 --- a/frontend/src/metabase/nav/components/AdminNavbar/AdminNavbar.tsx +++ b/frontend/src/metabase/nav/components/AdminNavbar/AdminNavbar.tsx @@ -37,6 +37,7 @@ export const AdminNavbar = ({ <AdminLogoLink to="/admin"> <AdminLogoContainer> <LogoIcon className="text-brand my2" dark /> + {/* eslint-disable-next-line no-literal-metabase-strings -- Metabase settings */} <AdminLogoText>{t`Metabase Admin`}</AdminLogoText> </AdminLogoContainer> </AdminLogoLink> diff --git a/frontend/src/metabase/nav/components/DatabasePromptBanner/DatabasePromptBanner.tsx b/frontend/src/metabase/nav/components/DatabasePromptBanner/DatabasePromptBanner.tsx index da5a13c87b0..8e28dbf4e30 100644 --- a/frontend/src/metabase/nav/components/DatabasePromptBanner/DatabasePromptBanner.tsx +++ b/frontend/src/metabase/nav/components/DatabasePromptBanner/DatabasePromptBanner.tsx @@ -39,6 +39,7 @@ export function DatabasePromptBanner({ location }: DatabasePromptBannerProps) { return ( <DatabasePromptBannerRoot role="banner"> + {/* eslint-disable-next-line no-literal-metabase-strings -- Only shows for admins*/} <Prompt>{t`Connect to your database to get the most from Metabase.`}</Prompt> <CallToActions> <GetHelpButton diff --git a/frontend/src/metabase/nav/components/ProfileLink/ProfileLink.jsx b/frontend/src/metabase/nav/components/ProfileLink/ProfileLink.jsx index e4b29f82096..6668d5e8448 100644 --- a/frontend/src/metabase/nav/components/ProfileLink/ProfileLink.jsx +++ b/frontend/src/metabase/nav/components/ProfileLink/ProfileLink.jsx @@ -133,7 +133,9 @@ function ProfileLink({ adminItems, onLogout }) { className="p2 h5 text-centered text-medium border-top" > <span className="block"> + {/* eslint-disable-next-line no-literal-metabase-strings -- This only shows on OSS instance */} <span className="text-bold">Metabase</span>{" "} + {/* eslint-disable-next-line no-literal-metabase-strings -- This only shows on OSS instance */} {t`is a Trademark of`} Metabase, Inc </span> <span>{t`and is built with care by a team from all across this pale blue dot.`}</span> diff --git a/frontend/src/metabase/nav/components/WhatsNewNotification/WhatsNewNotification.tsx b/frontend/src/metabase/nav/components/WhatsNewNotification/WhatsNewNotification.tsx index 0c01680dd3f..291603d35db 100644 --- a/frontend/src/metabase/nav/components/WhatsNewNotification/WhatsNewNotification.tsx +++ b/frontend/src/metabase/nav/components/WhatsNewNotification/WhatsNewNotification.tsx @@ -62,6 +62,7 @@ export function WhatsNewNotification() { </DismissIconButtonWrapper> </Flex> + {/* eslint-disable-next-line no-literal-metabase-strings -- This only shows for admins */} <Text weight="bold" size="sm">{t`Metabase has been updated`}</Text> <Anchor diff --git a/frontend/src/metabase/plugins/index.ts b/frontend/src/metabase/plugins/index.ts index 8795442dd9c..8c0a94b3be3 100644 --- a/frontend/src/metabase/plugins/index.ts +++ b/frontend/src/metabase/plugins/index.ts @@ -129,6 +129,7 @@ export const PLUGIN_SELECTORS = { canWhitelabel: (_state: State) => false, getLoadingMessage: (_state: State) => t`Doing science...`, getIsWhiteLabeling: (_state: State) => false, + // eslint-disable-next-line no-literal-metabase-strings -- This is the actual Metabase name, so we don't want to translate it. getApplicationName: (_state: State) => "Metabase", getShowMetabaseLinks: (_state: State) => true, getDashboardOverviewId: (_state: State) => undefined, diff --git a/frontend/src/metabase/public/components/EmbedFrame/LogoBadge.tsx b/frontend/src/metabase/public/components/EmbedFrame/LogoBadge.tsx index a0732a68ce0..ef51eba5111 100644 --- a/frontend/src/metabase/public/components/EmbedFrame/LogoBadge.tsx +++ b/frontend/src/metabase/public/components/EmbedFrame/LogoBadge.tsx @@ -12,6 +12,7 @@ function LogoBadge({ }) { const logoSize = variant === "large" ? 42 : 28; const Metabase = ( + // eslint-disable-next-line no-literal-metabase-strings -- This embedding badge which we don't want to show the whitelabeled name <MetabaseName key="metabase" isDark={dark} variant={variant}> Metabase </MetabaseName> diff --git a/frontend/src/metabase/public/components/EmbedModal/SelectEmbedTypePane/InteractiveEmbeddingCTA/InteractiveEmbeddingCTA.tsx b/frontend/src/metabase/public/components/EmbedModal/SelectEmbedTypePane/InteractiveEmbeddingCTA/InteractiveEmbeddingCTA.tsx index 6a6bec523fc..d4b3a35677a 100644 --- a/frontend/src/metabase/public/components/EmbedModal/SelectEmbedTypePane/InteractiveEmbeddingCTA/InteractiveEmbeddingCTA.tsx +++ b/frontend/src/metabase/public/components/EmbedModal/SelectEmbedTypePane/InteractiveEmbeddingCTA/InteractiveEmbeddingCTA.tsx @@ -28,6 +28,7 @@ const useCTAText = () => { return { showProBadge: true, + // eslint-disable-next-line no-literal-metabase-strings -- This only shows for admins description: t`Give your customers the full power of Metabase in your own app, with SSO, advanced permissions, customization, and more.`, linkText: t`Learn more`, url: `https://www.metabase.com/product/embedded-analytics?utm_source=${plan}&utm_media=static-embed-popover`, diff --git a/frontend/src/metabase/public/components/EmbedModal/StaticEmbedSetupPane/AppearanceSettings.tsx b/frontend/src/metabase/public/components/EmbedModal/StaticEmbedSetupPane/AppearanceSettings.tsx index 65e1d4e17a7..6f955116178 100644 --- a/frontend/src/metabase/public/components/EmbedModal/StaticEmbedSetupPane/AppearanceSettings.tsx +++ b/frontend/src/metabase/public/components/EmbedModal/StaticEmbedSetupPane/AppearanceSettings.tsx @@ -185,8 +185,10 @@ export const AppearanceSettings = ({ <> <Divider my="2rem" /> <StaticEmbedSetupPaneSettingsContentSection + // eslint-disable-next-line no-literal-metabase-strings -- This only shows for admins title={t`Removing the “Powered by Metabase†banner`} > + {/* eslint-disable-next-line no-literal-metabase-strings -- This only shows for admins */} <Text>{jt`This banner appears on all static embeds created with the Metabase open source version. You’ll need to upgrade to ${( <ExternalLink key="bannerPlan" diff --git a/frontend/src/metabase/public/components/widgets/LegaleseStep/LegaleseStep.tsx b/frontend/src/metabase/public/components/widgets/LegaleseStep/LegaleseStep.tsx index ce01436ed15..e8ec6977a92 100644 --- a/frontend/src/metabase/public/components/widgets/LegaleseStep/LegaleseStep.tsx +++ b/frontend/src/metabase/public/components/widgets/LegaleseStep/LegaleseStep.tsx @@ -40,6 +40,7 @@ export const LegaleseStep = ({ )}`} </Text> <Text> + {/* eslint-disable-next-line no-literal-metabase-strings -- This only shows for admins */} {t`When you embed charts or dashboards from Metabase in your own application that application isn't subject to the Affero General Public License that covers the rest of Metabase, provided you keep the Metabase logo and the "Powered by Metabase" visible on those embeds.`} </Text> <Text> diff --git a/frontend/src/metabase/redux/settings.ts b/frontend/src/metabase/redux/settings.ts index 73b931e772d..5213bb7053b 100644 --- a/frontend/src/metabase/redux/settings.ts +++ b/frontend/src/metabase/redux/settings.ts @@ -10,6 +10,7 @@ export const refreshSiteSettings = createAsyncThunk( REFRESH_SITE_SETTINGS, async ({ locale }: { locale?: string } = {}) => { const settings = await SessionApi.properties(null, { + // eslint-disable-next-line no-literal-metabase-strings -- Not a user facing string headers: locale ? { "X-Metabase-Locale": locale } : {}, }); MetabaseSettings.setAll(settings); diff --git a/frontend/src/metabase/route-guards.jsx b/frontend/src/metabase/route-guards.jsx index 13911ffd883..7ff7066c599 100644 --- a/frontend/src/metabase/route-guards.jsx +++ b/frontend/src/metabase/route-guards.jsx @@ -8,6 +8,7 @@ const MetabaseIsSetup = UserAuthWrapper({ predicate: authData => authData.hasUserSetup, failureRedirectPath: "/setup", authSelector: state => ({ hasUserSetup: MetabaseSettings.hasUserSetup() }), // HACK + // eslint-disable-next-line no-literal-metabase-strings -- Not a user facing string wrapperDisplayName: "MetabaseIsSetup", allowRedirectBack: false, redirectAction: routerActions.replace, diff --git a/frontend/src/metabase/setup/components/PreferencesStep/PreferencesStep.tsx b/frontend/src/metabase/setup/components/PreferencesStep/PreferencesStep.tsx index 06db35babdd..bc0f2aafa63 100644 --- a/frontend/src/metabase/setup/components/PreferencesStep/PreferencesStep.tsx +++ b/frontend/src/metabase/setup/components/PreferencesStep/PreferencesStep.tsx @@ -75,7 +75,6 @@ export const PreferencesStep = ({ <StepDescription> {t`In order to help us improve Metabase, we'd like to collect certain data about product usage.`}{" "} <ExternalLink - // eslint-disable-next-line no-unconditional-metabase-links-render -- Metabase setup href={Settings.docsUrl( "installation-and-operation/information-collection", )} diff --git a/frontend/src/metabase/setup/components/SetupHelp/SetupHelp.tsx b/frontend/src/metabase/setup/components/SetupHelp/SetupHelp.tsx index 7cdc23333ad..3777235ed66 100644 --- a/frontend/src/metabase/setup/components/SetupHelp/SetupHelp.tsx +++ b/frontend/src/metabase/setup/components/SetupHelp/SetupHelp.tsx @@ -9,7 +9,6 @@ export const SetupHelp = (): JSX.Element => { {t`If you feel stuck`},{" "} <ExternalLink className="link" - // eslint-disable-next-line no-unconditional-metabase-links-render -- Metabase setup href={MetabaseSettings.docsUrl( "configuring-metabase/setting-up-metabase", )} -- GitLab