1 | import React from 'react';
|
2 | import {shallow, mount} from 'enzyme';
|
3 | import VirtualizedList from 'react-virtualized/dist/commonjs/List';
|
4 |
|
5 | import getUID from '../global/get-uid';
|
6 | import Icon, {CheckmarkIcon} from '../icon';
|
7 |
|
8 | import List from './list';
|
9 | import ListItem from './list__item';
|
10 | import ListCustom from './list__custom';
|
11 | import ListLink from './list__link';
|
12 | import ListTitle from './list__title';
|
13 | import ListSeparator from './list__separator';
|
14 | import styles from './list.css';
|
15 |
|
16 | describe('List', () => {
|
17 | const Type = List.ListProps.Type;
|
18 |
|
19 |
|
20 | const shallowList = props => shallow(<List {...props}/>);
|
21 | const mountList = props => mount(<List {...props}/>);
|
22 |
|
23 | describe('virtualized', () => {
|
24 | function createItemMock(itemType) {
|
25 | return {
|
26 | rgItemType: itemType,
|
27 | label: getUID('list-test-')
|
28 | };
|
29 | }
|
30 |
|
31 | it('should pad the list with top/bottom margins', () => {
|
32 | const data = [
|
33 | createItemMock(List.ListProps.Type.ITEM),
|
34 | createItemMock(List.ListProps.Type.ITEM)
|
35 | ];
|
36 |
|
37 | const instance = shallowList({data}).instance();
|
38 |
|
39 | shallow(
|
40 | instance.renderItem({index: 0}),
|
41 | {disableLifecycleMethods: true}
|
42 | ).should.have.tagName('div');
|
43 | shallow(
|
44 | instance.renderItem({index: 3}),
|
45 | {disableLifecycleMethods: true}
|
46 | ).should.have.tagName('div');
|
47 | });
|
48 |
|
49 | it('should apply styles from virtualized', () => {
|
50 | const data = [
|
51 | createItemMock(List.ListProps.Type.ITEM),
|
52 | createItemMock(List.ListProps.Type.ITEM)
|
53 | ];
|
54 |
|
55 | const instance = shallowList({data}).instance();
|
56 | const style = {
|
57 | top: -1000
|
58 | };
|
59 | const parent = {};
|
60 |
|
61 | shallow(
|
62 | instance.renderItem({index: 0, style, parent}),
|
63 | {disableLifecycleMethods: true}
|
64 | ).should.have.style('top', '-1000px');
|
65 | shallow(
|
66 | instance.renderItem({index: 1, style, parent}),
|
67 | {disableLifecycleMethods: true}
|
68 | ).should.have.style('top', '-1000px');
|
69 | shallow(
|
70 | instance.renderItem({index: 2, style, parent}),
|
71 | {disableLifecycleMethods: true}
|
72 | ).should.have.style('top', '-1000px');
|
73 | shallow(
|
74 | instance.renderItem({index: 3, style, parent}),
|
75 | {disableLifecycleMethods: true}
|
76 | ).should.have.style('top', '-1000px');
|
77 | });
|
78 |
|
79 | it('should scroll to the active item', () => {
|
80 | const data = [
|
81 | createItemMock(List.ListProps.Type.ITEM),
|
82 | createItemMock(List.ListProps.Type.ITEM),
|
83 | createItemMock(List.ListProps.Type.ITEM),
|
84 | createItemMock(List.ListProps.Type.ITEM),
|
85 | createItemMock(List.ListProps.Type.ITEM),
|
86 | createItemMock(List.ListProps.Type.ITEM)
|
87 | ];
|
88 |
|
89 | const activeIndex = 1;
|
90 |
|
91 | const wrapper = mountList({data});
|
92 | wrapper.setState({
|
93 | activeIndex,
|
94 | needScrollToActive: true
|
95 | });
|
96 |
|
97 | wrapper.find(VirtualizedList).should.have.prop('scrollToIndex', 2);
|
98 | });
|
99 |
|
100 | it('should\'n scroll to the active item when needScrollToActive is false', () => {
|
101 | const data = [
|
102 | createItemMock(List.ListProps.Type.ITEM),
|
103 | createItemMock(List.ListProps.Type.ITEM),
|
104 | createItemMock(List.ListProps.Type.ITEM),
|
105 | createItemMock(List.ListProps.Type.ITEM),
|
106 | createItemMock(List.ListProps.Type.ITEM),
|
107 | createItemMock(List.ListProps.Type.ITEM)
|
108 | ];
|
109 |
|
110 | const activeIndex = 1;
|
111 |
|
112 | const wrapper = mountList({data});
|
113 | wrapper.setState({
|
114 | activeIndex,
|
115 | needScrollToActive: false
|
116 | });
|
117 |
|
118 | wrapper.find(VirtualizedList).should.not.have.prop('scrollToIndex', 2);
|
119 | });
|
120 | });
|
121 |
|
122 | it('should check type of item', () => {
|
123 | const itemMock = {
|
124 | rgItemType: Type.SEPARATOR
|
125 | };
|
126 |
|
127 | List.isItemType(Type.SEPARATOR, itemMock).should.been.equal(true);
|
128 | });
|
129 |
|
130 | it('by default item has type equal ITEM', () => {
|
131 | const itemMock = {};
|
132 |
|
133 | List.isItemType(Type.ITEM, itemMock).should.been.equal(true);
|
134 | List.isItemType(Type.SEPARATOR, itemMock).should.been.equal(false);
|
135 | });
|
136 |
|
137 | it('should deselect item', () => {
|
138 | const instance = shallowList({
|
139 | data: [
|
140 | {}
|
141 | ],
|
142 | activeIndex: 0
|
143 | }).instance();
|
144 |
|
145 | instance.clearSelected();
|
146 |
|
147 | should.not.exist(instance.getSelected());
|
148 | });
|
149 |
|
150 | describe('should track activeIndex', () => {
|
151 | let wrapper;
|
152 | let instance;
|
153 | beforeEach(() => {
|
154 | sandbox.stub(window, 'requestAnimationFrame').callsFake(cb => cb());
|
155 | wrapper = mountList({
|
156 | data: [{key: 0}, {key: 1}, {key: 2}],
|
157 | activeIndex: 0,
|
158 | restoreActiveIndex: true
|
159 | });
|
160 | instance = wrapper.instance();
|
161 | });
|
162 |
|
163 | it('should set activeIndex from props', () => {
|
164 | wrapper.should.have.state('activeIndex', 0);
|
165 | wrapper.state('activeItem').key.should.equal(0);
|
166 | });
|
167 |
|
168 | it('should activate item', () => {
|
169 | instance.hoverHandler(1)();
|
170 | wrapper.should.have.state('activeIndex', 1);
|
171 | wrapper.state('activeItem').key.should.equal(1);
|
172 | });
|
173 |
|
174 | it('should reset activeIndex when it\'s changed in props', () => {
|
175 | instance.hoverHandler(1)();
|
176 | const activeIndex = 2;
|
177 | wrapper.setProps({
|
178 | activeIndex
|
179 | });
|
180 | wrapper.should.have.state('activeIndex', activeIndex);
|
181 | wrapper.state('activeItem').key.should.equal(activeIndex);
|
182 | });
|
183 |
|
184 | it('shouldn\'t reset activeIndex when it isn\'t changed in props', () => {
|
185 | instance.hoverHandler(1)();
|
186 | wrapper.setProps({
|
187 | activeIndex: 0
|
188 | });
|
189 | wrapper.should.have.state('activeIndex', 1);
|
190 | wrapper.state('activeItem').key.should.equal(1);
|
191 | });
|
192 | });
|
193 |
|
194 | describe('should render items', () => {
|
195 | const mountFirstItem = instance =>
|
196 | mount(instance.renderItem({index: 1}));
|
197 |
|
198 | it('should render for empty element', () => {
|
199 | const instance = shallowList({
|
200 | data: [
|
201 | {}
|
202 | ]
|
203 | }).instance();
|
204 | const firstItemWrapper = mountFirstItem(instance).find(ListItem);
|
205 | firstItemWrapper.should.have.className(styles.action);
|
206 | firstItemWrapper.should.have.text('');
|
207 | });
|
208 |
|
209 | it('should render instance item if type is not defined', () => {
|
210 | const instance = shallowList({
|
211 | data: [
|
212 | {label: 'Hello!'}
|
213 | ]
|
214 | }).instance();
|
215 |
|
216 | mount(instance.renderItem({index: 1})).
|
217 | should.have.descendants('[data-test~="ring-list-item"]');
|
218 | });
|
219 |
|
220 | it('should render a if href defined', () => {
|
221 | const instance = shallowList({
|
222 | data: [
|
223 | {label: 'Hello!', href: 'http://www.jetbrains.com'}
|
224 | ]
|
225 | }).instance();
|
226 |
|
227 | const firstItemWrapper = mountFirstItem(instance).find(ListLink);
|
228 | firstItemWrapper.should.exist;
|
229 | firstItemWrapper.should.have.data('test', 'ring-link ring-list-link ring-list-item');
|
230 | firstItemWrapper.should.have.text('Hello!');
|
231 | firstItemWrapper.should.have.tagName('a');
|
232 | firstItemWrapper.should.have.attr('href', 'http://www.jetbrains.com');
|
233 | });
|
234 |
|
235 | it('should render a if url defined', () => {
|
236 | const instance = shallowList({
|
237 | data: [
|
238 | {label: 'Hello!', url: 'http://www.jetbrains.com'}
|
239 | ]
|
240 | }).instance();
|
241 |
|
242 | const firstItemWrapper = mountFirstItem(instance).find(ListLink);
|
243 | firstItemWrapper.should.exist;
|
244 | firstItemWrapper.should.have.data('test', 'ring-link ring-list-link ring-list-item');
|
245 | firstItemWrapper.should.have.text('Hello!');
|
246 | firstItemWrapper.should.have.tagName('a');
|
247 | firstItemWrapper.should.have.attr('href', 'http://www.jetbrains.com');
|
248 | });
|
249 |
|
250 | it('should render separator', () => {
|
251 | const instance = shallowList({
|
252 | data: [
|
253 | {rgItemType: List.ListProps.Type.SEPARATOR}
|
254 | ]
|
255 | }).instance();
|
256 |
|
257 | const firstItemWrapper = mountFirstItem(instance).find(ListSeparator);
|
258 | firstItemWrapper.should.exist;
|
259 | firstItemWrapper.should.have.className(styles.separator);
|
260 | });
|
261 |
|
262 | it('should render title', () => {
|
263 | const instance = shallowList({
|
264 | data: [
|
265 | {rgItemType: List.ListProps.Type.TITLE, label: 'Foo', description: 'Bar'}
|
266 | ]
|
267 | }).instance();
|
268 |
|
269 | const firstItemWrapper = mountFirstItem(instance).find(ListTitle);
|
270 | firstItemWrapper.should.exist;
|
271 | firstItemWrapper.should.have.text('FooBar');
|
272 | });
|
273 |
|
274 | it('should render pseudo link if link without href', () => {
|
275 | const instance = shallowList({
|
276 | data: [
|
277 | {label: 'Hello!', rgItemType: List.ListProps.Type.LINK}
|
278 | ]
|
279 | }).instance();
|
280 |
|
281 | const firstItemWrapper = mountFirstItem(instance).find(ListLink);
|
282 | firstItemWrapper.should.exist;
|
283 | firstItemWrapper.should.have.data('test', 'ring-link ring-list-link ring-list-item');
|
284 | firstItemWrapper.should.have.text('Hello!');
|
285 | firstItemWrapper.should.have.tagName('button');
|
286 | });
|
287 |
|
288 | it('should not render icon if not provided', () => {
|
289 | const instance = shallowList({
|
290 | data: [
|
291 | {label: 'Hello!', type: List.ListProps.Type.ITEM}
|
292 | ]
|
293 | }).instance();
|
294 |
|
295 | const firstItemWrapper = mountFirstItem(instance).find(ListItem);
|
296 | firstItemWrapper.should.not.have.descendants(`.${styles.icon}`);
|
297 | });
|
298 |
|
299 | it('should render icon if provided', () => {
|
300 | const instance = shallowList({
|
301 | data: [
|
302 | {label: 'Hello!', icon: 'http://some.url/', type: List.ListProps.Type.ITEM}
|
303 | ]
|
304 | }).instance();
|
305 |
|
306 | const icon = mountFirstItem(instance).find(`.${styles.icon}`);
|
307 | icon.prop('style').backgroundImage.should.contain('http://some.url');
|
308 | });
|
309 |
|
310 | it('should not render glyph if not provided', () => {
|
311 | const instance = shallowList({
|
312 | data: [
|
313 | {label: 'Hello!', type: List.ListProps.Type.ITEM}
|
314 | ]
|
315 | }).instance();
|
316 |
|
317 | mountFirstItem(instance).find('use').should.be.empty;
|
318 | });
|
319 |
|
320 | it('should render glyph if provided', () => {
|
321 | const instance = shallowList({
|
322 | data: [
|
323 | {label: 'Hello!', glyph: CheckmarkIcon, type: List.ListProps.Type.ITEM}
|
324 | ]
|
325 | }).instance();
|
326 |
|
327 | mountFirstItem(instance).find(Icon).should.have.prop('glyph', CheckmarkIcon);
|
328 | });
|
329 |
|
330 | it('should throw error on unknown type', () => {
|
331 | (() => {
|
332 | const instance = shallowList({
|
333 | data: [
|
334 | {label: 'Hello!', rgItemType: 'none'}
|
335 | ]
|
336 | }).instance();
|
337 |
|
338 | mountFirstItem(instance);
|
339 | }).should.throw(Error, 'Unknown menu element type: none');
|
340 | });
|
341 |
|
342 | it('should handle click', () => {
|
343 | const clicked = sandbox.stub();
|
344 |
|
345 | const instance = mountList({
|
346 | data: [
|
347 | {label: 'Hello!', onClick: clicked}
|
348 | ]
|
349 | }).instance();
|
350 |
|
351 | const firstItemWrapper = mountFirstItem(instance).find(ListItem);
|
352 | firstItemWrapper.simulate('click');
|
353 | clicked.should.have.been.called;
|
354 | });
|
355 |
|
356 | it('should handle select', () => {
|
357 | const onSelect = sandbox.stub();
|
358 |
|
359 | const instance = mountList({
|
360 | onSelect,
|
361 | data: [{label: 'Hello!'}]
|
362 | }).instance();
|
363 |
|
364 | const firstItemWrapper = mountFirstItem(instance).find(ListItem);
|
365 | firstItemWrapper.simulate('click');
|
366 | onSelect.should.have.been.called;
|
367 | });
|
368 |
|
369 | it('Should support custom elements', () => {
|
370 | const instance = shallowList({
|
371 | data: [
|
372 | {
|
373 | template: React.createElement('span', {}, 'custom item'),
|
374 | rgItemType: List.ListProps.Type.CUSTOM
|
375 | }
|
376 | ]
|
377 | }).instance();
|
378 |
|
379 | const firstItemWrapper = mountFirstItem(instance).find(ListCustom);
|
380 | firstItemWrapper.should.have.text('custom item');
|
381 | });
|
382 |
|
383 | it('Should support click on custom elements', () => {
|
384 | const onClick = sandbox.stub();
|
385 | const instance = mountList({
|
386 | data: [
|
387 | {
|
388 | template: React.createElement('span', {}, 'custom item'),
|
389 | rgItemType: List.ListProps.Type.CUSTOM,
|
390 | onClick
|
391 | }
|
392 | ]
|
393 | }).instance();
|
394 |
|
395 | const firstItemWrapper = mountFirstItem(instance).find(ListCustom);
|
396 | firstItemWrapper.simulate('click');
|
397 | onClick.should.have.been.called;
|
398 | });
|
399 |
|
400 | it('Should support disable property for custom elements', () => {
|
401 | const instance = shallowList({
|
402 | data: [
|
403 | {
|
404 | template: React.createElement('span', {}, 'custom item'),
|
405 | rgItemType: List.ListProps.Type.CUSTOM,
|
406 | disabled: true
|
407 | }
|
408 | ]
|
409 | }).instance();
|
410 |
|
411 | const firstItemWrapper = mountFirstItem(instance).find(ListCustom);
|
412 | firstItemWrapper.should.not.have.className(styles.action);
|
413 | });
|
414 | });
|
415 | });
|