mirror of https://github.com/grafana/grafana.git
FEMT: Add `no-restricted-img-srcs` rule (#105006)
This commit is contained in:
parent
56cfeb8616
commit
8f17f607fa
|
|
@ -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'] }],
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ module.exports = {
|
|||
__webpack_public_path__: '', // empty string
|
||||
},
|
||||
moduleNameMapper: {
|
||||
'\\.svg': '<rootDir>/public/test/mocks/svg.ts',
|
||||
'\\.(svg|png|jpg)': '<rootDir>/public/test/mocks/images.ts',
|
||||
'\\.css': '<rootDir>/public/test/mocks/style.ts',
|
||||
'react-inlinesvg': '<rootDir>/public/test/mocks/react-inlinesvg.tsx',
|
||||
// resolve directly as monaco and kusto don't have main property in package.json which jest needs
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 = <img src={foo} />;
|
||||
`,
|
||||
},
|
||||
{
|
||||
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: `<img src="public/img/checkbox.png" />`,
|
||||
errors: [
|
||||
{
|
||||
messageId: 'publicImg',
|
||||
suggestions: [
|
||||
{
|
||||
messageId: 'importImage',
|
||||
output: `import checkboxPng from 'img/checkbox.png';
|
||||
<img src={checkboxPng} />`,
|
||||
},
|
||||
{
|
||||
messageId: 'useBuildFolder',
|
||||
output: `<img src="public/build/img/checkbox.png" />`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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;`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -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<PluginMetaInfo> = {}): 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: '',
|
||||
|
|
|
|||
|
|
@ -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`;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
>
|
||||
<img src="public/img/grot-news.svg" alt="Grot reading news" />
|
||||
<img src={grotNewsSvg} alt="Grot reading news" />
|
||||
</a>
|
||||
<div className={styles.actions}>
|
||||
<IconButton
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { GrafanaTheme2 } from '@grafana/data';
|
|||
import { useStyles2 } from '@grafana/ui';
|
||||
import { News } from 'app/plugins/panel/news/component/News';
|
||||
import { useNewsFeed } from 'app/plugins/panel/news/useNewsFeed';
|
||||
import grotNewsSvg from 'img/grot-news.svg';
|
||||
|
||||
import { t } from '../../../internationalization';
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ export function NewsWrapper({ feedUrl }: NewsWrapperProps) {
|
|||
rel="noreferrer"
|
||||
title={t('news.link-title', 'Go to Grafana labs blog')}
|
||||
>
|
||||
<img src="public/img/grot-news.svg" alt="Grot reading news" />
|
||||
<img src={grotNewsSvg} alt="Grot reading news" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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')}
|
||||
>
|
||||
<div className={styles.bounce}>
|
||||
<img alt="" src="public/img/grafana_icon.svg" className={styles.logo} />
|
||||
<img alt="" src={grafanaIconSvg} className={styles.logo} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<BrandComponentProps & { logo?: string }> = ({ className, logo }) => {
|
||||
return <img className={className} src={`${logo ? logo : 'public/img/grafana_icon.svg'}`} alt="Grafana" />;
|
||||
return <img className={className} src={`${logo ? logo : grafanaIconSvg}`} alt="Grafana" />;
|
||||
};
|
||||
|
||||
const LoginBackground: FC<BrandComponentProps> = ({ className, children }) => {
|
||||
|
|
@ -24,7 +27,7 @@ const LoginBackground: FC<BrandComponentProps> = ({ 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<BrandComponentProps> = ({ className, children }) => {
|
|||
};
|
||||
|
||||
const MenuLogo: FC<BrandComponentProps> = ({ className }) => {
|
||||
return <img className={className} src="public/img/grafana_icon.svg" alt="Grafana" />;
|
||||
return <img className={className} src={grafanaIconSvg} alt="Grafana" />;
|
||||
};
|
||||
|
||||
const LoginBoxBackground = () => {
|
||||
|
|
|
|||
|
|
@ -271,5 +271,5 @@ const getImgUrl = (urlOrId: string) => {
|
|||
return urlOrId;
|
||||
}
|
||||
|
||||
return '/public/img/enterprise/highlights/' + urlOrId;
|
||||
return '/public/build/img/enterprise/highlights/' + urlOrId;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}}
|
||||
>
|
||||
<img
|
||||
src="public/img/grafana_icon.svg"
|
||||
src={grafanaIconSvg}
|
||||
alt="Grafana"
|
||||
width="80px"
|
||||
style={{ position: 'absolute', left: '23px', top: '20px' }}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@ import { GrafanaTheme2, NavModel } from '@grafana/data';
|
|||
import { LinkButton, useStyles2 } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import checkmarkSvg from 'img/licensing/checkmark.svg';
|
||||
import customerSupportSvg from 'img/licensing/customer_support.svg';
|
||||
import handinhandSupportSvg from 'img/licensing/handinhand_support.svg';
|
||||
import pluginEnterpriseSvg from 'img/licensing/plugin_enterprise.svg';
|
||||
import slaSvg from 'img/licensing/sla.svg';
|
||||
|
||||
import { getNavModel } from '../../core/selectors/navModel';
|
||||
import { StoreState } from '../../types';
|
||||
|
|
@ -112,15 +117,12 @@ const ServiceInfo = () => {
|
|||
<List>
|
||||
<Item
|
||||
title={t('admin.service-info.title-enterprise-plugins', 'Enterprise Plugins')}
|
||||
image="public/img/licensing/plugin_enterprise.svg"
|
||||
/>
|
||||
<Item
|
||||
title={t('admin.service-info.title-critical-sla-hours', 'Critical SLA: 2 hours')}
|
||||
image="public/img/licensing/sla.svg"
|
||||
image={pluginEnterpriseSvg}
|
||||
/>
|
||||
<Item title={t('admin.service-info.title-critical-sla-hours', 'Critical SLA: 2 hours')} image={slaSvg} />
|
||||
<Item
|
||||
title={t('admin.service-info.title-unlimited-expert-support', 'Unlimited Expert Support')}
|
||||
image="public/img/licensing/customer_support.svg"
|
||||
image={customerSupportSvg}
|
||||
>
|
||||
<Trans i18nKey="admin.service-info.year-round-support">24 × 7 × 365 support via</Trans>
|
||||
<List nested={true}>
|
||||
|
|
@ -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}
|
||||
/>
|
||||
</List>
|
||||
|
||||
|
|
@ -241,7 +243,7 @@ interface ItemProps {
|
|||
}
|
||||
|
||||
const Item = ({ children, title, image }: React.PropsWithChildren<ItemProps>) => {
|
||||
const imageUrl = image ? image : 'public/img/licensing/checkmark.svg';
|
||||
const imageUrl = image ? image : checkmarkSvg;
|
||||
const itemStyle = css({
|
||||
display: 'flex',
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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, string> = {
|
||||
[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]: '',
|
||||
|
|
|
|||
|
|
@ -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
|
|||
</Trans>
|
||||
</span>
|
||||
}
|
||||
image="public/img/grafana_icon.svg"
|
||||
image={grafanaIconSvg}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
value={RuleFormType.grafana}
|
||||
|
|
|
|||
|
|
@ -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
|
|||
</Trans>
|
||||
</span>
|
||||
}
|
||||
image="public/img/alerting/mimir_logo.svg"
|
||||
image={mimirLogoSvg}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
value={RuleFormType.cloudAlerting}
|
||||
|
|
|
|||
|
|
@ -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
|
|||
</Trans>
|
||||
</span>
|
||||
}
|
||||
image="public/img/alerting/mimir_logo_recording.svg"
|
||||
image={mimirLogoRecordingSvg}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
value={RuleFormType.cloudRecording}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<AlertmanagerCard
|
||||
name={BUILTIN_ALERTMANAGER_NAME}
|
||||
logo="public/img/grafana_icon.svg"
|
||||
logo={grafanaIconSvg}
|
||||
status={status}
|
||||
receiving={isReceiving}
|
||||
onEditConfiguration={handleEditConfiguration}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ import SVG from 'react-inlinesvg';
|
|||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Stack, Text, TextLink, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import atAGlanceDarkSvg from 'img/alerting/at_a_glance_dark.svg';
|
||||
import atAGlanceLightSvg from 'img/alerting/at_a_glance_light.svg';
|
||||
|
||||
export default function GettingStarted() {
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getWelcomePageStyles);
|
||||
|
||||
const atAGlanceImage = theme.name === 'dark' ? atAGlanceDarkSvg : atAGlanceLightSvg;
|
||||
|
||||
return (
|
||||
<div className={styles.grid}>
|
||||
<ContentBox>
|
||||
|
|
@ -41,11 +45,7 @@ export default function GettingStarted() {
|
|||
</ul>
|
||||
<div className={styles.svgContainer}>
|
||||
<Stack justifyContent={'center'}>
|
||||
<SVG
|
||||
src={`public/img/alerting/at_a_glance_${theme.name.toLowerCase()}.svg`}
|
||||
width={undefined}
|
||||
height={undefined}
|
||||
/>
|
||||
<SVG src={atAGlanceImage} width={undefined} height={undefined} />
|
||||
</Stack>
|
||||
</div>
|
||||
</Stack>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<img
|
||||
width={size}
|
||||
height={size}
|
||||
src="public/app/plugins/datasource/prometheus/img/prometheus_logo.svg"
|
||||
alt="Prometheus"
|
||||
/>
|
||||
);
|
||||
return <img width={size} height={size} src={prometheusLogoSvg} alt="Prometheus" />;
|
||||
case PromApplication.Mimir:
|
||||
return (
|
||||
<img width={size} height={size} src="public/app/plugins/datasource/prometheus/img/mimir_logo.svg" alt="Mimir" />
|
||||
);
|
||||
return <img width={size} height={size} src={mimirLogoSvg} alt="Mimir" />;
|
||||
case 'Loki':
|
||||
return <img width={size} height={size} src="public/app/plugins/datasource/loki/img/loki_icon.svg" alt="Loki" />;
|
||||
return <img width={size} height={size} src={lokiIconSvg} alt="Loki" />;
|
||||
case 'grafana':
|
||||
default:
|
||||
return <Icon name="grafana" />;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<DashboardRowProps> {
|
|||
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',
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 } = {
|
||||
|
|
|
|||
|
|
@ -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<PluginDashboard>) => ({
|
||||
|
|
@ -51,7 +52,7 @@ export const getMockDataSource = <T extends DataSourceJsonData>(
|
|||
orgId: 1,
|
||||
readOnly: false,
|
||||
type: 'cloudwatch',
|
||||
typeLogoUrl: 'public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png',
|
||||
typeLogoUrl: amazonWebServicesPng,
|
||||
url: '',
|
||||
user: '',
|
||||
secureJsonFields: {},
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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: {},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className={styles}>
|
||||
<img
|
||||
src={dsApi?.meta.info.logos.small || 'public/img/icn-datasource.svg'}
|
||||
src={dsApi?.meta.info.logos.small || icnDatasourceSvg}
|
||||
alt={dsApi?.type || t('explore.rich-history-card.datasource-not-exist', 'Data source does not exist anymore')}
|
||||
aria-label={t('explore.rich-history-card.datasource-icon-label', 'Data source icon')}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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: '',
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
|||
}}
|
||||
/>
|
||||
<div className={styles.imageContainer}>
|
||||
<img
|
||||
src={'public/img/provisioning/provisioning.svg'}
|
||||
className={styles.image}
|
||||
alt={'Grafana provisioning'}
|
||||
/>
|
||||
<img src={provisioningSvg} className={styles.image} alt={'Grafana provisioning'} />
|
||||
</div>
|
||||
</Stack>
|
||||
{(!hasPublicAccess || !hasImageRenderer) && hasItems && (
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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<Props> {
|
|||
}
|
||||
|
||||
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({
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -2,3 +2,6 @@ declare module '*.svg' {
|
|||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.png';
|
||||
declare module '*.jpg';
|
||||
|
|
@ -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 = () => {
|
|||
<NamespaceContext.Provider value={namespace.value}>
|
||||
<div style={{ backgroundColor: '#000', padding: '10px' }}>
|
||||
<Stack justifyContent={'space-between'}>
|
||||
<img height="40" src="public/img/grafana_icon.svg" alt="Grafana" />
|
||||
<img height="40" src={grafanaIconSvg} alt="Grafana" />
|
||||
<Select
|
||||
options={urls.value}
|
||||
isClearable={false /* TODO -- when we allow a landing page, this can be true */}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export default '__DEFAULT_MOCK_IMAGE_CONTENT__';
|
||||
|
|
@ -1 +0,0 @@
|
|||
export const svg = 'svg';
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
|
|
@ -68,6 +69,14 @@ module.exports = {
|
|||
new webpack.ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: 'public/img',
|
||||
to: 'img',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
|
|
|
|||
Loading…
Reference in New Issue