mirror of https://github.com/grafana/grafana.git
TableNG: Simplify Pill cellType and reduce scope (#107934)
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Details
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Details
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Details
CodeQL checks / Analyze (actions) (push) Has been cancelled
Details
CodeQL checks / Analyze (go) (push) Has been cancelled
Details
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Details
Lint Frontend / Detect whether code changed (push) Has been cancelled
Details
Crowdin Upload Action / upload-sources-to-crowdin (push) Has been cancelled
Details
Verify i18n / verify-i18n (push) Has been cancelled
Details
End-to-end tests / Detect whether code changed (push) Has been cancelled
Details
Frontend tests / Detect whether code changed (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
publish-kinds-next / main (push) Has been cancelled
Details
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Details
Build Release Packages / setup (push) Has been cancelled
Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Details
Shellcheck / Shellcheck scripts (push) Has been cancelled
Details
Verify Storybook (Playwright) / Verify Storybook (Playwright) (push) Has been cancelled
Details
Verify Storybook / Verify Storybook (push) Has been cancelled
Details
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Details
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Details
Lint Frontend / Lint (push) Has been cancelled
Details
Lint Frontend / Typecheck (push) Has been cancelled
Details
Lint Frontend / Betterer (push) Has been cancelled
Details
End-to-end tests / Build & Package Grafana (push) Has been cancelled
Details
End-to-end tests / Build E2E test runner (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/dashboards-suite, dashboards-suite) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/panels-suite, panels-suite) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/smoke-tests-suite, smoke-tests-suite) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/various-suite, various-suite) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (1, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (2, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (3, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (4, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (5, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (6, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (7, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (8, 8) (push) Has been cancelled
Details
End-to-end tests / All Playwright tests complete (push) Has been cancelled
Details
End-to-end tests / A11y test (push) Has been cancelled
Details
End-to-end tests / All E2E tests complete (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (1) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (2) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (3) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (4) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (5) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (6) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (7) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (8) (push) Has been cancelled
Details
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Details
Integration Tests / All backend integration tests complete (push) Has been cancelled
Details
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/amd64, darwin-amd64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/arm64, darwin-arm64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook, linux-amd64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v6,deb:grafana:linux/arm/v6, linux-armv6) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v7,deb:grafana:linux/arm/v7,docker:grafana:linux/arm/v7,docker:grafana:linux/arm/v7:ubuntu, linux-armv7) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm64,deb:grafana:linux/arm64,rpm:grafana:linux/arm64,docker:grafana:linux/arm64,docker:grafana:linux/arm64:ubuntu, linux-arm64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/s390x,deb:grafana:linux/s390x,rpm:grafana:linux/s390x,docker:grafana:linux/s390x,docker:grafana:linux/s390x:ubuntu, linux-s390x) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/amd64,zip:grafana:windows/amd64,msi:grafana:windows/amd64, windows-amd64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/arm64,zip:grafana:windows/arm64, windows-arm64) (push) Has been cancelled
Details
Build Release Packages / Upload artifacts (push) Has been cancelled
Details
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Details
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Details
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Details
CodeQL checks / Analyze (actions) (push) Has been cancelled
Details
CodeQL checks / Analyze (go) (push) Has been cancelled
Details
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Details
Lint Frontend / Detect whether code changed (push) Has been cancelled
Details
Crowdin Upload Action / upload-sources-to-crowdin (push) Has been cancelled
Details
Verify i18n / verify-i18n (push) Has been cancelled
Details
End-to-end tests / Detect whether code changed (push) Has been cancelled
Details
Frontend tests / Detect whether code changed (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
publish-kinds-next / main (push) Has been cancelled
Details
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Details
Build Release Packages / setup (push) Has been cancelled
Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Details
Shellcheck / Shellcheck scripts (push) Has been cancelled
Details
Verify Storybook (Playwright) / Verify Storybook (Playwright) (push) Has been cancelled
Details
Verify Storybook / Verify Storybook (push) Has been cancelled
Details
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Details
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Has been cancelled
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Has been cancelled
Details
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Details
Lint Frontend / Lint (push) Has been cancelled
Details
Lint Frontend / Typecheck (push) Has been cancelled
Details
Lint Frontend / Betterer (push) Has been cancelled
Details
End-to-end tests / Build & Package Grafana (push) Has been cancelled
Details
End-to-end tests / Build E2E test runner (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/dashboards-suite, dashboards-suite) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/panels-suite, panels-suite) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/smoke-tests-suite, smoke-tests-suite) (push) Has been cancelled
Details
End-to-end tests / ${{ matrix.suite }} (e2e/various-suite, various-suite) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (1, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (2, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (3, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (4, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (5, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (6, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (7, 8) (push) Has been cancelled
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (8, 8) (push) Has been cancelled
Details
End-to-end tests / All Playwright tests complete (push) Has been cancelled
Details
End-to-end tests / A11y test (push) Has been cancelled
Details
End-to-end tests / All E2E tests complete (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (1) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (2) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (3) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (4) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (5) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (6) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (7) (push) Has been cancelled
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (8) (push) Has been cancelled
Details
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Details
Integration Tests / All backend integration tests complete (push) Has been cancelled
Details
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/amd64, darwin-amd64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/arm64, darwin-arm64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook, linux-amd64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v6,deb:grafana:linux/arm/v6, linux-armv6) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v7,deb:grafana:linux/arm/v7,docker:grafana:linux/arm/v7,docker:grafana:linux/arm/v7:ubuntu, linux-armv7) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm64,deb:grafana:linux/arm64,rpm:grafana:linux/arm64,docker:grafana:linux/arm64,docker:grafana:linux/arm64:ubuntu, linux-arm64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/s390x,deb:grafana:linux/s390x,rpm:grafana:linux/s390x,docker:grafana:linux/s390x,docker:grafana:linux/s390x:ubuntu, linux-s390x) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/amd64,zip:grafana:windows/amd64,msi:grafana:windows/amd64, windows-amd64) (push) Has been cancelled
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/arm64,zip:grafana:windows/arm64, windows-arm64) (push) Has been cancelled
Details
Build Release Packages / Upload artifacts (push) Has been cancelled
Details
Co-authored-by: Paul Marbach <paul.marbach@grafana.com>
This commit is contained in:
parent
b0e85a637f
commit
7344c1c555
|
@ -218,7 +218,7 @@ export class FieldColorSchemeMode implements FieldColorMode {
|
|||
}
|
||||
} else if (this.useSeriesName) {
|
||||
return (_: number, _percent: number, _threshold?: Threshold) => {
|
||||
return colors[Math.abs(stringHash(field.state?.displayName ?? field.name)) % colors.length];
|
||||
return getColorByStringHash(colors, field.state?.displayName ?? field.name);
|
||||
};
|
||||
} else {
|
||||
return (_: number, _percent: number, _threshold?: Threshold) => {
|
||||
|
@ -263,6 +263,10 @@ export function getFieldSeriesColor(field: Field, theme: GrafanaTheme2): ColorSc
|
|||
return scale(value);
|
||||
}
|
||||
|
||||
export function getColorByStringHash(colors: string[], string: string) {
|
||||
return colors[Math.abs(stringHash(string)) % colors.length];
|
||||
}
|
||||
|
||||
function getFixedColor(field: Field, theme: GrafanaTheme2) {
|
||||
return () => {
|
||||
return theme.visualization.getColorByName(field.config.color?.fixedColor ?? FALLBACK_COLOR);
|
||||
|
|
|
@ -137,6 +137,8 @@ export {
|
|||
fieldColorModeRegistry,
|
||||
type FieldColorMode,
|
||||
getFieldSeriesColor,
|
||||
/** @internal */
|
||||
getColorByStringHash,
|
||||
} from './field/fieldColor';
|
||||
export { FieldConfigOptionsRegistry } from './field/FieldConfigOptionsRegistry';
|
||||
export { sortThresholds, getActiveThreshold } from './field/thresholds';
|
||||
|
|
|
@ -977,8 +977,6 @@ export enum ComparisonOperation {
|
|||
}
|
||||
|
||||
export interface TablePillCellOptions {
|
||||
color?: string;
|
||||
colorMode?: ('auto' | 'fixed' | 'mapped');
|
||||
type: TableCellDisplayMode.Pill;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,4 @@ TableFieldOptions: {
|
|||
|
||||
TablePillCellOptions: {
|
||||
type: TableCellDisplayMode & "pill"
|
||||
color?: string
|
||||
colorMode?: "auto" | "fixed" | "mapped"
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { render, RenderResult } from '@testing-library/react';
|
||||
|
||||
import { DataFrame, Field, FieldType, GrafanaTheme2, MappingType, createTheme } from '@grafana/data';
|
||||
import { TableCellDisplayMode, TablePillCellOptions } from '@grafana/schema';
|
||||
|
||||
import { mockThemeContext } from '../../../../themes/ThemeContext';
|
||||
|
||||
import { PillCell, inferPills } from './PillCell';
|
||||
import { PillCell, getStyles } from './PillCell';
|
||||
|
||||
describe('PillCell', () => {
|
||||
let pillClass: string;
|
||||
let restoreThemeContext: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
pillClass = getStyles(createTheme()).pill;
|
||||
restoreThemeContext = mockThemeContext(createTheme());
|
||||
});
|
||||
|
||||
|
@ -20,7 +22,6 @@ describe('PillCell', () => {
|
|||
|
||||
const mockCellOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'auto',
|
||||
};
|
||||
|
||||
const mockField: Field = {
|
||||
|
@ -37,7 +38,6 @@ describe('PillCell', () => {
|
|||
};
|
||||
|
||||
const defaultProps = {
|
||||
value: 'test-value',
|
||||
field: mockField,
|
||||
justifyContent: 'flex-start' as const,
|
||||
cellOptions: mockCellOptions,
|
||||
|
@ -50,170 +50,102 @@ describe('PillCell', () => {
|
|||
showFilters: false,
|
||||
};
|
||||
|
||||
describe('pill parsing', () => {
|
||||
it('should render pills for single values', () => {
|
||||
render(<PillCell {...defaultProps} />);
|
||||
expect(screen.getByText('test-value')).toBeInTheDocument();
|
||||
});
|
||||
const ser = new XMLSerializer();
|
||||
|
||||
it('should render pills for CSV values', () => {
|
||||
render(<PillCell {...defaultProps} value="value1,value2,value3" />);
|
||||
expect(screen.getByText('value1')).toBeInTheDocument();
|
||||
expect(screen.getByText('value2')).toBeInTheDocument();
|
||||
expect(screen.getByText('value3')).toBeInTheDocument();
|
||||
});
|
||||
const expectHTML = (result: RenderResult, expected: string) => {
|
||||
let actual = ser.serializeToString(result.asFragment()).replace(/xmlns=".*?" /g, '');
|
||||
expect(actual).toEqual(expected.replace(/^\s*|\n/gm, ''));
|
||||
};
|
||||
|
||||
it('should render pills for JSON array values', () => {
|
||||
render(<PillCell {...defaultProps} value='["item1","item2","item3"]' />);
|
||||
expect(screen.getByText('item1')).toBeInTheDocument();
|
||||
expect(screen.getByText('item2')).toBeInTheDocument();
|
||||
expect(screen.getByText('item3')).toBeInTheDocument();
|
||||
});
|
||||
// one class for lightTextPill, darkTextPill
|
||||
|
||||
it('should show dash for empty values', () => {
|
||||
render(<PillCell {...defaultProps} value="" />);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
describe('Color by hash (classic palette)', () => {
|
||||
const props = { ...defaultProps };
|
||||
|
||||
it('should show dash for null values', () => {
|
||||
render(<PillCell {...defaultProps} value={null as unknown as string} />);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('color mapping', () => {
|
||||
// These tests primarily ensure the color logic executes without throwing.
|
||||
// For true color verification, visual regression tests would be needed.
|
||||
|
||||
it('should use mapped colors when colorMode is mapped', () => {
|
||||
const mappedOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'mapped',
|
||||
};
|
||||
|
||||
render(<PillCell {...defaultProps} value="success,error,warning,unknown" cellOptions={mappedOptions} />);
|
||||
|
||||
const successPill = screen.getByText('success');
|
||||
const errorPill = screen.getByText('error');
|
||||
const warningPill = screen.getByText('warning');
|
||||
const unknownPill = screen.getByText('unknown');
|
||||
|
||||
expect(successPill).toBeInTheDocument();
|
||||
expect(errorPill).toBeInTheDocument();
|
||||
expect(warningPill).toBeInTheDocument();
|
||||
expect(unknownPill).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should use field-level value mappings when available', () => {
|
||||
const mappedOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'mapped',
|
||||
};
|
||||
|
||||
// Mock field with value mappings
|
||||
const fieldWithMappings: Field = {
|
||||
...mockField,
|
||||
config: {
|
||||
...mockField.config,
|
||||
mappings: [
|
||||
{
|
||||
type: MappingType.ValueToText,
|
||||
options: {
|
||||
success: { color: '#00FF00' },
|
||||
error: { color: '#FF0000' },
|
||||
warning: { color: '#FFFF00' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
display: (value: unknown) => ({
|
||||
text: String(value),
|
||||
color:
|
||||
String(value) === 'success'
|
||||
? '#00FF00'
|
||||
: String(value) === 'error'
|
||||
? '#FF0000'
|
||||
: String(value) === 'warning'
|
||||
? '#FFFF00'
|
||||
: '#FF780A',
|
||||
numeric: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
render(
|
||||
<PillCell
|
||||
{...defaultProps}
|
||||
value="success,error,warning,unknown"
|
||||
cellOptions={mappedOptions}
|
||||
field={fieldWithMappings}
|
||||
/>
|
||||
it('single value', () => {
|
||||
expectHTML(
|
||||
render(<PillCell {...props} value="value1" />),
|
||||
`<span class="${pillClass}" style="background-color: rgb(63, 43, 91); color: rgb(255, 255, 255);">value1</span>`
|
||||
);
|
||||
|
||||
const successPill = screen.getByText('success');
|
||||
const errorPill = screen.getByText('error');
|
||||
const warningPill = screen.getByText('warning');
|
||||
const unknownPill = screen.getByText('unknown');
|
||||
|
||||
expect(successPill).toBeInTheDocument();
|
||||
expect(errorPill).toBeInTheDocument();
|
||||
expect(warningPill).toBeInTheDocument();
|
||||
expect(unknownPill).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should use fixed color when colorMode is fixed', () => {
|
||||
const fixedOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'fixed',
|
||||
color: '#FF00FF',
|
||||
};
|
||||
|
||||
render(<PillCell {...defaultProps} cellOptions={fixedOptions} />);
|
||||
expect(screen.getByText('test-value')).toBeInTheDocument();
|
||||
it('empty string', () => {
|
||||
expectHTML(render(<PillCell {...props} value="" />), '');
|
||||
});
|
||||
|
||||
it('should use auto color when colorMode is auto', () => {
|
||||
const autoOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'auto',
|
||||
};
|
||||
// it('null', () => {
|
||||
// expectHTML(
|
||||
// render(<PillCell {...props} value={null} />),
|
||||
// '<span class="${pillClass}" style="background-color: rgb(63, 43, 91); color: rgb(255, 255, 255);">value1</span>'
|
||||
// );
|
||||
// });
|
||||
|
||||
render(<PillCell {...defaultProps} cellOptions={autoOptions} />);
|
||||
expect(screen.getByText('test-value')).toBeInTheDocument();
|
||||
it('CSV values', () => {
|
||||
expectHTML(
|
||||
render(<PillCell {...props} value="value1,value2,value3" />),
|
||||
`
|
||||
<span class="${pillClass}" style="background-color: rgb(63, 43, 91); color: rgb(255, 255, 255);">value1</span>
|
||||
<span class="${pillClass}" style="background-color: rgb(252, 226, 222); color: rgb(0, 0, 0);">value2</span>
|
||||
<span class="${pillClass}" style="background-color: rgb(81, 149, 206); color: rgb(0, 0, 0);">value3</span>
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inferPills', () => {
|
||||
// These tests verify the pill parsing logic handles various input formats correctly.
|
||||
// They ensure the function can extract pill values from different data structures.
|
||||
|
||||
it('should return empty array for null/undefined values', () => {
|
||||
expect(inferPills(null)).toEqual([]);
|
||||
expect(inferPills(undefined)).toEqual([]);
|
||||
expect(inferPills('')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should parse single values', () => {
|
||||
expect(inferPills('test')).toEqual(['test']);
|
||||
expect(inferPills('"quoted"')).toEqual(['quoted']);
|
||||
expect(inferPills("'quoted'")).toEqual(['quoted']);
|
||||
});
|
||||
|
||||
it('should parse CSV strings', () => {
|
||||
expect(inferPills('value1,value2,value3')).toEqual(['value1', 'value2', 'value3']);
|
||||
expect(inferPills(' value1 , value2 , value3 ')).toEqual(['value1', 'value2', 'value3']);
|
||||
expect(inferPills('value1, ,value3')).toEqual(['value1', 'value3']);
|
||||
});
|
||||
|
||||
it('should parse JSON arrays', () => {
|
||||
expect(inferPills('["item1","item2","item3"]')).toEqual(['item1', 'item2', 'item3']);
|
||||
expect(inferPills('["item1", "item2", "item3"]')).toEqual(['item1', 'item2', 'item3']);
|
||||
expect(inferPills('["item1", null, "item3"]')).toEqual(['item1', 'item3']);
|
||||
});
|
||||
|
||||
it('should handle mixed content', () => {
|
||||
// When JSON parsing fails, it falls back to CSV parsing
|
||||
expect(inferPills('["item1", "item2"],extra')).toEqual(['["item1"', '"item2"]', 'extra']);
|
||||
expect(inferPills('not-json,value')).toEqual(['not-json', 'value']);
|
||||
|
||||
it('JSON array values', () => {
|
||||
expectHTML(
|
||||
render(<PillCell {...props} value='["value1","value2","value3"]' />),
|
||||
`
|
||||
<span class="${pillClass}" style="background-color: rgb(63, 43, 91); color: rgb(255, 255, 255);">value1</span>
|
||||
<span class="${pillClass}" style="background-color: rgb(252, 226, 222); color: rgb(0, 0, 0);">value2</span>
|
||||
<span class="${pillClass}" style="background-color: rgb(81, 149, 206); color: rgb(0, 0, 0);">value3</span>
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: handle null values?
|
||||
});
|
||||
|
||||
describe('Color by value mappings', () => {
|
||||
const field: Field = {
|
||||
...mockField,
|
||||
config: {
|
||||
...mockField.config,
|
||||
mappings: [
|
||||
{
|
||||
type: MappingType.ValueToText,
|
||||
options: {
|
||||
success: { color: '#00FF00' },
|
||||
error: { color: '#FF0000' },
|
||||
warning: { color: '#FFFF00' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
display: (value: unknown) => ({
|
||||
text: String(value),
|
||||
color:
|
||||
value === 'success' ? '#00FF00' : value === 'error' ? '#FF0000' : value === 'warning' ? '#FFFF00' : '#FF780A',
|
||||
numeric: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
const props = {
|
||||
...defaultProps,
|
||||
field,
|
||||
};
|
||||
|
||||
it('CSV values', () => {
|
||||
expectHTML(
|
||||
render(<PillCell {...props} value="success,error,warning,unknown" />),
|
||||
`
|
||||
<span class="${pillClass}" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);">success</span>
|
||||
<span class="${pillClass}" style="background-color: rgb(255, 0, 0); color: rgb(0, 0, 0);">error</span>
|
||||
<span class="${pillClass}" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">warning</span>
|
||||
<span class="${pillClass}" style="background-color: rgb(255, 120, 10); color: rgb(0, 0, 0);">unknown</span>
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: handle null values?
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import { css } from '@emotion/css';
|
||||
import { Property } from 'csstype';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2, isDataFrame, classicColors, colorManipulator, Field } from '@grafana/data';
|
||||
import { TablePillCellOptions } from '@grafana/schema';
|
||||
import {
|
||||
GrafanaTheme2,
|
||||
classicColors,
|
||||
colorManipulator,
|
||||
Field,
|
||||
getColorByStringHash,
|
||||
FALLBACK_COLOR,
|
||||
} from '@grafana/data';
|
||||
import { FieldColorModeId } from '@grafana/schema';
|
||||
|
||||
import { useStyles2 } from '../../../../themes/ThemeContext';
|
||||
import { useStyles2, useTheme2 } from '../../../../themes/ThemeContext';
|
||||
import { TableCellRendererProps } from '../types';
|
||||
|
||||
const DEFAULT_PILL_BG_COLOR = '#FF780A';
|
||||
|
||||
interface Pill {
|
||||
value: string;
|
||||
key: string;
|
||||
|
@ -17,9 +21,9 @@ interface Pill {
|
|||
color: string;
|
||||
}
|
||||
|
||||
function createPills(pillValues: string[], cellOptions: TableCellRendererProps['cellOptions'], field: Field): Pill[] {
|
||||
function createPills(pillValues: string[], field: Field, theme: GrafanaTheme2): Pill[] {
|
||||
return pillValues.map((pill, index) => {
|
||||
const bgColor = getPillColor(pill, cellOptions, field);
|
||||
const bgColor = getPillColor(pill, field, theme);
|
||||
const textColor = colorManipulator.getContrastRatio('#FFFFFF', bgColor) >= 4.5 ? '#FFFFFF' : '#000000';
|
||||
return {
|
||||
value: pill,
|
||||
|
@ -30,156 +34,73 @@ function createPills(pillValues: string[], cellOptions: TableCellRendererProps['
|
|||
});
|
||||
}
|
||||
|
||||
export function PillCell({ value, field, justifyContent, cellOptions }: TableCellRendererProps) {
|
||||
const styles = useStyles2(getStyles, justifyContent);
|
||||
export function PillCell({ value, field }: TableCellRendererProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const theme = useTheme2();
|
||||
|
||||
const pills: Pill[] = useMemo(() => {
|
||||
const pillValues = inferPills(value);
|
||||
return createPills(pillValues, cellOptions, field);
|
||||
}, [value, cellOptions, field]);
|
||||
const pillValues = inferPills(String(value));
|
||||
return createPills(pillValues, field, theme);
|
||||
}, [value, field, theme]);
|
||||
|
||||
if (pills.length === 0) {
|
||||
return <div className={styles.cell}>-</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.cell}>
|
||||
<div className={styles.pillsContainer}>
|
||||
{pills.map((pill) => (
|
||||
<span
|
||||
key={pill.key}
|
||||
className={styles.pill}
|
||||
style={{
|
||||
backgroundColor: pill.bgColor,
|
||||
color: pill.color,
|
||||
}}
|
||||
>
|
||||
{pill.value}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return pills.map((pill) => (
|
||||
<span
|
||||
key={pill.key}
|
||||
className={styles.pill}
|
||||
style={{
|
||||
backgroundColor: pill.bgColor,
|
||||
color: pill.color,
|
||||
border: pill.bgColor === TRANSPARENT ? `1px solid ${theme.colors.border.strong}` : undefined,
|
||||
}}
|
||||
>
|
||||
{pill.value}
|
||||
</span>
|
||||
));
|
||||
}
|
||||
|
||||
export function inferPills(value: unknown): string[] {
|
||||
if (!value) {
|
||||
const SPLIT_RE = /\s*,\s*/;
|
||||
const TRANSPARENT = 'rgba(0,0,0,0)';
|
||||
|
||||
export function inferPills(value: string): string[] {
|
||||
if (value === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Handle DataFrame - not supported for pills
|
||||
if (isDataFrame(value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Handle different value types
|
||||
const stringValue = String(value);
|
||||
|
||||
// Try to parse as JSON first
|
||||
try {
|
||||
const parsed = JSON.parse(stringValue);
|
||||
if (Array.isArray(parsed)) {
|
||||
// JSON array of strings
|
||||
return parsed
|
||||
.filter((item) => item != null && item !== '')
|
||||
.map(String)
|
||||
.map((text) => text.trim())
|
||||
.filter((item) => item !== '');
|
||||
if (value[0] === '[') {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return value.trim().split(SPLIT_RE);
|
||||
}
|
||||
} catch {
|
||||
// Not valid JSON, continue with other parsing
|
||||
}
|
||||
|
||||
// Handle CSV string
|
||||
if (stringValue.includes(',')) {
|
||||
return stringValue
|
||||
.split(',')
|
||||
.map((text) => text.trim())
|
||||
.filter((item) => item !== '');
|
||||
}
|
||||
|
||||
// Single value - strip quotes
|
||||
return [stringValue.replace(/["'`]/g, '').trim()];
|
||||
return value.trim().split(SPLIT_RE);
|
||||
}
|
||||
|
||||
function isPillCellOptions(cellOptions: TableCellRendererProps['cellOptions']): cellOptions is TablePillCellOptions {
|
||||
return cellOptions?.type === 'pill';
|
||||
function getPillColor(value: string, field: Field, theme: GrafanaTheme2): string {
|
||||
const cfg = field.config;
|
||||
|
||||
if (cfg.mappings?.length ?? 0 > 0) {
|
||||
return field.display!(value).color ?? FALLBACK_COLOR;
|
||||
}
|
||||
|
||||
if (cfg.color?.mode === FieldColorModeId.Fixed) {
|
||||
return theme.visualization.getColorByName(cfg.color?.fixedColor ?? FALLBACK_COLOR);
|
||||
}
|
||||
|
||||
// TODO: instead of classicColors we need to pull colors from theme, same way as FieldColorModeId.PaletteClassicByName (see fieldColor.ts)
|
||||
return getColorByStringHash(classicColors, value);
|
||||
}
|
||||
|
||||
function getPillColor(pill: string, cellOptions: TableCellRendererProps['cellOptions'], field: Field): string {
|
||||
if (!isPillCellOptions(cellOptions)) {
|
||||
return getDeterministicColor(pill);
|
||||
}
|
||||
|
||||
const colorMode = cellOptions.colorMode || 'auto';
|
||||
|
||||
// Fixed color mode (highest priority)
|
||||
if (colorMode === 'fixed' && cellOptions.color) {
|
||||
return cellOptions.color;
|
||||
}
|
||||
|
||||
// Mapped color mode - use field's value mappings
|
||||
if (colorMode === 'mapped') {
|
||||
// Check if field has value mappings
|
||||
if (field.config.mappings && field.config.mappings.length > 0) {
|
||||
// Use the field's display processor to get the mapped value
|
||||
const displayValue = field.display!(pill);
|
||||
if (displayValue.color) {
|
||||
return displayValue.color;
|
||||
}
|
||||
}
|
||||
// Fallback to default color for unmapped values
|
||||
return cellOptions.color || DEFAULT_PILL_BG_COLOR;
|
||||
}
|
||||
|
||||
// Auto mode - deterministic color assignment based on string hash
|
||||
if (colorMode === 'auto') {
|
||||
return getDeterministicColor(pill);
|
||||
}
|
||||
|
||||
// Default color for unknown values or fallback
|
||||
return DEFAULT_PILL_BG_COLOR;
|
||||
}
|
||||
|
||||
function getDeterministicColor(text: string): string {
|
||||
// Create a simple hash of the string to get consistent colors
|
||||
let hash = 0;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const char = text.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash; // Convert to 32-bit integer
|
||||
}
|
||||
|
||||
// Use absolute value and modulo to get a consistent index
|
||||
const colorValues = Object.values(classicColors);
|
||||
const index = Math.abs(hash) % colorValues.length;
|
||||
|
||||
return colorValues[index];
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, justifyContent: Property.JustifyContent | undefined) => ({
|
||||
cell: css({
|
||||
display: 'flex',
|
||||
justifyContent: justifyContent || 'flex-start',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
padding: theme.spacing(0.5),
|
||||
}),
|
||||
pillsContainer: css({
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: theme.spacing(0.5),
|
||||
maxWidth: '100%',
|
||||
}),
|
||||
export const getStyles = (theme: GrafanaTheme2) => ({
|
||||
pill: css({
|
||||
display: 'inline-block',
|
||||
padding: theme.spacing(0.25, 0.75),
|
||||
marginInlineEnd: theme.spacing(0.5),
|
||||
marginBlock: theme.spacing(0.5),
|
||||
borderRadius: theme.shape.radius.default,
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
lineHeight: theme.typography.bodySmall.lineHeight,
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
whiteSpace: 'nowrap',
|
||||
textAlign: 'center',
|
||||
minWidth: 'fit-content',
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -109,6 +109,12 @@ export function getCellRenderer(field: Field, cellOptions: TableCellOptions): Ta
|
|||
if (cellType === TableCellDisplayMode.Auto) {
|
||||
return getAutoRendererResult(field);
|
||||
}
|
||||
|
||||
// TODO: add support boolean, enum, (maybe int). but for now just string fields
|
||||
if (cellType === TableCellDisplayMode.Pill && field.type !== FieldType.string) {
|
||||
return AUTO_RENDERER;
|
||||
}
|
||||
|
||||
return CELL_RENDERERS[cellType] ?? AUTO_RENDERER;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,18 @@ import {
|
|||
SortColumn,
|
||||
} from 'react-data-grid';
|
||||
|
||||
import { DataHoverClearEvent, DataHoverEvent, Field, FieldType, GrafanaTheme2, ReducerID } from '@grafana/data';
|
||||
import {
|
||||
DataHoverClearEvent,
|
||||
DataHoverEvent,
|
||||
FALLBACK_COLOR,
|
||||
Field,
|
||||
FieldType,
|
||||
getDisplayProcessor,
|
||||
GrafanaTheme2,
|
||||
ReducerID,
|
||||
} from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { TableCellHeight } from '@grafana/schema';
|
||||
import { FieldColorModeId, TableCellHeight } from '@grafana/schema';
|
||||
|
||||
import { useStyles2, useTheme2 } from '../../../themes/ThemeContext';
|
||||
import { ContextMenu } from '../../ContextMenu/ContextMenu';
|
||||
|
@ -272,11 +281,30 @@ export function TableNG(props: TableNGProps) {
|
|||
let _rowHeight = 0;
|
||||
|
||||
f.forEach((field, i) => {
|
||||
const cellOptions = getCellOptions(field);
|
||||
const cellType = cellOptions.type;
|
||||
|
||||
// make sure we use mappings exclusively if they exist, ignore default thresholds mode
|
||||
// we hack this by using the single color mode calculator
|
||||
if (cellType === TableCellDisplayMode.Pill && (field.config.mappings?.length ?? 0 > 0)) {
|
||||
field = {
|
||||
...field,
|
||||
config: {
|
||||
...field.config,
|
||||
color: {
|
||||
...field.config.color,
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: field.config.color?.fixedColor ?? FALLBACK_COLOR,
|
||||
},
|
||||
},
|
||||
};
|
||||
field.display = getDisplayProcessor({ field, theme });
|
||||
}
|
||||
|
||||
const justifyContent = getTextAlign(field);
|
||||
const footerStyles = getFooterStyles(justifyContent);
|
||||
const displayName = getDisplayName(field);
|
||||
const headerCellClass = getHeaderCellStyles(theme, justifyContent).headerCell;
|
||||
const cellOptions = getCellOptions(field);
|
||||
const renderFieldCell = getCellRenderer(field, cellOptions);
|
||||
|
||||
const cellInspect = isCellInspectEnabled(field);
|
||||
|
@ -293,7 +321,6 @@ export function TableNG(props: TableNGProps) {
|
|||
)
|
||||
: undefined;
|
||||
|
||||
const cellType = cellOptions.type;
|
||||
const shouldOverflow = shouldTextOverflow(field);
|
||||
const shouldWrap = shouldTextWrap(field);
|
||||
const withTooltip = withDataLinksActionsTooltip(field, cellType);
|
||||
|
|
|
@ -128,11 +128,14 @@ export function getMaxWrapCell(
|
|||
* Returns true if text overflow handling should be applied to the cell.
|
||||
*/
|
||||
export function shouldTextOverflow(field: Field): boolean {
|
||||
let type = getCellOptions(field).type;
|
||||
|
||||
return (
|
||||
field.type === FieldType.string &&
|
||||
// Tech debt: Technically image cells are of type string, which is misleading (kinda?)
|
||||
// so we need to ensure we don't apply overflow hover states for type image
|
||||
getCellOptions(field).type !== TableCellDisplayMode.Image &&
|
||||
type !== TableCellDisplayMode.Image &&
|
||||
type !== TableCellDisplayMode.Pill &&
|
||||
!shouldTextWrap(field) &&
|
||||
!isCellInspectEnabled(field)
|
||||
);
|
||||
|
|
|
@ -10,7 +10,6 @@ import { AutoCellOptionsEditor } from './cells/AutoCellOptionsEditor';
|
|||
import { BarGaugeCellOptionsEditor } from './cells/BarGaugeCellOptionsEditor';
|
||||
import { ColorBackgroundCellOptionsEditor } from './cells/ColorBackgroundCellOptionsEditor';
|
||||
import { ImageCellOptionsEditor } from './cells/ImageCellOptionsEditor';
|
||||
import { PillCellOptionsEditor } from './cells/PillCellOptionsEditor';
|
||||
import { SparklineCellOptionsEditor } from './cells/SparklineCellOptionsEditor';
|
||||
|
||||
// The props that any cell type editor are expected
|
||||
|
@ -78,9 +77,6 @@ export const TableCellOptionEditor = ({ value, onChange }: Props) => {
|
|||
{cellType === TableCellDisplayMode.Image && (
|
||||
<ImageCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
|
||||
)}
|
||||
{cellType === TableCellDisplayMode.Pill && (
|
||||
<PillCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import { t } from '@grafana/i18n';
|
||||
import { TablePillCellOptions } from '@grafana/schema';
|
||||
import { Field, ColorPicker, RadioButtonGroup, Stack } from '@grafana/ui';
|
||||
|
||||
import { TableCellEditorProps } from '../TableCellOptionEditor';
|
||||
|
||||
const colorModeOptions: Array<{ value: 'auto' | 'fixed' | 'mapped'; label: string }> = [
|
||||
{ value: 'auto', label: 'Auto' },
|
||||
{ value: 'fixed', label: 'Fixed color' },
|
||||
{ value: 'mapped', label: 'Value mapping' },
|
||||
];
|
||||
|
||||
export const PillCellOptionsEditor = ({ cellOptions, onChange }: TableCellEditorProps<TablePillCellOptions>) => {
|
||||
const colorMode = cellOptions.colorMode || 'auto';
|
||||
|
||||
const onColorModeChange = (mode: 'auto' | 'fixed' | 'mapped') => {
|
||||
const updatedOptions = { ...cellOptions, colorMode: mode };
|
||||
onChange(updatedOptions);
|
||||
};
|
||||
|
||||
const onColorChange = (color: string) => {
|
||||
const updatedOptions = { ...cellOptions, color };
|
||||
onChange(updatedOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction="column" gap={1}>
|
||||
<Field
|
||||
label={t('table.pill-cell-options-editor.label-color-mode', 'Color Mode')}
|
||||
description={t(
|
||||
'table.pill-cell-options-editor.description-color-mode',
|
||||
'Choose how colors are assigned to pills'
|
||||
)}
|
||||
noMargin
|
||||
>
|
||||
<RadioButtonGroup value={colorMode} onChange={onColorModeChange} options={colorModeOptions} />
|
||||
</Field>
|
||||
|
||||
{colorMode === 'fixed' && (
|
||||
<Field
|
||||
label={t('table.pill-cell-options-editor.label-fixed-color', 'Fixed Color')}
|
||||
description={t(
|
||||
'table.pill-cell-options-editor.description-fixed-color',
|
||||
'All pills in this column will use this color'
|
||||
)}
|
||||
noMargin
|
||||
>
|
||||
<ColorPicker color={cellOptions.color || '#FF780A'} onChange={onColorChange} enableNamedColors={false} />
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{colorMode === 'mapped' && (
|
||||
<Field
|
||||
label={t('table.pill-cell-options-editor.label-value-mappings-info', 'Value Mappings')}
|
||||
description={t(
|
||||
'table.pill-cell-options-editor.description-value-mappings-info',
|
||||
'For Value Mappings either use the global table Value Mappings or the Field overrides Value Mappings. The default will fall back to the Color Scheme. '
|
||||
)}
|
||||
noMargin
|
||||
>
|
||||
<div> </div>
|
||||
</Field>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
|
@ -11871,14 +11871,6 @@
|
|||
"name-show-table-footer": "Show table footer",
|
||||
"name-show-table-header": "Show table header",
|
||||
"name-wrap-header-text": "Wrap header text",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "Choose how colors are assigned to pills",
|
||||
"description-fixed-color": "All pills in this column will use this color",
|
||||
"description-value-mappings-info": "For Value Mappings either use the global table Value Mappings or the Field overrides Value Mappings. The default will fall back to the Color Scheme. ",
|
||||
"label-color-mode": "Color Mode",
|
||||
"label-fixed-color": "Fixed Color",
|
||||
"label-value-mappings-info": "Value Mappings"
|
||||
},
|
||||
"placeholder-column-width": "auto",
|
||||
"placeholder-fields": "All Numeric Fields"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue