I would like to close all other opened cards while I click another one, I would want something that works like Accordion Component.
My current code looks as below:
<List>
{
this.results.map((result: PTemplate, index: number) => {
return (
<ResultCard key={"row_"+index} result={result} id={index} initiallyExpanded={false}></ResultCard>
)
})
}
</List>
looks smth like this:
<Card key={'res_'+result.id} initiallyExpanded={initiallyExpanded} style={styles.resultInstance}>
<CardHeader actAsExpander={true} showExpandableButton={true} style={resultTypeStyle}>
<Flexbox>Header</Flexbox>
</CardHeader>
<CardText actAsExpander={false} expandable={true} key={'res_cont_'+result.id}>Description here</CardText>
</Card>
Any idea how would I do this?
Related
We're working on an inventory management page of sorts. The page makes a call to an external API and if that endpoint returns data, it displays that data on the page for the user. The API call uses the current logged in users ID, so we use userLoaded to wait for that ID before making the API call.
If an inventory doesn't exist (API returns nothing), we show some "Not Available" text.
However, when a page initially loads, it's showing the "Not Available" text, re-rendering a couple times, and then finally showing the correct data. Is there anyway to cut back on those re-renders or make it more graceful?
You can see the issue here: https://recordit.co/EYDLupg3xs or on the demo link here: https://showzone.io/inventory
Oddly enough, the table component is not re-rendering with the data - so it stays blank. But if you force the table to re-render (by using one of the Filters for example), the data does indeed show. How can we re-render the table component when the data is ready?
Here is the code (cut down a bit to make it easier to understand):
function Inventory() {
const { currentUser, userLoaded } = useAuth()
const [data, setData] = useState([])
const [inventoryExists, setInventoryExists] = useState(false)
const fetchData = useCallback(async () => {
if (userLoaded) {
const userInventoryData = await axios.get(
`https://showzone-api.herokuapp.com/api/user-inventory/${currentUser.uid}` // For the demo link, I hardcoded a value for testing purposes
)
setData(userInventoryData.data)
setInventoryExists(true)
}
}, [currentUser?.uid, userLoaded])
useEffect(fetchData, [fetchData])
return (
<>
.....
<TabPanel value="1" sx={{ padding: 0 }}>
<Card mb={6}>
<CardContent>
<Grid container spacing={6}>
<Grid item xs={12}>
<Typography>
<strong>Total Cards: </strong>{" "}
{inventoryExists
? data?.owned_count?.toLocaleString() +
" / " +
data?.total_card_count?.toLocaleString()
: "Not Available"}
</Typography>
<Typography>
<strong>Estimated Value: </strong>{" "}
{inventoryExists
? data?.owned_value?.toLocaleString()
: "Not Available"}
</Typography>
</Grid>
<Grid item xs={12} md={6} sx={{ display: "flex" }}>
{inventoryExists ? (
<div>
<Doughnut
data={{
datasets: [
{
data: [
Math.round(data?.owned_count),
Math.round(data?.total_card_count),
],
},
],
}}
/>
</div>
) : (
<BlankDoughnut text="Cards" />
)}
</Grid>
</Grid>
</CardContent>
</Card>
</TabPanel>
<Divider />
{inventoryExists ? (
<PlayerSearch id={currentUser?.uid} />
) : (
<PlayerSearch />
)}
</>
)
}
What I'm trying to do is get which is the key of the sider the user is using (without ReactDOM and with functional components)
my sample code here:
export default function DrawerSider() {
const history = useHistory();
const [selectedKey, setSelectedKey] = useState("sub1")
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
console.log(key)
}
return (
<Sider width={200} className="site-layout-background">
<Menu
defaultSelectedKeys={selectedKey}
mode="inline"
style={{ height: "100%", borderRight: 0 }}
>
<Menu.Item
key="sub1"
icon={<HomeOutlined />}
onClick={() => handleSelectKey("sub1","/dashboard/resumo")}
>
Dashboard
</Menu.Item>
<SubMenu key="sub2" icon={<UserOutlined />} title="Usuários">
<Menu.Item
key="1"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("1","/usuarios/novo")}
>
Adicionar usúario
</Menu.Item>
<Menu.Item
key="2"
icon={<TableOutlined />}
onClick={() => handleSelectKey("2","/usuarios/todos")}
>
Todos usúarios
</Menu.Item>
</SubMenu>
<SubMenu key="sub3" icon={<FormOutlined />} title="Formulários">
<Menu.Item
key="3"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("3","/formularios/novo")}
>
Adicionar formulário
</Menu.Item>
</SubMenu>
</SubMenu>
</Menu>
</Sider>
);
}
Obs: I'm using ant design lib
Someone knows how to make it works?
I tried to use a const on click event to set a state of the selected key , but it didn't work
There is no onClick on Menu.Item
only Menu component has onClick or onSelect, both will give you callback of the "clicked" key, the differences is onClick is for any menu item click (including expanding menu) while on select is when you select an actual menu item.
There are 2 ways to get the history string:
1 - use the history string as menuItem key:
the issue would be you cannot have 2 menu item that has same history key
2 - have a map which maps the menu key to history string
see below demo:
https://codesandbox.io/s/inline-menu-antd-4-18-2-forked-bgwyj?file=/index.js
const handleOnSelect = ({ item, key, keyPath, selectedKeys, domEvent }) => {
console.log(item);
console.log(keyPath);
console.log(selectedKeys);
console.log(domEvent);
handleSelectKey(key);
};
...
..
<Menu
onSelect={handleOnSelect}
...
>
...
</Menu>
You need to save the selected key as part of the component's state. You can set the current selected key as part of the onClick.
const selectedKey, setSelectedKey = useState("defaultSelectedKey")
Not sure what the useHistory hook is for but if you need to set that onClick as well as the selectedKey, move it all into one function like so
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
}
...
onclick={() => handleSelectedKey("3", "/usuarios/novo")}
https://ant.design/components/menu/
EDIT
Based on research into your design library, your onclick and handle select must be at the top level of the menu component. Just copy the syntax that they use in their example code.
https://ant.design/components/menu/
I got something like this
<Box>
<SomethingA>
</Box
I want to replace Box.SomethingA to Box.SomethingB
<Box>
<SomethingB>
</Box>
How I can do it?
I am guessing you want to replace it after an event has occurred, for ex: button click.
You can try this:
function App(){
const [box, changeBox] = useState(true)
return(
<div>
<button onClick={()=>{changeBox(!box)}} >Click</button>
<Box>
{ box ? <SomethingA/> : <SomethingB/>}
</Box>
</div>
)
}
I am new to React and am building a tab component using Material UI's Tabs component. I'd like to place the Material UI badge component within the Tab component's label prop, but I'm not sure how to go about this.
The Tab component looks as such:
<Tab
key={i}
label={label}
{...globalTabProps}
{...tabProps}
classes={{
wrapper: cx('MuiTab-wrapper'),
}}
/>
I'm trying to add the badge as such:
const label = {
<Badge
color="primary"
className={
badgeProps.badgeContent === ''
? classNames(classes.MuiBadge, classes.MuiBadgeDotted)
: classNames(classes.MuiBadge, classes.MuiBadgeNumber)
}
badgeContent={''}
invisible={false}
{...globalBadgeProps}
{...badgeProps}
></Badge>
};
Of course, this errors out (parsing error), but I don't think this is the correct way to handle this anyway.
Would anyone be able to point me in the right direction?
Many thanks!
You should wrap it with (), like so.
const label = (
<Badge
color="primary"
className={
badgeProps.badgeContent === ''
? classNames(classes.MuiBadge, classes.MuiBadgeDotted)
: classNames(classes.MuiBadge, classes.MuiBadgeNumber)
}
badgeContent={''}
invisible={false}
{...globalBadgeProps}
{...badgeProps}
></Badge>
)
Note the () wrapping it.
Then do it like so:
<Tab
key={i}
label={label}
{...globalTabProps}
{...tabProps}
classes={{
wrapper: cx('MuiTab-wrapper'),
}}
/>
What it is done inside:
const WhateverComponent = (props) => (
<div>
...
{props.label}
</div>
);
currently I am working on a project with React and Material UI. I want to hover on tabs that will open an menu, but this doesn't really work. I am hoping that you guys can help me (and maybe tell me if I'm approaching this correctly)
Where my tabs are basing of: https://imgur.com/a/HeiL2xo
My current project: https://imgur.com/a/Ik5NEkF
AppBarTop class
class AppBarTop extends Component {
state = {
value: 0,
open: false,
anchorEl: null
};
handleMenuClick = (index) => {
}
handleMenuOpen = (index, event) => {
const {currentTarget} = event;
this.setState({
open: !this.state.open,
anchorEl: currentTarget,
value: index
})
};
handleMenuClose = () => {
this.setState({
open: false,
anchorEl: null,
})
}
handleInputSearch = () => {
};
render() {
const {classes} = this.props;
const {anchorEl, open} = this.state;
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<img src={buddies} alt={"buddies"} height={50} width={50}/>
<div className={classes.grow}/>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon/>
</div>
<InputBase
placeholder="Search…"
onChange={this.handleInputSearch}
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
/>
</div>
<div className={classes.grow}/>
<List>
{TopMenu.map((item, index) => (
<Tab key={index} component={Link} to={{pathname: item.pathname}}
classes={{root: classes.tabItem}} label={item.label}/>
))}
</List>
</Toolbar>
<Paper className={classes.grow}>
<Tabs
value={this.state.value}
indicatorColor="primary"
textColor="primary"
centered>
{BottomMenu.map((item, index) => (
<Tab
key={index}
onMouseOver={this.handleMenuOpen.bind(this, index)}
data-key={index}
classes={{root: classes.tabItem}}
label={item.label}
aria-owns={open ? 'menu-list-grow' : undefined}
aria-haspopup={"true"}/>
))}
</Tabs>
<Popper open={open} anchorEl={anchorEl} id="menu-list-grow">
<Paper>
<MenuList>
{BottomMenu[this.state.value].items.map((item, index) => (
<MenuItem key={index} onClick={this.handleMenuClose}>{item}</MenuItem>
))}
</MenuList>
</Paper>
</Popper>
</Paper>
</AppBar>
</div>
);
}
}
export default withStyles(styles)(AppBarTop)
The key problem here is that the onMouseOver event handler is fired multiple times as you move around the <Tab> component. Your handleMenuOpen function is not built to handle this.
I've replicated your issue in a CodeSandbox here: https://codesandbox.io/s/qkw8rr4mk4
The following 3 points will fix your menu issues:
Change handleMenuOpen to be functional by explicitly setting open: true
Use onMouseEnter rather than onMouseOver. This is not required but it makes for more predictable functionality as onMouseEnter is only called once
To automatically close your menu when your mouse leaves them add the onMouseLeave={this.handleMenuClose.bind(this)} property to your parent <div> component
A CodeSandbox with the above 3 points implemented can be found at: https://codesandbox.io/s/6x9w9m6n7r