grafana-scenes
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese@grafana/scenes Framework
@grafana/scenes框架
Build reactive, data-driven Grafana plugin pages with declarative scene objects.
使用声明式场景对象构建响应式、数据驱动的Grafana插件页面。
Core Concepts
核心概念
Scenes composes a tree of objects: → → → layouts → panels. Each node can own data (), variables (), time ranges (), and behaviors () that propagate down the tree.
SceneAppSceneAppPageEmbeddedScene$data$variables$timeRange$behaviors场景由对象树组成: → → → 布局 → 面板。每个节点都可以拥有数据()、变量()、时间范围()和行为(),这些会向下传递到整个对象树中。
SceneAppSceneAppPageEmbeddedScene$data$variables$timeRange$behaviorsQuick Start: New Scene Page
快速入门:新建场景页面
1. Create the scene file
1. 创建场景文件
typescript
// src/components/scenes/MyFeature/scene.tsx
import {
EmbeddedScene, SceneFlexLayout, SceneFlexItem,
SceneQueryRunner, SceneVariableSet, QueryVariable,
PanelBuilders, VariableValueSelectors, SceneControlsSpacer,
} from '@grafana/scenes';
export function getMyFeatureScene(params: { datasource: DataSourceRef }) {
const queryRunner = new SceneQueryRunner({
datasource: params.datasource,
queries: [{ refId: 'A', expr: 'up{cluster=~"$cluster"}', instant: true, format: 'table' }],
});
const panel = PanelBuilders.table()
.setData(queryRunner)
.setTitle('My Table')
.build();
return new EmbeddedScene({
$variables: new SceneVariableSet({
variables: [
new QueryVariable({
name: 'cluster',
query: 'label_values(up, cluster)',
datasource: params.datasource,
isMulti: true, includeAll: true, defaultToAll: true,
}),
],
}),
controls: [new VariableValueSelectors({}), new SceneControlsSpacer()],
body: new SceneFlexLayout({
direction: 'column',
children: [new SceneFlexItem({ body: panel })],
}),
});
}typescript
// src/components/scenes/MyFeature/scene.tsx
import {
EmbeddedScene, SceneFlexLayout, SceneFlexItem,
SceneQueryRunner, SceneVariableSet, QueryVariable,
PanelBuilders, VariableValueSelectors, SceneControlsSpacer,
} from '@grafana/scenes';
export function getMyFeatureScene(params: { datasource: DataSourceRef }) {
const queryRunner = new SceneQueryRunner({
datasource: params.datasource,
queries: [{ refId: 'A', expr: 'up{cluster=~"$cluster"}', instant: true, format: 'table' }],
});
const panel = PanelBuilders.table()
.setData(queryRunner)
.setTitle('My Table')
.build();
return new EmbeddedScene({
$variables: new SceneVariableSet({
variables: [
new QueryVariable({
name: 'cluster',
query: 'label_values(up, cluster)',
datasource: params.datasource,
isMulti: true, includeAll: true, defaultToAll: true,
}),
],
}),
controls: [new VariableValueSelectors({}), new SceneControlsSpacer()],
body: new SceneFlexLayout({
direction: 'column',
children: [new SceneFlexItem({ body: panel })],
}),
});
}2. Create the page
2. 创建页面
typescript
// src/components/scenes/MyFeature/MyFeature.tsx
import { SceneAppPage, SceneTimeRange } from '@grafana/scenes';
export function getMyFeaturePage(params) {
return new SceneAppPage({
title: 'My Feature',
url: '/a/my-plugin-id/my-feature',
routePath: 'my-feature/*',
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }),
getScene: () => getMyFeatureScene(params),
drilldowns: [],
});
}typescript
// src/components/scenes/MyFeature/MyFeature.tsx
import { SceneAppPage, SceneTimeRange } from '@grafana/scenes';
export function getMyFeaturePage(params) {
return new SceneAppPage({
title: 'My Feature',
url: '/a/my-plugin-id/my-feature',
routePath: 'my-feature/*',
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }),
getScene: () => getMyFeatureScene(params),
drilldowns: [],
});
}3. Register in SceneApp
3. 在SceneApp中注册
Add the page to the pages array in the root scene file.
SceneApp将页面添加到根场景文件中的页面数组中。
SceneAppKey Patterns
关键模式
Drilldowns (click-through navigation)
钻取(点击跳转导航)
typescript
drilldowns: [{
routePath: ':cluster/*',
getPage: (match, parent) => new SceneAppPage({
title: decodeURIComponent(match.params.cluster),
url: `${parent.state.url}/${match.params.cluster}`,
routePath: `${match.params.cluster}/*`,
getScene: () => detailScene(decodeURIComponent(match.params.cluster)),
}),
}]typescript
drilldowns: [{
routePath: ':cluster/*',
getPage: (match, parent) => new SceneAppPage({
title: decodeURIComponent(match.params.cluster),
url: `${parent.state.url}/${match.params.cluster}`,
routePath: `${match.params.cluster}/*`,
getScene: () => detailScene(decodeURIComponent(match.params.cluster)),
}),
}]Tabs (sub-pages within a detail view)
标签页(详情视图内的子页面)
Pass instead of on a . Each tab is itself a with its own scene.
tabs: [SceneAppPage, ...]getSceneSceneAppPageSceneAppPage在上传递替代。每个标签页本身就是一个拥有独立场景的。
SceneAppPagetabs: [SceneAppPage, ...]getSceneSceneAppPageQuery with transformations
带转换的查询
Wrap a in to apply Grafana transforms or custom RxJS operators:
SceneQueryRunnerSceneDataTransformertypescript
new SceneDataTransformer({
$data: queryRunner,
transformations: [
{ id: 'organize', options: { renameByName: { 'Value #A': 'CPU' } } },
(ctx) => (source) => source.pipe(map((frames) => /* custom transform */)),
],
})将包裹在中,以应用Grafana转换或自定义RxJS操作符:
SceneQueryRunnerSceneDataTransformertypescript
new SceneDataTransformer({
$data: queryRunner,
transformations: [
{ id: 'organize', options: { renameByName: { 'Value #A': 'CPU' } } },
(ctx) => (source) => source.pipe(map((frames) => /* custom transform */)),
],
})Custom scene object
自定义场景对象
Extend with a static for custom interactive UI:
SceneObjectBaseComponenttypescript
class MyWidget extends SceneObjectBase<MyWidgetState> {
static Component = ({ model }: SceneComponentProps<MyWidget>) => {
const state = model.useState();
return <div>{state.value}</div>;
};
}继承并添加静态以实现自定义交互式UI:
SceneObjectBaseComponenttypescript
class MyWidget extends SceneObjectBase<MyWidgetState> {
static Component = ({ model }: SceneComponentProps<MyWidget>) => {
const state = model.useState();
return <div>{state.value}</div>;
};
}Table column overrides
表格列覆盖
Build objects for drill-down links, filtering, units, widths, custom cells.
ConfigOverrideRule创建对象以实现钻取链接、过滤、单位设置、宽度调整、自定义单元格等功能。
ConfigOverrideRulePanel types
面板类型
PanelBuilders.table().timeseries().stat().gauge().barchart().setData().setTitle().setUnit().setOption().setOverrides().build()PanelBuilders.table().timeseries().stat().gauge().barchart().setData().setTitle().setUnit().setOption().setOverrides().build()Common Pitfalls
常见陷阱
- Always use (with wildcard) on pages that have drilldowns or tabs
routePath: 'path/*' - /
encodeURIComponentURL params — K8s names can containdecodeURIComponent/ - Variables referenced in queries as must exist in an ancestor
$varNameSceneVariableSet - is called lazily; don't create side effects in the factory
getScene - For instant queries, set both and
instant: trueformat: 'table'
- 对于包含钻取或标签页的页面,务必使用(带通配符)
routePath: 'path/*' - 对URL参数使用/
encodeURIComponent—— K8s名称可能包含decodeURIComponent/ - 查询中引用的变量必须存在于祖先
$varName中SceneVariableSet - 是延迟调用的;不要在工厂函数中产生副作用
getScene - 对于即时查询,需同时设置和
instant: trueformat: 'table'