I am trying to achieve a behavior in simple column chart in React, where I can click on series point and have xAxis label change style. Also, when you click again, that style should be removed. It is the same behavior as we have for mouse over and mouse out but for click event. I can get it to work with mouse events, but not click event.
Is this possible to achieve? This is a code sample I have.
Do the following:
Maintain a state say current update its value with the current axis number upon onClick
Define x-Axis and labels in your config-options
Use formatter function inside label. This function provides current axis value as argument. use it and compare it with your current state and adjust the style dynamically.
Working copy of code sample is here
Code Snippet
class App extends React.Component {
state = {
current: "black"
};
options = {
tooltip: {
enabled: false
},
xAxis: {
labels: {
formatter: item => {
const color = this.state.current === item.value ? "red" : "black";
const fontWeight =
this.state.current === item.value ? "bold" : "normal";
return `<span style="color: ${color}; font-weight: ${fontWeight}">${
item.value
}</span>`;
}
}
},
series: [
{
data: [1, 2, 3, 4],
type: "column",
colors: ["#000000"],
cursor: "pointer",
point: {
events: {
click: (e, x, y) => {
this.setState({ current: e.point.x });
console.log(e.target, e.point.x);
}
// mouseOver: function(e) {
// $(this.series.chart.xAxis[0].labelGroup.element.childNodes[this.x]).css({fontWeight: 'bold'});
// },
// mouseOut: function() {
// $(this.series.chart.xAxis[0].labelGroup.element.childNodes[this.x]).css({fontWeight: 'normal'});
// }
}
}
}
]
};
render() {
return (
<div>
<h2>Highcharts</h2>
<ReactHighcharts config={this.options} />
</div>
);
}
}
Just use the click event function to change the label CSS style. For example:
series: [{
...,
point: {
events: {
click: function() {
var ticks = this.series.xAxis.ticks,
label,
fontWeight;
if (ticks[this.x]) {
label = ticks[this.x].label;
fontWeight = (
label.styles.fontWeight && label.styles.fontWeight === 'bold'
) ? 'normal' : 'bold';
ticks[this.x].label.css({
'fontWeight': fontWeight
});
}
}
}
}
}]
Live demo: http://jsfiddle.net/BlackLabel/6m4e8x0y/4991/
API Reference:
https://api.highcharts.com/highcharts/series.column.events.click
https://api.highcharts.com/class-reference/Highcharts.SVGElement#css
Related
I am using map() method in one of my function in React component to generate a new array which I will use to update the state using setState() method. but when I generate the new array (modified by map method) then state of my component is also getting updated. Eventhough I havent used setState method yet. (ofcourse the change of state is not visible, it didnt updated in browser yet, cuz setState() method hasnt been called yet, but backstage the state is updated.) I dont know WHY!!!
class Color1 extends Component {
state = {
colors: [
{ key: 1, color: 'green' },
{ key: 2, color: 'red' },
{ key: 3, color: 'red' },
{ key: 4, color: 'green' },
],
};
changeColor = color => {
console.log(this.state.colors);
const pehli = this.state.colors.map(e => {
if (e.key === color.key) {
e.color = e.color === 'green' ? 'red' : 'green';
return e;
} else {
return e;
}
});
console.log(
this.state.colors
);** /* For checking the state and it is updated */**
};
// So 'pehli' was supposed to be a new seperate array, it was supposed to mutate the colors array in state. But it does
you are mutating the original object here e.color = e.color === 'green' ? 'red' : 'green'; ... instead make a copy as
changeColor = color => {
console.log(this.state.colors);
const pehli = this.state.colors.map(e => {
const el = {...e}; // a copy here
if (el.key === color.key) {
el.color = el.color === 'green' ? 'red' : 'green';
}
return el
});
console.log(
this.state.colors
);
}
Ag-grid provides onCellClicked event for when a cell is clicked. But I noticed that it doesn't trigger when we click the chevron. I need to capture this event for analytics as a user might click the chevron directly rather than the cell itself. Any idea how to achieve this or what event to use here? I am using detailRenderer to render child rows, if that helps. Here is one of the columnDefs item:
{
headerName: "Line Item",
field: "displayLineNumber",
width: "100px",
sortable: false,
expandable: true,
onCellClicked: (event) => {
event.node.setExpanded(!event.node.expanded);
if (event.node.expanded === true) {
DataLayerUtils.pushEvent(
"click",
{
name: "Open Line Item",
type: "button",
},
{
click: {
category: "Quote Detail Table Interactions",
},
}
);
} else {
DataLayerUtils.pushEvent(
"click",
{
name: "Collapse Line Item",
type: "button",
},
{
click: {
category: "Quote Detail Table Interactions",
},
}
);
}
},
rowClass: ({ node, data }) => {
return `cmp-product-lines-grid__row ${
!data?.children || data.children.length === 0
? "cmp-product-lines-grid__row--notExpandable"
: ""
}`;
},
detailRenderer: ({ data }) => (
<section className="cmp-product-lines-grid__row cmp-product-lines-grid__row--expanded">
<ProductLinesChildGrid
gridProps={gridProps}
license={gridProps.agGridLicenseKey}
columnDefiniton={
whiteLabelMode ? whiteLabelCols() : quoteDetailsCols()
}
data={data.children}
onModelUpdateFinished={() => {
markupChanged(mutableGridData);
}}
/>
</section>
),
},
There's two ways to achieve this:
Implement the Grid Event rowGroupOpened (see: https://www.ag-grid.com/react-data-grid/grid-events/#reference-rowGrouping-rowGroupOpened)
This will be triggered whenever the user opens a group node.
onRowGroupOpened={(params) =>
console.log('onRowGroupOpened', params.expanded)
}
Implement the Grid Event cellMouseDown (see: https://www.ag-grid.com/react-data-grid/grid-events/#reference-selection-cellMouseDown) to capture the chevron click event.
This will be triggered only when the user clicks the expand chevron
onCellMouseDown={(params) => {
const isMasterRowGroupExpanded = params.event.target.classList.contains(
'ag-icon-tree-closed'
);
if (isMasterRowGroupExpanded) {
console.log('onCellMouseDown', params.value);
}
}}
See both of these approaches implemented in the following plunkr.
I would like to create a chart from react-chartjs-2 that only renders the first and last points. You'll notice I have that implemented inside pointRadius. For a simple static chart that renders once this actually works fine.
The problem is I have another component that will update someDynamicData which causes the line chart to be updated and when it gets updated the previously drawn points are still visible.
Is there a way to cleanup each chart so when I dynamically update it I always get just the first and last points drawn for the current chart im looking at?
I have tried hooking up a ref and manually calling update and delete which both do not seem to work.
function getData (someData) {
return {
labels: someData.map(d => d.point),
datasets: [
{
data: someData.map(d => d.point),
fill: false,
borderColor: 'white',
tension: 0.5
},
],
};
}
function getOptions (someData) {
return {
elements: {
point: {
radius: 0,
pointRadius: function (context) {
if (context.dataIndex === 0 || context.dataIndex === someData.length - 1) {
return 10;
}
},
},
tooltips: {
enabled: false
},
},
plugins: {
legend: {
display: false
}
}
};
}
const LineChart = ({someDynamicData}) => ( <Line data={getData(someDynamicData)} options={getOptions(someDynamicData)} />);
export default LineChart;
Here below is my next JS Code which is showing a simple ArcGIS map with the points or markers on the specific coordinates.
Can anyone please let me know that how can I display the popup / Info window for the points on the map? e.g. I click on any point and it will open a corresponding popup on it.
import NavBar from '#/components/NavBar'
import axios from 'axios';
import { useRef, useEffect, useState } from 'react';
import { loadModules } from 'esri-loader';
export default function Home({...props}) {
const [state, setState] = useState('');
const MapElement = useRef(null)
const options = {
url: 'https://js.arcgis.com/4.6/',
css: true
};
useEffect(() => {
var vehicleData = props.data
var map, point_symbol;
loadModules([
"esri/views/MapView",
"esri/WebMap",
"esri/Graphic",
"esri/geometry/Point",
"esri/PopupTemplate",
"esri/layers/FeatureLayer","dojo/domReady!"
],options).then(([ MapView, WebMap, Graphic, Point, PopupTemplate, FeatureLayer]) => {
const webmap = new WebMap({
basemap: "gray-vector"
})
var map = new MapView({
map: webmap,
center:[-6.357768833333333, 53.415487166666665],
zoom:6,
container: MapElement.current
})
map.popup.autoOpenEnabled = false;
for(var i=0, i_length=vehicleData.length; i<i_length; i++){
point_symbol = new Point({
longitude:vehicleData[i].longitude,
latitude: vehicleData[i].latitude,
spatialReference: { wkid: 3857 }
})
var template = new PopupTemplate({
title: vehicleData[i].company,
content: vehicleData[i].description
});
var graphic_symbol = new Graphic({
geometry: point_symbol,
symbol: {
type: "simple-marker",
style: "circle",
color: "orange",
size: "18px",
outline: {
color: [150, 200, 255],
width: 5
}
},
popupTemplate: template
});
map.graphics.add(graphic_symbol)
}
map.on("click", function(event) {
map.popup.open({
location: event.mapPoint,
features: [graphic_symbol]
});
});
// map.on("click", function(event) {
// console.log(vehicleData)
// map.popup.open({
// location: event.mapPoint, // location of the click on the view
// title: "You clicked here", // title displayed in the popup
// content: "Your description here" // content displayed in the popup
// });
// });
})
return () => {
if(!!map) {
map.destroy()
map=null
}
}
})
return (
<div id="home-container">
<NavBar />
<div className="app-wrapper" >
<div className="app-content">
<div className="no-padding">
<div className="row gy-4">
<div className="col-12">
<div style={{height:1000, width:1400}} ref={MapElement}></div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export async function getServerSideProps(context) {
let response = await axios(process.env.BASE_URL +'/devices/all',{
headers : {
'Authorization' : 'Bearer ' + process.env.TOKEN
}
})
let data = await response.data
return {
props : {
data: data
}
}
}
I need to display pop-up or info window corresponding to each markup multiple circles on the map. In the above code, API call is done by getServerSideProps, and data as an array of objects is passed to the component using props.
I am able to display multiple circles on the map but don't know how to display info window corresponding to each marker?
I think that in your code you are having a context problem with the variable i. When the popup shows the value of i always gonna be vehicleData.length.
So you can solve the context problem using let instead of var, but I will suggest you another approach.
Declare template, the popup template, outside the loop, and use two new properties that we will add in the next step.
var template = new PopupTemplate({
title: "{company}",
content: "{description}"
outFields: ["*"],
fieldInfos: [
{ fieldName: "company" },
{ fieldName: "description" }
]
});
Add the information you want to display, to the attributes of the graphics, like this,
var graphic_symbol = new Graphic({
geometry: point_symbol,
symbol: {
type: "simple-marker",
style: "circle",
color: "orange",
size: "18px",
outline: {
color: [150, 200, 255],
width: 5
}
},
popupTemplate: template,
attributes: { // <- here
company: vehicleData[i].company,
description: vehicleData[i].description
}
});
Finally, let the click event search for the graphic, like this,
map.on("click", function(event) {
map.popup.open({
location: event.mapPoint,
fetchFeatures: true
});
});
BTW, I just keep the last step because you change default on purpose, I am not seeing the reason here but you must have one.
#cabesuon is right. you can remove the on click event and remove map.popup.autoOpenEnabled = false; too. after that clicking on the graphic will open the popup info by default.
for the table format you may want to use a function for content
// The following snippet shows how to use a function
// to create a simple node and display it in the popup template content
let template = new PopupTemplate({
title: "Population by Gender",
content: setContentInfo
});
function setContentInfo(feature){ // feature here is the graphic, you may access its properties for the table
// create a chart for example
let node = domConstruct.create("div", { innerHTML: "Text Element inside an HTML div element." });
return node;
}
In my UsedComponents component I have a list of mapped radio buttons that each return componentType as value.
this.props.usedComponents.map((component, index) => (
<RadioButton
key={index}
id={component.componentType}
icon={ButtonIco}
label={component.componentName}
name="select"
value={component.componentType}
inputClass={classes.RadioInput}
labelClass={classes.RadioLabel}
selected={component.selected}
handleChange={this.props.selectComponent}
/>
))
My state looks like this:
constructor(props) {
super(props);
this.state = {
components: components,
usedComponents: [components[0], components[2], components[3]],
};
this.selectComponentHandler = this.selectComponentHandler.bind(this);
}
components is an imported array of objects that each look something like this:
{
componentType: "headerLogoNavigation",
componentName: "Header 02",
padding: "small",
fontSize: "small",
fontColor: "#1f1f1f",
fontFamily: "Sans-serif",
backgroundColor: "#ffffff",
image: placeholderLogo,
selected: false,
isEditing: false,
margins: false,
roundCorners: "none",
mobile: "false"
}
In my Page component I'm trying to pass a selectComponentHandler prop to my UsedComponents component that should select a component based on a value of a selected radio button and set its state to selected: true. For an added bonus it should set the state of any previously selected component to selected: false.So far I managed to figure out how to select the component but I'm not able to update its state. My final attempt to create this handler before I gave up looks like this:
selectComponentHandler = event => {
this.setState(prevState => {
let selected = prevState.usedComponents.filter(item => item.componentType === event.target.value);
selected.selected = 'true';
return { selected };
});
};
and it's an attempt to filter the prevState inside the setState for the componentType that matches event.target.value of the radio button and set it's state, but I messed up the logic or the syntax and my head is about to explode so I can't figure out what I did wrong.
Can someone help me figure this out?
I figured it out. It's a bit hacky but it works.
selectComponentHandler = event => {
const value = event.target.value;
this.setState(prevState => {
let selected = prevState.usedComponents.filter(item => item.componentType === value).shift();
let unSelected = prevState.usedComponents.filter(item => item.selected === true).shift();
if(unSelected) {
unSelected.selected = false;
}
selected.selected = true;
return { unSelected, selected };
});
};