Skip to content

Commit

Permalink
feat(DetailsTable): add DetailsTable component
Browse files Browse the repository at this point in the history
  • Loading branch information
akdetrick committed Jun 28, 2024
1 parent d0eb8ee commit a8db832
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/DescriptionList/DescriptionListRow.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import cc from "classcat";
import PropTypes from "prop-types";

const DescriptionListRow = ({ label, value, allowWrap }) => (
<>
<dt>{label}</dt>
<dd className={cc([{ allowWrap }])}>
<span>{value}</span>
</dd>
</>
);

DescriptionListRow.propTypes = {
label: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
allowWrap: PropTypes.bool,
};

DescriptionListRow.displayName = "DescriptionList.Row";

export default DescriptionListRow;
29 changes: 29 additions & 0 deletions src/DescriptionList/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react";
import PropTypes from "prop-types";
import DescriptionListRow from "./DescriptionListRow";

/**
* Displays an HTML description list as a responsive table to display labels and
* values of details about a user, product, account, application, etc.
*/
const DescriptionList = ({ children, kind = "plain" }) => {
return (
<div className="nds-descriptionList">
<dl className={`nds-descriptionList-list--${kind}`}>{children}</dl>
</div>
);
};

DescriptionList.propTypes = {
/** DescriptionList.Row items to render */
children: PropTypes.oneOfType([
PropTypes.node,
PropTypes.arrayOf(PropTypes.node),
]).isRequired,
/** Visual variant of DetailsTable */
kind: PropTypes.oneOf(["plain", "bordered"]),
};

DescriptionList.Row = DescriptionListRow;

export default DescriptionList;
99 changes: 99 additions & 0 deletions src/DescriptionList/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
@mixin columnLayout($suffix: "") {
display: flex #{$suffix};
flex-direction: column #{$suffix};
gap: var(--space-xxs) #{$suffix};

&.nds-descriptionList-list--bordered {
// only apply the border _between_ entries
dd + dt {
border-top: 1px solid var(--border-color-default) #{$suffix};
padding-top: var(--space-s) #{$suffix};
}
}

dt,
dd {
white-space: normal #{$suffix};
}

dd {
margin-bottom: var(--space-s) #{$suffix};
padding-left: unset #{$suffix};
}
}

@mixin rowLayout {
gap: unset;
display: grid;
row-gap: var(--space-m);

// The label column should never be shorter than content length (max-content).
// The label column should never be wider than 1/5th of the total width
grid-template-columns: minmax(max-content, 1fr) 4fr;

&.nds-descriptionList-list--bordered {
row-gap: var(--space-xs);

// only apply the border _between_ entries
dd + dt,
dd:nth-child(n + 3) {
border-top: 1px solid var(--border-color-default);
padding-top: var(--space-xs);
}
}

dt,
dd {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

dt {
margin-bottom: unset;
grid-column: 1;
min-width: max-content;
}

dd {
margin-bottom: unset;
grid-column: 2;
padding-left: var(--space-xl);
}
}

.nds-descriptionList {
container-name: ndsDl;
container-type: inline-size;

dl {
// stacked by default
@include columnLayout;

// spreads out into rows at viewport sizes larger than mobile
@include atMediaUp("s") {
@include rowLayout;
}

.allowWrap {
white-space: normal;
overflow: visible;

// allow the dd element to grow, but constrain
// line length of text to a readable length
span {
display: inline-block;
max-width: 56ch;
line-height: var(--font-lineHeight-default);
}
}
}
}

// If the container is narrow, regardless of the viewport size,
// use the mobile layout.
@container ndsDl (max-width: 18rem) {
dl {
@include columnLayout(!important);
}
}
89 changes: 89 additions & 0 deletions src/DescriptionList/index.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* eslint-disable jsx-a11y/anchor-is-valid,react/jsx-key */
import React from "react";
import DescriptionList from "./";
import ContentCard from "../ContentCard";
import ResponsiveFlex from "../ResponsiveFlex";

const Template = (args) => <DescriptionList {...args} />;

export const Overview = Template.bind({});
Overview.args = {
kind: "plain",
children: [
<DescriptionList.Row label="Name" value="Keith Hernandez" />,
<DescriptionList.Row label="Phone Number" value="(212) 222-5555" />,
<DescriptionList.Row label="Email" value="keith.hernandez@gmail.com" />,
<DescriptionList.Row label="Date of birth" value="10/20/1953" />,
<DescriptionList.Row label="Occupation" value="Baseball guy" />,
<DescriptionList.Row
label="Routing Number"
value="4648376409127263873737 (Checking)"
/>,
<DescriptionList.Row
allowWrap
label="Description"
value="A much longer value that has been allowed to wrap via the allowWrap prop on this row. The line length is constrained to a readable number of characters per line by using the CSS ch unit."
/>,
],
};

export const Bordered = Template.bind({});
Bordered.args = {
kind: "bordered",
children: [
<DescriptionList.Row label="Name" value="Keith Hernandez" />,
<DescriptionList.Row label="Phone Number" value="(212) 222-5555" />,
<DescriptionList.Row label="Email" value="keith.hernandez@gmail.com" />,
<DescriptionList.Row label="Date of birth" value="10/20/1953" />,
<DescriptionList.Row label="Occupation" value="Baseball guy" />,
<DescriptionList.Row
allowwrap
label="Routing Number"
value="464837640912726337 (Checking)"
/>,
],
};

export const InResponsiveCards = () => (
<div className="bgColor--smokeGrey padding--all--xxl">
<ResponsiveFlex gapSize="l" toRowAt="l">
<div style={{ flexGrow: 1 }}>
<ContentCard kind="elevated">
<h2 className="margin--bottom">Source</h2>
<DescriptionList>
<DescriptionList.Row label="Sender" value="Maria Beetle" />
<DescriptionList.Row
label="Account number"
value="464837640912726337 (Checking)"
/>
<DescriptionList.Row
label="Routing number"
value="0200121002120012 (My Bank)"
/>
</DescriptionList>
</ContentCard>
</div>
<div style={{ flexGrow: 1 }}>
<ContentCard kind="elevated">
<h2 className="margin--bottom">Destination</h2>
<DescriptionList>
<DescriptionList.Row label="Recipient" value="Quincy Beetle" />
<DescriptionList.Row
label="Account number"
value="464837640912726337 (Checking)"
/>
<DescriptionList.Row
label="Routing number"
value="0200121002120012 (Their Bank)"
/>
</DescriptionList>
</ContentCard>
</div>
</ResponsiveFlex>
</div>
);

export default {
title: "Components/DescriptionList",
component: DescriptionList,
};
4 changes: 4 additions & 0 deletions src/base-styles/defaults.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ fieldset {
margin: 0;
border: 0;
}

dt {
font-weight: var(--font-weight-bold);
}
8 changes: 8 additions & 0 deletions src/base-styles/reset.scss
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,11 @@ button:-moz-focusring,
-webkit-appearance: button;
font: inherit;
}

// Unset user agent definition list styles
dl,
dt,
dd {
padding: unset;
margin: unset;
}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ContentCard from "./ContentCard";
import CollapsibleCard from "./CollapsibleCard";
import Combobox from "./Combobox";
import DateInput from "./DateInput";
import DescriptionList from "./DescriptionList";
import Dialog from "./Dialog";
import Drawer from "./Drawer";
import Dropdown from "./Dropdown";
Expand Down Expand Up @@ -52,6 +53,7 @@ export {
CollapsibleCard,
Combobox,
DateInput,
DescriptionList,
Dialog,
Drawer,
Dropdown,
Expand Down
1 change: 1 addition & 0 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
@import "Combobox/";
@import "ContentCard/";
@import "CollapsibleCard/";
@import "DescriptionList/";
@import "IconButton/";
@import "MenuButton/";
@import "LoadingShim/";
Expand Down

0 comments on commit a8db832

Please sign in to comment.