Skip to content

Commit

Permalink
feat(Switch): Added Switch STC-352
Browse files Browse the repository at this point in the history
  • Loading branch information
giubatt committed Jul 28, 2021
1 parent 31386bf commit f6ce143
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 0 deletions.
90 changes: 90 additions & 0 deletions src/components/Switch/Switch.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Switch, SwitchProps } from "./Switch"
import { Meta, Story } from "@storybook/react/types-6-0"
import { useEffect, useState } from "react"

export default {
title: "Design System/Switch",
component: Switch,
decorators: [
(Story) => (
<div style={{ width: 50 }}>
<Story />
</div>
),
],
} as Meta

const Template: Story<SwitchProps> = (props: SwitchProps) => {
const [checked, setChecked] = useState(props.checked)
useEffect(() => setChecked(props.checked), [props.checked])

return (
<Switch
{...props}
onChange={(event) => {
setChecked(event.target.checked)
props.onChange?.(event)
}}
checked={checked}
/>
)
}

// Each story then reuses that template
export const Unchecked = Template.bind({})
Unchecked.args = {
checked: false,
disabled: false,
color: "secondary",
}

export const Primary = Template.bind({})
Primary.args = {
checked: true,
disabled: false,
color: "primary",
}

export const Secondary = Template.bind({})
Secondary.args = {
checked: true,
disabled: false,
color: "secondary",
}

export const Success = Template.bind({})
Success.args = {
checked: true,
disabled: false,
color: "success",
}

export const Text = Template.bind({})
Text.args = {
checked: true,
disabled: false,
color: "text",
}

export const Warning = Template.bind({})
Warning.args = {
checked: true,
disabled: false,
color: "warning",
}

export const Disabled = Template.bind({})
Disabled.args = {
checked: true,
disabled: true,
color: "primary",
}

export const Labeled = Template.bind({})
Labeled.args = {
checked: true,
disabled: false,
color: "primary",
label: "Switch Label",
id: "switch",
}
26 changes: 26 additions & 0 deletions src/components/Switch/Switch.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { render, screen } from "../../tests/utils"
import { axe } from "jest-axe"
import { Switch } from "./Switch"
import userEvent from "@testing-library/user-event"

describe("Switch", () => {
test("onChange is called when switch is clicked", async () => {
// Arrange
const onChange = jest.fn()
render(<Switch checked onChange={onChange} label="TEST_SWITCH" />)

// Assert
expect(onChange).not.toHaveBeenCalled()
userEvent.click(screen.getByRole("checkbox"))
expect(onChange).toHaveBeenCalled()
})

test("passes a11y check", async () => {
// Arrange
const { container } = render(<Switch label="SWITCH_LABEL" id="SWITCH_ID" />)
const results = await axe(container)

// Assert
expect(results).toHaveNoViolations()
})
})
87 changes: 87 additions & 0 deletions src/components/Switch/Switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/** @jsxImportSource theme-ui */

import { forwardRef } from "react"

export interface SwitchProps {
checked?: boolean
disabled?: boolean
color?: "primary" | "secondary" | "text" | "success" | "warning"
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
label?: React.ReactNode
name?: string
id?: string
}

export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
{
checked,
color = "secondary",
disabled = false,
onChange,
label,
name,
id,
}: SwitchProps,
ref
) {
return (
<div
sx={{
display: "flex",
gap: 1,
cursor: disabled ? "default" : "pointer",
opacity: disabled ? 0.4 : 1,
}}
>
<div
sx={{
display: "flex",
alignItems: "center",
height: 20,
width: 40,
borderRadius: 3,
backgroundColor: checked ? color : "text.90",
position: "relative",
transition: "all 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
}}
>
<input
ref={ref}
type="checkbox"
name={name}
id={id}
checked={checked}
onChange={onChange}
disabled={disabled}
sx={{
position: "absolute",
height: "100%",
width: "100%",
cursor: "inherit",
m: 0,
p: 0,
opacity: 0,
zIndex: 1,
}}
/>
<div
sx={{
height: 16,
width: 16,
borderRadius: 3,
backgroundColor: "#FFF",
position: "absolute",
left: "2px",
transition: "all 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
transform: checked ? "translateX(20px)" : null,
}}
/>
</div>
{label && (
<label id={`label-${id}`} htmlFor={id}>
{label}
</label>
)}
</div>
)
})
3 changes: 3 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ export { useSnackbar } from "./Snackbar/hooks/useSnackbar"
export { default as Spinner } from "./Spinner/Spinner"
export type { SpinnerProps } from "./Spinner/Spinner"

export { Switch } from "./Switch/Switch"
export type { SwitchProps } from "./Switch/Switch"

export { default as Table } from "./Table/Table"
export type { TableProps } from "./Table/Table"

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f6ce143

Please sign in to comment.