diff --git a/app/gui/src/project-view/components/visualizations/TableVisualization.vue b/app/gui/src/project-view/components/visualizations/TableVisualization.vue index ecbd3ec15636..c2573f35d8a3 100644 --- a/app/gui/src/project-view/components/visualizations/TableVisualization.vue +++ b/app/gui/src/project-view/components/visualizations/TableVisualization.vue @@ -8,7 +8,6 @@ import { } from '@/components/visualizations/TableVisualization/tableVizToolbar' import { Ast } from '@/util/ast' import { Pattern } from '@/util/ast/match' -import { LINKABLE_URL_REGEX } from '@/util/link' import { useVisualizationConfig } from '@/util/visualizationBuiltins' import type { CellClassParams, @@ -20,7 +19,7 @@ import type { } from 'ag-grid-enterprise' import { computed, onMounted, ref, shallowRef, watchEffect, type Ref } from 'vue' import { TableVisualisationTooltip } from './TableVisualization/TableVisualisationTooltip' -import { getCellValueType, isNumericType } from './TableVisualization/tableVizUtils' +import { formatText, getCellValueType, isNumericType } from './TableVisualization/tableVizUtils' export const name = 'Table' export const icon = 'table' @@ -193,54 +192,6 @@ function formatNumber(params: ICellRendererParams) { return needsGrouping ? numberFormatGroupped.format(value) : numberFormat.format(value) } -function formatText(params: ICellRendererParams) { - const htmlEscaped = params.value - .replaceAll('&', '&') - .replaceAll('<', '<') - .replaceAll('>', '>') - - if (textFormatterSelected.value === 'off') { - const replaceLinks = replaceLinksWithTag(htmlEscaped) - return replaceLinks.replace(/^\s+|\s+$/g, ' ') - } - - const partialMappings = { - '\r': '
', - '\n': '
', - '\t': '→ |', - } - const fullMappings = { - '\r': '
', - '\n': '
', - '\t': '→ |', - } - - const replaceSpaces = - textFormatterSelected.value === 'full' ? - htmlEscaped.replaceAll(' ', '·') - : htmlEscaped.replace(/ \s+|^ +| +$/g, function (match: string) { - return `${match.replaceAll(' ', '·')}` - }) - - const replaceLinks = replaceLinksWithTag(replaceSpaces) - - const replaceReturns = replaceLinks.replace( - /\r\n/g, - '␍␊
', - ) - - const renderOtherWhitespace = (match: string) => { - return textFormatterSelected.value === 'full' && match != ' ' ? - '' - : match - } - const newString = replaceReturns.replace(/[\s]/g, function (match: string) { - const mapping = textFormatterSelected.value === 'full' ? fullMappings : partialMappings - return mapping[match as keyof typeof mapping] || renderOtherWhitespace(match) - }) - return ` ${newString} ` -} - function setRowLimit(newRowLimit: number) { if (newRowLimit !== rowLimit.value) { rowLimit.value = newRowLimit @@ -252,13 +203,6 @@ function setRowLimit(newRowLimit: number) { } } -function replaceLinksWithTag(str: string) { - return str.replace( - LINKABLE_URL_REGEX, - (url: string) => `${url}`, - ) -} - function escapeHTML(str: string) { const mapping: Record = { '&': '&', @@ -286,7 +230,8 @@ function cellRenderer(params: ICellRendererParams) { else if (params.value === undefined) return '' else if (params.value === '') return 'Empty' else if (typeof params.value === 'number') return formatNumber(params) - else if (typeof params.value === 'string') return formatText(params) + else if (typeof params.value === 'string') + return formatText(params.value, textFormatterSelected.value) else if (Array.isArray(params.value)) return `[Vector ${params.value.length} items]` else if (typeof params.value === 'object') { const valueType = params.value?.type diff --git a/app/gui/src/project-view/components/visualizations/TableVisualization/tableVizUtils.ts b/app/gui/src/project-view/components/visualizations/TableVisualization/tableVizUtils.ts index f3ab1cb5fa3e..b3a5107df9fe 100644 --- a/app/gui/src/project-view/components/visualizations/TableVisualization/tableVizUtils.ts +++ b/app/gui/src/project-view/components/visualizations/TableVisualization/tableVizUtils.ts @@ -1,3 +1,6 @@ +import { LINKABLE_URL_REGEX } from '@/util/link' +import { TextFormatOptions } from '../TableVisualization.vue' + export const getCellValueType = (item: string) => { switch (true) { case isInteger(item): @@ -35,3 +38,55 @@ export const isNumericType = (valueType: string) => { const isNumber = ['Integer', 'Float', 'Decimal', 'Byte'] return isNumber.indexOf(valueType) != -1 } + +const replaceLinksWithTag = (str: string) => { + return str.replace( + LINKABLE_URL_REGEX, + (url: string) => `${url}`, + ) +} + +export const formatText = (input: string, textFormatterSelected: TextFormatOptions) => { + const htmlEscaped = input.replaceAll('<', '<').replaceAll('>', '>') + + if (textFormatterSelected === 'off') { + const replaceLinks = replaceLinksWithTag(htmlEscaped) + return replaceLinks.replace(/^\s+|\s+$/g, ' ') + } + + const partialMappings = { + '\r': '
', + '\n': '
', + '\t': '→ |', + } + const fullMappings = { + '\r': '
', + '\n': '
', + '\t': '→ |', + } + + const replaceSpaces = + textFormatterSelected === 'full' ? + htmlEscaped.replaceAll(' ', '·') + : htmlEscaped.replace(/ \s+|^ +| +$/g, function (match: string) { + return `${match.replaceAll(' ', '·')}` + }) + + const replaceLinks = replaceLinksWithTag(replaceSpaces) + + const replaceReturns = replaceLinks.replace( + /\r\n/g, + '␍␊
', + ) + + const renderOtherWhitespace = (match: string) => { + return textFormatterSelected === 'full' && match != ' ' ? + '' + : match + } + const newString = replaceReturns.replace(/[\s]/g, function (match: string) { + const mapping = textFormatterSelected === 'full' ? fullMappings : partialMappings + return mapping[match as keyof typeof mapping] || renderOtherWhitespace(match) + }) + return ` ${newString} ` +} diff --git a/app/gui/src/project-view/components/visualizations/__tests__/tableVizUtilsTests.spec.ts b/app/gui/src/project-view/components/visualizations/__tests__/tableVizUtilsTests.spec.ts index 1c5ee33f76da..479af6aa83c6 100644 --- a/app/gui/src/project-view/components/visualizations/__tests__/tableVizUtilsTests.spec.ts +++ b/app/gui/src/project-view/components/visualizations/__tests__/tableVizUtilsTests.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest' -import { getCellValueType, isNumericType } from '../TableVisualization/tableVizUtils' +import { formatText, getCellValueType, isNumericType } from '../TableVisualization/tableVizUtils' test('getCellValueType (Text)', () => { expect(getCellValueType('Alan')).toEqual('Char') @@ -40,3 +40,15 @@ test('isNumericType (Numeric Type)', () => { test('isNumericType (Char Type)', () => { expect(isNumericType('Char')).toEqual(false) }) + +test('formatText (text with link, full formatting)', () => { + expect(formatText('https://www.google.com/search?q=rock&roll', 'full')).toEqual( + ' https://www.google.com/search?q=rock&roll ', + ) +}) + +test('formatText (text, full formatting)', () => { + expect(formatText('rock & roll', 'full')).toEqual( + ' rock·&·roll ', + ) +})