Adding new chart types to react pivottable - reactjs

I'm wondering if it's possible to add new type of charts, like a radar chart, to the React library plotly/react-pivottable https://github.com/plotly/react-pivottable.
I would like to add a spider chart, always from the chart library plotly, but I can't understand where to start as the documentation is a litle poor and the GitHub repo is quite silence...
Maybe it's not even possible.
Does anyone know if it's possible?

Yes, it's completely possible to add custom charts. You need to copy makeRenderer function from original repo and customised it according to the chart types.
To add a new chart type (radar chart), to React Pivottable, you've to add directly on PlotlyRenderers.
Here's an example of how you could add a radar chart to React Pivottable:
const Plot = createPlotlyComponent(window.Plotly);
const PlotlyRenderers = createPlotlyRenderers(Plot);
const makeRenderer = (
PlotlyComponent,
traceOptions = {},
layoutOptions = {},
transpose = false
) {
class Renderer extends React.PureComponent {
render() {
const pivotData = new PivotData(this.props);
const rowKeys = pivotData.getRowKeys();
const colKeys = pivotData.getColKeys();
const traceKeys = transpose ? colKeys : rowKeys;
if (traceKeys.length === 0) {
traceKeys.push([]);
}
const datumKeys = transpose ? rowKeys : colKeys;
if (datumKeys.length === 0) {
datumKeys.push([]);
}
let fullAggName = this.props.aggregatorName;
const numInputs =
this.props.aggregators[fullAggName]([])().numInputs || 0;
if (numInputs !== 0) {
fullAggName += ` of ${this.props.vals.slice(0, numInputs).join(", ")}`;
}
const data = traceKeys.map((traceKey) => {
const r = [];
const theta = [];
for (const datumKey of datumKeys) {
const val = parseFloat(
pivotData
.getAggregator(
transpose ? datumKey : traceKey,
transpose ? traceKey : datumKey
)
.value()
);
r.push(isFinite(val) ? val : null);
theta.push(datumKey.join("-") || " ");
}
const trace = { name: traceKey.join("-") || fullAggName };
trace.fill = "toself";
trace.r = r;
trace.theta = theta.length > 1 ? theta : [fullAggName];
return Object.assign(trace, traceOptions);
});
const layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 50]
}
},
/* eslint-disable no-magic-numbers */
// width: window.innerWidth / 1.5,
// height: window.innerHeight / 1.4 - 50
// /* eslint-enable no-magic-numbers */
};
return (
<PlotlyComponent
data={data}
layout={Object.assign(
layout,
layoutOptions,
this.props.plotlyOptions
)}
config={this.props.plotlyConfig}
onUpdate={this.props.onRendererUpdate}
/>
);
}
}
return Renderer;
}
const radarChart = () => {
return makeRenderer(
Plot,
{ type: "scatterpolar" },
{},
true
);
}
PlotlyRenderers["Radar Chart"] = radarChart({});
const data = [
{
country: "Spain",
name: "Santiago",
surname: "Ramón y Cajal",
sex: "Male",
age: 57,
subject: "Medicine"
},
{
country: "United Kingdom",
name: "Ada",
surname: "Lovelace",
sex: "Female",
age: 47,
subject: "Computing"
},
{
country: "United Kingdom",
name: "Alan",
surname: "Turing",
sex: "Male",
age: 55,
subject: "Computing"
},
{
country: "France",
name: "Antoine",
surname: "Lavoisier",
sex: "Male",
age: 12,
subject: "Chemistry"
},
{
country: "Poland",
name: "Marie",
surname: "Curie",
sex: "Female",
age: 33,
subject: "Chemistry"
},
{
country: "Austria",
name: "Hedy",
surname: "Lamarr",
sex: "Female",
age: 34,
subject: "Computing"
},
{
country: "Austria",
name: "Erwin",
surname: "Schrödinger",
sex: "Male",
age: 38,
subject: "Physics"
}
];
export default function App() {
const [opts, setOpts] = useState({});
return (
<div className="App">
<PivotTableUI
data={data}
onChange={(e) => {
setOpts(e);
console.log(e);
}}
renderers={Object.assign({}, TableRenderers, PlotlyRenderers)}
cols={["sex"]}
rows={["subject", "country"]}
rendererName="Table Heatmap"
aggregatorName="Average"
vals={["age"]}
derivedAttributes={{ completeName: (el) => el.name + " " + el.surname }}
{...opts}
/>
</div>
);
}
Here is the complete code:
https://codesandbox.io/s/react-pivot-table-custom-charts-2utqbt?file=/src/App.js:3511-4468

Related

Problem with DOM and selected element of object

I would like to display the property of age and if a person is vaccinated under each name after each button's click, but these properties appear under the page. How can I do it?
Furthermore, which concepts of the dom should I explore in order to build logics where there is a correlation and interaction between dom and objects? (e.g. select an item via the dom and view its details).
<body>
<div class="container">
<div class="list-container"></div>
</div>
<script src="script.js"></script>
</body>
const persons = [
{ name: "carl", age: 20, vaccinated: true, id: 1 },
{ name: "alex", age: 45, vaccinated: false, id: 2 },
{ name: "alice", age: 12, vaccinated: true, id: 3 },
{ name: "erick", age: 2, vaccinated: true, id: 4 },
{ name: "fred", age: 23, vaccinated: false, id: 5 },
{ name: "wandy", age: 13, vaccinated: true, id: 6 },
];
const generalContainer = document.querySelector(".container");
const listContainer = document.querySelector(".list-container");
function renderItems(obj) {
let dataId;
let item;
let sub;
obj.forEach((element) => {
item = document.createElement("div");
let itemAttr = document.createAttribute("data-id");
itemAttr.value = element.id;
item.setAttributeNode(itemAttr);
dataId = item.dataset.id;
item.innerHTML = `<p>NAME: ${element.name}</p>
<button class="btn">${element.name}</button>
<div class='sub'></div>
`;
sub = document.querySelector(".sub");
listContainer.appendChild(item);
});
let btn = document.querySelectorAll(".btn");
let selected;
let subItem = document.createElement("div");
btn.forEach((el) => {
el.addEventListener("click", (e) => {
e.preventDefault();
let textValue = el.textContent;
selected = obj.find((ele) => {
return ele.name === textValue;
});
//subItem = document.createElement("div");
subItem.innerHTML = `
<div><p>AGE: ${selected.age}</p>
<p>VACCINE STATUS: ${
selected.vaccinated ? "Vaccinated" : "Not vaccinated"
}</div>`;
console.log(sub);
sub.appendChild(subItem);
item.append(sub);
});
});
}
renderItems(persons);
Try a more simplified version. Among other things, it uses event delegation for event listeners:
const persons = [{
name: "carl",
age: 20,
vaccinated: true,
id: 1
},
{
name: "alex",
age: 45,
vaccinated: false,
id: 2
},
{
name: "alice",
age: 12,
vaccinated: true,
id: 3
},
{
name: "erick",
age: 2,
vaccinated: true,
id: 4
},
{
name: "fred",
age: 23,
vaccinated: false,
id: 5
},
{
name: "wandy",
age: 13,
vaccinated: true,
id: 6
}
];
let listContainer = document.querySelector(".list-container");
function renderItems(obj) {
obj.forEach((element) => {
new_elem = `
<div data-id="${element.id}">
<p>NAME: ${element.name}</p>
<button but-id="${element.id}">${element.name}</button>
<div class='sub'></div>
</div>
`;
listContainer.insertAdjacentHTML("afterbegin", new_elem);
});
listContainer.addEventListener("click", (e) => {
person = e.target.getAttribute("but-id");
p_name = e.target.innerText;
dest = document.querySelector(`div[data-id="${person}"] div.sub`);
let selected = persons.find(({name}) => name === p_name);
subItem = `
<div>
<p>AGE: ${selected.age}</p>
<p>VACCINE STATUS: ${
selected.vaccinated ? "Vaccinated" : "Not vaccinated"
}</p>
</div>
`;
dest.innerHTML = subItem;
});
}
renderItems(persons);
<body>
<div class="container">
<div class="list-container"></div>
</div>
</body>

REACT: Updating nested state

I have two sets of data. Both are saved in a states.I want to update the rowsData inside the data1 state based on the values in data2 state. The "row" value in the data2 object refers to the "id" of rowsData in the data1 state and columns in the data2 refers to any data beside id in the rowsData object in data1. I want to pick "row" and "column" from data2 and cahnge the respective data inside rowsData in data1.
let tableData = {
columnsDef: [
{ title: "id",field: "id",className: "header-style" },
{ title: "First_name", field: "First_name", className: "header-style" },
{ title: "Last_name", field: "Last_name", className: "header-style" },
{ title: "Email", field: "Email", className: "header-style" },
],
rowsData:[
{ id: "1", First_name: "Donald", Last_name: "OConnell", Email: "DOCONNEL" },
{ id: "2", First_name: "Douglas", Last_name: "Grant", Email: "DGRANT" },
{ id: "3", First_name: "Jennifer", Last_name: "Whalen", Email: "JWHALEN" },
{ id: "4", First_name: "Michael", Last_name: "Hartstein", Email: "MHARTSTE" },
{ id: "5", First_name: "Pat", Last_name: "Fay", Email: "PFAY" },
{ id: "6", First_name: "Susan", Last_name: "Mavris", Email: "SMAVRIS" },
{ id: "7", First_name: "Hermann", Last_name: "Baer", Email: "HBAER" }
],
file: [
{ file: { path: "dummy_data_3 .csv"}}
],
}
let updatedTableData = [
{ "row": 2, "column": "Email", "oldValue": "DGRANT", "newValue": "DGRANT UPDATED" },
{ "row": 6, "column": "First_name", "oldValue": "Susan", "newValue": "SUSAN UPDATED" },
{ "row": 4, "column": "Last_name", "oldValue": "Hartstein", "newValue": "Hartstein UPDATED" }
]
const [data1, setData1] = useState(tableData)
const [data2, setData2] = useState(updatedTableData)
Here is the codesandbox link for the issue.
https://codesandbox.io/s/reverent-firefly-r87huj?file=/src/App.js
You can follow these steps for updating data1 from handleAppyChanges function:
Create a copy of rowsData:
const td = [...data1.rowsData];
Iterate over data2 and find related item, then update its related column:
data2.forEach((item) => {
let row = data1.rowsData.find((r) => r.id === item.row);
if (row) {
let index = data1.rowsData.findIndex((v) => v === row);
row[item.column] = item.newValue;
td[index] = row;
}
});
Update table data via state:
const newData = {
file: data1.file,
rowsData: td,
columnDef: data1.columnsDef
};
setData1(newData);
Here's the full function:
const handleAppyChanges = () => {
const td = [...data1.rowsData];
data2.forEach((item) => {
let row = data1.rowsData.find((r) => r.id === item.row);
if (row) {
let index = data1.rowsData.findIndex((v) => v === row);
row[item.column] = item.newValue;
td[index] = row;
}
});
const newData = {
file: data1.file,
rowsData: td,
columnDef: data1.columnsDef
};
setData1(newData);
console.log("Updated Data 1: ", td);
};
You can access the full code via codesandbox:
Apply into onChange and check it.
const handleAppyChanges = () => {
data2.map((item) => {
data1.rowsData.map((data) => {
//match both id and row
if (data.id == item.row) {
//check column is there or not
let column = data.hasOwnProperty(item.column);
if (column) {
//if yes then change the value
data[item.column] = item.newValue;
}
}
});
});
setData1({ ...data1 });
};

How to Search or Compare values out of array of Objects in Javascript and return based on input

I have simply iterated through an array of objects. But I have no clue how I should compare the previous object values and display data along with current data. as shown in the image.
My Half solutions:
const dataSet = [{
categoryId: 100,
parentCategoryId: -1,
name: "Business",
keyword: "Money",
},
{
categoryId: 200,
parentCategoryId: -1,
name: "Tutoring",
keyword: "Teaching",
},
{
categoryId: 101,
parentCategoryId: 100,
name: "Accounting",
keyword: "Taxes",
},
{
categoryId: 102,
parentCategoryId: 100,
name: "Taxation",
keyword: "",
},
{
categoryId: 201,
parentCategoryId: 200,
name: "Computer",
keyword: "",
},
{
categoryId: 103,
parentCategoryId: 101,
name: "Corporate Tax",
keyword: "",
},
{
categoryId: 202,
parentCategoryId: 201,
name: "Operating System",
keyword: "",
},
{
categoryId: 109,
parentCategoryId: 101,
name: "Small Business Tax",
keyword: "",
}
]
function solution(X) {
// search your keyword
dataSet.map((item)=>{
console.log("Item List: ", item);
if (X === item.categoryId){
const displayData = `\n\t ParentCategoryId : ${item.parentCategoryId} \n\t Name : ${item.name} \n\t Kewords : ${item.keyword}`;
try{
if(displayData) {
console.log("Your Searched Data:", displayData);
}
}catch (e) {
return console.log ("error:", e);
}
}
})
}
solution(201);
Below method will solve your problem.
function solution(cId){
let result = null;
const getParentNode = function(parentId){
const parentNode = dataSet.find(elm => elm.categoryId === parentId);
if(parentNode && parentNode.keyword === ""){
return getParentNode(parentNode.parentCategoryId);
}
return parentNode;
}
for(let i=0; i<dataSet.length; i++){
if(dataSet[i].categoryId === cId){
if(dataSet[i].keyword === "")
result = {...dataSet[i], keyword: getParentNode(dataSet[i].parentCategoryId).keyword};
else
result = dataSet[i];
break;
}
}
return result;
}
This may probably help you!
var result = result1.filter(function (o1) {
return result2.some(function (o2) {
return o1.id === o2.id; // return the ones with equal id
});
});
// if you want to be more clever...
let result = result1.filter(o1 => result2.some(o2 => o1.id === o2.id));

access line information, line id number and onChanged event

How can I get the line information and id number I want to edit.
Also how can I access the onChanged event?
Can you help me ?
I want to get the line information I want to edit.
I want to get the id number of the line I want to edit.
How can I access onChanged events? So how can I understand when textfield changes?
`
import React, { Component } from "react";
import Paper from '#material-ui/core/Paper';
import { EditingState,SearchState,IntegratedFiltering, GroupingState, IntegratedGrouping,} from '#devexpress/dx-react-grid';
import { ColumnChooser, Grid, Table, TableColumnResizing, TableColumnVisibility, TableEditColumn, TableEditRow, TableFixedColumns, TableHeaderRow, Toolbar, SearchPanel, TableGroupRow, GroupingPanel, DragDropProvider,} from '#devexpress/dx-react-grid-material-ui';
class UserInformation extends Component {
constructor(props) {
super(props);
this.state = {
isLoaded: true,
columns: [
{name: 'name', title: 'Name'},
{name: 'city', title: 'city'}
],
rows : [
{ sex: "Female", name: "Sandra", city: "Las Vegas", car: "Audi A4" },
{ sex: "Male", name: "Paul", city: "Paris", car: "Nissan Altima" },
{ sex: "Male", name: "Mark", city: "Paris", car: "Honda Accord" },
{ sex: "Male", name: "Paul", city: "Paris", car: "Nissan Altima" },
{ sex: "Female", name: "Linda", city: "Austin", car: "Toyota Corolla" }
]
};
this.commitChanges = this.commitChanges.bind(this);
//this.getRowId = this.getRowId.bind(this);
}
}
commitChanges({ added, changed, deleted }) {
let { rows } = this.state;
let changedRows;
if (added) {
console.log("added")
}
if (changed) {
changedRows = rows.map(row => (changed[row.id] ? { ...row, ...changed[row.id] } : row));
console.log("changed");
console.log(rows.map(row => (changed[row.id])))
}
if (deleted) {
console.log("deleted")
}
};
//getRowId(row) {
// console.log(this.state.rows.indexOf(row));
// return this.state.rows.indexOf(row);
//}
render() {
const { isLoaded, drList, rows, columns } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className={"animated fadeIn "}>
<Row>
<Paper>
<Grid
rows={rows}
columns={columns}
getRowId={this.getRowId}
>
<EditingState
onCommitChanges={this.commitChanges}
/>
<Table
/*columnExtensions={tableColumnExtensions}*/
/>
<TableHeaderRow />
<TableEditRow />
<TableEditColumn
showAddCommand
showEditCommand
showDeleteCommand
/>
</Grid>
</Paper>
</Row>
</div>
);
}
}
}
export default UserInformation;

React Native iterate through array objects and cacluate sum up value

I am writing a piece of function in my React Native App, I have called an api, and it turns me this json array with objects, and stored in this.props.data:
[{name:'Jhon', income:28, spending:20},
{name:'Anna', income:82, spending:50},
{name:'Peter', income:141, spending:70}
]
I wish to iterate throught this array object and sum up the income and spending value, eventually to generate out a new object (everyone) and appened to original this.props.data and make something looks like below:
[{name:'Jhon', income:28, spending:20},
{name:'Anna', income:82, spending:50},
{name:'Peter', income:141, spending:70},
{name:'Everyone', income:251, spending:140}
]
My current attempt is below, but doesn't really work, please advise with code example, thanks
const CustomListview = ({ itemList }) => (
var everyoneObject = {
name: 'everyone',
income: 0,
spending: 0
}
itemList.map(({name, income, spending}) => {
everyoneObject = {
name: 'everyone',
income: income++,
spending: spending++
}
})
itemList.push(everyoneObject);
<View style={styles.container}>
<FlatList
data={itemList}
renderItem={({ item }) =>
<TillReportRow
name={item.name}
income={item.income}
spending={item.spending}
/>}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
you have almost come near to correct logic,
this can be one way you achieve this,
let everyOneObj = { name:'Everyone', income: 0, spending: 0 };
itemList.forEach((item) => { `
everyOneObj.income += item.income;
everyOneObj.spending += item.spending;
});
itemList.push(everyOneObj);
You need to use reduce to create a single new object, not map
const itemList = [
{ name: "Jhon", income: 28, spending: 20 },
{ name: "Anna", income: 82, spending: 50 },
{ name: "Peter", income: 141, spending: 70 }
];
const everyone = itemList.reduce(
(acc, { income, spending }) => {
acc.income += income;
acc.spending += spending;
return acc;
},
{ name: "everyone", income: 0, spending: 0 }
);
itemList.push(everyone);
console.log(itemList);
which prints
[
{ name: "Jhon", income: 28, spending: 20 },
{ name: "Anna", income: 82, spending: 50 },
{ name: "Peter", income: 141, spending: 70 },
{ name: "everyone", income: 251, spending: 140 }
]

Resources