How to update props in Ag Grid status bar custom component - reactjs

I am working on a basic Ag Grid React application, and I added a custom status bar component to the grid, using their docs. My goal is to update the "total rows" item in the status bar when a filter is applied to the grid, however I cannot get the value in the status bar to change. The status bar component uses a state variable inherited from the Grid as a prop, but when the prop changes, the status bar does not re-render to reflect that.
Here's a demo that shows that even when the prop passed to the status bar component changes, nothing happens. You can test this by clicking the button and the console will show the "total" variable incrementing, yet the status bar remains unchanged.
Relevant code snippet:
import React, { useState } from 'react';
import { render } from 'react-dom';
import { AgGridColumn, AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import 'ag-grid-enterprise';
const CustomStatusBar = props => {
return (
<div className="ag-status-bar-center">
<div className="ag-status-name-value ag-status-panel ag-status-panel-total-row-count">
<span className="label">Total Rows</span>:
<span className="ag-status-name-value-value">{props.total}</span>
</div>
</div>
);
};
const App = () => {
const rowData = [
{ make: 'Toyota', model: 'Celica', price: 35000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Porsche', model: 'Boxter', price: 72000 }
];
const [total, setTotal] = useState(3);
return (
<div className="ag-theme-alpine" style={{ height: 400, width: 600 }}>
<AgGridReact
rowData={rowData}
frameworkComponents={{
customStatusBar: CustomStatusBar
}}
statusBar={{
statusPanels: [
{
statusPanel: 'customStatusBar',
align: 'center',
statusPanelParams: { total: total }
}
]
}}
>
<AgGridColumn field="make" />
<AgGridColumn field="model" />
<AgGridColumn field="price" />
</AgGridReact>
<button
onClick={() => {
setTotal(prevTotal => {
console.log(prevTotal);
return prevTotal + 1;
});
}}
>
Click me
</button>
</div>
);
};
render(<App />, document.getElementById('root'));

Instead of passing total inside the statusPanelParams. I'd recommend defining a getter/setter helper method on the Custom Status Bar, and pass in the total value when needed so that you can update it.
You can get the Status Bar Instance like this:
// example - get status bar component
const statusBarComponent = gridOptions.api.getStatusPanel('statusBarCompKey');
if (statusBarComponent) {
componentInstance = statusBarComponent.getFrameworkComponentInstance();
}
And if you define a method e.g. updateTotal on the status bar, you can call it like this:
componentInstance.updateTotal(total)
See this example for more information: https://www.ag-grid.com/react-data-grid/component-status-bar/#accessing-status-bar-panel-instances

Related

Antd some row properties are not reactive, is it bug or feature, or the code is wrong?

I'm trying to implement the dynamic Row's properties and found justify and align are not updated when the props are updated. Not sure why, maybe something is wrong with the code?
import React, { useState } from "react";
import "./index.css";
import { Col, Row, Button, Select } from "antd";
const App = () => {
const [justify, setjustify] = useState("end");
return (
<>
{justify}
<Select
value={justify}
options={[
{ value: "start", label: "start" },
{ value: "end", label: "end" }
]}
onChange={(val) => {
setjustify(val);
}}
/>
<Row justify={justify} style={{ height: "150px" }} align="bottom">
<Button type="primary">button1</Button>
<Button type="primary">button</Button>
</Row>
</>
);
};
export default App;
The justify property is changed only on the load, not dynamically. I mean the row is not updated on the property's change.
The code to play with is here: https://codesandbox.io/s/grid-sort-antd-5-0-7-forked-85qlli?file=/demo.js:0-662

React.js sharing state between components updating a dynamic classname on click rendered from .map function

The code:
https://codesandbox.io/s/react-props-conditional-class-o1h6sh
The issue:
When you put the child components (Card, Graph) inside the .map function it disrupts the independence of the click, such that clicking either fires all elements, in the case of the 'Card' component or fires both from the 'Graph' component.
The aim:
Be able to click any of the 4 links and envoke the dynamic classname "is-red", changing the text colour on the clicked element leaving the others unaffected but persisting the state of the changed element, i.e have the option to toggle any of the four links on and off, whilst not affecting the toggle state of the others.
Passing props to the parent component from two sibling child components works fine, the issue is introduced by the data.map function in addition to nesting the 'Graph' component inside the 'Card' component.
your Child Component "Graph" is inheriting the CSS color style from its Parent Component "Card" whenever the "isCardActive" state equals true.
you also have the same state isCardActive being used for both mapped elements [{ name: "John" }, { name: "Jane" }]. When isCardActive = true all mapped elements will be affected, not only the clicked one.
one solution would be assigning another className to the Child Component, and moving the states inside the Components before mapping it. for example :
styles.css
.is-red {
color: red;
}
.is-default-color {
color: black;
}
Graph.js
export default function Graph() {
const [isGraphActive, setIsGraphActive] = useState(false);
const handleGraphClick = () => {
setIsGraphActive((current) => !current);
};
return (
<>
<div
className={
"clicker" + (isGraphActive ? " is-red" : " is-default-color")
}
onClick={handleGraphClick}
>
Click from Graph
</div>
</>
);
}
Card.js
export default function Card({ graph, name }) {
const [isCardActive, setIsCardActive] = useState(false);
const handleCardClick = () => {
setIsCardActive((current) => !current);
};
return (
<>
<div className={"card__inner" + (isCardActive ? " is-red" : "")}>
<div className="clicker" onClick={handleCardClick}>
Click {name} from card
</div>
{graph}
</div>
</>
);
}
App.js
import "./styles.css";
import Card from "./Card";
import Graph from "./Graph";
const data = [{ name: "John" }, { name: "Jane" }];
export default function App() {
return (
<div className="App">
{data.map((el, index) => (
<Card key={index} name={el.name} graph={<Graph />} />
))}
</div>
);
}
full sandbox link here

code block not displaying when previewing in react-quill

In this code, I am trying to insert a code block using react-quilljs
import React, { useState } from 'react';
import hljs from 'highlight.js';
import { useQuill } from 'react-quilljs';
import 'quill/dist/quill.snow.css'; // Add css for snow theme
export default () => {
hljs.configure({
languages: ['javascript', 'ruby', 'python', 'rust'],
});
const theme = 'snow';
const modules = {
toolbar: [['code-block']],
syntax: {
highlight: (text) => hljs.highlightAuto(text).value,
},
};
const placeholder = 'Compose an epic...';
const formats = ['code-block'];
const { quill, quillRef } = useQuill({
theme,
modules,
formats,
placeholder,
});
const [content, setContent] = useState('');
React.useEffect(() => {
if (quill) {
quill.on('text-change', () => {
setContent(quill.root.innerHTML);
});
}
}, [quill]);
const submitHandler = (e) => {};
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
<form onSubmit={submitHandler}>
<button type='submit'>Submit</button>
</form>
{quill && (
<div
className='ql-editor'
dangerouslySetInnerHTML={{ __html: content }}
/>
)}
</div>
);
};
Using the above code, I get the following preview of the editor's content
There are two problems with this:
There is no code syntax highlighting, as I want to achieve this using the highlihgt.js package, inside the code block inside the editor, and
The code block is not displayed (with the black background and highlighting syntax when it's working) in the previewing div outside the editor.
How can I fix these two issues?
Your code is getting marked up by highlight.js with CSS classes:
<span class="hljs-keyword">const</span>
You are not seeing the impact of those CSS classes because you don't have a stylesheet loaded to handle them. You need to choose the theme that you want from the available styles and import the corresponding stylesheet.
import 'highlight.js/styles/darcula.css';
Look at the css in the editor mode. It depends on two class names ql-snow and ql-editor.
You can fix this issue by wrapping it around one more div with className ql-snow.
<div className='ql-snow'>
<div className='ql-editor' dangerouslySetInnerHTML={{ __html: content }}>
<div/>
</div>
This should work.
I got the same issue and when I used hjls what happened was that I got syntax highlighting in the editor but not in the value.
If you noticed the syntax gets highlighted some seconds after you write the code block, this means that the value gets set before the syntax gets highlighted.
So, I just set the value after 2 seconds using setTimeout and this solved my problem
Like this:
<ReactQuill
theme="snow"
value={value}
onChange={(content) => {
setTimeout(() => {
setValue(content)
}, 2000)
}}
modules={modules}
formats={formats}
bounds="#editor"
placeholder="Write something..."
className="text-black dark:text-white"
/>
I recently implemented this logic into my project. I used React Quill for the text editor, implemented syntax highlighting to it using highlight.js, and I also used React Markdown to display the formatted content on my website. React Markdown by default works with markdown, so you need a plugin (rehype-raw) to get it to parse HTML. This is my code, from my project. Just remove some of the unnecessary stuff from here that is specific to my project and use what you need.
// PLUGINS IMPORTS //
import { Typography } from "#mui/material";
import { useEffect, useState } from "react";
import hljs from "highlight.js";
import "react-quill/dist/quill.core.css";
import "react-quill/dist/quill.snow.css";
import "highlight.js/styles/atom-one-dark.css";
import ReactQuill from "react-quill";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
// COMPONENTS IMPORTS //
import { CreateButton } from "components/atoms";
// EXTRA IMPORTS //
import styles from "./create-post.module.css";
import { ETheme } from "types/theme";
/////////////////////////////////////////////////////////////////////////////
type CreatePostProps = {};
hljs.configure({
// optionally configure hljs
languages: ["javascript", "python", "c", "c++", "java", "HTML", "css", "matlab"],
});
const toolbarOptions = [
["bold", "italic", "underline", "strike"],
["blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }],
["link"],
[{ indent: "-1" }, { indent: "+1" }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ align: [] }],
];
const modules = {
syntax: {
highlight: function (text: string) {
return hljs.highlightAuto(text).value;
},
},
toolbar: toolbarOptions,
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
};
const formats = [
"header",
"font",
"size",
"bold",
"italic",
"underline",
"strike",
"blockquote",
"code-block",
"list",
"bullet",
"indent",
"link",
"align",
];
const placeholder = "Description";
const CreatePost = (props: CreatePostProps) => {
const [markdownText, setMarkdownText] = useState("");
useEffect(() => {
console.log({ markdownText });
}, [markdownText]);
return (
<main className={`${styles["create-post-wrapper"]}`}>
<header className={`${styles["create-post-header"]}`}>
<Typography variant="h6">Create a post</Typography>
</header>
{/* making the border a seperate div makes it easier to apply margin */}
<div className={`${styles["border"]} ${styles["top"]}`}></div>
<div>Choose a community</div>
<article className={`${styles["create-post"]}`}>
<section className={`${styles["inner-create-post"]}`}>
<section>Title</section>
<ReactQuill
value={markdownText}
onChange={value => setMarkdownText(value)}
theme="snow"
modules={modules}
formats={formats}
placeholder={placeholder}
/>
<div className="ql-snow">
<div className="ql-editor">
<ReactMarkdown children={markdownText} rehypePlugins={[rehypeRaw]} />
</div>
</div>
<div className={`${styles["border"]} ${styles["bottom"]}`}></div>
<section className={`${styles["post-button"]}`}>
<CreateButton
theme={ETheme.LIGHT}
buttonText="Post"
buttonProps={{
fullWidth: false,
}}
/>
</section>
</section>
</article>
</main>
);
};
export default CreatePost;
You can always add more options to toolbarOptions, but don't forget to also add them to formats if you do. Also, if you want to keep formatting anywhere else in your website, you need the two divs with these 2 classes around your markdown.
React Quill bacially saves everything into a string with HTML and classes, you import styles for those classes and it works like magic.

react How to hide the Card when another location is pressed

Developed with react and typescript.
Now the card is shown or hidden when you click on the div tag.
I want to hide the Card when it is displayed, even if another place other than the div tag is pressed.
import React, { FunctionComponent, useState } from 'react';
import { Card } from 'components/atoms/Card';
import { Display } from 'components/atoms/Display';
const Test: FunctionComponent = () => {
const [isDisplay, setIsDisplay] = useState(false);
const onClick = () => {
setIsDisplay(!isDisplay);
};
return (
<>
<div onClick={onClick} style={{ width: '100px', height: '100px' }}>
display Card
</div>
<Display enabled={isDisplay}>
<Card width={100} height={100}></Card>
</Display>
</>
);
};
export default Test;
Try this in your onClick method. It looks like you need to access the current state's value and update it.
setIsDisplay(state => !state);
It's explained here in the React docs.
https://reactjs.org/docs/hooks-reference.html#functional-updates

ag-grid-react does not render properly

Following the sample from the docs:
https://www.ag-grid.com/best-react-data-grid/index.php
After creating new react app (have tried several times on different machines)
create-react-app whatever
If I apply the stylesheets (ag-grid.css & theme-fresh.css) all that is rendered is a gray line across the page. Any other combination renders a blank page. Removing ag-grid.css renders the grid but its jumbled all over the place.
Has anyone used this lately successfully with React? Does anyone recommend something different? (requirements: paging, sorting, filtering, selectable row(s))
thanks :-)
import React, { Component } from 'react';
import {AgGridReact} from 'ag-grid-react';
import '../node_modules/ag-grid/dist/styles/ag-grid.css';
import '../node_modules/ag-grid/dist/styles/theme-fresh.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{headerName: 'First Name', field: 'name' },
{headerName: 'Last Name', field: 'lname' }
],
rowData: [
{ name: 'Dave', lname: 'Smith' },
{ name: 'Tommy', lname: 'Smith' },
{ name: 'Lea', lname: 'Jones' }
]
}
}
render() {
return (
<div className="ag-fresh">
<AgGridReact
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
rowSelection="multiple"
enableColResize="true"
enableSorting="true"
enableFilter="true"
groupHeaders="true"
rowHeight="22"
debug="true"
/>
</div>
);
}
}
export default App;
The outer grid required a height :-(
The documentation does not show this. Not sure why there is no min default height for the grid, but there is not.
So essentially you need something like this, where the grid is wrapped with an element which has a height:
<div className="ag-fresh">
<div className="grid_height_fix">
<AgGridReact columnDefs={this.state.columnDefs} rowData={this.state.data.gridDate} >
</AgGridReact>
</div>
</div>
.grid_height_fix {height:800px;}
The new and better way to do this :
const GridExample = () => {
// I use 100% because I set the height of the higher element manually, you can set something like 500px for the height here instead, or calculate it based on the screen size
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
...
></AgGridReact>
</div>
</div>
);
};
this way you have better controle over the dimensions.

Resources