Skip to content

Commit 5c6779a

Browse files
Merge pull request #2 from wintondeshong/master
Anchors, Buttons and Icon Handling
2 parents cf6b821 + e4726af commit 5c6779a

8 files changed

Lines changed: 493 additions & 15 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
"dist"
8585
],
8686
"jest": {
87+
"collectCoverageFrom": [
88+
"src/**/*.ts*",
89+
"!src/**/*.stories.ts*",
90+
"!src/tests/**/*"
91+
],
8792
"moduleNameMapper": {
8893
"react-spring": "<rootDir>/node_modules/react-spring/web.cjs",
8994
"react-spring/renderprops": "<rootDir>/node_modules/react-spring/renderprops.cjs"

src/atoms/anchors/anchor.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Anchor } from "./anchor";
44
import faker from "faker";
55
import { MemoryRouter } from "react-router-dom";
66

7-
describe("anchor", () => {
7+
describe("Anchor", () => {
88
it("when default props, renders link with correct url", async () => {
99
// Arrange
1010
const expected = `/some/random/path/${faker.random.word()}`;

src/atoms/buttons/button.test.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React from "react";
2+
import { render, wait } from "@testing-library/react";
3+
import { Button } from "./button";
4+
import faker from "faker";
5+
import { ButtonSizes } from "../constants/button-sizes";
6+
import { ButtonStyles } from "../constants/button-styles";
7+
8+
describe("Button", () => {
9+
it("when default props, renders button children", async () => {
10+
// Arrange
11+
const expected = faker.random.words();
12+
13+
// Act
14+
const { getByText } = render(<Button>{expected}</Button>);
15+
16+
// Assert
17+
expect(getByText(expected)).not.toBeNull();
18+
});
19+
20+
it("when accessibleText provided, renders child accessible span", () => {
21+
// Arrange
22+
const expected = faker.random.word();
23+
24+
// Act
25+
const { getByText } = render(
26+
<Button accessibleText={expected}></Button>
27+
);
28+
29+
// Assert
30+
expect(getByText(expected)).not.toBeUndefined();
31+
});
32+
33+
it("when cssClassName provided, adds provided className to button", () => {
34+
// Arrange
35+
const expected = faker.random.word();
36+
37+
// Act
38+
const { container } = render(<Button cssClassName={expected}></Button>);
39+
40+
// Assert
41+
expect(container.firstChild.className).toContain(expected);
42+
});
43+
44+
it("when size provided, adds size className to button", () => {
45+
// Arrange
46+
const expected = ButtonSizes.Large;
47+
48+
// Act
49+
const { container } = render(<Button size={expected}></Button>);
50+
51+
// Assert
52+
expect(container.firstChild.className).toContain(`-${expected}`);
53+
});
54+
55+
it(`when style of '${ButtonStyles.None}', has className of only 'none'`, () => {
56+
// Arrange
57+
const expected = ButtonStyles.None;
58+
59+
// Act
60+
const { container } = render(<Button style={expected}></Button>);
61+
62+
// Assert
63+
expect(container.firstChild.className).toBe(`-${expected}`);
64+
});
65+
66+
it("when style provided, adds style className to button", () => {
67+
// Arrange
68+
const expected = ButtonStyles.Destructive;
69+
70+
// Act
71+
const { container } = render(<Button style={expected}></Button>);
72+
73+
// Assert
74+
expect(container.firstChild.className).toContain(`-${expected}`);
75+
});
76+
});

src/atoms/buttons/button.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ButtonSizes } from "../constants/button-sizes";
22
import { ButtonStyles } from "../constants/button-styles";
33
import { ButtonTypes } from "../constants/button-types";
44
import React, { forwardRef } from "react";
5+
import { StringUtils } from "andculturecode-javascript-core";
56

67
// -----------------------------------------------------------------------------------------
78
// #region Interfaces
@@ -54,22 +55,18 @@ const Button: React.RefForwardingComponent<
5455
value,
5556
} = props;
5657

57-
const classNames = ["c-button"];
58-
59-
if (style === ButtonStyles.None) {
60-
classNames[0] = "";
61-
}
58+
let classNames = style === ButtonStyles.None ? [] : ["c-button"];
6259

6360
if (size != null) {
64-
classNames.push(size);
61+
classNames.push(`-${size}`);
6562
}
6663

6764
if (style != null) {
68-
classNames.push(style);
65+
classNames.push(`-${style}`);
6966
}
7067

71-
if (cssClassName != null && cssClassName.length > 0) {
72-
classNames.push(cssClassName);
68+
if (StringUtils.hasValue(cssClassName)) {
69+
classNames.push(cssClassName!);
7370
}
7471

7572
return (
Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ import { ReactComponent as Share } from "../../assets/icons/16px/Share.svg";
2525
import { ReactComponent as Trashcan } from "../../assets/icons/16px/Trashcan.svg";
2626
import { ReactComponent as WarningLarge } from "../../assets/icons/24px/Warning.svg";
2727

28+
// -----------------------------------------------------------------------------------------
29+
// #region Constants
30+
// -----------------------------------------------------------------------------------------
31+
2832
const SvgIcons: SvgIcon[] = [
2933
{ type: Icons.Checkmark, base: Checkmark, large: CheckmarkLarge },
3034
{ type: Icons.ChevronDown, base: ChevronDown, large: ChevronDownLarge },
@@ -44,4 +48,20 @@ const SvgIcons: SvgIcon[] = [
4448
{ type: Icons.Warning, base: WarningLarge, large: WarningLarge },
4549
];
4650

47-
export { SvgIcons };
51+
// #endregion Constants
52+
53+
// -----------------------------------------------------------------------------------------
54+
// #region Functions
55+
// -----------------------------------------------------------------------------------------
56+
57+
const getSvgIconByType = (type: Icons) => SvgIcons.find((i) => i.type === type);
58+
59+
// #endregion Functions
60+
61+
// -----------------------------------------------------------------------------------------
62+
// #region Exports
63+
// -----------------------------------------------------------------------------------------
64+
65+
export { getSvgIconByType, SvgIcons };
66+
67+
// #endregion Exports

src/atoms/icons/icon.test.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from "react";
2+
import { render, wait } from "@testing-library/react";
3+
import { Icon } from "./icon";
4+
import faker from "faker";
5+
import { Icons } from "../constants/icons";
6+
import { IconUtils } from "../../utilities/icon-utils";
7+
import { getSvgIconByType } from "../constants/svg-icons";
8+
import { IconSizes } from "../constants/icon-sizes";
9+
import { SvgIcon } from "../interfaces/svg-icon";
10+
11+
describe("Icon", () => {
12+
test("given icon type not found, when default props, renders bare icon <i>", async () => {
13+
// Arrange & Act
14+
const { container } = render(<Icon type={Icons.Checkmark} />);
15+
16+
// Assert
17+
expect(container.firstChild.nodeName).toBe("I");
18+
});
19+
20+
describe("given icon type exists", () => {
21+
let registeredIcon: SvgIcon;
22+
23+
beforeEach(() => {
24+
IconUtils.clearRegistry();
25+
registeredIcon = getSvgIconByType(Icons.ChevronUp);
26+
IconUtils.registerSvgIcon(registeredIcon);
27+
});
28+
29+
test("when default props, renders matching base icon", async () => {
30+
// Arrange & Act
31+
const { container } = render(<Icon type={registeredIcon.type} />);
32+
33+
// Assert
34+
expect(container.firstChild.nodeName).toBe("svg");
35+
});
36+
37+
test.each`
38+
size
39+
${IconSizes.Base}
40+
${IconSizes.Large}
41+
`("when size of $size, renders icon", async ({ size }) => {
42+
// Arrange & Act
43+
const { container } = render(
44+
<Icon size={size} type={registeredIcon.type} />
45+
);
46+
47+
// Assert
48+
expect(container.firstChild.nodeName).toBe("svg");
49+
expect(container.firstChild.getAttribute("class")).toContain(
50+
`-${size}`
51+
);
52+
});
53+
54+
test("when cssClassName provided, renders with className set", async () => {
55+
// Arrange
56+
const expected = faker.random.word();
57+
58+
// Act
59+
const { container } = render(
60+
<Icon cssClassName={expected} type={registeredIcon.type} />
61+
);
62+
63+
// Assert
64+
expect(container.firstChild.nodeName).toBe("svg");
65+
expect(container.firstChild.getAttribute("class")).toContain(
66+
expected
67+
);
68+
});
69+
}); // end 'given icon type exists'
70+
});

0 commit comments

Comments
 (0)