@jobber/components-native 0.16.0 → 0.18.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.
- package/dist/src/Flex/Flex.js +31 -0
- package/dist/src/Flex/Flex.styles.js +24 -0
- package/dist/src/Flex/index.js +1 -0
- package/dist/src/Flex/types.js +8 -0
- package/dist/src/IconButton/IconButton.js +9 -0
- package/dist/src/IconButton/IconButton.style.js +10 -0
- package/dist/src/IconButton/index.js +1 -0
- package/dist/src/index.js +13 -11
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/src/Flex/Flex.d.ts +3 -0
- package/dist/types/src/Flex/Flex.styles.d.ts +9 -0
- package/dist/types/src/Flex/index.d.ts +1 -0
- package/dist/types/src/Flex/types.d.ts +29 -0
- package/dist/types/src/IconButton/IconButton.d.ts +35 -0
- package/dist/types/src/IconButton/IconButton.style.d.ts +8 -0
- package/dist/types/src/IconButton/index.d.ts +1 -0
- package/dist/types/src/index.d.ts +13 -11
- package/package.json +4 -2
- package/src/Flex/Flex.styles.tsx +29 -0
- package/src/Flex/Flex.test.tsx +129 -0
- package/src/Flex/Flex.tsx +71 -0
- package/src/Flex/index.ts +1 -0
- package/src/Flex/types.ts +41 -0
- package/src/IconButton/IconButton.style.ts +11 -0
- package/src/IconButton/IconButton.test.tsx +79 -0
- package/src/IconButton/IconButton.tsx +66 -0
- package/src/IconButton/index.ts +1 -0
- package/src/index.ts +13 -11
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ViewStyle } from "react-native";
|
|
2
|
+
import { ColumnKeys } from "./types";
|
|
3
|
+
export declare const styles: {
|
|
4
|
+
row: {
|
|
5
|
+
flexDirection: "row";
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export declare const columnStyles: Record<ColumnKeys, ViewStyle>;
|
|
9
|
+
export declare const gapStyles: Record<"none" | "small" | "base" | "smallest" | "smaller" | "large", ViewStyle>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Flex";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ViewStyle } from "react-native";
|
|
2
|
+
export type ColumnKeys = "shrink" | "grow";
|
|
3
|
+
export interface FlexProps {
|
|
4
|
+
/**
|
|
5
|
+
* Determine how the children gets laid out on the flex grid. If there are more
|
|
6
|
+
* Children than elements in the template, it will render multiple rows.
|
|
7
|
+
*
|
|
8
|
+
* **Supported keys**
|
|
9
|
+
* - `"grow"` - Grows to the space available. If all children are set to
|
|
10
|
+
* grow, then they'll have equal width.
|
|
11
|
+
* - `"shrink"` - Shrinks to the smallest size possible. Normally the size of
|
|
12
|
+
* the child.
|
|
13
|
+
*
|
|
14
|
+
* By default, this will set every children to grow in equal widths.
|
|
15
|
+
*/
|
|
16
|
+
readonly template?: ColumnKeys[];
|
|
17
|
+
/**
|
|
18
|
+
* It works the same way as `alignItems` style with flex.
|
|
19
|
+
*/
|
|
20
|
+
readonly align?: ViewStyle["alignItems"];
|
|
21
|
+
/**
|
|
22
|
+
* The spacing between the children.
|
|
23
|
+
*/
|
|
24
|
+
readonly gap?: Spacing;
|
|
25
|
+
}
|
|
26
|
+
export declare const spacing: readonly ["none", "smallest", "smaller", "small", "base", "large"];
|
|
27
|
+
type ValuesOfSpacing<T extends typeof spacing> = T[number];
|
|
28
|
+
export type Spacing = ValuesOfSpacing<typeof spacing>;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { IconColorNames, IconNames } from "@jobber/design";
|
|
3
|
+
interface IconButtonProps {
|
|
4
|
+
/**
|
|
5
|
+
* Press handler
|
|
6
|
+
*/
|
|
7
|
+
onPress?(): void;
|
|
8
|
+
/** The icon to show. */
|
|
9
|
+
readonly name: IconNames;
|
|
10
|
+
/**
|
|
11
|
+
* Accessibilty label for the component. It's also used for testing
|
|
12
|
+
*/
|
|
13
|
+
readonly accessibilityLabel: string;
|
|
14
|
+
/**
|
|
15
|
+
* Determines the color of the icon. If not specified, some icons have a default system colour
|
|
16
|
+
* like quotes, jobs, and invoices.
|
|
17
|
+
* Others that don't have a system colour fall back to greyBlue.
|
|
18
|
+
*/
|
|
19
|
+
readonly color?: IconColorNames;
|
|
20
|
+
/**
|
|
21
|
+
* Sets a custom color for the icon. Can be a rgb() or hex value.
|
|
22
|
+
*/
|
|
23
|
+
readonly customColor?: string;
|
|
24
|
+
/**
|
|
25
|
+
* a component that would render over the icon
|
|
26
|
+
* e.g. the number of notifications over the activity feed icon
|
|
27
|
+
*/
|
|
28
|
+
readonly badge?: React.ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* Used to locate this button in tests
|
|
31
|
+
*/
|
|
32
|
+
readonly testID?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function IconButton({ badge, name, color, customColor, onPress, accessibilityLabel, testID, }: IconButtonProps): JSX.Element;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IconButton } from "./IconButton";
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./Divider";
|
|
3
|
-
export * from "./Typography";
|
|
4
|
-
export * from "./Text";
|
|
5
|
-
export * from "./ErrorMessageWrapper";
|
|
1
|
+
export * from "./ActionItem";
|
|
6
2
|
export * from "./ActionLabel";
|
|
7
|
-
export * from "./Content";
|
|
8
3
|
export * from "./ActivityIndicator";
|
|
9
|
-
export * from "./Card";
|
|
10
|
-
export * from "./StatusLabel";
|
|
11
4
|
export * from "./AtlantisContext";
|
|
12
5
|
export * from "./Button";
|
|
6
|
+
export * from "./Card";
|
|
7
|
+
export * from "./Chip";
|
|
8
|
+
export * from "./Content";
|
|
9
|
+
export * from "./Divider";
|
|
10
|
+
export * from "./ErrorMessageWrapper";
|
|
11
|
+
export * from "./Flex";
|
|
12
|
+
export * from "./Heading";
|
|
13
|
+
export * from "./Icon";
|
|
14
|
+
export * from "./IconButton";
|
|
13
15
|
export * from "./InputFieldWrapper";
|
|
14
16
|
export * from "./ProgressBar";
|
|
15
|
-
export * from "./
|
|
16
|
-
export * from "./
|
|
17
|
-
export * from "./
|
|
17
|
+
export * from "./StatusLabel";
|
|
18
|
+
export * from "./Text";
|
|
19
|
+
export * from "./Typography";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"module": "dist/src/index.js",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@jobber/design": "^0.40.0",
|
|
25
|
+
"lodash.chunk": "^4.2.0",
|
|
25
26
|
"react-hook-form": "^7.30.0",
|
|
26
27
|
"react-intl": "^6.4.2",
|
|
27
28
|
"react-native-gesture-handler": "^2.5.0",
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"@testing-library/jest-native": "^5.4.2",
|
|
36
37
|
"@testing-library/react-hooks": "^7.0.2",
|
|
37
38
|
"@testing-library/react-native": "^12.0.1",
|
|
39
|
+
"@types/lodash.chunk": "^4.2.7",
|
|
38
40
|
"@types/react": "^18.0.28",
|
|
39
41
|
"@types/react-native": "^0.71.6",
|
|
40
42
|
"@types/react-native-uuid": "^1.4.0",
|
|
@@ -47,5 +49,5 @@
|
|
|
47
49
|
"react": "^18",
|
|
48
50
|
"react-native": ">=0.69.2"
|
|
49
51
|
},
|
|
50
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "792c5dfee7847194e09713617184b61f70431e30"
|
|
51
53
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { StyleSheet, ViewStyle } from "react-native";
|
|
2
|
+
import { ColumnKeys, Spacing, spacing } from "./types";
|
|
3
|
+
import { tokens } from "../utils/design";
|
|
4
|
+
|
|
5
|
+
export const styles = StyleSheet.create({
|
|
6
|
+
row: { flexDirection: "row" },
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const columnStyles: Record<ColumnKeys, ViewStyle> = StyleSheet.create({
|
|
10
|
+
shrink: {
|
|
11
|
+
flexGrow: 0,
|
|
12
|
+
flexShrink: 1,
|
|
13
|
+
},
|
|
14
|
+
grow: {
|
|
15
|
+
flexGrow: 1,
|
|
16
|
+
flexShrink: 0,
|
|
17
|
+
flexBasis: 0,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const gapStyles = StyleSheet.create(
|
|
22
|
+
spacing.reduce((gapObj, space) => {
|
|
23
|
+
let paddingLeft = 0;
|
|
24
|
+
if (space !== "none") paddingLeft = tokens[`space-${space}`];
|
|
25
|
+
|
|
26
|
+
gapObj[space] = { paddingLeft };
|
|
27
|
+
return gapObj;
|
|
28
|
+
}, {} as Record<Spacing, ViewStyle>),
|
|
29
|
+
);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { View, ViewStyle } from "react-native";
|
|
4
|
+
import { ReactTestInstance } from "react-test-renderer";
|
|
5
|
+
import { JobberStyle } from "@jobber/design/foundation";
|
|
6
|
+
import { Flex } from "./Flex";
|
|
7
|
+
import { FlexProps, Spacing } from "./types";
|
|
8
|
+
import { columnStyles } from "./Flex.styles";
|
|
9
|
+
import { Text } from "../Text";
|
|
10
|
+
import { Icon } from "../Icon";
|
|
11
|
+
|
|
12
|
+
function getContentComponent(parentView: ReactTestInstance) {
|
|
13
|
+
return (parentView?.children[0] as ReactTestInstance)
|
|
14
|
+
?.children[0] as ReactTestInstance;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getFlexCol(flexRow: ReactTestInstance) {
|
|
18
|
+
return flexRow.children as ReactTestInstance[];
|
|
19
|
+
}
|
|
20
|
+
function setUp(props?: FlexProps) {
|
|
21
|
+
const container = render(
|
|
22
|
+
<View accessibilityLabel="contentView">
|
|
23
|
+
<Flex align={props?.align} template={props?.template} gap={props?.gap}>
|
|
24
|
+
<Icon name={"email"} />
|
|
25
|
+
<Text>Hi onLookers!</Text>
|
|
26
|
+
<Text>You look Great Today!</Text>
|
|
27
|
+
<Text>Thanks for coming to my Ted Talk :D</Text>
|
|
28
|
+
</Flex>
|
|
29
|
+
</View>,
|
|
30
|
+
);
|
|
31
|
+
const contentView = getContentComponent(
|
|
32
|
+
container.getByLabelText("contentView"),
|
|
33
|
+
);
|
|
34
|
+
const flexRow = container.getAllByTestId("ATL-Flex-Row");
|
|
35
|
+
const flexCol = getFlexCol(flexRow[0]);
|
|
36
|
+
return { ...container, contentView, flexRow, flexCol };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("Gap", () => {
|
|
40
|
+
const gapTestCases: [Spacing, number][] = [
|
|
41
|
+
["none", 0],
|
|
42
|
+
["smallest", JobberStyle["space-smallest"]],
|
|
43
|
+
["smaller", JobberStyle["space-smaller"]],
|
|
44
|
+
["small", JobberStyle["space-small"]],
|
|
45
|
+
["base", JobberStyle["space-base"]],
|
|
46
|
+
["large", JobberStyle["space-large"]],
|
|
47
|
+
];
|
|
48
|
+
it.each(gapTestCases)(
|
|
49
|
+
"Should have a gap of %s around the children components",
|
|
50
|
+
(a, expected) => {
|
|
51
|
+
const { contentView, flexCol } = setUp({
|
|
52
|
+
template: ["grow", "grow", "shrink"],
|
|
53
|
+
gap: a,
|
|
54
|
+
});
|
|
55
|
+
expect(flexCol[1].props.style).toContainEqual({
|
|
56
|
+
paddingLeft: expected,
|
|
57
|
+
});
|
|
58
|
+
expect(contentView.props.childSpacing).toEqual(a);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("Vertical alignment", () => {
|
|
64
|
+
it("should align children to center by default if align is not specified", () => {
|
|
65
|
+
const { flexRow } = setUp({
|
|
66
|
+
template: ["grow", "grow", "shrink"],
|
|
67
|
+
gap: "large",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(flexRow[0].props.style).toContainEqual({ alignItems: "center" });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const alignTestCases: [ViewStyle["alignItems"]][] = [
|
|
74
|
+
["flex-start"],
|
|
75
|
+
["flex-end"],
|
|
76
|
+
["center"],
|
|
77
|
+
["baseline"],
|
|
78
|
+
["stretch"],
|
|
79
|
+
];
|
|
80
|
+
it.each(alignTestCases)("should align children to %s", a => {
|
|
81
|
+
const { flexRow } = setUp({
|
|
82
|
+
template: ["grow", "grow", "shrink"],
|
|
83
|
+
align: a,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(flexRow[0].props.style).toContainEqual({
|
|
87
|
+
alignItems: a,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("Layout", () => {
|
|
93
|
+
it("should by default display a 1 row flex grid with equal spacing between each children", () => {
|
|
94
|
+
const { flexCol, flexRow } = setUp({});
|
|
95
|
+
|
|
96
|
+
expect(flexCol[0].props.style).toContainEqual(columnStyles.grow);
|
|
97
|
+
expect(flexCol[1].props.style).toContainEqual(columnStyles.grow);
|
|
98
|
+
expect(flexCol[2].props.style).toContainEqual(columnStyles.grow);
|
|
99
|
+
expect(flexCol[3].props.style).toContainEqual(columnStyles.grow);
|
|
100
|
+
expect(flexRow.length).toEqual(1);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should follow the template to decide whether to grow or shrink", () => {
|
|
104
|
+
const { flexCol } = setUp({
|
|
105
|
+
template: ["grow", "grow", "shrink"],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(flexCol[0].props.style).toContainEqual(columnStyles.grow);
|
|
109
|
+
expect(flexCol[1].props.style).toContainEqual(columnStyles.grow);
|
|
110
|
+
expect(flexCol[2].props.style).toContainEqual(columnStyles.shrink);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should create a flex grid with 2 rows", () => {
|
|
114
|
+
const { flexRow } = setUp({
|
|
115
|
+
template: ["grow", "grow", "shrink"],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(flexRow.length).toEqual(2);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should inject extra children on the last row of a multiRow flex grid if needed", () => {
|
|
122
|
+
const { flexRow } = setUp({
|
|
123
|
+
template: ["grow", "grow", "shrink"],
|
|
124
|
+
});
|
|
125
|
+
const flexCol2 = getFlexCol(flexRow[1]);
|
|
126
|
+
expect(flexRow.length > 1).toBeTruthy();
|
|
127
|
+
expect(flexCol2.length).toEqual(3);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { Children, PropsWithChildren } from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import chunk from "lodash.chunk";
|
|
4
|
+
import { columnStyles, gapStyles, styles } from "./Flex.styles";
|
|
5
|
+
import { FlexProps } from "./types";
|
|
6
|
+
import { Content } from "../Content";
|
|
7
|
+
|
|
8
|
+
export function Flex({
|
|
9
|
+
template = [],
|
|
10
|
+
align = "center",
|
|
11
|
+
gap = "base",
|
|
12
|
+
children,
|
|
13
|
+
}: PropsWithChildren<FlexProps>): JSX.Element {
|
|
14
|
+
if (template.length === 1) {
|
|
15
|
+
console.warn("Please use <Content /> component for a stacked layout");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const childrenArray = Children.toArray(children);
|
|
19
|
+
const chunkedChildren = chunk(
|
|
20
|
+
childrenArray,
|
|
21
|
+
template.length || childrenArray.length,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Content spacing="none" childSpacing={gap}>
|
|
26
|
+
{chunkedChildren.map((childArray, rowIndex) => (
|
|
27
|
+
<Row key={rowIndex} template={template} align={align} gap={gap}>
|
|
28
|
+
{injectChild(childArray)}
|
|
29
|
+
</Row>
|
|
30
|
+
))}
|
|
31
|
+
</Content>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
function injectChild(value: ReturnType<typeof Children.toArray>) {
|
|
35
|
+
const hasMoreRows = chunkedChildren.length > 1;
|
|
36
|
+
const childrenCount = value.length;
|
|
37
|
+
const templateCount = template.length;
|
|
38
|
+
|
|
39
|
+
if (hasMoreRows && childrenCount < templateCount) {
|
|
40
|
+
const missingChildCount = templateCount - childrenCount;
|
|
41
|
+
|
|
42
|
+
for (let index = 0; index < missingChildCount; index++) {
|
|
43
|
+
value.push(<React.Fragment key={index} />);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function Row({
|
|
52
|
+
template = [],
|
|
53
|
+
align = "center",
|
|
54
|
+
gap = "base",
|
|
55
|
+
children,
|
|
56
|
+
}: PropsWithChildren<FlexProps>): JSX.Element {
|
|
57
|
+
return (
|
|
58
|
+
<View testID="ATL-Flex-Row" style={[styles.row, { alignItems: align }]}>
|
|
59
|
+
{Children.map(children, (child, index) => (
|
|
60
|
+
<View
|
|
61
|
+
style={[
|
|
62
|
+
columnStyles[template[index]] || columnStyles.grow,
|
|
63
|
+
index > 0 && gap && gapStyles[gap],
|
|
64
|
+
]}
|
|
65
|
+
>
|
|
66
|
+
{child}
|
|
67
|
+
</View>
|
|
68
|
+
))}
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Flex";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ViewStyle } from "react-native";
|
|
2
|
+
|
|
3
|
+
export type ColumnKeys = "shrink" | "grow";
|
|
4
|
+
|
|
5
|
+
export interface FlexProps {
|
|
6
|
+
/**
|
|
7
|
+
* Determine how the children gets laid out on the flex grid. If there are more
|
|
8
|
+
* Children than elements in the template, it will render multiple rows.
|
|
9
|
+
*
|
|
10
|
+
* **Supported keys**
|
|
11
|
+
* - `"grow"` - Grows to the space available. If all children are set to
|
|
12
|
+
* grow, then they'll have equal width.
|
|
13
|
+
* - `"shrink"` - Shrinks to the smallest size possible. Normally the size of
|
|
14
|
+
* the child.
|
|
15
|
+
*
|
|
16
|
+
* By default, this will set every children to grow in equal widths.
|
|
17
|
+
*/
|
|
18
|
+
readonly template?: ColumnKeys[];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* It works the same way as `alignItems` style with flex.
|
|
22
|
+
*/
|
|
23
|
+
readonly align?: ViewStyle["alignItems"];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The spacing between the children.
|
|
27
|
+
*/
|
|
28
|
+
readonly gap?: Spacing;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const spacing = [
|
|
32
|
+
"none",
|
|
33
|
+
"smallest",
|
|
34
|
+
"smaller",
|
|
35
|
+
"small",
|
|
36
|
+
"base",
|
|
37
|
+
"large",
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
type ValuesOfSpacing<T extends typeof spacing> = T[number];
|
|
41
|
+
export type Spacing = ValuesOfSpacing<typeof spacing>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { tokens } from "../utils/design";
|
|
3
|
+
|
|
4
|
+
export const styles = StyleSheet.create({
|
|
5
|
+
container: {
|
|
6
|
+
width: tokens["space-largest"],
|
|
7
|
+
height: tokens["space-largest"],
|
|
8
|
+
justifyContent: "center",
|
|
9
|
+
alignItems: "center",
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
+
import { IconButton } from "./IconButton";
|
|
4
|
+
import { Text } from "../Text";
|
|
5
|
+
|
|
6
|
+
describe("IconButton", () => {
|
|
7
|
+
it("renders an IconButton", () => {
|
|
8
|
+
const pressHandler = jest.fn();
|
|
9
|
+
const { getByTestId } = render(
|
|
10
|
+
<IconButton
|
|
11
|
+
onPress={pressHandler}
|
|
12
|
+
name="job"
|
|
13
|
+
accessibilityLabel="Job Button"
|
|
14
|
+
testID="JobButton"
|
|
15
|
+
/>,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(getByTestId("JobButton")).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should call the onPress", () => {
|
|
22
|
+
const pressHandler = jest.fn();
|
|
23
|
+
const { getByLabelText } = render(
|
|
24
|
+
<IconButton
|
|
25
|
+
onPress={pressHandler}
|
|
26
|
+
name="job"
|
|
27
|
+
accessibilityLabel={"Job Button"}
|
|
28
|
+
/>,
|
|
29
|
+
);
|
|
30
|
+
fireEvent.press(getByLabelText("Job Button"));
|
|
31
|
+
expect(pressHandler).toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should render badge component", () => {
|
|
35
|
+
const pressHandler = jest.fn();
|
|
36
|
+
const badge = <Text>Hi</Text>;
|
|
37
|
+
const { getByText } = render(
|
|
38
|
+
<IconButton
|
|
39
|
+
onPress={pressHandler}
|
|
40
|
+
name="job"
|
|
41
|
+
badge={badge}
|
|
42
|
+
accessibilityLabel={"Job Button"}
|
|
43
|
+
/>,
|
|
44
|
+
);
|
|
45
|
+
expect(getByText("Hi")).toBeDefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should render IconButton with custom color", () => {
|
|
49
|
+
const pressHandler = jest.fn();
|
|
50
|
+
const { getByLabelText } = render(
|
|
51
|
+
<IconButton
|
|
52
|
+
onPress={pressHandler}
|
|
53
|
+
name="job"
|
|
54
|
+
customColor="#f33323"
|
|
55
|
+
accessibilityLabel={"Job Button"}
|
|
56
|
+
/>,
|
|
57
|
+
);
|
|
58
|
+
const iconBtnColorProp = getByLabelText("Job Button").findByProps({
|
|
59
|
+
customColor: "#f33323",
|
|
60
|
+
}).props.customColor;
|
|
61
|
+
|
|
62
|
+
expect(iconBtnColorProp).toEqual("#f33323");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should expose testID", () => {
|
|
66
|
+
const pressHandler = jest.fn();
|
|
67
|
+
const testID = "JobButton";
|
|
68
|
+
const { getByTestId } = render(
|
|
69
|
+
<IconButton
|
|
70
|
+
onPress={pressHandler}
|
|
71
|
+
name="job"
|
|
72
|
+
accessibilityLabel={"Job Button"}
|
|
73
|
+
testID={testID}
|
|
74
|
+
/>,
|
|
75
|
+
);
|
|
76
|
+
fireEvent.press(getByTestId(testID));
|
|
77
|
+
expect(pressHandler).toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { TouchableOpacity } from "react-native";
|
|
3
|
+
import { IconColorNames, IconNames } from "@jobber/design";
|
|
4
|
+
import { styles } from "./IconButton.style";
|
|
5
|
+
import { Icon } from "../Icon";
|
|
6
|
+
|
|
7
|
+
interface IconButtonProps {
|
|
8
|
+
/**
|
|
9
|
+
* Press handler
|
|
10
|
+
*/
|
|
11
|
+
onPress?(): void;
|
|
12
|
+
|
|
13
|
+
/** The icon to show. */
|
|
14
|
+
readonly name: IconNames;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Accessibilty label for the component. It's also used for testing
|
|
18
|
+
*/
|
|
19
|
+
readonly accessibilityLabel: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Determines the color of the icon. If not specified, some icons have a default system colour
|
|
23
|
+
* like quotes, jobs, and invoices.
|
|
24
|
+
* Others that don't have a system colour fall back to greyBlue.
|
|
25
|
+
*/
|
|
26
|
+
readonly color?: IconColorNames;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sets a custom color for the icon. Can be a rgb() or hex value.
|
|
30
|
+
*/
|
|
31
|
+
readonly customColor?: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* a component that would render over the icon
|
|
35
|
+
* e.g. the number of notifications over the activity feed icon
|
|
36
|
+
*/
|
|
37
|
+
readonly badge?: React.ReactNode;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Used to locate this button in tests
|
|
41
|
+
*/
|
|
42
|
+
readonly testID?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function IconButton({
|
|
46
|
+
badge,
|
|
47
|
+
name,
|
|
48
|
+
color,
|
|
49
|
+
customColor,
|
|
50
|
+
onPress,
|
|
51
|
+
accessibilityLabel,
|
|
52
|
+
testID,
|
|
53
|
+
}: IconButtonProps): JSX.Element {
|
|
54
|
+
return (
|
|
55
|
+
<TouchableOpacity
|
|
56
|
+
onPress={onPress}
|
|
57
|
+
style={styles.container}
|
|
58
|
+
accessibilityLabel={accessibilityLabel}
|
|
59
|
+
accessibilityRole="button"
|
|
60
|
+
testID={testID}
|
|
61
|
+
>
|
|
62
|
+
{badge}
|
|
63
|
+
<Icon name={name} color={color} customColor={customColor} />
|
|
64
|
+
</TouchableOpacity>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IconButton } from "./IconButton";
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./Divider";
|
|
3
|
-
export * from "./Typography";
|
|
4
|
-
export * from "./Text";
|
|
5
|
-
export * from "./ErrorMessageWrapper";
|
|
1
|
+
export * from "./ActionItem";
|
|
6
2
|
export * from "./ActionLabel";
|
|
7
|
-
export * from "./Content";
|
|
8
3
|
export * from "./ActivityIndicator";
|
|
9
|
-
export * from "./Card";
|
|
10
|
-
export * from "./StatusLabel";
|
|
11
4
|
export * from "./AtlantisContext";
|
|
12
5
|
export * from "./Button";
|
|
6
|
+
export * from "./Card";
|
|
7
|
+
export * from "./Chip";
|
|
8
|
+
export * from "./Content";
|
|
9
|
+
export * from "./Divider";
|
|
10
|
+
export * from "./ErrorMessageWrapper";
|
|
11
|
+
export * from "./Flex";
|
|
12
|
+
export * from "./Heading";
|
|
13
|
+
export * from "./Icon";
|
|
14
|
+
export * from "./IconButton";
|
|
13
15
|
export * from "./InputFieldWrapper";
|
|
14
16
|
export * from "./ProgressBar";
|
|
15
|
-
export * from "./
|
|
16
|
-
export * from "./
|
|
17
|
-
export * from "./
|
|
17
|
+
export * from "./StatusLabel";
|
|
18
|
+
export * from "./Text";
|
|
19
|
+
export * from "./Typography";
|