@ -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 , inferPill s } from './PillCell' ;
import { PillCell , getStyle s } 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,68 +50,63 @@ 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 ( ) ;
} ) ;
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> `
) ;
} ) ;
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' ,
} ;
it ( 'empty string' , ( ) = > {
expectHTML ( render ( < PillCell { ...props } value = "" / > ) , '' ) ;
} ) ;
render ( < PillCell { ...defaultProps } value = "success,error,warning,unknown" cellOptions = { mappedOptions } / > ) ;
// 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>'
// );
// });
const successPill = screen . getByText ( 'success' ) ;
const errorPill = screen . getByText ( 'error' ) ;
const warningPill = screen . getByText ( 'warning' ) ;
const unknownPill = screen . getByText ( 'unknown' ) ;
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 >
`
) ;
} ) ;
expect ( successPill ) . toBeInTheDocument ( ) ;
expect ( errorPill ) . toBeInTheDocument ( ) ;
expect ( warningPill ) . toBeInTheDocument ( ) ;
expect ( unknownPill ) . toBeInTheDocument ( ) ;
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 >
`
) ;
} ) ;
it ( 'should use field-level value mappings when available' , ( ) = > {
const mappedOptions : TablePillCellOptions = {
type : TableCellDisplayMode . Pill ,
colorMode : 'mapped' ,
} ;
// TODO: handle null values?
} ) ;
// Mock field with value mappings
const fieldWithMappings : Field = {
describe ( 'Color by value mappings' , ( ) = > {
const field : Field = {
. . . mockField ,
config : {
. . . mockField . config ,
@ -129,91 +124,28 @@ describe('PillCell', () => {
display : ( value : unknown ) = > ( {
text : String ( value ) ,
color :
String ( value ) === 'success'
? '#00FF00'
: String ( value ) === 'error'
? '#FF0000'
: String ( value ) === 'warning'
? '#FFFF00'
: '#FF780A' ,
value === 'success' ? '#00FF00' : value === 'error' ? '#FF0000' : value === 'warning' ? '#FFFF00' : '#FF780A' ,
numeric : 0 ,
} ) ,
} ;
render (
< PillCell
{ . . . defaultProps }
value = "success,error,warning,unknown"
cellOptions = { mappedOptions }
field = { fieldWithMappings }
/ >
) ;
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 ( 'should use auto color when colorMode is auto' , ( ) = > {
const autoOptions : TablePillCellOptions = {
type : TableCellDisplayMode . Pill ,
colorMode : 'auto' ,
const props = {
. . . defaultProps ,
field ,
} ;
render ( < PillCell { ...defaultProps } cellOptions = { autoOptions } / > ) ;
expect ( screen . getByText ( 'test-value' ) ) . toBeInTheDocument ( ) ;
} ) ;
} ) ;
} ) ;
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 ( '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 >
`
) ;
} ) ;
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' ] ) ;
// TODO: handle null values?
} ) ;
} ) ;