[improvement] Actionsheet: tsx (#2807)

This commit is contained in:
neverland 2019-02-20 15:55:02 +08:00 committed by GitHub
parent 9236132dbf
commit 52e6fd2f0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 147 additions and 102 deletions

View File

@ -1,98 +0,0 @@
import { use } from '../utils';
import { PopupMixin } from '../mixins/popup';
import Icon from '../icon';
import Loading from '../loading';
import Popup from '../popup';
const [sfc, bem] = use('actionsheet');
export default sfc({
props: {
...PopupMixin.props,
title: String,
actions: Array,
cancelText: String,
overlay: {
type: Boolean,
default: true
},
closeOnClickOverlay: {
type: Boolean,
default: true
}
},
methods: {
onSelect(event, item) {
event.stopPropagation();
if (!item.disabled && !item.loading) {
if (item.callback) {
item.callback(item);
}
this.$emit('select', item);
}
},
onCancel() {
this.$emit('input', false);
this.$emit('cancel');
}
},
render(h) {
const { title, cancelText, onCancel } = this;
const Header = () => (
<div class={[bem('header'), 'van-hairline--top-bottom']}>
{title}
<Icon name="close" class={bem('close')} onClick={onCancel} />
</div>
);
const Option = item => (
<div
class={[
bem('item', { disabled: item.disabled || item.loading }),
item.className,
'van-hairline--top'
]}
onClick={event => {
this.onSelect(event, item);
}}
>
{item.loading ? (
<Loading class={bem('loading')} size="20px" />
) : (
[
<span class={bem('name')}>{item.name}</span>,
item.subname && <span class={bem('subname')}>{item.subname}</span>
]
)}
</div>
);
const Footer = cancelText ? (
<div class={bem('cancel')} onClick={onCancel}>
{cancelText}
</div>
) : (
<div class={bem('content')}>{this.slots()}</div>
);
return (
<Popup
class={bem()}
value={this.value}
position="bottom"
onInput={value => {
this.$emit('input', value);
}}
>
{title ? Header() : this.actions.map(Option)}
{Footer}
</Popup>
);
}
});

View File

@ -0,0 +1,119 @@
import { use } from '../utils';
import { emit, inherit } from '../utils/functional';
import { PopupMixin } from '../mixins/popup';
import Icon from '../icon';
import Loading from '../loading';
import Popup from '../popup';
// Types
import { CreateElement, RenderContext } from 'vue/types';
import { DefaultSlots } from '../utils/use/sfc';
import { PopupMixinProps } from '../mixins/popup/type';
export type ActionsheetItem = {
name: string;
subname?: string;
loading?: boolean;
disabled?: boolean;
className?: string;
callback?: (item: ActionsheetItem) => void;
};
export type ActionsheetProps = PopupMixinProps & {
title?: string;
actions: ActionsheetItem[];
cancelText?: string;
};
const [sfc, bem] = use('actionsheet');
function Actionsheet(
h: CreateElement,
props: ActionsheetProps,
slots: DefaultSlots,
ctx: RenderContext<ActionsheetProps>
) {
const { title, cancelText } = props;
const onCancel = () => {
emit(ctx, 'input', false);
emit(ctx, 'cancel');
};
const Header = () => (
<div class={[bem('header'), 'van-hairline--top-bottom']}>
{title}
<Icon name="close" class={bem('close')} onClick={onCancel} />
</div>
);
const Option = (item: ActionsheetItem) => (
<div
class={[
bem('item', { disabled: item.disabled || item.loading }),
item.className,
'van-hairline--top'
]}
onClick={(event: Event) => {
event.stopPropagation();
if (!item.disabled && !item.loading) {
if (item.callback) {
item.callback(item);
}
emit(ctx, 'select', item);
}
}}
>
{item.loading ? (
<Loading class={bem('loading')} size="20px" />
) : (
[
<span class={bem('name')}>{item.name}</span>,
item.subname && <span class={bem('subname')}>{item.subname}</span>
]
)}
</div>
);
const Footer = cancelText ? (
<div class={bem('cancel')} onClick={onCancel}>
{cancelText}
</div>
) : (
<div class={bem('content')}>{slots.default && slots.default()}</div>
);
return (
<Popup
class={bem()}
value={props.value}
position="bottom"
onInput={(value: boolean) => {
emit(ctx, 'input', value);
}}
{...inherit(ctx)}
>
{title ? Header() : props.actions.map(Option)}
{Footer}
</Popup>
);
}
Actionsheet.props = {
...PopupMixin.props,
title: String,
actions: Array,
cancelText: String,
overlay: {
type: Boolean,
default: true
},
closeOnClickOverlay: {
type: Boolean,
default: true
}
};
export default sfc<ActionsheetProps>(Actionsheet);

View File

@ -3,6 +3,10 @@ import Actionsheet from '..';
test('callback events', () => {
const callback = jest.fn();
const onInput = jest.fn();
const onCancel = jest.fn();
const onSelect = jest.fn();
const wrapper = mount(Actionsheet, {
propsData: {
value: true,
@ -11,6 +15,13 @@ test('callback events', () => {
{ name: 'Option', disabled: true }
],
cancelText: 'Cancel'
},
context: {
on: {
input: onInput,
cancel: onCancel,
select: onSelect
}
}
});
@ -20,9 +31,9 @@ test('callback events', () => {
wrapper.find('.van-actionsheet__cancel').trigger('click');
expect(callback.mock.calls.length).toBe(1);
expect(wrapper.emitted('cancel')).toBeTruthy();
expect(wrapper.emitted('input')[0][0]).toBeFalsy();
expect(wrapper.emitted('select')[0][0]).toBeTruthy();
expect(wrapper.emitted('select')[0][1]).toBeFalsy();
expect(onCancel.mock.calls.length).toBeTruthy();
expect(onInput.mock.calls[0][0]).toBeFalsy();
expect(onSelect.mock.calls[0][0]).toBeTruthy();
expect(onSelect.mock.calls[0][1]).toBeFalsy();
expect(wrapper).toMatchSnapshot();
});

View File

@ -0,0 +1,13 @@
export type GetContainer = (container: HTMLElement) => void;
export type PopupMixinProps = {
value: boolean;
zIndex: string | number;
overlay?: boolean;
lockScroll: boolean;
lazyRender: boolean;
overlayClass?: any;
overlayStyle?: object | object[];
getContainer?: string | GetContainer;
closeOnClickOverlay?: boolean;
};