@butternutbox/pawprint-native 0.0.1 → 0.2.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/.turbo/turbo-build.log +15 -15
- package/CHANGELOG.md +30 -0
- package/COMPONENT_GUIDELINES.md +111 -4
- package/dist/index.cjs +12413 -1459
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1111 -13
- package/dist/index.d.ts +1111 -13
- package/dist/index.js +12365 -1457
- package/dist/index.js.map +1 -1
- package/package.json +29 -11
- package/src/__mocks__/asset-stub.ts +1 -0
- package/src/__mocks__/emotion-native.tsx +18 -0
- package/src/__mocks__/react-native-gesture-handler.tsx +41 -0
- package/src/__mocks__/react-native-reanimated.tsx +79 -0
- package/src/__mocks__/react-native-safe-area-context.tsx +6 -0
- package/src/__mocks__/react-native-svg.tsx +27 -0
- package/src/__mocks__/react-native-worklets.tsx +11 -0
- package/src/__mocks__/react-native.tsx +338 -0
- package/src/__mocks__/rn-primitives/avatar.tsx +24 -0
- package/src/__mocks__/rn-primitives/checkbox.tsx +19 -0
- package/src/__mocks__/rn-primitives/select.tsx +116 -0
- package/src/__mocks__/rn-primitives/slider.tsx +40 -0
- package/src/__mocks__/rn-primitives/slot.tsx +30 -0
- package/src/__mocks__/rn-primitives/switch.tsx +24 -0
- package/src/__mocks__/rn-primitives/toggle.tsx +16 -0
- package/src/components/atoms/Avatar/Avatar.stories.tsx +57 -49
- package/src/components/atoms/Avatar/Avatar.test.tsx +269 -0
- package/src/components/atoms/Avatar/Avatar.tsx +68 -22
- package/src/components/atoms/Avatar/index.ts +1 -6
- package/src/components/atoms/Badge/Badge.stories.tsx +5 -29
- package/src/components/atoms/Badge/Badge.test.tsx +90 -0
- package/src/components/atoms/Button/Button.test.tsx +123 -0
- package/src/components/atoms/Button/Button.tsx +1 -1
- package/src/components/atoms/CarouselControls/CarouselControls.stories.tsx +217 -0
- package/src/components/atoms/CarouselControls/CarouselControls.tsx +127 -0
- package/src/components/atoms/CarouselControls/index.ts +2 -0
- package/src/components/atoms/Hint/Hint.test.tsx +36 -0
- package/src/components/atoms/Icon/Icon.test.tsx +98 -0
- package/src/components/atoms/Icon/Icon.tsx +5 -1
- package/src/components/atoms/IconButton/IconButton.test.tsx +101 -0
- package/src/components/atoms/Illustration/Illustration.stories.tsx +2 -2
- package/src/components/atoms/Illustration/Illustration.test.tsx +55 -0
- package/src/components/atoms/Illustration/Illustration.tsx +3 -3
- package/src/components/atoms/Input/Input.stories.tsx +129 -86
- package/src/components/atoms/Input/Input.test.tsx +306 -0
- package/src/components/atoms/Input/Input.tsx +9 -1
- package/src/components/atoms/Input/InputField.tsx +226 -74
- package/src/components/atoms/Link/Link.test.tsx +89 -0
- package/src/components/atoms/Link/Link.tsx +7 -6
- package/src/components/atoms/Logo/Logo.registry.ts +30 -5
- package/src/components/atoms/Logo/Logo.stories.tsx +108 -0
- package/src/components/atoms/Logo/Logo.test.tsx +56 -0
- package/src/components/atoms/Logo/assets/BCorp.tsx +113 -0
- package/src/components/atoms/Logo/assets/ButternutFavicon.tsx +33 -0
- package/src/components/atoms/Logo/assets/ButternutPrimary.tsx +294 -0
- package/src/components/atoms/Logo/assets/ButternutTabbedBottom.tsx +294 -0
- package/src/components/atoms/Logo/assets/ButternutTabbedTop.tsx +294 -0
- package/src/components/atoms/Logo/assets/ButternutWordmark.tsx +274 -0
- package/src/components/atoms/Logo/assets/PsiBufetFavicon.tsx +45 -0
- package/src/components/atoms/Logo/assets/PsiBufetPrimary.tsx +218 -0
- package/src/components/atoms/Logo/assets/PsiBufetTabbedBottom.tsx +218 -0
- package/src/components/atoms/Logo/assets/PsiBufetTabbedTop.tsx +218 -0
- package/src/components/atoms/Logo/assets/PsiBufetWordmark.tsx +195 -0
- package/src/components/atoms/Logo/assets/index.ts +11 -0
- package/src/components/atoms/NumberInput/NumberInput.stories.tsx +183 -0
- package/src/components/atoms/NumberInput/NumberInput.test.tsx +261 -0
- package/src/components/atoms/NumberInput/NumberInput.tsx +129 -0
- package/src/components/atoms/NumberInput/NumberInputField.tsx +77 -0
- package/src/components/atoms/NumberInput/index.ts +4 -0
- package/src/components/atoms/Spinner/Spinner.test.tsx +46 -0
- package/src/components/atoms/Spinner/Spinner.tsx +14 -5
- package/src/components/atoms/Switch/Switch.test.tsx +92 -0
- package/src/components/atoms/Switch/Switch.tsx +28 -17
- package/src/components/atoms/Tag/Tag.test.tsx +70 -0
- package/src/components/atoms/TextArea/TextArea.stories.tsx +303 -0
- package/src/components/atoms/TextArea/TextArea.test.tsx +416 -0
- package/src/components/atoms/TextArea/TextArea.tsx +171 -0
- package/src/components/atoms/TextArea/TextAreaField.tsx +304 -0
- package/src/components/atoms/TextArea/TextAreaLabel.tsx +103 -0
- package/src/components/atoms/TextArea/index.ts +6 -0
- package/src/components/atoms/Typography/Typography.test.tsx +94 -0
- package/src/components/atoms/index.ts +3 -0
- package/src/components/molecules/Accordion/Accordion.stories.tsx +177 -0
- package/src/components/molecules/Accordion/Accordion.test.tsx +185 -0
- package/src/components/molecules/Accordion/Accordion.tsx +284 -0
- package/src/components/molecules/Accordion/index.ts +6 -0
- package/src/components/molecules/Animated/Animated.stories.tsx +254 -0
- package/src/components/molecules/Animated/Animated.tsx +283 -0
- package/src/components/molecules/Animated/index.ts +10 -0
- package/src/components/molecules/ButtonDock/ButtonDock.stories.tsx +44 -25
- package/src/components/molecules/ButtonDock/ButtonDock.test.tsx +83 -0
- package/src/components/molecules/ButtonDock/ButtonDock.tsx +16 -13
- package/src/components/molecules/ButtonGroup/ButtonGroup.stories.tsx +48 -29
- package/src/components/molecules/ButtonGroup/ButtonGroup.test.tsx +73 -0
- package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +25 -3
- package/src/components/molecules/Checkbox/Checkbox.stories.tsx +72 -0
- package/src/components/molecules/Checkbox/Checkbox.test.tsx +117 -0
- package/src/components/molecules/Checkbox/Checkbox.tsx +101 -95
- package/src/components/molecules/CopyField/CopyField.stories.tsx +313 -0
- package/src/components/molecules/CopyField/CopyField.test.tsx +431 -0
- package/src/components/molecules/CopyField/CopyField.tsx +156 -0
- package/src/components/molecules/CopyField/CopyFieldInput.tsx +127 -0
- package/src/components/molecules/CopyField/hooks/index.ts +1 -0
- package/src/components/molecules/CopyField/hooks/useCopyField.ts +25 -0
- package/src/components/molecules/CopyField/index.ts +4 -0
- package/src/components/molecules/DatePicker/DatePicker.stories.tsx +298 -0
- package/src/components/molecules/DatePicker/DatePicker.test.tsx +201 -0
- package/src/components/molecules/DatePicker/DatePicker.tsx +590 -0
- package/src/components/molecules/DatePicker/index.ts +2 -0
- package/src/components/molecules/Drawer/Drawer.stories.tsx +285 -0
- package/src/components/molecules/Drawer/Drawer.test.tsx +180 -0
- package/src/components/molecules/Drawer/Drawer.tsx +187 -0
- package/src/components/molecules/Drawer/DrawerBody.tsx +80 -0
- package/src/components/molecules/Drawer/DrawerClose.tsx +76 -0
- package/src/components/molecules/Drawer/DrawerContent.tsx +339 -0
- package/src/components/molecules/Drawer/DrawerContext.ts +19 -0
- package/src/components/molecules/Drawer/DrawerDescription.tsx +31 -0
- package/src/components/molecules/Drawer/DrawerDragContext.ts +11 -0
- package/src/components/molecules/Drawer/DrawerFooter.tsx +49 -0
- package/src/components/molecules/Drawer/DrawerFooterContext.ts +6 -0
- package/src/components/molecules/Drawer/DrawerGrabber.tsx +62 -0
- package/src/components/molecules/Drawer/DrawerHeader.tsx +244 -0
- package/src/components/molecules/Drawer/DrawerHeaderContext.ts +13 -0
- package/src/components/molecules/Drawer/DrawerOverlay.tsx +53 -0
- package/src/components/molecules/Drawer/DrawerTitle.tsx +32 -0
- package/src/components/molecules/Drawer/index.ts +12 -0
- package/src/components/molecules/FilterTab/FilterTab.stories.tsx +210 -0
- package/src/components/molecules/FilterTab/FilterTab.tsx +310 -0
- package/src/components/molecules/FilterTab/index.ts +2 -0
- package/src/components/molecules/MessageCard/MessageCard.stories.tsx +169 -0
- package/src/components/molecules/MessageCard/MessageCard.tsx +362 -0
- package/src/components/molecules/MessageCard/index.ts +10 -0
- package/src/components/molecules/Notification/Notification.stories.tsx +219 -0
- package/src/components/molecules/Notification/Notification.tsx +426 -0
- package/src/components/molecules/Notification/index.ts +2 -0
- package/src/components/molecules/NumberField/NumberField.stories.tsx +231 -0
- package/src/components/molecules/NumberField/NumberField.tsx +186 -0
- package/src/components/molecules/NumberField/NumberFieldInput.tsx +287 -0
- package/src/components/molecules/NumberField/index.ts +2 -0
- package/src/components/molecules/PasswordField/PasswordField.stories.tsx +362 -0
- package/src/components/molecules/PasswordField/PasswordField.test.tsx +369 -0
- package/src/components/molecules/PasswordField/PasswordField.tsx +194 -0
- package/src/components/molecules/PasswordField/PasswordFieldError.tsx +53 -0
- package/src/components/molecules/PasswordField/PasswordFieldInput.tsx +73 -0
- package/src/components/molecules/PasswordField/PasswordFieldRequirements.tsx +95 -0
- package/src/components/molecules/PasswordField/hooks/index.ts +2 -0
- package/src/components/molecules/PasswordField/hooks/usePasswordField.ts +113 -0
- package/src/components/molecules/PasswordField/index.ts +10 -0
- package/src/components/molecules/PictureSelector/PictureSelector.stories.tsx +204 -0
- package/src/components/molecules/PictureSelector/PictureSelector.tsx +335 -0
- package/src/components/molecules/PictureSelector/index.ts +5 -0
- package/src/components/molecules/Progress/Progress.stories.tsx +145 -0
- package/src/components/molecules/Progress/Progress.tsx +184 -0
- package/src/components/molecules/Progress/index.ts +2 -0
- package/src/components/molecules/Radio/Radio.test.tsx +104 -0
- package/src/components/molecules/Radio/Radio.tsx +1 -2
- package/src/components/molecules/SearchField/SearchField.stories.tsx +242 -0
- package/src/components/molecules/SearchField/SearchField.test.tsx +318 -0
- package/src/components/molecules/SearchField/SearchField.tsx +143 -0
- package/src/components/molecules/SearchField/SearchFieldInput.tsx +63 -0
- package/src/components/molecules/SearchField/hooks/index.ts +1 -0
- package/src/components/molecules/SearchField/hooks/useSearchField.ts +56 -0
- package/src/components/molecules/SearchField/index.ts +4 -0
- package/src/components/molecules/SegmentedControl/SegmentedControl.stories.tsx +31 -8
- package/src/components/molecules/SegmentedControl/SegmentedControl.test.tsx +141 -0
- package/src/components/molecules/SegmentedControl/SegmentedControl.tsx +237 -23
- package/src/components/molecules/SelectField/SelectField.stories.tsx +320 -0
- package/src/components/molecules/SelectField/SelectField.test.tsx +254 -0
- package/src/components/molecules/SelectField/SelectField.tsx +236 -0
- package/src/components/molecules/SelectField/SelectFieldContent.tsx +85 -0
- package/src/components/molecules/SelectField/SelectFieldItem.tsx +133 -0
- package/src/components/molecules/SelectField/SelectFieldTrigger.tsx +170 -0
- package/src/components/molecules/SelectField/SelectFieldValue.tsx +31 -0
- package/src/components/molecules/SelectField/hooks/index.ts +2 -0
- package/src/components/molecules/SelectField/hooks/useSelectField.ts +84 -0
- package/src/components/molecules/SelectField/index.ts +10 -0
- package/src/components/molecules/Slider/Slider.test.tsx +102 -0
- package/src/components/molecules/Slider/Slider.tsx +293 -180
- package/src/components/molecules/Tooltip/Tooltip.stories.tsx +168 -0
- package/src/components/molecules/Tooltip/Tooltip.tsx +326 -0
- package/src/components/molecules/Tooltip/index.ts +2 -0
- package/src/components/molecules/index.ts +15 -0
- package/src/test-utils.tsx +20 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +16 -2
- package/vitest.config.ts +114 -0
- package/vitest.setup.ts +16 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { screen } from "@testing-library/react"
|
|
3
|
+
import { describe, it, expect } from "vitest"
|
|
4
|
+
import { renderWithTheme } from "../../../test-utils"
|
|
5
|
+
import { ButtonDock } from "./ButtonDock"
|
|
6
|
+
|
|
7
|
+
describe("ButtonDock", () => {
|
|
8
|
+
describe("when component is rendering", () => {
|
|
9
|
+
it("renders children", () => {
|
|
10
|
+
renderWithTheme(
|
|
11
|
+
<ButtonDock>
|
|
12
|
+
<button>Continue</button>
|
|
13
|
+
</ButtonDock>
|
|
14
|
+
)
|
|
15
|
+
expect(screen.getByText("Continue")).toBeInTheDocument()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it("renders with default stacked variant", () => {
|
|
19
|
+
renderWithTheme(
|
|
20
|
+
<ButtonDock>
|
|
21
|
+
<button>Primary</button>
|
|
22
|
+
<button>Secondary</button>
|
|
23
|
+
</ButtonDock>
|
|
24
|
+
)
|
|
25
|
+
expect(screen.getByText("Primary")).toBeInTheDocument()
|
|
26
|
+
expect(screen.getByText("Secondary")).toBeInTheDocument()
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe("when rendering variants", () => {
|
|
31
|
+
it("renders stacked variant", () => {
|
|
32
|
+
renderWithTheme(
|
|
33
|
+
<ButtonDock variant="stacked">
|
|
34
|
+
<button>Action</button>
|
|
35
|
+
</ButtonDock>
|
|
36
|
+
)
|
|
37
|
+
expect(screen.getByText("Action")).toBeInTheDocument()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("renders inline variant", () => {
|
|
41
|
+
renderWithTheme(
|
|
42
|
+
<ButtonDock variant="inline">
|
|
43
|
+
<button>Action</button>
|
|
44
|
+
</ButtonDock>
|
|
45
|
+
)
|
|
46
|
+
expect(screen.getByText("Action")).toBeInTheDocument()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe("when rendering with description", () => {
|
|
51
|
+
it("renders description in stacked variant", () => {
|
|
52
|
+
renderWithTheme(
|
|
53
|
+
<ButtonDock description="Helper text">
|
|
54
|
+
<button>Action</button>
|
|
55
|
+
</ButtonDock>
|
|
56
|
+
)
|
|
57
|
+
expect(screen.getByText("Helper text")).toBeInTheDocument()
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
describe("when rendering with leading content", () => {
|
|
62
|
+
it("renders leading content in stacked variant", () => {
|
|
63
|
+
renderWithTheme(
|
|
64
|
+
<ButtonDock leadingContent={<span>Leading</span>}>
|
|
65
|
+
<button>Action</button>
|
|
66
|
+
</ButtonDock>
|
|
67
|
+
)
|
|
68
|
+
expect(screen.getByText("Leading")).toBeInTheDocument()
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe("when using with ref", () => {
|
|
73
|
+
it("forwards ref", () => {
|
|
74
|
+
const ref = React.createRef<any>()
|
|
75
|
+
renderWithTheme(
|
|
76
|
+
<ButtonDock ref={ref}>
|
|
77
|
+
<button>Action</button>
|
|
78
|
+
</ButtonDock>
|
|
79
|
+
)
|
|
80
|
+
expect(ref.current).toBeTruthy()
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -39,12 +39,14 @@ const StyledDockRoot = styled(View)<{
|
|
|
39
39
|
})
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
-
const StyledStackedInner = styled(View)(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
const StyledStackedInner = styled(View)<{ innerGap: number }>(
|
|
43
|
+
({ innerGap }) => ({
|
|
44
|
+
alignItems: "center",
|
|
45
|
+
gap: innerGap,
|
|
46
|
+
width: "100%",
|
|
47
|
+
maxWidth: 520
|
|
48
|
+
})
|
|
49
|
+
)
|
|
48
50
|
|
|
49
51
|
const StyledButtonGroup = styled(View)<{
|
|
50
52
|
groupDirection: "column" | "row"
|
|
@@ -85,7 +87,7 @@ const ButtonDock = React.forwardRef<View, ButtonDockProps>(
|
|
|
85
87
|
) => {
|
|
86
88
|
const theme = useTheme()
|
|
87
89
|
const { buttonDock, buttonGroup } = theme.tokens.components
|
|
88
|
-
const { dimensions } = theme.tokens.semantics
|
|
90
|
+
const { dimensions, typography } = theme.tokens.semantics
|
|
89
91
|
const isStacked = variant === "stacked"
|
|
90
92
|
|
|
91
93
|
const groupGap = parseTokenValue(
|
|
@@ -101,12 +103,14 @@ const ButtonDock = React.forwardRef<View, ButtonDockProps>(
|
|
|
101
103
|
dockBorderTopWidth={parseTokenValue(dimensions.borderWidth.sm)}
|
|
102
104
|
dockBorderTopColor={buttonDock.colour.border}
|
|
103
105
|
dockPaddingVertical={parseTokenValue(
|
|
104
|
-
buttonDock.spacing[variant].
|
|
106
|
+
buttonDock.spacing[variant].mobile.topPadding
|
|
105
107
|
)}
|
|
106
108
|
{...rest}
|
|
107
109
|
>
|
|
108
110
|
{isStacked ? (
|
|
109
|
-
<StyledStackedInner
|
|
111
|
+
<StyledStackedInner
|
|
112
|
+
innerGap={parseTokenValue(buttonGroup.spacing.gap)}
|
|
113
|
+
>
|
|
110
114
|
{leadingContent}
|
|
111
115
|
<StyledButtonGroup
|
|
112
116
|
groupDirection="column"
|
|
@@ -118,12 +122,11 @@ const ButtonDock = React.forwardRef<View, ButtonDockProps>(
|
|
|
118
122
|
</StyledButtonGroup>
|
|
119
123
|
{description && (
|
|
120
124
|
<Typography
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
weight="medium"
|
|
125
|
+
token={typography.body.medium.md}
|
|
126
|
+
color={buttonGroup.colour.text}
|
|
124
127
|
align="center"
|
|
125
128
|
>
|
|
126
|
-
{
|
|
129
|
+
{description}
|
|
127
130
|
</Typography>
|
|
128
131
|
)}
|
|
129
132
|
</StyledStackedInner>
|
|
@@ -11,65 +11,84 @@ export default {
|
|
|
11
11
|
layout: {
|
|
12
12
|
control: { type: "select" },
|
|
13
13
|
options: ["stacked", "inline"],
|
|
14
|
-
description: "Layout direction
|
|
14
|
+
description: "Layout direction for the buttons"
|
|
15
15
|
},
|
|
16
16
|
description: {
|
|
17
17
|
control: { type: "text" },
|
|
18
|
-
description: "Optional text below buttons"
|
|
18
|
+
description: "Optional text displayed below the buttons"
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
type PlaygroundArgs = ButtonGroupProps & {
|
|
24
|
+
primaryLabel: string
|
|
25
|
+
secondaryLabel: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Playground = ({
|
|
29
|
+
primaryLabel,
|
|
30
|
+
secondaryLabel,
|
|
31
|
+
...args
|
|
32
|
+
}: PlaygroundArgs) => (
|
|
33
|
+
<ButtonGroup {...args}>
|
|
34
|
+
<Button colour="primary">{primaryLabel}</Button>
|
|
35
|
+
{secondaryLabel?.trim() && (
|
|
36
|
+
<Button colour="secondary">{secondaryLabel}</Button>
|
|
37
|
+
)}
|
|
38
|
+
</ButtonGroup>
|
|
39
|
+
)
|
|
40
|
+
Playground.args = {
|
|
41
|
+
layout: "stacked",
|
|
42
|
+
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
|
43
|
+
primaryLabel: "Confirm",
|
|
44
|
+
secondaryLabel: "Cancel"
|
|
45
|
+
}
|
|
46
|
+
Playground.argTypes = {
|
|
47
|
+
primaryLabel: {
|
|
48
|
+
control: { type: "text" },
|
|
49
|
+
description: "Label for the primary button"
|
|
27
50
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</Button>
|
|
34
|
-
</ButtonGroup>
|
|
35
|
-
)
|
|
51
|
+
secondaryLabel: {
|
|
52
|
+
control: { type: "text" },
|
|
53
|
+
description:
|
|
54
|
+
"Label for the secondary button (leave empty for single button)"
|
|
55
|
+
}
|
|
36
56
|
}
|
|
37
57
|
|
|
38
58
|
export const Stacked = () => (
|
|
39
59
|
<View style={styles.column}>
|
|
40
|
-
<ButtonGroup
|
|
41
|
-
<Button
|
|
42
|
-
<Button
|
|
43
|
-
Secondary action
|
|
44
|
-
</Button>
|
|
60
|
+
<ButtonGroup description="Lorem ipsum dolor sit amet, consectetur adipiscing elit.">
|
|
61
|
+
<Button colour="primary">Confirm</Button>
|
|
62
|
+
<Button colour="secondary">Cancel</Button>
|
|
45
63
|
</ButtonGroup>
|
|
46
64
|
</View>
|
|
47
65
|
)
|
|
48
66
|
|
|
49
67
|
export const Inline = () => (
|
|
50
68
|
<View style={styles.column}>
|
|
51
|
-
<ButtonGroup
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
<ButtonGroup
|
|
70
|
+
layout="inline"
|
|
71
|
+
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
|
72
|
+
>
|
|
73
|
+
<Button colour="secondary">Cancel</Button>
|
|
74
|
+
<Button colour="primary">Confirm</Button>
|
|
54
75
|
</ButtonGroup>
|
|
55
76
|
</View>
|
|
56
77
|
)
|
|
57
78
|
|
|
58
79
|
export const SingleButton = () => (
|
|
59
80
|
<View style={styles.column}>
|
|
60
|
-
<ButtonGroup
|
|
61
|
-
<Button
|
|
81
|
+
<ButtonGroup description="Lorem ipsum dolor sit amet, consectetur adipiscing elit.">
|
|
82
|
+
<Button colour="primary">Continue</Button>
|
|
62
83
|
</ButtonGroup>
|
|
63
84
|
</View>
|
|
64
85
|
)
|
|
65
86
|
|
|
66
87
|
export const WithoutDescription = () => (
|
|
67
88
|
<View style={styles.column}>
|
|
68
|
-
<ButtonGroup
|
|
69
|
-
<Button
|
|
70
|
-
<Button
|
|
71
|
-
Cancel
|
|
72
|
-
</Button>
|
|
89
|
+
<ButtonGroup>
|
|
90
|
+
<Button colour="primary">Confirm</Button>
|
|
91
|
+
<Button colour="secondary">Cancel</Button>
|
|
73
92
|
</ButtonGroup>
|
|
74
93
|
</View>
|
|
75
94
|
)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { screen } from "@testing-library/react"
|
|
3
|
+
import { describe, it, expect } from "vitest"
|
|
4
|
+
import { renderWithTheme } from "../../../test-utils"
|
|
5
|
+
import { ButtonGroup } from "./ButtonGroup"
|
|
6
|
+
|
|
7
|
+
describe("ButtonGroup", () => {
|
|
8
|
+
describe("when component is rendering", () => {
|
|
9
|
+
it("renders children buttons", () => {
|
|
10
|
+
renderWithTheme(
|
|
11
|
+
<ButtonGroup>
|
|
12
|
+
<button>Confirm</button>
|
|
13
|
+
<button>Cancel</button>
|
|
14
|
+
</ButtonGroup>
|
|
15
|
+
)
|
|
16
|
+
expect(screen.getByText("Confirm")).toBeInTheDocument()
|
|
17
|
+
expect(screen.getByText("Cancel")).toBeInTheDocument()
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe("when rendering layouts", () => {
|
|
22
|
+
it("renders stacked layout by default", () => {
|
|
23
|
+
renderWithTheme(
|
|
24
|
+
<ButtonGroup>
|
|
25
|
+
<button>Action</button>
|
|
26
|
+
</ButtonGroup>
|
|
27
|
+
)
|
|
28
|
+
expect(screen.getByText("Action")).toBeInTheDocument()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it("renders inline layout", () => {
|
|
32
|
+
renderWithTheme(
|
|
33
|
+
<ButtonGroup layout="inline">
|
|
34
|
+
<button>Action</button>
|
|
35
|
+
</ButtonGroup>
|
|
36
|
+
)
|
|
37
|
+
expect(screen.getByText("Action")).toBeInTheDocument()
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe("when rendering with description", () => {
|
|
42
|
+
it("renders description text", () => {
|
|
43
|
+
renderWithTheme(
|
|
44
|
+
<ButtonGroup description="Choose an option">
|
|
45
|
+
<button>Yes</button>
|
|
46
|
+
<button>No</button>
|
|
47
|
+
</ButtonGroup>
|
|
48
|
+
)
|
|
49
|
+
expect(screen.getByText("Choose an option")).toBeInTheDocument()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("does not render description when not provided", () => {
|
|
53
|
+
renderWithTheme(
|
|
54
|
+
<ButtonGroup>
|
|
55
|
+
<button>Yes</button>
|
|
56
|
+
</ButtonGroup>
|
|
57
|
+
)
|
|
58
|
+
expect(screen.queryByText("Choose an option")).not.toBeInTheDocument()
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe("when using with ref", () => {
|
|
63
|
+
it("forwards ref", () => {
|
|
64
|
+
const ref = React.createRef<any>()
|
|
65
|
+
renderWithTheme(
|
|
66
|
+
<ButtonGroup ref={ref}>
|
|
67
|
+
<button>Action</button>
|
|
68
|
+
</ButtonGroup>
|
|
69
|
+
)
|
|
70
|
+
expect(ref.current).toBeTruthy()
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -26,7 +26,7 @@ const StyledGroupRoot = styled(View)<{
|
|
|
26
26
|
}))
|
|
27
27
|
|
|
28
28
|
const StyledButtonRow = styled(View)<{
|
|
29
|
-
rowDirection: "row
|
|
29
|
+
rowDirection: "row" | "column"
|
|
30
30
|
rowAlign?: "center"
|
|
31
31
|
rowGap: number
|
|
32
32
|
}>(({ rowDirection, rowAlign, rowGap }) => ({
|
|
@@ -36,6 +36,14 @@ const StyledButtonRow = styled(View)<{
|
|
|
36
36
|
...(rowAlign ? { alignItems: rowAlign } : {})
|
|
37
37
|
}))
|
|
38
38
|
|
|
39
|
+
const StyledChildSlot = styled(View)<{ slotInline: boolean }>(
|
|
40
|
+
({ slotInline }) => ({
|
|
41
|
+
...(slotInline
|
|
42
|
+
? { flex: 1, minWidth: 0 }
|
|
43
|
+
: { width: "100%", alignSelf: "stretch" })
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
|
|
39
47
|
/**
|
|
40
48
|
* ButtonGroup arranges 1 or 2 buttons in a stacked or inline layout
|
|
41
49
|
* with an optional description below.
|
|
@@ -67,7 +75,7 @@ export const ButtonGroup = React.forwardRef<View, ButtonGroupProps>(
|
|
|
67
75
|
{...rest}
|
|
68
76
|
>
|
|
69
77
|
<StyledButtonRow
|
|
70
|
-
rowDirection={isInline ? "row
|
|
78
|
+
rowDirection={isInline ? "row" : "column"}
|
|
71
79
|
rowAlign={isInline ? "center" : undefined}
|
|
72
80
|
rowGap={parseTokenValue(
|
|
73
81
|
isInline
|
|
@@ -75,7 +83,21 @@ export const ButtonGroup = React.forwardRef<View, ButtonGroupProps>(
|
|
|
75
83
|
: buttonGroup.spacing.stacked.gap
|
|
76
84
|
)}
|
|
77
85
|
>
|
|
78
|
-
{children
|
|
86
|
+
{React.Children.map(children, (child, index) => {
|
|
87
|
+
if (!React.isValidElement(child)) return child
|
|
88
|
+
return (
|
|
89
|
+
<StyledChildSlot key={index} slotInline={isInline}>
|
|
90
|
+
{React.cloneElement(
|
|
91
|
+
child as React.ReactElement<{
|
|
92
|
+
fullWidth?: boolean
|
|
93
|
+
}>,
|
|
94
|
+
{
|
|
95
|
+
fullWidth: true
|
|
96
|
+
}
|
|
97
|
+
)}
|
|
98
|
+
</StyledChildSlot>
|
|
99
|
+
)
|
|
100
|
+
})}
|
|
79
101
|
</StyledButtonRow>
|
|
80
102
|
{description && (
|
|
81
103
|
<Typography
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import { View, StyleSheet } from "react-native"
|
|
3
|
+
import {
|
|
4
|
+
LabradorChefsHat,
|
|
5
|
+
CockerSpanielChefsHat,
|
|
6
|
+
CockapooBowl
|
|
7
|
+
} from "@butternutbox/pawprint-illustrations/breeds"
|
|
3
8
|
import { Checkbox } from "./Checkbox"
|
|
4
9
|
import type { CheckboxProps } from "./Checkbox"
|
|
5
10
|
import { CheckboxGroup } from "./CheckboxGroup"
|
|
6
11
|
import { Typography } from "../../atoms/Typography"
|
|
7
12
|
|
|
13
|
+
const ILLUSTRATION_SIZE = 72
|
|
14
|
+
|
|
8
15
|
export default {
|
|
9
16
|
title: "Molecules/Checkbox",
|
|
10
17
|
component: Checkbox,
|
|
@@ -109,6 +116,71 @@ export const Tile = () => (
|
|
|
109
116
|
</View>
|
|
110
117
|
)
|
|
111
118
|
|
|
119
|
+
export const WithIllustration = () => (
|
|
120
|
+
<View style={styles.column}>
|
|
121
|
+
<View style={styles.section}>
|
|
122
|
+
<Typography size="sm" weight="semiBold" color="tertiary">
|
|
123
|
+
Vertical
|
|
124
|
+
</Typography>
|
|
125
|
+
<CheckboxGroup orientation="vertical">
|
|
126
|
+
<Checkbox
|
|
127
|
+
variant="tile"
|
|
128
|
+
label="Chicken"
|
|
129
|
+
subText="Tender & tasty"
|
|
130
|
+
illustration={
|
|
131
|
+
<LabradorChefsHat
|
|
132
|
+
width={ILLUSTRATION_SIZE}
|
|
133
|
+
height={ILLUSTRATION_SIZE}
|
|
134
|
+
/>
|
|
135
|
+
}
|
|
136
|
+
/>
|
|
137
|
+
<Checkbox
|
|
138
|
+
variant="tile"
|
|
139
|
+
label="Treats"
|
|
140
|
+
subText="Tasty rewards"
|
|
141
|
+
illustration={
|
|
142
|
+
<CockerSpanielChefsHat
|
|
143
|
+
width={ILLUSTRATION_SIZE}
|
|
144
|
+
height={ILLUSTRATION_SIZE}
|
|
145
|
+
/>
|
|
146
|
+
}
|
|
147
|
+
/>
|
|
148
|
+
<Checkbox
|
|
149
|
+
variant="tile"
|
|
150
|
+
label="Dry Food"
|
|
151
|
+
subText="Crunchy kibble"
|
|
152
|
+
illustration={
|
|
153
|
+
<CockapooBowl
|
|
154
|
+
width={ILLUSTRATION_SIZE}
|
|
155
|
+
height={ILLUSTRATION_SIZE}
|
|
156
|
+
/>
|
|
157
|
+
}
|
|
158
|
+
/>
|
|
159
|
+
</CheckboxGroup>
|
|
160
|
+
</View>
|
|
161
|
+
|
|
162
|
+
<View style={styles.section}>
|
|
163
|
+
<Typography size="sm" weight="semiBold" color="tertiary">
|
|
164
|
+
Selected
|
|
165
|
+
</Typography>
|
|
166
|
+
<CheckboxGroup orientation="vertical">
|
|
167
|
+
<Checkbox
|
|
168
|
+
variant="tile"
|
|
169
|
+
label="Chicken"
|
|
170
|
+
subText="Tender & tasty"
|
|
171
|
+
defaultChecked
|
|
172
|
+
illustration={
|
|
173
|
+
<LabradorChefsHat
|
|
174
|
+
width={ILLUSTRATION_SIZE}
|
|
175
|
+
height={ILLUSTRATION_SIZE}
|
|
176
|
+
/>
|
|
177
|
+
}
|
|
178
|
+
/>
|
|
179
|
+
</CheckboxGroup>
|
|
180
|
+
</View>
|
|
181
|
+
</View>
|
|
182
|
+
)
|
|
183
|
+
|
|
112
184
|
export const AllStates = () => (
|
|
113
185
|
<View style={styles.column}>
|
|
114
186
|
{(["standalone", "tile"] as const).map((variant) => (
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { screen } from "@testing-library/react"
|
|
3
|
+
import userEvent from "@testing-library/user-event"
|
|
4
|
+
import { describe, it, expect, vi } from "vitest"
|
|
5
|
+
import { renderWithTheme } from "../../../test-utils"
|
|
6
|
+
import { Checkbox } from "./Checkbox"
|
|
7
|
+
|
|
8
|
+
describe("Checkbox", () => {
|
|
9
|
+
describe("when component is rendering", () => {
|
|
10
|
+
it("renders label text", () => {
|
|
11
|
+
renderWithTheme(<Checkbox label="Accept terms" />)
|
|
12
|
+
expect(screen.getByText("Accept terms")).toBeInTheDocument()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it("renders label and subText", () => {
|
|
16
|
+
renderWithTheme(
|
|
17
|
+
<Checkbox label="Accept terms" subText="Required to proceed" />
|
|
18
|
+
)
|
|
19
|
+
expect(screen.getByText("Accept terms")).toBeInTheDocument()
|
|
20
|
+
expect(screen.getByText("Required to proceed")).toBeInTheDocument()
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe("when rendering variants", () => {
|
|
25
|
+
it("renders standalone variant", () => {
|
|
26
|
+
renderWithTheme(<Checkbox variant="standalone" label="Standalone" />)
|
|
27
|
+
expect(screen.getByText("Standalone")).toBeInTheDocument()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it("renders tile variant", () => {
|
|
31
|
+
renderWithTheme(<Checkbox variant="tile" label="Tile" />)
|
|
32
|
+
expect(screen.getByText("Tile")).toBeInTheDocument()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("renders tile variant with illustration", () => {
|
|
36
|
+
renderWithTheme(
|
|
37
|
+
<Checkbox
|
|
38
|
+
variant="tile"
|
|
39
|
+
label="With illustration"
|
|
40
|
+
illustration={<span data-testid="illustration">🐶</span>}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
expect(screen.getByTestId("illustration")).toBeInTheDocument()
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe("when component is uncontrolled", () => {
|
|
48
|
+
it("toggles checked state when clicked", async () => {
|
|
49
|
+
const user = userEvent.setup()
|
|
50
|
+
renderWithTheme(<Checkbox label="Toggle" />)
|
|
51
|
+
|
|
52
|
+
const checkbox = screen.getByRole("checkbox")
|
|
53
|
+
expect(checkbox).toHaveAttribute("aria-checked", "false")
|
|
54
|
+
|
|
55
|
+
await user.click(checkbox)
|
|
56
|
+
expect(checkbox).toHaveAttribute("aria-checked", "true")
|
|
57
|
+
|
|
58
|
+
await user.click(checkbox)
|
|
59
|
+
expect(checkbox).toHaveAttribute("aria-checked", "false")
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it("can be initially checked via defaultChecked", () => {
|
|
63
|
+
renderWithTheme(<Checkbox label="Checked" defaultChecked />)
|
|
64
|
+
|
|
65
|
+
const checkbox = screen.getByRole("checkbox")
|
|
66
|
+
expect(checkbox).toHaveAttribute("aria-checked", "true")
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe("when component is controlled", () => {
|
|
71
|
+
it("calls onCheckedChange when clicked", async () => {
|
|
72
|
+
const user = userEvent.setup()
|
|
73
|
+
const onCheckedChange = vi.fn()
|
|
74
|
+
renderWithTheme(
|
|
75
|
+
<Checkbox
|
|
76
|
+
label="Controlled"
|
|
77
|
+
checked={false}
|
|
78
|
+
onCheckedChange={onCheckedChange}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
await user.click(screen.getByRole("checkbox"))
|
|
83
|
+
expect(onCheckedChange).toHaveBeenCalledWith(true)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it("reflects controlled checked state", () => {
|
|
87
|
+
renderWithTheme(
|
|
88
|
+
<Checkbox label="Checked" checked={true} onCheckedChange={() => {}} />
|
|
89
|
+
)
|
|
90
|
+
expect(screen.getByRole("checkbox")).toHaveAttribute(
|
|
91
|
+
"aria-checked",
|
|
92
|
+
"true"
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe("when component is disabled", () => {
|
|
98
|
+
it("prevents interaction when disabled", async () => {
|
|
99
|
+
const user = userEvent.setup()
|
|
100
|
+
const onCheckedChange = vi.fn()
|
|
101
|
+
renderWithTheme(
|
|
102
|
+
<Checkbox label="Disabled" disabled onCheckedChange={onCheckedChange} />
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
await user.click(screen.getByRole("checkbox"))
|
|
106
|
+
expect(onCheckedChange).not.toHaveBeenCalled()
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe("when using with ref", () => {
|
|
111
|
+
it("forwards ref", () => {
|
|
112
|
+
const ref = React.createRef<any>()
|
|
113
|
+
renderWithTheme(<Checkbox ref={ref} label="Ref test" />)
|
|
114
|
+
expect(ref.current).toBeTruthy()
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
})
|