learningtribes-studio-frontend 1.4.2-alpha.3 → 1.4.2-alpha.5

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.
@@ -2,10 +2,8 @@ import React from 'react';
2
2
 
3
3
  import AssetsList from './index';
4
4
  import { courseDetails, testAssetsList } from '../../utils/testConstants';
5
- import messages from './displayMessages';
6
- import { mountWithIntl } from '../../utils/i18n/enzymeHelper';
5
+ import { shallowWithIntl } from '../../utils/i18n/enzymeHelper';
7
6
  import { paginationInitial, selectInitial } from '../../data/reducers/assets';
8
- import WrappedMessage from '../../utils/i18n/formattedMessageWrapper';
9
7
 
10
8
  const paginationMetadata = {
11
9
  ...paginationInitial,
@@ -22,24 +20,25 @@ const defaultProps = {
22
20
 
23
21
  let items;
24
22
  let list;
25
- let wrapper = mountWithIntl(
23
+ let wrapper = shallowWithIntl(
26
24
  <AssetsList
27
25
  {...defaultProps}
28
26
  />);
29
27
 
30
28
  const selectAssetSpy = jest.fn();
31
29
 
32
- // use to re-find the following variables after a change is made that would affect them
30
+ const mouseDown = node => node.simulate('mouseDown', { preventDefault() {} });
31
+ const keyDown = (node, key) => node.simulate('keyDown', { key, preventDefault() {} });
32
+
33
33
  const updateConstants = () => {
34
34
  wrapper.update();
35
35
 
36
- items = wrapper.find('ol li');
37
- list = wrapper.find('ol');
36
+ items = wrapper.find('[role="option"]');
37
+ list = wrapper.find('[role="listbox"]');
38
38
  };
39
39
 
40
- // use to remount the AssetsList at the start of a test for a clean slate
41
40
  const resetWrapper = () => {
42
- wrapper = mountWithIntl(
41
+ wrapper = shallowWithIntl(
43
42
  <AssetsList
44
43
  {...defaultProps}
45
44
  />);
@@ -55,132 +54,93 @@ describe('AssetsList', () => {
55
54
  });
56
55
 
57
56
  describe('renders', () => {
58
- describe('a list header with', () => {
59
- const header = wrapper.find('.list-header');
60
-
61
- it('a list header', () => {
62
- expect(header).toHaveLength(1);
63
- });
57
+ it('no list header', () => {
58
+ expect(wrapper.find('.list-header')).toHaveLength(0);
59
+ });
64
60
 
65
- it('a Name label', () => {
66
- const nameLabelText = header.find('#name-header WrappedMessage');
67
- expect(nameLabelText).toHaveLength(1);
68
- expect(nameLabelText.prop('message')).toEqual(messages.assetsListNameLabel);
69
- });
61
+ it('no date-added labels', () => {
62
+ expect(wrapper.find('#date-added-header')).toHaveLength(0);
63
+ expect(wrapper.find('[id^="asset-date-"]')).toHaveLength(0);
64
+ });
70
65
 
71
- it('a Date Added label', () => {
72
- const dateAddedLabelText = header.find('#date-added-header WrappedMessage');
73
- expect(dateAddedLabelText).toHaveLength(1);
74
- expect(dateAddedLabelText.prop('message')).toEqual(messages.assetsListDateLabel);
75
- });
66
+ it('no ordered list markup', () => {
67
+ expect(wrapper.find('ol')).toHaveLength(0);
68
+ expect(wrapper.find('li')).toHaveLength(0);
76
69
  });
77
70
 
78
- describe('a list with', () => {
79
- it('an ordered list', () => {
71
+ describe('a grid with', () => {
72
+ it('one listbox', () => {
80
73
  expect(list).toHaveLength(1);
81
74
  });
82
75
 
83
- it('correct number of list items', () => {
84
- expect(items).toHaveLength(defaultProps.paginationMetadata.pageSize);
76
+ it('correct number of options', () => {
77
+ expect(items).toHaveLength(defaultProps.assetsList.length);
85
78
  });
86
79
 
87
80
  items.forEach((item, index) => {
88
- describe(`a list item for ${defaultProps.assetsList[index].display_name} with`, () => {
89
- describe('a correct thumbnail element with', () => {
90
- const thumbnailElement = item.find('span').first();
91
- const thumbnailElementContainer = thumbnailElement.find('div');
92
-
93
- it('a thumbnail element', () => {
94
- expect(thumbnailElement).toHaveLength(1);
95
- });
96
-
97
- it('an aria-hidden attribute', () => {
98
- expect(thumbnailElement.prop('aria-hidden')).toEqual(true);
99
- });
100
-
101
- it('a thumbnail element', () => {
102
- expect(thumbnailElement).toHaveLength(1);
103
- });
81
+ const asset = defaultProps.assetsList[index];
104
82
 
105
- it('a thumbnail element container', () => {
106
- expect(thumbnailElementContainer).toHaveLength(1);
107
- });
108
-
109
- it('an assets-list-image-preview-container className', () => {
110
- expect(thumbnailElementContainer.prop('className').includes('assets-list-image-preview-container')).toEqual(true);
111
- });
112
-
113
- const thumbnail = defaultProps.assetsList[index].thumbnail;
114
-
115
- if (thumbnail) {
116
- describe('an img element with', () => {
117
- const imagePreviewElement = thumbnailElementContainer.find('img');
118
-
119
- it('an img element', () => {
120
- expect(imagePreviewElement).toHaveLength(1);
121
- });
122
-
123
- it('an assets-list-image-preview-image className', () => {
124
- expect(imagePreviewElement.prop('className').includes('assets-list-image-preview-image')).toEqual(true);
125
- });
126
-
127
- it('an empty alt', () => {
128
- expect(imagePreviewElement.prop('alt')).toEqual('');
129
- });
130
-
131
- it('correct src when courseDetails.base_url is defined', () => {
132
- expect(imagePreviewElement.prop('src')).toEqual(`${defaultProps.courseDetails.base_url}${thumbnail}`);
133
- });
83
+ describe(`an option for ${asset.display_name} with`, () => {
84
+ it('a file type badge', () => {
85
+ const badge = item.find('.file-type-badge');
86
+ expect(badge).toHaveLength(1);
87
+ expect(badge.text()).toEqual(asset.display_name.split('.').pop());
88
+ });
134
89
 
135
- it('correct src when courseDetails.base_url is not defined', () => {
136
- const newCourseDetails = { ...defaultProps.courseDetails };
90
+ if (asset.thumbnail) {
91
+ describe('a thumbnail with', () => {
92
+ const imagePreviewElement = item.find('img.asset-thumbnail');
137
93
 
138
- newCourseDetails.base_url = '';
94
+ it('an img element', () => {
95
+ expect(imagePreviewElement).toHaveLength(1);
96
+ });
139
97
 
140
- wrapper.setProps({
141
- courseDetails: newCourseDetails,
142
- });
98
+ it('an empty alt', () => {
99
+ expect(imagePreviewElement.prop('alt')).toEqual('');
100
+ });
143
101
 
144
- expect(imagePreviewElement.prop('src')).toEqual(`${defaultProps.courseDetails.base_url}${thumbnail}`);
145
- });
102
+ it('uses lazy loading', () => {
103
+ expect(imagePreviewElement.prop('loading')).toEqual('lazy');
146
104
  });
147
- } else {
148
- describe('a noImagePreview element with', () => {
149
- const noImagePreviewElement = thumbnailElementContainer.find('[data-identifier="asset-image-thumbnail"]');
150
105
 
151
- it('a noImagePreview element', () => {
152
- expect(noImagePreviewElement).toHaveLength(1);
153
- });
106
+ it('correct src when courseDetails.base_url is defined', () => {
107
+ expect(imagePreviewElement.prop('src')).toEqual(`${defaultProps.courseDetails.base_url}${asset.thumbnail}`);
108
+ });
154
109
 
155
- it('a text-center className', () => {
156
- expect(noImagePreviewElement.prop('className').includes('text-center')).toEqual(true);
110
+ it('correct src when courseDetails.base_url is not defined', () => {
111
+ wrapper.setProps({
112
+ courseDetails: {
113
+ ...defaultProps.courseDetails,
114
+ base_url: '',
115
+ },
157
116
  });
117
+ updateConstants();
158
118
 
159
- it('correct text', () => {
160
- const noImagePreviewElementText = thumbnailElementContainer.find(WrappedMessage);
161
- expect(noImagePreviewElementText.prop('message')).toEqual(messages.assetsListNoPreview);
162
- });
119
+ expect(items.at(index).find('img.asset-thumbnail').prop('src')).toEqual(asset.thumbnail);
163
120
  });
164
- }
165
- });
121
+ });
122
+ } else {
123
+ it('an icon when there is no thumbnail', () => {
124
+ expect(item.find('img.asset-thumbnail')).toHaveLength(0);
125
+ expect(item.find('.asset-icon i.far.fa-image')).toHaveLength(1);
126
+ });
127
+ }
166
128
 
167
- it('a display name element', () => {
168
- const displayNameElement = item.find(`#asset-name-${index}`);
129
+ it('a display name', () => {
130
+ const displayNameElement = item.find('.asset-name');
169
131
  expect(displayNameElement).toHaveLength(1);
170
- expect(displayNameElement.text()).toEqual(defaultProps.assetsList[index].display_name);
132
+ expect(displayNameElement.text()).toEqual(asset.display_name);
171
133
  });
172
134
 
173
- it('a date added element', () => {
174
- const dateAddedElement = item.find(`#asset-date-${index}`);
175
- expect(dateAddedElement).toHaveLength(1);
176
- expect(dateAddedElement.text()).toEqual(defaultProps.assetsList[index].date_added);
135
+ it('no date added element', () => {
136
+ expect(item.text()).not.toEqual(expect.stringContaining(asset.date_added));
177
137
  });
178
138
  });
179
139
  });
180
140
  });
181
141
  });
182
142
 
183
- describe('AssetsList', () => {
143
+ describe('selection state', () => {
184
144
  it('this.state.selectedAssetIndex is -1 by default', () => {
185
145
  expect(wrapper.state('selectedAssetIndex')).toEqual(-1);
186
146
  });
@@ -202,10 +162,10 @@ describe('AssetsList', () => {
202
162
  expect(wrapper.state('selectedAssetIndex')).toEqual(-1);
203
163
  });
204
164
 
205
- it('this.state.selectedAssetIndex is reset to to original index on pageChange to selectedAssetPage', () => {
165
+ it('this.state.selectedAssetIndex is reset to original index on pageChange to selectedAssetPage', () => {
206
166
  const assetIndex = 1;
207
167
 
208
- items.at(assetIndex).simulate('mouseDown');
168
+ mouseDown(items.at(assetIndex));
209
169
 
210
170
  wrapper.setProps({
211
171
  selectedAsset: defaultProps.assetsList[assetIndex],
@@ -231,7 +191,6 @@ describe('AssetsList', () => {
231
191
 
232
192
  wrapper.setProps({
233
193
  paginationMetadata: newPaginationData,
234
-
235
194
  });
236
195
 
237
196
  expect(wrapper.state('selectedAssetIndex')).toEqual(assetIndex);
@@ -258,17 +217,31 @@ describe('AssetsList', () => {
258
217
  it('this.state.selectedAssetIndex is set by clicking on a listbox option', () => {
259
218
  const assetIndex = 1;
260
219
 
261
- items.at(assetIndex).simulate('mouseDown');
220
+ mouseDown(items.at(assetIndex));
262
221
 
263
222
  expect(wrapper.state('selectedAssetIndex')).toEqual(assetIndex);
264
223
  });
265
224
 
266
225
  it('this.state.selectedAssetPage is set by clicking on a listbox option', () => {
267
- items.at(1).simulate('mouseDown');
226
+ mouseDown(items.at(1));
268
227
 
269
228
  expect(wrapper.state('selectedAssetPage')).toEqual(0);
270
229
  });
271
230
 
231
+ it('scrolls the selected option into view', () => {
232
+ const scrollIntoView = jest.fn();
233
+ wrapper.instance().listRef = {
234
+ querySelector: () => ({ scrollIntoView }),
235
+ };
236
+
237
+ wrapper.setState({
238
+ selectedAssetIndex: 2,
239
+ });
240
+
241
+ expect(scrollIntoView).toHaveBeenCalledTimes(1);
242
+ expect(scrollIntoView).toHaveBeenCalledWith({ block: 'nearest', inline: 'nearest' });
243
+ });
244
+
272
245
  it('onAssetClick calls selectAsset prop', () => {
273
246
  const assetIndex = 1;
274
247
 
@@ -276,7 +249,7 @@ describe('AssetsList', () => {
276
249
  selectAsset: selectAssetSpy,
277
250
  });
278
251
 
279
- items.at(assetIndex).simulate('mouseDown');
252
+ mouseDown(items.at(assetIndex));
280
253
 
281
254
  expect(selectAssetSpy).toHaveBeenCalledTimes(1);
282
255
  expect(selectAssetSpy).toHaveBeenCalledWith(defaultProps.assetsList[assetIndex]);
@@ -286,8 +259,8 @@ describe('AssetsList', () => {
286
259
  });
287
260
 
288
261
  describe('List Box', () => {
289
- it('has list-group className', () => {
290
- expect(list.prop('className').includes('list-group')).toEqual(true);
262
+ it('has assets-grid className', () => {
263
+ expect(list.prop('className').includes('assets-grid')).toEqual(true);
291
264
  });
292
265
 
293
266
  it('has listbox role', () => {
@@ -368,31 +341,31 @@ describe('AssetsList', () => {
368
341
  });
369
342
 
370
343
  describe('has a key handler', () => {
371
- const lastSelectableIndex = defaultProps.paginationMetadata.pageSize - 1;
344
+ const lastSelectableIndex = defaultProps.assetsList.length - 1;
372
345
 
373
- describe('arrow down', () => {
374
- it('arrow down increments this.state.selectedAssetIndex if last asset is not selected', () => {
346
+ describe('arrow right', () => {
347
+ it('arrow right increments this.state.selectedAssetIndex if last asset is not selected', () => {
375
348
  expect(wrapper.state('selectedAssetIndex')).toEqual(-1);
376
349
 
377
- list.simulate('keyDown', { key: 'ArrowDown' });
350
+ keyDown(list, 'ArrowRight');
378
351
 
379
352
  expect(wrapper.state('selectedAssetIndex')).toEqual(0);
380
353
  });
381
354
 
382
- it('arrow down sets this.state.selectedAssetPage if last asset is selected', () => {
355
+ it('arrow right sets this.state.selectedAssetPage if last asset is not selected', () => {
383
356
  expect(wrapper.state('selectedAssetPage')).toEqual(-1);
384
357
 
385
- list.simulate('keyDown', { key: 'ArrowDown' });
358
+ keyDown(list, 'ArrowRight');
386
359
 
387
360
  expect(wrapper.state('selectedAssetPage')).toEqual(0);
388
361
  });
389
362
 
390
- it('arrow down calls selectAsset prop if last asset is not selected', () => {
363
+ it('arrow right calls selectAsset prop if last asset is not selected', () => {
391
364
  wrapper.setProps({
392
365
  selectAsset: selectAssetSpy,
393
366
  });
394
367
 
395
- list.simulate('keyDown', { key: 'ArrowDown' });
368
+ keyDown(list, 'ArrowRight');
396
369
 
397
370
  expect(selectAssetSpy).toHaveBeenCalledTimes(1);
398
371
  expect(selectAssetSpy).toHaveBeenCalledWith(defaultProps.assetsList[0]);
@@ -400,27 +373,27 @@ describe('AssetsList', () => {
400
373
  selectAssetSpy.mockClear();
401
374
  });
402
375
 
403
- it('arrow down does not increment this.state.selectedAssetIndex if last asset is selected', () => {
376
+ it('arrow right does not increment this.state.selectedAssetIndex if last asset is selected', () => {
404
377
  wrapper.setState({
405
378
  selectedAssetIndex: lastSelectableIndex,
406
379
  });
407
380
 
408
- list.simulate('keyDown', { key: 'ArrowDown' });
381
+ keyDown(list, 'ArrowRight');
409
382
 
410
383
  expect(wrapper.state('selectedAssetIndex')).toEqual(lastSelectableIndex);
411
384
  });
412
385
 
413
- it('arrow down does not change this.state.selectedAssetPage if last asset is selected', () => {
386
+ it('arrow right does not change this.state.selectedAssetPage if last asset is selected', () => {
414
387
  wrapper.setState({
415
388
  selectedAssetIndex: lastSelectableIndex,
416
389
  });
417
390
 
418
- list.simulate('keyDown', { key: 'ArrowDown' });
391
+ keyDown(list, 'ArrowRight');
419
392
 
420
393
  expect(wrapper.state('selectedAssetPage')).toEqual(-1);
421
394
  });
422
395
 
423
- it('arrow down does not call selectAsset prop if last asset is selected', () => {
396
+ it('arrow right does not call selectAsset prop if last asset is selected', () => {
424
397
  wrapper.setProps({
425
398
  selectAsset: selectAssetSpy,
426
399
  });
@@ -429,7 +402,7 @@ describe('AssetsList', () => {
429
402
  selectedAssetIndex: lastSelectableIndex,
430
403
  });
431
404
 
432
- list.simulate('keyDown', { key: 'ArrowDown' });
405
+ keyDown(list, 'ArrowRight');
433
406
 
434
407
  expect(selectAssetSpy).toHaveBeenCalledTimes(0);
435
408
 
@@ -437,28 +410,28 @@ describe('AssetsList', () => {
437
410
  });
438
411
  });
439
412
 
440
- describe('arrow up', () => {
413
+ describe('arrow left', () => {
441
414
  it('decrements this.state.selectedAssetIndex if first asset is not selected', () => {
442
415
  wrapper.setState({
443
416
  selectedAssetIndex: lastSelectableIndex,
444
417
  });
445
418
 
446
- list.simulate('keyDown', { key: 'ArrowUp' });
419
+ keyDown(list, 'ArrowLeft');
447
420
 
448
421
  expect(wrapper.state('selectedAssetIndex')).toEqual(lastSelectableIndex - 1);
449
422
  });
450
423
 
451
- it('arrow up sets this.state.selectedAssetPage if first asset is not selected', () => {
424
+ it('arrow left sets this.state.selectedAssetPage if first asset is not selected', () => {
452
425
  wrapper.setState({
453
426
  selectedAssetIndex: lastSelectableIndex,
454
427
  });
455
428
 
456
- list.simulate('keyDown', { key: 'ArrowUp' });
429
+ keyDown(list, 'ArrowLeft');
457
430
 
458
431
  expect(wrapper.state('selectedAssetPage')).toEqual(0);
459
432
  });
460
433
 
461
- it('arrow up calls selectAsset prop if first asset is not selected', () => {
434
+ it('arrow left calls selectAsset prop if first asset is not selected', () => {
462
435
  wrapper.setProps({
463
436
  selectAsset: selectAssetSpy,
464
437
  });
@@ -467,7 +440,7 @@ describe('AssetsList', () => {
467
440
  selectedAssetIndex: lastSelectableIndex,
468
441
  });
469
442
 
470
- list.simulate('keyDown', { key: 'ArrowUp' });
443
+ keyDown(list, 'ArrowLeft');
471
444
 
472
445
  expect(selectAssetSpy).toHaveBeenCalledTimes(1);
473
446
  expect(selectAssetSpy)
@@ -476,32 +449,36 @@ describe('AssetsList', () => {
476
449
  selectAssetSpy.mockClear();
477
450
  });
478
451
 
479
- it('arrow up does not decrement this.state.selectedAssetIndex if first asset is selected', () => {
452
+ it('arrow left does not decrement this.state.selectedAssetIndex if first asset is selected', () => {
480
453
  wrapper.setState({
481
454
  selectedAssetIndex: 0,
482
455
  });
483
456
 
484
- list.simulate('keyDown', { key: 'ArrowUp' });
457
+ keyDown(list, 'ArrowLeft');
485
458
 
486
459
  expect(wrapper.state('selectedAssetIndex')).toEqual(0);
487
460
  });
488
461
 
489
- it('arrow up does not change this.state.selectedAssetPage if first asset is selected', () => {
462
+ it('arrow left does not change this.state.selectedAssetPage if first asset is selected', () => {
490
463
  wrapper.setState({
491
464
  selectedAssetIndex: 0,
492
465
  });
493
466
 
494
- list.simulate('keyDown', { key: 'ArrowUp' });
467
+ keyDown(list, 'ArrowLeft');
495
468
 
496
469
  expect(wrapper.state('selectedAssetPage')).toEqual(-1);
497
470
  });
498
471
 
499
- it('arrow up does not call selectAsset prop if first asset is selected', () => {
472
+ it('arrow left does not call selectAsset prop if first asset is selected', () => {
500
473
  wrapper.setProps({
501
474
  selectAsset: selectAssetSpy,
502
475
  });
503
476
 
504
- list.simulate('keyDown', { key: 'ArrowUp' });
477
+ wrapper.setState({
478
+ selectedAssetIndex: 0,
479
+ });
480
+
481
+ keyDown(list, 'ArrowLeft');
505
482
 
506
483
  expect(selectAssetSpy).toHaveBeenCalledTimes(0);
507
484
 
@@ -515,8 +492,8 @@ describe('AssetsList', () => {
515
492
  items.forEach((item, index) => {
516
493
  describe(`list box option at index ${index} has correct static props`, () => {
517
494
  it('className', () => {
518
- expect(item.prop('className').includes('list-group-item list-group-item-action')).toEqual(true);
519
- expect(item.prop('className').includes('active')).toEqual(false);
495
+ expect(item.prop('className').includes('asset-grid-item')).toEqual(true);
496
+ expect(item.prop('className').includes('selected')).toEqual(false);
520
497
  });
521
498
 
522
499
  it('id', () => {
@@ -531,13 +508,17 @@ describe('AssetsList', () => {
531
508
  expect(item.prop('tabIndex')).toEqual('-1');
532
509
  });
533
510
 
534
- it('aria-labelledby', () => {
535
- expect(item.prop('aria-labelledby')).toEqual(`name-header asset-name-${index} date-added-header asset-date-${index}`);
511
+ it('aria-label', () => {
512
+ expect(item.prop('aria-label')).toEqual(defaultProps.assetsList[index].display_name);
513
+ });
514
+
515
+ it('no aria-labelledby', () => {
516
+ expect(item.prop('aria-labelledby')).toBeUndefined();
536
517
  });
537
518
  });
538
519
  });
539
520
 
540
- it('has active className when selected', () => {
521
+ it('has selected className when selected', () => {
541
522
  const selectedAssetIndex = 2;
542
523
  const selectedAsset = defaultProps.assetsList[selectedAssetIndex];
543
524
 
@@ -547,7 +528,7 @@ describe('AssetsList', () => {
547
528
 
548
529
  updateConstants();
549
530
 
550
- expect(items.at(selectedAssetIndex).prop('className').includes('active')).toEqual(true);
531
+ expect(items.at(selectedAssetIndex).prop('className').includes('selected')).toEqual(true);
551
532
  });
552
533
 
553
534
  it('has aria-selected attribute when selected', () => {
@@ -564,4 +545,3 @@ describe('AssetsList', () => {
564
545
  });
565
546
  });
566
547
  });
567
-
@@ -2,8 +2,6 @@ import classNames from 'classnames';
2
2
  import PropTypes from 'prop-types';
3
3
  import React from 'react';
4
4
 
5
- import WrappedMessage from '../../utils/i18n/formattedMessageWrapper';
6
- import messages from './displayMessages';
7
5
  import styles from './AssetsList.scss';
8
6
 
9
7
  const initialState = {
@@ -51,6 +49,12 @@ export default class AssetsList extends React.Component {
51
49
  }
52
50
  }
53
51
 
52
+ componentDidUpdate(prevProps, prevState) {
53
+ if (prevState.selectedAssetIndex !== this.state.selectedAssetIndex) {
54
+ this.scrollSelectedOptionIntoView();
55
+ }
56
+ }
57
+
54
58
  onAssetClick = (asset, index) => {
55
59
  this.setState({
56
60
  selectedAssetIndex: index,
@@ -118,6 +122,7 @@ export default class AssetsList extends React.Component {
118
122
  className={styles['asset-thumbnail']}
119
123
  src={previewSrc}
120
124
  alt=""
125
+ loading="lazy"
121
126
  />
122
127
  ) : (
123
128
  <div className={styles['asset-icon']}>
@@ -142,6 +147,18 @@ export default class AssetsList extends React.Component {
142
147
  getSelectedAssetIndex = (assetsList, selectedAsset) => (assetsList.findIndex(
143
148
  asset => asset.id === selectedAsset.id));
144
149
 
150
+ scrollSelectedOptionIntoView = () => {
151
+ if (!this.listRef || this.state.selectedAssetIndex < 0) {
152
+ return;
153
+ }
154
+
155
+ const option = this.listRef.querySelector(`#asset-list-option-${this.state.selectedAssetIndex}`);
156
+
157
+ if (option && typeof option.scrollIntoView === 'function') {
158
+ option.scrollIntoView({ block: 'nearest', inline: 'nearest' });
159
+ }
160
+ }
161
+
145
162
  handleKeyDown = (e) => {
146
163
  switch (e.key) {
147
164
  case 'ArrowRight': {
@@ -184,6 +201,7 @@ export default class AssetsList extends React.Component {
184
201
  className={styles['assets-grid']}
185
202
  onFocus={this.onListBoxFocus}
186
203
  onKeyDown={this.handleKeyDown}
204
+ ref={(node) => { this.listRef = node; }}
187
205
  role="listbox"
188
206
  tabIndex="0"
189
207
  >
@@ -1,5 +1,6 @@
1
1
  @import 'edx-bootstrap';
2
2
 
3
+ .edit-image-modal {
3
4
  // Remove negative margins in Bootstrap v4.0.0 so the body does not extend outside the modal
4
5
  .modal-body .row {
5
6
  margin: 0;
@@ -170,3 +171,4 @@
170
171
  color: #09092d;
171
172
  }
172
173
  }
174
+ }