Skip to content
This repository has been archived by the owner on Jul 31, 2024. It is now read-only.

Commit

Permalink
FLOW-958 : @ollion/flow-dashboard (#205)
Browse files Browse the repository at this point in the history
* FLOW-958 flow-dashboard package added

* FLOW-958 f-dashboard story added

* FLOW-958 gridstack added

* FLOW-958 worker added

* FLOW-958 worker added

* FLOW-958 rebased

* FLOW-958 worker updated

* FLOW-958 timeseries chart added

* FLOW-958 tooltip possitioning logic updated

* FLOW-958 tooltip css updated

* FLOW-958 x and y lines feature added

* FLOW-958 multiline chart

* FLOW-958 code segregation

* FLOW-958 area chart added

* FLOW-958 bar area line chart series added

* FLOW-958 colors updated

* FLOW-958 colors updated

* FLOW-958 namespace updated from cldcvr to ollion

* FLOW-958 tooltip is now configurable

* FLOW-958 responsive behavior added for chart

* FLOW-958 tick config added for xAxis

* FLOW-958 yaxis tick config added

* FLOW-958 interval type ticks fixed

* FLOW-958 x-axis tick removal logic updated for small widths or when ticks are overlapping

* FLOW-958 legends added

* FLOW-958 Legend interaction updated

* FLOW-958 tested legend overflow with 50 series

* FLOW-958 legends directions added

* FLOW-958 legends template added

* FLOW-958 all options story added

* FLOW-958 sample tests added to pass lint checks

* FLOW-958 integration with f-dashboard

* FLOW-958 number widget dimentions updated

* FLOW-958 mock data utils updated

* FLOW-958 mock data util updated

* FLOW-958 realtime data story updated

* FLOW-958 widget header option added

* FLOW-958 footer option added for widget

* FLOW-958 tooltip sync feature

* FLOW-958 story updated to generate randon data

* FLOW-958 changelog updated

* FLOW-958 code duplication reduced
vikas-cldcvr authored Jan 31, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 8a613f4 commit 2a75c8b
Showing 31 changed files with 4,400 additions and 2,470 deletions.
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -52,6 +52,7 @@ jobs:
"@ollion/flow-core-config":"packages/flow-core-config/CHANGELOG.md",
"@ollion/flow-form-builder":"packages/flow-form-builder/CHANGELOG.md",
"@ollion/flow-lineage":"packages/flow-lineage/CHANGELOG.md",
"@ollion/flow-dashboard":"packages/flow-dashboard/CHANGELOG.md",
"@ollion/custom-elements-manifest-to-types":"packages/custom-elements-manifest-to-types/CHANGELOG.md"
}
3 changes: 2 additions & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -9,7 +9,8 @@ const alias = [
"flow-lineage",
"flow-log",
"flow-md-editor",
"flow-table"
"flow-table",
"flow-dashboard"
].map(pkg => ({
find: `@ollion/${pkg}`,
replacement: path.resolve(__dirname, "../packages", pkg, "src")
7 changes: 7 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -13,6 +13,8 @@ import "@ollion/flow-table";
import "@ollion/flow-md-editor";
import "@ollion/flow-form-builder";
import "@ollion/flow-lineage";
import "@ollion/flow-dashboard";

import { setCustomElementsManifest, setCustomElements } from "@storybook/web-components";
import { themes } from "@storybook/theming";
import { Preview } from "@storybook/web-components";
@@ -123,6 +125,9 @@ async function run() {
const tableCustomElements = await (
await fetch(new URL("../packages/flow-table/custom-elements.json", import.meta.url))
).json();
const dashboardCustomElements = await (
await fetch(new URL("../packages/flow-dashboard/custom-elements.json", import.meta.url))
).json();

const mdEditorCustomElements = await (
await fetch(new URL("../packages/flow-md-editor/custom-elements.json", import.meta.url))
@@ -140,6 +145,8 @@ async function run() {

setCustomElementsManifest(mdEditorCustomElements);
setCustomElements(mdEditorCustomElements);
setCustomElementsManifest(dashboardCustomElements);
setCustomElements(dashboardCustomElements);
}

run();
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@
"@changesets/cli": "^2.25.0",
"@ollion/custom-elements-manifest-to-types": "workspace:*",
"@ollion/prettier-config": "^2.1.0",
"@faker-js/faker": "^7.6.0",
"@faker-js/faker": "^8.3.1",
"@storybook/addon-actions": "^7.5.3",
"@storybook/addon-essentials": "^7.5.3",
"@storybook/addon-links": "^7.5.3",
@@ -66,7 +66,8 @@
"sass": "^1.52.3",
"storybook": "^7.5.3",
"typescript": "^5.2.2",
"vite": "^4.4.11"
"vite": "^4.4.11",
"@types/d3": "7.4.3"
},
"dependencies": {
"@ollion/flow-aws-icon": "latest",
@@ -77,11 +78,13 @@
"@ollion/flow-lineage": "workspace:*",
"@ollion/flow-log": "workspace:*",
"@ollion/flow-md-editor": "workspace:*",
"@ollion/flow-product-icon": "latest",
"@ollion/flow-product-icon": "1.14.0",
"@ollion/flow-system-icon": "latest",
"@ollion/flow-table": "workspace:*",
"@ollion/flow-dashboard": "workspace:*",
"jspdf": "^2.5.1",
"lit": "^3.1.0"
"lit": "^3.1.0",
"d3": "^7.6.1"
},
"loki": {
"configurations": {
3 changes: 2 additions & 1 deletion packages/flow-core/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Flow-core

![npm](https://badgen.net/npm/v/@ollion/flow-core) ![license](https://badgen.net/npm/license/@ollion/flow-core) ![types](https://badgen.net/npm/types/@ollion/flow-core) ![downloads](https://badgen.net//npm/dw/@ollion/flow-core) ![build](https://github.com/ollionorg/flow-core/actions/workflows/build.yml/badge.svg) ![release](https://github.com/ollionorg/flow-core/actions/workflows/release.yml/badge.svg) ![CodeQL](https://github.com/ollionorg/flow-core/workflows/CodeQL/badge.svg) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=ollion_flow-core&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=ollion_flow-core) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=ollion_flow-core&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=ollion_flow-core) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ollion_flow-core&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ollion_flow-core) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=ollion_flow-core&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=ollion_flow-core) [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=ollion_flow-core&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=ollion_flow-core) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=ollion_flow-core&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=ollion_flow-core) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=ollion_flow-core&metric=bugs)](https://sonarcloud.io/summary/new_code?id=ollion_flow-core)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-core%2Fpackage.json&query=%24.version&prefix=v&logo=npm&label=%40ollion%2Fflow-core) ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-form-builder%2Fpackage.json&query=%24.version&prefix=v&logo=npm&label=%40ollion%2Fflow-form-builder) ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-table%2Fpackage.json&query=%24.version&prefix=v&logo=npm&label=%40ollion%2Fflow-table) ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-lineage%2Fpackage.json&query=%24.version&prefix=v&logo=npm&label=%40ollion%2Fflow-lineage) ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-log%2Fpackage.json&query=%24.version&prefix=v&logo=npm&label=%40ollion%2Fflow-log) ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-code-editor%2Fpackage.json&query=%24.version&prefix=v&logo=npm&label=%40ollion%2Fflow-code-editor) ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-md-editor%2Fpackage.json&query=%24.version&prefix=v&logo=npm&label=%40ollion%2Fflow-md-editor)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Follionorg%2Fflow-core%2Fmain%2Fpackages%2Fflow-core%2Fpackage.json&query=%24.license&label=license) ![build](https://github.com/ollionorg/flow-core/actions/workflows/build.yml/badge.svg) ![release](https://github.com/ollionorg/flow-core/actions/workflows/release.yml/badge.svg) ![CodeQL](https://github.com/ollionorg/flow-core/workflows/CodeQL/badge.svg) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=cldcvr_flow-core&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=cldcvr_flow-core) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=cldcvr_flow-core&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=cldcvr_flow-core) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=cldcvr_flow-core&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=cldcvr_flow-core) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=cldcvr_flow-core&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=cldcvr_flow-core) [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=cldcvr_flow-core&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=cldcvr_flow-core) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=cldcvr_flow-core&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=cldcvr_flow-core) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=cldcvr_flow-core&metric=bugs)](https://sonarcloud.io/summary/new_code?id=cldcvr_flow-core)

Flow-core is the base library required by any flow dependecies. It consists mostly of building blocks such design + system tokens, atoms, molecules, base themes, etc.

8 changes: 8 additions & 0 deletions packages/flow-dashboard/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*
!dist/**/*.*
!shims.d.ts
!custom-elements.json
!html.html-data.json
!src/**/*.*
!README.md
!umd/**/*.*
13 changes: 13 additions & 0 deletions packages/flow-dashboard/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<h4 className="margin-btm-8">Release Notes</h4>

# Change Log

## [0.0.1] - 2023-11-03

### First Release

- `@ollion/flow-dashboard` released.

### Note

- Since this is the first release, we are testing it with various frameworks (thus, it is not production-ready)
21 changes: 21 additions & 0 deletions packages/flow-dashboard/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

Copyright (c) 2022 CloudCover Consultancy Pvt Ltd

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
60 changes: 60 additions & 0 deletions packages/flow-dashboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Flow Dashboard

The Flow dashboard is built on the Flow design framework ([website](https://flow.ollion.com/) / [github](https://github.com/ollionorg/flow-core))

# Installation

### 1️⃣ Install flow dashboard dependency

```
npm i --save @ollion/flow-dashboard
```

**Note:** after installation, re-start your application.

<br>

### 2️⃣ Import flow-dashboard into your project

Paste the below snippet in your project and add your application startup/runtime code to it.

```javascript
import "@ollion/flow-core";
import "@ollion/flow-dashboard";
```

<br>

### 3️⃣ For a typescript enabled project (optional)

**Note:** After adding, re-start your application. Make sure you are using version >4.5

**For Vue 3:**
Copy paste below import types in your `main.ts` file.

```Javascript
import "@ollion/flow-dashboard/dist/types/vue3";
```

<details>
<summary>For Vue 2</summary>

Copy paste below import types in your `main.ts` file.

```Javascript
import "@ollion/flow-dashboard/dist/types/vue2";
```

</details>

<details>
<summary>For React</summary>

**React**: Include react type in `tsconfig.json` file like below.

```json
"include": ["src", "./node_modules/@ollion/flow-dashboard/dist/types/react.ts"]
```

</details>
<br>
12 changes: 12 additions & 0 deletions packages/flow-dashboard/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

#!/bin/bash

HERE=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

cd "$HERE"

pnpm run analyze

echo "building library..."
pnpm vite build --emptyOutDir
pnpm vite build --emptyOutDir --config vite.umd.config.ts
67 changes: 67 additions & 0 deletions packages/flow-dashboard/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "@ollion/flow-dashboard",
"version": "0.0.1",
"description": "Dashboard as code",
"module": "dist/flow-dashboard.es.js",
"main": "dist/flow-dashboard.cjs.js",
"types": "dist/src/index.d.ts",
"scripts": {
"build": "bash ./compile.sh",
"build:watch": "concurrently --kill-others \"vite build --emptyOutDir --watch\" \"tsc --watch\"",
"analyze": "cem analyze --litelement --globs \"src/**/*.ts\" && wca analyze src --format vscode --outFile html.html-data.json",
"analyze:watch": "cem analyze --litelement --globs \"src/**/*.ts\" --watch",
"test": "web-test-runner ./src/**/*.test.ts --node-resolve --port 8095",
"test:file": "pnpm run build && web-test-runner --node-resolve --port 8095",
"test:watch": "pnpm run build && web-test-runner ./src/**/*.test.ts --node-resolve --watch --port 8095"
},
"keywords": [
"web-components",
"lit-element",
"typescript",
"lit"
],
"dependencies": {
"@ollion/flow-core": "workspace:*",
"@ollion/flow-core-config": "workspace:*",
"axios": "^0.27.2",
"d3": "^7.6.1",
"gridstack": "^9.5.0",
"lit": "^3.1.0",
"rxjs": "^7.8.1"
},
"peerDependencies": {
"@ollion/flow-core": "^*",
"@ollion/flow-core-config": "^*"
},
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.5.7",
"@open-wc/testing": "^3.1.5",
"@types/d3": "7.4.3",
"@types/jest": "29.5.5",
"@web/dev-server-esbuild": "^0.4.1",
"@web/test-runner": "^0.17.1",
"concurrently": "^8.2.1",
"esbuild-sass-plugin": "2.2.6",
"lit-html": "^3.1.0",
"sass": "^1.52.3",
"typescript": "^5.2.2",
"vite": "^4.4.11",
"web-component-analyzer": "^2.0.0-next.4"
},
"repository": {
"type": "git",
"url": "https://github.com/ollionorg/flow-core.git",
"directory": "packages/flow-dashboard"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"customElements": "custom-elements.json",
"bugs": {
"url": "https://github.com/ollionorg/flow-core/issues"
},
"homepage": "https://github.com/ollionorg/flow-core/packages/flow-dashboard#readme",
"author": "@ollion",
"license": "MIT"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@import "../../../../flow-core/src/mixins/scss/mixins";

@import "gridstack/dist/gridstack.min.css";

f-dashboard {
@include base();
height: 100%;
width: 100%;

display: flex;
overflow: auto;
.grid-stack {
width: 100%;
.grid-stack-item {
.grid-stack-item-content {
background: var(--color-surface-secondary);
display: flex;
flex-direction: column;
f-timeseries-chart {
flex: 1 0 100%;
}
> f-div[height="fill-container"] {
flex: 1 1;
max-height: 100%;
}
> f-div[width="fill-container"] {
width: 100%;
}
}
}
}
}

f-div[direction="column"] {
> f-dashboard {
flex: 1 1;
max-height: 100%;
}
}

f-div[direction="row"] {
> f-dashboard {
flex: 1 1;
max-width: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { html, nothing } from "lit";
import { FDashboardWidget } from "../../types";

export function getWidgetHeader(widget: FDashboardWidget) {
if (widget.header) {
if (typeof widget.header === "object") {
return html`<f-div
align="middle-left"
height="hug-content"
padding="medium"
direction="column"
border="small solid subtle bottom"
>
<f-text ellipsis .tooltip=${widget.header.title} variant="heading" weight="medium"
>${widget.header.title}</f-text
>
<f-text ellipsis state="secondary" .tooltip=${widget.header.description} size="small"
>${widget.header.description}</f-text
>
</f-div>`;
} else if (typeof widget.header === "function") {
return widget.header();
}
}

return nothing;
}

export function getWidgetFooter(widget: FDashboardWidget) {
if (widget.footer) {
if (typeof widget.footer === "string") {
return html`<f-div
align="middle-left"
height="hug-content"
padding="medium"
direction="column"
border="small solid subtle top"
>
<f-text state="subtle" size="small">${widget.footer}</f-text>
</f-div>`;
} else if (typeof widget.footer === "function") {
return widget.footer();
}
}

return nothing;
}

export function renderWidget(widget: FDashboardWidget) {
switch (widget.type) {
case "big-number":
return html`<f-div padding="medium" class="big-number">${widget.data.toFixed(2)}</f-div>`;

case "timeseries":
return html`<f-div padding="medium"
><f-timeseries-chart .config=${widget.data}></f-timeseries-chart
></f-div>`;

default:
return nothing;
}
}
108 changes: 108 additions & 0 deletions packages/flow-dashboard/src/components/f-dashboard/f-dashboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { CSSResult, html, PropertyValueMap, unsafeCSS } from "lit";
import { property } from "lit/decorators.js";
import { FRoot, flowElement } from "@ollion/flow-core";
import globalStyle from "./f-dashboard-global.scss?inline";
import { injectCss } from "@ollion/flow-core-config";
import { GridStack } from "gridstack";
import { FDashboardConfig } from "../../types";
import { getWidgetHeader, renderWidget, getWidgetFooter } from "./f-dashboard-utils";
import { createRef, Ref, ref } from "lit/directives/ref.js";

import { keyed } from "lit/directives/keyed.js";

injectCss("f-dashboard", globalStyle);

// const pollingWorker = new Worker(new URL("./polling-worker.ts", import.meta.url));
@flowElement("f-dashboard")
export class FDashboard extends FRoot {
/**
* css loaded from scss file
*/
static styles: CSSResult[] = [unsafeCSS(globalStyle)];

/**
* @attribute comments baout title
*/
@property({ type: Object })
config!: FDashboardConfig;

/**
* mention required fields here for generating vue types
*/
readonly required = ["config"];

gridStack?: GridStack;

gridStackElement: Ref<HTMLDivElement> = createRef<HTMLDivElement>();

createRenderRoot() {
return this;
}
protected willUpdate(
_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>
): void {
if (this.gridStack) {
// destroy existing grid
this.gridStack.destroy(false);
}
}

render() {
return html`
<div class="grid-stack" ${ref(this.gridStackElement)}>
${this.config.widgets.map(wgt => {
return keyed(
wgt.id,
html`<div
id="${wgt.id}"
class="grid-stack-item"
gs-w="${wgt.placement.w}"
gs-h="${wgt.placement.h}"
>
<div class="grid-stack-item-content">
${getWidgetHeader(wgt)}${renderWidget(wgt)}${getWidgetFooter(wgt)}
</div>
</div>`
);
})}
</div>
`;
}

protected updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
super.updated(changedProperties);
try {
this.gridStack = GridStack.init({ margin: "4px" });

this.updateWidgetFontSize();
this.gridStack.on("resizecontent", () => {
this.updateWidgetFontSize();
});
} catch (er) {
//ignore girdstack error for now
}
}

updateWidgetFontSize() {
this.querySelectorAll<HTMLDivElement>(".grid-stack-item-content > .big-number").forEach(
widgetContainer => {
const wHeight = widgetContainer.offsetHeight;
const wWidth = widgetContainer.offsetWidth;
const fontSize = Math.min(wHeight, wWidth) * 0.3 + "px";
widgetContainer.style.fontSize = fontSize;
widgetContainer.style.display = "flex";
widgetContainer.style.alignItems = "center";
widgetContainer.style.justifyContent = "center";
}
);
}
}

/**
* Required for typescript
*/
declare global {
export interface HTMLElementTagNameMap {
"f-dashboard": FDashboard;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
@import "../../../../flow-core/src/mixins/scss/mixins";
f-timeseries-chart {
@include base();
display: flex;
flex-direction: column;
min-height: 100px;

svg {
.domain {
stroke: var(--color-border-secondary);
}
.tick {
line {
stroke: var(--color-border-secondary);
&.grid-line {
stroke: var(--color-border-subtle);
}
}
text {
stroke: var(--color-text-secondary);
}
}
.tooltip-line {
stroke: var(--color-border-default);
stroke-dasharray: 6;
}
.tooltip-point {
fill: var(--color-primary-default);
}
.area-path,
.bars {
fill-opacity: 0.5;
}
}
.f-chart-tooltip {
position: fixed;
pointer-events: none;
z-index: 1;
&.hide {
display: none;
}
&.show {
display: flex;
}
transition:
left 0.2s linear,
top 0.2s linear,
bottom 0.2s linear,
right 0.2s linear;
}

.disable-legend {
opacity: 0.4;
}

.series-path,
.custom-lines {
transition:
opacity 0.3s linear,
stroke-width 0.3s linear;
}
.series-path.disable,
.custom-lines.disable {
opacity: 0.5;
fill-opacity: 0.5;
.bars {
fill-opacity: 0.3;
}
}
.series-path.active,
.custom-lines.active {
opacity: 1;
fill-opacity: 1 !important;
stroke-opacity: 1;
.bars {
fill-opacity: 1;
}
&.line-path {
stroke-width: 2pt !important;
}
}

.f-timeseries-wrapper {
&[data-legends-position="top"] {
flex-direction: column-reverse;
}
&[data-legends-position="left"] {
flex-direction: row-reverse;
}
&[data-legends-position="right"] {
flex-direction: row;
}
}
}

f-div[direction="column"] {
> f-timeseries-chart {
flex: 1 1;
max-height: 100%;
width: 100%;
}
}

f-div[direction="row"] {
> f-timeseries-chart {
flex: 1 1;
max-width: 100%;
height: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { HTMLTemplateResult } from "lit";

export type AxisLine = {
value: number;
color: string;
};

export type YAxisLine = AxisLine;
export type XAxisLine = AxisLine;
export type TimeseriesPoint = {
date: number;
value: number;
};

export type SeriesType = "line" | "bar" | "area";
export type TimeseriesData = {
seriesName: string;
points: TimeseriesPoint[];
color: string;
type: SeriesType;
disable?: boolean;
};

export type FTimeseriesTickAuto = {
type: "auto";
};

export type TickInterval = {
type: "milliseconds" | "seconds" | "minutes" | "hours" | "days" | "months" | "years";
every: number;
};

export type FTimeseriesXTickInterval = {
type: "interval";
interval: TickInterval;
};

export type FTimeseriesXTickValues = {
type: "values";
values: Date[];
};

export type FTimeseriesYTickValues = {
type: "values";
values: number[];
};

export type FTimeseriesXTickConfig = {
format?: (tickDate: Date) => string;
} & (FTimeseriesTickAuto | FTimeseriesXTickInterval | FTimeseriesXTickValues);

export type FTimeseriesYTickConfig = {
format?: (value: number) => string;
} & (FTimeseriesTickAuto | FTimeseriesYTickValues);

export type FTimeseriesLegendTemplate = (
interactions: FTimeseriesLegendInteraction
) => HTMLTemplateResult;
export type FTimeseriesLegendInteraction = {
click: (seriesName: string) => void;
mouseLeave: () => void;
mouseEnter: (seriesName: string) => void;
};

export type FTimeseriesChartConfig = {
data: TimeseriesData[];
size?: {
width?: number;
height?: number;
margin?: {
top?: number;
right?: number;
left?: number;
bottom?: number;
};
};
xAxis?: {
lines?: XAxisLine[];
tickConfig?: FTimeseriesXTickConfig;
};
yAxis?: {
lines?: YAxisLine[];
tickConfig?: FTimeseriesYTickConfig;
};
legends?: {
disabled?: boolean;
position?: "bottom" | "left" | "right" | "top";
template?: FTimeseriesLegendTemplate;
};
tooltipTemplate?: (tooltipDate: Date, tooltipPoints: TooltipPoints) => HTMLTemplateResult;
};

export type TooltipPoints = {
seriesName: string;
value: number;
color: string;
type: SeriesType;
date: number;
}[];
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { TickInterval, TooltipPoints } from "./f-timeseries-chart-types";
import * as d3 from "d3";
import { html } from "lit";
import { Subject } from "rxjs";

export const TOOLTIP_SYNC = new Subject<number>();

export function getTickInterval({ type, every }: TickInterval) {
return d3.timeInterval(
(_date: Date) => {},
(date: Date, _step: number) => {
switch (type) {
case "milliseconds":
date.setMilliseconds(date.getMilliseconds() + every);
break;
case "seconds":
date.setSeconds(date.getSeconds() + every);
break;
case "minutes":
date.setMinutes(date.getMinutes() + every);
break;
case "hours":
date.setHours(date.getHours() + every);
break;
case "days":
date.setDate(date.getDate() + every);
break;
case "months":
date.setMonth(date.getMonth() + every);
break;
case "years":
date.setFullYear(date.getFullYear() + every);
}
}
);
}

export function defaultTooltipTemplate(tooltipDate: Date, tooltipPoints: TooltipPoints) {
return html`<f-div width="100%" direction="column" gap="small">
<f-text>Date : ${tooltipDate.toLocaleDateString()} ${tooltipDate.toLocaleTimeString()}</f-text>
${tooltipPoints.map(point => {
return html`<f-text
>${point.seriesName} :
<f-text inline weight="bold" .state=${"custom," + point.color}
>${point?.value}</f-text
></f-text
>`;
})}
</f-div>`;
}

export function escapeSeriesName(name: string) {
return name.replace(/[^a-zA-Z0-9]/g, "_");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect } from "@open-wc/testing";

// IconPack to test
import IconPack from "@ollion/flow-system-icon/dist/types/icon-pack";
// import flow-core elements
import "@ollion/flow-core";
import "@ollion/flow-dashboard";
import { ConfigUtil } from "@ollion/flow-core";
import { FTimeseriesChart } from "@ollion/flow-dashboard";

ConfigUtil.setConfig({ iconPack: IconPack });

describe("f-timeseries-chart", () => {
it("is defined", () => {
const el = document.createElement("f-timeseries-chart");
expect(el).instanceOf(FTimeseriesChart);
});
});

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions packages/flow-dashboard/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./components/f-dashboard/f-dashboard";
export * from "./components/f-timeseries-chart/f-timeseries-chart";
export * from "./types";
export * from "./components/f-timeseries-chart/f-timeseries-chart-types";
36 changes: 36 additions & 0 deletions packages/flow-dashboard/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { HTMLTemplateResult } from "lit";
import { FTimeseriesChartConfig } from "./components/f-timeseries-chart/f-timeseries-chart-types";

export type FDashboardConfig = {
widgets: FDashboardWidget[];
};

export type FDashboardWidgetGridPlacement = {
h: number;
w: number;
x?: number;
y?: number;
};

export type FDashboardWidgetCore<T> = {
id: string;
placement: FDashboardWidgetGridPlacement;
data: T;
header?:
| {
title: string;
description?: string;
}
| (() => HTMLTemplateResult);
footer?: string | (() => HTMLTemplateResult);
};

export type FDashboardBigNumberWidget = FDashboardWidgetCore<number> & {
type: "big-number";
dataType?: "storage" | "time" | "count" | "currency" | "percentage";
};
export type FDashboardWidget = FDashboardBigNumberWidget | FDashboardTimeseriesWidget;

export type FDashboardTimeseriesWidget = FDashboardWidgetCore<FTimeseriesChartConfig> & {
type: "timeseries";
};
7 changes: 7 additions & 0 deletions packages/flow-dashboard/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": ["src/**/*"]
}
27 changes: 27 additions & 0 deletions packages/flow-dashboard/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { defineConfig } from "vite";

export default defineConfig({
build: {
// Disabling minification makes it easy to debug during development
// And all modern bundlers will consume the library and minify it anyway
minify: false,
sourcemap: true,
lib: {
entry: "src/index.ts",
name: "flow-dashboard",
fileName: format => `flow-dashboard.${format}.js`,
formats: ["es", "cjs"]
},
rollupOptions: {
// If we want to publish standalone components we don't externalize lit,
// if you are going to use lit in your own project, you can make it a dep instead.
// external: /^lit/, <-- comment this line
external: ["@ollion/flow-core-config", "@ollion/flow-core", /^lit/],
output: {
globals: {
"@ollion/flow-core": "@ollion/flow-core"
}
}
}
}
});
17 changes: 17 additions & 0 deletions packages/flow-dashboard/vite.umd.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineConfig } from "vite";

/**
* UMD build with no externals , to consume through CDN in static html files.
*/
export default defineConfig({
build: {
sourcemap: true,
lib: {
entry: "src/index.ts",
name: "flowDashboard",
fileName: format => `flow-dashboard.${format}.js`,
formats: ["umd"]
},
outDir: "umd"
}
});
5 changes: 5 additions & 0 deletions packages/flow-dashboard/web-test-runner.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { esbuildPlugin } from "@web/dev-server-esbuild";

export default {
plugins: [esbuildPlugin({ ts: true })]
};
4,741 changes: 2,286 additions & 2,455 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

134 changes: 134 additions & 0 deletions stories/flow-dashboard/f-dashboard.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Meta } from "@storybook/web-components";
import { html } from "lit-html";
import {
FDashboard,
FDashboardConfig,
FDashboardWidget,
FTimeseriesChartConfig
} from "@ollion/flow-dashboard";
import { generateTimeseriesChartData } from "./mock-data-utils";
import { faker } from "@faker-js/faker";
import { createRef, ref } from "lit/directives/ref.js";

export default {
title: "@ollion/flow-dashboard/f-dashboard",
argTypes: {
field: {
control: false
}
}
} as Meta<any>;

const getWidgets = () => {
const iconsNames = [
"p-azure",
"p-google",
"p-aws",
"p-hadoop",
"p-sonarcloud",
"p-snowflake",
"p-terraform",
"p-discord"
];
const widgets: FDashboardWidget[] = [];
const startFrom = new Date();
for (let index = 0; index < 10; index++) {
if (index % 2 === 0) {
widgets.push({
type: "timeseries",
data: {
data: generateTimeseriesChartData(startFrom)
},
id: faker.string.alpha(10),
header() {
const name = faker.company.name();
const description = faker.lorem.sentences(3);
return html`<f-div
align="middle-left"
height="hug-content"
padding="medium"
gap="medium"
border="small solid subtle bottom"
>
<f-icon .source=${faker.helpers.arrayElement(iconsNames)} size="large"></f-icon>
<f-div direction="column" align="middle-left">
<f-text ellipsis .tooltip=${name} variant="heading" weight="medium">${name}</f-text>
<f-text ellipsis .tooltip=${description} size="small">${description}</f-text>
</f-div>
</f-div>`;
},
footer: () => {
const date = faker.date.recent({ refDate: new Date() });
const state = faker.helpers.arrayElement(["danger", "success", "warning"]);
return html`<f-div
padding="medium"
gap="auto"
border="small solid subtle top"
height="hug-content"
>
<f-div gap="small" align="middle-left">
<f-icon source="i-clock-outline" size="small" .state=${state}></f-icon>
<f-text .state=${state} size="small"
>Last updated on ${date.toLocaleDateString()} ${date.toLocaleTimeString()}</f-text
>
</f-div>
<f-button label="view details" size="x-small" icon-right="i-new-tab"></f-button>
</f-div>`;
},
placement: {
w: faker.number.int({ min: 4, max: 8 }),
h: faker.number.int({ min: 3, max: 4 })
}
});
} else {
widgets.push({
type: "big-number",
data: faker.number.int({ min: 11, max: 999 }),
dataType: "count",
id: faker.string.alpha(10),
header: {
title: faker.company.name(),
description: faker.lorem.sentences(3)
},
footer: `Powered by Flow`,
placement: {
w: faker.number.int({ min: 1.5, max: 3 }),
h: faker.number.int({ min: 1.5, max: 2 })
}
});
}
}

return widgets;
};
const Template = () => {
const dashboardRef = createRef<FDashboard>();
const dashboardConfig: FDashboardConfig = {
widgets: getWidgets()
};
const randomize = () => {
if (dashboardRef.value) {
dashboardRef.value.config = {
widgets: getWidgets()
};
}
};

return html`<f-div height="100%" width="100%" gap="small" direction="column">
<f-div
variant="curved"
state="primary"
height="hug-content"
padding="medium"
gap="auto"
align="middle-left"
>
<f-text state="inherit">Click on randomize button to generate new data</f-text>
<f-button @click=${randomize} label="randomize"></f-button>
</f-div>
<f-dashboard ${ref(dashboardRef)} .config=${dashboardConfig}> </f-dashboard>
</f-div>`;
};

export const basic = Template.bind({});
395 changes: 395 additions & 0 deletions stories/flow-dashboard/f-timeseries-chart.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,395 @@
import { faker } from "@faker-js/faker";
import {
FTimeseriesChartConfig,
FTimeseriesChart,
YAxisLine,
XAxisLine,
TooltipPoints,
TimeseriesData
} from "@ollion/flow-dashboard";
import { html } from "lit-html";
import { createRef, ref } from "lit/directives/ref.js";
import { generateTimeseriesChartData } from "./mock-data-utils";

export default {
title: "@ollion/flow-dashboard/f-timeseries-chart",

parameters: {
controls: {
hideNoControlsWarning: true
}
}
};

function getXYLines(chartData: TimeseriesData[]) {
const yLines: YAxisLine[] = [
{
value: faker.number.int({ min: 150, max: 200 }),
color: "var(--color-danger-default)"
},
{
value: faker.number.int({ min: 50, max: 100 }),
color: "var(--color-warning-default)"
}
];

const xLines: XAxisLine[] = [
{
value: chartData[0].points[+(chartData[0].points.length / 3).toFixed(0)].date,
color: "yellow"
},
{
value: chartData[0].points[+(chartData[0].points.length / 2).toFixed(0)].date,
color: "yellow"
}
];

return {
xLines,
yLines
};
}

export const AllOptions = {
render: () => {
const chartData = generateTimeseriesChartData(new Date());
const { xLines, yLines } = getXYLines(chartData);

const chartRef = createRef<FTimeseriesChart>();

const customTickValues = [];
for (let d = 0; d < 10; d++) {
const date = new Date();
date.setMinutes(date.getMinutes() + 15 * (d + 1));
customTickValues.push(date);
}
const chartConfig: FTimeseriesChartConfig = {
data: chartData,
xAxis: {
lines: xLines,
tickConfig: {
format: (d: Date) => {
return `${d.getHours()}h${d.getMinutes()}m`;
},
// type: "auto"
type: "interval",
interval: {
type: "minutes",
every: 5
}
// type: "values",
// values: customTickValues
}
},
yAxis: {
lines: yLines,
tickConfig: {
format: (value: number) => {
return `#${value}`;
},
type: "auto"
// type: "values",
// values: [50, 100]
}
},
legends: {
disabled: false,
position: "bottom",
template: ({ click, mouseEnter, mouseLeave }) => {
const icons = ["i-area", "i-bar", "i-line"];
return html`<f-div height="hug-content" gap="medium" align="middle-center">
${chartData.map((series, idx) => {
return html`<f-div
.id=${"legend-" + series.seriesName}
class="timeseries-legend"
width="hug-content"
height="hug-content"
@click=${() => click(series.seriesName)}
@mouseenter=${() => mouseEnter(series.seriesName)}
@mouseleave=${() => mouseLeave()}
clickable
align="middle-left"
gap="small"
>
<f-icon-button
.state=${"custom," + series.color}
category="packed"
size="medium"
.icon=${icons[idx]}
></f-icon-button>
<f-text>${series.seriesName}</f-text></f-div
>`;
})}
</f-div>`;
}
},
tooltipTemplate: (tooltipDate: Date, tooltipPoints: TooltipPoints) => {
return html`<f-div
width="280px"
max-height="500px"
overflow="scroll"
direction="column"
gap="small"
>
${tooltipPoints.map(point => {
return html`<f-text weight="medium" .state=${"custom," + point.color}
>${point.seriesName} : ${point?.value}</f-text
>`;
})}
<f-divider state="subtle"></f-divider>
<f-div align="middle-right" gap="x-small">
<f-text inline variant="code" size="small">
${tooltipDate.toLocaleDateString()} | ${tooltipDate.toLocaleTimeString()}</f-text
>
</f-div>
</f-div>`;
}
};

const interval = setInterval(() => {
const chartDataFlat = chartData.map(series => series.points).flat();
const newPoints = generateTimeseriesChartData(
new Date(chartDataFlat[chartDataFlat.length - 1].date + 60 * 1000),
chartData.length,
1
);
newPoints.forEach((element, idx) => {
const series = chartData[idx];
series?.points.shift();
series?.points.push(...element.points);
});
if (chartRef.value) {
chartRef.value.config = { ...chartConfig };
}
}, 1000);

setTimeout(() => {
clearInterval(interval);
}, 3000);
return html`<f-div height="500px">
<f-timeseries-chart ${ref(chartRef)} .config=${chartConfig}></f-timeseries-chart>
</f-div>`;
},

name: "all-options"
};

export const CustomLegendTemplate = {
render: () => {
const chartData = generateTimeseriesChartData(new Date());

const chartRef = createRef<FTimeseriesChart>();

const chartConfig: FTimeseriesChartConfig = {
data: chartData,
xAxis: {
tickConfig: {
format: (d: Date) => {
return `${d.getHours()}h${d.getMinutes()}m`;
},
// type: "auto"
type: "interval",
interval: {
type: "minutes",
every: 5
}
// type: "values",
// values: customTickValues
}
},
yAxis: {
tickConfig: {
format: (value: number) => {
return `#${value}`;
},
type: "auto"
// type: "values",
// values: [50, 100]
}
},
legends: {
disabled: false,
position: "bottom",
template: ({ click, mouseEnter, mouseLeave }) => {
const icons = ["i-user", "i-home", "i-computer"];
return html`<f-div height="hug-content" align="middle-center">
${chartData.map((series, idx) => {
return html`<f-div
.id=${"legend-" + series.seriesName}
class="timeseries-legend"
width="hug-content"
height="hug-content"
padding="small medium"
@click=${() => click(series.seriesName)}
@mouseenter=${() => mouseEnter(series.seriesName)}
@mouseleave=${() => mouseLeave()}
clickable
align="middle-left"
gap="small"
>
<f-icon-button
.state=${"custom," + series.color}
category="packed"
size="small"
.icon=${icons[idx]}
></f-icon-button>
<f-text .state=${"custom," + series.color}>${series.seriesName}</f-text></f-div
>`;
})}
</f-div>`;
}
}
};
return html`<f-div height="500px">
<f-timeseries-chart ${ref(chartRef)} .config=${chartConfig}></f-timeseries-chart>
</f-div>`;
},

name: "legends-template"
};

export const Lines = {
render: () => {
const chartData = generateTimeseriesChartData(new Date());

const { xLines, yLines } = getXYLines(chartData);

const chartRef = createRef<FTimeseriesChart>();

const chartConfig: FTimeseriesChartConfig = {
data: chartData,
xAxis: {
lines: xLines
},
yAxis: {
lines: yLines
}
};

return html`<f-div height="500px">
<f-timeseries-chart ${ref(chartRef)} .config=${chartConfig}></f-timeseries-chart>
</f-div>`;
},

name: "x-&-y-lines"
};

export const CustomTooltip = {
render: () => {
const chartData = generateTimeseriesChartData(new Date());

const chartRef = createRef<FTimeseriesChart>();

const chartConfig: FTimeseriesChartConfig = {
data: chartData,

tooltipTemplate: (tooltipDate: Date, tooltipPoints: TooltipPoints) => {
return html`<f-div
width="280px"
max-height="500px"
overflow="scroll"
direction="column"
gap="small"
>
${tooltipPoints.map(point => {
return html`<f-text weight="medium" .state=${"custom," + point.color}
>${point.seriesName} : ${point?.value}</f-text
>`;
})}
<f-divider state="subtle"></f-divider>
<f-div align="middle-right" gap="x-small">
<f-text inline variant="code" size="small">
${tooltipDate.toLocaleDateString()} | ${tooltipDate.toLocaleTimeString()}</f-text
>
</f-div>
</f-div>`;
}
};

return html`<f-div height="500px">
<f-timeseries-chart ${ref(chartRef)} .config=${chartConfig}></f-timeseries-chart>
</f-div>`;
},

name: "tooltip-template"
};

export const TickFormat = {
render: () => {
const chartData = generateTimeseriesChartData(new Date());

const chartRef = createRef<FTimeseriesChart>();

const chartConfig: FTimeseriesChartConfig = {
data: chartData,
xAxis: {
tickConfig: {
format: (d: Date) => {
return `${d.getHours()}h${d.getMinutes()}m`;
},
type: "auto"
}
},
yAxis: {
tickConfig: {
format: (value: number) => {
return `#${value}`;
},
type: "auto"
}
}
};

return html`<f-div height="500px">
<f-timeseries-chart ${ref(chartRef)} .config=${chartConfig}></f-timeseries-chart>
</f-div>`;
},

name: "tick-format"
};

export const Realtime = {
render: () => {
const chartData = generateTimeseriesChartData(new Date());

const { xLines, yLines } = getXYLines(chartData);

const chartRef = createRef<FTimeseriesChart>();

const chartConfig: FTimeseriesChartConfig = {
data: chartData,
xAxis: {
lines: xLines
},
yAxis: {
lines: yLines
}
};

const interval = setInterval(() => {
const chartDataFlat = chartData.map(series => series.points).flat();
const newPoints = generateTimeseriesChartData(
new Date(chartDataFlat[chartDataFlat.length - 1].date + 60 * 1000),
chartData.length,
1
);
newPoints.forEach((element, idx) => {
const series = chartData[idx];
series?.points.shift();
series?.points.push(...element.points);
});
if (chartRef.value) {
chartRef.value.config = { ...chartConfig };
}
}, 1000);

setTimeout(() => {
clearInterval(interval);
}, 60000 * 60);
return html`<f-div height="500px">
<f-timeseries-chart ${ref(chartRef)} .config=${chartConfig}></f-timeseries-chart>
</f-div>`;
},

name: "realtime-data"
};
63 changes: 63 additions & 0 deletions stories/flow-dashboard/mock-data-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { TimeseriesData, TimeseriesPoint } from "@ollion/flow-dashboard";
import { faker } from "@faker-js/faker";

export function getColor() {
return "#" + Math.floor(faker.number.float({ min: 0, max: 1 }) * 16777215).toString(16);
}

export function generateTimeseriesChartData(
from?: Date,
seriesCount?: number,
pointsCount?: number
): TimeseriesData[] {
const startFrom = new Date().getTime();
const masterData: TimeseriesData[] = [];

const numberOfPoints = pointsCount ?? 150; //faker.number.int({ min: 50, max: 150 });
const numberOfSeries = seriesCount ?? faker.number.int({ min: 1, max: 3 });
const colors = [
"#66c2ff",
"#ff6666",
"#99ff99",
"#ffb366",
"#cc99ff",
"#99ccff",
"#ffcc99",
"#66ff99",
"#ff99cc",
"#ccccff"
];
const seriesColors = faker.helpers.arrayElements(colors, numberOfSeries);
for (let j = 0; j < numberOfSeries; j++) {
const startDate = from ? from.getTime() : startFrom;
const points: TimeseriesPoint[] = [];
for (let i = 0; i < numberOfPoints; i++) {
const currentDate = startDate + i * 60 * 1000;
let fluctuatingValue = Math.floor(faker.number.float({ min: 0, max: 1 }) * 10) + 50 * (j + 1); //faker.number.float({ min: 0, max: 1 }) * (yOffSet ?? 100) + Math.sin(i / 8) * 50; // Adding a sine wave for fluctuation
if (fluctuatingValue < 0) {
fluctuatingValue *= -1;
}
if (fluctuatingValue % 9 === 0) {
fluctuatingValue = 50 * (j + 1) * getRndInteger(1, 2);
}
const dataPoint: TimeseriesPoint = {
date: currentDate,
value: +fluctuatingValue.toFixed(0)
};

points.push(dataPoint);
}
masterData.push({
seriesName: faker.location.country(),
points,
color: seriesColors[j],
type: faker.helpers.arrayElement(["line", "bar", "area"])
});
}

return masterData;
}

export function getRndInteger(min: number, max: number) {
return faker.number.int({ min, max });
}
22 changes: 13 additions & 9 deletions stories/utils/mock-users-data.ts
Original file line number Diff line number Diff line change
@@ -11,16 +11,20 @@ import { createRef } from "lit/directives/ref.js";
import { FPopover } from "@ollion/flow-core";

export const popoverRef = createRef<FPopover>();

function getId() {
return faker.string.alpha(10);
}
export default function getFakeUsers(rowCount = 100, columnCount = 8): FTableSchemaData {
const users = [];

for (let i = 0; i < rowCount; i++) {
const firstName: FTableSchemaCell<string> = {
value: faker.name.firstName(),
value: faker.person.firstName(),
align: "middle-left"
};
const lastName = {
value: faker.name.lastName(),
value: faker.person.lastName(),
template: function () {
return html`<f-div gap="x-small" align="middle-center" width="100%" height="100%"
><f-text inline state="success">${this.value}</f-text></f-div
@@ -39,7 +43,7 @@ export default function getFakeUsers(rowCount = 100, columnCount = 8): FTableSch
{
icon: "i-chat",
tooltip: "This is Tooltip",
id: faker.random.alpha(5),
id: getId(),
onClick(_event, element) {
if (popoverRef.value) {
popoverRef.value.open = true;
@@ -50,18 +54,18 @@ export default function getFakeUsers(rowCount = 100, columnCount = 8): FTableSch
{
icon: "i-mail",
tooltip: "This is 2nd Tooltip",
id: faker.random.alpha(5)
id: getId()
},
{
icon: "i-star",
tooltip: "This is 3rd Tooltip",
id: faker.random.alpha(5)
id: getId()
}
]
};
const mobile = { value: faker.phone.number() };
const sex = { value: faker.name.sex() };
const age = { value: faker.random.numeric(2) };
const sex = { value: faker.person.sex() };
const age = { value: faker.number.int({ min: 18, max: 60 }) };
const birthDate: FTableSchemaCell & { value: Date } = {
value: faker.date.birthdate({ min: 18, max: 65, mode: "age" }),
template: function () {
@@ -78,11 +82,11 @@ export default function getFakeUsers(rowCount = 100, columnCount = 8): FTableSch
};

const address = {
value: `${faker.address.street()}, ${faker.address.city()}, ${faker.address.stateAbbr()}, ${faker.address.zipCode()} ${faker.address.country()}`
value: `${faker.location.street()}, ${faker.location.city()}, ${faker.location.state()}, ${faker.location.zipCode()} ${faker.location.country()}`
};

const userRow: FTableSchemaDataRow = {
id: faker.random.alpha(10),
id: getId(),
disableSelection: i % 2 === 0,
expandIconPosition: "left",
data: { firstName, lastName, age, birthDate, email, mobile, sex, address },
3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -27,6 +27,9 @@
{
"path": "./packages/flow-table"
},
{
"path": "./packages/flow-dashboard"
},
{
"path": "./packages/flow-md-editor"
}

0 comments on commit 2a75c8b

Please sign in to comment.