Merge pull request #132 from unix/test

test: add test cases
This commit is contained in:
witt
2020-04-25 16:41:25 +08:00
committed by GitHub
8 changed files with 1110 additions and 3 deletions

View File

@@ -0,0 +1,181 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Textarea should render correctly 1`] = `
"<div class=\\"wrapper \\"><textarea placeholder=\\"placeholder\\"></textarea><style>
.wrapper {
display: inline-flex;
box-sizing: border-box;
user-select: none;
width: initial;
min-width: 12.5rem;
max-width: 95vw;
height: auto;
border-radius: 5px;
border: 1px solid #eaeaea;
color: #000;
transition: border 0.2s ease 0s, color 0.2s ease 0s;
}
.wrapper.hover {
border-color: #666;
}
.wrapper.disabled {
background-color: #fafafa;
border-color: #eaeaea;
cursor: not-allowed;
}
textarea {
background-color: transparent;
box-shadow: none;
display: block;
font-family: -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", sans-serif;
font-size: .875rem;
width: 100%;
height: 100%;
min-height: 6.25rem;
resize: none;
border: none;
outline: none;
padding: 8pt;
}
.disabled > textarea {
cursor: not-allowed;
}
</style></div>"
`;
exports[`Textarea should work with different styles 1`] = `
"<div><div class=\\"wrapper \\"><textarea></textarea><style>
.wrapper {
display: inline-flex;
box-sizing: border-box;
user-select: none;
width: initial;
min-width: 12.5rem;
max-width: 95vw;
height: auto;
border-radius: 5px;
border: 1px solid #666;
color: #000;
transition: border 0.2s ease 0s, color 0.2s ease 0s;
}
.wrapper.hover {
border-color: #666;
}
.wrapper.disabled {
background-color: #fafafa;
border-color: #eaeaea;
cursor: not-allowed;
}
textarea {
background-color: transparent;
box-shadow: none;
display: block;
font-family: -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", sans-serif;
font-size: .875rem;
width: 100%;
height: 100%;
min-height: 6.25rem;
resize: none;
border: none;
outline: none;
padding: 8pt;
}
.disabled > textarea {
cursor: not-allowed;
}
</style></div><div class=\\"wrapper \\"><textarea></textarea><style>
.wrapper {
display: inline-flex;
box-sizing: border-box;
user-select: none;
width: 20%;
min-width: 12.5rem;
max-width: 95vw;
height: auto;
border-radius: 5px;
border: 1px solid #eaeaea;
color: #000;
transition: border 0.2s ease 0s, color 0.2s ease 0s;
}
.wrapper.hover {
border-color: #666;
}
.wrapper.disabled {
background-color: #fafafa;
border-color: #eaeaea;
cursor: not-allowed;
}
textarea {
background-color: transparent;
box-shadow: none;
display: block;
font-family: -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", sans-serif;
font-size: .875rem;
width: 100%;
height: 100%;
min-height: 6.25rem;
resize: none;
border: none;
outline: none;
padding: 8pt;
}
.disabled > textarea {
cursor: not-allowed;
}
</style></div><div class=\\"wrapper \\"><textarea></textarea><style>
.wrapper {
display: inline-flex;
box-sizing: border-box;
user-select: none;
width: initial;
min-width: 12.5rem;
max-width: 95vw;
height: auto;
border-radius: 5px;
border: 1px solid #eaeaea;
color: #000;
transition: border 0.2s ease 0s, color 0.2s ease 0s;
}
.wrapper.hover {
border-color: #666;
}
.wrapper.disabled {
background-color: #fafafa;
border-color: #eaeaea;
cursor: not-allowed;
}
textarea {
background-color: transparent;
box-shadow: none;
display: block;
font-family: -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", sans-serif;
font-size: .875rem;
width: 100%;
height: 100%;
min-height: 100px;
resize: none;
border: none;
outline: none;
padding: 8pt;
}
.disabled > textarea {
cursor: not-allowed;
}
</style></div></div>"
`;

View File

@@ -0,0 +1,72 @@
import React from 'react'
import { mount } from 'enzyme'
import { Textarea } from 'components'
import { nativeEvent } from 'tests/utils'
describe('Textarea', () => {
it('should render correctly', () => {
const wrapper = mount(<Textarea placeholder="placeholder" />)
expect(wrapper.html()).toMatchSnapshot()
expect(() => wrapper.unmount()).not.toThrow()
})
it('should work with different styles', () => {
const wrapper = mount(
<div>
<Textarea status="secondary" />
<Textarea width="20%" />
<Textarea minHeight="100px" />
</div>
)
expect(wrapper.html()).toMatchSnapshot()
expect(() => wrapper.unmount()).not.toThrow()
})
it('should set textarea from value', () => {
const wrapper = mount(<Textarea initialValue="test-value" />)
let el = wrapper.find('textarea').getDOMNode() as HTMLTextAreaElement
expect(el.value).toEqual('test-value')
wrapper.setProps({ value: 'test-value2' })
el = wrapper.find('textarea').getDOMNode() as HTMLTextAreaElement
expect(el.value).toEqual('test-value2')
})
it('should trigger events when textarea changed', () => {
let value = ''
const handler = jest.fn()
.mockImplementation(e => value = e.target.value)
const wrapper = mount(<Textarea onChange={handler} />)
wrapper.find('textarea')
.simulate('change', { target: { value: 'test-value' } })
expect(handler).toHaveBeenCalled()
expect(value).toEqual('test-value')
handler.mockRestore()
})
it('should ignore events when disabled or readonly', () => {
const handler = jest.fn()
const wrapper = mount(<Textarea onChange={handler} disabled />)
wrapper.find('textarea')
.simulate('change', { target: { value: 'test-value' } })
expect(handler).not.toHaveBeenCalled()
wrapper.setProps({ disabled: false, readOnly: true })
wrapper.find('textarea')
.simulate('change', { target: { value: 'test-value2' } })
expect(handler).not.toHaveBeenCalled()
handler.mockRestore()
})
it('should pass through blur event', () => {
const blurHandler = jest.fn()
const focusHandler = jest.fn()
const wrapper = mount(<Textarea onBlur={blurHandler} onFocus={focusHandler} />)
wrapper.find('textarea').simulate('focus', nativeEvent)
expect(focusHandler).toHaveBeenCalled()
wrapper.find('textarea').simulate('blur', nativeEvent)
expect(blurHandler).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,251 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`UseToast should render different actions 1`] = `
"<div class=\\"toast-container \\"><div class=\\"toast \\"><div class=\\"message\\">hello</div><div class=\\"action\\"><button class=\\"btn \\"><div class=\\"text\\">remove</div><style>
.btn {
box-sizing: border-box;
display: inline-block;
padding: 0 0.625rem;
height: 1.5rem;
line-height: 1.5rem;
min-width: min-content;
width: auto;
border-radius: 5px;
font-weight: 400;
font-size: .75rem;
user-select: none;
outline: none;
text-transform: capitalize;
justify-content: center;
text-align: center;
white-space: nowrap;
transition: all 0.2s ease;
position: relative;
overflow: hidden;
color: #fff;
background-color: #000;
border: 1px solid #000;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
}
.btn:hover {
color: #000;
background-color: #fff;
border-color: #000;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
transform: translate3d(0px, 0px, 0px);
}
.text {
position: relative;
z-index: 1;
display: inline-flex;
justify-content: center;
align-items: center;
text-align: center;
line-height: inherit;
top: -1px;
}
.text :global(p), .text :global(pre), .text :global(div) {
margin: 0;
}
</style></button><button class=\\"btn \\"><div class=\\"text\\">remove</div><style>
.btn {
box-sizing: border-box;
display: inline-block;
padding: 0 0.625rem;
height: 1.5rem;
line-height: 1.5rem;
min-width: min-content;
width: auto;
border-radius: 5px;
font-weight: 400;
font-size: .75rem;
user-select: none;
outline: none;
text-transform: capitalize;
justify-content: center;
text-align: center;
white-space: nowrap;
transition: all 0.2s ease;
position: relative;
overflow: hidden;
color: #666;
background-color: #fff;
border: 1px solid #eaeaea;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
}
.btn:hover {
color: #000;
background-color: #fff;
border-color: #000;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
transform: translate3d(0px, 0px, 0px);
}
.text {
position: relative;
z-index: 1;
display: inline-flex;
justify-content: center;
align-items: center;
text-align: center;
line-height: inherit;
top: -1px;
}
.text :global(p), .text :global(pre), .text :global(div) {
margin: 0;
}
</style></button></div><style>
.toast {
width: 420px;
max-width: 90vw;
max-height: 75px;
display: flex;
justify-content: space-between;
align-items: center;
color: #000;
background-color: #fff;
color: #000;
border: 0;
border-radius: 5px;
padding: 16pt;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
position: absolute;
bottom: 0;
right: 0;
opacity: 0;
transform: translate3d(0, 100%, 0px) scale(1);
transition: all 400ms ease;
}
.toast.visible {
opacity: 1;
transform: translate3d(0, calc(100% + -75px + -0px), -0px) scale(1);
}
.toast.hide {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.message {
align-items: center;
height: 100%;
transition: opacity .4s ease;
font-size: .875rem;
display: -webkit-box;
word-break: break-all;
padding-right: 8pt;
overflow: hidden;
max-height: 100%;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 1.1rem;
}
.toast :global(button + button) {
margin-left: 4pt;
}
</style></div><style>
.toast-container {
position: fixed;
width: 420px;
max-width: 90vw;
bottom: 16pt;
right: 16pt;
z-index: 2000;
transition: all 400ms ease;
box-sizing: border-box;
}
.toast-container.hover {
transform: translate3d(0, -10px, 0);
}
</style></div>"
`;
exports[`UseToast should work with different types 1`] = `
"<div class=\\"toast-container \\"><div class=\\"toast \\"><div class=\\"message\\">hello</div><div class=\\"action\\"></div><style>
.toast {
width: 420px;
max-width: 90vw;
max-height: 75px;
display: flex;
justify-content: space-between;
align-items: center;
color: #000;
background-color: #0070f3;
color: #fff;
border: 0;
border-radius: 5px;
padding: 16pt;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
position: absolute;
bottom: 0;
right: 0;
opacity: 0;
transform: translate3d(0, 100%, 0px) scale(1);
transition: all 400ms ease;
}
.toast.visible {
opacity: 1;
transform: translate3d(0, calc(100% + -75px + -0px), -0px) scale(1);
}
.toast.hide {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.message {
align-items: center;
height: 100%;
transition: opacity .4s ease;
font-size: .875rem;
display: -webkit-box;
word-break: break-all;
padding-right: 8pt;
overflow: hidden;
max-height: 100%;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 1.1rem;
}
.toast :global(button + button) {
margin-left: 4pt;
}
</style></div><style>
.toast-container {
position: fixed;
width: 420px;
max-width: 90vw;
bottom: 16pt;
right: 16pt;
z-index: 2000;
transition: all 400ms ease;
box-sizing: border-box;
}
.toast-container.hover {
transform: translate3d(0, -10px, 0);
}
</style></div>"
`;

View File

@@ -0,0 +1,151 @@
import React from 'react'
import { mount, ReactWrapper } from 'enzyme'
import { useToasts, ZEITUIProvider } from 'components'
import { nativeEvent, updateWrapper } from 'tests/utils'
const MockToast: React.FC<{}> = () => {
const [, setToast] = useToasts()
const clickHandler = (e: any = {}) => {
const keys = ['text', 'delay', 'type', 'actions']
const params = keys.reduce((pre, key) => {
const value = e.target[key]
if (!value) return pre
return { ...pre, [key]: value }
}, {})
setToast(params)
}
return <button id="btn" onClick={clickHandler}>btn</button>
}
const triggerToast = (wrapper: ReactWrapper, params = {}) => {
wrapper.find('#btn').simulate('click', {
...nativeEvent,
target: params,
})
}
const expectToastIsShow = (wrapper: ReactWrapper) => {
const toast = wrapper.find('.toast-container').find('.toast')
expect(toast.length).not.toBe(0)
}
const expectToastIsHidden = (wrapper: ReactWrapper) => {
const toast = wrapper.find('.toast-container').find('.toast')
expect(toast.length).toBe(0)
}
describe('UseToast', () => {
it('should render correctly', async () => {
const wrapper = mount(<ZEITUIProvider><MockToast /></ZEITUIProvider>)
expectToastIsHidden(wrapper)
triggerToast(wrapper, { text: 'test-value' })
await updateWrapper(wrapper)
expectToastIsShow(wrapper)
})
it('should work with different types', async () => {
const wrapper = mount(<ZEITUIProvider><MockToast /></ZEITUIProvider>)
expectToastIsHidden(wrapper)
triggerToast(wrapper, { type: 'success', text: 'hello' })
await updateWrapper(wrapper)
expectToastIsShow(wrapper)
expect(wrapper.find('.toast-container').html()).toMatchSnapshot()
})
it('should close toast', async () => {
const wrapper = mount(<ZEITUIProvider><MockToast /></ZEITUIProvider>)
expectToastIsHidden(wrapper)
triggerToast(wrapper, { delay: 100, text: 'hello' })
await updateWrapper(wrapper, 0)
expectToastIsShow(wrapper)
// Element already hidden, but Dom was removed after delay
await updateWrapper(wrapper, 350)
const toast = wrapper.find('.toast-container').find('.hide')
expect(toast.length).not.toBe(0)
})
it('the removeal should be delayed when hover is triggerd', async () => {
const wrapper = mount(<ZEITUIProvider><MockToast /></ZEITUIProvider>)
expectToastIsHidden(wrapper)
triggerToast(wrapper, { delay: 100, text: 'hello' })
await updateWrapper(wrapper, 0)
expectToastIsShow(wrapper)
wrapper.find('.toast-container').simulate('mouseEnter', nativeEvent)
await updateWrapper(wrapper, 350)
// Hover event will postpone hidden event
let toast = wrapper.find('.toast-container').find('.hide')
expect(toast.length).toBe(0)
// Restart hidden event after mouse leave
wrapper.find('.toast-container').simulate('mouseLeave', nativeEvent)
await updateWrapper(wrapper, 350 + 200)
toast = wrapper.find('.toast-container').find('.hide')
expect(toast.length).not.toBe(0)
})
it('should render different actions', async () => {
const wrapper = mount(<ZEITUIProvider><MockToast /></ZEITUIProvider>)
const actions = [{
name: 'remove',
handler: () => {},
}, {
name: 'remove',
handler: () => {},
passive: true,
}]
triggerToast(wrapper, { actions, text: 'hello' })
await updateWrapper(wrapper)
expectToastIsShow(wrapper)
expect(wrapper.find('.toast-container').html()).toMatchSnapshot()
})
it('should close toast when action triggered', async () => {
const wrapper = mount(<ZEITUIProvider><MockToast /></ZEITUIProvider>)
const actions = [{
name: 'remove',
handler: (_event: any, cancel: Function) => cancel()
}]
expectToastIsHidden(wrapper)
triggerToast(wrapper, { actions, text: 'hello' })
await updateWrapper(wrapper)
expectToastIsShow(wrapper)
wrapper.find('.action').find('.btn').at(0)
.simulate('click', nativeEvent)
// Element already hidden, but Dom was removed after delay
await updateWrapper(wrapper, 250)
const toast = wrapper.find('.toast-container').find('.hide')
expect(toast.length).not.toBe(0)
})
it('should work with multiple toasts', async () => {
const wrapper = mount(<ZEITUIProvider><MockToast /></ZEITUIProvider>)
expectToastIsHidden(wrapper)
triggerToast(wrapper, { delay: 100, text: 'hello' })
triggerToast(wrapper, { delay: 100, text: 'hello' })
triggerToast(wrapper, { delay: 100, text: 'hello' })
triggerToast(wrapper, { delay: 100, text: 'hello' })
triggerToast(wrapper, { delay: 100, text: 'hello' })
triggerToast(wrapper, { delay: 200, text: 'hello' })
/**
* If there are multiple Toasts at different deplay in the stack,
* the destory Dom event will wait for the maximum delay time.
*/
await updateWrapper(wrapper, 350)
expectToastIsShow(wrapper)
await updateWrapper(wrapper, 200)
const toast = wrapper.find('.toast-container').find('.hide')
expect(toast.length).not.toBe(0)
})
})

View File

@@ -76,13 +76,17 @@ const ToastItem: React.FC<ToatItemProps> = React.memo(({
}, [])
useEffect(() => {
let unMount = false
const shouldBeHide = reverseIndex > 2 || toast.willBeDestroy
if (!shouldBeHide) return
if (!shouldBeHide || unMount) return
const timer = setTimeout(() => {
setHide(true)
clearTimeout(timer)
}, 150)
return () => clearTimeout(timer)
return () => {
unMount = true
clearTimeout(timer)
}
}, [reverseIndex, toast.willBeDestroy])
return (

View File

@@ -35,10 +35,13 @@ const useToasts = (): [Array<Toast>, (t: Toast) => void] => {
// Wait for all components to display before destroying
// The destory means direct remove all element, whether in animation or not.
const nextDestoryTime = delay + 500
/* istanbul ignore next */
if (nextDestoryTime < maxDestoryTime.current) return
clearTimeout(destoryTimer.current)
maxDestoryTime.current = nextDestoryTime
destoryTimer.current = window.setTimeout(() => {
/* istanbul ignore next */
updateToasts((currentToasts: Array<ToastWithID>) => {
if (destoryStack.current.length < currentToasts.length) {
return currentToasts
@@ -49,7 +52,6 @@ const useToasts = (): [Array<Toast>, (t: Toast) => void] => {
clearTimeout(destoryTimer.current)
}, maxDestoryTime.current)
}
const setToast = (toast: Toast): void => {
const id = `toast-${getId()}`

View File

@@ -0,0 +1,369 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Toggle should render correctly 1`] = `
"<label class=\\"\\"><input type=\\"checkbox\\"><div class=\\"toggle \\"><span class=\\"inner\\"></span></div><style>
label {
-webkit-tap-highlight-color: transparent;
display: inline-block;
vertical-align: center;
white-space: nowrap;
user-select: none;
padding: 3px 0;
position: relative;
cursor: pointer;
}
input {
overflow: hidden;
visibility: hidden;
height: 0;
opacity: 0;
width: 0;
position: absolute;
background-color: transparent;
z-index: -1;
}
.toggle {
height: .875rem;
width: 1.75rem;
border-radius: .875rem;
transition-delay: 0.12s;
transition-duration: 0.2s;
transition-property: background, border;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
position: relative;
border: 1px solid transparent;
background-color: #eaeaea;
padding: 0;
}
.inner {
width: calc(.875rem - 2px);
height: calc(.875rem - 2px);
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 1px;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0, rgba(0, 0, 0, 0.1) 0 1px 3px 0;
transition: left 280ms cubic-bezier(0, 0, 0.2, 1);
border-radius: 50%;
background-color: #fff;
}
.disabled {
border-color: #eaeaea;
background-color: #fafafa;
}
.disabled > .inner {
background-color: #eaeaea;
}
.disabled.checked {
border-color: #888;
background-color: #888;
}
.checked {
background-color: #0070f3;
}
.checked > .inner {
left: calc(100% - (.875rem - 2px));
box-shadow: none;
}
</style></label>"
`;
exports[`Toggle should work with different sizes 1`] = `
"<div><label class=\\"\\"><input type=\\"checkbox\\"><div class=\\"toggle \\"><span class=\\"inner\\"></span></div><style>
label {
-webkit-tap-highlight-color: transparent;
display: inline-block;
vertical-align: center;
white-space: nowrap;
user-select: none;
padding: 3px 0;
position: relative;
cursor: pointer;
}
input {
overflow: hidden;
visibility: hidden;
height: 0;
opacity: 0;
width: 0;
position: absolute;
background-color: transparent;
z-index: -1;
}
.toggle {
height: .835rem;
width: 1.67rem;
border-radius: .835rem;
transition-delay: 0.12s;
transition-duration: 0.2s;
transition-property: background, border;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
position: relative;
border: 1px solid transparent;
background-color: #eaeaea;
padding: 0;
}
.inner {
width: calc(.835rem - 2px);
height: calc(.835rem - 2px);
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 1px;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0, rgba(0, 0, 0, 0.1) 0 1px 3px 0;
transition: left 280ms cubic-bezier(0, 0, 0.2, 1);
border-radius: 50%;
background-color: #fff;
}
.disabled {
border-color: #eaeaea;
background-color: #fafafa;
}
.disabled > .inner {
background-color: #eaeaea;
}
.disabled.checked {
border-color: #888;
background-color: #888;
}
.checked {
background-color: #0070f3;
}
.checked > .inner {
left: calc(100% - (.835rem - 2px));
box-shadow: none;
}
</style></label><label class=\\"\\"><input type=\\"checkbox\\"><div class=\\"toggle \\"><span class=\\"inner\\"></span></div><style>
label {
-webkit-tap-highlight-color: transparent;
display: inline-block;
vertical-align: center;
white-space: nowrap;
user-select: none;
padding: 3px 0;
position: relative;
cursor: pointer;
}
input {
overflow: hidden;
visibility: hidden;
height: 0;
opacity: 0;
width: 0;
position: absolute;
background-color: transparent;
z-index: -1;
}
.toggle {
height: .835rem;
width: 1.67rem;
border-radius: .835rem;
transition-delay: 0.12s;
transition-duration: 0.2s;
transition-property: background, border;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
position: relative;
border: 1px solid transparent;
background-color: #eaeaea;
padding: 0;
}
.inner {
width: calc(.835rem - 2px);
height: calc(.835rem - 2px);
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 1px;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0, rgba(0, 0, 0, 0.1) 0 1px 3px 0;
transition: left 280ms cubic-bezier(0, 0, 0.2, 1);
border-radius: 50%;
background-color: #fff;
}
.disabled {
border-color: #eaeaea;
background-color: #fafafa;
}
.disabled > .inner {
background-color: #eaeaea;
}
.disabled.checked {
border-color: #888;
background-color: #888;
}
.checked {
background-color: #0070f3;
}
.checked > .inner {
left: calc(100% - (.835rem - 2px));
box-shadow: none;
}
</style></label><label class=\\"\\"><input type=\\"checkbox\\"><div class=\\"toggle \\"><span class=\\"inner\\"></span></div><style>
label {
-webkit-tap-highlight-color: transparent;
display: inline-block;
vertical-align: center;
white-space: nowrap;
user-select: none;
padding: 3px 0;
position: relative;
cursor: pointer;
}
input {
overflow: hidden;
visibility: hidden;
height: 0;
opacity: 0;
width: 0;
position: absolute;
background-color: transparent;
z-index: -1;
}
.toggle {
height: .875rem;
width: 1.75rem;
border-radius: .875rem;
transition-delay: 0.12s;
transition-duration: 0.2s;
transition-property: background, border;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
position: relative;
border: 1px solid transparent;
background-color: #eaeaea;
padding: 0;
}
.inner {
width: calc(.875rem - 2px);
height: calc(.875rem - 2px);
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 1px;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0, rgba(0, 0, 0, 0.1) 0 1px 3px 0;
transition: left 280ms cubic-bezier(0, 0, 0.2, 1);
border-radius: 50%;
background-color: #fff;
}
.disabled {
border-color: #eaeaea;
background-color: #fafafa;
}
.disabled > .inner {
background-color: #eaeaea;
}
.disabled.checked {
border-color: #888;
background-color: #888;
}
.checked {
background-color: #0070f3;
}
.checked > .inner {
left: calc(100% - (.875rem - 2px));
box-shadow: none;
}
</style></label><label class=\\"\\"><input type=\\"checkbox\\"><div class=\\"toggle \\"><span class=\\"inner\\"></span></div><style>
label {
-webkit-tap-highlight-color: transparent;
display: inline-block;
vertical-align: center;
white-space: nowrap;
user-select: none;
padding: 3px 0;
position: relative;
cursor: pointer;
}
input {
overflow: hidden;
visibility: hidden;
height: 0;
opacity: 0;
width: 0;
position: absolute;
background-color: transparent;
z-index: -1;
}
.toggle {
height: 1rem;
width: 2rem;
border-radius: 1rem;
transition-delay: 0.12s;
transition-duration: 0.2s;
transition-property: background, border;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
position: relative;
border: 1px solid transparent;
background-color: #eaeaea;
padding: 0;
}
.inner {
width: calc(1rem - 2px);
height: calc(1rem - 2px);
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 1px;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0, rgba(0, 0, 0, 0.1) 0 1px 3px 0;
transition: left 280ms cubic-bezier(0, 0, 0.2, 1);
border-radius: 50%;
background-color: #fff;
}
.disabled {
border-color: #eaeaea;
background-color: #fafafa;
}
.disabled > .inner {
background-color: #eaeaea;
}
.disabled.checked {
border-color: #888;
background-color: #888;
}
.checked {
background-color: #0070f3;
}
.checked > .inner {
left: calc(100% - (1rem - 2px));
box-shadow: none;
}
</style></label></div>"
`;

View File

@@ -0,0 +1,77 @@
import React from 'react'
import { mount, ReactWrapper } from 'enzyme'
import { Toggle } from 'components'
import { nativeEvent, updateWrapper } from 'tests/utils'
const expectToggleIsChecked = (wrapper: ReactWrapper) => {
expect(wrapper.find('.checked').length).not.toBe(0)
}
const expectToggleIsUnChecked = (wrapper: ReactWrapper) => {
expect(wrapper.find('.checked').length).toBe(0)
}
describe('Toggle', () => {
it('should render correctly', () => {
const wrapper = mount(<Toggle />)
expect(wrapper.html()).toMatchSnapshot()
expect(() => wrapper.unmount()).not.toThrow()
})
it('should work with different sizes', () => {
const wrapper = mount(
<div>
<Toggle size="mini" />
<Toggle size="small" />
<Toggle size="medium" />
<Toggle size="large" />
</div>
)
expect(wrapper.html()).toMatchSnapshot()
expect(() => wrapper.unmount()).not.toThrow()
})
it('should set toggle follow checked prop', async () => {
const wrapper = mount(<Toggle initialChecked={true} />)
expectToggleIsChecked(wrapper)
wrapper.setProps({ checked: false })
await updateWrapper(wrapper)
expectToggleIsUnChecked(wrapper)
wrapper.setProps({ checked: true })
await updateWrapper(wrapper)
expectToggleIsChecked(wrapper)
})
it('should trigger events when toggle changed', async () => {
let checked = false
const changeHandler = jest.fn()
.mockImplementation(e => checked = e.target.checked)
const wrapper = mount(<Toggle onChange={changeHandler} />)
wrapper.find('input').simulate('change', {
...nativeEvent,
target: { checked: true },
})
await updateWrapper(wrapper)
expectToggleIsChecked(wrapper)
expect(changeHandler).toHaveBeenCalled()
expect(checked).toEqual(true)
})
it('should ignore events when toggle disabled', async () => {
const changeHandler = jest.fn()
const wrapper = mount(<Toggle onChange={changeHandler} disabled />)
wrapper.find('input').simulate('change', {
...nativeEvent,
target: { checked: true },
})
await updateWrapper(wrapper)
expectToggleIsUnChecked(wrapper)
expect(changeHandler).not.toHaveBeenCalled()
})
})