Change the direction of ag-grid to RTL - reactjs

I am using ag-grid in react, and I want to change the direction to RTL, also use of local text. Based on the ag-grid documentation enableRtl={true} would change the style of the grid. It is true when we set it permanently, but when I want to change it programmatically, it does not change. I passed the variable through props to the component for changing the direction and translating the text. This is my code
const Grid= ({dir}) =>{
return (
<div
className="ag-theme-balham"
style={{ height: '100%', width: '100%' }}
>
<AgGridReact
defaultColDef={defaultColDef}
columnDefs={column}
rowData={rowData}
enableRtl={dir === 'rtl'}
localeTextFunc={(key, defaultValue) => {
return dir === 'rtl' ? gridLocal[key] : defaultValue;
}}
/>
</div>
);
}
before changing direction
after changing direction
As it should be
I searched for the solution, but I cannot find a way to solve it.
What should I do? What is wrong with my code?
Edit: where I used the Grid component
const MainLayout = (props) => {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = useState(false);
const handleDrawerOpen = () => { setOpen(true) };
const handleDrawerClose = () => { setOpen(false) };
return (
<div >
<Topbar
setLang={props.setLang}
drawerOpen={handleDrawerOpen}
drawerClose={handleDrawerClose}
status={open}
handleSignOut={props.handleSignOut}
/>
<div style={{height:'48px'}}/>
<div className={classes.content}>
<Sidebar open={open} drawerClose={handleDrawerClose}/>
<Grid dir={theme.direction}/>
</div>
</div>
);
}

I have the same issue. After searching many times, I found only one solution.
I used angular not react, but, I can tell you the steps I used to solve this problem.
I followed the following steps:
I stored the value of the direction in the localstorage
Created a button to trigger an event that will change the value of the direction.
Get the value of the direction from the localstorage in the constructor
Inside the trigger, I changed the value on the localstorage for each click event.
finally, at each click event refresh the page with window.location.reload() to see the effect.
I know. It is not the perfect solution, but this is the only solution that worked for me. I think this problem is not found in the enterprise version of ag-grid.
Update:
I found a better solution. I use angular not React.
In angular, I used <ng-template> as follow:
<ng-template [ngIf]="AgGridDir === 'rtl'" [ngIfElse]="LeftGrid" #Withbutton>
<ag-grid-angular style="width: 100%; height: 450px;"
[class]="Table_Color_mode===Constants.light || Theme_dir===Constants.light?'ag-theme-alpine':'ag-theme-alpine-dark'"
[rowData]="ItemsMainCategories | async" [columnDefs]="columnDefs" (gridReady)="OnGridReady($event)"
[defaultColDef]="defaultColDef" [overlayLoadingTemplate]="overlayLoadingTemplate" [animateRows]="true"
[enableRtl]="true" [frameworkComponents]="agFrameworks" rowSelection="single"
(rowEditingStarted)="onRowEditingStarted($event)" (cellValueChanged)="UpdateItemMainCat($event)">
</ag-grid-angular>
</ng-template>
<ng-template #LeftGrid>
<ag-grid-angular style="width: 100%; height: 450px;"
[class]="Table_Color_mode===Constants.light || Theme_dir===Constants.light?'ag-theme-alpine':'ag-theme-alpine-dark'"
[rowData]="ItemsMainCategories | async" [columnDefs]="columnDefs" (gridReady)="OnGridReady($event)"
[defaultColDef]="defaultColDef" [overlayLoadingTemplate]="overlayLoadingTemplate" [animateRows]="true"
[enableRtl]="false" [frameworkComponents]="agFrameworks" rowSelection="single"
(rowEditingStarted)="onRowEditingStarted($event)" (cellValueChanged)="UpdateItemMainCat($event)">
</ag-grid-angular>
</ng-template>
<ng-template> is a tag created by Angular framework that can be used to render some components or HTML tags based on a condition.
As you can see, I used the same Ag-grid table with the same parameters. Each one with an ng-template. I used a variable called AgGridDir which is dynamically changed to rtl to ltr based on the user choice.
If the user chooses 'rtl' the first template is rendered and the [enableRtl]="true" is set to true, and if the user chooses 'ltr', then the second template will be rendered dynamically without reloading the page and the [enableRtl]="false".
I don't know about react much details, but, I want to share the idea.

Related

Cannot set defaultSize after changing the width using manual drag / the resizer of the tomkp/react-split-pane

I am new to react and using open-source components in my project.
Here's a similar problem but the answers weren't providing a solution.
So I have a state leftPaneWidth and I am closing the left pane on a button click which is nothing just changing the state. The defaultWidth is set to the mentioned state.
But whenever I use the resizer as shown in this example. The state change happens but the defaultSize cannot be set.
Button and state code
const [leftPaneWidth, setLeftPaneWidth] = useState(400);
const handleSidePane = () => {
if (leftPaneWidth > 0) {
setLeftPaneWidth(0);
} else {
setLeftPaneWidth(400);
}
};
SplitPane Code
<SplitPane style={{ height: "100%" }} minSize={0} maxSize={600} defaultSize={leftPaneWidth} split="vertical">
<Pane className="dataset-splitpane-left">
<div>..... content 1 .....</div>
</Pane>
<Pane className="dataset-splitpane-right">
<div>..... content 2 .....</div>
</Pane>
</SplitPane>
<button className="dataset-splitpane-handle-side-pane-button" onClick={handleSidePane}>
Open/close
</button>
Please help me with this. 🙏
Thanks in advance.

How can I access the "arrow" button click event on Material UI data grid?

In the documentation of Material UI Data Grid it has this code snippet (I've made a couple slight changes like importing DataGrid instead of DataGridPro, etc.):
import { DataGrid, useGridApiRef } from '#material-ui/data-grid';
import Alert from '#material-ui/lab/Alert';
export default function PageChange() {
const apiRef = useGridApiRef();
const [message, setMessage] = React.useState('');
const data = {'dummy-data-for-now': ''};
React.useEffect(() => {
/* ---- Theoretically I would replace columnResize with 'pageChange' ---- */
return apiRef.current.subscribeEvent('columnResize', (params) => {
setMessage(
`Column ${params.colDef.headerName} resized to ${params.computedWidth}px.`,
);
});
}, [apiRef]);
return (
<div style={{ width: '100%' }}>
<div style={{ height: 180, width: '100%' }}>
<DataGrid apiRef={apiRef} {...data} />
</div>
{message && (
<Alert severity="info" style={{ marginTop: 8 }}>
{message}
</Alert>
)}
</div>
);
}
I am implementing API pagination of 10 results at a time. I would like to be able to make a new API call for the next (or previous) 10 results when I click the arrow buttons.
The documentation refers to pageChange as an event option, but when I use the code snippet, I don't know how to utilize it. I would really appreciate any insight anyone might have on how to handle this situation.
This is a picture of the data grid button, for reference
#MaxAlex, thank you for the tip! This sent me on the right path and I finally got it working with server-side pagination. Resource
The key for me was to realize that on the onPageChange property within the DataGrid: onPageChange={(newPage) => handlePageChange(newPage)}, "newPage" was an object with the page number. I didn't need to specifically access the forward and backward arrows, but instead, just manage "newPage" in my handlePageChange function.

How to change style attribute of element with React Hooks?

Hello I have seen tutorials but not sure how to change element attributes in React like with a button click change the width of an element. This is easy with vanilla javascript using document.querySelector. But how is it done in React?? State? or useRef? It says to avoid refs too much so is there a useState way to do this? Toggling the width is my goal, but any advice is appreciated. I don't think I am supposed to use document.querySelector...
My Attempt:
const myRef = useRef("80%");
const changeWidth = () => {
widthRef.style.width = "100px";
};
<div>
<div style ={{width:400px, height: 400px}} ref ={myRef}>
<button onClick={changeWidth}></button>
</div
you can change style directly using style prop
//
const [width, setWidth] = useState("400px")
function hanldeWidthChange() {
// you can set width to any value you want
setWidth("100px")
}
<div>
<div style ={{width: width, height: "400px"}}>
<button onClick={() => hanldeWidthChange()} ></button>
</div>
</div

Can't get Popover to display in correct position in Dialog

I have a Dialog and have a ListItem that when you click on it goes into edit mode by showing a Popover. This was working in an older version of MUI using a Modal but since getting on the latest that didn't work and I'm trying to use a Popover. I tried to make a simple example on CodeSandox but that works. What happens is the Popover is always in the upper left of the page instead of the ListItem.
I have simplified my code to a simple Button and Popover in the Dialog and still have the same problem and have ran out of ideas on what to try next. The error I get in the console is
[Warning] Material-UI: the `anchorEl` prop provided to the component is invalid.
The anchor element should be part of the document layout.
Make sure the element is present in the document or that it's not display none.
When the item is clicked I do event.currentTarget just like in the examples and this is what the console.log looks like for it.
[Log] <button class="MuiButtonBase-root MuiButton-root MuiButton-text" tabindex="0" type="button"> (main.chunk.js, line 26437)
<span class="MuiButton-label">Click Me</span>
<span class="MuiTouchRipple-root">
<span class="MuiTouchRipple-ripple MuiTouchRipple-rippleVisible" style="width: 117.2006825918689px; height: 117.2006825918689px; top: -34.60034129593445px; left: -25.60034129593445px;">
<span class="MuiTouchRipple-child MuiTouchRipple-childLeaving"></span>
</span>
</span>
</button>
I even tried doing disablePortal in the Dialog which didn't fix it. I also tried using refs which fixed the anchorEl warning but still displays relative to the page and not the element. Any ideas?
For anyone that comes across this issue with Material UI, there are a couple of things that you can do.
One is to make sure that if you have multiple nested functional components, that your anchorEl / click handlers for the popover are defined within the specific functional component that holds the popover. If you have nested functional components and the parent component holds the state, it will rerender the children on every state change, which can reset the anchorEl reference.
Second - React.memo can prevent unnecessary rerenders on functional components (it only works if props don't change but should still reap performance benefits in child components).
I have nested elements this is how I solved this without doing anything too extra.
So my main functional component simply returned something like this
const filters = () => {
const [anchorEl, setAnchorEl] = useState(null)
const popoverOpen = Boolean(anchorEl)
const handleTogglePopover = (event: any) => {
event.preventDefault()
if (anchorEl === null) {
setAnchorEl(event.currentTarget)
} else {
setAnchorEl(null)
}
}
const SortActions = () => {
return (
<Box>
<MyCustomRadioButton/>
</Box>
)
}
const FilterButtons = () => {
return (
<Box>
<ButtonBase
onClick={handleTogglePopover}
aria-owns={popoverOpen ? 'my-popover-id-name' : undefined}
aria-haspopup={true}
>
{/* contents (this is a comment in html in react) */}
</ButtonBase>
<Popover
id={'my-popover-id-name'}
open={popoverOpen}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left'
}}
onClose={handleTogglePopover}
>
<SortActions/>
</Popover>
</Box>
)
}
return (
<Box>
{/* THIS LINE IS THE LINE I CHANGED TO GET IT TO WORK */}
<FilterButtons/>
</Box>
)
}
I changed that line to {FilterButtons()} instead. It looks like the component that is rendering the popover needs to exist within the functional component calling it.
However any nested components under do not need to be declared within the calling functional component.
From what I have gathered in looking at many peoples solutions is to use React.memo for this but this worked for me. Something about React re-rendering the popover losing the state when its called as a nested component rather than a function within the component causes the state to be lost? I assume it has to do with how JavaScript works in terms of encapsulation within a function.
I know this is an older question but I know people will still run by this question eventually.
It's also possible to get this error due to what it's saying - you might be trying to use an element that has display: none style as an achorEl for your component, which isn't supported as the underlying logic calculating the position of the anchor element needs it to be visible on screen.
Check if there is any display: none; style
May be anchorEl used in multiple nested functional components problem
Try to use memo concept to prevent component rerender

Dropdown menu flip position issue. React-Select + Material-UI Popper

I use the example autocomplete field from the Material-UI lib documentation. (https://material-ui.com/demos/autocomplete/#react-select)
There is a problem with fliping the menu when it opens at the bottom of the page or the browser's viewport.
Is there a way to fix this problem with Material-UI and react-select?
Or do I need to write something custom?
If you are not using a <Menu/> custom component, you can use the menuPlacement="auto" prop of <Select/>, then your problem is solved.
const components = {
Control,
// Menu , <-- delete it
NoOptionsMessage,
Option,
Placeholder,
SingleValue,
ValueContainer
};
https://github.com/JedWatson/react-select/issues/403
Otherwise you can choose another selector, material-ui provides 2 more differents integration with the <Popper/> component: react-autosuggest and downshift.
https://material-ui.com/demos/autocomplete/
Hope it helps!
i've faced the same problem, for <Select /> component i have used what TomLgls suggest, but for <AsyncSelect /> as a work-around, i used some offset calculations in my component :
const rootHeight = document.getElementById('root').offsetHeight ;
const selectElement = document.getElementById('async_select_container_id');
const selectOffsetBottom= selectElement.offsetHeight + selectElement.offsetTop;
...
<AsyncSelect
{...listProps}
menuPlacement={
rootHeight - selectOffsetBottom > 210 ? 'auto' : 'top' // react-select menu height is 200px in my case
}
/>
i hope it helps as well
If you have created customMenu component then in that give className as open-menu-top and write this code for class:
.menu-open-top {
top: auto;
bottom: 100%;
}
Your CustomMenu maybe look like this:
const CustomMenu = ({ children, innerProps, innerRef, selectProps }) => {
return (
<div
ref={innerRef}
{...innerProps}
className={`rs-menu ${customMenuClass} open-menu-top`}
>
{Your Logic}
</div>
);
};

Resources