diff --git a/eslint.config.js b/eslint.config.js index 2860db1a2c7..99d651e1819 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -91,6 +91,7 @@ module.exports = [ 'no-duplicate-case': 'error', '@grafana/no-border-radius-literal': 'error', '@grafana/no-unreduced-motion': 'error', + '@grafana/no-restricted-img-srcs': 'error', 'react/prop-types': 'off', // need to ignore emotion's `css` prop, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md#rule-options 'react/no-unknown-property': ['error', { ignore: ['css'] }], diff --git a/jest.config.js b/jest.config.js index 5418ede2e38..982b0cdf714 100644 --- a/jest.config.js +++ b/jest.config.js @@ -44,7 +44,7 @@ module.exports = { __webpack_public_path__: '', // empty string }, moduleNameMapper: { - '\\.svg': '/public/test/mocks/svg.ts', + '\\.(svg|png|jpg)': '/public/test/mocks/images.ts', '\\.css': '/public/test/mocks/style.ts', 'react-inlinesvg': '/public/test/mocks/react-inlinesvg.tsx', // resolve directly as monaco and kusto don't have main property in package.json which jest needs diff --git a/packages/grafana-eslint-rules/index.cjs b/packages/grafana-eslint-rules/index.cjs index 11be6258993..c3f69b593e5 100644 --- a/packages/grafana-eslint-rules/index.cjs +++ b/packages/grafana-eslint-rules/index.cjs @@ -4,6 +4,7 @@ const noUnreducedMotion = require('./rules/no-unreduced-motion.cjs'); const noUntranslatedStrings = require('./rules/no-untranslated-strings.cjs'); const noTranslationTopLevel = require('./rules/no-translation-top-level.cjs'); const themeTokenUsage = require('./rules/theme-token-usage.cjs'); +const noRestrictedImgSrcs = require('./rules/no-restricted-img-srcs.cjs'); module.exports = { rules: { @@ -13,5 +14,6 @@ module.exports = { 'theme-token-usage': themeTokenUsage, 'no-untranslated-strings': noUntranslatedStrings, 'no-translation-top-level': noTranslationTopLevel, + 'no-restricted-img-srcs': noRestrictedImgSrcs, }, }; diff --git a/packages/grafana-eslint-rules/rules/import-utils.cjs b/packages/grafana-eslint-rules/rules/import-utils.cjs new file mode 100644 index 00000000000..4d44341ca9c --- /dev/null +++ b/packages/grafana-eslint-rules/rules/import-utils.cjs @@ -0,0 +1,96 @@ +// @ts-check +const { AST_NODE_TYPES } = require('@typescript-eslint/utils'); +const { upperFirst } = require('lodash'); + +/** @typedef {import('@typescript-eslint/utils/ts-eslint').RuleContext<'publicImg' | 'importImage' | 'useBuildFolder', []>} RuleContextWithOptions */ + +/** + * @param {string} str + */ +const camelCase = (str) => { + return str + .replace(/[-_]/g, ' ') + .split(' ') + .map((word, index) => (index === 0 ? word : upperFirst(word))) + .join(''); +}; + +/** + * @param {string} value + * @returns {string} + */ +const convertPathToImportName = (value) => { + const fullFileName = value.split('/').pop() || ''; + const fileType = fullFileName.split('.').pop(); + const fileName = fullFileName.replace(`.${fileType}`, ''); + return camelCase(fileName) + upperFirst(fileType); +}; + +/** + * @param {import('@typescript-eslint/utils/ts-eslint').RuleFixer} fixer + * @param {import('@typescript-eslint/utils').TSESTree.StringLiteral} node + * @param {RuleContextWithOptions} context + */ +function getImageImportFixers(fixer, node, context) { + const { value: importPath } = node; + const pathWithoutPublic = importPath.replace('public/', ''); + + /** e.g. public/img/checkbox.png -> checkboxPng */ + const imageImportName = convertPathToImportName(importPath); + + const body = context.sourceCode.ast.body; + + const existingImport = body.find( + (node) => node.type === AST_NODE_TYPES.ImportDeclaration && node.source.value === pathWithoutPublic + ); + + const fixers = []; + + // If there's no existing import at all, add a fixer for this + if (!existingImport) { + const importStatementFixer = fixer.insertTextBefore( + body[0], + `import ${imageImportName} from '${pathWithoutPublic}';\n` + ); + fixers.push(importStatementFixer); + } + + const isInAttribute = node.parent.type === AST_NODE_TYPES.JSXAttribute; + const variableReplacement = isInAttribute ? `{${imageImportName}}` : imageImportName; + const variableFixer = fixer.replaceText(node, variableReplacement); + fixers.push(variableFixer); + + return fixers; +} + +/** + * @param {import('@typescript-eslint/utils/ts-eslint').RuleFixer} fixer + * @param {import('@typescript-eslint/utils').TSESTree.StringLiteral} node + */ +const replaceWithPublicBuild = (fixer, node) => { + const { value } = node; + + const startingQuote = node.raw.startsWith('"') ? '"' : "'"; + return fixer.replaceText( + node, + `${startingQuote}${value.replace('public/img/', 'public/build/img/')}${startingQuote}` + ); +}; + +/** + * @param {string} value + */ +const isInvalidImageLocation = (value) => { + return ( + value.startsWith('public/img/') || + (!value.startsWith('public/build/') && + !value.startsWith('public/plugins/') && + /public.*(\.svg|\.png|\.jpg|\.jpeg|\.gif)$/.test(value)) + ); +}; + +module.exports = { + getImageImportFixers, + replaceWithPublicBuild, + isInvalidImageLocation, +}; diff --git a/packages/grafana-eslint-rules/rules/no-restricted-img-srcs.cjs b/packages/grafana-eslint-rules/rules/no-restricted-img-srcs.cjs new file mode 100644 index 00000000000..ac138a2b9f9 --- /dev/null +++ b/packages/grafana-eslint-rules/rules/no-restricted-img-srcs.cjs @@ -0,0 +1,78 @@ +// @ts-check +/** @typedef {import('@typescript-eslint/utils').TSESTree.Literal} Literal */ +/** @typedef {import('@typescript-eslint/utils').TSESTree.TemplateLiteral} TemplateLiteral */ +const { getImageImportFixers, replaceWithPublicBuild, isInvalidImageLocation } = require('./import-utils.cjs'); + +const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils'); + +const createRule = ESLintUtils.RuleCreator( + (name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}` +); + +const imgSrcRule = createRule({ + create(context) { + return { + /** + * @param {Literal|TemplateLiteral} node + */ + 'Literal, TemplateLiteral'(node) { + if (node.type === AST_NODE_TYPES.TemplateLiteral) { + if (node.quasis.some((quasi) => isInvalidImageLocation(quasi.value.raw))) { + return context.report({ + node, + messageId: 'publicImg', + }); + } + return; + } + + const { value } = node; + + if (value && typeof value === 'string' && isInvalidImageLocation(value)) { + const canUseBuildFolder = value.startsWith('public/img/'); + /** + * @type {import('@typescript-eslint/utils/ts-eslint').SuggestionReportDescriptor<"publicImg" | "importImage" | "useBuildFolder">[]} + */ + const suggestions = [ + { + messageId: 'importImage', + fix: (fixer) => getImageImportFixers(fixer, node, context), + }, + ]; + + if (canUseBuildFolder) { + suggestions.push({ + messageId: 'useBuildFolder', + fix: (fixer) => replaceWithPublicBuild(fixer, node), + }); + } + + return context.report({ + node, + messageId: 'publicImg', + suggest: suggestions, + }); + } + }, + }; + }, + name: 'no-restricted-img-srcs', + meta: { + fixable: 'code', + hasSuggestions: true, + type: 'problem', + docs: { + description: 'Disallow references to images in the public folder', + }, + messages: { + publicImg: + "Don't reference image sources from the public folder. Either use the build folder or import the image", + importImage: 'Import image instead', + useBuildFolder: 'Use public/build path instead', + }, + schema: [], + }, + defaultOptions: [], +}); + +module.exports = imgSrcRule; diff --git a/packages/grafana-eslint-rules/tests/no-restricted-img-srcs.test.js b/packages/grafana-eslint-rules/tests/no-restricted-img-srcs.test.js new file mode 100644 index 00000000000..e3daa098418 --- /dev/null +++ b/packages/grafana-eslint-rules/tests/no-restricted-img-srcs.test.js @@ -0,0 +1,143 @@ +/* eslint-disable @grafana/no-restricted-img-srcs */ +import { RuleTester } from 'eslint'; + +import noRestrictedImgSrcs from '../rules/no-restricted-img-srcs.cjs'; + +RuleTester.setDefaultConfig({ + languageOptions: { + ecmaVersion: 2018, + sourceType: 'module', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, +}); + +const ruleTester = new RuleTester(); + +ruleTester.run('eslint no-restricted-img-srcs', noRestrictedImgSrcs, { + valid: [ + { + name: 'uses build folder', + code: `const foo = 'public/build/img/checkbox.png';`, + }, + { + name: 'uses import', + code: ` +import foo from 'img/checkbox.png'; +const bar = foo; +const baz = ; +`, + }, + { + name: 'plugin folder', + code: `const foo = 'public/plugins/foo/checkbox.png';`, + }, + { + name: 'template literal', + code: `const foo = \`something else\``, + }, + ], + invalid: [ + { + name: 'references public folder', + code: ` +const foo = 'public/img/checkbox-128-icon.png';`, + errors: [ + { + messageId: 'publicImg', + suggestions: [ + { + messageId: 'importImage', + output: ` +import checkbox128IconPng from 'img/checkbox-128-icon.png'; +const foo = checkbox128IconPng;`, + }, + { + messageId: 'useBuildFolder', + output: ` +const foo = 'public/build/img/checkbox-128-icon.png';`, + }, + ], + }, + ], + }, + { + name: 'template literal', + code: ` +const isDark = true ? 'dark' : 'light'; +const foo = \`public/img/checkbox-128-icon-\${isDark}.png\`;`, + errors: [ + { + messageId: 'publicImg', + }, + ], + }, + { + name: 'fixes jsx attribute', + code: ``, + errors: [ + { + messageId: 'publicImg', + suggestions: [ + { + messageId: 'importImage', + output: `import checkboxPng from 'img/checkbox.png'; +`, + }, + { + messageId: 'useBuildFolder', + output: ``, + }, + ], + }, + ], + }, + { + name: 'fixes with existing import', + code: ` +import checkboxPng from 'img/checkbox.png'; +const foo = checkboxPng; +const bar = 'public/img/checkbox.png';`, + errors: [ + { + messageId: 'publicImg', + suggestions: [ + { + messageId: 'importImage', + output: ` +import checkboxPng from 'img/checkbox.png'; +const foo = checkboxPng; +const bar = checkboxPng;`, + }, + { + messageId: 'useBuildFolder', + output: ` +import checkboxPng from 'img/checkbox.png'; +const foo = checkboxPng; +const bar = 'public/build/img/checkbox.png';`, + }, + ], + }, + ], + }, + { + name: 'image elsewhere in public folder', + code: `const foo = 'public/app/plugins/datasource/alertmanager/img/logo.svg';`, + errors: [ + { + messageId: 'publicImg', + suggestions: [ + { + messageId: 'importImage', + output: `import logoSvg from 'app/plugins/datasource/alertmanager/img/logo.svg'; +const foo = logoSvg;`, + }, + ], + }, + ], + }, + ], +}); diff --git a/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx b/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx index 0a3c7669489..7d25546e85c 100644 --- a/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx +++ b/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx @@ -10,6 +10,7 @@ import { PluginSignatureStatus, PluginType, } from '@grafana/data'; +import iconGaugeSvg from 'app/plugins/panel/gauge/img/icon_gauge.svg'; import { reportInteraction } from '../utils'; @@ -249,8 +250,8 @@ function createPluginMetaInfo(info: Partial = {}): PluginMetaInf description: 'Standard gauge visualization', links: [], logos: { - large: 'public/app/plugins/panel/gauge/img/icon_gauge.svg', - small: 'public/app/plugins/panel/gauge/img/icon_gauge.svg', + large: iconGaugeSvg, + small: iconGaugeSvg, }, screenshots: [], updated: '', diff --git a/packages/grafana-ui/rollup.config.ts b/packages/grafana-ui/rollup.config.ts index 5dcad62f82f..c8e86c46f74 100644 --- a/packages/grafana-ui/rollup.config.ts +++ b/packages/grafana-ui/rollup.config.ts @@ -9,6 +9,7 @@ const icons = rq('../../public/app/core/icons/cached.json'); const pkg = rq('./package.json'); const iconSrcPaths = icons.map((iconSubPath) => { + // eslint-disable-next-line @grafana/no-restricted-img-srcs return `../../public/img/icons/${iconSubPath}.svg`; }); diff --git a/packages/grafana-ui/src/components/Icon/utils.test.ts b/packages/grafana-ui/src/components/Icon/utils.test.ts index 2898856086e..bc7d7c7cc55 100644 --- a/packages/grafana-ui/src/components/Icon/utils.test.ts +++ b/packages/grafana-ui/src/components/Icon/utils.test.ts @@ -30,7 +30,7 @@ describe('Icon utils', () => { it('should return icon root based on __grafana_public_path__', () => { const { getIconRoot } = require('./utils'); - expect(getIconRoot()).toEqual('somepath/public/img/icons/'); + expect(getIconRoot()).toEqual('somepath/public/build/img/icons/'); }); }); @@ -42,7 +42,7 @@ describe('Icon utils', () => { it('should return default icon root', () => { const { getIconRoot } = require('./utils'); - expect(getIconRoot()).toEqual('public/img/icons/'); + expect(getIconRoot()).toEqual('public/build/img/icons/'); }); }); }); diff --git a/packages/grafana-ui/src/components/Icon/utils.ts b/packages/grafana-ui/src/components/Icon/utils.ts index 229aa5361e0..263e68ac0ff 100644 --- a/packages/grafana-ui/src/components/Icon/utils.ts +++ b/packages/grafana-ui/src/components/Icon/utils.ts @@ -53,9 +53,9 @@ export function getIconRoot(): string { const grafanaPublicPath = typeof window !== 'undefined' && window.__grafana_public_path__; if (grafanaPublicPath) { - iconRoot = grafanaPublicPath + 'img/icons/'; + iconRoot = grafanaPublicPath + 'build/img/icons/'; } else { - iconRoot = 'public/img/icons/'; + iconRoot = 'public/build/img/icons/'; } return iconRoot; diff --git a/public/app/core/components/AppChrome/News/NewsDrawer.tsx b/public/app/core/components/AppChrome/News/NewsDrawer.tsx index c5e5fad1c20..71aef1bac3b 100644 --- a/public/app/core/components/AppChrome/News/NewsDrawer.tsx +++ b/public/app/core/components/AppChrome/News/NewsDrawer.tsx @@ -5,6 +5,7 @@ import { selectors } from '@grafana/e2e-selectors'; import { IconButton, Drawer, useStyles2, Text } from '@grafana/ui'; import { t } from 'app/core/internationalization'; import { DEFAULT_FEED_URL } from 'app/plugins/panel/news/constants'; +import grotNewsSvg from 'img/grot-news.svg'; import { NewsWrapper } from './NewsWrapper'; @@ -28,7 +29,7 @@ export function NewsContainer({ onClose }: NewsContainerProps) { title={t('news.link-title', 'Go to Grafana labs blog')} className={styles.grot} > - Grot reading news + Grot reading news
- Grot reading news + Grot reading news
diff --git a/public/app/core/components/BouncingLoader/BouncingLoader.tsx b/public/app/core/components/BouncingLoader/BouncingLoader.tsx index df69d10806a..272b7816df7 100644 --- a/public/app/core/components/BouncingLoader/BouncingLoader.tsx +++ b/public/app/core/components/BouncingLoader/BouncingLoader.tsx @@ -2,6 +2,7 @@ import { css, keyframes } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; +import grafanaIconSvg from 'img/grafana_icon.svg'; import { t } from '../../internationalization'; @@ -16,7 +17,7 @@ export function BouncingLoader() { aria-label={t('bouncing-loader.label', 'Loading')} >
- +
); diff --git a/public/app/core/components/Branding/Branding.tsx b/public/app/core/components/Branding/Branding.tsx index 76352fe0eb4..d87f2e1b333 100644 --- a/public/app/core/components/Branding/Branding.tsx +++ b/public/app/core/components/Branding/Branding.tsx @@ -3,6 +3,9 @@ import { FC } from 'react'; import { colorManipulator } from '@grafana/data'; import { useTheme2 } from '@grafana/ui'; +import g8LoginDarkSvg from 'img/g8_login_dark.svg'; +import g8LoginLightSvg from 'img/g8_login_light.svg'; +import grafanaIconSvg from 'img/grafana_icon.svg'; export interface BrandComponentProps { className?: string; @@ -10,7 +13,7 @@ export interface BrandComponentProps { } export const LoginLogo: FC = ({ className, logo }) => { - return Grafana; + return Grafana; }; const LoginBackground: FC = ({ className, children }) => { @@ -24,7 +27,7 @@ const LoginBackground: FC = ({ className, children }) => { right: 0, bottom: 0, top: 0, - background: `url(public/img/g8_login_${theme.isDark ? 'dark' : 'light'}.svg)`, + background: `url(${theme.isDark ? g8LoginDarkSvg : g8LoginLightSvg})`, backgroundPosition: 'top center', backgroundSize: 'auto', backgroundRepeat: 'no-repeat', @@ -43,7 +46,7 @@ const LoginBackground: FC = ({ className, children }) => { }; const MenuLogo: FC = ({ className }) => { - return Grafana; + return Grafana; }; const LoginBoxBackground = () => { diff --git a/public/app/core/components/Upgrade/UpgradeBox.tsx b/public/app/core/components/Upgrade/UpgradeBox.tsx index 96f0b0e99c9..446e05f205d 100644 --- a/public/app/core/components/Upgrade/UpgradeBox.tsx +++ b/public/app/core/components/Upgrade/UpgradeBox.tsx @@ -271,5 +271,5 @@ const getImgUrl = (urlOrId: string) => { return urlOrId; } - return '/public/img/enterprise/highlights/' + urlOrId; + return '/public/build/img/enterprise/highlights/' + urlOrId; }; diff --git a/public/app/features/admin/LicenseChrome.tsx b/public/app/features/admin/LicenseChrome.tsx index 129c8ef71b9..2c83c6b99b4 100644 --- a/public/app/features/admin/LicenseChrome.tsx +++ b/public/app/features/admin/LicenseChrome.tsx @@ -3,11 +3,14 @@ import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2, useTheme2 } from '@grafana/ui'; +import grafanaIconSvg from 'img/grafana_icon.svg'; +import headerDarkSvg from 'img/licensing/header_dark.svg'; +import headerLightSvg from 'img/licensing/header_light.svg'; const title = { fontWeight: 500, fontSize: '26px', lineHeight: '123%' }; const getStyles = (theme: GrafanaTheme2) => { - const backgroundUrl = theme.isDark ? 'public/img/licensing/header_dark.svg' : 'public/img/licensing/header_light.svg'; + const backgroundUrl = theme.isDark ? headerDarkSvg : headerLightSvg; const footerBg = theme.isDark ? theme.v1.palette.dark9 : theme.v1.palette.gray6; return { @@ -56,7 +59,7 @@ export function LicenseChrome({ header, editionNotice, subheader, children }: Pr }} > Grafana { - + 24 × 7 × 365 support via @@ -134,7 +136,7 @@ const ServiceInfo = () => { 'admin.service-info.title-handinhand-support-in-the-upgrade-process', 'Hand-in-hand support in the upgrade process' )} - image="public/img/licensing/handinhand_support.svg" + image={handinhandSupportSvg} /> @@ -241,7 +243,7 @@ interface ItemProps { } const Item = ({ children, title, image }: React.PropsWithChildren) => { - const imageUrl = image ? image : 'public/img/licensing/checkmark.svg'; + const imageUrl = image ? image : checkmarkSvg; const itemStyle = css({ display: 'flex', diff --git a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap index b409ffd1b60..18a5bae9e28 100644 --- a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap +++ b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap @@ -110,7 +110,7 @@ exports[`useContactPoints should return contact points with status 1`] = ` "name": "Grafana IRM", }, Symbol(receiver_plugin_metadata): { - "icon": "public/img/alerting/oncall_logo.svg", + "icon": "__DEFAULT_MOCK_IMAGE_CONTENT__", "title": "Grafana OnCall", }, }, @@ -363,7 +363,7 @@ exports[`useContactPoints when having oncall plugin installed and no alert manag Symbol(receiver_plugin_metadata): { "description": "grafana-integration", "externalUrl": "/a/grafana-oncall-app/integrations/ABC123", - "icon": "public/img/alerting/oncall_logo.svg", + "icon": "__DEFAULT_MOCK_IMAGE_CONTENT__", "title": "Grafana OnCall", "warning": undefined, }, diff --git a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts index 5ac5aa95219..5ec202d11c9 100644 --- a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts +++ b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts @@ -1,8 +1,11 @@ +import irmLogoSvg from 'img/alerting/irm_logo.svg'; +import oncallLogoSvg from 'img/alerting/oncall_logo.svg'; + import { SupportedPlugin } from '../../../types/pluginBridges'; export const GRAFANA_APP_RECEIVERS_SOURCE_IMAGE: Record = { - [SupportedPlugin.OnCall]: 'public/img/alerting/oncall_logo.svg', - [SupportedPlugin.Irm]: 'public/img/alerting/irm_logo.svg', + [SupportedPlugin.OnCall]: oncallLogoSvg, + [SupportedPlugin.Irm]: irmLogoSvg, [SupportedPlugin.Incident]: '', [SupportedPlugin.MachineLearning]: '', [SupportedPlugin.Labels]: '', diff --git a/public/app/features/alerting/unified/components/rule-editor/rule-types/GrafanaManagedAlert.tsx b/public/app/features/alerting/unified/components/rule-editor/rule-types/GrafanaManagedAlert.tsx index 1082af39e63..ec2c7b8f370 100644 --- a/public/app/features/alerting/unified/components/rule-editor/rule-types/GrafanaManagedAlert.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/rule-types/GrafanaManagedAlert.tsx @@ -1,3 +1,5 @@ +import grafanaIconSvg from 'img/grafana_icon.svg'; + import { Trans } from '../../../../../../core/internationalization'; import { RuleFormType } from '../../../types/rule-form'; @@ -16,7 +18,7 @@ const GrafanaManagedRuleType = ({ selected = false, disabled, onClick }: SharedP } - image="public/img/grafana_icon.svg" + image={grafanaIconSvg} selected={selected} disabled={disabled} value={RuleFormType.grafana} diff --git a/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiAlert.tsx b/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiAlert.tsx index ff17689288c..e1134cc7056 100644 --- a/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiAlert.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiAlert.tsx @@ -1,3 +1,5 @@ +import mimirLogoSvg from 'img/alerting/mimir_logo.svg'; + import { Trans } from '../../../../../../core/internationalization'; import { RuleFormType } from '../../../types/rule-form'; @@ -22,7 +24,7 @@ const MimirFlavoredType = ({ selected = false, disabled = false, onClick }: Prop } - image="public/img/alerting/mimir_logo.svg" + image={mimirLogoSvg} selected={selected} disabled={disabled} value={RuleFormType.cloudAlerting} diff --git a/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiRecordingRule.tsx b/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiRecordingRule.tsx index 226083ce922..6031d007fea 100644 --- a/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiRecordingRule.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/rule-types/MimirOrLokiRecordingRule.tsx @@ -1,3 +1,5 @@ +import mimirLogoRecordingSvg from 'img/alerting/mimir_logo_recording.svg'; + import { Trans } from '../../../../../../core/internationalization'; import { RuleFormType } from '../../../types/rule-form'; @@ -18,7 +20,7 @@ const RecordingRuleType = ({ selected = false, disabled = false, onClick }: Shar } - image="public/img/alerting/mimir_logo_recording.svg" + image={mimirLogoRecordingSvg} selected={selected} disabled={disabled} value={RuleFormType.cloudRecording} diff --git a/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx b/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx index 8499c8161cc..0b0e88aee33 100644 --- a/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx +++ b/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx @@ -2,6 +2,7 @@ import { capitalize } from 'lodash'; import { Badge, Button, Card, Stack, Text, TextLink } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; +import alertmanagerLogo from 'app/plugins/datasource/alertmanager/img/logo.svg'; import { ConnectionStatus } from '../../hooks/useExternalAmSelector'; import { ProvisioningBadge } from '../Provisioning'; @@ -28,7 +29,7 @@ export function AlertmanagerCard({ name, href, url, - logo = 'public/app/plugins/datasource/alertmanager/img/logo.svg', + logo = alertmanagerLogo, provisioned = false, readOnly = provisioned, showStatus = true, diff --git a/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx b/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx index b40619143f3..6bf84b0c0b7 100644 --- a/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx +++ b/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx @@ -1,3 +1,5 @@ +import grafanaIconSvg from 'img/grafana_icon.svg'; + import { ConnectionStatus } from '../../hooks/useExternalAmSelector'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; import { isInternalAlertmanagerInterestedInAlerts } from '../../utils/settings'; @@ -24,7 +26,7 @@ export default function InternalAlertmanager({ onEditConfiguration }: Props) { return ( @@ -41,11 +45,7 @@ export default function GettingStarted() {
- +
diff --git a/public/app/features/alerting/unified/rule-list/components/Namespace.tsx b/public/app/features/alerting/unified/rule-list/components/Namespace.tsx index 44f6a5caa5e..95141121cf7 100644 --- a/public/app/features/alerting/unified/rule-list/components/Namespace.tsx +++ b/public/app/features/alerting/unified/rule-list/components/Namespace.tsx @@ -4,6 +4,9 @@ import { PropsWithChildren } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Icon, Stack, TextLink, useStyles2 } from '@grafana/ui'; import { t } from 'app/core/internationalization'; +import lokiIconSvg from 'app/plugins/datasource/loki/img/loki_icon.svg'; +import mimirLogoSvg from 'app/plugins/datasource/prometheus/img/mimir_logo.svg'; +import prometheusLogoSvg from 'app/plugins/datasource/prometheus/img/prometheus_logo.svg'; import { PromApplication, RulesSourceApplication } from 'app/types/unified-alerting-dto'; import { WithReturnButton } from '../../components/WithReturnButton'; @@ -54,20 +57,11 @@ interface NamespaceIconProps { export const DataSourceIcon = ({ application, size = 16 }: NamespaceIconProps) => { switch (application) { case PromApplication.Prometheus: - return ( - Prometheus - ); + return Prometheus; case PromApplication.Mimir: - return ( - Mimir - ); + return Mimir; case 'Loki': - return Loki; + return Loki; case 'grafana': default: return ; diff --git a/public/app/features/alerting/unified/utils/datasource.ts b/public/app/features/alerting/unified/utils/datasource.ts index b39a2d66252..d54efeadcb7 100644 --- a/public/app/features/alerting/unified/utils/datasource.ts +++ b/public/app/features/alerting/unified/utils/datasource.ts @@ -18,6 +18,7 @@ import { RulesSourceIdentifier, RulesSourceUid, } from 'app/types/unified-alerting'; +import grafanaIconSvg from 'img/grafana_icon.svg'; import { alertmanagerApi } from '../api/alertmanagerApi'; import { PERMISSIONS_CONTACT_POINTS } from '../components/contact-points/permissions'; @@ -112,7 +113,7 @@ export function isAlertmanagerDataSourceInterestedInAlerts( const grafanaAlertManagerDataSource: AlertManagerDataSource = { name: GRAFANA_RULES_SOURCE_NAME, - imgUrl: 'public/img/grafana_icon.svg', + imgUrl: grafanaIconSvg, hasConfigurationAPI: true, }; diff --git a/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx b/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx index 42cdc8211f7..092e1b748e8 100644 --- a/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx +++ b/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx @@ -10,6 +10,8 @@ import { Icon, TextLink, Themeable2, withTheme2 } from '@grafana/ui'; import appEvents from 'app/core/app_events'; import { Trans, t } from 'app/core/internationalization'; import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/constants'; +import grabDarkSvg from 'img/grab_dark.svg'; +import grabLightSvg from 'img/grab_light.svg'; import { ShowConfirmModalEvent } from '../../../../types/events'; import { DashboardModel } from '../../state/DashboardModel'; @@ -175,6 +177,7 @@ export class UnthemedDashboardRow extends Component { export const DashboardRow = withTheme2(UnthemedDashboardRow); const getStyles = (theme: GrafanaTheme2) => { + const dragHandle = theme.name === 'dark' ? grabDarkSvg : grabLightSvg; const actions = css({ color: theme.colors.text.secondary, opacity: 0, @@ -244,7 +247,7 @@ const getStyles = (theme: GrafanaTheme2) => { cursor: 'move', width: '16px', height: '100%', - background: 'url("public/img/grab_dark.svg") no-repeat 50% 50%', + background: `url("${dragHandle}") no-repeat 50% 50%`, backgroundSize: '8px', visibility: 'hidden', position: 'absolute', diff --git a/public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx b/public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx index 9c1d602206a..6599f12fa9a 100644 --- a/public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx +++ b/public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx @@ -3,10 +3,12 @@ import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2, useTheme2 } from '@grafana/ui'; +import grafanaTextLogoDarkSvg from 'img/grafana_text_logo_dark.svg'; +import grafanaTextLogoLightSvg from 'img/grafana_text_logo_light.svg'; const FOOTER_URL = 'https://grafana.com/?src=grafananet&cnt=public-dashboards'; -const GRAFANA_LOGO_LIGHT_URL = 'public/img/grafana_text_logo_light.svg'; -const GRAFANA_LOGO_DARK_URL = 'public/img/grafana_text_logo_dark.svg'; +const GRAFANA_LOGO_LIGHT_URL = grafanaTextLogoLightSvg; +const GRAFANA_LOGO_DARK_URL = grafanaTextLogoDarkSvg; const GRAFANA_LOGO_DEFAULT_VALUE = 'grafana-logo'; export interface PublicDashboardCfg { diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationPickerNg.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationPickerNg.tsx index 3e71a6c3dac..8e37f3deaaf 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationPickerNg.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationPickerNg.tsx @@ -289,7 +289,7 @@ function getTransformationGridStyles(theme: GrafanaTheme2) { const getImagePath = (id: string, disabled: boolean) => { const folder = config.theme2.isDark ? 'dark' : 'light'; - return `public/img/transformations/${folder}/${id}.svg`; + return `public/build/img/transformations/${folder}/${id}.svg`; }; const TransformationDescriptionOverrides: { [key: string]: string } = { diff --git a/public/app/features/datasources/__mocks__/dataSourcesMocks.ts b/public/app/features/datasources/__mocks__/dataSourcesMocks.ts index de80e33284e..b58dec19453 100644 --- a/public/app/features/datasources/__mocks__/dataSourcesMocks.ts +++ b/public/app/features/datasources/__mocks__/dataSourcesMocks.ts @@ -1,6 +1,7 @@ import { merge } from 'lodash'; import { DataSourceSettings, DataSourcePluginMeta, DataSourceJsonData } from '@grafana/data'; +import amazonWebServicesPng from 'app/plugins/datasource/cloudwatch/img/amazon-web-services.png'; import { DataSourceSettingsState, PluginDashboard } from 'app/types'; export const getMockDashboard = (override?: Partial) => ({ @@ -51,7 +52,7 @@ export const getMockDataSource = ( orgId: 1, readOnly: false, type: 'cloudwatch', - typeLogoUrl: 'public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png', + typeLogoUrl: amazonWebServicesPng, url: '', user: '', secureJsonFields: {}, diff --git a/public/app/features/datasources/state/buildCategories.ts b/public/app/features/datasources/state/buildCategories.ts index 4758dcb359a..498ae84933a 100644 --- a/public/app/features/datasources/state/buildCategories.ts +++ b/public/app/features/datasources/state/buildCategories.ts @@ -1,6 +1,36 @@ import { DataSourcePluginMeta, PluginType } from '@grafana/data'; import { featureEnabled } from '@grafana/runtime'; import { DataSourcePluginCategory } from 'app/types'; +import grafanaIconSvg from 'img/grafana_icon.svg'; +import adobeAnalyticsSvg from 'img/plugins/adobe-analytics.svg'; +import appdynamicsSvg from 'img/plugins/appdynamics.svg'; +import atlassianStatuspageSvg from 'img/plugins/atlassian-statuspage.svg'; +import auroraSvg from 'img/plugins/aurora.svg'; +import azureCosmosdbSvg from 'img/plugins/azure-cosmosdb.svg'; +import azureDevopsPng from 'img/plugins/azure-devops.png'; +import catchpointSvg from 'img/plugins/catchpoint.svg'; +import cloudflareJpg from 'img/plugins/cloudflare.jpg'; +import cockroachdbJpg from 'img/plugins/cockroachdb.jpg'; +import datadogPng from 'img/plugins/datadog.png'; +import droneSvg from 'img/plugins/drone.svg'; +import dynatracePng from 'img/plugins/dynatrace.png'; +import gitlabSvg from 'img/plugins/gitlab.svg'; +import honeycombPng from 'img/plugins/honeycomb.png'; +import jiraLogoPng from 'img/plugins/jira_logo.png'; +import mongodbSvg from 'img/plugins/mongodb.svg'; +import netlifySvg from 'img/plugins/netlify.svg'; +import newrelicSvg from 'img/plugins/newrelic.svg'; +import oraclePng from 'img/plugins/oracle.png'; +import pagerdutySvg from 'img/plugins/pagerduty.svg'; +import salesforceSvg from 'img/plugins/salesforce.svg'; +import sapHanaPng from 'img/plugins/sap_hana.png'; +import servicenowSvg from 'img/plugins/servicenow.svg'; +import signalfxLogoSvg from 'img/plugins/signalfx-logo.svg'; +import snowflakeSvg from 'img/plugins/snowflake.svg'; +import splunkLogo128Png from 'img/plugins/splunk_logo_128.png'; +import sumoSvg from 'img/plugins/sumo.svg'; +import wavefrontSvg from 'img/plugins/wavefront.svg'; +import zendeskSvg from 'img/plugins/zendesk.svg'; export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePluginCategory[] { const categories: DataSourcePluginCategory[] = [ @@ -99,175 +129,175 @@ function getEnterprisePhantomPlugins(): DataSourcePluginMeta[] { id: 'grafana-splunk-datasource', name: 'Splunk', description: 'Visualize and explore Splunk logs', - imgUrl: 'public/img/plugins/splunk_logo_128.png', + imgUrl: splunkLogo128Png, }), getPhantomPlugin({ id: 'grafana-oracle-datasource', name: 'Oracle', description: 'Visualize and explore Oracle SQL', - imgUrl: 'public/img/plugins/oracle.png', + imgUrl: oraclePng, }), getPhantomPlugin({ id: 'grafana-dynatrace-datasource', name: 'Dynatrace', description: 'Visualize and explore Dynatrace data', - imgUrl: 'public/img/plugins/dynatrace.png', + imgUrl: dynatracePng, }), getPhantomPlugin({ id: 'grafana-servicenow-datasource', description: 'ServiceNow integration and data source', name: 'ServiceNow', - imgUrl: 'public/img/plugins/servicenow.svg', + imgUrl: servicenowSvg, }), getPhantomPlugin({ id: 'grafana-datadog-datasource', description: 'DataDog integration and data source', name: 'DataDog', - imgUrl: 'public/img/plugins/datadog.png', + imgUrl: datadogPng, }), getPhantomPlugin({ id: 'grafana-newrelic-datasource', description: 'New Relic integration and data source', name: 'New Relic', - imgUrl: 'public/img/plugins/newrelic.svg', + imgUrl: newrelicSvg, }), getPhantomPlugin({ id: 'grafana-mongodb-datasource', description: 'MongoDB integration and data source', name: 'MongoDB', - imgUrl: 'public/img/plugins/mongodb.svg', + imgUrl: mongodbSvg, }), getPhantomPlugin({ id: 'grafana-snowflake-datasource', description: 'Snowflake integration and data source', name: 'Snowflake', - imgUrl: 'public/img/plugins/snowflake.svg', + imgUrl: snowflakeSvg, }), getPhantomPlugin({ id: 'grafana-wavefront-datasource', description: 'Wavefront integration and data source', name: 'Wavefront', - imgUrl: 'public/img/plugins/wavefront.svg', + imgUrl: wavefrontSvg, }), getPhantomPlugin({ id: 'dlopes7-appdynamics-datasource', description: 'AppDynamics integration and data source', name: 'AppDynamics', - imgUrl: 'public/img/plugins/appdynamics.svg', + imgUrl: appdynamicsSvg, }), getPhantomPlugin({ id: 'grafana-saphana-datasource', description: 'SAP HANA® integration and data source', name: 'SAP HANA®', - imgUrl: 'public/img/plugins/sap_hana.png', + imgUrl: sapHanaPng, }), getPhantomPlugin({ id: 'grafana-honeycomb-datasource', description: 'Honeycomb integration and datasource', name: 'Honeycomb', - imgUrl: 'public/img/plugins/honeycomb.png', + imgUrl: honeycombPng, }), getPhantomPlugin({ id: 'grafana-salesforce-datasource', description: 'Salesforce integration and datasource', name: 'Salesforce', - imgUrl: 'public/img/plugins/salesforce.svg', + imgUrl: salesforceSvg, }), getPhantomPlugin({ id: 'grafana-jira-datasource', description: 'Jira integration and datasource', name: 'Jira', - imgUrl: 'public/img/plugins/jira_logo.png', + imgUrl: jiraLogoPng, }), getPhantomPlugin({ id: 'grafana-gitlab-datasource', description: 'GitLab integration and datasource', name: 'GitLab', - imgUrl: 'public/img/plugins/gitlab.svg', + imgUrl: gitlabSvg, }), getPhantomPlugin({ id: 'grafana-splunk-monitoring-datasource', description: 'SignalFx integration and datasource', name: 'Splunk Infrastructure Monitoring', - imgUrl: 'public/img/plugins/signalfx-logo.svg', + imgUrl: signalfxLogoSvg, }), getPhantomPlugin({ id: 'grafana-azuredevops-datasource', description: 'Azure Devops datasource', name: 'Azure Devops', - imgUrl: 'public/img/plugins/azure-devops.png', + imgUrl: azureDevopsPng, }), getPhantomPlugin({ id: 'grafana-sumologic-datasource', description: 'SumoLogic integration and datasource', name: 'SumoLogic', - imgUrl: 'public/img/plugins/sumo.svg', + imgUrl: sumoSvg, }), getPhantomPlugin({ id: 'grafana-pagerduty-datasource', description: 'PagerDuty datasource', name: 'PagerDuty', - imgUrl: 'public/img/plugins/pagerduty.svg', + imgUrl: pagerdutySvg, }), getPhantomPlugin({ id: 'grafana-catchpoint-datasource', description: 'Catchpoint datasource', name: 'Catchpoint', - imgUrl: 'public/img/plugins/catchpoint.svg', + imgUrl: catchpointSvg, }), getPhantomPlugin({ id: 'grafana-azurecosmosdb-datasource', description: 'Azure CosmosDB datasource', name: 'Azure CosmosDB', - imgUrl: 'public/img/plugins/azure-cosmosdb.svg', + imgUrl: azureCosmosdbSvg, }), getPhantomPlugin({ id: 'grafana-adobeanalytics-datasource', description: 'Adobe Analytics datasource', name: 'Adobe Analytics', - imgUrl: 'public/img/plugins/adobe-analytics.svg', + imgUrl: adobeAnalyticsSvg, }), getPhantomPlugin({ id: 'grafana-cloudflare-datasource', description: 'Cloudflare datasource', name: 'Cloudflare', - imgUrl: 'public/img/plugins/cloudflare.jpg', + imgUrl: cloudflareJpg, }), getPhantomPlugin({ id: 'grafana-cockroachdb-datasource', description: 'CockroachDB datasource', name: 'CockroachDB', - imgUrl: 'public/img/plugins/cockroachdb.jpg', + imgUrl: cockroachdbJpg, }), getPhantomPlugin({ id: 'grafana-netlify-datasource', description: 'Netlify datasource', name: 'Netlify', - imgUrl: 'public/img/plugins/netlify.svg', + imgUrl: netlifySvg, }), getPhantomPlugin({ id: 'grafana-drone-datasource', description: 'Drone datasource', name: 'Drone', - imgUrl: 'public/img/plugins/drone.svg', + imgUrl: droneSvg, }), getPhantomPlugin({ id: 'grafana-zendesk-datasource', description: 'Zendesk datasource', name: 'Zendesk', - imgUrl: 'public/img/plugins/zendesk.svg', + imgUrl: zendeskSvg, }), getPhantomPlugin({ id: 'grafana-atlassianstatuspage-datasource', description: 'Atlassian Statuspage datasource', name: 'Atlassian Statuspage', - imgUrl: 'public/img/plugins/atlassian-statuspage.svg', + imgUrl: atlassianStatuspageSvg, }), getPhantomPlugin({ id: 'grafana-aurora-datasource', description: 'Aurora data source', name: 'Aurora', - imgUrl: 'public/img/plugins/aurora.svg', + imgUrl: auroraSvg, }), ]; } @@ -281,7 +311,7 @@ function getGrafanaCloudPhantomPlugin(): DataSourcePluginMeta { baseUrl: '', info: { description: 'Hosted Graphite, Prometheus, and Loki', - logos: { small: 'public/img/grafana_icon.svg', large: 'asd' }, + logos: { small: grafanaIconSvg, large: grafanaIconSvg }, author: { name: 'Grafana Labs' }, links: [ { diff --git a/public/app/features/datasources/state/navModel.ts b/public/app/features/datasources/state/navModel.ts index ec804d29336..c576bf23055 100644 --- a/public/app/features/datasources/state/navModel.ts +++ b/public/app/features/datasources/state/navModel.ts @@ -5,6 +5,7 @@ import config from 'app/core/config'; import { contextSrv } from 'app/core/core'; import { highlightTrial } from 'app/features/admin/utils'; import { AccessControlAction } from 'app/types'; +import icnDatasourceSvg from 'img/icn-datasource.svg'; import { GenericDataSourcePlugin } from '../types'; @@ -169,7 +170,7 @@ export function getDataSourceLoadingNav(pageName: string): NavModel { readOnly: false, type: loadingDSType, typeName: loadingDSType, - typeLogoUrl: 'public/img/icn-datasource.svg', + typeLogoUrl: icnDatasourceSvg, url: '', user: '', secureJsonFields: {}, diff --git a/public/app/features/dimensions/editors/ResourcePicker.tsx b/public/app/features/dimensions/editors/ResourcePicker.tsx index 663d8a149c9..a3531488194 100644 --- a/public/app/features/dimensions/editors/ResourcePicker.tsx +++ b/public/app/features/dimensions/editors/ResourcePicker.tsx @@ -133,7 +133,7 @@ export const ResourcePicker = (props: Props) => { // strip the SVG off icons in the icons folder function getDisplayName(src?: string, name?: string): string | undefined { - if (src?.startsWith('public/img/icons')) { + if (src?.startsWith('public/build/img/icons')) { const idx = name?.lastIndexOf('.svg') ?? 0; if (idx > 0) { return name!.substring(0, idx); diff --git a/public/app/features/explore/RichHistory/RichHistoryCard.tsx b/public/app/features/explore/RichHistory/RichHistoryCard.tsx index b931ecec06e..6d115006d4d 100644 --- a/public/app/features/explore/RichHistory/RichHistoryCard.tsx +++ b/public/app/features/explore/RichHistory/RichHistoryCard.tsx @@ -19,6 +19,7 @@ import { setQueries } from 'app/features/explore/state/query'; import { dispatch } from 'app/store/store'; import { ShowConfirmModalEvent } from 'app/types/events'; import { RichHistoryQuery } from 'app/types/explore'; +import icnDatasourceSvg from 'img/icn-datasource.svg'; import ExploreRunQueryButton from '../ExploreRunQueryButton'; @@ -419,7 +420,7 @@ function DatasourceInfo({ dsApi, size }: { dsApi?: DataSourceApi; size: 'sm' | ' return (
{dsApi?.type diff --git a/public/app/features/expressions/ExpressionDatasource.ts b/public/app/features/expressions/ExpressionDatasource.ts index 18d43ba08b6..57f4b45ffbf 100644 --- a/public/app/features/expressions/ExpressionDatasource.ts +++ b/public/app/features/expressions/ExpressionDatasource.ts @@ -10,6 +10,7 @@ import { } from '@grafana/data'; import { DataSourceWithBackend, getDataSourceSrv, getTemplateSrv } from '@grafana/runtime'; import { ExpressionDatasourceRef } from '@grafana/runtime/internal'; +import icnDatasourceSvg from 'img/icn-datasource.svg'; import { ExpressionQueryEditor } from './ExpressionQueryEditor'; import { ExpressionDatasourceUID, ExpressionQuery, ExpressionQueryType } from './types'; @@ -77,8 +78,8 @@ export const instanceSettings: DataSourceInstanceSettings = { name: 'Grafana Labs', }, logos: { - small: 'public/img/icn-datasource.svg', - large: 'public/img/icn-datasource.svg', + small: icnDatasourceSvg, + large: icnDatasourceSvg, }, description: 'Adds expression support to Grafana', screenshots: [], @@ -96,8 +97,8 @@ dataSource.meta = { id: ExpressionDatasourceRef.type, info: { logos: { - small: 'public/img/icn-datasource.svg', - large: 'public/img/icn-datasource.svg', + small: icnDatasourceSvg, + large: icnDatasourceSvg, }, }, } as DataSourcePluginMeta; diff --git a/public/app/features/panel/components/PanelPluginError.tsx b/public/app/features/panel/components/PanelPluginError.tsx index 6ea6ad97698..5eaf38b0383 100644 --- a/public/app/features/panel/components/PanelPluginError.tsx +++ b/public/app/features/panel/components/PanelPluginError.tsx @@ -5,6 +5,7 @@ import { PureComponent, ReactNode } from 'react'; import { PanelProps, PanelPlugin, PluginType, PanelPluginMeta } from '@grafana/data'; import { Alert } from '@grafana/ui'; import { AppNotificationSeverity } from 'app/types'; +import grafanaIconSvg from 'img/grafana_icon.svg'; import { t, Trans } from '../../../core/internationalization'; @@ -85,7 +86,7 @@ export function getPanelPluginNotFound(id: string, silent?: boolean): PanelPlugi links: [], logos: { large: '', - small: 'public/img/grafana_icon.svg', + small: grafanaIconSvg, }, screenshots: [], updated: '', diff --git a/public/app/features/plugins/admin/helpers.ts b/public/app/features/plugins/admin/helpers.ts index 9152b412ad3..38660fba145 100644 --- a/public/app/features/plugins/admin/helpers.ts +++ b/public/app/features/plugins/admin/helpers.ts @@ -222,8 +222,8 @@ export function mapToCatalogPlugin(local?: LocalPlugin, remote?: RemotePlugin, e const keywords = remote?.keywords || local?.info.keywords || []; let logos = { - small: `/public/img/icn-${type}.svg`, - large: `/public/img/icn-${type}.svg`, + small: `/public/build/img/icn-${type}.svg`, + large: `/public/build/img/icn-${type}.svg`, }; if (remote) { diff --git a/public/app/features/provisioning/GettingStarted/GettingStarted.tsx b/public/app/features/provisioning/GettingStarted/GettingStarted.tsx index a734d7029b4..5610a5f7c8d 100644 --- a/public/app/features/provisioning/GettingStarted/GettingStarted.tsx +++ b/public/app/features/provisioning/GettingStarted/GettingStarted.tsx @@ -5,6 +5,7 @@ import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Stack, useStyles2 } from '@grafana/ui'; import { useGetFrontendSettingsQuery, Repository } from 'app/api/clients/provisioning'; import { t, Trans } from 'app/core/internationalization'; +import provisioningSvg from 'img/provisioning/provisioning.svg'; import { EnhancedFeatures } from './EnhancedFeatures'; import { FeaturesList } from './FeaturesList'; @@ -154,11 +155,7 @@ export default function GettingStarted({ items }: Props) { }} />
- {'Grafana + {'Grafana
{(!hasPublicAccess || !hasImageRenderer) && hasItems && ( diff --git a/public/app/features/teams/state/navModel.ts b/public/app/features/teams/state/navModel.ts index 4b225312b40..4a3edfbb769 100644 --- a/public/app/features/teams/state/navModel.ts +++ b/public/app/features/teams/state/navModel.ts @@ -5,9 +5,10 @@ import config from 'app/core/config'; import { contextSrv } from 'app/core/services/context_srv'; import { highlightTrial } from 'app/features/admin/utils'; import { AccessControlAction, Team, TeamPermissionLevel } from 'app/types'; +import userProfilePng from 'img/user_profile.png'; const loadingTeam = { - avatarUrl: 'public/img/user_profile.png', + avatarUrl: userProfilePng, id: 1, uid: '', name: 'Loading', diff --git a/public/app/features/variables/pickers/shared/VariableOptions.tsx b/public/app/features/variables/pickers/shared/VariableOptions.tsx index 22c6a7d7e2e..fd9544a99d3 100644 --- a/public/app/features/variables/pickers/shared/VariableOptions.tsx +++ b/public/app/features/variables/pickers/shared/VariableOptions.tsx @@ -6,6 +6,8 @@ import { GrafanaTheme2, VariableOption } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Tooltip, Themeable2, withTheme2, clearButtonStyles, stylesFactory } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; +import checkboxPng from 'img/checkbox.png'; +import checkboxWhitePng from 'img/checkbox_white.png'; import { ALL_VARIABLE_VALUE } from '../../constants'; @@ -139,7 +141,7 @@ class VariableOptions extends PureComponent { } const getStyles = stylesFactory((theme: GrafanaTheme2) => { - const checkboxImageUrl = theme.isDark ? 'public/img/checkbox.png' : 'public/img/checkbox_white.png'; + const checkboxImageUrl = theme.isDark ? checkboxPng : checkboxWhitePng; return { hideVariableOptionIcon: css({ diff --git a/public/app/plugins/datasource/cloud-monitoring/components/__snapshots__/VariableQueryEditor.test.tsx.snap b/public/app/plugins/datasource/cloud-monitoring/components/__snapshots__/VariableQueryEditor.test.tsx.snap index 9deeaac0e1a..5506942fcce 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/__snapshots__/VariableQueryEditor.test.tsx.snap +++ b/public/app/plugins/datasource/cloud-monitoring/components/__snapshots__/VariableQueryEditor.test.tsx.snap @@ -73,7 +73,7 @@ exports[`VariableQueryEditor renders correctly 1`] = ` aria-hidden="true" class="css-1d3xu67-Icon" height="16" - id="public/img/icons/unicons/angle-down.svg" + id="public/build/img/icons/unicons/angle-down.svg" loader="[object Object]" title="" width="16" diff --git a/public/app/plugins/panel/table/suggestions.ts b/public/app/plugins/panel/table/suggestions.ts index a848257318f..bcfead0fe31 100644 --- a/public/app/plugins/panel/table/suggestions.ts +++ b/public/app/plugins/panel/table/suggestions.ts @@ -1,5 +1,6 @@ import { VisualizationSuggestionsBuilder } from '@grafana/data'; import { TableFieldOptions } from '@grafana/schema'; +import icnTablePanelSvg from 'app/plugins/panel/table/img/icn-table-panel.svg'; import { SuggestionName } from 'app/types/suggestions'; import { Options } from './panelcfg.gen'; @@ -27,7 +28,7 @@ export class TableSuggestionsSupplier { if (builder.dataSummary.fieldCount === 0) { list.append({ cardOptions: { - imgSrc: 'public/app/plugins/panel/table/img/icn-table-panel.svg', + imgSrc: icnTablePanelSvg, }, }); } else { diff --git a/public/app/plugins/panel/table/table-new/suggestions.ts b/public/app/plugins/panel/table/table-new/suggestions.ts index a848257318f..bcfead0fe31 100644 --- a/public/app/plugins/panel/table/table-new/suggestions.ts +++ b/public/app/plugins/panel/table/table-new/suggestions.ts @@ -1,5 +1,6 @@ import { VisualizationSuggestionsBuilder } from '@grafana/data'; import { TableFieldOptions } from '@grafana/schema'; +import icnTablePanelSvg from 'app/plugins/panel/table/img/icn-table-panel.svg'; import { SuggestionName } from 'app/types/suggestions'; import { Options } from './panelcfg.gen'; @@ -27,7 +28,7 @@ export class TableSuggestionsSupplier { if (builder.dataSummary.fieldCount === 0) { list.append({ cardOptions: { - imgSrc: 'public/app/plugins/panel/table/img/icn-table-panel.svg', + imgSrc: icnTablePanelSvg, }, }); } else { diff --git a/public/app/types/svg.d.ts b/public/app/types/images.d.ts similarity index 61% rename from public/app/types/svg.d.ts rename to public/app/types/images.d.ts index cdb2b1a9a23..e0be1b3c071 100644 --- a/public/app/types/svg.d.ts +++ b/public/app/types/images.d.ts @@ -2,3 +2,6 @@ declare module '*.svg' { const content: string; export default content; } + +declare module '*.png'; +declare module '*.jpg'; diff --git a/public/swagger/SwaggerPage.tsx b/public/swagger/SwaggerPage.tsx index f07a8d29f03..48a1eadc9ce 100644 --- a/public/swagger/SwaggerPage.tsx +++ b/public/swagger/SwaggerPage.tsx @@ -7,6 +7,7 @@ import { createTheme, monacoLanguageRegistry, SelectableValue } from '@grafana/d import { Stack, Select, UserIcon, UserView, Button } from '@grafana/ui'; import { setMonacoEnv } from 'app/core/monacoEnv'; import { ThemeProvider } from 'app/core/utils/ConfigProvider'; +import grafanaIconSvg from 'img/grafana_icon.svg'; import { Trans } from '../app/core/internationalization'; @@ -85,7 +86,7 @@ export const Page = () => {
- Grafana + Grafana