diff --git a/.eslintrc b/.eslintrc
index 0d8aad4..69a164d 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -15,7 +15,8 @@
"plugins": [
"@typescript-eslint",
"react",
- "react-hooks"
+ "react-hooks",
+ "import"
],
"rules": {
"no-console": "warn",
@@ -32,7 +33,21 @@
"prefer-spread": ["off"],
"react-hooks/exhaustive-deps": "off",
"arrow-parens": "error",
- "arrow-spacing": "error"
+ "arrow-spacing": "error",
+ "sort-imports": ["error", {"ignoreCase": true, "ignoreDeclarationSort": true}],
+ "import/order": [
+ "error",
+ { "groups":
+ [
+ "external",
+ "builtin",
+ "internal",
+ "parent",
+ "sibling",
+ "index"
+ ]
+ }
+ ]
},
"settings": {
"react": {
diff --git a/.gitignore b/.gitignore
index ebbbb77..d410eca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ node_modules
build
dist
.rpt2_cache
+coverage
# misc
.DS_Store
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index b8ed5cf..08bfd22 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at opensource@hodgef.com. All
+reported by contacting the project team at it.team@keyvalue.systems. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..6e314b4
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,76 @@
+
+
+
+
+
+## Pull Request Checklist
+
+
+
+- [ ] **Read the contributing guidelines.**
+- [ ] **Linked to an issue:** Fixes # (replace with the issue number, if applicable)
+- [ ] **Branch is up-to-date with the base branch:** `main`
+- [ ] **Changes pass tests locally:** `npm test` or `yarn test`
+- [ ] **Documentation has been updated, if necessary**
+- [ ] **Code follows the style guide of the project**
+
+
+## Description
+
+
+
+
+
+
+
+## Screenshots (if applicable)
+
+
+
+
+
+
+
+## Additional Notes
+
+
+
+
+
+
+
+## Related Issues or PRs
+
+
+
+
+
+
+
+## Reviewer Guidelines
+
+
+
+
+
+
+
+## Testing Instructions
+
+
+
+
+
+
+
+## Checklist for Reviewers
+
+
+
+- [ ] Code follows project conventions and style
+- [ ] Changes do not introduce new warnings or errors
+- [ ] Unit tests cover the changes
+- [ ] Documentation is updated
+
+
+## By submitting this pull request, I confirm that my contribution is made under the terms of the MIT License.
diff --git a/README.md b/README.md
index ab4a05b..9cf5a31 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,3 @@
-
-
-
-
# React Dot Matrix Chart
@@ -10,10 +6,9 @@
-
->A customizable ready to use Dot Matrix Chart Component for React
+> A customizable ready to use Dot Matrix Chart Component for React
-Try tweaking a dot matrix using this codesandbox link here
+Try tweaking a dot matrix using this codesandbox link here
## Installation
@@ -25,53 +20,42 @@ npm install @keyvaluesystems/react-dot-matrix-chart
You’ll need to install React separately since it isn't included in the package.
-
-
## Usage
-
-
React Dot Matrix Chart can run in a very basic mode by just providing the `dataPoints` like given below:
```jsx
+import DotMatrix from "@keyvaluesystems/react-dot-matrix-chart";
-import DotMatrix from '@keyvaluesystems/react-dot-matrix-chart';
-
-
-
+;
```
The datapoints is an array of objects with the following keys:
-- `name` - a string that represents each category
-- `count` - a number to specify the count of each category present(used to find the number of dots to be displayed)
-- `color` - a string to specify which colour to be used to represent the category in the dot matrix
-
-
+- `name` - a string that represents each category
+- `count` - a number to specify the count of each category present(used to find the number of dots to be displayed)
+- `color` - a string to specify which colour to be used to represent the category in the dot matrix
An example for dataPoints array is shown below:
```jsx
const dataPointsArray = [
{
- name: 'Category 1',
+ name: "Category 1",
count: 10,
- color: 'gray'
+ color: "gray"
},
{
- name: 'Category 2',
+ name: "Category 2",
count: 10,
- color: 'black'
+ color: "black"
},
{
- name: 'Category 3',
+ name: "Category 3",
count: 10,
- color: 'green'
+ color: "green"
}
];
-
```
You can specify the number of rows or columns to be present in the chart as well using the dimensions prop.
@@ -79,31 +63,21 @@ You can specify the number of rows or columns to be present in the chart as well
```jsx
```
If the count given in the dataPoints array results in a partial percentage (decimal value), a gradient dot will be displayed as shown below
+
-We can also control the display of the legend consisting of the details regarding the colour distribution using the props 'showLegend' and 'legendPosition' as follows.
-
-```jsx
-
-```
## Props
-
-
Props that can be passed to the component are listed below:
@@ -130,11 +104,11 @@ Props that can be passed to the component are listed below:
{ rows: 5, columns: 12 } |
- styles?: object |
+ spaceBetweenDots?: number |
- Provides you with a bunch of callback functions to override the default styles.
+ To specify the distance between each dot
|
- undefined |
+ 4 |
showLegend?: boolean |
@@ -144,16 +118,22 @@ Props that can be passed to the component are listed below:
false |
- legendPosition?: string |
+ legendPosition?: 'left' | 'left-start' | 'left-end | 'right' | 'right-start' | 'right-end' | 'top'| 'top-start' | 'top-bottom' | 'bottom' | 'bottom-start' | 'bottom-end' |
- To specify the position of the legend. The values that can be passed using this prop are 'left', 'right', 'top' and 'bottom'
+ To specify the position of the legend.
|
- right |
+ right-end |
+
+
+ styles?: object |
+
+ Provides you with a bunch of callback functions to override the default styles.
+ |
+ undefined |
-
## Style Customizations
All the default styles provided by this package are overridable using the `styles` prop.
@@ -163,20 +143,19 @@ the below code shows all the overridable styles:
({...styles}),
- DotsContainer: () => ({...styles}),
- Dot: () => ({...styles}),
- LegendContainer: () => ({...styles}),
- LegendName: () => ({...styles}),
- LegendDot: () => ({...styles})
+ Container: () => ({ ...styles }),
+ DotsContainer: () => ({ ...styles }),
+ Dot: () => ({ ...styles }),
+ LegendContainer: () => ({ ...styles }),
+ LegendName: () => ({ ...styles }),
+ LegendDot: () => ({ ...styles })
}}
/>
-
```
-- `Container` - overrides the dot matrix chart container style
-- `DotsContainer` - overrides the dot matrix chart dots container style
-- `Dot` - overrides the style of each dot in the chart
-- `LegendContainer` - overrides the legend (details) container style
-- `LegendName` - overrides the legend name style
-- `LegendDot` - overrides the legend dot style
\ No newline at end of file
+- `Container` - overrides the dot matrix chart container style
+- `DotsContainer` - overrides the dot matrix chart dots container style
+- `Dot` - overrides the style of each dot in the chart
+- `LegendContainer` - overrides the legend (details) container style
+- `LegendName` - overrides the legend name style
+- `LegendDot` - overrides the legend dot style
diff --git a/STYLE_GUIDELINES.md b/STYLE_GUIDELINES.md
new file mode 100644
index 0000000..7781de0
--- /dev/null
+++ b/STYLE_GUIDELINES.md
@@ -0,0 +1,23 @@
+## SCSS Style Guidelines for @keyvaluesystems/react-dot-matrix-chart
+
+**Introduction**
+
+As an open-source project utilizing SCSS, @keyvaluesystems/react-dot-matrix-chart strives to maintain a consistent and well-structured codebase. These SCSS style guidelines serve as a reference for contributors, ensuring that their SCSS code adheres to established conventions and best practices.
+
+**SCSS Coding Conventions**
+
+- Organize SCSS files into a logical structure.
+- Use meaningful and descriptive names for variables, mixins, and classes.
+- Use SCSS nesting judiciously to organize complex styles.
+- Include comments to explain non-obvious logic and complex styles.
+- Utilize SCSS variables to define reusable values.
+- Employ a SCSS linting tool.
+- Should support devices with all resolutions.
+- Follow CamelCase conventions for class names that concisely convey their purpose, enhancing code organization and readability.
+- Adhere to the practice of reusing style classes to improve code organization and maintainability.
+
+**Documentation Practices**
+
+- Provide clear documentation for exported mixins and variables.
+- Include a README file within the SCSS directory if necessary.
+- Add comments to SCSS files.
diff --git a/package-lock.json b/package-lock.json
index e3cdcc8..993fb25 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -53,7 +53,6 @@
"ts-loader": "^9.4.2",
"typescript": "^4.9.5",
"url-loader": "^4.1.1",
- "uuid": "^9.0.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "4.11.1",
@@ -33928,14 +33927,6 @@
"node": ">= 0.4.0"
}
},
- "node_modules/uuid": {
- "version": "9.0.0",
- "dev": true,
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
"node_modules/uuid-browser": {
"version": "3.1.0",
"dev": true,
diff --git a/package.json b/package.json
index b90c254..d8c07ef 100644
--- a/package.json
+++ b/package.json
@@ -77,7 +77,6 @@
"ts-loader": "^9.4.2",
"typescript": "^4.9.5",
"url-loader": "^4.1.1",
- "uuid": "^9.0.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "4.11.1",
@@ -92,6 +91,9 @@
"eslintConfig": {
"extends": "./.eslintrc.json"
},
+ "prettier": {
+ "trailingComma": "none"
+ },
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/scripts/testMock.js",
diff --git a/src/lib/dot-matrix/Chart.tsx b/src/lib/dot-matrix/Chart.tsx
index d6c8bd4..251e056 100644
--- a/src/lib/dot-matrix/Chart.tsx
+++ b/src/lib/dot-matrix/Chart.tsx
@@ -1,76 +1,77 @@
-import React, { useMemo } from 'react';
-import { v4 } from 'uuid';
-import {
- getNumberOfDots,
- getStyles,
- hasOverlapping
-} from './utils/utils';
+import React, { useMemo } from "react";
+
+import { getNumberOfDots, getStyles, hasOverlapping } from "./utils/utils";
import {
- Elements,
DEFAULT_COLUMNS,
+ DEFAULT_DOT_WIDTH,
DEFAULT_ROWS,
- DEFAULT_ROW_WIDTH,
- DEFAULT_ROW_GAP
-} from './constants';
-import { ChartProps, DataPointType } from './types';
-import classes from './styles.module.scss';
+ Elements
+} from "./constants";
+import { ChartProps, DataPointType } from "./types";
+import classes from "./styles.module.scss";
-const Chart = (props: ChartProps) : JSX.Element => {
+const Chart = (props: ChartProps): JSX.Element => {
const {
dimensions = {},
styles,
- data,
- overlappingValues,
+ dotsToBeMapped,
+ fractionalDots,
total,
- width
+ width,
+ spaceBetweenDots
} = props;
- const {
- rows = DEFAULT_ROWS,
- columns = DEFAULT_COLUMNS
- } = dimensions;
+
+ const { rows = DEFAULT_ROWS, columns = DEFAULT_COLUMNS } = dimensions;
const dotWidth = useMemo(
- () => (width ? width / columns - DEFAULT_ROW_GAP : DEFAULT_ROW_WIDTH),
+ () => (width ? width / columns - spaceBetweenDots : DEFAULT_DOT_WIDTH),
[width]
);
+
return (
- {data?.map((dataItem: DataPointType, rowIndex: number) => (
-
- {dataItem && Array.apply(null, Array(getNumberOfDots(dataItem, rows, columns, total))).map((item: null, columnIndex: number) => (
-
- {(hasOverlapping(overlappingValues, rowIndex, columnIndex) && (
-
- )) || (
+ {dotsToBeMapped?.map((dataItem: DataPointType, rowIndex: number) => (
+
+ {dataItem &&
+ [...Array(getNumberOfDots(dataItem, rows, columns, total))].map(
+ (item: null, columnIndex: number) => (
- )}
-
- ))}
+ id="dot-matrix-dots"
+ key={`${dataItem.name}-${rowIndex}-${columnIndex}`}
+ >
+ {hasOverlapping(fractionalDots, rowIndex, columnIndex) ? (
+
+ ) : (
+
+ )}
+
+ )
+ )}
))}
diff --git a/src/lib/dot-matrix/DotMatrix.tsx b/src/lib/dot-matrix/DotMatrix.tsx
index 8af4f72..cc744f4 100644
--- a/src/lib/dot-matrix/DotMatrix.tsx
+++ b/src/lib/dot-matrix/DotMatrix.tsx
@@ -1,17 +1,20 @@
-import React from 'react';
-import classes from './styles.module.scss';
-import { DotMatrixPropType } from './types';
-import { useDotMatrix } from './custom-hooks/useDotMatrix';
-import useChartContainerWidth from './custom-hooks/useChartContainerWidth';
-import Chart from './Chart';
-import Legend from './Legend';
-import { getLegendPosition, getStyles } from './utils/utils';
+import React from "react";
+
+import { useDotMatrix } from "./custom-hooks/useDotMatrix";
+import { getLegendPosition, getStyles } from "./utils/utils";
import {
- Elements,
DEFAULT_COLUMNS,
+ DEFAULT_GAP,
+ DEFAULT_LEGEND_POSITION,
DEFAULT_ROWS,
- DEFAULT_LEGEND_POSITION
-} from './constants';
+ Elements
+} from "./constants";
+import { DotMatrixPropType } from "./types";
+import Chart from "./Chart";
+import Legend from "./Legend";
+import useChartContainerWidth from "./custom-hooks/useChartContainerWidth";
+import classes from "./styles.module.scss";
+
const DotMatrix = (props: DotMatrixPropType): JSX.Element => {
const {
dataPoints,
@@ -19,16 +22,21 @@ const DotMatrix = (props: DotMatrixPropType): JSX.Element => {
rows: DEFAULT_ROWS,
columns: DEFAULT_COLUMNS
},
+ spaceBetweenDots = DEFAULT_GAP,
styles = {},
showLegend = false,
legendPosition = DEFAULT_LEGEND_POSITION
} = props;
- const width = useChartContainerWidth('dots-container', [
+ const width = useChartContainerWidth("dots-container", [
showLegend,
legendPosition
]);
- const [data, total, overlappingValues] = useDotMatrix(dataPoints, dimensions);
+
+ const [dotsToBeMapped, totalDots, fractionalDots] = useDotMatrix(
+ dataPoints,
+ dimensions
+ );
return (
@@ -43,17 +51,14 @@ const DotMatrix = (props: DotMatrixPropType): JSX.Element => {
- {showLegend && (
-
-
-
- )}
+ {showLegend && }
);
diff --git a/src/lib/dot-matrix/Legend.tsx b/src/lib/dot-matrix/Legend.tsx
index 35c0303..77a02e1 100644
--- a/src/lib/dot-matrix/Legend.tsx
+++ b/src/lib/dot-matrix/Legend.tsx
@@ -1,41 +1,38 @@
-import React from 'react';
-import { v4 } from 'uuid';
-import { getStyles } from './utils/utils';
+import React from "react";
+
+import { getStyles } from "./utils/utils";
import { DataPointType, LegendProps } from "./types";
-import {
- Elements
-} from './constants';
-import classes from './styles.module.scss';
+import { Elements } from "./constants";
+import classes from "./styles.module.scss";
const Legend = (props: LegendProps): JSX.Element => {
- const {
- styles,
- data
- } = props;
+ const { styles, data } = props;
+
return (
- {data?.map((point: DataPointType) => (
-
+ {data?.map((point: DataPointType, index) => (
+
))}
- )
+ );
};
export default Legend;
\ No newline at end of file
diff --git a/src/lib/dot-matrix/constants.ts b/src/lib/dot-matrix/constants.ts
index 0851001..7d4dd48 100644
--- a/src/lib/dot-matrix/constants.ts
+++ b/src/lib/dot-matrix/constants.ts
@@ -1,13 +1,13 @@
-export const COLOR_PALATTE = [
- '#fd7f6f',
- '#7eb0d5',
- '#b2e061',
- '#bd7ebe',
- '#ffb55a',
- '#ffee65',
- '#beb9db',
- '#fdcce5',
- '#8bd3c7'
+export const COLOR_PALETTE = [
+ "#fd7f6f",
+ "#7eb0d5",
+ "#b2e061",
+ "#bd7ebe",
+ "#ffb55a",
+ "#ffee65",
+ "#beb9db",
+ "#fdcce5",
+ "#8bd3c7"
];
export enum Elements {
@@ -23,8 +23,10 @@ export const DEFAULT_ROWS = 5;
export const DEFAULT_COLUMNS = 12;
-export const DEFAULT_LEGEND_POSITION = 'right';
+export const DEFAULT_LEGEND_POSITION = "right-end";
-export const DEFAULT_ROW_WIDTH = 35;
+export const DEFAULT_DOT_WIDTH = 10;
-export const DEFAULT_ROW_GAP = 4;
+export const DEFAULT_GAP = 4;
+
+export const DEFAULT_DOT_CONTAINER_WIDTH = 600;
diff --git a/src/lib/dot-matrix/custom-hooks/useChartContainerWidth.ts b/src/lib/dot-matrix/custom-hooks/useChartContainerWidth.ts
index 0ab431a..18fa9e1 100644
--- a/src/lib/dot-matrix/custom-hooks/useChartContainerWidth.ts
+++ b/src/lib/dot-matrix/custom-hooks/useChartContainerWidth.ts
@@ -1,11 +1,13 @@
-import { useState, useEffect } from 'react';
-import { findContainerWidth } from '../utils/utils';
+import { useEffect, useState } from "react";
+
+import { DEFAULT_DOT_CONTAINER_WIDTH } from "../constants";
+import { findContainerWidth } from "../utils/utils";
const useChartContainerWidth = (
id: string,
dependencyArray: Array
): number => {
- const [width, setWidth] = useState(0);
+ const [width, setWidth] = useState(DEFAULT_DOT_CONTAINER_WIDTH);
useEffect(() => {
updateContainerWidth();
@@ -15,12 +17,19 @@ const useChartContainerWidth = (
updateContainerWidth();
}, [...dependencyArray]);
- window.onresize = (): void => {
- updateContainerWidth();
- };
+ useEffect(() => {
+ if (typeof window !== "undefined") {
+ window.addEventListener("resize", updateContainerWidth);
+
+ return () => {
+ window.removeEventListener("resize", updateContainerWidth);
+ };
+ }
+ }, []);
const updateContainerWidth = (): void => {
- const widthValue = findContainerWidth(id);
+ let widthValue;
+ if (typeof window !== "undefined") widthValue = findContainerWidth(id);
if (widthValue) setWidth(widthValue);
};
return width;
diff --git a/src/lib/dot-matrix/custom-hooks/useDotMatrix.ts b/src/lib/dot-matrix/custom-hooks/useDotMatrix.ts
index 7860c40..59d863c 100644
--- a/src/lib/dot-matrix/custom-hooks/useDotMatrix.ts
+++ b/src/lib/dot-matrix/custom-hooks/useDotMatrix.ts
@@ -1,42 +1,47 @@
-import { useMemo} from "react";
-import { DataPointType } from '../types';
-import { COLOR_PALATTE, DEFAULT_COLUMNS , DEFAULT_ROWS } from '../constants';
-import { isColorPresent } from "../utils/utils";
+import { useMemo } from "react";
-export const useDotMatrix = (dataPoints: DataPointType[], dimensions: { rows?: number, columns?: number }): [DataPointType[], number, number[]] => {
+import { DataPointType, DimensionProp } from "../types";
+import { COLOR_PALETTE, DEFAULT_COLUMNS, DEFAULT_ROWS } from "../constants";
+import { isColorAlreadyUsed } from "../utils/utils";
- const [data, total] = useMemo(() => {
- const values: DataPointType[] = [];
- let totalVal = 0
+export const useDotMatrix = (
+ dataPoints: DataPointType[],
+ dimensions: DimensionProp
+): [DataPointType[], number, number[]] => {
+ const [dotsToBeMapped, totalDots] = useMemo(() => {
+ const dotMatrixData: DataPointType[] = [];
+ let totalCount = 0;
if (dataPoints) {
let colorIndex = 0;
dataPoints.forEach((point: DataPointType) => {
- totalVal += point.count;
+ totalCount += point.count;
let { color } = point;
if (!color) {
do {
- color = COLOR_PALATTE[colorIndex];
+ color = COLOR_PALETTE[colorIndex];
colorIndex++;
- } while (isColorPresent(dataPoints, color, values))
+ } while (isColorAlreadyUsed(dataPoints, color, dotMatrixData));
}
- values.push({ ...point, color });
- })
+ dotMatrixData.push({ ...point, color });
+ });
}
- return [values, totalVal]
+ return [dotMatrixData, totalCount]
}, [dataPoints]);
- const overlappingValues: number[] = useMemo(() => {
- const partial: Array = [];
- if (total) {
- data?.forEach((each: DataPointType) => {
+ // Calculates fractional part of a category based on the provided data points
+ // relative to the total number of dots and dimension
+ const fractionalDots: number[] = useMemo(() => {
+ const fractionalParts: Array = [];
+ if (totalDots) {
+ dotsToBeMapped?.forEach((point: DataPointType) => {
const { rows = DEFAULT_ROWS, columns = DEFAULT_COLUMNS } = dimensions;
- const percentage = each.count / total;
- const partialDots = percentage * rows * columns;
- const value = partialDots - Math.floor(partialDots);
- partial.push(value);
- })
+ const pointPercentage = point.count / totalDots;
+ const dotsCount = pointPercentage * rows * columns;
+ const dotFraction = dotsCount - Math.floor(dotsCount);
+ fractionalParts?.push(dotFraction);
+ });
}
- return partial;
- }, [total]);
- return [data, total, overlappingValues];
-}
\ No newline at end of file
+ return fractionalParts;
+ }, [totalDots]);
+ return [dotsToBeMapped, totalDots, fractionalDots];
+};
\ No newline at end of file
diff --git a/src/lib/dot-matrix/styles.module.scss b/src/lib/dot-matrix/styles.module.scss
index aacc5dd..c2fabc4 100644
--- a/src/lib/dot-matrix/styles.module.scss
+++ b/src/lib/dot-matrix/styles.module.scss
@@ -20,6 +20,7 @@
}
.legendDot {
border-radius: 50%;
+ flex-shrink: 0;
width: 1.6rem;
height: 1.6rem;
}
@@ -40,4 +41,16 @@
padding: 7px;
width: max-content;
}
+}
+@media (max-width: 768px) {
+ .container{
+ .legendDot {
+ width: 0.6rem;
+ height: 0.6rem;
+ }
+ .legend .name {
+ font-size: 8px;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/src/lib/dot-matrix/types.d.ts b/src/lib/dot-matrix/types.d.ts
index d469883..108fd8c 100644
--- a/src/lib/dot-matrix/types.d.ts
+++ b/src/lib/dot-matrix/types.d.ts
@@ -1,39 +1,54 @@
export interface DataPointType {
- name: string,
- count: number,
- color?: string
+ name: string;
+ count: number;
+ color?: string;
}
export interface ChartProps {
- dimensions?: DimensionProp,
- styles: StyleProp,
- data: DataPointType[],
- overlappingValues: number[],
- total: number,
- width: number
+ dimensions?: DimensionProp;
+ styles: StyleProp;
+ dotsToBeMapped: DataPointType[];
+ fractionalDots: number[];
+ total: number;
+ width: number;
+ spaceBetweenDots: number;
}
export type DimensionProp = {
rows?: number;
- columns?: number
-}
+ columns?: number;
+};
export type StyleProp = {
- Dot?: () => {},
- DotsContainer?: () => {},
- Container?: () => {},
- LegendContainer?: () => {},
- LegendName?: () => {},
- LegendDot?: () => {}
-}
+ Dot?: () => {};
+ DotsContainer?: () => {};
+ Container?: () => {};
+ LegendContainer?: () => {};
+ LegendName?: () => {};
+ LegendDot?: () => {};
+};
+
export interface LegendProps {
- styles: StyleProp,
- data: DataPointType[]
+ styles: StyleProp;
+ data: DataPointType[];
}
+
export interface DotMatrixPropType {
- dataPoints: DataPointType[],
- dimensions?: DimensionProp,
- showLegend?: boolean,
- legendPosition?: 'left' | 'right' | 'top' | 'bottom'
- styles?: StyleProp
+ dataPoints: DataPointType[];
+ dimensions?: DimensionProp;
+ spaceBetweenDots?: number;
+ showLegend?: boolean;
+ legendPosition?:
+ | "left"
+ | "left-start"
+ | "left-end"
+ | "right"
+ | "right-start"
+ | "right-end"
+ | "top"
+ | "top-start"
+ | "top-end"
+ | "bottom"
+ | "bottom-start"
+ | "bottom-end";
+ styles?: StyleProp;
}
-
diff --git a/src/lib/dot-matrix/utils/utils.ts b/src/lib/dot-matrix/utils/utils.ts
index c00f63e..0f0217a 100644
--- a/src/lib/dot-matrix/utils/utils.ts
+++ b/src/lib/dot-matrix/utils/utils.ts
@@ -1,32 +1,43 @@
import { DataPointType, StyleProp } from "../types";
-import { Elements } from '../constants';
-export const getNumberOfDots = (point: DataPointType, rows: number, columns: number, total: number): number => {
+import { Elements } from "../constants";
+
+export const getNumberOfDots = (
+ point: DataPointType,
+ rows: number,
+ columns: number,
+ total: number
+): number => {
const percentage = point.count / total;
return Math.floor(percentage * rows * columns);
-}
+};
-export const isColorPresent = (dataPoints: DataPointType[], color: string, dataValues: DataPointType[] = []): boolean => {
+export const isColorAlreadyUsed = (
+ dataPoints: DataPointType[],
+ color: string,
+ dataValues: DataPointType[] = []
+): boolean => {
const findColor = dataPoints?.find((e) => e.color === color);
const colorInLocal = dataValues?.find((e) => e.color === color);
return Boolean(findColor) || Boolean(colorInLocal);
-}
+};
+
+export const getLegendPosition = (
+ legendPosition: string
+): { flexDirection: string; alignItems: string } => {
+ const [position, alignment] = legendPosition.split("-");
-export const getLegendPosition = (legendPosition: string): {flexDirection: string, alignItems: string} => {
const flexDirection = {
- left: 'row-reverse',
- right: 'row',
- top: 'column-reverse',
- bottom: 'column'
+ left: "row-reverse",
+ right: "row",
+ top: "column-reverse",
+ bottom: "column"
};
- const alignment = {
- left: 'end',
- right: 'end',
- top: 'center',
- bottom: 'center'
- }
- return { flexDirection: flexDirection[legendPosition], alignItems: alignment[legendPosition]};
-}
+ return {
+ flexDirection: flexDirection[position],
+ alignItems: alignment ? alignment : "center"
+ };
+};
export const getStyles = (element: Elements, styles: StyleProp): object => {
const getElementStyle = styles[element];
@@ -36,15 +47,21 @@ export const getStyles = (element: Elements, styles: StyleProp): object => {
return {};
};
-export const hasOverlapping = (values: number[], indexRow: number, indexColumn: number): boolean => (
- indexColumn === 0 && indexRow > 0 && values[indexRow - 1] < 1 && values[indexRow - 1] !== 0
-);
+export const hasOverlapping = (
+ values: number[],
+ indexRow: number,
+ indexColumn: number
+): boolean =>
+ indexColumn === 0 &&
+ indexRow > 0 &&
+ values[indexRow - 1] < 1 &&
+ values[indexRow - 1] !== 0;
-export const findContainerWidth = (id: string):number => {
+export const findContainerWidth = (id: string): number => {
const element = document.getElementById(id);
let value = 0;
if (element) {
value = element.clientWidth;
}
return value;
-}
\ No newline at end of file
+};
diff --git a/src/lib/tests/dotMatrix.test.tsx b/src/lib/tests/dotMatrix.test.tsx
index 511df04..8b9849b 100644
--- a/src/lib/tests/dotMatrix.test.tsx
+++ b/src/lib/tests/dotMatrix.test.tsx
@@ -1,29 +1,32 @@
-import React from 'react';
+import React from "react";
import {
render,
- fireEvent,
queryByAttribute,
queryAllByAttribute
} from "@testing-library/react";
-import DotMatrix from '../dot-matrix';
-import { DotMatrixPropType } from '../dot-matrix/types';
+import "@testing-library/jest-dom";
-const getById = queryByAttribute.bind(null, 'id');
-const getAllById = queryAllByAttribute.bind(null, 'id');
+import { DotMatrixPropType } from "../dot-matrix/types";
+import { getStyles, hasOverlapping } from "../dot-matrix/utils/utils";
+import { Elements } from "../dot-matrix/constants";
+import DotMatrix from "../dot-matrix";
+
+const getById = queryByAttribute.bind(null, "id");
+const getAllById = queryAllByAttribute.bind(null, "id");
test("If all categories are displayed in Dot Matrix Chart", async () => {
const dotMatrixProps: DotMatrixPropType = {
dataPoints: [
{
- name: 'Category 1',
+ name: "Category 1",
count: 10
},
{
- name: 'Category 2',
+ name: "Category 2",
count: 10
},
{
- name: 'Categroy 3',
+ name: "Categroy 3",
count: 10
}
]
@@ -33,7 +36,8 @@ test("If all categories are displayed in Dot Matrix Chart", async () => {
const category1 = await getById(dom.container, "each-category-0-0");
const category2 = await getById(dom.container, "each-category-1-0");
const category3 = await getById(dom.container, "each-category-2-0");
- if (!category1 || !category2 || !category3) throw Error("All Categories not present");
+ if (!category1 || !category2 || !category3)
+ throw Error("All Categories not present");
} else {
throw Error("No DOM Found");
}
@@ -41,17 +45,19 @@ test("If all categories are displayed in Dot Matrix Chart", async () => {
test("If the color is used in the Dot Matrix Chart", async () => {
const dotMatrixProps: DotMatrixPropType = {
- dataPoints: [{
- name: 'Category 1',
- count: 12,
- color: 'black'
- }]
- }
+ dataPoints: [
+ {
+ name: "Category 1",
+ count: 12,
+ color: "black"
+ }
+ ]
+ };
const dom = render();
if (dom) {
const dot = await getById(dom.container, "each-category-0-0");
if (!dot) throw Error("Dot Absent");
- expect(dot.style._values["background-color"]).toBe('black')
+ expect(dot.style._values["background-color"]).toBe("black");
} else {
throw Error("No DOM Found");
}
@@ -59,22 +65,64 @@ test("If the color is used in the Dot Matrix Chart", async () => {
test("If the number of dots rendered in Dot Matrix Chart is correct", async () => {
const dotMatrixProps: DotMatrixPropType = {
- dataPoints: [{
- name: 'Category 1',
- count: 12,
- color: 'black'
- }],
+ dataPoints: [
+ {
+ name: "Category 1",
+ count: 12,
+ color: "black"
+ }
+ ],
dimensions: {
rows: 5,
columns: 5
}
- }
+ };
const dom = render();
if (dom) {
const dot = await getAllById(dom.container, "dot-matrix-dots");
if (dot?.length === 0) throw Error("Dots Absent");
- expect(dot.length).toBe(25)
+ expect(dot.length).toBe(25);
} else {
throw Error("No DOM Found");
}
});
+
+test("renders DotMatrix with legend when showLegend is true", () => {
+ const dotMatrixProps: DotMatrixPropType = {
+ dataPoints: [
+ {
+ name: "Category 1",
+ count: 12,
+ color: "black"
+ }
+ ],
+ showLegend: true
+ };
+ const { container } = render();
+
+ const legendContainer = getById(container, "legend-container");
+ expect(legendContainer).toBeInTheDocument();
+});
+
+test("getStyles util should return an empty object when no styles are provided", () => {
+ const result = getStyles(Elements.Container, {});
+ expect(result).toEqual({});
+});
+
+test("getStyles util should return the style object for a specific element if available", async () => {
+ const mockStyle = { color: "red", fontSize: "16px" };
+ const styles = {
+ [Elements.DotsContainer]: () => mockStyle,
+ [Elements.Dot]: () => ({})
+ };
+ const result = getStyles(Elements.DotsContainer, styles);
+ expect(result).toEqual(mockStyle);
+});
+
+test("should return true when [indexRow - 1] is not equal to 0 and less than 1", () => {
+ const values = [2, 0.5, 1];
+ const indexRow = 2;
+ const indexColumn = 0;
+ const result = hasOverlapping(values, indexRow, indexColumn);
+ expect(result).toBe(true);
+});
diff --git a/src/stories/Component.stories.tsx b/src/stories/Component.stories.tsx
index 8967219..b332980 100644
--- a/src/stories/Component.stories.tsx
+++ b/src/stories/Component.stories.tsx
@@ -1,48 +1,133 @@
-import React from 'react';
-import { ComponentStory, ComponentMeta } from '@storybook/react';
-import Component from '../lib';
+import React from "react";
+import { ComponentStory, ComponentMeta } from "@storybook/react";
+
+import Component from "../lib";
export default {
- title: 'Storybook/Dot Matrix Chart',
- component: Component,
- parameters: {
- // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout
- layout: 'fullscreen',
- },
- } as ComponentMeta;
+ title: "Storybook/Dot Matrix Chart",
+ component: Component,
+ parameters: {
+ // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout
+ layout: "fullscreen"
+ }
+} as ComponentMeta;
- const Template: ComponentStory = (args) => ;
+const Template: ComponentStory = (args) => (
+
+);
export const DotMatrixExample = Template.bind({});
DotMatrixExample.args = {
dimensions: {
rows: 5,
- columns: 10,
+ columns: 5
},
- styles:{
+ dataPoints: [
+ {
+ name: "Electronics",
+ count: 25
+ },
+ {
+ name: "Fashion",
+ count: 18
+ },
+ {
+ name: "Home & Garden",
+ count: 12
+ },
+ {
+ name: "Sports & Outdoors",
+ count: 30
+ },
+ {
+ name: "Beauty & Personal Care",
+ count: 15
+ }
+ ]
+};
+export const DotMatrixWithCustomStyles = Template.bind({});
+DotMatrixWithCustomStyles.args = {
+ styles: {
+ LegendContainer: () => ({
+ border: "1px solid white",
+ width: "60%",
+ "@media (maxWidth: 768px)": {
+ width: "50%"
+ },
+ "@media (maxWidth: 480px)": {
+ width: "40%"
+ }
+ })
+ },
+ dimensions: {
+ rows: 8,
+ columns: 8
},
- dataPoints:[
+ showLegend: true,
+ legendPosition: "right",
+ dataPoints: [
{
- name: 'Category 1',
+ name: "Technology",
count: 15,
- color: '#96C3EB'
+ color: "#4CAF50"
+ },
+ {
+ name: "Healthcare",
+ count: 5,
+ color: "#FF5722"
+ },
+ {
+ name: "Finance",
+ count: 10,
+ color: "#2196F3"
+ },
+ {
+ name: "Education",
+ count: 10,
+ color: "#FFC107"
+ },
+ {
+ name: "Entertainment",
+ count: 10,
+ color: "#9C27B0"
+ }
+ ]
+};
+
+export const DotMatrixWithGradientDot = Template.bind({});
+DotMatrixWithGradientDot.args = {
+ showLegend: true,
+ dimensions: {
+ rows: 6,
+ columns: 5
+ },
+ legendPosition: "right-start",
+ dataPoints: [
+ {
+ name: "Art",
+ count: 21,
+ color: "#FFD700"
},
{
- name: 'Category 2',
- count: 5
+ name: "Science",
+ count: 23,
+ color: "#ADD8E6"
},
{
- name: 'Category 3',
- count: 10
+ name: "Sports",
+ count: 24,
+ color: "#FFA07A"
},
{
- name: 'Category 3',
- count: 10
+ name: "Nature",
+ count: 21,
+ color: "#98FB98"
},
{
- name: 'Category 3',
- count: 10
+ name: "Travel",
+ count: 25,
+ color: "#FFB6C1"
}
]
-};
\ No newline at end of file
+};