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

Feature/charts package #1648

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/charts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Shoreline Charts

`shoreline-components` and `echarts` are peer dependencies of `shoreline-charts`

```sh
pnpm add @vtex/shoreline echarts @vtex/shoreline-charts
```
54 changes: 54 additions & 0 deletions packages/charts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@vtex/shoreline-charts",
"description": "Shoreline datavis library",
"version": "0.0.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"files": [
"dist"
],
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"engines": {
"node": ">=16"
},
"scripts": {
"prebuild": "rm -rf dist",
"dev": "tsup --watch",
"build": "tsup"
},
"repository": {
"directory": "packages/charts",
"type": "git",
"url": "git+https://github.com/vtex/shoreline.git"
},
"bugs": {
"url": "https://github.com/vtex/shoreline/issues"
},
"peerDependencies": {
"@vtex/shoreline": "1.x",
"echarts": "5.x",
"react": "18.x",
"react-dom": "18.x"
},
"devDependencies": {
"@types/lodash": "^4.17.4",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"@types/lodash": "^4.17.4",

"@vtex/shoreline": "workspace:*",
"echarts": "5.5.0"
},
"dependencies": {
"@vtex/shoreline-utils": "workspace:*",
"echarts-for-react": "^3.0.2",
"vitest-canvas-mock": "^0.3.3"
}
}
102 changes: 102 additions & 0 deletions packages/charts/src/chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
useRef,
useEffect,
useMemo,
forwardRef,
useImperativeHandle,
type ComponentPropsWithRef,
useCallback,
} from 'react'
import type { EChartsOption, SetOptionOpts } from 'echarts'
import ReactECharts from 'echarts-for-react'
import type * as echarts from 'echarts'

import { defaultTheme } from './theme/themes'
import type { ChartConfig } from './types/chart'
import { getChartOptions } from './utils/chart'
import { canUseDOM } from '@vtex/shoreline-utils'

/**
* Render a Shoreline Chart with echarts
* @see https://echarts.apache.org/en/index.html
*/
export const Chart = forwardRef<echarts.EChartsType | undefined, ChartProps>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this undefined?

Copy link
Author

@natanfernandes natanfernandes Jun 17, 2024

Choose a reason for hiding this comment

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

I was facing a type error with ref, on the useImperativeHandle and tried the undefined

Copy link
Contributor

Choose a reason for hiding this comment

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

What does this echarts.ECartsType return? Some HTMLDivElement?

Copy link
Author

Choose a reason for hiding this comment

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

It returns the Chart instance, the same as in chartRef.current.getEchartsInstance()

function Charts(props, ref) {
const {
option,
settings,
loading = false,
chartConfig,
style,
...otherProps
} = props

const chartRef = useRef<ReactECharts>(null)

useImperativeHandle(ref, () => {
if (chartRef.current) {
return chartRef.current.getEchartsInstance()
}
return undefined
})

const chartOptions: EChartsOption = useMemo(() => {
const { type, variant } = chartConfig
return getChartOptions(option, type, variant) || option
}, [option, chartConfig])

const handleResize = useCallback(() => {
if (chartRef.current) {
chartRef.current.getEchartsInstance().resize()
}
}, [])

useEffect(() => {
if (!canUseDOM) return

window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [handleResize, canUseDOM])

if (loading) return <div>loading...</div>

return (
<div data-sl-chart {...otherProps}>
<ReactECharts
ref={chartRef}
theme={defaultTheme}
option={chartOptions}
style={style}
opts={{
renderer: 'svg',
}}
/>
</div>
)
}
)

export interface ChartsOptions {
/**
* Echarts options
*/
option: EChartsOption
/**
* Echarts settings
*/
settings?: SetOptionOpts
/**
* Wether is loading
* @default false
*/
loading?: boolean
/**
* Configs containing type of chart and its variants, each variant is a pre-defined chart style for each type
* @default default
*/
chartConfig: ChartConfig
}

export type ChartProps = ChartsOptions & ComponentPropsWithRef<'div'>
2 changes: 2 additions & 0 deletions packages/charts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Chart } from './chart'
export type { ChartProps } from './chart'
56 changes: 56 additions & 0 deletions packages/charts/src/stories/bar-charts.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Chart } from '../index'

export default {
title: 'Charts/bar',
}

export function Basic() {
return (
<Chart
option={{
xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
series: { data: [1, 2, 3, 4, 5, 6, 7] },
}}
chartConfig={{ type: 'bar' }}
style={{ height: 550 }}
/>
)
}

export function Horizontal() {
return (
<Chart
option={{
xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
series: [
{ data: [1, 2, 3, 4, 5, 6, 7] },
{ data: [1, 4, 2, 1, 4, 3, 5] },
],
}}
chartConfig={{ type: 'bar', variant: 'horizontal' }}
style={{ height: 550 }}
/>
)
}

export function MultiType() {
return (
<Chart
option={{
xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
series: [
{ data: [1, 2, 3, 4, 5, 6, 7] },
{ data: [1, 4, 2, 1, 4, 3, 5], type: 'line' },
],
}}
chartConfig={{ type: 'bar', variant: 'default' }}
style={{ height: 550 }}
/>
)
}
8 changes: 8 additions & 0 deletions packages/charts/src/tests/__fixtures__/chartData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const BAR_CHART_DATA = {
xAxis: {
weekdays: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
series: {
dayNumbers: [1, 2, 3, 4, 5, 6, 7],
},
}
38 changes: 38 additions & 0 deletions packages/charts/src/tests/charts.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
describe,
expect,
test,
render,
waitFor,
screen,
} from '@vtex/shoreline-test-utils'
import { Chart } from '../chart'
import { BAR_CHART_DATA } from './__fixtures__/chartData'

describe('@vtex.shoreline-charts bar chart tests', () => {
test('renders the bar chart with correct data', async () => {
const { container } = render(
<Chart
option={{
xAxis: {
data: BAR_CHART_DATA.xAxis.weekdays,
},
series: { data: BAR_CHART_DATA.series.dayNumbers },
}}
chartConfig={{ type: 'bar' }}
style={{ width: '100%', height: '400px' }}
/>
)

const divChartContainer = container.querySelector('[data-sl-chart]')
await waitFor(() => expect(divChartContainer).toBeInTheDocument())

BAR_CHART_DATA.xAxis.weekdays.forEach((value) =>
waitFor(() => expect(screen.queryByText(value)).toBeInTheDocument())
)

BAR_CHART_DATA.series.dayNumbers.forEach((value) =>
waitFor(() => expect(screen.queryByText(value)).toBeInTheDocument())
)
})
})
20 changes: 20 additions & 0 deletions packages/charts/src/tests/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { vi } from 'vitest'
import 'vitest-canvas-mock'

vi.mock('echarts', async () => {
const echarts = await vi.importActual<typeof import('echarts')>('echarts')
return {
...echarts,
init: vi.fn(() => {
return {
setOption: vi.fn(),
resize: vi.fn(),
getOption: vi.fn(),
dispose: vi.fn(),
clear: vi.fn(),
on: vi.fn(),
off: vi.fn(),
}
}),
}
})
32 changes: 32 additions & 0 deletions packages/charts/src/theme/chartStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const CHART_STYLES: any = {
bar: {
default: {
xAxis: {
type: 'category',
},
yAxis: {
type: 'value',
},
series: {
type: 'bar',
},
},
horizontal: {
xAxis: {
type: 'value',
},
yAxis: {
type: 'category',
},
series: {
type: 'bar',
itemStyle: {
borderRadius: [0, 4, 4, 0],
},
},
},
},
line: {
default: {},
},
}
22 changes: 22 additions & 0 deletions packages/charts/src/theme/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const CATEGORICAL = {
primary: '#014592',
secondary: '#9C56F3',
tertiary: '#0D504D',
quaternary: '#CA226A',
quinary: '#F95D47',
senary: '#5C12B6',
septenary: '#08A822',
octonary: '#EF5997',
nonary: '#157BF4',
denary: '#B18D01',
undenary: '#013A5E',
duodenary: '#01A29B;',
ternary: '#B24D01',
fourteen: '#720000',
}
Comment on lines +1 to +16
Copy link
Contributor

@matheusps matheusps Jun 17, 2024

Choose a reason for hiding this comment

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

It looks to me that this naming could be simpler. For example: 100, 200, 300, 400, 500... OR 1, 2, 3, 4, 5... You would write less and semantic actually gets lost after quaternary, for example. What do you think?

Copy link
Author

Choose a reason for hiding this comment

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

Maybe we can achieve a better way to name it, for now i'm was following the token names defined in Figma

Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think about this naming, @beatrizmilhomem, @davicostalf ?


export const BASE = {
lineColor: '#ADADAD',
textSoft: '#707070',
bgLineColor: '#EBEBEB',
}
Loading