Skip to content

Commit

Permalink
Add List and refactor Text
Browse files Browse the repository at this point in the history
  • Loading branch information
moroshko committed Feb 1, 2020
1 parent bdb73ca commit b6ea1bd
Show file tree
Hide file tree
Showing 40 changed files with 790 additions and 524 deletions.
13 changes: 11 additions & 2 deletions src/components/Flex.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
responsiveFlexGutter,
responsiveFlexPlaceItems
} from "../utils/css";
import { isObjectEmpty } from "../utils/core";

const DIRECTIONS = ["row", "column"];
const PLACE_ITEMS = [
Expand Down Expand Up @@ -52,7 +53,9 @@ function Flex(_props) {
const flexCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, {
height: responsiveHeight,
placeItems: responsiveFlexPlaceItems,
direction: responsiveFlexDirection,
direction: responsiveFlexDirection
});
const flexItemCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, {
gutter: responsiveFlexGutter
});

Expand All @@ -65,7 +68,13 @@ function Flex(_props) {
}}
data-testid={testId}
>
{childrenArray}
{isObjectEmpty(flexItemCSS)
? childrenArray
: childrenArray.map((child, index) => (
<div css={flexItemCSS} key={index}>
{child}
</div>
))}
</div>
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ LegalLinks.propTypes = {
function LegalCopy({ children, testId }) {
return (
<Text
intent="legal"
textStyle="legal"
align="center"
margin="7 0 0 0"
margin-md="6 0 0 0"
Expand Down
130 changes: 130 additions & 0 deletions src/components/List.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { useContext } from "react";
import PropTypes from "prop-types";
import useTheme from "../hooks/useTheme";
import useTextStyle, { TextStyleProvider } from "../hooks/useTextStyle";
import {
responsiveMarginType,
responsivePropType
} from "../hooks/useResponsiveProp";
import useResponsivePropsCSS from "../hooks/useResponsivePropsCSS";
import { mergeProps } from "../utils/component";
import { responsiveMargin } from "../utils/css";

const ListContext = React.createContext();

const TYPES = ["unordered", "ordered"];
const TEXT_STYLES = ["subtitle1", "subtitle2", "body1", "body2"];

const DEFAULT_PROPS = {
type: "unordered",
textStyle: "body1"
};

List.TYPES = TYPES;
List.TEXT_STYLES = TEXT_STYLES;
List.DEFAULT_PROPS = DEFAULT_PROPS;

function Item({ children, testId }) {
const theme = useTheme();
const { type, textStyle } = useContext(ListContext);
const fontSizeInt = parseInt(theme.getTextStyleCSS(textStyle).fontSize, 10);
const markerContainerMargin = `${fontSizeInt * 0.75}px`;
const unorderedCircleSize = `${fontSizeInt * 0.5}px`;
const spaceBetweenItems = `${fontSizeInt * 0.75}px`;
const unorderedCSS = {
width: unorderedCircleSize,
height: unorderedCircleSize
};

return (
<li
css={{
...theme[`listItem.${type}`],
":not(:first-of-type)": {
marginTop: spaceBetweenItems
},
"& ol": theme["list.ordered.nested"]
}}
data-testid={testId}
>
{type === "unordered" && (
<>
<div
css={{
...theme.listItemMarkerContainer,
marginRight: markerContainerMargin
}}
aria-hidden="true"
>
<div
css={{
...theme[`listItemMarker.${type}`],
...(type === "unordered" && unorderedCSS)
}}
/>
&#8203;
{/* See: https://twitter.com/adamwathan/status/1217864323466432516 */}
</div>
</>
)}
<div css={theme[`listItemContent.${type}.${textStyle}`]}>{children}</div>
</li>
);
}

Item.propTypes = {
children: PropTypes.node.isRequired,
testId: PropTypes.string
};

function List(props) {
const theme = useTheme();
const { textStyle: inheritedTextStyle } = useTextStyle();
const inheritedProps = {
textStyle: inheritedTextStyle
};
const mergedProps = mergeProps(props, DEFAULT_PROPS, inheritedProps, {
type: type => TYPES.includes(type),
textStyle: textStyle => TEXT_STYLES.includes(textStyle)
});
const { type, textStyle, children, testId } = mergedProps;
const responsivePropsCSS = useResponsivePropsCSS(mergedProps, DEFAULT_PROPS, {
margin: responsiveMargin
});
const ListComponent = type === "unordered" ? "ul" : "ol";
const items = React.Children.toArray(children).filter(
// Ignore all children that aren't List.Item
child => child.type === Item
);

return (
<ListContext.Provider value={{ type, textStyle }}>
<TextStyleProvider value={textStyle}>
<ListComponent
css={{
...theme.list,
...theme[`list.${type}`],
...theme[`list.${type}.${textStyle}`],
...theme.getTextStyleCSS(textStyle),
...responsivePropsCSS
}}
data-testid={testId}
>
{items}
</ListComponent>
</TextStyleProvider>
</ListContext.Provider>
);
}

List.propTypes = {
...responsiveMarginType,
...responsivePropType("textStyle", PropTypes.oneOf(TEXT_STYLES)),
type: PropTypes.oneOf(TYPES),
children: PropTypes.node.isRequired,
testId: PropTypes.string
};

List.Item = Item;

export default List;
65 changes: 65 additions & 0 deletions src/components/List.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from "react";
import { render } from "../utils/test";
import "@testing-library/jest-dom/extend-expect";
import List from "./List";

describe("List", () => {
it("unordered", () => {
const { container, getByText } = render(
<List>
<List.Item>First Item</List.Item>
<List.Item>Second Item</List.Item>
<List.Item>Third Item</List.Item>
</List>
);

expect(container.firstChild.tagName).toBe("UL");

getByText("First Item");
getByText("Second Item");
getByText("Third Item");
});

it("ordered", () => {
const { container, getByText } = render(
<List type="ordered">
<List.Item>First Item</List.Item>
<List.Item>Second Item</List.Item>
<List.Item>Third Item</List.Item>
</List>
);

expect(container.firstChild.tagName).toBe("OL");

getByText("First Item");
getByText("Second Item");
getByText("Third Item");
});

it("ignores children that are not List.Item", () => {
const { queryByText } = render(
<List>
<p>Hello</p>
<List.Item>First Item</List.Item>
<List.Item>Second Item</List.Item>
<div>World</div>
<List.Item>Third Item</List.Item>
</List>
);

expect(queryByText("Hello")).not.toBeInTheDocument();
expect(queryByText("World")).not.toBeInTheDocument();
});

it("with testId", () => {
const { container } = render(
<List testId="my-list">
<List.Item>First Item</List.Item>
<List.Item>Second Item</List.Item>
<List.Item>Third Item</List.Item>
</List>
);

expect(container.firstChild).toHaveAttribute("data-testid", "my-list");
});
});
4 changes: 2 additions & 2 deletions src/components/Stepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function Item(_props) {
<div css={theme["stepper.itemLabel"]}>
{label && (
<Text
intent="body2"
textStyle="body2"
color={isCurrent ? "primary.blue.t100" : "black"}
align="center"
>
Expand Down Expand Up @@ -77,7 +77,7 @@ function Item(_props) {
>
{isPrevious && !isMinor && <Icon name="tick" color="white" />}
{!isPrevious && !isMinor && (
<Text intent="subtitle2" color={isPrevious ? "white" : "black"}>
<Text textStyle="subtitle2" color={isPrevious ? "white" : "black"}>
<strong>{majorStepNumber}</strong>
</Text>
)}
Expand Down
Loading

0 comments on commit b6ea1bd

Please sign in to comment.