Skip to content

Commit

Permalink
feat(Table): URL cell can fallback to short-text cell (#263)
Browse files Browse the repository at this point in the history
If the value provided in the URL column does not use one of the following protocols: `http`,` https`, `ftp`, `ftps`, `sftp`, `mailto, the content will be displayed in a short-text cell instead.
  • Loading branch information
etienneburdet authored Nov 6, 2024
1 parent f4c5e15 commit 89ca583
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 38 deletions.
2 changes: 1 addition & 1 deletion packages/visualizations-react/stories/Table/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default [
isFeatured: true,
wordCount: 1200,
readingTime: 5.5,
url: 'https://example.com/lorem-ipsum',
url: 'htt:broken-url',
geopoint: [2.357573,48.837904],
geoshape: 'centre-val-de-loire',
},
Expand Down
2 changes: 1 addition & 1 deletion packages/visualizations-react/stories/Table/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const columns: Column[] = [
key: 'url',
dataFormat: 'url',
options: {
display: () => 'link',
display: value => value.startsWith('https://') ? 'link' : 'broken link',
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
<script lang="ts">
import { warn } from './utils';
import ShortTextFormat from './ShortTextFormat.svelte';
import { isValidUrl } from './utils';
export let rawValue: unknown;
export let display = (v: string) => v;
export let target: HTMLAnchorElement['target'] = '_blank';
export let rel = 'nofollow noreferrer noopener';
function format(v: unknown) {
try {
// eslint-disable-next-line no-new
new URL(v as string);
return v as string;
} catch (error) {
warn(v, 'URL');
return null;
$: format = (v: unknown) => {
if (isValidUrl(v)) {
return {
text: display(v),
href: v,
};
}
}
// eslint-disable-next-line no-console
console.warn(`ODS Dataviz SDK - Table: no url detected in ${v}. Formatting as string.`);
return { text: null, href: null };
};
$: value = format(rawValue);
$: ({ text, href } = format(rawValue));
</script>

{#if value}
<a href={value} {target}>{display(value)}</a>
{#if text}
<a {href} {rel} {target}>{text}</a>
{:else}
{rawValue}
<ShortTextFormat {rawValue} {display} />
{/if}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import BooleanFormat from './BooleanFormat.svelte';
import DateFormat from './DateFormat.svelte';
import GeoFormat from './GeoFormat.svelte';
import LongTextFormat from './LongTextFormat.svelte';
import NumberFormat from './NumberFormat.svelte';
import URLFormat from './URLFormat.svelte';
import ShortTextFormat from './ShortTextFormat.svelte';
import LongTextFormat from './LongTextFormat.svelte';
import GeoFormat from './GeoFormat.svelte';
import URLFormat from './URLFormat.svelte';
import { isValidRawValue } from './utils';

const Format = {
Expand Down
12 changes: 12 additions & 0 deletions packages/visualizations/src/components/Table/Cell/Format/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,15 @@ export function warn(value: unknown, format: string) {
// eslint-disable-next-line no-console
console.warn(`ODS Dataviz SDK - Table: ${value} is not a valid ${format}`);
}

export function isValidUrl(text: unknown): text is string {
try {
const url = new URL(text as string);
if (['http:', 'https:', 'ftp:', 'ftps:', 'sftp:', 'mailto:'].includes(url.protocol)) {
return true;
}
return false;
} catch {
return false;
}
}
6 changes: 4 additions & 2 deletions packages/visualizations/src/components/Table/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { DataFrame, Source, Async } from 'types';
import type { WebGlMapData, WebGlMapOptions } from 'components/Map';
import type { DATA_FORMAT } from './constants';
import type { Async, DataFrame, Source } from 'types';
import type { Pagination } from '../Pagination/types';
import type { DATA_FORMAT } from './constants';

type DataFormatKey = keyof typeof DATA_FORMAT;
export type DataFormat = typeof DATA_FORMAT[DataFormatKey];
Expand Down Expand Up @@ -82,6 +82,8 @@ export type URLColumn = BaseColumn & {
display?: (v: string) => string;
/** Default is `_blank` */
target?: string;
/** Default is `nofollow noopener noreferrer` */
rel?: string;
};
};

Expand Down
31 changes: 14 additions & 17 deletions packages/visualizations/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,23 @@ Only path working at runtime will work (it's a problem with TS that copy aliases
Anywhere else, aliases are fine :)
*/

/* We export Svelte types from the main package to avoid conflicting versions
It still is purely a dev thing and we'll bundle they types directly */
// eslint-disable-next-line import/no-extraneous-dependencies
export type { ComponentConstructorOptions, SvelteComponent } from 'svelte';
export { default as Chart, _ChartDataLabels } from './components/Chart';
export { default as MarkdownText } from './components/MarkdownText';
export { default as KpiCard } from './components/KpiCard';
export { ChoroplethGeoJson, ChoroplethVectorTiles } from './components/ChoroplethMap/WebGl';
export { default as ChoroplethSvg } from './components/ChoroplethMap/Svg';
export { default as Table } from './components/Table';

export * from './components/Map';

export * from './types';
export * from './components/Chart/types';
export * from './components/KpiCard/types';
export { default as ChoroplethSvg } from './components/ChoroplethMap/Svg';
export * from './components/ChoroplethMap/types';
export * from './components/MarkdownText/types';
export { ChoroplethGeoJson, ChoroplethVectorTiles } from './components/ChoroplethMap/WebGl';
export { default as KpiCard } from './components/KpiCard';
export * from './components/KpiCard/types';
export * from './components/Legend/types';
export * from './components/Table/types';
export * from './components/Map';
export { default as MarkdownText } from './components/MarkdownText';
export * from './components/MarkdownText/types';
export * from './components/Pagination/types';
export { default as Table } from './components/Table';
export * from './components/Table/types';
export * from './components/utils';

/* We export Svelte types from the main package to avoid conflicting versions
It still is purely a dev thing and we'll bundle they types directly */
// eslint-disable-next-line import/no-extraneous-dependencies
export type { SvelteComponent, ComponentConstructorOptions } from 'svelte';
export * from './types';

1 comment on commit 89ca583

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage for this commit

94.71%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   index.ts100%100%100%100%
src/client
   constants.ts100%100%100%100%
   error.ts100%100%100%100%
   index.ts84.14%74.03%100%95.31%102–103, 124, 13, 146, 148, 148–149, 15, 15, 151, 162, 169, 169, 17, 17, 171, 176, 179, 182, 184, 52, 82
   types.ts100%100%100%100%
src/odsql
   clauses.ts82.61%71.43%80%90.91%14, 32, 42
   index.ts92.05%83.72%95.74%94.19%111, 146, 25, 28, 56–57, 57, 57–58, 68, 78–79

Please sign in to comment.