Skip to content

Commit

Permalink
feat: Add a DescriptionList component
Browse files Browse the repository at this point in the history
  • Loading branch information
haideralsh committed Dec 13, 2024
1 parent 104b4dc commit 92a6a51
Show file tree
Hide file tree
Showing 8 changed files with 468 additions and 1 deletion.
46 changes: 46 additions & 0 deletions src/DescriptionList/DescriptionDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { styled } from "styled-components";
import { useDescriptionListContext } from "./DescriptionListContext";

export const DescriptionDetails = styled.dd(({ theme }) => {
const { autoLayoutBreakpoint, showDivider, density, layout } = useDescriptionListContext();

return {
margin: 0,
paddingLeft: theme.space.none,
paddingRight: theme.space.none,
color: theme.colors.darkGrey,

...(density === "compact" && {
paddingTop: theme.space.x0_25,
paddingBottom: theme.space.x0_25,
}),
...(density === "medium" && {
paddingTop: theme.space.x0_75,
paddingBottom: theme.space.x0_75,
}),
...(density === "relaxed" && {
paddingTop: theme.space.x1_5,
paddingBottom: theme.space.x1_5,
}),

...(showDivider &&
layout === "inline" && {
borderTopWidth: "1px",
borderTopStyle: "solid",
borderTopColor: theme.colors.lightGrey,
}),

[`@container (min-width: ${autoLayoutBreakpoint})`]: {
...(showDivider &&
layout !== "stacked" && {
borderTopWidth: "1px",
borderTopStyle: "solid",
borderTopColor: theme.colors.lightGrey,
}),

"&:nth-child(2)": {
border: "none",
},
},
};
});
215 changes: 215 additions & 0 deletions src/DescriptionList/DescriptionList.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import React from "react";
import { Flex } from "../Flex";
import { Heading4 } from "../Type";
import { Link } from "../Link";
import { Icon } from "../Icon";
import { StatusIndicator } from "../StatusIndicator";
import { DescriptionDetails, DescriptionList, DescriptionTerm } from ".";

export default {
title: "Components/DescriptionList",
component: DescriptionList,
};

const SampleContent = () => (
<>
<DescriptionTerm>Customer</DescriptionTerm>
<DescriptionDetails>Nulogy</DescriptionDetails>
<DescriptionTerm>
<Flex display="inlineFlex" as="span" alignItems="center" gap="half">
Order number <Icon icon="info" size="x2_5" />
</Flex>
</DescriptionTerm>
<DescriptionDetails>
<Link href="/customer-details">P12-90381-2039</Link>
</DescriptionDetails>
<DescriptionTerm>Status</DescriptionTerm>
<DescriptionDetails>
<StatusIndicator type="success">Paid</StatusIndicator>
</DescriptionDetails>
<DescriptionTerm>Amount</DescriptionTerm>
<DescriptionDetails>$202.12</DescriptionDetails>
<DescriptionTerm>Amount after exchange</DescriptionTerm>
<DescriptionDetails>
<Flex as="span" alignItems="center" gap="half">
US $202.12 <Icon icon="arrowForward" color="midGrey" /> CA $287.43
</Flex>
</DescriptionDetails>
</>
);

export function WithDifferentLayouts() {
return (
<Flex flexDirection="column" gap="x4">
<Flex flexDirection="column" gap="x1" flex="1">
<Heading4>Auto Layout (Default)</Heading4>
<DescriptionList layout="auto">
<SampleContent />
</DescriptionList>
</Flex>
<Flex>
<Flex flexDirection="column" gap="x1" flex="1">
<Heading4>Stacked Layout</Heading4>
<DescriptionList layout="stacked">
<SampleContent />
</DescriptionList>
</Flex>

<Flex flexDirection="column" gap="x1" flex="1">
<Heading4>Inline Layout</Heading4>
<DescriptionList layout="inline">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>
</Flex>
);
}

export function WithDifferentDensities() {
return (
<Flex gap="x4" flexDirection="column">
<Flex flexDirection="column" gap="x1">
<Heading4>Compact Density</Heading4>
<Flex>
<DescriptionList density="compact" layout="stacked">
<SampleContent />
</DescriptionList>
<DescriptionList density="compact" layout="inline">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>

<Flex flexDirection="column" gap="x1">
<Heading4>Medium Density (Default)</Heading4>
<Flex>
<DescriptionList density="medium" layout="stacked">
<SampleContent />
</DescriptionList>
<DescriptionList density="medium" layout="inline">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>

<Flex flexDirection="column" gap="x1">
<Heading4>Relaxed Density</Heading4>
<Flex>
<DescriptionList density="relaxed" layout="stacked">
<SampleContent />
</DescriptionList>
<DescriptionList density="relaxed" layout="inline">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>
</Flex>
);
}

export function WithDifferentFontSizes() {
return (
<Flex gap="x4">
<Flex flexDirection="column" gap="x1" flexBasis="100%">
<Heading4>Smaller Font Size</Heading4>
<DescriptionList fontSize="smaller">
<SampleContent />
</DescriptionList>
</Flex>

<Flex flexDirection="column" gap="x1" flexBasis="100%">
<Heading4>Small Font Size</Heading4>
<DescriptionList fontSize="small">
<SampleContent />
</DescriptionList>
</Flex>

<Flex flexDirection="column" gap="x1" flexBasis="100%">
<Heading4>Medium Font Size (Default)</Heading4>
<DescriptionList fontSize="medium">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>
);
}

export function WithDividers() {
return (
<Flex gap="x4">
<Flex flexDirection="column" gap="x1" flexBasis="100%">
<Heading4>With Dividers (Stacked layout)</Heading4>
<DescriptionList showDivider layout="stacked">
<SampleContent />
</DescriptionList>
</Flex>
<Flex flexDirection="column" gap="x1" flexBasis="100%">
<Heading4>With Dividers (Inline layout)</Heading4>
<DescriptionList showDivider layout="inline">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>
);
}

export function WithCustomBreakpoints() {
return (
<Flex gap="x4" flexDirection="column">
<Flex flexDirection="column" gap="x1">
<Heading4>Auto layout custom breakpoint (800px)</Heading4>
<DescriptionList layout="auto" autoLayoutBreakpoint="800px">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>
);
}

export function WithCustomDescriptionTermWidth() {
return (
<Flex gap="x4" flexDirection="column">
<Flex flexDirection="column" gap="x1">
<Heading4>Custom description term max-width (33.33%)</Heading4>
<DescriptionList layout="inline" descriptionTermMaxWidth="30%">
<SampleContent />
</DescriptionList>
</Flex>
<Flex flexDirection="column" gap="x1">
<Heading4>Custom description term max-width (320px)</Heading4>
<DescriptionList layout="inline" descriptionTermMaxWidth="320px">
<SampleContent />
</DescriptionList>
</Flex>
<Flex flexDirection="column" gap="x1">
<Heading4>Custom description term max-width (20em)</Heading4>
<DescriptionList layout="inline" descriptionTermMaxWidth="20em">
<SampleContent />
</DescriptionList>
</Flex>
</Flex>
);
}

export function CombinedFeatures() {
return (
<Flex gap="x4" flexDirection="column">
<Flex flexDirection="column" gap="x1">
<Heading4>
Auto Layout + Dividers + Compact Density + Small Font + 40% Term Width + 720px auto layout breakpoint
</Heading4>
<DescriptionList
layout="auto"
showDivider
density="compact"
fontSize="small"
descriptionTermMaxWidth="40%"
autoLayoutBreakpoint="720px"
>
<SampleContent />
</DescriptionList>
</Flex>
</Flex>
);
}
66 changes: 66 additions & 0 deletions src/DescriptionList/DescriptionList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { PropsWithChildren } from "react";
import styled from "styled-components";
import { Link } from "../Link";
import { DescriptionListProvider, useDescriptionListContext, DescriptionListConfig } from "./DescriptionListContext";

export type Props = PropsWithChildren<DescriptionListConfig>;

export function DescriptionList({
descriptionTermMaxWidth = "50%",
layout = "auto",
showDivider = false,
density = "medium",
fontSize = "medium",
autoLayoutBreakpoint = "640px",
children,
}: Props) {
return (
<DescriptionListProvider
descriptionTermMaxWidth={descriptionTermMaxWidth}
layout={layout}
showDivider={showDivider}
density={density}
fontSize={fontSize}
autoLayoutBreakpoint={autoLayoutBreakpoint}
>
<DescriptionListContainer>
<StyledDescriptionList>{children}</StyledDescriptionList>
</DescriptionListContainer>
</DescriptionListProvider>
);
}

export const DescriptionListContainer = styled.div({
containerType: "inline-size",
width: "100%",
});

export const StyledDescriptionList = styled.dl(({ theme }) => {
const { descriptionTermMaxWidth, layout, fontSize, autoLayoutBreakpoint } = useDescriptionListContext();

return {
margin: 0,
display: "grid",
fontSize: theme.fontSizes[fontSize] ?? theme.fontSizes.medium,
lineHeight: theme.lineHeights.base,

...(layout === "inline" && {
gridTemplateColumns: `minmax(0, ${descriptionTermMaxWidth}) auto`,
}),

...((layout === "stacked" || layout === "auto") && {
gridTemplateColumns: "1fr",
}),

[`${Link}`]: {
fontSize: "inherit",
lineHeight: "inherit",
},

[`@container (min-width: ${autoLayoutBreakpoint})`]: {
...(layout === "auto" && {
gridTemplateColumns: `minmax(0, min(50%, ${descriptionTermMaxWidth})) auto`,
}),
},
};
});
19 changes: 19 additions & 0 deletions src/DescriptionList/DescriptionListContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { PropsWithChildren } from "react";
import { DefaultNDSThemeType } from "../theme";

export interface DescriptionListConfig {
descriptionTermMaxWidth?: string;
layout?: "stacked" | "inline" | "auto";
showDivider?: boolean;
density?: "medium" | "compact" | "relaxed";
fontSize?: keyof DefaultNDSThemeType["fontSizes"];
autoLayoutBreakpoint?: string;
}

const DescriptionListContext = React.createContext<DescriptionListConfig>({});

export const useDescriptionListContext = () => React.useContext(DescriptionListContext);

export function DescriptionListProvider({ children, ...config }: PropsWithChildren<DescriptionListConfig>) {
return <DescriptionListContext.Provider value={{ ...config }}>{children}</DescriptionListContext.Provider>;
}
38 changes: 38 additions & 0 deletions src/DescriptionList/DescriptionTerm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { styled } from "styled-components";
import { useDescriptionListContext } from "./DescriptionListContext";

export const DescriptionTerm = styled.dt(({ theme }) => {
const { showDivider, density, layout } = useDescriptionListContext();

return {
gridColumnStart: 1,
color: theme.colors.midGrey,
paddingLeft: theme.space.none,
paddingRight: theme.space.none,

...(density === "compact" && {
paddingTop: theme.space.x0_25,
paddingBottom: theme.space.x0_25,
}),

...(density === "medium" && {
paddingTop: theme.space.x0_75,
paddingBottom: theme.space.x0_75,
}),

...(density === "relaxed" && {
paddingTop: theme.space.x1_5,
paddingBottom: theme.space.x1_5,
}),

...(showDivider && {
borderTopWidth: "1px",
borderTopStyle: "solid",
borderTopColor: theme.colors.lightGrey,
}),

"&:first-child": {
border: "none",
},
};
});
Loading

0 comments on commit 92a6a51

Please sign in to comment.