propeldata / ui-kit Goto Github PK
View Code? Open in Web Editor NEWReact components for data visualization and quickly building data analytics dashboards
Home Page: https://storybook.propeldata.com/
License: MIT License
React components for data visualization and quickly building data analytics dashboards
Home Page: https://storybook.propeldata.com/
License: MIT License
Even though we added a timeZone support here (#110), we don't have documentation on this feature yet.
We should make sure that it's documented for Counter, TimeSeries and Leaderboard. For example on this page (https://github.com/propeldata/ui-kit/blob/main/packages/ui-kit/src/components/TimeSeries/README.md) for TimeZone.
Closes PRO-2314 (this is an internal ticket ID).
The way we're using Parcel has allowed the following TypeScript issues to creep in without breaking the build. Ideally, Parcel (or whatever build tool we decide to use) fails the build when this happens, which will prevent us from merging to main.
@propeldata/react-counter:build: Building...
@propeldata/react-counter:build:
@propeldata/react-counter:build: @parcel/transformer-typescript-types: 'response' is of type 'unknown'.
@propeldata/react-counter:build: /Users/mark/src/propel/ui-kit/packages/react/counter/src/Counter.tsx:92:26
@propeldata/react-counter:build: 91 |
@propeldata/react-counter:build: > 92 | const metricData = response.metricByName.counter
@propeldata/react-counter:build: > | ^^^^^^^^ 'response' is of type 'unknown'.
@propeldata/react-counter:build: 93 |
@propeldata/react-counter:build: 94 | setHasError(false)
@propeldata/react-counter:build:
@propeldata/react-counter:build:
@propeldata/react-counter:build: Bundling...
@propeldata/react-counter:build: Packaging & Optimizing...
@propeldata/react-counter:build: ✨ Built in 2.94s
@propeldata/react-counter:build:
@propeldata/react-counter:build: dist/index.js 10.24 KB 65ms
@propeldata/react-counter:build: dist/index.module.js 9.59 KB 69ms
@propeldata/react-counter:build: dist/index.d.ts 1.57 KB 47ms
@propeldata/react-leaderboard:build:
@propeldata/react-leaderboard:build:
@propeldata/react-leaderboard:build:
@propeldata/react-leaderboard:build: @parcel/transformer-typescript-types: 'response' is of type 'unknown'.
@propeldata/react-leaderboard:build: /Users/mark/src/propel/ui-kit/packages/react/leaderboard/src/Leaderboard.tsx:229:26
@propeldata/react-leaderboard:build: 228 |
@propeldata/react-leaderboard:build: > 229 | const metricData = response.metricByName.leaderboard
@propeldata/react-leaderboard:build: > | ^^^^^^^^ 'response' is of type 'unknown'.
@propeldata/react-leaderboard:build: 230 |
@propeldata/react-leaderboard:build: 231 | const headers = metricData.headers
@propeldata/react-leaderboard:build: Bundling...
@propeldata/react-leaderboard:build: Packaging & Optimizing...
@propeldata/react-leaderboard:build: ✨ Built in 3.22s
@propeldata/react-leaderboard:build:
@propeldata/react-leaderboard:build: dist/index.js 34.77 KB 49ms
@propeldata/react-leaderboard:build: dist/index.module.js 33.17 KB 45ms
@propeldata/react-leaderboard:build: dist/index.d.ts 4.71 KB 19ms
@propeldata/react-time-series:build:
@propeldata/react-time-series:build:
@propeldata/react-time-series:build: @parcel/transformer-typescript-types: Type '_DeepPartialObject<{ [key: string]: { type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?:
@propeldata/react-time-series:build: number | undefined; grace?: string | ... 1 more ... | undefined; ticks: { ...; }; }; }> | _DeepPartialObject<...>' is not assignable to type 'Partial<{ [key: string]: ScaleOptionsByType<"linear" |
@propeldata/react-time-series:build: "logarithmic">; }>'.
@propeldata/react-time-series:build: Type '_DeepPartialObject<{ [key: string]: { type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | ...
@propeldata/react-time-series:build: 1 more ... | undefined; ticks: { ...; }; }; }>' is not assignable to type 'Partial<{ [key: string]: ScaleOptionsByType<"linear" | "logarithmic">; }>'.
@propeldata/react-time-series:build: 'string' index signatures are incompatible.
@propeldata/react-time-series:build: Type '_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number |
@propeldata/react-time-series:build: undefined; ticks: { ...; }; }> | undefined' is not assignable to type 'ScaleOptionsByType<"linear" | "logarithmic"> | undefined'.
@propeldata/react-time-series:build: Type '_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number |
@propeldata/react-time-series:build: undefined; ticks: { ...; }; }>' is not assignable to type 'ScaleOptionsByType<"linear" | "logarithmic"> | undefined'.
@propeldata/react-time-series:build: Type '_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number |
@propeldata/react-time-series:build: undefined; ticks: { ...; }; }>' is not assignable to type '{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined;
@propeldata/react-time-series:build: grace?: string | number | undefined; ticks: { ...; }; }'.
@propeldata/react-time-series:build: Type '_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number |
@propeldata/react-time-series:build: undefined; ticks: { ...; }; }>' is not assignable to type '{ type: "linear"; }'.
@propeldata/react-time-series:build: Types of property 'type' are incompatible.
@propeldata/react-time-series:build: Type '"linear" | undefined' is not assignable to type '"linear"'.
@propeldata/react-time-series:build: Type 'undefined' is not assignable to type '"linear"'.
@propeldata/react-time-series:build:
@propeldata/react-time-series:build: /Users/mark/src/propel/ui-kit/packages/react/time-series/src/TimeSeries.tsx:189:11
@propeldata/react-time-series:build: 188 | values,
@propeldata/react-time-series:build: > 189 | scales,
@propeldata/react-time-series:build: > | ^^^^^^ Type '\_DeepPartialObject<{ [key: string]: { type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | ... 1 more ... | undefined; ticks: { ...; }; }; }> | \_DeepPartialObject<...>' is not assignable to type 'Partial<{ [key: string]: ScaleOptionsByType<"linear" | "logarithmic">; }>'.
@propeldata/react-time-series:build: Type '\_DeepPartialObject<{ [key: string]: { type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | ... 1 more ... | undefined; ticks: { ...; }; }; }>' is not assignable to type 'Partial<{ [key: string]: ScaleOptionsByType<"linear" | "logarithmic">; }>'.
@propeldata/react-time-series:build: 'string' index signatures are incompatible.
@propeldata/react-time-series:build: Type '\_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number | undefined; ticks: { ...; }; }> | undefined' is not assignable to type 'ScaleOptionsByType<"linear" | "logarithmic"> | undefined'.
@propeldata/react-time-series:build: Type '\_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number | undefined; ticks: { ...; }; }>' is not assignable to type 'ScaleOptionsByType<"linear" | "logarithmic"> | undefined'.
@propeldata/react-time-series:build: Type '\_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number | undefined; ticks: { ...; }; }>' is not assignable to type '{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number | undefined; ticks: { ...; }; }'.
@propeldata/react-time-series:build: Type '\_DeepPartialObject<{ type: "linear"; } & CartesianScaleOptions & { beginAtZero: boolean; suggestedMin?: number | undefined; suggestedMax?: number | undefined; grace?: string | number | undefined; ticks: { ...; }; }>' is not assignable to type '{ type: "linear"; }'.
@propeldata/react-time-series:build: Types of property 'type' are incompatible.
@propeldata/react-time-series:build: Type '"linear" | undefined' is not assignable to type '"linear"'.
@propeldata/react-time-series:build: Type 'undefined' is not assignable to type '"linear"'.
@propeldata/react-time-series:build: 190 | variant,
@propeldata/react-time-series:build: 191 | customPlugins
@propeldata/react-time-series:build:
@propeldata/react-time-series:build: @parcel/transformer-typescript-types: 'response' is of type 'unknown'.
@propeldata/react-time-series:build:
@propeldata/react-time-series:build: /Users/mark/src/propel/ui-kit/packages/react/time-series/src/TimeSeries.tsx:260:26
@propeldata/react-time-series:build: 259 |
@propeldata/react-time-series:build: > 260 | const metricData = response.metricByName.timeSeries
@propeldata/react-time-series:build: > | ^^^^^^^^ 'response' is of type 'unknown'.
@propeldata/react-time-series:build: 261 |
@propeldata/react-time-series:build: 262 | const labels: string[] = [...metricData.labels]
@propeldata/react-time-series:build:
@propeldata/react-time-series:build: Bundling...
@propeldata/react-time-series:build: Packaging & Optimizing...
@propeldata/react-time-series:build: ✨ Built in 3.67s
@propeldata/react-time-series:build:
@propeldata/react-time-series:build: dist/index.js 34.02 KB 48ms
@propeldata/react-time-series:build: dist/index.module.js 32.9 KB 52ms
@propeldata/react-time-series:build: dist/index.d.ts 3.83 KB 27ms
The UI Kit should provide a pie chart component.
While developers can create pie charts using custom components and Query Hooks, there are still details they must work through:
Pie charts are so common that we should offer a first-class implementation of them, so that developers don't have to set up custom components or make these decisions — they can just get to visualizing.
The most important part of the design is "how do we query the data?" Propel's GraphQL API does not (yet) offer a pie chart API, but what would we expect from one? Probably it would look very similar to a Leaderboard query, but it would also support fetching a "total". By subtracting the N segments from the total, we can determine the N+1-th segment containing all other values.
For example, imagine we have a data set with
Then, the total is 200. If we run the queries below asking for two segments
, we'd expect the following responses.
Note that we pass filters1
and filters2
. filters2
must include all of filters1
, but it must also exclude entries with dimension values matching the N segments. For example, if filters1
is empty, filters2
must be equivalent to
dimension != 'A' AND dimension != 'B'
GraphQL Queries | JSON Response |
---|---|
query PieChart1(
$metric: MetricInput!
$timeRange: TimeRangeInput!
$timeZone: String
$dimension: String!
$sort: Sort
$segments: Int!
$filters1: [FilterInput!]
) {
leaderboard(input: {
metric: $metric
timeRange: $timeRange
timeZone: $timeZone
dimensions: [{
columnName: $dimension
}]
sort: $sort
rowLimit: $segments
filters: $filters1
}) {
rows
}
} query PieChart2(
$metric: MetricInput!
$timeRange: TimeRangeInput!
$timeZone: String
$filters2: [FilterInput!]
) {
counter(input: {
metric: $metric
timeRange: $timeRange
timeZone: $timeZone
filters: $filters2
}) {
value
}
} |
{
"data": {
"leaderboard": {
"rows": [
["A", "100"],
["B", "50"]
]
}
}
} {
"data": {
"counter": {
"value": "200"
}
}
} |
In the UI Kit, we could determine that the N+1-th segment should be proportional to 200 - 100 - 50 = 50 in the resulting pie chart.
GraphQL Query | JSON Response |
---|---|
query PieChart(
$metric: MetricInput!
$timeRange: TimeRangeInput!
$timeZone: String
$dimension: String!
$sort: Sort
$segments: Int!
$filters: [FilterInput!]
) {
leaderboard(input: {
metric: $metric
timeRange: $timeRange
timeZone: $timeZone
dimensions: [{
columnName: $dimension
}]
sort: $sort
rowLimit: $segments
filters: $filters
}) {
rows
}
counter(input: {
metric: $metric
timeRange: $timeRange
timeZone: $timeZone
filters: $filters
}) {
value
}
} |
{
"data": {
"leaderboard": {
"rows": [
["A", "100"],
["B", "50"]
]
},
"counter": {
"value": "200"
}
}
} |
This seems to work for COUNT metrics. Does it work for SUM metrics? It seems like the "correct" way to handle SUM metrics is to actually issue a subsequent counter query that explicitly filters out the top N segments.
Here is a screenshot from a recent Figma:
There should be props for
Additionally, there should be callbacks for interactivity with the segments (for example, "onhover", etc.).
Developers can continue to create pie charts using custom components and Query Hooks.
Pie charts will be a new component, and so they can be added in a minor version. They do not represent a breaking change.
We have a mixture of graphql-request versions in use. For example, @propeldata/react-counter is using ^3.6.1
, while @propeldata/ui-kit-graphql is using ^6.0.0
.
I noticed that there isn’t a style guide on how to maintain the Docs of your project. This makes it difficult for contributors who want to contribute to the docs understand what to do.
Create a style guide that has information on how to display code snippets and formatting headings, and the tone of voice you want for the docs. This would make it easier for contributors to see how they improve the Docs and increase productivity.
In order to implement upcoming features such as the Time Range Picker and the Filters we’ll require to use a Select component, we’ll need to decide whether implementing the Select component ourselves or using a third party library.
The Select component should include the next set of features
Implementing the Select component by ourselves will require that we fulfill the requirements that would mostly be already fulfilled by a third party library, it provides the advantage that we would have fully control around the component and any related bug would be in our hands to fix.
This would imply implementing by ourselves at least
Ideally we would expose our customers with an API to fully customize the Select component’s styles, using a third party library would make this process a lot easy since we can expose the library’s API to our customers similar to what we do with Chart.js, implementing this ourselves would require:
The component will require providing with full accessibility support
The component will require support for autocompletion
The component will require support for selecting multiple options
Pros | Cons |
---|---|
Radix is a popular library (13k stars on Github) | Radix doesn’t support autocompletion, although they plan to implement a ComboBox which is similar to our use case |
Radix provides a low-level customization by having a different component for each piece of the Select | Radix primitives have not been maintained since 4 months ago, which makes more unlikely that we’ll get the ComboBox component shortly |
Radix allows installing only its Select dependency which makes it more lightweight for our use case | Even if we make our own autocomplete on top of Radix that would sacrifice part of the Accessibility |
Radix contains other components that we could use for future use cases | Radix’s learning curve is pronounced since it’s necessary to use all of the sub components together and style each of them |
Pros | Cons |
---|---|
React-select is a popular library (26.9k stars on Github) | React-select has 296 unresolved issues on GitHub |
React-select provides built-in default autocompletion which is easy to disable | React-select could be an overkill for our use case |
React-select enables easy customization of each of the internal component’s CSS and enables to create a wrapper for each of the internal components | React-select’s last commit was 2 months ago |
React-select’s documentation is extensive | React-select does not contain other components that we could use for future use cases |
Note: Base-UI is a more modern and lightweight version of Material-UI that also allows better customization, it’s maintained by Material-UI and could be a better option to consider
Pros | Cons |
---|---|
Material-UI is a popular library (90.3k stars on Github) | It requires installing the entire library to use the Select component |
Only 111 open issues related to Select on Github | Material-UI is not a lightweight library |
Material-UI provides a Base-UI library which provides a basic set of components that are more customizable and lightweight (2.94 MB) This library also doesn’t require to install emotion. | Base-UI is currently in beta. |
Material-UI contains other components that we could use for future use cases | Material-UI requires installing emotion, which would add more weight to the bundle size |
Requirement | Radix | React-select | MUI |
---|---|---|---|
Accessibility | ✅ | ✅ | ✅ |
Keyboard | ✅ | ✅ | ✅ |
Lightweight | ✅ | ✅ | ❌ |
Well maintained | ✅ | ||
Customizable | ✅ | ✅ | ✅ |
Autocompletion | ✅ | ✅ | |
Multi-select | ✅ | ✅ | ✅ |
Lib | GitHub starts | Open issues | Closed issues* | Opened issues* | Contributors |
---|---|---|---|---|---|
https://github.com/mantinedev/mantine | 22.9k | 46 | 48 | 13 | 475 |
https://github.com/shoelace-style/shoelace | 10.8k | 37 | 5 | 11 | 132 |
https://github.com/mui/material-ui | 90.3k | 1.5k | 89 | 88 | 2887 |
https://github.com/radix-ui/primitives | 13.1k | 365 | 14 | 29 | 45 |
*- for the past month
The Contributing section could use more detail. New contributors might need a more structured idea on how to how to do contribute to your project
I suggest creating a separate Contributing file with information on how to do a PR, raise an issue, and code reviews. It would give new contributors a clearer idea on how to work on your project & serve as refresher for people who have been contributing to your project for a while.
The Foundations in Figma include all the essential design tokens for our Design System, such as the color palette, spacing, shadows, typography, etc. We need to establish a reliable and efficient method to sync the Foundations with the UI Kit.
The initial synchronization was completed manually, which proved to be unreliable, labor-intensive, and not very scalable. Fortunately, most of these tasks should be easy to automate.
The sync process involves two main steps:
The export step can be achieved by using a free plugin to export variables from Figma as JSON
We could implement the parsing step similarly to how we generated types. The script would parse the JSON, update the common design tokens SCSS, and refresh the type definitions.
Additionally, we could create a checker to scan all SCSS files in the codebase. This would detect any discrepancies between the token list from Figma and the current tokens in the codebase. For example, if --propel-border-color
exists in the codebase but not in the JSON, it would throw an error.
No response
The export step can also be achieved by using Figma's API, which requires a paid Enterprise subscription. For simplicity, I recommend starting with the JSON export and exploring a fully automated solution later.
No response
No response
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.