I'm using react-big-calendar to create and update event times by dragging them.
I'm using useState to handle the events, and the events themselves are static.
The problem is, they're not loading at all.
And I'm getting the following message in the console:
"Warning: Failed prop type: Invalid prop events[0] of type array supplied to Calendar, expected object."
If anyone could help, I'll appreciate it.
Thanks in advance.
import { useEffect, useState } from 'react';
import { Calendar, dateFnsLocalizer } from 'react-big-calendar';
import withDragAndDrop from "react-big-calendar/lib/addons/dragAndDrop";
import moment from 'moment';
import { format, parse, startOfWeek, getDay } from 'date-fns';
import enUS from 'date-fns/locale/en-US';
import 'react-big-calendar/lib/css/react-big-calendar.css';
import "react-big-calendar/lib/addons/dragAndDrop/styles.css";
const DnDCalendar = withDragAndDrop(Calendar);
const locales = {
'en-US': enUS,
};
const localizer = dateFnsLocalizer({
format,
parse,
startOfWeek,
getDay,
locales
});
const initialEvents = [
{
id: 0,
title: "All Day Event very long title",
allDay: true,
start: new Date(2015, 3, 0),
end: new Date(2015, 3, 1)
},
{
id: 1,
title: "Long Event",
start: new Date(2015, 3, 7),
end: new Date(2015, 3, 10)
},
];
const EventComponent = ({ start, end, title }) => {
return (
<>
<p>{title}</p>
<p>{start}</p>
<p>{end}</p>
</>
);
};
const EventAgenda = ({ event }) => {
return (
<span>
<em style={{ color: "magenta" }}>{event.title}</em>
<p>{event.desc}</p>
</span>
);
};
const HomePage = () => {
const [events, setEvents] = useState([initialEvents]);
const onEventDrop = ({ event, start, end, allDay }) => {
console.log("event clicked");
console.log(start, event, end, allDay);
};
const addEvent = ({ event, start, end, allDay }) => {
const newEvent = {
id: events.length,
title: "New event",
start: new Date(new Date(start).setHours(new Date().getHours() - 3)),
end: new Date(new Date(end).setHours(new Date().getHours() + 3)),
desc: "This is a new event"
}
setEvents(state => [ ...state, newEvent ]);
};
return (
<DnDCalendar
defaultView='week'
selectable
events={events}
startAccessor="start"
endAccessor="end"
defaultDate={moment().toDate()}
min={new Date(2008, 0, 1, 1, 0)} // 8.00 AM
max={new Date(2008, 0, 1, 23, 59)}
localizer={localizer}
toolbar
resizable
onEventDrop={onEventDrop}
onSelectSlot={addEvent}
onSelectEvent={event => alert(event.desc)}
components={{
event: EventComponent,
agenda: {
event: EventAgenda
}
}}
/>
)
}
export default HomePage
as you mentioned "And I'm getting the following message in the console: "Warning: Failed prop type: Invalid prop events[0] of type array supplied to Calendar, expected object." you have to pass "initialEvents" directly to useState instead of "array of array"
const [events, setEvents] = useState(initialEvents);
You need to change few things.
First, since all your events are in 2015, replace this line of code :
defaultDate={moment().toDate()}
with :
defaultDate={new Date(2015, 3, 1)}
Second, as mentioned in previous answer, since your initialEvents is already array, you set to be initial state like this :
const [events, setEvents] = useState(initialEvents); // you don't need extra []
With these two correction, you shoud be able to see event with tittle 'All day Event very long tittle' on first page, and second event on next page in week view.
Related
The example in this sandbox is a contrived example, but it illustrates the point.
Clicking the "Add column" button is supposed to add a new column. It works the first time, but doesn't work after that. You'll notice from the log that this issue has to do with the fact that columns is always in its original state. Therefore, a column is always being added to this original state, not the current state.
I imagine this issue is related to the fact that the column header is being re-created on each call to renderHeader, but I'm unsure about how to pass the state to the newly created header component.
There are 2 ways to update your state:
The first way:
setColumns(newColumns)
The second way:
setColumns(columns => newColumns)
You should use the second way because the new state depend on the current state.
onClick={() => {
setColumns((columns) => {
console.log(columns.map((column) => column.field));
const newColumnName = `Column ${columns.length}`;
const newColumns = [...columns];
newColumns.splice(
columns.length - 1,
0,
createColumn(newColumnName)
);
return newColumns;
});
}}
Your component:
import React, { useState } from "react";
import { DataGrid } from "#mui/x-data-grid";
import Button from "#mui/material/Button";
import "./styles.css";
export default function App() {
const [rows] = useState([
{ id: 1, "Column 1": 1, "Column 2": 2 },
{ id: 2, "Column 1": 3, "Column 2": 3 },
{ id: 3, "Column 1": 4, "Column 2": 5 }
]);
const createColumn = (name) => {
return {
field: name,
align: "center",
editable: true,
sortable: false
};
};
const [columns, setColumns] = useState([
createColumn("Column 1"),
createColumn("Column 2"),
{
field: "Add a split",
width: 150,
sortable: false,
renderHeader: (params) => {
return (
<Button
variant="contained"
onClick={() => {
setColumns((columns) => { // <== use callback
console.log(columns.map((column) => column.field));
const newColumnName = `Column ${columns.length}`;
const newColumns = [...columns];
newColumns.splice(
columns.length - 1,
0,
createColumn(newColumnName)
);
return newColumns;
});
}}
>
Add column
</Button>
);
}
}
]);
return (
<div className="App">
<DataGrid
className="App-data-grid"
rows={rows}
columns={columns}
disableSelectionOnClick
disableColumnMenu
/>
</div>
);
}
https://codesandbox.io/s/mui-sandbox-forked-1im0p?file=/src/App.js:0-1562
I think this has to do with the way the closure for (params) => {...} captures the value of columns. At the same time, you have this strange circular thing going on: useState() returns columns and also uses columns in one of its parameters. You will need to find a way to break this circularity.
I have react component in which I am showing data in the table, also have Select / dropdown. The table and select are in same component. I need to refresh table component soon the value change in the dropdown. My implementation does refresh the table and call API but there is delay when that happened. I am not sure if I have implemented correctly?
the idea is eziSearchCriteria is in useState and onChange event I am assign value to it.
const MyComponent = () => {
const[eziSearchCriteria, setEziSearchCriteria] = useState<IEziStatusSearchCriteriaForm>();
const eziSitesStatusCovers = [
{ label: 'UNSCHEDULED', value: 'UNSCHEDULED' },
{ label: 'COVERED', value: 'COVERED' },
{ label: 'PART COVERED', value: 'PART COVERED' },
];
useEffect(() =>{
setInitialPageLoad(true);
setDefaultSearchCriteria();
},[]);
const handleSearchFilter = (event) =>{
if(event!=null){
eziSearchCriteria.coverStatus = event.value;
setEziSearchCriteria(eziSearchCriteria);
}
}
return (
<div>
<div className="searchFilter">
<Select
options={eziSitesStatusCovers}
onChange = {handleSearchFilter}
/>
</div>
{ eziSearchCriteria ? (
<TableItems
url={url}
apiUrl ={api.eziTrackerStatus}
columns={columns}
customParams= {eziSearchCriteria}
key={eziSearchCriteria.coverStatus}
></TableItems> ) : null}
</div>
);
};
....
export interface IEziStatusSearchCriteriaForm{
startTime: string,
endTime: string,
scheduleId?: number,
coverStatus: string
}
I have found the issue and answer.
I was changing one value in the object eziSearchCriteria but object is same/ reference variable and react state not considering to render on this change. I created new object and assign to eziSearchCriteria and it worked straight away.
const handleSearchFilter = (event) =>{
if(event!=null){
eziSearchCriteria.coverStatus = event.value;
var searchCriteria2 : IEziStatusSearchCriteriaForm = {
startTime: "12-08-2020", //MM:DD:YYYY
endTime: "12-09-2020",
schedAction_Active: "Active",
coverStatus: event.value
}
setEziSearchCriteria(searchCriteria2);
}
}
I am trying to build a calendar app with Full Calendar and Elastic Ui, I use state to manage data/event input, using form to add event to the calendar, and selecting the date on calendar also update the form value. The app is partly working now, I can input events to the calendar, but only after I select/click on the date from the calendar then the form would work fine. However if I input on the text field or date picker in the form before clicking on the calendar, the following error appear and the whole page turn blank.
Maximum update depth exceeded.
The above error occurred in the DatePicker component:
Can't perform a React state update on an unmounted component.
anyone have any idea what's happening? Or how can I find the root of the problem?
There are also two error when the webapp first loaded:
index.js:1 Warning: Can't call setState on a component that is not yet
mounted. This is a no-op, but it might indicate a bug in your
application. Instead, assign to this.state directly or define a
state = {}; class property with the desired state in the EuiIcon
component. console. #
index.js:1 Warning: Can't
call setState on a component that is not yet mounted. This is a no-op,
but it might indicate a bug in your application. Instead, assign to
this.state directly or define a state = {}; class property with
the desired state in the CalendarDataProvider component.
import React, { Component, useState } from 'react';
import FullCalendar from '#fullcalendar/react'
import dayGridPlugin from '#fullcalendar/daygrid'
import timeGridPlugin from '#fullcalendar/timegrid'
import interactionPlugin from "#fullcalendar/interaction"
import googleCalendarPlugin from '#fullcalendar/google-calendar'
import moment from 'moment'
import {EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiForm, EuiFormRow, EuiButton, EuiSwitch,
EuiDatePicker, EuiDatePickerRange, EuiRadioGroup
} from '#elastic/eui'
require('dotenv').config()
const AddEventBar = (props) => {
const [isDisabled, setDisabled] = useState(false)
const [StartRepeatDate, setStartRepeatDate] = useState(null)
const [EndRepeatDate, setEndRepeatDate] = useState(null)
const [radioIdSelected, setRadioIdSelected] = useState(null);
const handleChangeStart = (date) => {
setStartRepeatDate(date)
}
const handleChangeEnd = (date) => {
setEndRepeatDate(date)
}
const radioHandler = (optionId) => {
setRadioIdSelected(optionId);
};
const radios = [
{
id: 'daily',
label: 'daily',
},
{
id: 'weekly',
label: 'weekly',
},
];
return (
<EuiFlexGroup>
<EuiFlexItem>
<EuiForm>
<EuiFormRow label='Event Title'>
<EuiFieldText
onChange={props.titleHandler}
aria-label="Use aria labels when no actual label is in use"
/>
</EuiFormRow>
<EuiFormRow label = 'Event Date'>
<EuiDatePicker
placeholder='Start'
selected={moment(props.start_date)}
onChange={props.startDatePickerHandler}
aria-label="Start date"
/>
</EuiFormRow>
<EuiFormRow label='Event Time'>
<EuiDatePickerRange
startDateControl = {
<EuiDatePicker
showTimeSelect
showTimeSelectOnly
selected={moment(props.start_date + ' ' + props.start_time)}
onChange={props.startDatePickerHandler}
timeFormat="hh:mm a"
dateFormat="hh:mm a"
/>}
endDateControl = {
<EuiDatePicker
showTimeSelect
showTimeSelectOnly
selected={moment(props.start_date + ' ' + props.end_time)}
onChange={props.endDatePickerHandler}
timeFormat="hh:mm a"
dateFormat="hh:mm a"
/>
}
>
</EuiDatePickerRange>
</EuiFormRow>
<EuiFormRow>
<EuiSwitch
label="Repeat Event"
checked={isDisabled}
onChange={(e) => setDisabled(e.target.checked)}
/>
</EuiFormRow>
<EuiFormRow>
<EuiDatePickerRange
startDateControl={
<EuiDatePicker
placeholder='Start'
disabled={!isDisabled}
selected={StartRepeatDate}
onChange={handleChangeStart}
startDate={StartRepeatDate}
endDate={EndRepeatDate}
isInvalid={StartRepeatDate > EndRepeatDate}
aria-label="Start date"
/>
}
endDateControl={
<EuiDatePicker
placeholder='End'
disabled={!isDisabled}
selected={EndRepeatDate}
onChange={handleChangeEnd}
startDate={StartRepeatDate}
endDate={EndRepeatDate}
isInvalid={StartRepeatDate > EndRepeatDate}
aria-label="End date"
/>
}
/>
</EuiFormRow>
<EuiFormRow>
<EuiRadioGroup
disabled={!isDisabled}
options={radios}
idSelected={radioIdSelected}
onChange={(id) => radioHandler(id)}
name="radio group"
/>
</EuiFormRow>
<EuiFormRow>
<EuiButton onClick={props.addEventHandler} type='submit'>
Add Event
</EuiButton>
</EuiFormRow>
</EuiForm>
</EuiFlexItem>
</EuiFlexGroup>
)
}
class Calendar extends Component {
state = {
selectInfo: null,
// dateClickInfo: null,
allDay: false,
title: null,
start_date: moment(),
end_date: moment(),
start_time: null,
end_time: null,
}
addEventHandler = (state) => {
if(this.state.title === null){
alert('Please enter title')
}
else if(this.state.selectInfo === null){
alert('Please select event date')
}
else {
if(this.state.start_time || this.state.end_time){
let calendarApi = this.state.selectInfo.view.calendar
calendarApi.addEvent({
title: this.state.title,
id: new Date(),
start: this.state.start_date.concat('T', this.state.start_time),
end: this.state.end_date.concat('T', this.state.end_time),
})
} else {
let calendarApi = this.state.selectInfo.view.calendar
calendarApi.addEvent({
title: this.state.title,
id: new Date(),
start: this.state.start_date,
end: this.state.end_date,
allDay: this.state.selectInfo.allDay,
})
}
}
}
titleHandler = (title) => {
const titleValue = title.target.value
this.setState({title: titleValue})
}
startDatePickerHandler = (date) => {
// date is moment object
const startDate = date.format().slice(0, 10)
const startTime = date.format().slice(11, 16)
// console.log('start date:' + startDate + ', start time:' + startTime)
this.setState({start_date: startDate, start_time: startTime})
}
endDatePickerHandler = (date) => {
// date is moment object
const endDate = date.format().slice(0, 10)
const endTime = date.format().slice(11, 16)
// console.log('end date:' + endDate + ', end time:' + endTime)
this.setState({end_date: endDate, end_time: endTime})
}
handleEventClick = (clickInfo) => {
if (window.confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.remove()
}
}
handleDateSelect = (selectInfo) => {
let calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if(selectInfo) {
if (selectInfo.startStr.length >= 10){
const startStr = selectInfo.startStr.slice(0, 10)
const endStr = selectInfo.endStr.slice(0, 10)
const startTime = selectInfo.startStr.slice(11, 16)
const endTime = selectInfo.endStr.slice(11, 16)
this.setState({selectInfo: selectInfo, start_date: startStr, end_date: endStr, start_time: startTime, end_time: endTime})
} else {
this.setState({selectInfo: selectInfo, start_date:selectInfo.startStr, end_date: selectInfo.endStr})
}
}
}
render() {
return (
<EuiFlexGroup direction='row'>
<EuiFlexItem grow={6}>
<FullCalendar
plugins={[ dayGridPlugin, interactionPlugin, timeGridPlugin, googleCalendarPlugin ]}
googleCalendarApiKey = {process.env.REACT_APP_API_KEY}
events = {{
googleCalendarId: process.env.REACT_APP_GOOGLE_CALENDAR_ID
}}
headerToolbar={{
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
}}
initialView="dayGridMonth"
selectable={true}
editable={true}
expandRows={true}
select={this.handleDateSelect}
// dateClick={dateClickHandler}
eventClick={this.handleEventClick}
/>
</EuiFlexItem>
<EuiFlexItem grow={3}>
<AddEventBar
title={this.state.title}
start_date={this.state.start_date}
end_date={this.state.end_date}
start_time={this.state.start_time}
end_time={this.state.end_time}
titleHandler={this.titleHandler}
addEventHandler={this.addEventHandler}
startDatePickerHandler={this.startDatePickerHandler}
endDatePickerHandler={this.endDatePickerHandler}
></AddEventBar>
</EuiFlexItem>
</EuiFlexGroup>
)
}}
export default Calendar
I am trying to change the view from month to week but in meantime it give me an error . I am new to react and react-big-calendar could someone please help me how to solve this problem . When I change calendar view to month it working fine but when I changed it to week or day so it give me an error. please help me Thanks
Code
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import MyCalendar from 'react-big-calendar';
import CustomToolbar from './toolbar';
import Popup from 'react-popup';
import Input from './input';
import moment from 'moment';
import { fetchEvents, createEvent, updateEvent, deleteEvent } from '../actions';
// Setup the localizer by providing the moment (or globalize) Object to the correct localizer.
const localizer = MyCalendar.momentLocalizer(moment); // or globalizeLocalizer
class Calendar extends Component {
componentDidMount() {
this.props.fetchEvents();
}
//RENDER SINGLE EVENT POPUP CONTENT
renderEventContent(slotInfo) {
const date = moment(slotInfo.start).format('MMMM D, YYYY');
return (
<div>
<p>Date: <strong>{date}</strong></p>
<p>Subject: {slotInfo.taskChage}</p>
<p>Time : {slotInfo.time}</p>
<p>Date : { slotInfo.date}</p>
<p>Notes : {slotInfo.notes}</p>
<p>User Id : {slotInfo.userId}</p>
</div>
);
}
//ON SELECT EVENT HANDLER FUNCTION
onSelectEventHandler = (slotInfo) => {
Popup.create({
title: slotInfo.title,
content: this.renderEventContent(slotInfo),
buttons: {
right: [{
text: 'Edit',
className: 'info',
action: function () {
Popup.close(); //CLOSE PREVIOUS POPUP
this.openPopupForm(slotInfo); //OPEN NEW EDIT POPUP
}.bind(this)
}, {
text: 'Delete',
className: 'danger',
action: function () {
//CALL EVENT DELETE ACTION
this.props.deleteEvent(slotInfo.id);
Popup.close();
}.bind(this)
}]
}
});
}
//HANDLE FUNCITON ON SELECT EVENT SLOT
onSelectEventSlotHandler = (slotInfo) => {
this.openPopupForm(slotInfo); //OPEN POPUP FOR CREATE/EDIT EVENT
}
//POPUP-FORM FUNCTION FOR CREATE AND EDIT EVENT
openPopupForm = (slotInfo) => {
let newEvent = false;
let popupTitle = "Update Event";
if(!slotInfo.hasOwnProperty('id')) {
slotInfo.id = moment().format('x'); //Generate id with Unix Millisecond Timestamp
slotInfo.title = null;
slotInfo.taskChange = null;
slotInfo.message=null;
popupTitle = "Create Event";
newEvent = true;
}
let titleChange = function (value) {
slotInfo.title = value;
};
let taskChange = function (value) {
slotInfo.taskChage = value;
};
let timeChange = function (value) {
slotInfo.time = value;
};
let dateChnage = function ( value){
slotInfo.date=value;
};
let notesChange = function ( value){
slotInfo.notes=value;
};
let userId = function ( value){
slotInfo.userId=value;
};
Popup.create({
title: popupTitle,
content: <div>
<Input onChange={titleChange} placeholder="Subject" />
<Input onChange={taskChange} placeholder="Task Type" />
<Input onChange={timeChange} placeholder="Time"/>
<Input onChange={dateChnage} placeholder="Date"/>
<Input onChange={notesChange} placeholder="Notes"/>
<Input onChange={userId} placeholder="User Id"/>
</div>,
buttons: {
left: ['cancel'],
right: [{
text: 'Save',
className: 'success',
action: function () {
//CHECK THE ID PROPERTY FOR CREATE/UPDATE
if(newEvent) {
this.props.createEvent(slotInfo); //EVENT CREATE ACTION
} else {
this.props.updateEvent(slotInfo); //EVENT UPDATE ACTION
}
Popup.close();
}.bind(this)
}]
}
});
}
//EVENT STYLE GETTER FOR SLYLING AN EVENT ITEM
eventStyleGetter(event, start, end, isSelected) {
let current_time = moment().format('YYYY MM DD');
let event_time = moment(event.start).format('YYYY MM DD');
let background = (current_time>event_time) ? '#DE6987' : '#8CBD4C';
return {
style: {
backgroundColor: background
}
};
}
render() {
return (
<div className="calendar-container">
<MyCalendar
popup
selectable
localizer={localizer}
defaultView={MyCalendar.Views.WEEK}
components={{toolbar: CustomToolbar}}
views={['week']}
style={{height: 600}}
events={this.props.events}
eventPropGetter={(this.eventStyleGetter)}
onSelectEvent={(slotInfo) => this.onSelectEventHandler(slotInfo)}
onSelectSlot={(slotInfo) => this.onSelectEventSlotHandler(slotInfo)}
/>
{console.log(this.props.event)}
<Popup />
</div>
);
}
}
function mapStateToProps(state) {
return {
events: state.events
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
fetchEvents,
createEvent,
updateEvent,
deleteEvent
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Calendar);
For anyone finding this, a few things.
Your localizer handles manipulation of dates under the hood. All dates that you pass in (from your getNow and date props, all the way to your individual event.start and event.end dates) should be true JS Date objects.
Your various method props, for working with events or setting styling or whatever, will receive true JS Date objects, not localizer objects or UTC strings or whatever.
RBC will only work with true JS Date objects. If you pass it moment instances or date strings or something else, it might display but it will operate funky, as RBC will handle all conversions under the hood, and it's use of the date-arithmatic library, internally, works with true JS Dates and not your localizer objects.
const formatted = moment(time).toDate();
I had the same issue before. The solution depends on your date field.
If date field is like start: new Date('2024-09-02T08:00:00-04:00'),
then use this: startAccessor="start"
If date field is like "start": "2024-01-15T08:00:00-04:00",
then use this: startAccessor={(event) => { return new Date(event.start) }}
In the 2nd case, below will throw same error.
startAccessor={(event) => { return moment(event.start) }}
Hope this helps.
Make sure that you have the correct values for start and end keys in your event object,
your event object should be like :
data = [
{
title: "My event",
allDay: false,
start: new Date(2020, 10, 25, 10, 0), // 10.00 AM
end: new Date(2020, 10, 25, 11, 0), // 2.00 PM
}
]
this happens when the date you are trying to display is "String" when actually "date" is an "Object" type, then you could do something like this:
as event data example convert string to date object.
{
id: 8,
title: "Meeting",
start: new Date("2022-05-12T08:00:00.000Z"),
end: new Date("2022-05-12T09:00:00.000Z")
}
According to the documentation, you can Set the chart-specific events you want to listen to and the corresponding callback. The example is given uses select which works fine (I've setup an example here. The problem comes when I try to use any other chart type.
From the google charts documentation, for a bar chart, I should be able to use a click event:
When I add a click event like so:
{
eventName: "click",
callback({}) {
console.log("clicked");
}
}
Nothing happens, but the select event still works. I've setup a code sandbox here to demonstrate this behavior. This also happens for animationfinish, onmouseover, and every other event I've checked.
Looks like rakannimer already answered this in #263 on the GitHub repository, but figured I'd answer this anyway in case anyone else was having this issue.
As this stack overflow answer does a great job of explaining, the ready event must be fired before chart events (like those in the screenshot) can be triggered. Therefore, if you want to use any other event, you have to initiate them in a callback like this:
<Chart
chartType="ScatterChart"
width="80%"
height="400px"
data={data}
options={options}
legendToggle
chartEvents={[
{
eventName: "ready",
callback: ({ chartWrapper, google }) => {
const chart = chartWrapper.getChart();
google.visualization.events.addListener(chart, "onmouseover", e => {
const { row, column } = e;
console.warn("MOUSE OVER ", { row, column });
});
google.visualization.events.addListener(chart, "onmouseout", e => {
const { row, column } = e;
console.warn("MOUSE OUT ", { row, column });
});
}
}
]}
/>
Please check below code snippet for adding click event:
import * as React from "react";
import { Chart } from "react-google-charts";
const chartEvents = [
{
callback: ({ chartWrapper, google }) => {
const chart = chartWrapper.getChart();
chart.container.addEventListener("click", (ev) => console.log(ev))
},
eventName: "ready"
}
];
const data = [
["age", "weight"],
[8, 12],
[4, 5.5],
[11, 14],
[4, 5],
[3, 3.5],
[6.5, 7]
];
const options = {
title: "Age vs. Weight comparison",
hAxis: { title: "Age", viewWindow: { min: 0, max: 15 } },
vAxis: { title: "Weight", viewWindow: { min: 0, max: 15 } },
legend: "none"
};
const ExampleChart = () => {
return (
<Chart
chartType="ScatterChart"
data={data}
options={options}
graphID="ScatterChart"
width="100%"
height="400px"
chartEvents={chartEvents}
/>
);
};
export default ExampleChart;
In addition to the solution provided by #jake, the chartWrapper is no more available in the callback event. In my case, replacing it with wrapper works. Like
<Chart
chartType="ScatterChart"
width="80%"
height="400px"
data={data}
options={options}
legendToggle
chartEvents={[
{
eventName: "ready",
callback: ({ wrapper, google }) => {
const chart = wrapper.getChart();
google.visualization.events.addListener(chart, "onmouseover", e => {
const { row, column } = e;
console.warn("MOUSE OVER ", { row, column });
});
google.visualization.events.addListener(chart, "onmouseout", e => {
const { row, column } = e;
console.warn("MOUSE OUT ", { row, column });
});
}
}
]}
/>```