mirror of
https://github.com/grafana/grafana.git
synced 2025-12-18 22:16:21 -05:00
e2e: add tests for translations (#114390)
e2e: add tests for translations
This commit is contained in:
parent
8bf3ac9710
commit
3490c3b0fd
46 changed files with 678 additions and 20 deletions
|
|
@ -21,3 +21,7 @@ apps:
|
|||
org_id: 1
|
||||
org_name: Main Org.
|
||||
disabled: false
|
||||
panels:
|
||||
- type: grafana-e2etest-panel
|
||||
org_id: 1
|
||||
org_name: Main Org.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Route, Routes } from 'react-router-dom';
|
|||
import { AppRootProps } from '@grafana/data';
|
||||
|
||||
import { ROUTES } from '../../constants';
|
||||
import { AddedComponents, AddedLinks, ExposedComponents } from '../../pages';
|
||||
import { AddedComponents, AddedLinks, Config, ExposedComponents } from '../../pages';
|
||||
import { testIds } from '../../testIds';
|
||||
|
||||
export function App(props: AppRootProps) {
|
||||
|
|
@ -13,6 +13,7 @@ export function App(props: AppRootProps) {
|
|||
<Route path={ROUTES.ExposedComponents} element={<ExposedComponents />} />
|
||||
<Route path={ROUTES.AddedComponents} element={<AddedComponents />} />
|
||||
<Route path={ROUTES.AddedLinks} element={<AddedLinks />} />
|
||||
<Route path={ROUTES.Config} element={<Config />} />
|
||||
|
||||
<Route path={'*'} element={<ExposedComponents />} />
|
||||
</Routes>
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@ export enum ROUTES {
|
|||
ExposedComponents = 'exposed-components',
|
||||
AddedComponents = 'added-components',
|
||||
AddedLinks = 'added-links',
|
||||
Config = 'config',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { defineConfig } from 'i18next-cli';
|
||||
import pluginJson from './plugin.json';
|
||||
|
||||
export default defineConfig({
|
||||
locales: pluginJson.languages,
|
||||
extract: {
|
||||
input: ['**/*.{tsx,ts}'],
|
||||
output: 'locales/{{language}}/{{namespace}}.json',
|
||||
defaultNS: pluginJson.id,
|
||||
functions: ['t', '*.t'],
|
||||
transComponents: ['Trans'],
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"config-page": {
|
||||
"header": {
|
||||
"text": "Is this translated"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"config-page": {
|
||||
"header": {
|
||||
"text": "¿Está traducido?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"config-page": {
|
||||
"header": {
|
||||
"text": "Det här är översatt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,9 @@ import { App } from './components/App';
|
|||
import { QueryModal } from './components/QueryModal';
|
||||
import { selectQuery } from './utils/utils';
|
||||
import pluginJson from './plugin.json';
|
||||
import { initPluginTranslations } from '@grafana/i18n';
|
||||
|
||||
await initPluginTranslations(pluginJson.id);
|
||||
|
||||
export const plugin = new AppPlugin<{}>()
|
||||
.setRootPage(App)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
"build": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -c ./webpack.config.ts --env production",
|
||||
"dev": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -w -c ./webpack.config.ts --env development",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx ."
|
||||
"lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx .",
|
||||
"i18n-extract": "i18next-cli extract --sync-primary"
|
||||
},
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
|
|
@ -20,17 +21,19 @@
|
|||
"@types/semver": "7.5.8",
|
||||
"@types/uuid": "9.0.8",
|
||||
"glob": "10.5.0",
|
||||
"i18next-cli": "^1.24.22",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.5.4",
|
||||
"webpack": "5.95.0",
|
||||
"webpack-merge": "5.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">= 22 <25"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.11.2",
|
||||
"@grafana/data": "workspace:*",
|
||||
"@grafana/i18n": "workspace:*",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
"@grafana/schema": "workspace:*",
|
||||
"@grafana/ui": "workspace:*",
|
||||
|
|
@ -42,5 +45,6 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"@grafana/runtime": "*"
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@4.11.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { Trans } from '@grafana/i18n';
|
||||
import { PluginPage } from '@grafana/runtime';
|
||||
import { Stack } from '@grafana/ui';
|
||||
|
||||
export function Config() {
|
||||
return (
|
||||
<PluginPage>
|
||||
<Stack direction={'column'} gap={4}>
|
||||
<section>
|
||||
<h3>
|
||||
<Trans i18nKey="config-page.header.text">Is this translated</Trans>
|
||||
</h3>
|
||||
</section>
|
||||
</Stack>
|
||||
</PluginPage>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export { ExposedComponents } from './ExposedComponents';
|
||||
export { AddedComponents } from './AddedComponents';
|
||||
export { AddedLinks } from './AddedLinks';
|
||||
export { Config } from './Config';
|
||||
|
|
|
|||
|
|
@ -82,10 +82,11 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=10.4.0",
|
||||
"grafanaDependency": ">=12.0.0",
|
||||
"plugins": [],
|
||||
"extensions": {
|
||||
"exposedComponents": ["grafana-extensionexample1-app/reusable-component/v1", "grafana/add-to-dashboard-form/v1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"languages": ["en-US", "es-ES", "sv-SE"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import { FRENCH_FRANCE } from '@grafana/i18n';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
import pluginJson from '../../plugin.json';
|
||||
import { ROUTES } from '../../constants';
|
||||
|
||||
test.use({ userPreferences: { language: FRENCH_FRANCE } });
|
||||
|
||||
test('should display default translation (en-US)', async ({ gotoAppPage }) => {
|
||||
const configPage = await gotoAppPage({ pluginId: pluginJson.id, path: ROUTES.Config });
|
||||
|
||||
await expect(configPage.ctx.page.getByText('Is this translated')).toBeVisible();
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { SWEDISH_SWEDEN } from '@grafana/i18n';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
import pluginJson from '../../plugin.json';
|
||||
import { ROUTES } from '../../constants';
|
||||
|
||||
test.use({ userPreferences: { language: SWEDISH_SWEDEN } });
|
||||
|
||||
test('should display correct translation', async ({ gotoAppPage }) => {
|
||||
const configPage = await gotoAppPage({ pluginId: pluginJson.id, path: ROUTES.Config });
|
||||
|
||||
await expect(configPage.ctx.page.getByText('Det här är översatt')).toBeVisible();
|
||||
});
|
||||
|
|
@ -34,6 +34,7 @@ const config = async (env: Env): Promise<Configuration> => {
|
|||
],
|
||||
}),
|
||||
],
|
||||
externals: [...(baseConfig.externals as any), 'i18next'],
|
||||
};
|
||||
|
||||
return mergeWithCustomize({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ChangeEvent } from 'react';
|
||||
import { Checkbox, InlineField, InlineSwitch, Input, SecretInput, Select } from '@grafana/ui';
|
||||
import { DataSourcePluginOptionsEditorProps, SelectableValue, toOption } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { MyDataSourceOptions, MySecureJsonData } from '../types';
|
||||
|
||||
interface Props extends DataSourcePluginOptionsEditorProps<MyDataSourceOptions, MySecureJsonData> {}
|
||||
|
|
@ -45,36 +46,46 @@ export function ConfigEditor(props: Props) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<InlineField label="Path" labelWidth={14} interactive tooltip={'Json field returned to frontend'}>
|
||||
<InlineField
|
||||
label={t('config-editor.path.label', 'Path')}
|
||||
labelWidth={14}
|
||||
interactive
|
||||
tooltip={t('config-editor.path.tooltip', 'Json field returned to frontend')}
|
||||
>
|
||||
<Input
|
||||
id="config-editor-path"
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => onJsonDataChange('path', e.target.value)}
|
||||
value={jsonData.path}
|
||||
placeholder="Enter the path, e.g. /api/v1"
|
||||
placeholder={t('config-editor.path.placeholder', 'Enter the path, e.g. /api/v1')}
|
||||
width={40}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="API Key" labelWidth={14} interactive tooltip={'Secure json field (backend only)'}>
|
||||
<InlineField
|
||||
label={t('config-editor.api-key.label', 'API Key')}
|
||||
labelWidth={14}
|
||||
interactive
|
||||
tooltip={t('config-editor.api-key.tooltip', 'Secure json field (backend only)')}
|
||||
>
|
||||
<SecretInput
|
||||
required
|
||||
id="config-editor-api-key"
|
||||
isConfigured={secureJsonFields.apiKey}
|
||||
value={secureJsonData?.apiKey}
|
||||
placeholder="Enter your API key"
|
||||
placeholder={t('config-editor.api-key.placeholder', 'Enter your API key')}
|
||||
width={40}
|
||||
onReset={onResetAPIKey}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => onSecureJsonDataChange('path', e.target.value)}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Switch Enabled">
|
||||
<InlineField label={t('config-editor.switch-enabled.label', 'Switch Enabled')}>
|
||||
<InlineSwitch
|
||||
width={40}
|
||||
label="Switch Enabled"
|
||||
label={t('config-editor.switch-enabled.label', 'Switch Enabled')}
|
||||
value={jsonData.switchEnabled ?? false}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => onJsonDataChange('switchEnabled', e.target.checked)}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Checkbox Enabled">
|
||||
<InlineField label={t('config-editor.checkbox-enabled.label', 'Checkbox Enabled')}>
|
||||
<Checkbox
|
||||
width={40}
|
||||
id="config-checkbox-enabled"
|
||||
|
|
@ -82,7 +93,7 @@ export function ConfigEditor(props: Props) {
|
|||
onChange={(e: ChangeEvent<HTMLInputElement>) => onJsonDataChange('checkboxEnabled', e.target.checked)}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Auth type">
|
||||
<InlineField label={t('config-editor.auth-type.label', 'Auth type')}>
|
||||
<Select
|
||||
width={40}
|
||||
inputId="config-auth-type"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { defineConfig } from 'i18next-cli';
|
||||
import pluginJson from './plugin.json';
|
||||
|
||||
export default defineConfig({
|
||||
locales: pluginJson.languages,
|
||||
extract: {
|
||||
input: ['**/*.{tsx,ts}'],
|
||||
output: 'locales/{{language}}/{{namespace}}.json',
|
||||
defaultNS: pluginJson.id,
|
||||
functions: ['t', '*.t'],
|
||||
transComponents: ['Trans'],
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"config-editor": {
|
||||
"api-key": {
|
||||
"label": "API Key",
|
||||
"placeholder": "Enter your API key",
|
||||
"tooltip": "Secure json field (backend only)"
|
||||
},
|
||||
"auth-type": {
|
||||
"label": "Auth type"
|
||||
},
|
||||
"checkbox-enabled": {
|
||||
"label": "Checkbox Enabled"
|
||||
},
|
||||
"path": {
|
||||
"label": "Path",
|
||||
"placeholder": "Enter the path, e.g. /api/v1",
|
||||
"tooltip": "Json field returned to frontend"
|
||||
},
|
||||
"switch-enabled": {
|
||||
"label": "Switch Enabled"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"config-editor": {
|
||||
"api-key": {
|
||||
"label": "Clave de API",
|
||||
"placeholder": "Ingrese su clave de API",
|
||||
"tooltip": "Campo JSON seguro (solo backend)"
|
||||
},
|
||||
"auth-type": {
|
||||
"label": "Tipo de autenticación"
|
||||
},
|
||||
"checkbox-enabled": {
|
||||
"label": "Casilla habilitada"
|
||||
},
|
||||
"path": {
|
||||
"label": "Ruta",
|
||||
"placeholder": "Ingrese la ruta, p. ej. /api/v1",
|
||||
"tooltip": "Campo JSON devuelto al frontend"
|
||||
},
|
||||
"switch-enabled": {
|
||||
"label": "Interruptor habilitado"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"config-editor": {
|
||||
"api-key": {
|
||||
"label": "API-nyckel",
|
||||
"placeholder": "Ange din API-nyckel",
|
||||
"tooltip": "Säkert json-fält (endast backend)"
|
||||
},
|
||||
"auth-type": {
|
||||
"label": "Autentiseringstyp"
|
||||
},
|
||||
"checkbox-enabled": {
|
||||
"label": "Kryssruta aktiverad"
|
||||
},
|
||||
"path": {
|
||||
"label": "Sökväg",
|
||||
"placeholder": "Ange sökvägen, t.ex. /api/v1",
|
||||
"tooltip": "Json-fält som returneras till frontend"
|
||||
},
|
||||
"switch-enabled": {
|
||||
"label": "Omkopplare aktiverad"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,10 @@ import { DataSource } from './datasource';
|
|||
import { ConfigEditor } from './components/ConfigEditor';
|
||||
import { QueryEditor } from './components/QueryEditor';
|
||||
import { MyQuery, MyDataSourceOptions } from './types';
|
||||
import pluginJson from './plugin.json';
|
||||
import { initPluginTranslations } from '@grafana/i18n';
|
||||
|
||||
await initPluginTranslations(pluginJson.id);
|
||||
|
||||
export const plugin = new DataSourcePlugin<DataSource, MyQuery, MyDataSourceOptions>(DataSource)
|
||||
.setConfigEditor(ConfigEditor)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
"build": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -c ./webpack.config.ts --env production",
|
||||
"dev": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -w -c ./webpack.config.ts --env development",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx ."
|
||||
"lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx .",
|
||||
"i18n-extract": "i18next-cli extract --sync-primary"
|
||||
},
|
||||
"author": "Grafana",
|
||||
"license": "Apache-2.0",
|
||||
|
|
@ -20,17 +21,19 @@
|
|||
"@types/semver": "7.5.8",
|
||||
"@types/uuid": "9.0.8",
|
||||
"glob": "10.4.1",
|
||||
"i18next-cli": "^1.24.22",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.5.4",
|
||||
"webpack": "5.95.0",
|
||||
"webpack-merge": "5.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">= 22 <25"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.11.2",
|
||||
"@grafana/data": "workspace:*",
|
||||
"@grafana/i18n": "workspace:*",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
"@grafana/schema": "workspace:*",
|
||||
"@grafana/ui": "workspace:*",
|
||||
|
|
@ -43,5 +46,5 @@
|
|||
"peerDependencies": {
|
||||
"@grafana/runtime": "*"
|
||||
},
|
||||
"packageManager": "yarn@4.4.0"
|
||||
"packageManager": "yarn@4.11.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
"updated": "%TODAY%"
|
||||
},
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=10.4.0",
|
||||
"grafanaDependency": ">=12.0.0",
|
||||
"plugins": []
|
||||
}
|
||||
},
|
||||
"languages": ["en-US", "es-ES", "sv-SE"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import { FRENCH_FRANCE } from '@grafana/i18n';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
import pluginJson from '../../plugin.json';
|
||||
|
||||
test.use({ userPreferences: { language: FRENCH_FRANCE } });
|
||||
|
||||
test('should display default translation (en-US)', async ({ createDataSourceConfigPage }) => {
|
||||
const configPage = await createDataSourceConfigPage({ type: pluginJson.id });
|
||||
|
||||
await expect(configPage.ctx.page.getByLabel('API Key')).toBeVisible();
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { SWEDISH_SWEDEN } from '@grafana/i18n';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
import pluginJson from '../../plugin.json';
|
||||
|
||||
test.use({ userPreferences: { language: SWEDISH_SWEDEN } });
|
||||
|
||||
test('should display correct translation', async ({ createDataSourceConfigPage }) => {
|
||||
const configPage = await createDataSourceConfigPage({ type: pluginJson.id });
|
||||
|
||||
await expect(configPage.ctx.page.getByLabel('API-nyckel')).toBeVisible();
|
||||
});
|
||||
|
|
@ -34,6 +34,7 @@ const config = async (env: Env): Promise<Configuration> => {
|
|||
],
|
||||
}),
|
||||
],
|
||||
externals: [...(baseConfig.externals as any), 'i18next'],
|
||||
};
|
||||
|
||||
return mergeWithCustomize({
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# Changelog
|
||||
0
e2e-playwright/test-plugins/grafana-test-panel/README.md
Normal file
0
e2e-playwright/test-plugins/grafana-test-panel/README.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { css, cx } from '@emotion/css';
|
||||
import { useStyles2, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { PanelDataErrorView } from '@grafana/runtime';
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import React from 'react';
|
||||
import { SimpleOptions } from '../types';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
|
||||
interface Props extends PanelProps<SimpleOptions> {}
|
||||
|
||||
const getStyles = () => {
|
||||
return {
|
||||
wrapper: css`
|
||||
font-family: Open Sans;
|
||||
position: relative;
|
||||
`,
|
||||
svg: css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
`,
|
||||
textBox: css`
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 10px;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export const SimplePanel: React.FC<Props> = ({ options, data, width, height, fieldConfig, id }) => {
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
if (data.series.length === 0) {
|
||||
return <PanelDataErrorView fieldConfig={fieldConfig} panelId={id} data={data} needsStringField />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
styles.wrapper,
|
||||
css`
|
||||
width: ${width}px;
|
||||
height: ${height}px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
className={styles.svg}
|
||||
width={width}
|
||||
height={height}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox={`-${width / 2} -${height / 2} ${width} ${height}`}
|
||||
>
|
||||
<g>
|
||||
<circle data-testid="simple-panel-circle" style={{ fill: theme.colors.primary.main }} r={100} />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<div className={styles.textBox}>
|
||||
{options.showSeriesCount && (
|
||||
<div data-testid="simple-panel-series-counter">
|
||||
<Trans
|
||||
i18nKey="components.simplePanel.options.showSeriesCount"
|
||||
defaults="Number of series: {{numberOfSeries}}"
|
||||
values={{ numberOfSeries: data.series.length }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Trans
|
||||
i18nKey="components.simplePanel.options.textOptionValue"
|
||||
defaults="Text option value: {{optionValue}}"
|
||||
values={{ optionValue: options.text }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { defineConfig } from 'i18next-cli';
|
||||
import pluginJson from './plugin.json';
|
||||
|
||||
export default defineConfig({
|
||||
locales: pluginJson.languages,
|
||||
extract: {
|
||||
input: ['**/*.{tsx,ts}'],
|
||||
output: 'locales/{{language}}/{{namespace}}.json',
|
||||
defaultNS: pluginJson.id,
|
||||
functions: ['t', '*.t'],
|
||||
transComponents: ['Trans'],
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 81.9 71.52"><defs><style>.cls-1{fill:#84aff1;}.cls-2{fill:#3865ab;}.cls-3{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="42.95" y1="16.88" x2="81.9" y2="16.88" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2cc0c"/><stop offset="1" stop-color="#ff9830"/></linearGradient></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M55.46,62.43A2,2,0,0,1,54.07,59l4.72-4.54a2,2,0,0,1,2.2-.39l3.65,1.63,3.68-3.64a2,2,0,1,1,2.81,2.84l-4.64,4.6a2,2,0,0,1-2.22.41L60.6,58.26l-3.76,3.61A2,2,0,0,1,55.46,62.43Z"/><path class="cls-2" d="M37,0H2A2,2,0,0,0,0,2V31.76a2,2,0,0,0,2,2H37a2,2,0,0,0,2-2V2A2,2,0,0,0,37,0ZM4,29.76V8.84H35V29.76Z"/><path class="cls-3" d="M79.9,0H45a2,2,0,0,0-2,2V31.76a2,2,0,0,0,2,2h35a2,2,0,0,0,2-2V2A2,2,0,0,0,79.9,0ZM47,29.76V8.84h31V29.76Z"/><path class="cls-2" d="M37,37.76H2a2,2,0,0,0-2,2V69.52a2,2,0,0,0,2,2H37a2,2,0,0,0,2-2V39.76A2,2,0,0,0,37,37.76ZM4,67.52V46.6H35V67.52Z"/><path class="cls-2" d="M79.9,37.76H45a2,2,0,0,0-2,2V69.52a2,2,0,0,0,2,2h35a2,2,0,0,0,2-2V39.76A2,2,0,0,0,79.9,37.76ZM47,67.52V46.6h31V67.52Z"/><rect class="cls-1" x="10.48" y="56.95" width="4" height="5.79"/><rect class="cls-1" x="17.43" y="53.95" width="4" height="8.79"/><rect class="cls-1" x="24.47" y="50.95" width="4" height="11.79"/><path class="cls-1" d="M19.47,25.8a6.93,6.93,0,1,1,6.93-6.92A6.93,6.93,0,0,1,19.47,25.8Zm0-9.85a2.93,2.93,0,1,0,2.93,2.93A2.93,2.93,0,0,0,19.47,16Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"components": {
|
||||
"simplePanel": {
|
||||
"options": {
|
||||
"showSeriesCount": "Number of series: {{numberOfSeries}}",
|
||||
"textOptionValue": "Text option value: {{optionValue}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"panel": {
|
||||
"options": {
|
||||
"seriesCountSize": {
|
||||
"name": "Series counter size",
|
||||
"options": {
|
||||
"lg": "Large",
|
||||
"md": "Medium",
|
||||
"sm": "Small"
|
||||
}
|
||||
},
|
||||
"showSeriesCount": {
|
||||
"name": "Show series counter"
|
||||
},
|
||||
"text": {
|
||||
"defaultValue": "Default value of text input option",
|
||||
"description": "Description of panel option",
|
||||
"name": "Simple text option"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"components": {
|
||||
"simplePanel": {
|
||||
"options": {
|
||||
"showSeriesCount": "Número de series: {{numberOfSeries}}",
|
||||
"textOptionValue": "Valor de opción de texto: {{optionValue}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"panel": {
|
||||
"options": {
|
||||
"seriesCountSize": {
|
||||
"name": "Tamaño del contador de series",
|
||||
"options": {
|
||||
"lg": "Grande",
|
||||
"md": "Mediano",
|
||||
"sm": "Pequeño"
|
||||
}
|
||||
},
|
||||
"showSeriesCount": {
|
||||
"name": "Mostrar contador de series"
|
||||
},
|
||||
"text": {
|
||||
"defaultValue": "Valor predeterminado de la opción de entrada de texto",
|
||||
"description": "Descripción de la opción del panel",
|
||||
"name": "Opción de texto simple"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"components": {
|
||||
"simplePanel": {
|
||||
"options": {
|
||||
"showSeriesCount": "Antal serier: {{numberOfSeries}}",
|
||||
"textOptionValue": "Textalternativ värde: {{optionValue}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"panel": {
|
||||
"options": {
|
||||
"seriesCountSize": {
|
||||
"name": "Storlek på serieräknare",
|
||||
"options": {
|
||||
"lg": "Stor",
|
||||
"md": "Medel",
|
||||
"sm": "Liten"
|
||||
}
|
||||
},
|
||||
"showSeriesCount": {
|
||||
"name": "Visa serieräknare"
|
||||
},
|
||||
"text": {
|
||||
"defaultValue": "Standardvärde för textinmatningsalternativ",
|
||||
"description": "Beskrivning av panelalternativ",
|
||||
"name": "Enkelt textalternativ"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
e2e-playwright/test-plugins/grafana-test-panel/module.ts
Normal file
46
e2e-playwright/test-plugins/grafana-test-panel/module.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { initPluginTranslations, t } from '@grafana/i18n';
|
||||
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { SimpleOptions } from './types';
|
||||
import { SimplePanel } from './components/SimplePanel';
|
||||
import pluginJson from './plugin.json';
|
||||
|
||||
await initPluginTranslations(pluginJson.id);
|
||||
|
||||
export const plugin = new PanelPlugin<SimpleOptions>(SimplePanel).setPanelOptions((builder) => {
|
||||
return builder
|
||||
.addTextInput({
|
||||
path: 'text',
|
||||
name: t('panel.options.text.name', 'Simple text option'),
|
||||
description: t('panel.options.text.description', 'Description of panel option'),
|
||||
defaultValue: t('panel.options.text.defaultValue', 'Default value of text input option'),
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
path: 'showSeriesCount',
|
||||
name: t('panel.options.showSeriesCount.name', 'Show series counter'),
|
||||
defaultValue: false,
|
||||
})
|
||||
.addRadio({
|
||||
path: 'seriesCountSize',
|
||||
defaultValue: 'sm',
|
||||
|
||||
name: t('panel.options.seriesCountSize.name', 'Series counter size'),
|
||||
settings: {
|
||||
options: [
|
||||
{
|
||||
value: 'sm',
|
||||
label: t('panel.options.seriesCountSize.options.sm', 'Small'),
|
||||
},
|
||||
{
|
||||
value: 'md',
|
||||
label: t('panel.options.seriesCountSize.options.md', 'Medium'),
|
||||
},
|
||||
{
|
||||
value: 'lg',
|
||||
label: t('panel.options.seriesCountSize.options.lg', 'Large'),
|
||||
},
|
||||
],
|
||||
},
|
||||
showIf: (config) => config.showSeriesCount,
|
||||
});
|
||||
});
|
||||
50
e2e-playwright/test-plugins/grafana-test-panel/package.json
Normal file
50
e2e-playwright/test-plugins/grafana-test-panel/package.json
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "@test-plugins/grafana-e2etest-panel",
|
||||
"version": "12.4.0-pre",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -c ./webpack.config.ts --env production",
|
||||
"dev": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -w -c ./webpack.config.ts --env development",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx .",
|
||||
"i18n-extract": "i18next-cli extract --sync-primary"
|
||||
},
|
||||
"author": "Grafana",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@grafana/plugin-configs": "workspace:*",
|
||||
"@types/lodash": "4.17.7",
|
||||
"@types/node": "24.9.2",
|
||||
"@types/prismjs": "1.26.4",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/react-dom": "18.3.5",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/uuid": "9.0.8",
|
||||
"glob": "10.4.1",
|
||||
"i18next-cli": "^1.24.22",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.5.4",
|
||||
"webpack": "5.95.0",
|
||||
"webpack-merge": "5.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 22 <25"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.11.2",
|
||||
"@grafana/data": "workspace:*",
|
||||
"@grafana/i18n": "workspace:*",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
"@grafana/schema": "workspace:*",
|
||||
"@grafana/ui": "workspace:*",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-router-dom": "^6.22.0",
|
||||
"rxjs": "7.8.1",
|
||||
"tslib": "2.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@grafana/runtime": "*"
|
||||
},
|
||||
"packageManager": "yarn@4.11.0"
|
||||
}
|
||||
26
e2e-playwright/test-plugins/grafana-test-panel/plugin.json
Normal file
26
e2e-playwright/test-plugins/grafana-test-panel/plugin.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/grafana/grafana/main/docs/sources/developers/plugins/plugin.schema.json",
|
||||
"type": "panel",
|
||||
"name": "Grafana E2ETest Panel",
|
||||
"id": "grafana-e2etest-panel",
|
||||
"info": {
|
||||
"keywords": ["panel"],
|
||||
"description": "",
|
||||
"author": {
|
||||
"name": "Grafana"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/logo.svg",
|
||||
"large": "img/logo.svg"
|
||||
},
|
||||
"links": [],
|
||||
"screenshots": [],
|
||||
"version": "%VERSION%",
|
||||
"updated": "%TODAY%"
|
||||
},
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=12.0.0",
|
||||
"plugins": []
|
||||
},
|
||||
"languages": ["en-US", "es-ES", "sv-SE"]
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { FRENCH_FRANCE } from '@grafana/i18n';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({ userPreferences: { language: FRENCH_FRANCE } });
|
||||
|
||||
test('should display default translation (en-US)', async ({ panelEditPage }) => {
|
||||
panelEditPage.setVisualization('Grafana E2ETest Panel');
|
||||
|
||||
await expect(panelEditPage.panel.locator.getByText('Text option value:')).toBeVisible();
|
||||
const options = panelEditPage.getCustomOptions('Grafana E2ETest Panel');
|
||||
const showSeriesCounter = options.getSwitch('Show series counter');
|
||||
await expect(showSeriesCounter.locator()).toBeVisible();
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { SWEDISH_SWEDEN } from '@grafana/i18n';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({ userPreferences: { language: SWEDISH_SWEDEN } });
|
||||
|
||||
test('should display correct translation', async ({ panelEditPage }) => {
|
||||
panelEditPage.setVisualization('Grafana E2ETest Panel');
|
||||
|
||||
await expect(panelEditPage.panel.locator.getByText('Textalternativ värde:')).toBeVisible();
|
||||
const options = panelEditPage.getCustomOptions('Grafana E2ETest Panel');
|
||||
const showSeriesCounter = options.getSwitch('Visa serieräknare');
|
||||
await expect(showSeriesCounter.locator()).toBeVisible();
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"types": ["node", "jest", "@testing-library/jest-dom"]
|
||||
},
|
||||
"extends": "@grafana/plugin-configs/tsconfig.json",
|
||||
"include": ["."]
|
||||
}
|
||||
7
e2e-playwright/test-plugins/grafana-test-panel/types.ts
Normal file
7
e2e-playwright/test-plugins/grafana-test-panel/types.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
type SeriesSize = 'sm' | 'md' | 'lg';
|
||||
|
||||
export interface SimpleOptions {
|
||||
text: string;
|
||||
showSeriesCount: boolean;
|
||||
seriesCountSize: SeriesSize;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import CopyWebpackPlugin from 'copy-webpack-plugin';
|
||||
import grafanaConfig, { type Env } from '@grafana/plugin-configs/webpack.config.ts';
|
||||
import { mergeWithCustomize, unique } from 'webpack-merge';
|
||||
import { type Configuration } from 'webpack';
|
||||
|
||||
function skipFiles(f: string): boolean {
|
||||
if (f.includes('/dist/')) {
|
||||
// avoid copying files already in dist
|
||||
return false;
|
||||
}
|
||||
if (f.includes('/node_modules/')) {
|
||||
// avoid copying tsconfig.json
|
||||
return false;
|
||||
}
|
||||
if (f.includes('/package.json')) {
|
||||
// avoid copying package.json
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const config = async (env: Env): Promise<Configuration> => {
|
||||
const baseConfig = await grafanaConfig(env);
|
||||
const customConfig = {
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
// To `compiler.options.output`
|
||||
{ from: 'README.md', to: '.', force: true },
|
||||
{ from: 'plugin.json', to: '.' },
|
||||
{ from: 'CHANGELOG.md', to: '.', force: true },
|
||||
{ from: '**/*.json', to: '.', filter: skipFiles },
|
||||
{ from: '**/*.svg', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional
|
||||
],
|
||||
}),
|
||||
],
|
||||
externals: [...(baseConfig.externals as any), 'i18next'],
|
||||
};
|
||||
|
||||
return mergeWithCustomize({
|
||||
customizeArray: unique('plugins', ['CopyPlugin'], (plugin) => plugin.constructor && plugin.constructor.name),
|
||||
})(baseConfig, customConfig);
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -130,6 +130,11 @@ func run(ctx context.Context, cmd *cli.Command) error {
|
|||
".yarnrc.yml",
|
||||
".yarn",
|
||||
"packages/*/package.json",
|
||||
"packages/grafana-data",
|
||||
"packages/grafana-i18n",
|
||||
"packages/grafana-runtime",
|
||||
"packages/grafana-schema",
|
||||
"packages/grafana-ui",
|
||||
"packages/grafana-plugin-configs",
|
||||
"public/app/plugins/*/*/package.json",
|
||||
"e2e-playwright/test-plugins/*/package.json",
|
||||
|
|
|
|||
|
|
@ -203,5 +203,9 @@ export default defineConfig<PluginOptions>({
|
|||
testMatch: ['global-teardown.spec.ts'],
|
||||
dependencies: ['dashboard-cujs'],
|
||||
}),
|
||||
withAuth({
|
||||
name: 'grafana-e2etest-panel',
|
||||
testDir: path.join(testDirRoot, '/test-plugins/grafana-test-panel'),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ stack_id = 12345
|
|||
enable_alpha = true
|
||||
|
||||
[plugins]
|
||||
allow_loading_unsigned_plugins=grafana-extensionstest-app,grafana-extensionexample1-app,grafana-extensionexample2-app,grafana-extensionexample3-app,grafana-e2etest-datasource
|
||||
allow_loading_unsigned_plugins=grafana-extensionstest-app,grafana-extensionexample1-app,grafana-extensionexample2-app,grafana-extensionexample3-app,grafana-e2etest-datasource,grafana-e2etest-panel
|
||||
|
||||
[database]
|
||||
type=sqlite3
|
||||
|
|
|
|||
38
yarn.lock
38
yarn.lock
|
|
@ -9576,6 +9576,7 @@ __metadata:
|
|||
dependencies:
|
||||
"@emotion/css": "npm:11.11.2"
|
||||
"@grafana/data": "workspace:*"
|
||||
"@grafana/i18n": "workspace:*"
|
||||
"@grafana/plugin-configs": "workspace:*"
|
||||
"@grafana/runtime": "workspace:*"
|
||||
"@grafana/schema": "workspace:*"
|
||||
|
|
@ -9588,6 +9589,7 @@ __metadata:
|
|||
"@types/semver": "npm:7.5.8"
|
||||
"@types/uuid": "npm:9.0.8"
|
||||
glob: "npm:10.5.0"
|
||||
i18next-cli: "npm:^1.24.22"
|
||||
react: "npm:18.3.1"
|
||||
react-dom: "npm:18.3.1"
|
||||
react-router-dom: "npm:^6.22.0"
|
||||
|
|
@ -9608,6 +9610,7 @@ __metadata:
|
|||
dependencies:
|
||||
"@emotion/css": "npm:11.11.2"
|
||||
"@grafana/data": "workspace:*"
|
||||
"@grafana/i18n": "workspace:*"
|
||||
"@grafana/plugin-configs": "workspace:*"
|
||||
"@grafana/runtime": "workspace:*"
|
||||
"@grafana/schema": "workspace:*"
|
||||
|
|
@ -9620,6 +9623,41 @@ __metadata:
|
|||
"@types/semver": "npm:7.5.8"
|
||||
"@types/uuid": "npm:9.0.8"
|
||||
glob: "npm:10.4.1"
|
||||
i18next-cli: "npm:^1.24.22"
|
||||
react: "npm:18.3.1"
|
||||
react-dom: "npm:18.3.1"
|
||||
react-router-dom: "npm:^6.22.0"
|
||||
rxjs: "npm:7.8.1"
|
||||
ts-node: "npm:10.9.2"
|
||||
tslib: "npm:2.6.3"
|
||||
typescript: "npm:5.5.4"
|
||||
webpack: "npm:5.95.0"
|
||||
webpack-merge: "npm:5.10.0"
|
||||
peerDependencies:
|
||||
"@grafana/runtime": "*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@test-plugins/grafana-e2etest-panel@workspace:e2e-playwright/test-plugins/grafana-test-panel":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@test-plugins/grafana-e2etest-panel@workspace:e2e-playwright/test-plugins/grafana-test-panel"
|
||||
dependencies:
|
||||
"@emotion/css": "npm:11.11.2"
|
||||
"@grafana/data": "workspace:*"
|
||||
"@grafana/i18n": "workspace:*"
|
||||
"@grafana/plugin-configs": "workspace:*"
|
||||
"@grafana/runtime": "workspace:*"
|
||||
"@grafana/schema": "workspace:*"
|
||||
"@grafana/ui": "workspace:*"
|
||||
"@types/lodash": "npm:4.17.7"
|
||||
"@types/node": "npm:24.9.2"
|
||||
"@types/prismjs": "npm:1.26.4"
|
||||
"@types/react": "npm:18.3.18"
|
||||
"@types/react-dom": "npm:18.3.5"
|
||||
"@types/semver": "npm:7.5.8"
|
||||
"@types/uuid": "npm:9.0.8"
|
||||
glob: "npm:10.4.1"
|
||||
i18next-cli: "npm:^1.24.22"
|
||||
react: "npm:18.3.1"
|
||||
react-dom: "npm:18.3.1"
|
||||
react-router-dom: "npm:^6.22.0"
|
||||
|
|
|
|||
Loading…
Reference in a new issue