ChartJS - Why is the transition on the first dataset bad? - reactjs

So I have react-datepicker to choose the dates of my data, the data that is returned looks like this:
{clicks: 6, date: "2022-05-15", link: "instagram"},
{clicks: 5, date: "2022-05-15", link: "google"}...
On load and every change in the datePicker, I have this function which sets the data variables, the date will be moment():
const onChangeClick = (dates: any) => {
const [start, end] = dates;
setStartDateClick(start);
setEndDateClick(end);
if (start & end) {
let dates = {
dateFrom: moment(start).format('YYYY-MM-DD'),
dateTo: moment(end).format("YYYY-MM-DD")
}
_statisticsService.getClickStats(dates).then((response: any) => {
response.data.map((e: any) => {
clicksData[e?.link].push(e)
})
setInstaDataClick(clicksData['instagram'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setFacebookDataClick(clicksData['facebook'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setGoogleDataClick(clicksData['google'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setTripadvisorDataClick(clicksData['tripadvisor'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setWebsiteDataClick(clicksData['website'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
})
setLabelsClick(getDaysArray(clicksData[0]?.date, clicksData[clicksData.length - 1].date))
}
};
And later I use this values in the datasets:
const data = {
labels: labelsClick,
datasets: [
{
label: 'Instagram',
data: instaDataClick,
borderColor: '#c32aa3',
backgroundColor: '#c32aa3',
},
{
label: 'Facebook',
data: facebookDataClick,
borderColor: '#1877f2',
backgroundColor: '#1877f2',
},
{
label: 'Google',
data: googleDataClick,
borderColor: '#ea4335',
backgroundColor: '#ea4335',
},
{
label: 'Tripadvisor',
data: tripadvisorDataClick,
borderColor: '#34e0a1',
backgroundColor: '#34e0a1',
},
{
label: 'Website',
data: websiteDataClick,
borderColor: '#010101',
backgroundColor: '#010101',
},
],
};
And at the end this is how I preview the Line chart
<div className='datepicker-container'>
<DatePicker
onChange={onChangeClick}
startDate={startDateClick}
endDate={endDateClick}
selectsRange
placeholderText='Select Date'
isClearable
/>
</div>
<div className='chart'>
<Line options={options} data={data} />
</div>
The options for this line look like this, x axis is the date and y is the clicks:
const options: ChartOptions<'line'> = {
responsive: true,
scales: {
y: {
title: {
display: true,
text: 'Date'
},
ticks: {
precision: 0,
}
},
x: {
type: 'time',
title: {
display: true,
text: "Date"
},
time: {
unit: 'day',
stepSize: 1,
displayFormats: {
day: "MMM DD"
}
}
},
},
parsing: {
xAxisKey: 'date',
yAxisKey: 'clicks',
},
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Click statistics',
},
},
};
And now THE PROBLEM:
On first load, the first line in the dataset, in this case Instagram, shows like this:
The instagram line has a different animation, what could be the problem?
[edit]: Changing the animation in options won't affect the instagram line, it will still come from the left, depending on the animation, it will come either slower or faster, but always from left.

For anyone who has the same issue in the future, here is the solution:
The problem with my code was that the first state of the instaDataClick variable was an empty array, it should have a date property, because the x axis is the date.
so the variable should look something like this:
const initialStartDate = new Date(moment().subtract(10, 'd').format('YYYY-MM-DD'))
const initialEndDate = new Date(moment().format('YYYY-MM-DD'))
const [instaDataClick, setInstaDataClick] = useState(
getDaysArray(initialStartDate.toString(), initialEndDate.toString()).map((e)=>{
return {clicks: null, date: e}
})
)
the function getDaysArray:
const getDaysArray = function (start: any, end: any) {
for (var arr = [], dt = new Date(start); dt <= new Date(end); dt.setDate(dt.getDate() + 1)) {
arr.push(new Date(dt).toString().substr(4, 6));
}
return arr;
};
And by doing this, we're setting the initial position of the line.

Related

Fullcalendar resource timeline does not show last week of the year

I'm trying resource timeline custom view like below. The problem is last week of the year does not show and the last week of the previous year show up wrongly. Am i doing something wrong or this is a fullcalendar bug i don't know.
Sorry for my bad english. Any help would be greatly appriciated!
Thanks!
Here is my fullCalendar settings
{
initialDate: moment().startOf('year')._d,
customButtons: {
eventFilterButton: {
text: 'Filtrele',
click: function(e) {
setFilterPopupPosition({
top: e.clientY + window.scrollY,
left: e.clientX - 220,
});
setFilterPopupvisibility(true);
}
}
},
slotMinWidth: 40,
showNonCurrentDates: false,
fixedWeekCount: true,
slotLabelFormat: [
{ month: "long"}, // top level of text
{ week: "number" } // lower level of text
],
slotLabelContent: (arg) => {
if ( Number(arg.text) ) {
const result = getWeekOfMonth(arg.date);
return result;
}
},
height: window.innerHeight - 220,
eventMaxStack: 5,
scrollTimeReset: false,
stickyHeaderDates: true,
eventBackgroundColor: 'transparent',
eventBorderColor: 'transparent',
selectable: true,
initialView: 'resourceTimelineMonths',
locale: 'en',
weekends: true,
defaultView: 'resourceTimeline',
eventContent: renderEventContent,
views: {
resourceTimelineMonths: {
type: 'resourceTimeline',
duration: { months: 12 },
buttonText: 'Aylık Görünüm',
slotDuration: {
weeks: 1,
}
},
},
headerToolbar: {
right: 'today eventFilterButton' // prev,next
},
resourceLabelContent: function (renderInfo) {
return (<CalendarResourceItem resources={calendarResources.current} forceUpdateValue={forceUpdateValue} expanded={rersourceGroupExpandState.current[renderInfo.resource.id]} renderInfo={renderInfo} resourceMenuClickCallback={resourceMenuClickCallback} expandCallback={handleResourceExpand}/>)
},
resourceAreaWidth: '222px',
resourceAreaHeaderContent: yearPicker,
dayHeaderFormat: {
day: 'short',
weekday: 'short',
month: 'numeric',
week: 'numeric',
omitCommas: true,
},
allDayDefault: true,
droppable: true,
editable: true,
resourceGroupLabelContent: function (renderInfo) {
console.log('resourceGroupLabelContent', renderInfo)
return (
<div
className="schedule--resourceLabel"
onClick={() => handleResourceLAbelClick(renderInfo.groupValue)}
>
{renderInfo.groupValue}{' '}
{rersourceGroupExpandState.current[renderInfo.resource.id] ? (
<Icon rotate={180} component={IconChevronUp} />
) : (
<Icon component={IconChevronUp} />
)}
</div>
)
},
resourceLabelDidMount: (info) => {
setTimeout(() => {
// console.warn('************************ resourceGroupLabelDidMount --------------------------', info)
resourceExpanderElementList.push({
selector: info.resource.id,
el: info.el.querySelector('.fc-datagrid-expander'),
})
taskAssignmentStore.dispatch({
type: 'SET_RESOURCE_EXPAND_LABELS',
resourceLabels: resourceExpanderElementList,
})
rersourceGroupExpandState.current[info.resource.id] = false
});
},
}

Render React Component Inside of Highcharts Graph

I'd like to be able to render a React Component inside of a HighCharts Graph. I'd like to render the component DatePicker inside my HighCharts Graph, rather than being above it like it currently is? I'm using the HighCharts Official React Wrapper, and this is what my code looks like:
function Graph(props) {
const [startDate, setStartDate] = useState(new Date('1969-11-19 PST'));
const [bx, setBx] = useState([]);
const [by, setBy] = useState([]);
const [bz, setBz] = useState([]);
async function getData(table, year, month, day) {
...
}
useEffect(() => {
...
}, [startDate])
var options = {
chart: {
events: {
load: function() {
this.showLoading();
setTimeout(this.hideLoading.bind(this), 2000);
}
}
},
lang: {
noData: "Awaiting for data"
},
rangeSelector: {
inputEnabled: false,
buttons: [
{
type: 'all',
text: '1d',
title: 'View 1 Day'
},
{
type: 'minute',
count: 10,
text: '10m',
title: 'View 10 minutes'
},
{
type: 'minute',
count: 1,
text: '1m',
title: 'View 1 minutes'
},
{
type: 'second',
count: 30,
text: '30s',
title: 'View 30 seconds'
},
]
},
chart: {
zoomType: 'x'
},
title: {
text: props.title
},
xAxis: {
type: 'datetime'
},
time: {
timezoneOffset: 8*60
},
series: [
{
marker: {
enabled: true,
radius: 2
},
color: 'red',
name: 'Bx',
pointStart: startDate.getTime(),
pointInterval: 1000,
data: bx // data has to be [x, y] pairs
},
{
marker: {
enabled: true,
radius: 2
},
color: 'blue',
name: 'By',
pointStart: startDate.getTime(),
pointInterval: 1000,
data: by
},
{
marker: {
enabled: true,
radius: 2
},
color: 'green',
name: 'Bz',
pointStart: startDate.getTime(),
pointInterval: 1000,
data: bz
}
]
}
const isValidDate = (date) => {
date.getTime();
const origDate = new Date('1969-11-19 PST');
const finalDate = new Date('1970-04-03 PST');
console.log("GET DATE", date.getTime()) // getting number day of month
const bool = origDate.getTime() <= date.getTime() && date.getTime() <= finalDate.getTime();
return bool;
}
return (
<React.Fragment>
<DatePicker
selected={startDate}
filterDate={isValidDate}
onChange={(date) => setStartDate(date)}
/>
<HighchartsReact
highcharts={Highcharts}
constructorType={'stockChart'}
options={options}
/>
</React.Fragment>
);
}
I see this example from the HighCharts React Official Wrapper Github, but I'm still a bit confused by it and am wondering if I could simply do the same thing with a functional component. https://codesandbox.io/s/1o5y7r31k3
I've made a codesandbox of my code here https://codesandbox.io/s/friendly-moon-y8guc

Filter/update already rendered chart.js in react.js

I'm new here, because I have decided to dive into programming, so I can fill free time between treatments in the hospital. I'm absolutely new in the programming field with no previous coding background.
The summary:
I am working on a simple page, where I fetch data from a Postgre database that is visualized using chart.js. The page is a built-in cube.js playground, using a Reactjs template. Currently, I can display various charts depending on my criteria. Like display monthly sales of a certain product in Australia. Or, I can display a second chart with daily sales in the countries I choose. Or ignore all sales that were in a certain currency. Right now, every new criterion means I have to use cube.js playground and generate a new chart on the page.
What I would like to achieve is to be able to filter already rendered charts (by a dropdown button outside the chart or inside the chart, it doesn't matter too much) and having the chart updated. Something like the pictures here, where the OP can filter charts based on the date, factory, etc.
I've tried Chart.js Example with Dynamic Dataset, chart.js tutorial on
Updating Charts and various others. But I can't seem to be able to implement any of those solutions in my code.
Here is my current code:
ChartRenderer.js
import React from "react";
import PropTypes from "prop-types";
import { useCubeQuery } from "#cubejs-client/react";
import Row from "react-bootstrap/Row";
import Spin from "react-bootstrap/Spinner";
import Col from "react-bootstrap/Col";
import { Statistic, Table } from "antd";
import { Line, Bar, Pie } from "react-chartjs-2";
const COLORS_SERIES = [
"#931F1D",
"#141446",
"#7A77FF",
];
const commonOptions = {
maintainAspectRatio: true,
};
const TypeToChartComponent = {
line: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
borderColor: COLORS_SERIES[index],
backgroundColor: COLORS_SERIES[index],
fill: false,
tension: 0.4,
})),
};
const options = { ...commonOptions };
return <Line data={data} options={options} />;
},
bar: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES[index],
fill: false,
})),
};
const options = {
...commonOptions,
scales: {
xAxes: [
{
stacked: true,
},
],
},
};
return <Bar data={data} options={options} />;
},
area: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES[index],
fill: true,
})),
};
const options = {
...commonOptions,
scales: {
yAxes: [
{
stacked: true,
},
],
},
};
return <Line data={data} options={options} />;
},
pie: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES,
hoverBackgroundColor: COLORS_SERIES,
borderColor: COLORS_SERIES,
hoverBorderColor: "white",
hoverOffset: 10,
})),
};
const options = { ...commonOptions };
return <Pie data={data} options={options} />;
},
number: ({ resultSet }) => {
return (
<Row
type="flex"
justify="space-around"
align="middle"
style={{ height: "100%" }}
>
<Col align="left">
{resultSet.seriesNames().map((s) => (
<Statistic value={resultSet.totalRow()[s.key]} />
))}
</Col>
</Row>
);
},
table: ({ resultSet, pivotConfig }) => {
return (
<Table
pagination={false}
columns={resultSet.tableColumns(pivotConfig)}
dataSource={resultSet.tablePivot(pivotConfig)}
/>
);
},
};
const TypeToMemoChartComponent = Object.keys(TypeToChartComponent)
.map((key) => ({
[key]: React.memo(TypeToChartComponent[key]),
}))
.reduce((a, b) => ({ ...a, ...b }));
const renderChart =
(Component) =>
({ resultSet, error }) =>
(resultSet && <Component resultSet={resultSet} />) ||
(error && error.toString()) || <Spin animation="grow text-primary" />;
const ChartRenderer = ({ vizState }) => {
const { query, chartType } = vizState;
const component = TypeToMemoChartComponent[chartType];
const renderProps = useCubeQuery(query);
return component && renderChart(component)(renderProps);
};
ChartRenderer.propTypes = {
vizState: PropTypes.object,
cubejsApi: PropTypes.object,
};
ChartRenderer.defaultProps = {
vizState: {},
cubejsApi: null,
};
export default ChartRenderer;
DashBoardPage.js
import React from "react";
import Col from "react-bootstrap/Col";
import DateRangePicker from 'react-bootstrap-daterangepicker';
import ChartRenderer from "../components/ChartRenderer";
import Dashboard from "../components/Dashboard";
import DashboardItem from "../components/DashboardItem";
const DashboardItems = [
{
id: 0,
name: "Sold by customers today",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
granularity: "day",
dateRange: "Today",
},
],
order: {},
dimensions: [],
filters: [
{
member: "PostgreSqlTable.operation",
operator: "contains",
values: ["Sell"],
},
],
},
chartType: "number",
},
},
{
id: 1,
name: "Bought by customers today",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
dateRange: "Today",
},
],
order: {},
filters: [
{
member: "PostgreSqlTable.operation",
operator: "contains",
values: ["Buy"],
},
],
},
chartType: "number",
},
},
{
id: 2,
name: "Money in the wallet",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
},
],
order: {
"PostgreSqlTable.amount": "desc",
},
dimensions: ["PostgreSqlTable.currency"],
filters: [
{
member: "PostgreSqlTable.currency",
operator: "equals",
values: ["EUR"],
},
],
},
chartType: "number",
},
},
{
id: 3,
name: "Monthly sales filtered by week",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
granularity: "week",
dateRange: "This month",
},
],
order: {
"PostgreSqlTable.amount": "desc",
},
dimensions: ["PostgreSqlTable.operation"],
filters: [
{
member: "PostgreSqlTable.operation",
operator: "notContains",
values: ["Register"],
},
],
limit: 5000,
},
chartType: "line",
},
},
{
id: 4,
name: "Countries with most customers",
vizState: {
query: {
measures: ["PostgreSqlTable.count"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
},
],
order: {
"PostgreSqlTable.count": "desc",
},
dimensions: ["PostgreSqlTable.country"],
limit: 5,
},
chartType: "pie",
},
},
];
const DashboardPage = () => {
const dashboardItem = (item) => (
<Col className="col-4">
<DashboardItem title={item.name}>
<ChartRenderer vizState={item.vizState} />
</DashboardItem>
</Col>
);
const Empty = () => (
<div
style={{
textAlign: "center",
padding: 12,
}}
>
<h2>
No items added
</h2>
</div>
);
return DashboardItems.length ? (
<Dashboard dashboardItems={DashboardItems}>
{DashboardItems.map(dashboardItem)}
</Dashboard>
) : (
<Empty />
);
};
export default DashboardPage;
At this moment, I have no clue how to implement the filter in react.js+chart.js. I have also tried to update the array, but no success (I followed also this tutorial)
I would be most grateful for any help.
Thank you in advance, stay healthy.
Tatsu
I'd recommend using the <QueryBuilder/> component available in the Cube.js-React integration; this component provides a similar interface as that in the Developer Playground.

react-graph-vis - Grapg is not re rendering even ofter state changes

When I try to update the state on the hover event, the actual state value is getting changed but the graph is not re-rendering.
in the console, I am able to see the node label is changed to sample. but the graph is not rerendering.
Here is my react function based component.
import React, { useEffect, useState } from 'react';
import Graph from 'react-graph-vis';
import './vis-network.css';
function RelationGraph1() {
const [graph, setGraph] = useState({
nodes: [
{
id: 1,
label: 'Node 1',
title: '',
},
{ id: 2, label: 'Node 2', title: '' },
{ id: 3, label: 'Node 3', title: '' },
{ id: 4, label: 'Node 4', title: '' },
{ id: 5, label: 'Node 5', title: '' },
],
edges: [
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 2, to: 4 },
{ from: 2, to: 5 },
],
});
const options = {
layout: {
hierarchical: false,
},
edges: {
color: '#1D1D1D',
},
interaction: {
hover: true,
navigationButtons: true,
tooltipDelay: 0,
},
nodes: {
borderWidth: 0,
borderWidthSelected: 0,
color: '#0262C4',
shape: 'circle',
size: 1,
shadow: {
enabled: true,
color: 'rgba(0,0,0,0.5)',
size: 10,
x: 5,
y: 5,
},
font: {
color: '#fff',
size: 13,
bold: {
mod: 'bold',
},
},
},
};
const events = {
select: function (event) {
var { nodes, edges } = event;
console.log('Selected nodes:');
console.log(nodes);
console.log('Selected edges:');
console.log(edges);
},
showPopup: (id) => { // node id
const data = graph.nodes.map((el) => {
if (el.id === id) {
el.label = `sample node name`;
}
return el;
});
setGraph({ ...graph, nodes: data });
},
};
return (
<Graph
graph={graph}
options={options}
events={events}
style={{ height: '450px' }}
/>
);
}
export default RelationGraph1;
Really Appriciate for the help. Thanks !
I was able to update the label in hoverNode event like this:
hoverNode: (e) => {
const data = graph.nodes.map((el) => {
if (el.id === e.node) return { ...el, label: "sample node name" };
else return el;
});
const temp = { ...graph };
temp.nodes = data;
setGraph(temp);
},
Sample: https://codesandbox.io/s/long-bird-4h444?file=/src/App.js:1235-1501

Unable to convert timestamp to hours minutes and secondes in React apex-chart

I am using react apex chart to create a chart that will display the average response time for each agent.
I have managed to get the result in a timestamp format but i am unable to convert that into hours, minutes and seconds to display that in yaxis, i have checked the documentation docs link but they are giving examples for date time only.
here is the result that i am getting with the component bellow
import React, { useState } from 'react';
import Chart from 'react-apexcharts';
const AvgResponseTimeChart = (props) => {
const { prod_data } = props;
const [ data, setData ] = useState([
{
x: 'Agent one',
y: 1589670005
},
{
x: 'Agent one',
y: 1589670307
}
]);
const [ series, setSeries ] = useState([ { data } ]);
const [ options, setOptions ] = useState({
chart: {
type: 'bar',
height: 350
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '25%',
endingShape: 'rounded'
}
},
dataLabels: {
enabled: false
},
stroke: {
show: true,
width: 2,
colors: [ 'transparent' ]
},
xaxis: {
type: 'category'
},
yaxis: {
labels: {
datetimeFormatter: {
formatter: function(value, timestamp) {
return new Date(timestamp).toLocaleTimeString();
}
}
}
},
fill: {
opacity: 1
},
tooltip: {
y: {
formatter: function(value, timestamp) {
return new Date(timestamp);
}
}
}
});
return (
<div id="chart">
<Chart options={options} series={series} type="bar" height={350} />
</div>
);
};
export default AvgResponseTimeChart;
I have searched for similar issues without success if, someone can help me with that i will be really grateful
Try to add lables to yaxis in chartOptions this way:
labels: {
show: true,
formatter: (val) => { return new Date(val); }
}
And remove the tooltip as well.

Resources