Skip to content

Commit

Permalink
Merge pull request #47 from brown-ccv/line-chart
Browse files Browse the repository at this point in the history
feat: single line chart
  • Loading branch information
mcmcgrath13 authored Dec 15, 2020
2 parents bcb58f6 + e422172 commit 66cdcce
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 1 deletion.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"deploy": "firebase deploy"
},
"dependencies": {
"@brown-ccv/disco-styles": "2.3.0",
"@brown-ccv/disco-styles": "2.4.0",
"@fortawesome/free-brands-svg-icons": "^5.14.0",
"@fortawesome/vue-fontawesome": "2",
"bulma": "^0.9.0",
Expand Down
89 changes: 89 additions & 0 deletions src/components/d-chart-line.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<script>
import vegaBaseMixin from '@/mixins/vega-base-mixin.js';
export default {
mixins: [vegaBaseMixin],
props: {
x: {
type: String,
required: true,
},
xLabel: {
type: String,
required: true,
},
y: {
type: String,
required: true,
},
yLabel: {
type: String,
required: true,
},
},
computed: {
baseSpec() {
return {
$schema: 'https://vega.github.io/schema/vega/v5.json',
description:
'A basic line chart example, with value labels shown upon mouse hover.',
height: this.height,
padding: 5,
data: [
{
name: 'data',
values: this.dataset,
},
],
scales: [
{
name: 'xscale',
type: 'linear',
domain: { data: 'data', field: this.x },
range: 'width',
round: true,
zero: false,
},
{
name: 'yscale',
type: 'linear',
domain: { data: 'data', field: this.y },
nice: true,
zero: false,
range: 'height',
},
],
axes: [
{
orient: 'bottom',
scale: 'xscale',
title: this.xLabel,
},
{ orient: 'left', scale: 'yscale', title: this.yLabel },
],
marks: [
{
type: 'line',
from: { data: 'data' },
encode: {
enter: {
x: { scale: 'xscale', field: this.x },
y: { scale: 'yscale', field: this.y },
strokeCap: { value: 'round' },
tooltip: {
signal: `{ '${this.xLabel}': datum.${this.x}, '${this.yLabel}': datum.${this.y} }`,
},
},
update: {
interpolate: 'linear',
defined: { signal: `isValid(datum.${this.y})` },
},
},
},
],
};
},
},
};
</script>
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export { default as DChartContainer } from './d-chart-container.vue';
export { default as DBaseDashboard } from './d-base-dashboard.vue';
export { default as DHeatMap } from './d-chart-heatmap.vue';
export { default as DMultiLineChart } from './d-chart-multiline.vue';
export { default as DLineChart } from './d-chart-line.vue';

export { default as discoBaseMixin } from '../mixins/disco-base-mixin.js';
export { default as vegaBaseMixin } from '../mixins/vega-base-mixin.js';
84 changes: 84 additions & 0 deletions src/stories/linechart.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Meta, ArgsTable, Story, Canvas } from '@storybook/addon-docs/blocks';
import DChartContainer from '../components/d-chart-container.vue';
import DLineChart from '../components/d-chart-line.vue';

<Meta
title="Visualization/Charts/Line Chart"
component={DLineChart}
/>


# Line Chart

A Line Chart shows a variables over x and y coordinates.

<ArgsTable story="Line Chart" />

## DLineChart component

```html
<DLineChart
id="linechart"
:dataset="[
{ month: 1, temp: 2, depth: 3},
{ month: 2, temp: 4, depth: 3.1},
{ month: 3, temp: 1, depth: 3.4},
]"
:min-width="400"
x="month"
x-label="Month"
y="depth"
y-label="Water Depth"
:include-actions="true"
/>
```

export const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { DChartContainer, DLineChart },
template: `
<DChartContainer width="full">
<template #title>Line Chart</template>
<template #description>
A line chart shows the temp of observations at each month.
</template
>
<template #chart>
<DLineChart
id="linechart"
:dataset="dataset"
:min-width="minWidth"
:height="height"
:x="x"
:x-label="xLabel"
:y="y"
:y-label="yLabel"
:include-actions="includeActions"
:spec-override="specOverride"
:enable-darkmode="enableDarkmode"
/>
</template>
</DChartContainer>
`
});

<Canvas>
<Story name="Line Chart" args={{
includeActions: true,
minWidth: 400,
height: 300,
x: 'month',
y: 'temp',
xLabel: 'Month',
yLabel: 'Temperature (C)',
dataset: [
{ month: 1, temp: 2, depth: 3},
{ month: 2, temp: 4, depth: 3.1},
{ month: 3, temp: 1, depth: 3.4},
],
specOverride: {axes: [{orient: 'top'}]},
enableDarkmode: true,
}}>
{Template.bind({})}
</Story>
</Canvas>
67 changes: 67 additions & 0 deletions tests/unit/d-chart-line.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { render, waitFor } from '@testing-library/vue';
import DLineChart from '@/components/d-chart-line.vue';
import '@testing-library/jest-dom';
import ResizeObserver from '../__mocks__/ResizeObserver'; // eslint-disable-line no-unused-vars

let props = {
id: 'line',
minWidth: 400,
height: 300,
x: 'month',
y: 'temp',
xLabel: 'Month',
yLabel: 'Temperature (C)',
dataset: [
{ month: 1, temp: 2, depth: 3 },
{ month: 2, temp: 4, depth: 3.1 },
{ month: 3, temp: 1, depth: 3.4 },
],
};

test('has id passed in props', async () => {
const { container } = render(DLineChart, { props });
const main = container.firstElementChild;
await waitFor(() =>
expect(main).toHaveAttribute('id', expect.stringContaining(props.id))
);
});

test('has vizualization rendered', async () => {
const { container, queryByText } = render(DLineChart, { props });
const main = container.firstElementChild;
await waitFor(() => expect(main).not.toBeEmptyDOMElement());

// spot check props are passing through
expect(queryByText(props.xLabel)).toBeInTheDocument();
expect(queryByText(props.yLabel)).toBeInTheDocument();

// actions are there
expect(main).toHaveClass('has-actions');
});

test('can not include actions', async () => {
const { container } = render(DLineChart, {
props: {
...props,
includeActions: false,
},
});
const main = container.firstElementChild;
await waitFor(() => expect(main).not.toBeEmptyDOMElement());

expect(main).not.toHaveClass('has-actions');
});

test('can override spec', async () => {
const { container, queryByText } = render(DLineChart, {
props: {
...props,
specOverride: { axes: [{ title: 'Something else' }] },
},
});
const main = container.firstElementChild;
await waitFor(() => expect(main).not.toBeEmptyDOMElement());

expect(queryByText('Something else')).toBeInTheDocument();
expect(queryByText(props.xLabel)).not.toBeInTheDocument();
});

0 comments on commit 66cdcce

Please sign in to comment.