Skip to content

Commit

Permalink
feat: 🎸 imp radio com
Browse files Browse the repository at this point in the history
  • Loading branch information
careteenL committed Aug 23, 2020
1 parent c3c2d21 commit 13915e5
Show file tree
Hide file tree
Showing 3 changed files with 346 additions and 0 deletions.
133 changes: 133 additions & 0 deletions src/components/radio/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import React, { useState } from "react";
import { Radio } from "./index";
import { withKnobs, text, boolean, select } from "@storybook/addon-knobs";
import { action } from "@storybook/addon-actions";
import { color } from "../shared/styles";
import { Icon } from "../icon";

export default {
title: "Entry/Radio",
component: Radio,
decorators: [withKnobs],
};

export const knobsRadio = () => (
<Radio
appearance={select<keyof typeof color>(
"color",
Object.keys(color) as Array<keyof typeof color>,
"primary"
)}
label={text("label", "i am radio")}
onChange={onChange}
hideLabel={boolean("hideLabel", false)}
error={text("error", "")}
description={text("description", "")}
disabled={boolean("disabled", false)}
></Radio>
);

export const testColors = () => (
<div>
{Object.keys(color).map((v, i) => (
<Radio
key={i}
name="group2"
label={v}
appearance={v as keyof typeof color}
/>
))}
</div>
);

const onChange = action("change");

export const testOnchange = () => (
<form>
<Radio name="group1" label="apple" onChange={onChange} />
<Radio name="group1" label="banana" onChange={onChange} />
<Radio name="group1" label="pear" onChange={onChange} />
<Radio name="group1" label="mongo" onChange={onChange} />
<Radio name="group1" label="watermelon" onChange={onChange} />
</form>
);

export const testDisabled = () => <Radio label="disabled" disabled></Radio>;

export const testExtraText = () => (
<Radio
label="the radio has extra text"
error="error text"
description="description text"
></Radio>
);

export const testHideLabel = () => (
<Radio
label="the radio has extra text"
description="label will hidden"
hideLabel
></Radio>
);

export const withIcon = () => (
<Radio
label={
<span>
<Icon icon="redux"></Icon>with icon
</span>
}
></Radio>
);

function ParentControl() {
const [state, setState] = useState(() => new Array(5).fill(false));
const onClick = (e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
const target = e.target as HTMLInputElement;
const index = (target.value as unknown) as number;
let newArr = new Array(5).fill(false);
newArr[index] = true;
setState(newArr);
};
return (
<div>
<Radio
label="apple"
onClick={onClick}
value={0}
checked={state[0]}
onChange={() => {}}
/>
<Radio
label="banana"
onClick={onClick}
value={1}
checked={state[1]}
onChange={() => {}}
/>
<Radio
label="pear"
onClick={onClick}
value={2}
checked={state[2]}
onChange={() => {}}
/>
<Radio
label="mongo"
onClick={onClick}
value={3}
checked={state[3]}
onChange={() => {}}
/>
<Radio
label="watermelon"
onClick={onClick}
value={4}
checked={state[4]}
onChange={() => {}}
/>
</div>
);
}

export const testParentControl = () => <ParentControl></ParentControl>;
47 changes: 47 additions & 0 deletions src/components/radio/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from "react";
import { render } from "@testing-library/react";
import { Radio } from "./index";
import { color } from "../shared/styles";

const testfn = jest.fn();
const disablefn = jest.fn();
describe("test Radio component", () => {
it("should checked when clicked", () => {
const wrapper = render(<Radio label="test" onChange={testfn}></Radio>);
expect(wrapper).toMatchSnapshot();
const input = wrapper.container.querySelector("input")!;
expect(testfn).not.toHaveBeenCalled();
// fireEvent.click(input);
// expect(testfn).toHaveBeenCalled();
});
it("should render extra text", () => {
const wrapper = render(
<Radio
label="test"
error="errortext"
description="description"
onChange={testfn}
></Radio>
);
expect(wrapper).toMatchSnapshot();
const errortext = wrapper.getByText("errortext");
expect(errortext).toHaveStyle(`color:${color.negative}`);
const description = wrapper.getByText("description");
expect(description).toHaveStyle(`color:${color.mediumdark}`);
});
it("should hide label", () => {
const wrapper = render(<Radio label="test" hideLabel></Radio>);
expect(wrapper).toMatchSnapshot();
const text = wrapper.getByText("test");
expect(text).toHaveStyle("clip-path: inset(100%)");
});
it("should disabled", () => {
const wrapper = render(
<Radio label="test" disabled onChange={disablefn}></Radio>
);
expect(wrapper).toMatchSnapshot();
const text = wrapper.getByText("test");
// fireEvent.click(text);
// expect(disablefn).not.toHaveBeenCalled();
});
});
166 changes: 166 additions & 0 deletions src/components/radio/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React, { ReactNode, AllHTMLAttributes } from "react";
import styled, { css } from "styled-components";
import { color, typography } from "../shared/styles";
import { rgba } from "polished";

const Label = styled.label<RadioProps>`
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
font-size: ${typography.size.s2}px;
font-weight: ${typography.weight.bold};
position: relative;
height: 1em;
display: flex;
align-items: center;
opacity: ${(props) => (props.disabled ? 0.5 : 1)};
`;

const OptionalText = styled.span<RadioProps>`
${(props) =>
props.hideLabel &&
css`
border: 0px !important;
clip: rect(0 0 0 0) !important;
-webkit-clip-path: inset(100%) !important;
clip-path: inset(100%) !important;
height: 1px !important;
overflow: hidden !important;
padding: 0px !important;
position: absolute !important;
white-space: nowrap !important;
width: 1px !important;
`}
`;

const Description = styled.div`
font-size: ${typography.size.s1}px;
font-weight: ${typography.weight.regular};
color: ${color.mediumdark};
margin-top: 4px;
margin-left: calc(${typography.size.s2}px + 0.4em);
width: 100%;
`;

const RadioWrapper = styled.div`
display: flex;
align-items: center;
flex-wrap: wrap;
`;

const Input = styled.input<RadioProps>`
margin: 0 0.4em 0 0;
font-size: initial;
opacity: 0;
& + span {
&:before,
&:after {
position: absolute;
top: 0;
left: 0;
height: 1em;
width: 1em;
content: "";
display: block;
border-radius: 3em;
}
}
& + span:before {
box-shadow: ${color.mediumdark} 0 0 0 1px inset;
}
&:focus + span:before {
box-shadow: ${(props) => color[props.appearance!]} 0 0 0 1px inset;
}
&:checked + span:before {
box-shadow: ${(props) => color[props.appearance!]} 0 0 0 1px inset;
}
&:checked:focus + span:before {
box-shadow: ${(props) => color[props.appearance!]} 0 0 0 1px inset,
${(props) => rgba(color[props.appearance!], 0.3)} 0 0 5px 2px;
}
& + span:after {
transition: all 150ms ease-out;
transform: scale3d(0, 0, 1);
height: 10px;
margin-left: 2px;
margin-top: 2px;
width: 10px;
opacity: 0;
}
&:checked + span:after {
transform: scale3d(1, 1, 1);
background: ${(props) => color[props.appearance!]};
opacity: 1;
}
`;

const Error = styled.span`
font-weight: ${typography.weight.regular};
font-size: ${typography.size.s2}px;
color: ${color.negative};
margin-left: 6px;
height: 1em;
display: flex;
align-items: center;
`;

export interface RadioProps
extends Omit<AllHTMLAttributes<HTMLInputElement>, "as" | "label"> {
/** 主题色 */
appearance?: keyof typeof color;
/** label展示 */
label?: ReactNode;
/** 是否隐藏label*/
hideLabel?: boolean;
/** 错误文本 */
error?: string;
/** 描述文本 */
description?: string;
/** wrapper类名 */
wrapperClass?: string;
}

export function Radio(props: RadioProps) {
const {
wrapperClass,
error,
description,
label,
hideLabel,
style,
...restProps
} = props;
const { disabled } = props;

return (
<RadioWrapper className={wrapperClass} style={style}>
<Label disabled={disabled}>
<Input
{...restProps}
role="radio"
aria-invalid={!!error}
type="radio"
/>
<span>
<OptionalText hideLabel={hideLabel}>{label}</OptionalText>
</span>
</Label>
{error && <Error>{error}</Error>}
{description && <Description>{description}</Description>}
</RadioWrapper>
);
}

Radio.defaultProps = {
appearance: "primary",
hideLabel: false,
};

export default Radio;

0 comments on commit 13915e5

Please sign in to comment.