I need to map icons to my app bar.
It does not show anything. Once I try it the app bar component does not show any icons.
I am not sure how to map through a jsx component inside an array so I can show it in the app bar structure I already have.
-icons.js---------------
import { Download, GitHub, Mail, WhatsApp } from "#mui/icons-material";
export const icons = [
{
id: 1,
ariaLabel: "whatsapp",
tooltip: "Chat in whatsapp",
icon: WhatsApp,
},
{
id: 2,
ariaLabel: "github",
tooltip: "View github profile",
icon: GitHub,
},
{
id: 3,
ariaLabel: "mail",
tooltip: "Write email",
icon: Mail,
},
{
id: 4,
ariaLabel: "curriculum vitae",
tooltip: "Dowloand CV",
icon: Download,
},
];
-Appbar.jsx------------
<Appbar>
{icons.map((Icon) => (
<Box key={Icon.id} sx={{ mr: 10 }}>
<Tooltip title={Icon.tooltip}>
<IconButton aria-label={Icon.ariaLabel}>
<Icon />
</IconButton>
</Tooltip>
</Box>
))}
</Appbar>;
after mapping icons you can access the name of your component icon like this <Icon.icon /> . So just change <Icon /> to <Icon.icon /> .
this is a demo in codesandbox to show you
<Appbar>
{icons.map((Icon) => {
return(
<Box key={Icon.id} sx={{ mr: 10 }}>
<Tooltip title={Icon.tooltip}>
<IconButton aria-label={Icon.ariaLabel}>
<Icon />
</IconButton>
</Tooltip>
</Box>
)})}
</Appbar>;
Code <== The answer would be like
<div>
{icons.map((item, index) => (
<div key={index}>
{item.tooltip}
<item.icon />
</div>
))}
</div>
Related
I have 3 cards I want to render on the screen all with a similar layout. What is this pattern called when we have a component as a value?
const steps = [
{
label: "Order details",
component: OrderDateStep,
},
{
label: "Driver details",
component: OrderDriverStep,
},
{
label: "Acknowledgements",
component: OrderAcknowledgementStep,
},
];
Additionally I keep running into an issue when these are conditionally rendered. I want to wait until stripe has initialised before displaying the form. However, I get an error Error: Rendered more hooks than during the previous render.. I know I can just add the different components but that isn't very scalable. Is there another way I can achieve this re-usable pattern without running into this issue with the number of hooks changing? Why does using step[x].component() change the number of hooks where just using the component does not?
{stripe && (
<Elements
stripe={stripe}
options={{
clientSecret: paymentIntent?.client_secret,
}}
>
{steps.map((step, index) => {
return (
<Box
key={step.label}
sx={{
mt: 3,
}}
>
<Box sx={{ my: 2 }}>
<Typography variant="h5">{step.label}</Typography>
</Box>
{step.component()}
</Box>
);
})}
<Box sx={{ display: "flex", justifyContent: "end" }}>
<Button variant="contained" onClick={submitForm}>
Submit
</Button>
</Box>
</Elements>
)}
If you want to make sure something is filled, or rendered, before displaying other data in react, you can just do
{
loadedVariable ?
<div>
......
</div>
:null
}
If your question is not fully answered by the point i get home i'll be happy to help you further.
Add one more conditionally into your render component to make sure that steps had filled:
{stripe && steps.length && (
<Elements
stripe={stripe}
options={{
clientSecret: paymentIntent?.client_secret,
}}
>
{steps.map((step, index) => {
return (
<Box
key={step.label}
sx={{
mt: 3,
}}
>
<Box sx={{ my: 2 }}>
<Typography variant="h5">{step.label}</Typography>
</Box>
{step.component()}
</Box>
);
})}
<Box sx={{ display: "flex", justifyContent: "end" }}>
<Button variant="contained" onClick={submitForm}>
Submit
</Button>
</Box>
</Elements>
)}
How to create multi-level navigation menu with MaterialUI and TypeScript?
I would like to add to the '/questions' the follwing:
2 navigation menu:
/questions/Tags
/questions/Users
like as in the Screenshot
export function NavBar(...) {
return (
<>
<Drawer>
<List>
<NavItem
to="/questions"
primary="questions"
icon={CloudOff}
/>
)}
<NavItem to="/questions" primary="questions" icon={Window} />
</List>
</Drawer>
</>
);
}
Lot of posibility...
The best way it's to prepare an array with the menu information and map on, something like this :
const TestArea = ({ params }) => {
const menu = [{ mainMenu: "tata", subMenu: ["toto", "titi"] }];
return (
<>
<Toolbar
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
}}
>
{menu.map((item) => {
return (
<>
<Typography variant="overline">{item.mainMenu}</Typography>
{item.subMenu.map((subItem) => {
return (
<Typography variant="caption" style={{ marginLeft: "40%" }}>
{subItem}
</Typography>
);
})}
</>
);
})}
</Toolbar>
</>
);
};
With this base, you can customize with the component of your choose, render element like link with a path to...
Button color="inherit" component={Link} to="/classic">
Yes, I know, it's plain JSX not TSX, it's just for example.
If you need more information, say me !
I am complete my project. Now I am trying to get a production build. But I am getting an error with name "Component definition is missing display name". I am using nextjs.
From my whole proect only two component, show that error like image-
Clikc to see the image. I use material-table-core with mui5 in both component.
I am fully confused why I am seeing this error?
One of that component is-
import React from "react";
import { forwardRef } from "react";
import { Box, Stack, SvgIcon, Typography, ButtonBase, InputBase, Rating } from "#mui/material";
import { Star, ArrowDownward } from '#mui/icons-material';
import MaterialTable, { Column } from "#material-table/core";
//Custom Icon
import { DeleteIcon, ArrowUp, ArrowDown } from "Utilis/Icons";
//Theme
import theme from "Theme";
//Styles
import useStyles from "Styles/Common/DataTable.styles";
//Data
import cartData from "Data/Cart.data";
const DataTable = () => {
const classes = useStyles();
const columns = [
{ title: 'Product', field: 'product', cellStyle: { width: "75%" } },
{ title: 'Price', field: 'price', cellStyle: { width: "5%" } },
{ title: 'Quantity', field: 'quantity', cellStyle: { width: "10%" } },
{ title: 'Subtotal', field: 'subtotal', cellStyle: { width: "5%" } },
{ title: 'Remove', field: 'action', cellStyle: { width: "5%" } }
];
const data = [];
cartData && cartData.forEach(cart => {
data.push({
product: <Stack direction="row" sx={{ alignItems: "center" }}>
<Box>
<Box component="img" className={classes.ProductImages} src={cart.product.images[0].url} alt="Product Image" />
</Box>
<Box>
<Typography className={classes.ProductTitle} variant="h6" component="h6">
{cart.product.name}
</Typography>
<Rating
name="half-rating-read"
defaultValue={5}
precision={4}
readOnly
className={classes.Star}
emptyIcon={<Star fontSize="inherit" />}
/>
</Box>
</Stack>,
price: <Typography className={classes.ProductPrice} variant="h6" component="h6">
${cart.product.price}
</Typography>,
quantity:
<Box sx={{ position: "relative" }} className="sdfjshfggb">
<InputBase
className={classes.InputField}
value={cart.count}
type="text"
/>
<Box className={classes.CounterButton}>
<ButtonBase>
<SvgIcon viewBox="0 0 23 13">
{ArrowUp}
</SvgIcon>
</ButtonBase>
<ButtonBase>
<SvgIcon viewBox="0 0 23 13">
{ArrowDown}
</SvgIcon>
</ButtonBase>
</Box>
</Box>,
subtotal:
<Typography variant="h6" component="h6" className={classes.ProductPriceTotal}>
${cart.product.price * cart.count}
</Typography>,
action:
<ButtonBase className={classes.RemoveButton} >
<SvgIcon viewBox="0 0 24 24">{DeleteIcon}</SvgIcon>
</ButtonBase >
})
})
return (
<Box className={classes.DataTable}>
<MaterialTable
columns={columns}
data={data}
options={{
search: false,
showTitle: false,
toolbar: false,
selection: true,
paging: false,
headerStyle: {
backgroundColor: theme.palette.primary.input_bg,
color: theme.palette.primary.main,
}
}}
localization={{
body: {
emptyDataSourceMessage: (
<Typography variant="body1" component="p">
Cart is Empty!
</Typography>
),
},
}}
icons={{ SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref} />) }}
onSelectionChange={(e) => console.log(e)}
/>
</Box>
);
};
export default DataTable;
I am new in react/nextjs. This is my first project. Anyone can help me?
You are using an arrow funciton. You can either switch to a regular function, which will solve the problem of the displayName or you can set the displayName of the arrow function right before exporting it. Exporting an arrow function directly doesn't give the component a displayName.
DataTable.displayName = "DataTable"
export default DataTable;
I don't know what I am doing wrong but I cannot access the option.slug from the object I receive from the API. I am trying to change pass a URL slug and change the route of my app. I am trying to do this using the onChange prop inside my useAutocomplete() definition.
Accessing the option.slug or any other property works well inside of my JSX structure. As you can see I am grabbing option.title and so on to render my list items which works well...
Instead of grabbing the actual slug I keep getting the route of "/components/undefined"
But when I try accessing the option.slug on the top level of my component in the useAutocomplete() definition it doesn't seem to work. Based on the original documentation though, that's the way to do it.
https://material-ui.com/components/autocomplete/#useautocomplete
export default function NavigationSearch({ onClose, results }) {
const router = useRouter();
const {
getRootProps,
getInputLabelProps,
getInputProps,
getListboxProps,
getOptionProps,
groupedOptions
} = useAutocomplete({
id: 'use-autocomplete-demo',
options: results,
getOptionLabel: (option) => option.title,
onChange: (option) => router.push(`/components/${option.slug}`)
});
return (
<React.Fragment>
{/* 1. SEARCH INPUT */}
<Container maxWidth="md" disableGutters>
<Box display="flex" width="100%">
<motion.div
style={{ width: 'inherit' }}
initial="hidden"
animate="visible"
variants={animation}>
<Box display="flex" width="inherit">
<Search width="inherit" {...getRootProps()}>
<SearchIcon color="primary" />
<InputBase
placeholder="Search for components, patterns..."
style={{ color: 'inherit', width: 'inherit' }}
autoFocus
{...getInputProps()}
/>
</Search>
</Box>
</motion.div>
<IconButton color="primary" onClick={onClose}>
<CloseIcon />
</IconButton>
</Box>
</Container>
{/* SEARCH RESULTS */}
<BackdropOverlay open={true} onClick={onClose}>
<Container maxWidth="md" disableGutters>
{groupedOptions.length > 0 ? (
<Results>
<List
{...getListboxProps()}
subheader={
<Box paddingX={2}>
<Typography
variant="overline"
color="textSecondary"
gutterBottom>
Popular search results
</Typography>
</Box>
}>
{groupedOptions.map((option, index) => (
<Item
button
key={option.title}
{...getOptionProps({
option,
index
})}>
<Box
display="flex"
justifyContent="space-between"
width="100%">
<Box>
<Typography color="textPrimary">
{option.title}
</Typography>
<Typography variant="caption" color="textSecondary">
{option.type}
</Typography>
</Box>
<Box alignSelf="flex-end">
<Typography variant="caption" color="textSecondary">
/components/button
</Typography>
</Box>
</Box>
</Item>
))}
</List>
</Results>
) : null}
</Container>
</BackdropOverlay>
</React.Fragment>
);
}
This is the API response that I store in the results prop:
0: {id: 1, title: "Button", slug: "button"}
1: {id: 2, title: "Switch", slug: "switch"}
2: {id: 3, title: "Tags", slug: "tags"}
3: {id: 4, title: "Checkbox", slug: "checkbox"}
4: {id: 5, title: "Toast", slug: "toast"}
Autocomplete component uses useAutocomplete hook under the hood so they share the same API. In Autocomplete API. This is the signature of onChange:
function(event: object, value: T | T[], reason: string) => void
The second argument is the option value which is what you need here. So change your code to:
onChange: (_, option) => router.push(`/components/${option.slug}`)
To fix the undefined value issue.
I have some chips that work like tabs on my page. They are mutually exclusive and change color when selected. How do I achieve this.
I think it has got something to do with variant = "outlined". I do want the initial chip to have 'outlined' and onClick, it needs to have a background
const theme = createMuiTheme({
overrides: {
MuiChip: {
clickableColorSecondary: {
'&:hover, &:focus': {
backgroundColor: 'red',
},
'&:active': {
backgroundColor: 'green',
},
},
}
}
});
Chips
<Badge badgeContent={4} color="secondary" classes={{ badge: classes.badge }} >
<Chip
label="Label 1"
clickable
className={classes.chip}
color="secondary"
variant="outlined"
onClick={handleClick}
/>
</Badge>
<Badge badgeContent={4} color="secondary" classes={{ badge: classes.badge }} >
<Chip
label="Label 2"
clickable
className={classes.chip}
color="secondary"
variant="outlined"
onClick={handleClick}
/>
</Badge>
<Badge badgeContent={4} color="secondary" classes={{ badge: classes.badge }} >
<Chip
label="Label 3"
clickable
className={classes.chip}
color="secondary"
variant="outlined"
onClick={handleClick}
/>
</Badge>
This does not override the selected chip style as I expected
I also tried wrapping it in another theme
const chipsTheme = createMuiTheme({
overrides: {
MuiChip: {
clickableColorPrimary: {
'&:hover, &:focus': {
backgroundColor: 'red',
},
'&:active': {
backgroundColor: 'green',
},
},
clickableColorSecondary: {
'&:hover, &:focus': {
backgroundColor: 'red',
},
'&:active': {
backgroundColor: 'green',
},
}
}
}
});
<MuiThemeProvider theme={chipsTheme}>
<Chip
label="INVENTORY"
clickable
className={classes.chip}
color="secondary"
variant="outlined"
onClick={handleClick}
/>
</MuiThemeProvider>
But it does not change its style at all. How Can I make its change color on click and also clear other colors when another chip is clicked?