@king-design/intact 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/dropdown/dropdown.ts +34 -70
- package/components/dropdown/index.spec.ts +53 -4
- package/components/dropdown/item.ts +15 -5
- package/components/dropdown/menu.ts +3 -3
- package/components/dropdown/usePosition.ts +3 -0
- package/components/menu/demos/collapse.md +1 -1
- package/components/menu/index.spec.ts +9 -1
- package/components/menu/item.ts +7 -0
- package/components/pagination/index.spec.ts +23 -0
- package/components/pagination/index.ts +3 -1
- package/components/tooltip/content.ts +15 -1
- package/components/tooltip/content.vdt +6 -1
- package/components/tooltip/index.spec.ts +57 -1
- package/components/tooltip/styles.ts +1 -1
- package/components/tooltip/tooltip.ts +8 -0
- package/components/virtual.ts +98 -0
- package/es/components/dropdown/dropdown.d.ts +6 -5
- package/es/components/dropdown/dropdown.js +40 -68
- package/es/components/dropdown/index.spec.js +96 -17
- package/es/components/dropdown/item.d.ts +1 -1
- package/es/components/dropdown/item.js +16 -4
- package/es/components/dropdown/usePosition.js +4 -2
- package/es/components/menu/index.spec.js +26 -15
- package/es/components/menu/item.d.ts +2 -0
- package/es/components/menu/item.js +5 -0
- package/es/components/pagination/index.js +4 -1
- package/es/components/pagination/index.spec.js +49 -0
- package/es/components/tooltip/content.d.ts +1 -0
- package/es/components/tooltip/content.js +18 -1
- package/es/components/tooltip/content.vdt.js +3 -1
- package/es/components/tooltip/index.spec.js +108 -0
- package/es/components/tooltip/styles.d.ts +22 -0
- package/es/components/tooltip/styles.js +1 -1
- package/es/components/tooltip/tooltip.d.ts +1 -0
- package/es/components/tooltip/tooltip.js +11 -0
- package/es/components/virtual.d.ts +8 -0
- package/es/components/virtual.js +126 -0
- package/es/index.d.ts +2 -2
- package/es/index.js +2 -2
- package/es/site/data/components/dialog/demos/basic/react.js +1 -4
- package/es/site/data/components/menu/demos/collapse/index.js +1 -1
- package/es/site/data/components/menu/demos/collapse/react.js +1 -1
- package/index.ts +2 -2
- package/package.json +4 -3
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createVNode,
|
|
12
12
|
nextTick,
|
|
13
13
|
} from 'intact';
|
|
14
|
-
import {bind, isTextChildren} from '../utils';
|
|
14
|
+
import {bind, isTextChildren, getRestProps} from '../utils';
|
|
15
15
|
import {EMPTY_OBJ, isFunction, noop} from 'intact-shared';
|
|
16
16
|
import {Options, position, Feedback} from '../position';
|
|
17
17
|
import {cx} from '@emotion/css';
|
|
@@ -21,6 +21,7 @@ import {Portal, PortalProps} from '../portal';
|
|
|
21
21
|
import {useShowHideEvents} from '../../hooks/useShowHideEvents';
|
|
22
22
|
import {usePosition, FeedbackCallback} from './usePosition';
|
|
23
23
|
import type {Events} from '../types';
|
|
24
|
+
import { Virtual } from '../virtual';
|
|
24
25
|
|
|
25
26
|
export type Position = Options
|
|
26
27
|
export type PositionShorthand = 'left' | 'bottom' | 'right' | 'top'
|
|
@@ -35,6 +36,7 @@ export interface DropdownProps {
|
|
|
35
36
|
disabled?: boolean
|
|
36
37
|
value?: boolean
|
|
37
38
|
position?: Position | 'left' | 'bottom' | 'right' | 'top'
|
|
39
|
+
collison?: Position['collision']
|
|
38
40
|
of?: 'self' | 'parent' | Event
|
|
39
41
|
container?: PortalProps['container']
|
|
40
42
|
}
|
|
@@ -45,6 +47,8 @@ export interface DropdownEvents {
|
|
|
45
47
|
hide: []
|
|
46
48
|
mouseenter: [MouseEvent]
|
|
47
49
|
mouseleave: [MouseEvent]
|
|
50
|
+
click: [MouseEvent]
|
|
51
|
+
contextmenu: [MouseEvent]
|
|
48
52
|
positioned: [Feedback]
|
|
49
53
|
}
|
|
50
54
|
|
|
@@ -58,6 +62,7 @@ const typeDefs: Required<TypeDefs<DropdownProps>> = {
|
|
|
58
62
|
// Event is undefined in NodeJs
|
|
59
63
|
of: ['self', 'parent', typeof Event === 'undefined' ? undefined : Event],
|
|
60
64
|
container: [String, Function],
|
|
65
|
+
collison: ['none', 'fit', 'flip', 'flipfit', Array],
|
|
61
66
|
};
|
|
62
67
|
|
|
63
68
|
const defaults = (): Partial<DropdownProps> => ({
|
|
@@ -71,6 +76,8 @@ const events: Events<DropdownEvents> = {
|
|
|
71
76
|
hide: true,
|
|
72
77
|
mouseenter: true,
|
|
73
78
|
mouseleave: true,
|
|
79
|
+
click: true,
|
|
80
|
+
contextmenu: true,
|
|
74
81
|
positioned: true,
|
|
75
82
|
};
|
|
76
83
|
|
|
@@ -104,28 +111,18 @@ export class Dropdown<
|
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
const [trigger, menu] = children as DropdownChildren;
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const clonedTrigger = isTextChildren(trigger) ?
|
|
111
|
-
createVNode('span', null, trigger) :
|
|
112
|
-
directClone(trigger);
|
|
113
|
-
const triggerProps = this.triggerProps = this.normalizeTriggerProps(trigger.props || EMPTY_OBJ);
|
|
114
|
-
// add a className for opening status
|
|
115
|
-
let className = trigger.className || triggerProps.className;
|
|
114
|
+
const props = this.initEventCallbacks();
|
|
115
|
+
let {className, value, container} = this.get();
|
|
116
116
|
className = cx({
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
'k-dropdown-open': value,
|
|
118
|
+
[className!]: !!className,
|
|
119
119
|
});
|
|
120
|
-
|
|
121
|
-
clonedTrigger.props = {...triggerProps, ...props, className};
|
|
122
|
-
clonedTrigger.className = className;
|
|
123
|
-
|
|
120
|
+
|
|
124
121
|
this.menuVNode = menu;
|
|
125
122
|
|
|
126
123
|
return [
|
|
127
|
-
|
|
128
|
-
h(Portal, {children: menu, container
|
|
124
|
+
h(Virtual, {...props, ...getRestProps(this), className}, trigger),
|
|
125
|
+
h(Portal, {children: menu, container})
|
|
129
126
|
];
|
|
130
127
|
};
|
|
131
128
|
|
|
@@ -135,8 +132,7 @@ export class Dropdown<
|
|
|
135
132
|
public showedDropdown: Dropdown | null = null;
|
|
136
133
|
public positionHook = usePosition();
|
|
137
134
|
|
|
138
|
-
|
|
139
|
-
private triggerProps: any = null;
|
|
135
|
+
protected timer: number | undefined = undefined;
|
|
140
136
|
|
|
141
137
|
init() {
|
|
142
138
|
provide(DROPDOWN, this);
|
|
@@ -167,6 +163,12 @@ export class Dropdown<
|
|
|
167
163
|
clearTimeout(this.timer);
|
|
168
164
|
this.set('value', true);
|
|
169
165
|
|
|
166
|
+
// should show parent dropdown
|
|
167
|
+
const parentDropdown = this.dropdown;
|
|
168
|
+
if (parentDropdown) {
|
|
169
|
+
parentDropdown.show();
|
|
170
|
+
}
|
|
171
|
+
|
|
170
172
|
if (shouldFocus) {
|
|
171
173
|
nextTick(() => {
|
|
172
174
|
this.focusFirst();
|
|
@@ -178,9 +180,15 @@ export class Dropdown<
|
|
|
178
180
|
if (this.get('disabled')) return;
|
|
179
181
|
if (!this.get('value')) return;
|
|
180
182
|
|
|
183
|
+
const showedDropdown = this.showedDropdown;
|
|
184
|
+
if (showedDropdown) {
|
|
185
|
+
showedDropdown.hide(immediately);
|
|
186
|
+
}
|
|
187
|
+
|
|
181
188
|
if (immediately) {
|
|
182
189
|
this.set('value', false);
|
|
183
190
|
} else {
|
|
191
|
+
clearTimeout(this.timer);
|
|
184
192
|
this.timer = window.setTimeout(() => {
|
|
185
193
|
this.set('value', false);
|
|
186
194
|
}, 200);
|
|
@@ -198,29 +206,27 @@ export class Dropdown<
|
|
|
198
206
|
|
|
199
207
|
@bind
|
|
200
208
|
private onEnter(e: MouseEvent) {
|
|
201
|
-
this.callOriginalCallback(e.type === 'click' ? 'ev-click' : 'ev-mouseenter', e);
|
|
202
|
-
|
|
203
209
|
this.show();
|
|
210
|
+
this.trigger(e.type as 'mouseenter', e);
|
|
204
211
|
}
|
|
205
212
|
|
|
206
213
|
@bind
|
|
207
214
|
private onContextMenu(e: MouseEvent) {
|
|
208
|
-
this.callOriginalCallback('ev-contextmenu', e);
|
|
209
|
-
|
|
210
215
|
e.preventDefault();
|
|
211
216
|
this.set('of', e);
|
|
212
217
|
this.show();
|
|
218
|
+
this.trigger('contextmenu', e);
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
@bind
|
|
216
222
|
private onLeave(e: MouseEvent) {
|
|
217
|
-
this.callOriginalCallback('ev-mouseleave', e);
|
|
218
|
-
|
|
219
223
|
this.hide();
|
|
224
|
+
this.trigger(e.type as 'mouseleave', e);
|
|
220
225
|
}
|
|
221
226
|
|
|
222
|
-
private initEventCallbacks(
|
|
223
|
-
const
|
|
227
|
+
private initEventCallbacks() {
|
|
228
|
+
const trigger = this.get('trigger');
|
|
229
|
+
const props: Record<string, Function | string> = {};
|
|
224
230
|
|
|
225
231
|
switch (trigger) {
|
|
226
232
|
case 'focus':
|
|
@@ -241,48 +247,6 @@ export class Dropdown<
|
|
|
241
247
|
|
|
242
248
|
return props;
|
|
243
249
|
}
|
|
244
|
-
|
|
245
|
-
private callOriginalCallback(name: string, e: MouseEvent) {
|
|
246
|
-
const callback = this.triggerProps[name];
|
|
247
|
-
const callbackOnDropdown = this.get<Function>(name);
|
|
248
|
-
if (isFunction(callback)) callback(e);
|
|
249
|
-
if (isFunction(callbackOnDropdown)) callbackOnDropdown(e);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
private normalizeTriggerProps(props: any) {
|
|
253
|
-
// if use kpc in react or vue, normalize props by Wrapper.props.vnode;
|
|
254
|
-
const vnode = props.vnode;
|
|
255
|
-
if (!vnode) return props;
|
|
256
|
-
|
|
257
|
-
// maybe we render the intact component in react slot property, in this case
|
|
258
|
-
// the $isReact is false. so use the vnode $$typeof field as gauge
|
|
259
|
-
if (vnode.$$typeof || (this as any).$isVueNext) {
|
|
260
|
-
const _props = vnode.props;
|
|
261
|
-
if (!_props) return props;
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
vnode,
|
|
265
|
-
'ev-click': _props.onClick,
|
|
266
|
-
'ev-mouseenter': _props.onMouseEnter,
|
|
267
|
-
'ev-mouseleave': _props.onMouseLeave,
|
|
268
|
-
'ev-contextmenu': _props.onContextMenu,
|
|
269
|
-
className: _props.className || _props.class /* vue-next */,
|
|
270
|
-
};
|
|
271
|
-
} else if ((this as any).$isVue) {
|
|
272
|
-
const data = vnode.data;
|
|
273
|
-
const on = data && data.on || EMPTY_OBJ;
|
|
274
|
-
const ret: Record<string, any> = {vnode};
|
|
275
|
-
['click', 'mouseenter', 'mouseleave', 'contextmenu'].forEach(event => {
|
|
276
|
-
const method = on[event];
|
|
277
|
-
if (method) {
|
|
278
|
-
ret[`ev-${event}`] = method;
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
return ret;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return props;
|
|
285
|
-
}
|
|
286
250
|
}
|
|
287
251
|
|
|
288
252
|
function useDocumentClickForDropdown(dropdown: Dropdown) {
|
|
@@ -4,6 +4,7 @@ import {Dropdown, DropdownMenu, DropdownItem} from '../dropdown';
|
|
|
4
4
|
import BasicDemo from '~/components/dropdown/demos/basic';
|
|
5
5
|
import NestedDemo from '~/components/dropdown/demos/nested';
|
|
6
6
|
import ContextMenuDemo from '~/components/dropdown/demos/contextmenu';
|
|
7
|
+
import TooltipDemo from '~/components/dropdown/demos/tooltip';
|
|
7
8
|
|
|
8
9
|
describe('Dropdown', () => {
|
|
9
10
|
afterEach((done) => {
|
|
@@ -72,7 +73,8 @@ describe('Dropdown', () => {
|
|
|
72
73
|
expect(dropdown.parentNode).to.exist;
|
|
73
74
|
});
|
|
74
75
|
|
|
75
|
-
it('nested dropdown', async ()
|
|
76
|
+
it('nested dropdown', async function() {
|
|
77
|
+
this.timeout(0);
|
|
76
78
|
const [instance, element] = mount(NestedDemo);
|
|
77
79
|
|
|
78
80
|
(element.firstElementChild as HTMLElement).click();
|
|
@@ -89,6 +91,20 @@ describe('Dropdown', () => {
|
|
|
89
91
|
await wait(500);
|
|
90
92
|
const hoverSubDropdown = getElement('.k-dropdown-menu')!;
|
|
91
93
|
expect(hoverSubDropdown.innerHTML).to.matchSnapshot();
|
|
94
|
+
|
|
95
|
+
const [hoverItem1] = hoverSubDropdown.querySelectorAll<HTMLElement>('.k-dropdown-item');
|
|
96
|
+
dispatchEvent(hoverItem, 'mouseleave');
|
|
97
|
+
dispatchEvent(hoverItem1, 'mouseenter');
|
|
98
|
+
await wait(500);
|
|
99
|
+
const hoverSubDropdown1 = getElement('.k-dropdown-menu')!;
|
|
100
|
+
expect(hoverSubDropdown1.textContent).to.eql('item 1item 2');
|
|
101
|
+
|
|
102
|
+
const [hoverItem2] = hoverSubDropdown1.querySelectorAll<HTMLElement>('.k-dropdown-item');
|
|
103
|
+
dispatchEvent(hoverItem1, 'mouseleave');
|
|
104
|
+
dispatchEvent(hoverItem2, 'mouseenter');
|
|
105
|
+
await wait(1000);
|
|
106
|
+
const hoverSubDropdown2 = getElement('.k-dropdown-menu')!;
|
|
107
|
+
expect(hoverSubDropdown2 === hoverSubDropdown1).to.be.true;
|
|
92
108
|
});
|
|
93
109
|
|
|
94
110
|
it('hide on click document', async () => {
|
|
@@ -281,13 +297,13 @@ describe('Dropdown', () => {
|
|
|
281
297
|
expect(parent.scrollTop).to.eql(item.offsetHeight * 2 - 40);
|
|
282
298
|
});
|
|
283
299
|
|
|
284
|
-
it('trigger
|
|
300
|
+
it('focus trigger type', async () => {
|
|
285
301
|
class Demo extends Component {
|
|
286
302
|
static template = `
|
|
287
303
|
const {Dropdown, DropdownMenu, DropdownItem} = this;
|
|
288
304
|
<div>
|
|
289
305
|
<Dropdown trigger="focus">
|
|
290
|
-
<input ref="trigger" />
|
|
306
|
+
<input ref="trigger" ev-focusin={this.onFocus} />
|
|
291
307
|
<DropdownMenu>
|
|
292
308
|
<DropdownItem>test1</DropdownItem>
|
|
293
309
|
<DropdownItem>test2</DropdownItem>
|
|
@@ -298,12 +314,14 @@ describe('Dropdown', () => {
|
|
|
298
314
|
private Dropdown = Dropdown;
|
|
299
315
|
private DropdownItem = DropdownItem;
|
|
300
316
|
private DropdownMenu = DropdownMenu;
|
|
317
|
+
public onFocus = sinon.spy((e: MouseEvent) => console.log(e));
|
|
301
318
|
}
|
|
302
319
|
const [instance] = mount(Demo);
|
|
303
320
|
|
|
304
321
|
dispatchEvent(instance.refs.trigger, 'focusin');
|
|
305
322
|
await wait(500);
|
|
306
323
|
expect(getElement('.k-dropdown-menu')).to.be.exist;
|
|
324
|
+
expect(instance.onFocus.callCount).to.eql(1);
|
|
307
325
|
|
|
308
326
|
// clicking anywhere should not hide menu
|
|
309
327
|
dispatchEvent(document, 'click');
|
|
@@ -314,4 +332,35 @@ describe('Dropdown', () => {
|
|
|
314
332
|
await wait(700);
|
|
315
333
|
expect(getElement('.k-dropdown-menu')).to.not.be.exist;
|
|
316
334
|
});
|
|
317
|
-
|
|
335
|
+
|
|
336
|
+
it('wrap by tooltip', async () => {
|
|
337
|
+
const [instance, element] = mount(TooltipDemo);
|
|
338
|
+
|
|
339
|
+
dispatchEvent(element.firstChild as HTMLElement, 'mouseenter');
|
|
340
|
+
await wait();
|
|
341
|
+
const dropdown = getElement('.k-dropdown-menu')!;
|
|
342
|
+
const [item1, item2, item3, item4] = dropdown.querySelectorAll('.k-dropdown-item');
|
|
343
|
+
|
|
344
|
+
dispatchEvent(item1, 'mouseenter');
|
|
345
|
+
await wait();
|
|
346
|
+
expect(getElement('.k-tooltip-content')!.textContent).to.eql('item 1')
|
|
347
|
+
|
|
348
|
+
dispatchEvent(item1, 'mouseleave');
|
|
349
|
+
dispatchEvent(item3, 'mouseenter');
|
|
350
|
+
await wait();
|
|
351
|
+
expect(getElement('.k-tooltip-content')!.textContent).to.eql('disabled')
|
|
352
|
+
|
|
353
|
+
dispatchEvent(item3, 'mouseleave');
|
|
354
|
+
dispatchEvent(item4, 'mouseenter');
|
|
355
|
+
await wait();
|
|
356
|
+
expect(getElement('.k-tooltip-content')!.textContent).to.eql('This is a nested Dropdown.');
|
|
357
|
+
|
|
358
|
+
dispatchEvent(item4, 'click');
|
|
359
|
+
await wait();
|
|
360
|
+
expect(getElement('.k-dropdown-menu')!.textContent).to.eql('item 1item 2');
|
|
361
|
+
|
|
362
|
+
dispatchEvent(item4, 'mouseleave');
|
|
363
|
+
await wait(800);
|
|
364
|
+
expect(getElement('.k-dropdown-menu')).to.not.be.exist;
|
|
365
|
+
});
|
|
366
|
+
});
|
|
@@ -5,6 +5,7 @@ import {useItemKeyboard, MenuKeyboardMethods} from './useKeyboard';
|
|
|
5
5
|
import {Dropdown, DROPDOWN} from './dropdown';
|
|
6
6
|
import {DropdownMenu, DROPDOWN_MENU} from './menu';
|
|
7
7
|
import {IgnoreClickEvent} from '../../hooks/useDocumentClick';
|
|
8
|
+
import { Dropdown as ExportDropdown, DropdownMenu as ExportDropdownMenu } from '.';
|
|
8
9
|
|
|
9
10
|
export interface DropdownItemProps {
|
|
10
11
|
disabled?: boolean
|
|
@@ -64,10 +65,19 @@ export class DropdownItem extends Component<DropdownItemProps, DropdownItemEvent
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
hasSubMenu() {
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
// wrapped by Dropdown rather than DropdownMenu
|
|
69
|
+
let parent = this.$senior;
|
|
70
|
+
while (parent) {
|
|
71
|
+
// Tooltip extends Dropdown, it's also a instance of Dropdown
|
|
72
|
+
// so use constructor to detect
|
|
73
|
+
// if (parent instanceof DropdownMenu) {
|
|
74
|
+
if (parent.constructor === ExportDropdownMenu) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (parent.constructor === ExportDropdown) {
|
|
78
|
+
return parent;
|
|
79
|
+
}
|
|
80
|
+
parent = parent.$senior;
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
83
|
|
|
@@ -105,4 +115,4 @@ function useKeyboardForDropdownItem(instance: DropdownItem) {
|
|
|
105
115
|
instance.select();
|
|
106
116
|
},
|
|
107
117
|
});
|
|
108
|
-
}
|
|
118
|
+
}
|
|
@@ -35,8 +35,8 @@ export class DropdownMenu<
|
|
|
35
35
|
@bind
|
|
36
36
|
protected onMouseEnter(e: MouseEvent) {
|
|
37
37
|
const dropdown = this.dropdown!;
|
|
38
|
-
dropdown
|
|
39
|
-
dropdown
|
|
38
|
+
dropdown.show();
|
|
39
|
+
dropdown.trigger('mouseenter', e);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
@bind
|
|
@@ -78,4 +78,4 @@ function useKeyboardForDropdownMenu(dropdown: Dropdown) {
|
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
return {lock};
|
|
81
|
-
}
|
|
81
|
+
}
|
|
@@ -21,6 +21,8 @@ export function usePosition() {
|
|
|
21
21
|
// return if object is the same
|
|
22
22
|
if (
|
|
23
23
|
isObject(newValue) && isObject(oldValue) &&
|
|
24
|
+
// is not event object
|
|
25
|
+
!(newValue instanceof Event) &&
|
|
24
26
|
JSON.stringify(newValue) === JSON.stringify(oldValue)
|
|
25
27
|
) {
|
|
26
28
|
return;
|
|
@@ -56,6 +58,7 @@ export function usePosition() {
|
|
|
56
58
|
position(findDomFromVNode(instance.menuVNode!, true) as HTMLElement, {
|
|
57
59
|
my: 'left top+8',
|
|
58
60
|
at: 'left bottom',
|
|
61
|
+
collision: instance.get('collison'),
|
|
59
62
|
...pos,
|
|
60
63
|
of: ofElement,
|
|
61
64
|
using: _feedback => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {Component} from 'intact';
|
|
2
|
+
import BasicDemo from '~/components/menu/demos/basic';
|
|
2
3
|
import CollapseDemo from '~/components/menu/demos/collapse';
|
|
3
4
|
import AccordionDemo from '~/components/menu/demos/accordion';
|
|
4
5
|
import {mount, unmount, dispatchEvent, getElement, wait} from '../../test/utils';
|
|
@@ -8,13 +9,17 @@ describe('Menu', () => {
|
|
|
8
9
|
afterEach(() => unmount());
|
|
9
10
|
|
|
10
11
|
it('expand and shrink sub menu', async () => {
|
|
11
|
-
const [instance, element] = mount(
|
|
12
|
+
const [instance, element] = mount(BasicDemo);
|
|
13
|
+
|
|
14
|
+
await wait();
|
|
12
15
|
|
|
16
|
+
// shrink
|
|
13
17
|
const title = element.querySelector('.k-expanded .k-menu-title') as HTMLElement;
|
|
14
18
|
title.click();
|
|
15
19
|
await wait(500);
|
|
16
20
|
expect(element.outerHTML).to.matchSnapshot();
|
|
17
21
|
expect(instance.get('expandedKeys')).to.eql([]);
|
|
22
|
+
|
|
18
23
|
title.click();
|
|
19
24
|
await wait(500);
|
|
20
25
|
expect(element.outerHTML).to.matchSnapshot();
|
|
@@ -24,6 +29,9 @@ describe('Menu', () => {
|
|
|
24
29
|
it('select', async () => {
|
|
25
30
|
const [instance, element] = mount(CollapseDemo);
|
|
26
31
|
|
|
32
|
+
instance.set('collapse', false);
|
|
33
|
+
await wait();
|
|
34
|
+
|
|
27
35
|
expect(element.innerHTML).to.matchSnapshot();
|
|
28
36
|
|
|
29
37
|
const [title, disabledTitle] = element.querySelectorAll<HTMLElement>('.k-menu-title');
|
package/components/menu/item.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {useDropdown} from './useDropdown';
|
|
|
9
9
|
import {useRouter, navigate} from '../../hooks/useRouter';
|
|
10
10
|
import {useRecordItem} from '../../hooks/useRecordComponent';
|
|
11
11
|
import {MENU_RECORD_KEY, useHighlightItem} from './useHighlight';
|
|
12
|
+
import {Events} from '../types';
|
|
12
13
|
|
|
13
14
|
export interface MenuItemProps {
|
|
14
15
|
key: Key
|
|
@@ -32,11 +33,17 @@ const typeDefs: Required<TypeDefs<MenuItemProps>> = {
|
|
|
32
33
|
disabled: Boolean,
|
|
33
34
|
};
|
|
34
35
|
|
|
36
|
+
const events: Events<MenuItemEvents> = {
|
|
37
|
+
click: true,
|
|
38
|
+
select: true,
|
|
39
|
+
};
|
|
40
|
+
|
|
35
41
|
export const MENU_ITEM = 'MenuItem';
|
|
36
42
|
|
|
37
43
|
export class MenuItem extends Component<MenuItemProps, MenuItemEvents> {
|
|
38
44
|
static template = template;
|
|
39
45
|
static typeDefs = typeDefs;
|
|
46
|
+
static events = events;
|
|
40
47
|
|
|
41
48
|
public rootMenu = inject<Menu>(ROOT_MENU)!;
|
|
42
49
|
public parentMenu = inject<Menu>(MENU)!;
|
|
@@ -3,6 +3,8 @@ import GotoDemo from '~/components/pagination/demos/goto';
|
|
|
3
3
|
import CurrentDemo from '~/components/pagination/demos/current';
|
|
4
4
|
import DisableDemo from '~/components/pagination/demos/disable';
|
|
5
5
|
import {mount, unmount, dispatchEvent, wait} from '../../test/utils';
|
|
6
|
+
import { Component } from 'intact';
|
|
7
|
+
import { Pagination } from '.';
|
|
6
8
|
|
|
7
9
|
describe('Pagination', () => {
|
|
8
10
|
// afterEach(() => unmount());
|
|
@@ -89,4 +91,25 @@ describe('Pagination', () => {
|
|
|
89
91
|
expect(input.value).to.eql('10');
|
|
90
92
|
expect(instance.get('value2')).to.eql(10);
|
|
91
93
|
});
|
|
94
|
+
|
|
95
|
+
it('should not set value to 0 when total is 0 on intialization', async () => {
|
|
96
|
+
class Demo extends Component {
|
|
97
|
+
static template = `
|
|
98
|
+
const { Pagination } = this;
|
|
99
|
+
<Pagination total={0} v-model="page" />
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
static defaults() {
|
|
103
|
+
return {
|
|
104
|
+
page: 1,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private Pagination = Pagination;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const [instance] = mount(Demo);
|
|
112
|
+
// await wait();
|
|
113
|
+
expect(instance.get('page')).to.eql(1);
|
|
114
|
+
});
|
|
92
115
|
});
|
|
@@ -9,6 +9,7 @@ import template from './content.vdt';
|
|
|
9
9
|
import {bind} from '../utils';
|
|
10
10
|
import {Tooltip} from './tooltip';
|
|
11
11
|
import {useArrow} from './useArrow';
|
|
12
|
+
import { tooltip as tooltipTheme } from './styles';
|
|
12
13
|
|
|
13
14
|
export interface TooltipContentProps extends DropdownMenuProps { }
|
|
14
15
|
export interface TooltipContentEvents extends DropdownMenuEvents { }
|
|
@@ -35,10 +36,23 @@ export class TooltipContent extends DropdownMenu<
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
@bind
|
|
38
|
-
private onEnter() {
|
|
39
|
+
private onEnter(elem: HTMLElement) {
|
|
40
|
+
// fix the width, https://github.com/ksc-fe/kpc/issues/873
|
|
41
|
+
const maxWidth = parseInt(tooltipTheme.maxWidth);
|
|
42
|
+
const width = elem.offsetWidth;
|
|
43
|
+
if (width === maxWidth) {
|
|
44
|
+
elem.style.width = `${width}px`;
|
|
45
|
+
}
|
|
39
46
|
this.dropdown!.position();
|
|
40
47
|
}
|
|
41
48
|
|
|
49
|
+
@bind
|
|
50
|
+
private onAfterLeave(elem: HTMLElement) {
|
|
51
|
+
// remove the position after leave, https://github.com/ksc-fe/kpc/issues/873
|
|
52
|
+
const style = elem.style;
|
|
53
|
+
style.left = style.top = style.width = '';
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
@bind
|
|
43
57
|
protected onMouseEnter(e: MouseEvent) {
|
|
44
58
|
const dropdown = this.dropdown as Tooltip;
|
|
@@ -22,7 +22,12 @@ const classNameObj = {
|
|
|
22
22
|
|
|
23
23
|
<t:super
|
|
24
24
|
value={!!value && !this.isEmptyChildren}
|
|
25
|
-
transition={{
|
|
25
|
+
transition={{
|
|
26
|
+
name: 'k-fade',
|
|
27
|
+
onEnter: this.onEnter,
|
|
28
|
+
onAfterLeave: this.onAfterLeave,
|
|
29
|
+
onLeaveCancelled: this.onAfterLeave,
|
|
30
|
+
}}
|
|
26
31
|
class={classNameObj}
|
|
27
32
|
>
|
|
28
33
|
<b:children>
|
|
@@ -6,8 +6,9 @@ import ContentDemo from '~/components/tooltip/demos/content';
|
|
|
6
6
|
import ConfirmDemo from '~/components/tooltip/demos/confirm';
|
|
7
7
|
import AlwaysDemo from '~/components/tooltip/demos/always';
|
|
8
8
|
import {Tooltip} from './';
|
|
9
|
-
import {
|
|
9
|
+
import {Dialog} from '../dialog';
|
|
10
10
|
import {mount, unmount, dispatchEvent, getElement, wait} from '../../test/utils';
|
|
11
|
+
import { tooltip as tooltipTheme } from './styles';
|
|
11
12
|
|
|
12
13
|
describe('Tooltip', () => {
|
|
13
14
|
afterEach((done) => {
|
|
@@ -352,4 +353,59 @@ describe('Tooltip', () => {
|
|
|
352
353
|
console.log(JSON.stringify(content.getBoundingClientRect()));
|
|
353
354
|
expect(content.getBoundingClientRect().top < 0).to.be.true;
|
|
354
355
|
});
|
|
356
|
+
|
|
357
|
+
it('should add className', async () => {
|
|
358
|
+
class Demo extends Component {
|
|
359
|
+
static template = `
|
|
360
|
+
const Tooltip = this.Tooltip;
|
|
361
|
+
<div>
|
|
362
|
+
<Tooltip content="hello" class="a">
|
|
363
|
+
<div ref="test" class="b">test</div>
|
|
364
|
+
</Tooltip>
|
|
365
|
+
</div>
|
|
366
|
+
`;
|
|
367
|
+
Tooltip = Tooltip;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const [instance, element] = mount(Demo);
|
|
371
|
+
const trigger = instance.refs.test;
|
|
372
|
+
expect(trigger.className).to.eql('b a');
|
|
373
|
+
|
|
374
|
+
dispatchEvent(trigger, 'mouseenter');
|
|
375
|
+
await wait();
|
|
376
|
+
const dropdown = getElement('.k-tooltip-content')!;
|
|
377
|
+
expect(dropdown.classList.contains('a')).to.be.true;
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should fix the width in small container', async () => {
|
|
381
|
+
class Demo extends Component {
|
|
382
|
+
static template = `
|
|
383
|
+
const {Tooltip, Dialog} = this;
|
|
384
|
+
<div style="text-align: right; position: relative; width: 300px;">
|
|
385
|
+
<Tooltip content="这是一段很长的描述文字,这是一段很长的描述文字" container={dom => dom}>
|
|
386
|
+
<span class="trigger">test</span>
|
|
387
|
+
</Tooltip>
|
|
388
|
+
</div>
|
|
389
|
+
`
|
|
390
|
+
private Tooltip = Tooltip;
|
|
391
|
+
private Dialog = Dialog;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const [instance, element] = mount(Demo);
|
|
395
|
+
const trigger = element.querySelector<HTMLDivElement>('.trigger')!;
|
|
396
|
+
const expecedWidth = parseInt(tooltipTheme.maxWidth);
|
|
397
|
+
|
|
398
|
+
dispatchEvent(trigger, 'mouseenter');
|
|
399
|
+
await wait();
|
|
400
|
+
const content = getElement('.k-tooltip-content')!;
|
|
401
|
+
const width = content.offsetWidth;
|
|
402
|
+
expect(width).to.eql(expecedWidth);
|
|
403
|
+
|
|
404
|
+
dispatchEvent(trigger, 'mouseleave');
|
|
405
|
+
await wait();
|
|
406
|
+
dispatchEvent(trigger, 'mouseenter');
|
|
407
|
+
await wait();
|
|
408
|
+
const newWidth = content.offsetWidth;
|
|
409
|
+
expect(newWidth).to.eql(width);
|
|
410
|
+
});
|
|
355
411
|
});
|
|
@@ -49,6 +49,7 @@ const defaults = (): Partial<TooltipProps> => ({
|
|
|
49
49
|
showArrow: true,
|
|
50
50
|
theme: 'dark',
|
|
51
51
|
position: {my: 'center bottom-10', at: 'center top', collision: 'flipfit'},
|
|
52
|
+
collison: 'flipfit',
|
|
52
53
|
hoverable: false,
|
|
53
54
|
always: false,
|
|
54
55
|
confirm: false,
|
|
@@ -72,6 +73,13 @@ export class Tooltip<
|
|
|
72
73
|
static defaults = defaults;
|
|
73
74
|
static events = events;
|
|
74
75
|
|
|
76
|
+
show(shouldFocus: boolean = false) {
|
|
77
|
+
if (this.get('disabled')) return;
|
|
78
|
+
|
|
79
|
+
clearTimeout(this.timer);
|
|
80
|
+
this.set('value', true);
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
hide(immediately: boolean) {
|
|
76
84
|
if (this.get('always')) return;
|
|
77
85
|
|