mirror of https://github.com/grafana/grafana
Merge pull request #15305 from avaly/feature/ansi-colors
Support ANSI colors codes in Loki logspull/15294/head^2
commit
2c92365969
@ -0,0 +1,24 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { shallow } from 'enzyme'; |
||||||
|
|
||||||
|
import { LogMessageAnsi } from './LogMessageAnsi'; |
||||||
|
|
||||||
|
describe('<LogMessageAnsi />', () => { |
||||||
|
it('renders string without ANSI codes', () => { |
||||||
|
const wrapper = shallow(<LogMessageAnsi value="Lorem ipsum" />); |
||||||
|
|
||||||
|
expect(wrapper.find('span').exists()).toBe(false); |
||||||
|
expect(wrapper.text()).toBe('Lorem ipsum'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('renders string with ANSI codes', () => { |
||||||
|
const value = 'Lorem \u001B[31mipsum\u001B[0m et dolor'; |
||||||
|
const wrapper = shallow(<LogMessageAnsi value={value} />); |
||||||
|
|
||||||
|
expect(wrapper.find('span')).toHaveLength(1); |
||||||
|
expect(wrapper.find('span').first().prop('style')).toMatchObject(expect.objectContaining({ |
||||||
|
color: expect.any(String) |
||||||
|
})); |
||||||
|
expect(wrapper.find('span').first().text()).toBe('ipsum'); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
import React, { PureComponent } from 'react'; |
||||||
|
import ansicolor from 'ansicolor'; |
||||||
|
|
||||||
|
interface Style { |
||||||
|
[key: string]: string; |
||||||
|
} |
||||||
|
|
||||||
|
interface ParsedChunk { |
||||||
|
style: Style; |
||||||
|
text: string; |
||||||
|
} |
||||||
|
|
||||||
|
function convertCSSToStyle(css: string): Style { |
||||||
|
return css.split(/;\s*/).reduce((accumulated, line) => { |
||||||
|
const match = line.match(/([^:\s]+)\s*:\s*(.+)/); |
||||||
|
|
||||||
|
if (match && match[1] && match[2]) { |
||||||
|
const key = match[1].replace(/-(a-z)/g, (_, character) => character.toUpperCase()); |
||||||
|
accumulated[key] = match[2]; |
||||||
|
} |
||||||
|
|
||||||
|
return accumulated; |
||||||
|
}, {}); |
||||||
|
} |
||||||
|
|
||||||
|
interface Props { |
||||||
|
value: string; |
||||||
|
} |
||||||
|
|
||||||
|
interface State { |
||||||
|
chunks: ParsedChunk[]; |
||||||
|
prevValue: string; |
||||||
|
} |
||||||
|
|
||||||
|
export class LogMessageAnsi extends PureComponent<Props, State> { |
||||||
|
state = { |
||||||
|
chunks: [], |
||||||
|
prevValue: '', |
||||||
|
}; |
||||||
|
|
||||||
|
static getDerivedStateFromProps(props, state) { |
||||||
|
if (props.value === state.prevValue) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
const parsed = ansicolor.parse(props.value); |
||||||
|
|
||||||
|
return { |
||||||
|
chunks: parsed.spans.map((span) => { |
||||||
|
return span.css ? |
||||||
|
{ |
||||||
|
style: convertCSSToStyle(span.css), |
||||||
|
text: span.text |
||||||
|
} : |
||||||
|
{ text: span.text }; |
||||||
|
}), |
||||||
|
prevValue: props.value |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
const { chunks } = this.state; |
||||||
|
|
||||||
|
return chunks.map( |
||||||
|
(chunk, index) => chunk.style ? |
||||||
|
<span key={index} style={chunk.style}>{chunk.text}</span> : |
||||||
|
chunk.text |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue