@@ -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