Skip to content

Commit

Permalink
chore(Switch): migrate to Tailwind
Browse files Browse the repository at this point in the history
  • Loading branch information
DSil committed Oct 5, 2023
1 parent 2e6b7e4 commit 951112c
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import * as React from "react";
import { action } from "@storybook/addon-actions";
import { select, text, boolean } from "@storybook/addon-knobs";

import RenderInRtl from "../../utils/rtl/RenderInRtl";
import * as Icons from "../../icons";

import Switch from ".";

const getIcons = (name: string, defaultIcon: string) =>
select(name, [null, ...Object.keys(Icons)], defaultIcon);

const getIcon = (source: string | null) => source && Icons[source];

export default {
title: "Tailwind/Switch",
};

export const Default = () => {
const checked = boolean("checked", true);
return <Switch onChange={action("onChange")} checked={checked} />;
};

Default.story = {
name: "Default Switch",
};

export const CustomIcon = () => {
const checked = boolean("checked", true);
const Icon = getIcon(getIcons("icon", "Lock"));
return <Switch onChange={action("onChange")} checked={checked} icon={Icon && <Icon />} />;
};

CustomIcon.story = {
name: "Custom icon",
parameters: {
info: "You can try all possible configurations of this component. However, check Orbit.Kiwi for more detailed design guidelines.",
},
};

export const Playground = () => {
const checked = boolean("checked", false);
const dataTest = text("dataTest", "");
const Icon = getIcon(getIcons("icon", "Lock"));
const disabled = boolean("disabled", false);

return (
<Switch
onChange={action("onChange")}
checked={checked}
disabled={disabled}
dataTest={dataTest}
icon={Icon && <Icon />}
/>
);
};

Playground.story = {
parameters: {
info: "You can try all possible configurations of this component. However, check Orbit.Kiwi for more detailed design guidelines.",
},
};

export const Rtl = () => {
const checked = boolean("checked", true);
return (
<RenderInRtl>
<Switch onChange={action("onChange")} checked={checked} />
</RenderInRtl>
);
};

Rtl.story = {
name: "RTL",

parameters: {
info: "This is a preview of this component in RTL setup.",
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from "react";
import userEvent from "@testing-library/user-event";

import { screen, render, fireEvent } from "../../../test-utils";
import KEY_CODE_MAP from "../../../common/keyMaps";
import Switch from "..";

describe("Switch", () => {
const user = userEvent.setup();

it("should have expected DOM output", async () => {
const dataTest = "test";
const onChange = jest.fn();
const onFocus = jest.fn();

render(<Switch dataTest={dataTest} checked onChange={onChange} onFocus={onFocus} />);

await user.click(screen.getByRole("switch"));
expect(onChange).toHaveBeenCalled();

fireEvent.keyDown(screen.getByTestId("test"), { keyCode: KEY_CODE_MAP.SPACE });
fireEvent.keyDown(screen.getByTestId("test"), { keyCode: KEY_CODE_MAP.ENTER });
expect(onChange).toHaveBeenCalledTimes(3);

await user.tab();
expect(onFocus).toHaveBeenCalled();
expect(screen.getByRole("switch")).toHaveAttribute("checked");
expect(screen.getByTestId(dataTest)).toBeInTheDocument();
});

it("should be disabled", async () => {
const onChange = jest.fn();
render(<Switch checked onChange={onChange} disabled />);

await user.click(screen.getByRole("switch"));
expect(onChange).not.toHaveBeenCalled();
expect(screen.getByRole("switch")).toHaveAttribute("disabled");
});
});
59 changes: 59 additions & 0 deletions packages/orbit-components/src/tmp-tailwind/Switch/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use client";

import * as React from "react";
import cx from "clsx";

import Circle from "../../icons/Circle";
import handleKeyDown from "../../utils/handleKeyDown";
import type { Props } from "../../Switch/types";

const Switch = React.forwardRef<HTMLInputElement, Props>(
({ onChange, checked, dataTest, id, icon, onBlur, onFocus, disabled, ariaLabelledby }, ref) => {
return (
<label className="inline-block">
<div
className={cx(
"duration-fast relative flex h-[20px] w-[40px] items-center justify-between rounded-[100px] transition-colors",
disabled ? "opacity-50" : "cursor-pointer",
checked ? "bg-blue-normal" : "bg-cloud-dark",
)}
>
<input
className="peer absolute inset-0 m-0 h-full w-full p-0 opacity-0"
ref={ref}
checked={checked}
disabled={disabled}
aria-checked={checked}
role="switch"
aria-labelledby={ariaLabelledby}
onKeyDown={!disabled ? handleKeyDown<HTMLInputElement>(undefined, onChange) : undefined}
onBlur={!disabled ? onBlur : undefined}
onChange={!disabled ? onChange : undefined}
onFocus={!disabled ? onFocus : undefined}
type="checkbox"
data-test={dataTest}
id={id}
/>
<div
className={cx(
"rounded-circle bg-white-normal duration-fast shadow-switch absolute box-border inline-flex h-[24px] w-[24px] items-center justify-center",
"peer-focus:outline-blue-normal peer-focus:outline peer-focus:outline-2",
"[&_svg]:h-[12px] [&_svg]:w-[12px]",
!disabled && "active:shadow-action-active",
!checked && (icon ? "[&_svg]:text-ink-normal" : "[&_svg]:text-cloud-dark"),
checked
? "[&_svg]:text-blue-normal left-[calc(100%+2px)] -translate-x-full"
: "left-[-3px]",
)}
>
{icon || <Circle />}
</div>
</div>
</label>
);
},
);

Switch.displayName = "Switch";

export default Switch;

0 comments on commit 951112c

Please sign in to comment.