Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First implementation of the Grid component #1571

Merged
merged 10 commits into from Apr 28, 2023
2 changes: 1 addition & 1 deletion app/src/paths.js
Expand Up @@ -224,4 +224,4 @@ const componentsMap = [
},
];

export default componentsMap;
export default componentsMap;
219 changes: 219 additions & 0 deletions lib/src/grid/Grid.stories.tsx
@@ -0,0 +1,219 @@
import React from "react";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import styled from "styled-components";
import DxcGrid from "./Grid";
import DxcInset from "../inset/Inset";

export default {
title: "Grid",
component: DxcGrid,
};

export const Chromatic = () => (
<>
<Title title="Default" level={4} />
<ExampleContainer>
<DxcGrid>
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
</DxcGrid>
</ExampleContainer>
<Title title="Place items" level={4} />
<ExampleContainer>
<DxcGrid templateRows={["200px"]} placeItems="center">
<ColoredContainer height="50px" width="50px" />
</DxcGrid>
<DxcGrid placeItems={{ justifyItems: "end" }}>
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
</DxcGrid>
<DxcGrid templateRows={["repeat(3, 100px)"]} placeItems={{ alignItems: "end", justifyItems: "center" }}>
<ColoredContainer height="50px" width="50px" />
<ColoredContainer height="50px" width="50px" />
<ColoredContainer height="50px" width="50px" />
</DxcGrid>
</ExampleContainer>
<Title title="Place content" level={4} />
<Container height="200px">
<DxcGrid placeContent="center">
<ColoredContainer height="50px" width="50px" />
<ColoredContainer height="50px" width="50px" />
</DxcGrid>
</Container>
<Container height="200px">
<DxcGrid placeContent={{ alignContent: "center" }}>
<ColoredContainer height="50px" width="50px" />
</DxcGrid>
</Container>
<Container height="200px">
<DxcGrid placeContent={{ alignContent: "center", justifyContent: "end" }}>
<ColoredContainer height="50px" width="50px" />
</DxcGrid>
</Container>
<Title title="Place self" level={4} />
<ExampleContainer>
<DxcGrid templateRows={["repeat(3, 100px)"]}>
<DxcGrid.GridItem placeSelf="center">
<ColoredContainer height="50px" width="50px" />
</DxcGrid.GridItem>
<DxcGrid.GridItem placeSelf={{ alignSelf: "end" }}>
<ColoredContainer height="40px" width="40px" />
<ColoredContainer height="30px" width="30px" />
</DxcGrid.GridItem>
<DxcGrid.GridItem placeSelf={{ alignSelf: "center", justifySelf: "end" }}>
<ColoredContainer height="50px" width="50px" />
</DxcGrid.GridItem>
</DxcGrid>
</ExampleContainer>
<Title title="Halstack layout using template areas" level={4} />
<ExampleContainer>
<DxcGrid
templateColumns={["repeat(4, 1fr)"]}
templateRows={["40px", "200px", "60px"]}
templateAreas={["header header header header", "sidenav main main main", "sidenav footer footer footer"]}
gap={{ rowGap: "0.5rem", columnGap: "1rem" }}
>
<DxcGrid.GridItem areaName="header" as="header">
<ColoredContainer height="100%" />
</DxcGrid.GridItem>
<DxcGrid.GridItem areaName="main" as="main">
<ColoredContainer height="100%" />
</DxcGrid.GridItem>
<DxcGrid.GridItem areaName="sidenav" as="nav">
<ColoredContainer height="100%" />
</DxcGrid.GridItem>
<DxcGrid.GridItem areaName="footer" as="footer">
<ColoredContainer height="100%" />
</DxcGrid.GridItem>
</DxcGrid>
</ExampleContainer>
<Title title="Template rows and columns with flexible sizes" level={4} />
<ExampleContainer>
<DxcGrid templateColumns={["1fr", "1fr", "1fr"]} templateRows={["1fr", "3fr", "1fr"]} gap="0.5rem">
<DxcGrid.GridItem column={{ start: 1, end: -1 }}>
<ColoredContainer color="yellow" height="100%">
Header
</ColoredContainer>
</DxcGrid.GridItem>
<DxcGrid.GridItem column={1}>
<ColoredContainer color="lightcyan" height="100%">
Sidenav
</ColoredContainer>
</DxcGrid.GridItem>
<DxcGrid
column={{ start: 2, end: -1 }}
templateRows={["repeat(4, 1fr)"]}
templateColumns={["repeat(2, 1fr)"]}
gap="1rem"
>
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
<ColoredContainer />
</DxcGrid>
<DxcGrid.GridItem column={{ start: 1, end: -1 }}>
<ColoredContainer color="black" height="100%">
Footer
</ColoredContainer>
</DxcGrid.GridItem>
</DxcGrid>
</ExampleContainer>
<Title title="Overlapping" level={4} />
<DxcInset bottom="2rem">
<ExampleContainer>
<DxcGrid templateRows={["50px", "50px"]}>
<ColoredContainer color="yellow" height="100px">
1
</ColoredContainer>
<ColoredContainer color="transparent" height="100px">
2
</ColoredContainer>
</DxcGrid>
</ExampleContainer>
</DxcInset>
<Title title="Implicit rows and columns" level={4} />
<ExampleContainer>
<DxcGrid templateColumns={["50px"]} templateRows={["50px", "50px"]} autoRows="50px" autoColumns="50px">
<DxcGrid.GridItem>
<ColoredContainer height="50px">1</ColoredContainer>
</DxcGrid.GridItem>
<DxcGrid.GridItem row={2}>
<ColoredContainer height="50px">3</ColoredContainer>
</DxcGrid.GridItem>
<DxcGrid.GridItem row={6} column={1}>
<ColoredContainer height="50px">5</ColoredContainer>
</DxcGrid.GridItem>
<DxcGrid.GridItem row={3}>
<ColoredContainer height="50px">4</ColoredContainer>
</DxcGrid.GridItem>
<DxcGrid.GridItem row={{ start: 1, end: 2 }} column={{ start: 5, end: "span 2" }}>
<ColoredContainer height="50px">2</ColoredContainer>
</DxcGrid.GridItem>
</DxcGrid>
</ExampleContainer>
<Title title="Autoflow 'row' (default)" level={4} />
<ExampleContainer>
<DxcGrid templateColumns={["repeat(5, 1fr)"]} templateRows={["1fr", "1fr"]} autoFlow="row" autoColumns="1fr">
<DxcGrid.GridItem row={{ start: 1, end: "span 2" }} column={1}>
<ColoredContainer height="100%">1</ColoredContainer>
</DxcGrid.GridItem>
<ColoredContainer color="lightyellow">2</ColoredContainer>
<ColoredContainer color="lightcyan">3</ColoredContainer>
<ColoredContainer color="lightgreen">4</ColoredContainer>
<DxcGrid.GridItem row={{ start: 1, end: -1 }} column={-2}>
<ColoredContainer color="lightpink" height="100%">
5
</ColoredContainer>
</DxcGrid.GridItem>
</DxcGrid>
</ExampleContainer>
<Title title="Autoflow 'column'" level={4} />
<ExampleContainer>
<DxcGrid templateColumns={["repeat(5, 1fr)"]} templateRows={["1fr", "1fr"]} autoFlow="column" autoColumns="1fr">
<DxcGrid.GridItem row={{ start: 1, end: -1 }} column={1}>
<ColoredContainer height="100%">1</ColoredContainer>
</DxcGrid.GridItem>
<ColoredContainer color="lightyellow">2</ColoredContainer>
<ColoredContainer color="lightcyan">3</ColoredContainer>
<ColoredContainer color="lightgreen">4</ColoredContainer>
<DxcGrid.GridItem row={{ start: 1, end: -1 }} column={-2}>
<ColoredContainer color="lightpink" height="100%">
5
</ColoredContainer>
</DxcGrid.GridItem>
</DxcGrid>
</ExampleContainer>
</>
);

const Container = styled.div<{ height?: string }>`
display: grid;
overflow: auto;
margin: 2.5rem;
${({ height }) => height && `height: ${height}`};
`;

const ColoredContainer = styled.div<{ color?: string; width?: string; height?: string }>`
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
background-color: ${({ color }) => color ?? "#e5d5f6"};
padding: 1rem;
border: 1px solid #a46ede;
border-radius: 0.5rem;
font-family: Open Sans, sans-serif;
font-size: 1.5rem;
font-weight: bold;
color: #a46ede;

${({ width }) => width && `width: ${width}`};
${({ height }) => height && `height: ${height}`};
`;
61 changes: 61 additions & 0 deletions lib/src/grid/Grid.tsx
@@ -0,0 +1,61 @@
import React from "react";
import styled from "styled-components";
import GridPropsType, { GridItemProps } from "./types";

const DxcGrid = (props: GridPropsType): JSX.Element => <Grid {...props} />;

const Grid = styled.div<GridPropsType>`
display: grid;
${({ templateColumns }) => templateColumns && `grid-template-columns: ${templateColumns.join(" ")};`}
${({ templateRows }) => templateRows && `grid-template-rows: ${templateRows.join(" ")};`}
${({ templateAreas }) => templateAreas && `grid-template-areas: ${templateAreas.map((row) => `"${row}"`).join(" ")};`}
${({ autoColumns }) => autoColumns && `grid-auto-columns: ${autoColumns};`}
${({ autoRows }) => autoRows && `grid-auto-rows: ${autoRows};`}
${({ autoFlow }) => autoFlow && `grid-auto-flow: ${autoFlow};`}
${({ gap }) =>
gap != null &&
(typeof gap === "string" ? `gap: ${gap};` : `row-gap: ${gap.rowGap ?? ""}; column-gap: ${gap.columnGap ?? ""};`)}
${({ placeItems }) =>
placeItems &&
(typeof placeItems === "string"
? `place-items: ${placeItems}`
: `align-items: ${placeItems.alignItems ?? ""}; justify-items: ${placeItems.justifyItems ?? ""};`)}
${({ placeContent }) =>
placeContent &&
(typeof placeContent === "string"
? `place-content: ${placeContent}`
: `align-content: ${placeContent.alignContent ?? ""}; justify-content: ${placeContent.justifyContent ?? ""};`)}

${({ areaName }) => areaName && `grid-area: ${areaName};`}
${({ column }) =>
column &&
`grid-column: ${
typeof column === "string" || typeof column === "number" ? column : `${column.start} / ${column.end};`
};`}
${({ row }) =>
row && `grid-row: ${typeof row === "string" || typeof row === "number" ? row : `${row.start} / ${row.end};`};`}
${({ placeSelf }) =>
placeSelf &&
(typeof placeSelf === "string"
? `place-self: ${placeSelf}`
: `align-self: ${placeSelf.alignSelf ?? ""}; justify-self: ${placeSelf.justifySelf ?? ""};`)}
`;

const GridItem = styled.div<GridItemProps>`
${({ areaName }) => areaName && `grid-area: ${areaName};`}
${({ column }) =>
column &&
`grid-column: ${
typeof column === "string" || typeof column === "number" ? column : `${column.start} / ${column.end};`
};`}
${({ row }) =>
row && `grid-row: ${typeof row === "string" || typeof row === "number" ? row : `${row.start} / ${row.end};`};`}
${({ placeSelf }) =>
placeSelf &&
(typeof placeSelf === "string"
? `place-self: ${placeSelf}`
: `align-self: ${placeSelf.alignSelf ?? ""}; justify-self: ${placeSelf.justifySelf ?? ""};`)}
`;

DxcGrid.GridItem = GridItem;
export default DxcGrid;
46 changes: 46 additions & 0 deletions lib/src/grid/types.ts
@@ -0,0 +1,46 @@
type Spaces = "0rem" | "0.125rem" | "0.25rem" | "0.5rem" | "1rem" | "1.5rem" | "2rem" | "3rem" | "4rem" | "5rem";
type Gap = { rowGap: Spaces; columnGap?: Spaces } | { rowGap?: Spaces; columnGap: Spaces } | Spaces;
type GridCell = { start: number | string; end: number | string };

type PlaceSelfValues = "auto" | "start" | "end" | "center" | "stretch" | "baseline";
type PlaceContentValues =
| "normal"
| "start"
| "end"
| "center"
| "stretch"
| "space-between"
| "space-around"
| "space-evenly"
| "baseline";
type PlaceItemsValues = "normal" | "start" | "end" | "center" | "stretch" | "baseline";
type PlaceObject<Type, Suffix extends string> = {
[Property in keyof Type as `${string & Property}${Capitalize<string & Suffix>}`]: Type[Property];
};
type PlaceGeneric<PlaceValues, Element extends string> =
| PlaceObject<{ justify?: PlaceValues; align: PlaceValues }, Element>
| PlaceObject<{ justify: PlaceValues; align?: PlaceValues }, Element>
| PlaceValues;

export type GridItemProps = {
areaName?: string;
column?: number | string | GridCell;
row?: number | string | GridCell;
placeSelf?: PlaceGeneric<PlaceSelfValues, "self">;
as?: keyof HTMLElementTagNameMap;
children: React.ReactNode;
};

type Props = GridItemProps & {
autoColumns?: string;
autoRows?: string;
autoFlow?: "row" | "column" | "row dense" | "column dense";
gap?: Spaces | Gap;
placeContent?: PlaceGeneric<PlaceContentValues, "content">;
placeItems?: PlaceGeneric<PlaceItemsValues, "items">;
templateAreas?: string[];
templateColumns?: string[];
templateRows?: string[];
};

export default Props;
2 changes: 2 additions & 0 deletions lib/src/main.ts
Expand Up @@ -39,6 +39,7 @@ import DxcFlex from "./flex/Flex";
import DxcTypography from "./typography/Typography";
import DxcParagraph from "./paragraph/Paragraph";
import DxcBulletedList from "./bulleted-list/BulletedList";
import DxcGrid from "./grid/Grid";

import HalstackContext, { HalstackProvider, HalstackLanguageContext } from "./HalstackContext";
import { BackgroundColorProvider } from "./BackgroundColorContext";
Expand Down Expand Up @@ -89,4 +90,5 @@ export {
DxcTypography,
DxcParagraph,
DxcBulletedList,
DxcGrid,
};