@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.
Files changed (44) hide show
  1. package/components/dropdown/dropdown.ts +34 -70
  2. package/components/dropdown/index.spec.ts +53 -4
  3. package/components/dropdown/item.ts +15 -5
  4. package/components/dropdown/menu.ts +3 -3
  5. package/components/dropdown/usePosition.ts +3 -0
  6. package/components/menu/demos/collapse.md +1 -1
  7. package/components/menu/index.spec.ts +9 -1
  8. package/components/menu/item.ts +7 -0
  9. package/components/pagination/index.spec.ts +23 -0
  10. package/components/pagination/index.ts +3 -1
  11. package/components/tooltip/content.ts +15 -1
  12. package/components/tooltip/content.vdt +6 -1
  13. package/components/tooltip/index.spec.ts +57 -1
  14. package/components/tooltip/styles.ts +1 -1
  15. package/components/tooltip/tooltip.ts +8 -0
  16. package/components/virtual.ts +98 -0
  17. package/es/components/dropdown/dropdown.d.ts +6 -5
  18. package/es/components/dropdown/dropdown.js +40 -68
  19. package/es/components/dropdown/index.spec.js +96 -17
  20. package/es/components/dropdown/item.d.ts +1 -1
  21. package/es/components/dropdown/item.js +16 -4
  22. package/es/components/dropdown/usePosition.js +4 -2
  23. package/es/components/menu/index.spec.js +26 -15
  24. package/es/components/menu/item.d.ts +2 -0
  25. package/es/components/menu/item.js +5 -0
  26. package/es/components/pagination/index.js +4 -1
  27. package/es/components/pagination/index.spec.js +49 -0
  28. package/es/components/tooltip/content.d.ts +1 -0
  29. package/es/components/tooltip/content.js +18 -1
  30. package/es/components/tooltip/content.vdt.js +3 -1
  31. package/es/components/tooltip/index.spec.js +108 -0
  32. package/es/components/tooltip/styles.d.ts +22 -0
  33. package/es/components/tooltip/styles.js +1 -1
  34. package/es/components/tooltip/tooltip.d.ts +1 -0
  35. package/es/components/tooltip/tooltip.js +11 -0
  36. package/es/components/virtual.d.ts +8 -0
  37. package/es/components/virtual.js +126 -0
  38. package/es/index.d.ts +2 -2
  39. package/es/index.js +2 -2
  40. package/es/site/data/components/dialog/demos/basic/react.js +1 -4
  41. package/es/site/data/components/menu/demos/collapse/index.js +1 -1
  42. package/es/site/data/components/menu/demos/collapse/react.js +1 -1
  43. package/index.ts +2 -2
  44. 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 triggerType = this.get('trigger');
108
- const props = this.initEventCallbacks(triggerType);
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
- [className]: className,
118
- 'k-dropdown-open': this.get('value'),
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
- clonedTrigger,
128
- h(Portal, {children: menu, container: this.get('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
- private timer: number | undefined = undefined;
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(trigger: DropdownProps['trigger']) {
223
- const props: Record<string, Function> = {};
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: focus', async() => {
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
- // TODO: wrapped by Tooltip
68
- const parent = this.$senior;
69
- if (parent instanceof Dropdown) {
70
- return parent
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!.show();
39
- dropdown!.trigger('mouseenter', e);
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 => {
@@ -64,7 +64,7 @@ export default class extends Component<Props> {
64
64
  return {
65
65
  expandedKeys: ['3'],
66
66
  selectedKey: '3-2',
67
- collapse: false,
67
+ collapse: true,
68
68
  theme: 'dark'
69
69
  } as MenuProps;
70
70
  }
@@ -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(CollapseDemo);
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');
@@ -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
  });
@@ -100,7 +100,9 @@ export class Pagination extends Component<PaginationProps, PaginationEvents> {
100
100
 
101
101
  if (page > totalPages) {
102
102
  page = totalPages;
103
- } else if (page < 1) {
103
+ }
104
+ // perhaps totalPages is 0
105
+ if (page < 1) {
104
106
  page = 1;
105
107
  }
106
108
 
@@ -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={{name: 'k-fade', onEnter: this.onEnter}}
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 {Radio} from '../radio';
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
  });
@@ -31,7 +31,7 @@ const defaults = {
31
31
  smallPadding: '4px',
32
32
  };
33
33
 
34
- let tooltip: typeof defaults;
34
+ export let tooltip: typeof defaults;
35
35
  setDefault(() => {
36
36
  tooltip = deepDefaults(theme, {tooltip: defaults}).tooltip;
37
37
  });
@@ -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