@khanacademy/wonder-blocks-cell 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {render, screen} from "@testing-library/react";
4
+
5
+ import Icon, {icons} from "@khanacademy/wonder-blocks-icon";
6
+ import {HeadingMedium} from "@khanacademy/wonder-blocks-typography";
7
+
8
+ import BasicCell from "../basic-cell.js";
9
+
10
+ describe("BasicCell", () => {
11
+ it("should render the default BasicCell component", () => {
12
+ // Arrange
13
+
14
+ // Act
15
+ render(<BasicCell title="Basic cell" />);
16
+
17
+ // Assert
18
+ expect(screen.getByText("Basic cell")).toBeInTheDocument();
19
+ });
20
+
21
+ it("should render the title using a Typography element", () => {
22
+ // Arrange
23
+
24
+ // Act
25
+ render(<BasicCell title={<HeadingMedium>Basic cell</HeadingMedium>} />);
26
+
27
+ // Assert
28
+ expect(
29
+ screen.getByRole("heading", {name: "Basic cell"}),
30
+ ).toBeInTheDocument();
31
+ });
32
+
33
+ it("should render the leftAccessory", () => {
34
+ // Arrange
35
+
36
+ // Act
37
+ render(
38
+ <BasicCell
39
+ title="Basic cell"
40
+ leftAccessory={
41
+ <Icon icon={icons.caretRight} aria-label="Caret icon" />
42
+ }
43
+ />,
44
+ );
45
+
46
+ // Assert
47
+ expect(screen.getByLabelText("Caret icon")).toBeInTheDocument();
48
+ });
49
+
50
+ it("should render the rightAccessory", () => {
51
+ // Arrange
52
+
53
+ // Act
54
+ render(
55
+ <BasicCell
56
+ title="Basic cell"
57
+ rightAccessory={
58
+ <Icon icon={icons.caretRight} aria-label="Caret icon" />
59
+ }
60
+ />,
61
+ );
62
+
63
+ // Assert
64
+ expect(screen.getByLabelText("Caret icon")).toBeInTheDocument();
65
+ });
66
+
67
+ it("should add testId to the content wrapper", () => {
68
+ // Arrange
69
+
70
+ // Act
71
+ render(<BasicCell title="Basic cell" testId="cellId" />);
72
+
73
+ // Assert
74
+ expect(screen.getByTestId("cellId")).toHaveTextContent("Basic cell");
75
+ });
76
+
77
+ it("should add a button if onClick is set", () => {
78
+ // Arrange
79
+
80
+ // Act
81
+ render(<BasicCell title="Basic cell" onClick={jest.fn()} />);
82
+
83
+ // Assert
84
+ expect(screen.getByRole("button")).toBeInTheDocument();
85
+ });
86
+
87
+ it("should allow clicking the cell if onClick is set", () => {
88
+ // Arrange
89
+ const onClickMock = jest.fn();
90
+ render(<BasicCell title="Basic cell" onClick={onClickMock} />);
91
+
92
+ // Act
93
+ screen.getByRole("button").click();
94
+
95
+ // Assert
96
+ expect(onClickMock).toHaveBeenCalled();
97
+ });
98
+ });
@@ -0,0 +1,103 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {render, screen} from "@testing-library/react";
4
+
5
+ import {HeadingMedium} from "@khanacademy/wonder-blocks-typography";
6
+
7
+ import DetailCell from "../detail-cell.js";
8
+
9
+ describe("DetailCell", () => {
10
+ it("should render the default DetailCell component", () => {
11
+ // Arrange
12
+
13
+ // Act
14
+ render(<DetailCell title="Detail cell" />);
15
+
16
+ // Assert
17
+ expect(screen.getByText("Detail cell")).toBeInTheDocument();
18
+ });
19
+
20
+ it("should render the title using a Typography element", () => {
21
+ // Arrange
22
+
23
+ // Act
24
+ render(
25
+ <DetailCell
26
+ title={<HeadingMedium>Detail cell title</HeadingMedium>}
27
+ />,
28
+ );
29
+
30
+ // Assert
31
+ expect(
32
+ screen.getByRole("heading", {name: "Detail cell title"}),
33
+ ).toBeInTheDocument();
34
+ });
35
+
36
+ it("should render the subtitle1 using a plain text", () => {
37
+ // Arrange
38
+
39
+ // Act
40
+ render(
41
+ <DetailCell
42
+ title="Detail cell"
43
+ subtitle1="Detail cell subtitle 1"
44
+ />,
45
+ );
46
+
47
+ // Assert
48
+ expect(screen.getByText("Detail cell subtitle 1")).toBeInTheDocument();
49
+ });
50
+
51
+ it("should render the subtitle1 using a Typography element", () => {
52
+ // Arrange
53
+
54
+ // Act
55
+ render(
56
+ <DetailCell
57
+ title="Detail cell"
58
+ subtitle1={
59
+ <HeadingMedium>Detail cell subtitle 1</HeadingMedium>
60
+ }
61
+ />,
62
+ );
63
+
64
+ // Assert
65
+ expect(
66
+ screen.getByRole("heading", {name: "Detail cell subtitle 1"}),
67
+ ).toBeInTheDocument();
68
+ });
69
+
70
+ it("should render the subtitle2 using a plain text", () => {
71
+ // Arrange
72
+
73
+ // Act
74
+ render(
75
+ <DetailCell
76
+ title="Detail cell"
77
+ subtitle2="Detail cell subtitle 2"
78
+ />,
79
+ );
80
+
81
+ // Assert
82
+ expect(screen.getByText("Detail cell subtitle 2")).toBeInTheDocument();
83
+ });
84
+
85
+ it("should render the subtitle2 using a Typography element", () => {
86
+ // Arrange
87
+
88
+ // Act
89
+ render(
90
+ <DetailCell
91
+ title="Detail cell"
92
+ subtitle2={
93
+ <HeadingMedium>Detail cell subtitle 2</HeadingMedium>
94
+ }
95
+ />,
96
+ );
97
+
98
+ // Assert
99
+ expect(
100
+ screen.getByRole("heading", {name: "Detail cell subtitle 2"}),
101
+ ).toBeInTheDocument();
102
+ });
103
+ });
@@ -0,0 +1,40 @@
1
+ // @flow
2
+ import * as React from "react";
3
+
4
+ import {LabelMedium} from "@khanacademy/wonder-blocks-typography";
5
+
6
+ import CellCore from "./internal/cell-core.js";
7
+
8
+ import type {CellProps} from "../util/types.js";
9
+
10
+ /**
11
+ * BasicCell is the simplest form of the Cell. It is a compacted-height Cell
12
+ * with limited subviews and accessories, to be used for simple lists, like
13
+ * dropdown option items, navigation items, settings dialogs, etc.
14
+ *
15
+ * ### Usage
16
+ *
17
+ * ```jsx
18
+ * import {BasicCell} from "@khanacademy/wonder-blocks-cell";
19
+ *
20
+ * <BasicCell
21
+ * title="Basic cell"
22
+ * rightAccessory={<Icon icon={icons.caretRight} size="medium" />}
23
+ * />
24
+ * ```
25
+ */
26
+ function BasicCell(props: CellProps): React.Node {
27
+ const {title, ...coreProps} = props;
28
+
29
+ return (
30
+ <CellCore {...coreProps}>
31
+ {typeof title === "string" ? (
32
+ <LabelMedium>{title}</LabelMedium>
33
+ ) : (
34
+ title
35
+ )}
36
+ </CellCore>
37
+ );
38
+ }
39
+
40
+ export default BasicCell;
@@ -0,0 +1,100 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {StyleSheet} from "aphrodite";
4
+
5
+ import Color from "@khanacademy/wonder-blocks-color";
6
+ import {Strut} from "@khanacademy/wonder-blocks-layout";
7
+ import Spacing from "@khanacademy/wonder-blocks-spacing";
8
+ import {LabelSmall, LabelLarge} from "@khanacademy/wonder-blocks-typography";
9
+
10
+ import CellCore from "./internal/cell-core.js";
11
+
12
+ import type {CellProps, TypographyText} from "../util/types.js";
13
+
14
+ type SubtitleProps = {|
15
+ subtitle?: TypographyText,
16
+ /**
17
+ * If true, the subtitle will use the alpha color defined in the parent
18
+ * component/element.
19
+ */
20
+ disabled?: boolean,
21
+ |};
22
+
23
+ const Subtitle = ({subtitle, disabled}: SubtitleProps): React.Node => {
24
+ if (!subtitle) {
25
+ return null;
26
+ }
27
+
28
+ if (typeof subtitle === "string") {
29
+ return (
30
+ <LabelSmall style={!disabled && styles.subtitle}>
31
+ {subtitle}
32
+ </LabelSmall>
33
+ );
34
+ }
35
+
36
+ return subtitle;
37
+ };
38
+
39
+ type DetailCellProps = {|
40
+ ...CellProps,
41
+
42
+ /**
43
+ * You can either provide a string or a custom node Typography element (or
44
+ * nothing at all). Both a string or a custom node Typography element will
45
+ * occupy the “Subtitle1” area of the Cell.
46
+ */
47
+ subtitle1?: TypographyText,
48
+
49
+ /**
50
+ * You can either provide a string or a custom node Typography element (or
51
+ * nothing at all). Both a string or a custom node Typography element will
52
+ * occupy the “Subtitle2” area of the Cell.
53
+ */
54
+ subtitle2?: TypographyText,
55
+ |};
56
+
57
+ /**
58
+ * This is a variant of BasicCell that allows adding subtitles, before and after
59
+ * the cell title.
60
+ *
61
+ * ### Usage
62
+ *
63
+ * ```jsx
64
+ * import {DetailCell} from "@khanacademy/wonder-blocks-cell";
65
+ *
66
+ * <DetailCell
67
+ * leftAccessory={<Icon icon={icons.contentVideo} size="medium" />}
68
+ * subtitle1="Subtitle 1"
69
+ * title="Detail cell"
70
+ * subtitle1="Subtitle 2"
71
+ * rightAccessory={<Icon icon={icons.caretRight} size="medium" />}
72
+ * />
73
+ * ```
74
+ */
75
+ function DetailCell(props: DetailCellProps): React.Node {
76
+ const {title, subtitle1, subtitle2, ...coreProps} = props;
77
+
78
+ return (
79
+ <CellCore {...coreProps}>
80
+ <Subtitle subtitle={subtitle1} disabled={coreProps.disabled} />
81
+ {subtitle1 && <Strut size={Spacing.xxxxSmall_2} />}
82
+ {typeof title === "string" ? (
83
+ <LabelLarge>{title}</LabelLarge>
84
+ ) : (
85
+ title
86
+ )}
87
+ {/* Add a vertical spacing between the title and the subtitle */}
88
+ {subtitle2 && <Strut size={Spacing.xxxxSmall_2} />}
89
+ <Subtitle subtitle={subtitle2} disabled={coreProps.disabled} />
90
+ </CellCore>
91
+ );
92
+ }
93
+
94
+ const styles = StyleSheet.create({
95
+ subtitle: {
96
+ color: Color.offBlack64,
97
+ },
98
+ });
99
+
100
+ export default DetailCell;
@@ -0,0 +1,95 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {render, screen} from "@testing-library/react";
4
+
5
+ import CellCore from "../cell-core.js";
6
+
7
+ describe("CellCore", () => {
8
+ it("should render the CellCore component", () => {
9
+ // Arrange
10
+
11
+ // Act
12
+ render(
13
+ <CellCore>
14
+ <div>cell core content</div>
15
+ </CellCore>,
16
+ );
17
+
18
+ // Assert
19
+ expect(screen.getByText("cell core content")).toBeInTheDocument();
20
+ });
21
+
22
+ it("should NOT add a button by default", () => {
23
+ // Arrange
24
+
25
+ // Act
26
+ render(
27
+ <CellCore>
28
+ <div>cell core content</div>
29
+ </CellCore>,
30
+ );
31
+
32
+ // Assert
33
+ expect(screen.queryByRole("button")).not.toBeInTheDocument();
34
+ });
35
+
36
+ it("should add a button if onClick is set", () => {
37
+ // Arrange
38
+
39
+ // Act
40
+ render(
41
+ <CellCore onClick={jest.fn()}>
42
+ <div>cell core content</div>
43
+ </CellCore>,
44
+ );
45
+
46
+ // Assert
47
+ expect(screen.getByRole("button")).toBeInTheDocument();
48
+ });
49
+
50
+ it("should add aria-label to the button", () => {
51
+ // Arrange
52
+
53
+ // Act
54
+ render(
55
+ <CellCore onClick={jest.fn()} aria-label="some description">
56
+ <div>cell core content</div>
57
+ </CellCore>,
58
+ );
59
+
60
+ // Assert
61
+ expect(
62
+ screen.getByRole("button", {name: "some description"}),
63
+ ).toBeInTheDocument();
64
+ });
65
+
66
+ it("should add aria-disabled if disabled is set", () => {
67
+ // Arrange
68
+
69
+ // Act
70
+ render(
71
+ <CellCore onClick={jest.fn()} disabled={true}>
72
+ <div>cell core content</div>
73
+ </CellCore>,
74
+ );
75
+
76
+ // Assert
77
+ expect(screen.getByRole("button")).toBeDisabled();
78
+ });
79
+
80
+ it("should add aria-current if active is set", () => {
81
+ // Arrange
82
+
83
+ // Act
84
+ const {container} = render(
85
+ <CellCore active={true}>
86
+ <div>cell core content</div>
87
+ </CellCore>,
88
+ );
89
+
90
+ // Assert
91
+ // Verify that the root element has the aria-current attribute
92
+ // eslint-disable-next-line testing-library/no-node-access
93
+ expect(container.firstChild).toHaveAttribute("aria-current", "true");
94
+ });
95
+ });
@@ -0,0 +1,47 @@
1
+ // @flow
2
+
3
+ import {getHorizontalRuleStyles} from "../common.js";
4
+
5
+ describe("getHorizontalRuleStyles", () => {
6
+ it("should get 'inset' styles as an array", () => {
7
+ // Arrange
8
+
9
+ // Act
10
+ const styles = getHorizontalRuleStyles("inset");
11
+
12
+ // Assert
13
+ // Verify that both classes are injected
14
+ expect(styles).toMatchObject([
15
+ {
16
+ _name: /horizontalRule/,
17
+ },
18
+ {
19
+ _name: /horizontalRuleInset/,
20
+ },
21
+ ]);
22
+ });
23
+
24
+ it("should get 'full-width' styles as an object", () => {
25
+ // Arrange
26
+
27
+ // Act
28
+ const styles = getHorizontalRuleStyles("full-width");
29
+
30
+ // Assert
31
+ // Verify that only one class is injected
32
+ expect(styles).toMatchObject({
33
+ _name: /horizontalRule/,
34
+ });
35
+ });
36
+
37
+ it("should not inject styles with 'none'", () => {
38
+ // Arrange
39
+
40
+ // Act
41
+ const styles = getHorizontalRuleStyles("none");
42
+
43
+ // Assert
44
+ // Verify that we don't inject any styles
45
+ expect(styles).toMatchObject({});
46
+ });
47
+ });