Search code examples
cssreactjstextlayoutmaterial-ui

React - Mapping variables with a consistent layout


My goal is to have a container, something like @mui/Box, that displays rows of an icon and two texts. The icon and the first text are a unit and the second text is its own. Both the first, and the second texts of each row are dynamic texts, they can vary in length. I want that all 3 elements always start at the same position no matter the content of the texts. So something like this:

Desired outcome

And even when a texts changes in length, each of the 3 elements start at the same position

Desired outcome with longer label

But I just can't get it to work. I tried mapping each iconText into their own Box, but then the layouts always differ. Probably because they are each in their own container, so I thought maybe I can just map the variables of each iconText as an element and have them all positioned by a grid. And that does work (that's the layout of the images above), but then I have the issue that those elements are only wrapped in a react fragment and you can't set the key property on that.

I tried so many different layouts and looked for anything about dynamic text layouts, but nothing worked right.

The layout with grid looks like this

<Box sx={{ display: "grid", gridTemplateColumns: "1fr auto", columnGap: 3 }}>
    {iconTexts.map((iconText, index) => (
        <>
            <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
              {iconText.icon}
              <Typography variant="h6" component={"h3"} sx={{ color: "#394454" }}>
                {iconText.label}
              </Typography>
            </Box>
            <Typography variant="h4" component={"h2"}>
              {iconText.value}
            </Typography>
        </>
    ))}
</Box>

Which would work so well, but I can't set the keys.

My question is how can I achieve the desired dynamic text layout with either grid or flex, or actually any styling.

You can look at the example in this codesandbox.


Solution

  • Grid is good solution. But since you show the list of Box, please wrap the card with Box instead of Fragment, and use key attribute.

    like this.

    <Box sx={{ display: "grid", gridTemplateColumns: "1fr auto", columnGap: 3 }}>
        {iconTexts.map((iconText, index) => (
            <Box key={index} sx={{ display: "contents" }}>
                <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
                    {iconText.icon}
                    <Typography variant="h6" component="h3" sx={{ color: "#394454" }}>
                        {iconText.label}
                    </Typography>
                </Box>
                <Typography variant="h4" component="h2">
                    {iconText.value}
                </Typography>
            </Box>
        ))}
    </Box>