mirror of
https://github.com/grafana/grafana.git
synced 2025-09-27 04:23:51 +08:00

* add custom plugins * update bundles * provision app plugins and their dashboards * add one more script that run e2e tests using e2e test server * add e2e tests * regenerate jsonnet dashboards * ignore custom plugins and playwright report * use minified * cleanup tests * update codeowners * add leading slash * document new script * document custom-plugins * cleanup * twist modules * add readme
217 lines
6.3 KiB
JavaScript
217 lines
6.3 KiB
JavaScript
define(['react', '@grafana/data', '@grafana/ui', '@grafana/runtime', '@emotion/css', 'rxjs'], function (
|
|
React,
|
|
data,
|
|
ui,
|
|
runtime,
|
|
css,
|
|
rxjs
|
|
) {
|
|
'use strict';
|
|
|
|
const styles = {
|
|
modalBody: 'ape-modal-body',
|
|
mainPageContainer: 'ape-main-page-container',
|
|
};
|
|
|
|
class RootComponent extends React.PureComponent {
|
|
render() {
|
|
return React.createElement(
|
|
'div',
|
|
{ 'data-testid': styles.mainPageContainer, className: 'page-container' },
|
|
'Hello Grafana!'
|
|
);
|
|
}
|
|
}
|
|
|
|
const asyncWrapper = (fn) => {
|
|
return function () {
|
|
const gen = fn.apply(this, arguments);
|
|
return new Promise((resolve, reject) => {
|
|
function step(key, arg) {
|
|
let info, value;
|
|
try {
|
|
info = gen[key](arg);
|
|
value = info.value;
|
|
} catch (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
if (info.done) {
|
|
resolve(value);
|
|
} else {
|
|
Promise.resolve(value).then(next, throw_);
|
|
}
|
|
}
|
|
function next(value) {
|
|
step('next', value);
|
|
}
|
|
function throw_(value) {
|
|
step('throw', value);
|
|
}
|
|
next();
|
|
});
|
|
};
|
|
};
|
|
|
|
const getStyles = (theme) => ({
|
|
colorWeak: css.css`color: ${theme.colors.text.secondary};`,
|
|
marginTop: css.css`margin-top: ${theme.spacing(3)};`,
|
|
});
|
|
|
|
const updatePlugin = asyncWrapper(function* (pluginId, settings) {
|
|
const response = runtime
|
|
.getBackendSrv()
|
|
.fetch({ url: `/api/plugins/${pluginId}/settings`, method: 'POST', data: settings });
|
|
return rxjs.lastValueFrom(response);
|
|
});
|
|
|
|
const handleUpdate = asyncWrapper(function* (pluginId, settings) {
|
|
try {
|
|
yield updatePlugin(pluginId, settings);
|
|
window.location.reload();
|
|
} catch (error) {
|
|
console.error('Error while updating the plugin', error);
|
|
}
|
|
});
|
|
|
|
const configPageBody = ({ plugin }) => {
|
|
const styles = getStyles(ui.useStyles2());
|
|
const { enabled, jsonData } = plugin.meta;
|
|
return React.createElement(
|
|
'div',
|
|
null,
|
|
React.createElement(ui.Legend, null, 'Enable / Disable '),
|
|
!enabled &&
|
|
React.createElement(
|
|
React.Fragment,
|
|
null,
|
|
React.createElement('div', { className: styles.colorWeak }, 'The plugin is currently not enabled.'),
|
|
React.createElement(
|
|
ui.Button,
|
|
{
|
|
className: styles.marginTop,
|
|
variant: 'primary',
|
|
onClick: () => handleUpdate(plugin.meta.id, { enabled: true, pinned: true, jsonData: jsonData }),
|
|
},
|
|
'Enable plugin'
|
|
)
|
|
),
|
|
enabled &&
|
|
React.createElement(
|
|
React.Fragment,
|
|
null,
|
|
React.createElement('div', { className: styles.colorWeak }, 'The plugin is currently enabled.'),
|
|
React.createElement(
|
|
ui.Button,
|
|
{
|
|
className: styles.marginTop,
|
|
variant: 'destructive',
|
|
onClick: () => handleUpdate(plugin.meta.id, { enabled: false, pinned: false, jsonData: jsonData }),
|
|
},
|
|
'Disable plugin'
|
|
)
|
|
)
|
|
);
|
|
};
|
|
|
|
const selectQueryModal = ({ targets = [], onDismiss }) => {
|
|
const [selectedQuery, setSelectedQuery] = React.useState(targets[0]);
|
|
return React.createElement(
|
|
'div',
|
|
{ 'data-testid': styles.modalBody },
|
|
React.createElement(
|
|
'p',
|
|
null,
|
|
'Please select the query you would like to use to create "something" in the plugin.'
|
|
),
|
|
React.createElement(
|
|
ui.HorizontalGroup,
|
|
null,
|
|
targets.map((query) =>
|
|
React.createElement(ui.FilterPill, {
|
|
key: query.refId,
|
|
label: query.refId,
|
|
selected: query.refId === (selectedQuery ? selectedQuery.refId : null),
|
|
onClick: () => setSelectedQuery(query),
|
|
})
|
|
)
|
|
),
|
|
React.createElement(
|
|
ui.Modal.ButtonRow,
|
|
null,
|
|
React.createElement(ui.Button, { variant: 'secondary', fill: 'outline', onClick: onDismiss }, 'Cancel'),
|
|
React.createElement(
|
|
ui.Button,
|
|
{
|
|
disabled: !Boolean(selectedQuery),
|
|
onClick: () => {
|
|
onDismiss && onDismiss();
|
|
alert(`You selected query "${selectedQuery.refId}"`);
|
|
},
|
|
},
|
|
'OK'
|
|
)
|
|
)
|
|
);
|
|
};
|
|
|
|
const plugin = new data.AppPlugin()
|
|
.setRootPage(RootComponent)
|
|
.addConfigPage({
|
|
title: 'Configuration',
|
|
icon: 'cog',
|
|
body: configPageBody,
|
|
id: 'configuration',
|
|
})
|
|
.configureExtensionLink({
|
|
title: 'Open from time series or pie charts (path)',
|
|
description: 'This link will only be visible on time series and pie charts',
|
|
extensionPointId: data.PluginExtensionPoints.DashboardPanelMenu,
|
|
path: `/a/myorg-extensions-app/`,
|
|
configure: (context) => {
|
|
if (context.dashboard?.title === 'Link Extensions (path)') {
|
|
switch (context.pluginId) {
|
|
case 'timeseries':
|
|
return {};
|
|
case 'piechart':
|
|
return { title: `Open from ${context.pluginId}` };
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
})
|
|
.configureExtensionLink({
|
|
title: 'Open from time series or pie charts (onClick)',
|
|
description: 'This link will only be visible on time series and pie charts',
|
|
extensionPointId: data.PluginExtensionPoints.DashboardPanelMenu,
|
|
onClick: (_, { context, openModal }) => {
|
|
const targets = context?.targets || [];
|
|
const title = context?.title;
|
|
if (!targets.length) return;
|
|
if (targets.length > 1) {
|
|
openModal({
|
|
title: `Select query from "${title}"`,
|
|
body: (props) => React.createElement(selectQueryModal, { ...props, targets: targets }),
|
|
});
|
|
} else {
|
|
alert(`You selected query "${targets[0].refId}"`);
|
|
}
|
|
},
|
|
configure: (context) => {
|
|
if (context.dashboard?.title === 'Link Extensions (onClick)') {
|
|
switch (context.pluginId) {
|
|
case 'timeseries':
|
|
return {};
|
|
case 'piechart':
|
|
return { title: `Open from ${context.pluginId}` };
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
return { plugin: plugin };
|
|
});
|