Skip to content

Commit cb99645

Browse files
committed
color support
1 parent 2125635 commit cb99645

File tree

3 files changed

+243
-150
lines changed

3 files changed

+243
-150
lines changed

.github/workflows/release.yml

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
permissions:
22
contents: write
3-
3+
44
name: Release
55

66
on:
77
push:
88
tags:
9-
- 'v*' # Run workflow on version tags, e.g. v1.0.0.
9+
- "v*" # Run workflow on version tags, e.g. v1.0.0.
1010

1111
jobs:
1212
release:
@@ -19,41 +19,14 @@ jobs:
1919
- name: Setup Node.js environment
2020
uses: actions/setup-node@v3
2121
with:
22-
node-version: '16'
23-
cache: 'npm'
24-
25-
- name: Setup Go environment
26-
uses: actions/setup-go@v3
27-
with:
28-
go-version: '1.19'
22+
node-version: "20"
23+
cache: "yarn"
2924

3025
- name: Install dependencies
31-
run: npm ci
26+
run: yarn
3227

3328
- name: Build and test frontend
34-
run: npm run build
35-
36-
- name: Check for backend
37-
id: check-for-backend
38-
run: |
39-
if [ -f "Magefile.go" ]
40-
then
41-
echo "has-backend=true" >> $GITHUB_OUTPUT
42-
fi
43-
44-
- name: Test backend
45-
if: steps.check-for-backend.outputs.has-backend == 'true'
46-
uses: magefile/mage-action@v2
47-
with:
48-
version: latest
49-
args: coverage
50-
51-
- name: Build backend
52-
if: steps.check-for-backend.outputs.has-backend == 'true'
53-
uses: magefile/mage-action@v2
54-
with:
55-
version: latest
56-
args: buildAll
29+
run: yarn build
5730

5831
- name: Warn missing Grafana API key
5932
run: |
@@ -124,4 +97,4 @@ jobs:
12497
- Paste this [.zip asset link](https://github.com/${{ github.repository }}/releases/download/v${{ steps.metadata.outputs.plugin-version }}/${{ steps.metadata.outputs.archive }}) in the Plugin URL field
12598
- Paste this [.zip.md5 link](https://github.com/${{ github.repository }}/releases/download/v${{ steps.metadata.outputs.plugin-version }}/${{ steps.metadata.outputs.archive-checksum }}) in the MD5 field
12699
127-
Once done please remove these instructions and publish this release.
100+
Once done please remove these instructions and publish this release.

src/dataParser.test.ts

Lines changed: 170 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,170 @@ describe('dataParser', () => {
167167
});
168168
});
169169

170-
describe('parseData - legacy format fallback', () => {
171-
it('should fall back to legacy parser with wrong number of columns', () => {
170+
describe('parseData - 4-column format with color', () => {
171+
it('should parse 4-column data with color column detected by name', () => {
172+
const frame = toDataFrame({
173+
fields: [
174+
createField('source', FieldType.string, ['A', 'B', 'C']),
175+
createField('destination', FieldType.string, ['B', 'C', 'D']),
176+
createField('color', FieldType.string, ['#FF0000', '#00FF00', '#0000FF']),
177+
createField('value', FieldType.number, [100, 200, 300]),
178+
],
179+
});
180+
181+
const mockData = {
182+
series: [frame],
183+
};
184+
185+
const options = { valueField: 'value' };
186+
const [pluginData, displayNames] = parseData(mockData, options, false, 'blue');
187+
188+
// Check nodes
189+
expect(pluginData.nodes).toHaveLength(4);
190+
expect(pluginData.nodes.map((n: any) => n.name)).toEqual(['A', 'B', 'C', 'D']);
191+
192+
// Check links have custom colors
193+
expect(pluginData.links).toHaveLength(3);
194+
expect(pluginData.links[0].color).toBe('#FF0000');
195+
expect(pluginData.links[1].color).toBe('#00FF00');
196+
expect(pluginData.links[2].color).toBe('#0000FF');
197+
198+
// Check display names include color column
199+
expect(displayNames).toHaveLength(4);
200+
expect(displayNames).toEqual(['source', 'destination', 'color', 'value']);
201+
});
202+
203+
it('should parse 4-column data with color column detected by value pattern', () => {
204+
const frame = toDataFrame({
205+
fields: [
206+
createField('from', FieldType.string, ['A', 'B']),
207+
createField('to', FieldType.string, ['B', 'C']),
208+
createField('link_color', FieldType.string, ['red', 'blue']),
209+
createField('amount', FieldType.number, [100, 200]),
210+
],
211+
});
212+
213+
const mockData = {
214+
series: [frame],
215+
};
216+
217+
const options = { valueField: 'amount' };
218+
const [pluginData] = parseData(mockData, options, false, 'green');
219+
220+
// Check links have the provided colors
221+
expect(pluginData.links).toHaveLength(2);
222+
expect(pluginData.links[0].color).toBe('red');
223+
expect(pluginData.links[1].color).toBe('blue');
224+
});
225+
226+
it('should handle named colors from the fixColor function', () => {
227+
const frame = toDataFrame({
228+
fields: [
229+
createField('source', FieldType.string, ['A', 'B']),
230+
createField('destination', FieldType.string, ['B', 'C']),
231+
createField('color', FieldType.string, ['dark-green', 'dark-blue']),
232+
createField('value', FieldType.number, [100, 200]),
233+
],
234+
});
235+
236+
const mockData = {
237+
series: [frame],
238+
};
239+
240+
const options = { valueField: 'value' };
241+
const [pluginData] = parseData(mockData, options, false, 'orange');
242+
243+
// Check that named colors are converted
244+
expect(pluginData.links[0].color).toBe('#1A7311'); // dark-green
245+
expect(pluginData.links[1].color).toBe('rgb(18, 80, 176)'); // dark-blue
246+
});
247+
248+
it('should handle RGB color strings', () => {
249+
const frame = toDataFrame({
250+
fields: [
251+
createField('source', FieldType.string, ['A']),
252+
createField('destination', FieldType.string, ['B']),
253+
createField('colour', FieldType.string, ['rgb(255, 128, 0)']),
254+
createField('value', FieldType.number, [100]),
255+
],
256+
});
257+
258+
const mockData = {
259+
series: [frame],
260+
};
261+
262+
const options = { valueField: 'value' };
263+
const [pluginData] = parseData(mockData, options, false, 'blue');
264+
265+
expect(pluginData.links[0].color).toBe('rgb(255, 128, 0)');
266+
});
267+
268+
it('should handle HSL color strings', () => {
269+
const frame = toDataFrame({
270+
fields: [
271+
createField('source', FieldType.string, ['A']),
272+
createField('destination', FieldType.string, ['B']),
273+
createField('color', FieldType.string, ['hsl(120, 100%, 50%)']),
274+
createField('value', FieldType.number, [100]),
275+
],
276+
});
277+
278+
const mockData = {
279+
series: [frame],
280+
};
281+
282+
const options = { valueField: 'value' };
283+
const [pluginData] = parseData(mockData, options, false, 'blue');
284+
285+
expect(pluginData.links[0].color).toBe('hsl(120, 100%, 50%)');
286+
});
287+
288+
it('should detect color column with "colour" spelling', () => {
289+
const frame = toDataFrame({
290+
fields: [
291+
createField('source', FieldType.string, ['A']),
292+
createField('destination', FieldType.string, ['B']),
293+
createField('colour', FieldType.string, ['#ABCDEF']),
294+
createField('value', FieldType.number, [100]),
295+
],
296+
});
297+
298+
const mockData = {
299+
series: [frame],
300+
};
301+
302+
const options = { valueField: 'value' };
303+
const [pluginData] = parseData(mockData, options, false, 'blue');
304+
305+
expect(pluginData.links[0].color).toBe('#ABCDEF');
306+
});
307+
308+
it('should use different colors for each link when colors are provided', () => {
309+
const frame = toDataFrame({
310+
fields: [
311+
createField('source', FieldType.string, ['A', 'A', 'B']),
312+
createField('destination', FieldType.string, ['B', 'C', 'C']),
313+
createField('color', FieldType.string, ['#FF0000', '#00FF00', '#0000FF']),
314+
createField('value', FieldType.number, [100, 150, 200]),
315+
],
316+
});
317+
318+
const mockData = {
319+
series: [frame],
320+
};
321+
322+
const options = { valueField: 'value' };
323+
const [pluginData] = parseData(mockData, options, false, 'blue');
324+
325+
// Each link should have its own color
326+
expect(pluginData.links[0].color).toBe('#FF0000');
327+
expect(pluginData.links[1].color).toBe('#00FF00');
328+
expect(pluginData.links[2].color).toBe('#0000FF');
329+
});
330+
});
331+
332+
describe('parseData - invalid format', () => {
333+
it('should return empty data with wrong number of columns', () => {
172334
const frame = toDataFrame({
173335
fields: [
174336
createField('col1', FieldType.string, ['A']),
@@ -183,17 +345,17 @@ describe('dataParser', () => {
183345
series: [frame],
184346
};
185347

186-
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
348+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
187349
const options = { valueField: 'value' };
188350
const [pluginData] = parseData(mockData, options, false, 'blue');
189351

190-
expect(consoleWarnSpy).toHaveBeenCalledWith(
191-
expect.stringContaining('Expected 3 columns')
352+
expect(consoleErrorSpy).toHaveBeenCalledWith(
353+
expect.stringContaining('Expected 3 or 4 columns')
192354
);
193-
expect(pluginData.nodes).toBeDefined();
194-
expect(pluginData.links).toBeDefined();
355+
expect(pluginData.nodes).toHaveLength(0);
356+
expect(pluginData.links).toHaveLength(0);
195357

196-
consoleWarnSpy.mockRestore();
358+
consoleErrorSpy.mockRestore();
197359
});
198360
});
199361

0 commit comments

Comments
 (0)