Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT][TABLE][SC-46247] Basic version #223

Merged
merged 12 commits into from
Feb 23, 2024
8 changes: 8 additions & 0 deletions packages/visualizations-react/src/components/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Table as _Table, TableOptions, DataFrame } from '@opendatasoft/visualizations';
import { FC } from 'react';
import { Props } from './Props';
import wrap from './ReactImpl';

// Explicit name and type are needed for storybook
const Table: FC<Props<DataFrame, TableOptions>> = wrap(_Table);
export default Table;
1 change: 1 addition & 0 deletions packages/visualizations-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { default as ChoroplethGeoJson } from './components/ChoroplethGeoJson';
export { default as ChoroplethVectorTiles } from './components/ChoroplethVectorTiles';
export { default as ChoroplethSvg } from './components/ChoroplethSvg';
export { default as PoiMap } from './components/PoiMap';
export { default as Table } from './components/Table';
91 changes: 91 additions & 0 deletions packages/visualizations-react/stories/Table/Table.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Column, TableOptions, TableData, Async, Theme } from '@opendatasoft/visualizations';

import { Table } from '../../src';

import value from './data';

const theme: Required<Theme> = {
textColor: '#000000',
borderColor: '#fcd4cf',
header: {
textColor: '#ffffff',
backgroundColor: '#fd635d',
borderColor: '#f94346',
},
dataRow: {
activeBackgroundColor: '#f9aea4',
},
};

const meta: ComponentMeta<typeof Table> = {
title: 'Table/Table',
component: Table,
};
export default meta;

const data: Async<TableData> = {
value,
loading: false,
};

const columns: Column[] = [
{
title: 'Title',
key: 'title',
},
{
title: 'Content',
key: 'content',
},
{
title: 'Published date',
key: 'datePublished',
},
{
title: 'Featured',
key: 'isFeatured',
},
{
title: 'Word count',
key: 'wordCount',
},
{
title: 'Reading time',
key: 'readingTime',
},
{
title: 'URL',
key: 'url',
},
];

const options: TableOptions = {
columns,
title: 'My table',
subtitle: 'and a subtitle...',
description: 'An aria description',
source: {
href: '',
},
};

const Template: ComponentStory<typeof Table> = (args) => (
<div style={{ maxWidth: '800px' }}>
{' '}
<Table {...args} />
</div>
);

export const Playground = Template.bind({});
Playground.args = {
data,
options,
};

export const CustomTheme = Template.bind({});
CustomTheme.args = {
data,
options: { ...options, theme },
};
92 changes: 92 additions & 0 deletions packages/visualizations-react/stories/Table/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
export default [
{
title: "Lorem Ipsum Blog Post",
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio.",
datePublished: "2024-02-12",
isFeatured: true,
wordCount: 1200,
readingTime: 5.5,
url: "https://example.com/lorem-ipsum"
},
{
title: "Pellentesque Nec Blog Post",
content: "Pellentesque nec nisl vitae massa egestas tristique. Mauris auctor consequat justo.",
datePublished: "2024-02-13",
isFeatured: false,
wordCount: 800,
readingTime: 3.8,
url: "https://example.com/pellentesque-nec"
},
{
title: "Fusce Sit Amet Blog Post",
content: "Fusce sit amet justo vitae libero finibus viverra. Sed tincidunt risus eu tortor fermentum blandit.",
datePublished: "2024-02-14",
isFeatured: true,
wordCount: 1500,
readingTime: 7.2,
url: "https://example.com/fusce-sit-amet"
},
{
title: "Vestibulum Nec Blog Post",
content: "Vestibulum nec ante non dui cursus fermentum. Suspendisse eu aliquam turpis.",
datePublished: "2024-02-15",
isFeatured: false,
wordCount: 1000,
readingTime: 4.5,
url: "https://example.com/vestibulum-nec"
},
{
title: "Cras At Blog Post",
content: "Cras at odio eget nisi bibendum aliquam id nec nisl. Donec ultricies nisi vel arcu rhoncus, nec pellentesque mauris aliquam.",
datePublished: "2024-02-16",
isFeatured: true,
wordCount: 1300,
readingTime: 6.0,
url: "https://example.com/cras-at"
},
{
title: "Quisque A Blog Post",
content: "Quisque a sem sit amet turpis scelerisque volutpat a et arcu. Aenean luctus venenatis ex, non accumsan odio accumsan et.",
datePublished: "2024-02-17",
isFeatured: false,
wordCount: 900,
readingTime: 4.0,
url: "https://example.com/quisque-a"
},
{
title: "Ut Vitae Blog Post",
content: "Ut vitae eros sit amet felis tincidunt tristique. Nullam non nisi nec justo rhoncus imperdiet.",
datePublished: "2024-02-18",
isFeatured: true,
wordCount: 1100,
readingTime: 5.0,
url: "https://example.com/ut-vitae"
},
{
title: "Integer Id Blog Post",
content: "Integer id lectus vitae justo euismod finibus. Aliquam a sem at lectus gravida luctus.",
datePublished: "2024-02-19",
isFeatured: false,
wordCount: 950,
readingTime: 4.2,
url: "https://example.com/integer-id"
},
{
title: "Nam Ullamcorper Blog Post",
content: "Nam ullamcorper semper lacus, ac hendrerit leo tempor nec. Nunc id urna a urna scelerisque viverra sit amet a felis.",
datePublished: "2024-02-20",
isFeatured: true,
wordCount: 1400,
readingTime: 6.5,
url: "https://example.com/nam-ullamcorper"
},
{
title: "Curabitur Eu Blog Post",
content: "Curabitur eu nisl sit amet nulla luctus cursus eu vel orci. Duis auctor justo vitae nibh rhoncus commodo.",
datePublished: "2024-02-21",
isFeatured: false,
wordCount: 1200,
readingTime: 5.5,
url: "https://example.com/curabitur-eu"
}
];
21 changes: 21 additions & 0 deletions packages/visualizations/src/components/Table/Body.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
import type { Column, TableData } from './types';
import Cell from './Cell.svelte';

function parseValue(value: unknown) {
return value === undefined || value === null ? '' : value;
}

export let columns: Column[];
export let records: TableData;
</script>

<tbody>
{#each records as record}
<tr>
{#each columns as column}
<Cell value={parseValue(record[column.key])} />
{/each}
</tr>
{/each}
</tbody>
5 changes: 5 additions & 0 deletions packages/visualizations/src/components/Table/Cell.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
export let value: unknown;
</script>

<td>{value}</td>
15 changes: 15 additions & 0 deletions packages/visualizations/src/components/Table/Header.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import type { Column } from './types';

export let columns: Column[];
</script>

<thead>
<tr>
{#each columns as column}
<th>
{column.title}
</th>
{/each}
</tr>
</thead>
103 changes: 103 additions & 0 deletions packages/visualizations/src/components/Table/Table.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script lang="ts">
import type { Async } from '../../types';
import type { TableData, TableOptions } from './types';

import { generateId } from '../utils';

import SourceLink from '../utils/SourceLink.svelte';
import Header from './Header.svelte';
import Body from './Body.svelte';
import parseTheme from './utils/theme';

export let data: Async<TableData>;
export let options: TableOptions;

const tableId = `table-${generateId()}`;

$: ({ value: records } = data);
// FIXME: Eslint is in conflict with prettier
// eslint-disable-next-line object-curly-newline
$: ({ columns, title, subtitle, description, source, theme } = options);

$: style = parseTheme(theme);
</script>

<div class="container" {style}>
<div class="header">
{#if title}
<h3>{title}</h3>
{/if}
{#if subtitle}
<p>{subtitle}</p>
{/if}
</div>
<div class="table-wrapper">
KevinFabre-ods marked this conversation as resolved.
Show resolved Hide resolved
<table aria-describedby={description ? tableId : undefined}>
<Header {columns} />
{#if records}
<Body {records} {columns} />
{/if}
</table>
</div>
{#if description}
<p id={tableId} class="a11y-invisible-description">{description}</p>
{/if}
{#if source}
<SourceLink {source} />
{/if}
</div>

<style>
.container {
--spacing-50: 6px;
--spacing-75: 9px;
--spacing-100: 13px;

/* FIXME: Only using flex style to center source link */
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.header {
margin-bottom: var(--spacing-100);
}
.header h3,
.header p {
margin: 0;
}
.table-wrapper {
border: solid 1px var(--border-color);
border-radius: var(--spacing-50);
overflow-x: auto;
max-width: 100%;
margin-bottom: var(--spacing-100);
}
table {
border-collapse: collapse;
color: var(--text-color);
white-space: nowrap;
}
:global(.container th),
:global(.container td) {
padding: var(--spacing-75);
font-weight: normal;
text-align: left;
}
:global(.container th) {
color: var(--header-text-color);
background-color: var(--header-background-color);
}
:global(.container thead) {
border-bottom: 2px solid var(--header-border-color);
}
:global(.container tbody tr:not(:last-child)) {
border-bottom: 1px solid var(--border-color);
}
:global(.container tbody tr:hover) {
background-color: var(--active-row-background-color);
KevinFabre-ods marked this conversation as resolved.
Show resolved Hide resolved
}
/* Suitable for elements that are used via aria-describedby or aria-labelledby */
.a11y-invisible-description {
display: none;
}
</style>
9 changes: 9 additions & 0 deletions packages/visualizations/src/components/Table/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import TableImpl from './Table.svelte';
import SvelteImpl from '../SvelteImpl';
import type { TableData, TableOptions } from './types';

export default class KpiCard extends SvelteImpl<TableData, TableOptions> {
protected getSvelteComponentClass(): typeof TableImpl {
return TableImpl;
}
}
30 changes: 30 additions & 0 deletions packages/visualizations/src/components/Table/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { DataFrame, Source } from '../types';

export type TableData = DataFrame;

export type Column = {
key: string;
title: string;
};

export type Theme = Partial<{
textColor: string;
borderColor: string;
dataRow: {
activeBackgroundColor: string;
};
header: {
textColor: string;
backgroundColor: string;
borderColor: string;
};
}>;

export type TableOptions = {
columns: Column[];
title?: string;
subtitle?: string;
description?: string;
source?: Source;
theme?: Theme;
};
Loading
Loading