Skip to content

Commit

Permalink
Merge pull request #181 from hatemhosny/releases/v0.2.0
Browse files Browse the repository at this point in the history
Prepare release v0.2.0
  • Loading branch information
hatemhosny authored Sep 1, 2024
2 parents d609cf7 + a9f07ee commit 959387d
Show file tree
Hide file tree
Showing 19 changed files with 137 additions and 115 deletions.
28 changes: 16 additions & 12 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
- **I'm submitting a ...**
[ ] bug report
[ ] feature request
[ ] question about the decisions made in the repository
[ ] question about how to use this project
**I'm submitting a ...**

- **Summary**
- [ ] bug report
- [ ] feature request
- [ ] question about the decisions made in the repository
- [ ] question about how to use this project

- **Link to minimal reproduction**
<!--
please provide a link to a minimal reproduction of the issue e.g. on https://livecodes.io
You may use this as a starting point: https://livecodes.io/?x=id/6sdjrf25i67
-->
**Summary**

- **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
**Link to minimal reproduction**

<!--
please provide a link to a minimal reproduction of the issue e.g. on https://livecodes.io
You may use this as a starting point: https://livecodes.io/?x=id/6sdjrf25i67
-->

**Other information**

<!-- (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.) -->
25 changes: 14 additions & 11 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
- **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
**What kind of change does this PR introduce?**

- **What is the current behavior?** (You can also link to an open issue here)
<!-- (Bug fix, feature, docs update, ...) -->

- **What is the new behavior (if this is a feature change)?**
**What is the current behavior?**

- **Issue related to this PR**
<!--
please make sure that these changes have been discussed and approved in an issue.
Otherwise, please do: https://github.com/hatemhosny/racing-bars/issues/new
-->
**What is the new behavior (if this is a feature change)?**

[ ] The changes have been discussed in an issue and the approach has been approved.
**Issue related to this PR**

issue:
<!--
please make sure that these changes have been discussed and approved in an issue.
Otherwise, please do: https://github.com/hatemhosny/racing-bars/issues/new
-->

- **Other information**:
- [ ] The changes have been discussed in an issue and the approach has been approved.

- issue link:

**Other information**:
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Happy linting! 💖
"ticker",
"utils",
"store",
"styles"
"styles",
"types"
]
}
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. See [standa

---

## [v0.2.0](https://github.com/hatemhosny/racing-bars/compare/v0.1.2...v0.2.0) (2024-09-01)

### Bug Fixes

- **data:** fix worker mixing data for multiple charts ([5160018](https://github.com/hatemhosny/racing-bars/commit/5160018b08c8a07683f8865cc9a194fac8cd2adb))
- **types:** fix library type definitions ([929f0af](https://github.com/hatemhosny/racing-bars/commit/929f0af452dacb28f341b47edabad3a0235507af))

### Features

- **data:** auto detect `dataShape` ([5ede0f7](https://github.com/hatemhosny/racing-bars/commit/5ede0f7a02a60d2d66e3ddd7dcca4a4be57423a5))
- **data:** handle empty or invalid data ([544c39d](https://github.com/hatemhosny/racing-bars/commit/544c39d56f5bf6e914f080e5d3c235e5b8455bfd))
- **options:** auto detect `dataType` ([9030de4](https://github.com/hatemhosny/racing-bars/commit/9030de44ce89fb4552aa8508983fb1e4f0570cad))

---

## [v0.1.2](https://github.com/hatemhosny/racing-bars/compare/v0.1.1...v0.1.2) (2024-08-30)

### Bug Fixes
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"serve": "live-server . --open=demo/ --watch=build,demo --cors --quiet",
"watch": "nodemon --watch src -e \"*\" ./scripts/build.js --dev",
"build": "run-s clean build:*",
"build:lib": "node ./scripts/build.js",
"build:ts": "tsc --emitDeclarationOnly --outFile build/racing-bars.d.ts && node ./scripts/patch-types.js",
"build:lib": "node ./scripts/build.js",
"build:ts-website": "cd website && npm run typecheck",
"build:website": "npm run copy:data && cd website && npm run build",
"copy:data": "recursive-copy data website/static/data",
Expand All @@ -42,7 +42,7 @@
"lint:stylelint-website": "stylelint \"website/**/*.{css,scss}\"",
"postinstall": "cd website && npm install",
"reset": "git clean -dfx && git reset --hard && npm i",
"clean": "recursive-delete build && recursive-delete tmp && recursive-delete website/build && recursive-delete website/static/data && recursive-delete website/docs/api",
"clean": "recursive-delete build && recursive-delete tmp && recursive-delete website/build && recursive-delete website/static/data && recursive-delete website/static/lib && recursive-delete website/docs/api",
"start-release": "node ./scripts/start-release.js"
},
"dependencies": {
Expand Down
6 changes: 5 additions & 1 deletion scripts/patch-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ const patchTypes = async () => {
.replace('declare module "index"', 'declare module "racing-bars"')
.replace('from "index"', 'from "racing-bars"')
.replace('declare module "react"', 'declare module "racing-bars/react"')
.replace('declare module "vue"', 'declare module "racing-bars/vue"');
.replace('declare module "vue"', 'declare module "racing-bars/vue"')
.replace(/declare module "lib\//g, 'declare module "racing-bars/lib/')
.replace(/from "lib\//g, 'from "racing-bars/lib/')
.replace(/declare module "shared\//g, 'declare module "racing-bars/shared/')
.replace(/from "shared\//g, 'from "racing-bars/shared/');
fs.writeFile(path.resolve(dtsPath), patched);
};

Expand Down
22 changes: 19 additions & 3 deletions src/lib/load-data.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import { json, csv, tsv, xml } from './d3';
import type { Data, WideData } from './data';
import type { Options, Data, WideData } from './index';

export function loadData(
url: string,
type: 'json' | 'csv' | 'tsv' | 'xml' = 'json',
type: Options['dataType'] = 'auto',
): Promise<Data[]> | Promise<WideData[]> {
const supportedTypes: Array<Exclude<Options['dataType'], 'auto'>> = ['json', 'csv', 'tsv', 'xml'];
const isSupported = (t: any) => supportedTypes.includes(t);

const detectDataType = () => {
const t = type.toLowerCase();
if (isSupported(t)) {
return t;
}
const extension = url.split('.').pop()?.toLowerCase() || '';
if (isSupported(extension)) {
return extension;
}
return 'json';
};

const handleError = () => {
throw new Error(`Failed to load data as ${type.toUpperCase()} from ${url}`);
};
switch (type) {

switch (detectDataType()) {
case 'json':
return json(url).catch(handleError) as Promise<Data[]> | Promise<WideData[]>;
case 'csv':
Expand Down
4 changes: 2 additions & 2 deletions src/lib/options/options.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export interface OptionsAction extends Action {
}

export interface Options {
dataShape: 'long' | 'wide';
dataType: 'json' | 'csv' | 'tsv' | 'xml';
dataShape: 'long' | 'wide' | 'auto';
dataType: 'json' | 'csv' | 'tsv' | 'xml' | 'auto';
dataTransform: null | ((data: Data[] | WideData[]) => Data[] | WideData[]);
fillDateGapsInterval: null | 'year' | 'month' | 'day';
fillDateGapsValue: 'last' | 'interpolate';
Expand Down
4 changes: 2 additions & 2 deletions src/lib/options/options.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type { Options, OptionsAction } from './options.models';
import { actionTypes } from './options.actions';

export const defaultOptions: Options = {
dataShape: 'long',
dataType: 'json',
dataShape: 'auto',
dataType: 'auto',
dataTransform: null,
fillDateGapsInterval: null,
fillDateGapsValue: 'interpolate',
Expand Down
20 changes: 10 additions & 10 deletions src/lib/utils/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { loadData } from '../load-data';
// eslint-disable-next-line import/no-internal-modules
import workerSrc from '../../../tmp/racing-bars.worker.js';
import { getDates, getNextDate } from './dates';
import { createWorkerFromContent } from './utils';
import { createWorkerFromContent, generateId } from './utils';

const worker = createWorkerFromContent(workerSrc);

Expand All @@ -22,22 +22,22 @@ export async function prepareData(
}
data = dataTransform(await data);
}
const messageId = generateId();
worker.postMessage({
type: 'prepare-data',
data,
options: removeFnOptions(store.getState().options),
baseUrl: location.href,
messageId,
});
const preparedData = await new Promise<Data[]>((resolve) => {
worker.addEventListener(
'message',
(event) => {
if (event.data.type === 'data-prepared') {
resolve(event.data.data);
}
},
{ once: true },
);
const onMessage = (event: MessageEvent) => {
if (event.data.type === 'data-prepared' && event.data.messageId === messageId) {
resolve(event.data.data);
worker.removeEventListener('message', onMessage);
}
};
worker.addEventListener('message', onMessage);
});
storeDataCollections(preparedData, store, changingOptions);
return preparedData;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { prepareData } from './prepare-data';
const worker: Worker = self as any as Worker;

worker.addEventListener('message', async (event) => {
const { type, data, options, baseUrl } = event.data;
const { type, data, options, baseUrl, messageId } = event.data;
if (type === 'prepare-data') {
const result = await prepareData(data, options, baseUrl);
worker.postMessage({ type: 'data-prepared', data: result });
worker.postMessage({ type: 'data-prepared', data: result, messageId });
}
});
22 changes: 20 additions & 2 deletions src/lib/worker/prepare-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function prepareData(
baseUrl: string,
): Promise<Data[]> {
return fetchData(data, options.dataType, baseUrl)
.then(handleEmptyData)
.then(filterByDate(options.startDate, options.endDate))
.then(wideDataToLong(options.dataShape))
.then(processFixedOrder(options.fixedOrder))
Expand All @@ -22,7 +23,7 @@ export function prepareData(

function fetchData(
data: Data[] | WideData[] | Promise<Data[]> | Promise<WideData[]> | string,
dataType: 'json' | 'csv' | 'tsv' | 'xml',
dataType: Options['dataType'],
baseUrl: string,
) {
if (typeof data === 'string') {
Expand All @@ -43,6 +44,12 @@ function isRelativeUrl(url: string) {
return !url.startsWith('https://') && !url.startsWith('http://') && !url.startsWith('data:');
}

function handleEmptyData(data: Data[] | WideData[]): Data[] | WideData[] {
return !Array.isArray(data) || data.length === 0
? [{ date: getDateString(new Date()), value: 0, name: '' }]
: data;
}

function filterByDate(startDate: string, endDate: string) {
return function (data: Data[] | WideData[]): Data[] | WideData[] {
return data
Expand Down Expand Up @@ -107,9 +114,20 @@ function calculateLastValues(makeCumulative = false) {
};
}

function detectDataShape(data: Data[] | WideData[], dataShape: Options['dataShape']) {
if (dataShape === 'long' || dataShape === 'wide') return dataShape;
const firstRow = data[0];
if ('date' in firstRow && 'name' in firstRow && 'value' in firstRow) {
return 'long';
}
return 'wide';
}

function wideDataToLong(dataShape: Options['dataShape'], nested = false) {
return function (data: WideData[]) {
if (dataShape === 'long') return data as Data[];
if (dataShape === 'long' || detectDataShape(data, dataShape) === 'long') {
return data as Data[];
}

const long = [] as Data[];
data.forEach((row) => {
Expand Down
2 changes: 1 addition & 1 deletion src/package.lib.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "racing-bars",
"version": "0.1.2",
"version": "0.2.0",
"description": "Bar chart race made easy 📶",
"author": "Hatem Hosny",
"license": "MIT",
Expand Down
26 changes: 14 additions & 12 deletions website/docs/documentation/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ race(data, '#race');
#### Load data from URL

Data can be loaded from a URL. The following formats are supported:
`json` (default), `csv`, `tsv` and `xml`.
`json`, `csv`, `tsv` and `xml`.

Data type is automatically detected by file extension, otherwise `json` is assumed.

- Load JSON from URL

Expand Down Expand Up @@ -216,7 +218,7 @@ date,Canada,Egypt,Greece,Panama,Singapore

### Usage

For wide data to be processed, the [`options`](./options.md) object should have the field [`dataShape`](./options.md#datashape) set to `"wide"`
For wide data to be processed, the [`options`](./options.md) object should have the field [`dataShape`](./options.md#datashape) set (or auto-detected) to `"wide"`

- JS array of objects

Expand Down Expand Up @@ -281,70 +283,70 @@ Example for long data with [optional fields](#long-data):
"date": "2017-01-01",
"name": "Egypt",
"value": 96.44,
"icon": "https://www.countryflags.io/eg/flat/64.png",
"icon": "https://flagsapi.com/EG/flat/64.png",
"group": "Africa"
},
{
"date": "2017-01-01",
"name": "Singapore",
"value": 5.61,
"icon": "https://www.countryflags.io/sg/flat/64.png",
"icon": "https://flagsapi.com/SG/flat/64.png",
"group": "Asia"
},
{
"date": "2017-01-01",
"name": "Greece",
"value": 10.75,
"icon": "https://www.countryflags.io/gr/flat/64.png",
"icon": "https://flagsapi.com/GR/flat/64.png",
"group": "Europe"
},
{
"date": "2017-01-01",
"name": "Panama",
"value": 4.11,
"icon": "https://www.countryflags.io/pa/flat/64.png",
"icon": "https://flagsapi.com/PA/flat/64.png",
"group": "North America"
},
{
"date": "2018-01-01",
"name": "Greece",
"value": 10.73,
"icon": "https://www.countryflags.io/gr/flat/64.png",
"icon": "https://flagsapi.com/GR/flat/64.png",
"group": "Europe"
},
{
"date": "2018-01-01",
"name": "Singapore",
"value": 5.64,
"icon": "https://www.countryflags.io/sg/flat/64.png",
"icon": "https://flagsapi.com/SG/flat/64.png",
"group": "Asia"
},
{
"date": "2018-01-01",
"name": "Canada",
"value": 37.06,
"icon": "https://www.countryflags.io/ca/flat/64.png",
"icon": "https://flagsapi.com/CA/flat/64.png",
"group": "North America"
},
{
"date": "2018-01-01",
"name": "Egypt",
"value": 98.42,
"icon": "https://www.countryflags.io/eg/flat/64.png",
"icon": "https://flagsapi.com/EG/flat/64.png",
"group": "Africa"
},
{
"date": "2017-01-01",
"name": "Canada",
"value": 36.54,
"icon": "https://www.countryflags.io/ca/flat/64.png",
"icon": "https://flagsapi.com/CA/flat/64.png",
"group": "North America"
},
{
"date": "2018-01-01",
"name": "Panama",
"value": 4.18,
"icon": "https://www.countryflags.io/pa/flat/64.png",
"icon": "https://flagsapi.com/PA/flat/64.png",
"group": "North America"
}
]
Expand Down
Loading

0 comments on commit 959387d

Please sign in to comment.