diff --git a/README.md b/README.md index 923d569..a3263ec 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# ReactJS with storybook + This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). ## Available Scripts diff --git a/package-lock.json b/package-lock.json index 0617e44..cfea5b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4262,6 +4262,47 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "chart.js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.8.0.tgz", + "integrity": "sha512-Di3wUL4BFvqI5FB5K26aQ+hvWh8wnP9A3DWGvXHVkO13D3DSnaSsdZx29cXlEsYKVkn1E2az+ZYFS4t0zi8x0w==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.3.0.tgz", + "integrity": "sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g==", + "requires": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^0.5.3" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + } + } + }, + "chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "requires": { + "color-name": "^1.0.0" + } + }, + "chartjs-plugin-annotation": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-0.5.7.tgz", + "integrity": "sha1-G/DjAZmmqf+Yic4PN6HnVagNEL8=", + "requires": { + "chart.js": "^2.4.0" + } + }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -9866,6 +9907,11 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -11888,6 +11934,15 @@ "whatwg-fetch": "3.0.0" } }, + "react-chartjs-2": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.8.0.tgz", + "integrity": "sha512-BPpC+qfnh37DkcXvxRwA1rdD9rX/0AQrwru4VZTLofCCuZBwRsc7PbfxjilvoB6YlHhorwZu40YDWEQkoz7xfQ==", + "requires": { + "lodash": "^4.17.4", + "prop-types": "^15.5.8" + } + }, "react-clientside-effect": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.2.tgz", diff --git a/package.json b/package.json index c334a42..a1ee62a 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,11 @@ "dependencies": { "@material-ui/core": "^4.5.0", "@material-ui/icons": "^4.4.3", + "chart.js": "^2.8.0", + "chartjs-plugin-annotation": "^0.5.7", "prop-types": "^15.7.2", "react": "^16.10.1", + "react-chartjs-2": "^2.8.0", "react-dom": "^16.10.1", "react-scripts": "3.2.0" }, diff --git a/src/serviceWorker.js b/serviceWorker.js similarity index 100% rename from src/serviceWorker.js rename to serviceWorker.js diff --git a/src/modules/common/components/ChartComponent.js b/src/modules/common/components/ChartComponent.js new file mode 100644 index 0000000..2823252 --- /dev/null +++ b/src/modules/common/components/ChartComponent.js @@ -0,0 +1,226 @@ +import React, { useState } from 'react'; +import { Line } from 'react-chartjs-2'; +import annotationPlugin from 'chartjs-plugin-annotation'; +import PropTypes from 'prop-types'; +import '../styles/ChartComponent.css'; +import sampleData from '../constants/chartSample'; + +// styles options for the Chart +const options = { + layout: { + padding: { + left: 40, + right: 40, + top: 40, + bottom: 20, + }, + }, + legend: { + display: false, + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true, + suggestedMax: 10, + callback: () => '', + }, + gridLines: { + display: false, + drawBorder: false, + }, + }], + xAxes: [{ + ticks: { + fontFamily: 'Nunito', + fontColor: '#727272', + fontSize: 10, + }, + gridLines: { + display: false, + drawBorder: false, + }, + }], + }, + plugins: [annotationPlugin], + annotation: { + annotations: [{ + type: 'line', + mode: 'horizontal', + scaleID: 'y-axis-0', + value: 5, + borderColor: 'rgba(0, 0, 0, 0.4)', + borderWidth: 1, + label: { + enabled: false, + content: 'Test label', + }, + }], + }, +}; + +// default months array used for labels in the chart +const listMonth = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']; + +// the Data configuration for charts +const dataChart = (propData, monthLabels, lengthArray) => ({ + labels: propData.mind.length < monthLabels.length + ? monthLabels.slice(0, propData.mind.length).reverse() + : monthLabels.reverse(), + datasets: [ + { + label: 'MIND', + fill: false, + pointRadius: 0, + borderColor: '#5065CB', + data: propData.mind.slice(0, lengthArray).reverse(), + }, + { + label: 'BODY', + fill: false, + pointRadius: 0, + borderColor: '#CB50C4', + data: propData.body.slice(0, lengthArray).reverse(), + }, + { + label: 'SOUL', + fill: false, + pointRadius: 0, + borderColor: '#50ADCB', + data: propData.soul.slice(0, lengthArray).reverse(), + }, + { + label: 'WORK', + fill: false, + pointRadius: 0, + borderColor: '#FFB800', + data: propData.work.slice(0, lengthArray).reverse(), + }, + { + label: 'PLAY', + fill: false, + pointRadius: 0, + borderColor: '#FD9184', + data: propData.play.slice(0, lengthArray).reverse(), + }, + { + label: 'LOVE', + fill: false, + pointRadius: 0, + borderColor: '#A0AEF6', + data: propData.love.slice(0, lengthArray).reverse(), + }, + ], +}); + +// A helper funcion used to get an array of a category +// if the array length is 1 month, add 0 values to compare +const buildCategoryArray = (data, category) => { + const wellnessList = []; + data.map((record) => !!record[category] && wellnessList.push(record[category])); + if (wellnessList.length === 1) wellnessList.push(0); + + return wellnessList; +}; + +// A function that return an object of each category with array of values +const data = (propData) => ({ + mind: buildCategoryArray(propData, 'mind'), + body: buildCategoryArray(propData, 'body'), + soul: buildCategoryArray(propData, 'soul'), + work: buildCategoryArray(propData, 'work'), + play: buildCategoryArray(propData, 'play'), + love: buildCategoryArray(propData, 'love'), +}); + +// A function that return a months array from current Month to last 'X' months +// the 'X' value can be 12, 6 or 3. +const calculateMonths = (lengthArray) => { + const currentMonth = new Date().getMonth(); + const ordererMonth = []; + + listMonth.forEach((val, index) => { + if (currentMonth < index) { + ordererMonth.push(listMonth[(listMonth.length - index) + currentMonth]); + } else ordererMonth.push(listMonth[currentMonth - index]); + }); + return ordererMonth.slice(0, lengthArray); +}; + +const ColoredLine = ({ color }) => ( +
+); + +// main functional component exported +export default function ChartComponent({ + propData, + sample, + disable, + text, +}) { + const [month, setMonth] = useState(12); + const secData = sample ? sampleData : propData; + + return ( +
+ { text ? {text} : null} +
+ +
+

Wellness Chart

+ +
+ +
+ +
+
+ + MIND + + BODY + + SOUL + + WORK + + PLAY + + LOVE +
+
+
+ ); +} + +// The PropTypes for the props received +ChartComponent.propTypes = { + propData: PropTypes.arrayOf(PropTypes.any), + sample: PropTypes.bool, + disable: PropTypes.bool, + text: PropTypes.string, +}; + +ChartComponent.defaultProps = { + propData: [], + sample: false, + disable: false, + text: null, +}; + +ColoredLine.propTypes = { + color: PropTypes.string, +}; + +ColoredLine.defaultProps = { + color: null, +}; diff --git a/src/modules/common/components/docs/ChartComponent/index.stories.js b/src/modules/common/components/docs/ChartComponent/index.stories.js new file mode 100644 index 0000000..f245071 --- /dev/null +++ b/src/modules/common/components/docs/ChartComponent/index.stories.js @@ -0,0 +1,135 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import ChartComponent from '../../ChartComponent'; + +const propData = [ + { + mind: 10, + body: 9, + soul: 10, + work: 9, + play: 10, + love: 9, + }, + { + mind: 9, + body: 7, + soul: 5, + work: 3, + play: 1, + love: 4, + }, + { + mind: 7, + body: 3, + soul: 1, + work: 1, + play: 5, + love: 9, + }, + { + mind: 4, + body: 7, + soul: 4, + work: 7, + play: 4, + love: 7, + }, + { + mind: 8, + body: 4, + soul: 10, + work: 1, + play: 2, + love: 6, + }, + { + mind: 5, + body: 9, + soul: 5, + work: 9, + play: 5, + love: 9, + }, + { + mind: 5, + body: 1, + soul: 7, + work: 5, + play: 9, + love: 3, + }, + { + mind: 6, + body: 3, + soul: 6, + work: 3, + play: 6, + love: 3, + }, + { + mind: 2, + body: 4, + soul: 6, + work: 8, + play: 10, + love: 7, + }, + { + mind: 9, + body: 7, + soul: 5, + work: 3, + play: 1, + love: 4, + }, + { + mind: 7, + body: 3, + soul: 1, + work: 1, + play: 5, + love: 9, + }, + { + mind: 4, + body: 7, + soul: 4, + work: 7, + play: 4, + love: 7, + }, +]; + +const dummyText = 'With a partner account, you can be able to see this chart.'; + +storiesOf('ChartComponent', module) + .add('Show ChartComponent component with sample data', () => ( + + )) + .add('Show ChartComponent component with Blurry effect and a Text sample', () => ( + + )) + .add('Show ChartComponent component of 12 months', () => ( + + )) + .add('Show ChartComponent component of 9 months', () => ( + + )) + .add('Show ChartComponent component of 4 months', () => ( + + )) + .add('Show ChartComponent component of 2 months', () => ( + + )) + .add('Show ChartComponent component of 1 months', () => ( + + )); diff --git a/src/modules/common/constants/chartSample.js b/src/modules/common/constants/chartSample.js new file mode 100644 index 0000000..1bc2e1d --- /dev/null +++ b/src/modules/common/constants/chartSample.js @@ -0,0 +1,98 @@ +export default [ + { + mind: 4, + body: 9, + soul: 6, + work: 4, + play: 5, + love: 5, + }, + { + mind: 6, + body: 7, + soul: 8, + work: 3, + play: 1, + love: 10, + }, + { + mind: 8, + body: 5, + soul: 6, + work: 1, + play: 5, + love: 5, + }, + { + mind: 6, + body: 3, + soul: 8, + work: 7, + play: 1, + love: 10, + }, + { + mind: 4, + body: 5, + soul: 10, + work: 1, + play: 5, + love: 5, + }, + { + mind: 6, + body: 7, + soul: 8, + work: 9, + play: 1, + love: 10, + }, + { + mind: 8, + body: 9, + soul: 6, + work: 5, + play: 5, + love: 5, + }, + { + mind: 6, + body: 7, + soul: 8, + work: 3, + play: 1, + love: 10, + }, + { + mind: 4, + body: 5, + soul: 6, + work: 8, + play: 5, + love: 5, + }, + { + mind: 6, + body: 3, + soul: 8, + work: 3, + play: 1, + love: 10, + }, + { + mind: 8, + body: 5, + soul: 10, + work: 6, + play: 5, + love: 5, + }, + { + mind: 4, + body: 7, + soul: 8, + work: 1, + play: 1, + love: 10, + }, +]; diff --git a/src/modules/common/styles/ChartComponent.css b/src/modules/common/styles/ChartComponent.css new file mode 100644 index 0000000..ca2493a --- /dev/null +++ b/src/modules/common/styles/ChartComponent.css @@ -0,0 +1,89 @@ +@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap'); +@import url('https://fonts.googleapis.com/css?family=Nunito&display=swap'); + +.containerChart { + display: flex; + justify-content: center; + align-items: left; + flex-flow: column; + max-width: 740px; + margin: 20px; +} + +.titleChart { + color: #727272; + font-family: 'Open Sans'; + font-weight: normal; + font-size: 28px; + line-height: 38px; +} + +.headerChart { + display: flex; + flex-direction: row; + place-content: space-between; +} + +.selectOptionChart { + height: 30px; + width: 160px; + padding-left: 10px; + align-self: center; + border: hidden; + background-color: inherit; + color: #727272; + font-family: 'Nunito'; + font-size: 16px; + line-height: 22px; + text-align: right; + -webkit-appearance: none; + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAPsAAAD7AFKhtV5AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAALFQTFRF////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcidCwAAADp0Uk5TAAEDBAUGCw0PGx4gISstLkBER11fYGFnaWtsk5eYmp2eo6Spq6ytrq+wsbK2zM/d4OPw8vP09vf4/uaZfbgAAAHzSURBVHja7drZTsJQFIXhBXWeBwQF5wlFAUUm1/s/mDeYgPS057RnbzXZ67Jp8/1J26QXBWw2m81ms9lsNpvN9jeW7J8c71a1tOru8cl+Mndg53VKkuP2pga/2R6T5PR1Z3ag0hxxtkFd3q8PvrVRswIAuODcboTvQ/VmXjsHgCMu7Fa0oHq7qB0CG+/UK/jp820dZ6RawZJPnuKaagUpPq/Qo1ZBms8ehlQqSPU5RJc6Bek+u7ikSoHD5yUa1Chw+Wxgra9Q4PT7a8DBp3iB0/88AIAWhQucPlsAgORetsDt38++CWQL8n3ZAh9fssDPlytw+w/J4pkyBf6+TEGIL1EQ5scvCPVjF4T7cQuK+DELivnxCtz+Y5J9ZZyC4n6cgjJ+jIJyfvmCsn7ZgvJ+uYIMf8X/KS5eEMcvXhDLL1rg9tuBfrGCmH6Rgrh+eEFsP7Qgvh9WIOGHFMj4/gVSvm+BnO9X4PafSvs+BbJ+foG0n1cg72cXaPiZBSp+VoGOH14Q2w8teI7uhxVI+CEFMr5/gZTvWyDn+xVI+j4Fsn5+gbSfVyDvZxd0FPysAh3fXaDluwr0/PSCzirwmwW6/nKBtg8kdwvvv7oPoPHxzQ9nf6Nob+tlQpKTl2381pK9Wm0vgc1ms9lsNpvNZrPZ/vW+AEbmqyT8wqAZAAAAAElFTkSuQmCC'); + background-position: calc(100% - 10px) calc(1em - 7px); + background-size: 13px 13px; + background-repeat: no-repeat; +} + +.canvasChartContainer{ + border-radius: 20px; + background-color: #FFFFFF; + box-shadow: 0 0 32px 0 rgba(4,11,35,0.08); +} + +.textChartBody{ + display: flex; + width: 95%; + padding-left: 5px; + color: #727272; + font-family: 'Nunito'; + font-size: 10px; + letter-spacing: 2px; + line-height: 14px; + text-align: center; + margin-top: 25px; +} +.lineChartBody{ + height: 2px; + width: 32px; + border: 0; +} + +.setBlurEffectChart{ + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: white; + filter: blur(8px); + z-index:0; +} +.textDisabledChart{ + width: 42%; + z-index:1; + font-size: 40px; + color: #727272; + font-family: 'Nunito'; + text-align: center; + position: absolute; + justify-content: center; + align-items: center; +}