-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |