lightning-base-components 1.21.2-alpha → 1.21.3-alpha

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 (222) hide show
  1. package/metadata/raptor.json +28 -1
  2. package/package.json +28 -2
  3. package/scopedImports/@salesforce-label-LightningRichTextEditor.colorPicker.js +1 -0
  4. package/src/lightning/accordion/accordion-section.slds.css +3 -3
  5. package/src/lightning/accordion/accordion.slds.css +1 -2
  6. package/src/lightning/accordionSection/accordion-section.slds.css +3 -3
  7. package/src/lightning/accordionSection/accordionSection.js +3 -1
  8. package/src/lightning/accordionSection/button.slds.css +1 -1
  9. package/src/lightning/badge/badge.js +1 -0
  10. package/src/lightning/badge/badge.js-meta.xml +3 -0
  11. package/src/lightning/baseCombobox/base-combobox.slds.css +11 -6
  12. package/src/lightning/baseCombobox/baseCombobox.html +1 -1
  13. package/src/lightning/baseCombobox/baseCombobox.js +2 -2
  14. package/src/lightning/baseCombobox/baseCombobox.js-meta.xml +6 -0
  15. package/src/lightning/baseCombobox/input-text.slds.css +41 -68
  16. package/src/lightning/baseCombobox/keyboard.js +12 -4
  17. package/src/lightning/baseCombobox/listbox.slds.css +51 -99
  18. package/src/lightning/baseCombobox/spinner.slds.css +62 -62
  19. package/src/lightning/baseComboboxFormattedText/baseComboboxFormattedText.js-meta.xml +6 -0
  20. package/src/lightning/baseComboboxItem/baseComboboxItem.js +10 -6
  21. package/src/lightning/baseComboboxItem/baseComboboxItem.js-meta.xml +6 -0
  22. package/src/lightning/baseComboboxItem/listbox.slds.css +51 -99
  23. package/src/lightning/baseFormattedText/baseFormattedText.js +2 -2
  24. package/src/lightning/button/button.js +2 -1
  25. package/src/lightning/button/button.slds.css +1 -1
  26. package/src/lightning/buttonIcon/button-icon.slds.css +1 -1
  27. package/src/lightning/buttonIconStateful/button-icon-stateful.slds.css +4 -2
  28. package/src/lightning/buttonIconStateful/button-icon.slds.css +1 -1
  29. package/src/lightning/buttonIconStateful/button.slds.css +1 -1
  30. package/src/lightning/buttonMenu/button-icon.slds.css +1 -1
  31. package/src/lightning/buttonMenu/button-menu.slds.css +8 -2
  32. package/src/lightning/buttonMenu/button.slds.css +1 -1
  33. package/src/lightning/buttonStateful/button-stateful.slds.css +6 -2
  34. package/src/lightning/buttonStateful/button.slds.css +1 -1
  35. package/src/lightning/buttonStateful/buttonStateful.js +4 -1
  36. package/src/lightning/calendar/calendar.js-meta.xml +6 -0
  37. package/src/lightning/calendar/calendar.slds.css +9 -2
  38. package/src/lightning/colorPickerCustom/color-picker-custom.slds.css +22 -23
  39. package/src/lightning/colorPickerCustom/input-text.slds.css +41 -68
  40. package/src/lightning/colorPickerPanel/color-picker-panel.slds.css +9 -10
  41. package/src/lightning/colorPickerPanel/popover.slds.css +0 -2
  42. package/src/lightning/combobox/combobox.slds.css +1 -2
  43. package/src/lightning/combobox/form-element.slds.css +54 -54
  44. package/src/lightning/datatable/__examples__/customDatatableWrapper/customDatatableWrapper.js +0 -69
  45. package/src/lightning/datatable/__examples__/customDatatypeDeleteRowBtn/customDatatypeDeleteRowBtn.html +1 -1
  46. package/src/lightning/datatable/__examples__/customDatatypeDeleteRowBtn/customDatatypeDeleteRowBtn.js +1 -16
  47. package/src/lightning/datatable/__examples__/customDatatypeLink/customDatatypeLink.html +3 -3
  48. package/src/lightning/datatable/__examples__/customDatatypeRowOrderingBtn/customDatatypeRowOrderingBtn.html +1 -8
  49. package/src/lightning/datatable/__examples__/customDatatypeRowOrderingBtn/customDatatypeRowOrderingBtn.js +2 -39
  50. package/src/lightning/datatable/__examples__/customDatatypeTable/customNumber.html +1 -1
  51. package/src/lightning/datatable/__examples__/customDatatypeTable/customNumberEdit.html +2 -0
  52. package/src/lightning/datatable/__examples__/customDatatypeTable/deleteRow.html +3 -2
  53. package/src/lightning/datatable/__examples__/customDatatypeTable/iconPill.html +1 -1
  54. package/src/lightning/datatable/__examples__/customNestedComponent/customNestedComponent.html +10 -0
  55. package/src/lightning/datatable/__examples__/customNestedComponent/customNestedComponent.js +12 -0
  56. package/src/lightning/datatable/autoWidthStrategy.js +147 -191
  57. package/src/lightning/datatable/columnResizer.js +35 -35
  58. package/src/lightning/datatable/columnWidthManager.js +118 -177
  59. package/src/lightning/datatable/columns.js +90 -59
  60. package/src/lightning/datatable/datagrid.slds.css +187 -0
  61. package/src/lightning/datatable/datatable.js +248 -229
  62. package/src/lightning/datatable/errors.js +3 -0
  63. package/src/lightning/datatable/fixedWidthStrategy.js +22 -29
  64. package/src/lightning/datatable/headerActions.js +7 -9
  65. package/src/lightning/datatable/infiniteLoading.js +15 -15
  66. package/src/lightning/datatable/inlineEdit.js +255 -235
  67. package/src/lightning/datatable/keyboard.js +318 -282
  68. package/src/lightning/datatable/renderManager.js +10 -7
  69. package/src/lightning/datatable/resizeObserver.js +11 -59
  70. package/src/lightning/datatable/rowLevelActions.js +6 -5
  71. package/src/lightning/datatable/rowNumber.js +23 -23
  72. package/src/lightning/datatable/rowSelection.js +173 -145
  73. package/src/lightning/datatable/rowSelectionShared.js +13 -6
  74. package/src/lightning/datatable/rows.js +231 -196
  75. package/src/lightning/datatable/sort.js +26 -22
  76. package/src/lightning/datatable/templates/div/div.css +2 -57
  77. package/src/lightning/datatable/templates/div/div.html +13 -6
  78. package/src/lightning/datatable/templates/div/div.lbc.native.css +3 -0
  79. package/src/lightning/datatable/templates/div/div.lbc.synthetic.css +86 -0
  80. package/src/lightning/datatable/templates/table/table.html +1 -0
  81. package/src/lightning/datatable/utils.js +5 -5
  82. package/src/lightning/datatable/widthManagerShared.js +24 -21
  83. package/src/lightning/datatable/wrapText.js +25 -26
  84. package/src/lightning/datepicker/datepicker.js +32 -9
  85. package/src/lightning/datepicker/datepicker.js-meta.xml +6 -0
  86. package/src/lightning/datepicker/form-element.slds.css +54 -54
  87. package/src/lightning/datepicker/input-text.slds.css +41 -68
  88. package/src/lightning/datetimepicker/datetimepicker.js-meta.xml +6 -0
  89. package/src/lightning/datetimepicker/form-element.slds.css +54 -54
  90. package/src/lightning/datetimepicker/input-text.slds.css +41 -68
  91. package/src/lightning/dualListbox/dual-listbox.slds.css +7 -2
  92. package/src/lightning/dualListbox/form-element.slds.css +54 -54
  93. package/src/lightning/dualListbox/listbox.slds.css +51 -99
  94. package/src/lightning/dynamicIcon/dynamic-icon-strength.slds.css +1 -2
  95. package/src/lightning/dynamicIcon/dynamic-icon-trend.slds.css +1 -2
  96. package/src/lightning/formattedDateTime/formattedDateTime.js +7 -62
  97. package/src/lightning/formattedDateTime/formattedDateTime.js-meta.xml +3 -0
  98. package/src/lightning/formattedLocation/formattedLocation.html +1 -3
  99. package/src/lightning/formattedLocation/formattedLocation.js +3 -25
  100. package/src/lightning/formattedLookup/events.js +2 -4
  101. package/src/lightning/formattedNumber/formattedNumber.js +2 -49
  102. package/src/lightning/formattedRichText/formattedRichText.js +5 -5
  103. package/src/lightning/formattedRichText/linkTextNodes.js +58 -0
  104. package/src/lightning/groupedCombobox/form-element.slds.css +54 -54
  105. package/src/lightning/groupedCombobox/grouped-combobox.slds.css +0 -2
  106. package/src/lightning/groupedCombobox/groupedCombobox.js-meta.xml +1 -1
  107. package/src/lightning/groupedCombobox/input-text.slds.css +41 -68
  108. package/src/lightning/helptext/button-icon.slds.css +1 -1
  109. package/src/lightning/helptext/form-element.slds.css +54 -54
  110. package/src/lightning/icon/icon.slds.css +12 -25
  111. package/src/lightning/input/form-element.slds.css +54 -54
  112. package/src/lightning/inputAddress/form-element.slds.css +54 -54
  113. package/src/lightning/inputAddress/input-address.slds.css +1 -2
  114. package/src/lightning/inputAddress/input-text.slds.css +41 -68
  115. package/src/lightning/inputAddress/inputAddress.js +1 -0
  116. package/src/lightning/inputAddress/inputAddress.js-meta.xml +3 -0
  117. package/src/lightning/inputLocation/form-element.slds.css +54 -54
  118. package/src/lightning/inputLocation/input-location.slds.css +1 -2
  119. package/src/lightning/inputLocation/input-text.slds.css +41 -68
  120. package/src/lightning/inputName/form-element.slds.css +54 -54
  121. package/src/lightning/inputName/input-text.slds.css +41 -68
  122. package/src/lightning/interactiveDialogBase/interactive-dialog-base.slds.css +0 -3
  123. package/src/lightning/interactiveDialogBase/interactiveDialogBase.js-meta.xml +6 -0
  124. package/src/lightning/lookupAddress/form-element.slds.css +54 -54
  125. package/src/lightning/lookupAddress/listbox.slds.css +51 -99
  126. package/src/lightning/lookupAddress/location.js +2 -0
  127. package/src/lightning/lookupAddress/lookup-address.slds.css +0 -2
  128. package/src/lightning/lookupAddress/lookupAddress.js +15 -10
  129. package/src/lightning/menuDivider/menu-divider.slds.css +0 -2
  130. package/src/lightning/menuItem/menu-item.slds.css +8 -2
  131. package/src/lightning/menuSubheader/menu-subheader.slds.css +1 -2
  132. package/src/lightning/modalBase/modal-base.slds.css +3 -3
  133. package/src/lightning/modalBase/modalBase.js +0 -8
  134. package/src/lightning/modalBase/modalBase.js-meta.xml +6 -0
  135. package/src/lightning/modalBody/modal-body.slds.css +1 -2
  136. package/src/lightning/modalFooter/modal-footer.slds.css +2 -2
  137. package/src/lightning/modalFooter/modalFooter.js +0 -21
  138. package/src/lightning/modalHeader/modal-header.slds.css +1 -2
  139. package/src/lightning/modalHeader/modalHeader.js +0 -22
  140. package/src/lightning/overlay/overlay.js-meta.xml +6 -0
  141. package/src/lightning/pill/pill.slds.css +32 -58
  142. package/src/lightning/pillContainer/button.slds.css +1 -1
  143. package/src/lightning/pillContainer/listbox.slds.css +51 -99
  144. package/src/lightning/pillContainer/pill-container.slds.css +6 -10
  145. package/src/lightning/pillContainer/pill.slds.css +32 -58
  146. package/src/lightning/popup/popover.slds.css +0 -2
  147. package/src/lightning/primitiveBubble/primitiveBubble.js-meta.xml +6 -0
  148. package/src/lightning/primitiveButton/primitiveButoon.js-meta.xml +6 -0
  149. package/src/lightning/primitiveCellCheckbox/checkbox.css +2 -0
  150. package/src/lightning/primitiveColorpickerButton/color-picker-button.slds.css +16 -38
  151. package/src/lightning/primitiveCustomCell/primitiveCustomCell.js +26 -1
  152. package/src/lightning/primitiveHeaderFactory/nonsortableHeader.css +1 -0
  153. package/src/lightning/primitiveHeaderFactory/selectableHeader.css +2 -0
  154. package/src/lightning/primitiveIcon/icon.slds.css +12 -25
  155. package/src/lightning/primitiveIcon/primitiveIcon.js-meta.xml +6 -0
  156. package/src/lightning/primitiveIframe/primitiveIframe.js +3 -1
  157. package/src/lightning/primitiveInputCheckbox/form-element.slds.css +54 -54
  158. package/src/lightning/primitiveInputCheckbox/primitiveInputCheckbox.js +5 -2
  159. package/src/lightning/primitiveInputCheckbox/primitiveInputCheckbox.js-meta.xml +6 -0
  160. package/src/lightning/primitiveInputCheckboxButton/form-element.slds.css +54 -54
  161. package/src/lightning/primitiveInputCheckboxButton/input-checkbox-button.slds.css +6 -4
  162. package/src/lightning/primitiveInputCheckboxButton/primitiveInputCheckboxButton.js +5 -2
  163. package/src/lightning/primitiveInputCheckboxButton/primitiveInputCheckboxButton.js-meta.xml +6 -0
  164. package/src/lightning/primitiveInputColor/form-element.slds.css +54 -54
  165. package/src/lightning/primitiveInputColor/input-color.slds.css +2 -3
  166. package/src/lightning/primitiveInputColor/input-text.slds.css +41 -68
  167. package/src/lightning/primitiveInputColor/primitiveInputColor.js +5 -2
  168. package/src/lightning/primitiveInputColor/primitiveInputColor.js-meta.xml +6 -0
  169. package/src/lightning/primitiveInputFile/button.slds.css +1 -1
  170. package/src/lightning/primitiveInputFile/form-element.slds.css +54 -54
  171. package/src/lightning/primitiveInputFile/input-file.slds.css +1 -4
  172. package/src/lightning/primitiveInputFile/primitiveInputFile.js +4 -2
  173. package/src/lightning/primitiveInputFile/primitiveInputFile.js-meta.xml +6 -0
  174. package/src/lightning/primitiveInputRadio/primitiveInputRadio.js +4 -2
  175. package/src/lightning/primitiveInputSimple/form-element.slds.css +54 -54
  176. package/src/lightning/primitiveInputSimple/input-text.slds.css +41 -68
  177. package/src/lightning/primitiveInputSimple/primitiveInputSimple.js-meta.xml +6 -0
  178. package/src/lightning/primitiveInputToggle/form-element.slds.css +54 -54
  179. package/src/lightning/primitiveInputToggle/input-toggle.slds.css +50 -27
  180. package/src/lightning/primitiveInputToggle/primitiveInputToggle.js +5 -2
  181. package/src/lightning/primitiveInputToggle/primitiveInputToggle.js-meta.xml +6 -0
  182. package/src/lightning/progressBar/progress-bar.slds.css +8 -10
  183. package/src/lightning/progressRing/progress-ring.slds.css +0 -23
  184. package/src/lightning/progressStep/progressStep.js +1 -14
  185. package/src/lightning/radioGroup/form-element.slds.css +54 -54
  186. package/src/lightning/radioGroup/radioGroup.html +1 -2
  187. package/src/lightning/radioGroup/radioGroup.js +1 -0
  188. package/src/lightning/routingService/routingService.js +31 -5
  189. package/src/lightning/select/form-element.slds.css +54 -54
  190. package/src/lightning/select/select.slds.css +4 -2
  191. package/src/lightning/sldsUtilsVisibility/sldsUtilsVisibility.css +4 -0
  192. package/src/lightning/spinner/spinner.slds.css +62 -62
  193. package/src/lightning/tab/tab.js +4 -2
  194. package/src/lightning/tab/tab.slds.css +14 -7
  195. package/src/lightning/tabBar/tab-bar.slds.css +16 -6
  196. package/src/lightning/tabset/__docs__/tabset.md +24 -1
  197. package/src/lightning/tabset/tabset.js +25 -38
  198. package/src/lightning/tabset/tabset.slds.css +0 -2
  199. package/src/lightning/textarea/form-element.slds.css +54 -54
  200. package/src/lightning/textarea/textarea.js +5 -1
  201. package/src/lightning/textarea/textarea.slds.css +22 -9
  202. package/src/lightning/timepicker/form-element.slds.css +54 -54
  203. package/src/lightning/timepicker/timepicker.js-meta.xml +6 -0
  204. package/src/lightning/timepicker/timepicker.slds.css +2 -2
  205. package/src/lightning/toast/__docs__/toast.md +20 -22
  206. package/src/lightning/toast/button-icon.slds.css +1 -1
  207. package/src/lightning/toast/icon.slds.css +12 -25
  208. package/src/lightning/toast/toast.js +15 -12
  209. package/src/lightning/toast/toast.slds.css +6 -18
  210. package/src/lightning/toastContainer/toast.slds.css +6 -18
  211. package/src/lightning/toastContainer/toastContainer.js +25 -17
  212. package/src/lightning/tooltipLibrary/tooltipLibrary.js +12 -9
  213. package/src/lightning/tree/tree.js +2 -0
  214. package/src/lightning/utils/classSet.js +9 -3
  215. package/src/lightning/utilsPrivate/formatUtils.js +158 -0
  216. package/src/lightning/utilsPrivate/textUtils.js +16 -0
  217. package/src/lightning/utilsPrivate/utilsPrivate.js +56 -15
  218. package/src/lightning/utilsPrivate/validationUtils.js +59 -0
  219. package/src/lightning/verticalNavigationSection/vertical-navigation-section.slds.css +0 -2
  220. package/src/lightning/datatable/resizeSensor.js +0 -244
  221. package/src/lightning/formattedRichText/linkify.js +0 -43
  222. package/src/lightning/utilsPrivate/smartSetAttribute.js +0 -19
@@ -40,27 +40,23 @@ const TAB = 9;
40
40
  const SPACE = 32;
41
41
 
42
42
  // Navigation Direction
43
- const NAVIGATION_DIR = (() => {
44
- if (isRTL()) {
45
- return {
46
- RIGHT: -1,
47
- LEFT: 1,
48
- USE_CURRENT: 0,
49
- RESET: 2,
50
- TAB_FORWARD: -1,
51
- TAB_BACKWARD: 1,
52
- };
53
- }
54
-
55
- return {
56
- RIGHT: 1,
57
- LEFT: -1,
58
- USE_CURRENT: 0,
59
- RESET: 2,
60
- TAB_FORWARD: 1,
61
- TAB_BACKWARD: -1,
62
- };
63
- })();
43
+ const NAVIGATION_DIR = isRTL()
44
+ ? {
45
+ RIGHT: -1,
46
+ LEFT: 1,
47
+ USE_CURRENT: 0,
48
+ RESET: 2,
49
+ TAB_FORWARD: -1,
50
+ TAB_BACKWARD: 1,
51
+ }
52
+ : {
53
+ RIGHT: 1,
54
+ LEFT: -1,
55
+ USE_CURRENT: 0,
56
+ RESET: 2,
57
+ TAB_FORWARD: 1,
58
+ TAB_BACKWARD: -1,
59
+ };
64
60
 
65
61
  // Selectors
66
62
  const SELECTORS = {
@@ -137,66 +133,83 @@ export function handleKeydownOnTable(event) {
137
133
  function reactToKeyboardInActionMode(template, state, event) {
138
134
  switch (event.detail.keyCode) {
139
135
  case ARROW_LEFT:
140
- return reactToArrowLeft(template, state, event);
136
+ reactToArrowLeft(template, state, event);
137
+ break;
141
138
  case ARROW_RIGHT:
142
- return reactToArrowRight(template, state, event);
139
+ reactToArrowRight(template, state, event);
140
+ break;
143
141
  case ARROW_UP:
144
- return reactToArrowUp(template, state, event);
142
+ reactToArrowUp(template, state, event);
143
+ break;
145
144
  case ARROW_DOWN:
146
- return reactToArrowDown(template, state, event);
145
+ reactToArrowDown(template, state, event);
146
+ break;
147
147
  case ENTER:
148
148
  case SPACE:
149
- return reactToEnter(template, state, event);
149
+ reactToEnter(template, state, event);
150
+ break;
150
151
  case ESCAPE:
151
- return reactToEscape(template, state, event);
152
+ reactToEscape(template, state, event);
153
+ break;
152
154
  case TAB:
153
- return reactToTab(template, state, event);
155
+ reactToTab(template, state, event);
156
+ break;
154
157
  default:
155
- return state;
158
+ break;
156
159
  }
157
160
  }
158
161
 
159
162
  function reactToKeyboardInNavMode(element, state, event) {
163
+ const {
164
+ activeCell: { colKeyValue, rowKeyValue },
165
+ } = state;
166
+ const { keyCode, shiftKey } = event;
160
167
  const syntheticEvent = {
161
168
  detail: {
162
- rowKeyValue: state.activeCell.rowKeyValue,
163
- colKeyValue: state.activeCell.colKeyValue,
164
- keyCode: event.keyCode,
165
- shiftKey: event.shiftKey,
169
+ rowKeyValue,
170
+ colKeyValue,
171
+ keyCode: keyCode,
172
+ shiftKey: shiftKey,
166
173
  },
167
174
  preventDefault: () => {},
168
175
  stopPropagation: () => {},
169
176
  };
170
-
171
177
  // We need event.preventDefault so that actions like arrow up or down
172
178
  // does not scroll the table but instead sets focus on the right cells
173
179
  switch (event.keyCode) {
174
180
  case ARROW_LEFT:
175
181
  event.preventDefault();
176
- return reactToArrowLeft(element, state, syntheticEvent);
182
+ reactToArrowLeft(element, state, syntheticEvent);
183
+ break;
177
184
  case ARROW_RIGHT:
178
185
  event.preventDefault();
179
- return reactToArrowRight(element, state, syntheticEvent);
186
+ reactToArrowRight(element, state, syntheticEvent);
187
+ break;
180
188
  case ARROW_UP:
181
189
  event.preventDefault();
182
- return reactToArrowUp(element, state, syntheticEvent);
190
+ reactToArrowUp(element, state, syntheticEvent);
191
+ break;
183
192
  case ARROW_DOWN:
184
193
  event.preventDefault();
185
- return reactToArrowDown(element, state, syntheticEvent);
194
+ reactToArrowDown(element, state, syntheticEvent);
195
+ break;
186
196
  case ENTER:
187
197
  case SPACE:
188
198
  event.preventDefault();
189
- return reactToEnter(element, state, syntheticEvent);
199
+ reactToEnter(element, state, syntheticEvent);
200
+ break;
190
201
  case ESCAPE:
191
202
  // td, th or div[role=gridcell/rowheader] is the active element in the
192
203
  // action mode if cell doesn't have action elements; hence this can be
193
204
  // reached and we should react to escape as exiting from action mode
194
205
  syntheticEvent.detail.keyEvent = event;
195
- return reactToEscape(element, state, syntheticEvent);
206
+ reactToEscape(element, state, syntheticEvent);
207
+ break;
196
208
  case TAB:
197
- return reactToTab(element, state, syntheticEvent);
209
+ reactToTab(element, state, syntheticEvent);
210
+ break;
198
211
  default:
199
- return state;
212
+ break;
200
213
  }
201
214
  }
202
215
 
@@ -209,29 +222,26 @@ function moveFromCellToRow(element, state) {
209
222
  function reactToArrowLeft(element, state, event) {
210
223
  const { rowKeyValue, colKeyValue } = event.detail;
211
224
  const { colIndex } = getIndexesByKeys(state, rowKeyValue, colKeyValue);
212
- const { columns } = state;
213
-
214
225
  // Move from navigation mode to row mode when user
215
226
  // arrows left when in nav mode and on the first column
216
227
  if (colIndex === 0 && canBeRowNavigationMode(state)) {
217
228
  moveFromCellToRow(element, state);
218
229
  } else {
219
230
  const nextColIndex = getNextIndexLeft(state, colIndex);
220
-
221
231
  if (nextColIndex === undefined) {
222
232
  return;
223
233
  }
224
-
225
234
  setBlurActiveCell(element, state);
226
-
227
- // update activeCell
235
+ // Untracked state change.
236
+ // Update activeCell.
228
237
  state.activeCell = {
229
238
  rowKeyValue,
230
239
  colKeyValue: generateColKeyValue(
231
- columns[nextColIndex],
240
+ state.columns[nextColIndex],
232
241
  nextColIndex
233
242
  ),
234
243
  };
244
+ // Tracked state change.
235
245
  setFocusActiveCell(element, state, NAVIGATION_DIR.LEFT);
236
246
  }
237
247
  }
@@ -240,18 +250,18 @@ function reactToArrowRight(element, state, event) {
240
250
  const { rowKeyValue, colKeyValue } = event.detail;
241
251
  const { colIndex } = getIndexesByKeys(state, rowKeyValue, colKeyValue);
242
252
  const nextColIndex = getNextIndexRight(state, colIndex);
243
- const { columns } = state;
244
-
245
253
  if (nextColIndex === undefined) {
246
254
  return;
247
255
  }
248
-
249
256
  setBlurActiveCell(element, state);
250
-
251
- // update activeCell
257
+ // Untracked state change.
258
+ // Update activeCell.
252
259
  state.activeCell = {
253
260
  rowKeyValue,
254
- colKeyValue: generateColKeyValue(columns[nextColIndex], nextColIndex),
261
+ colKeyValue: generateColKeyValue(
262
+ state.columns[nextColIndex],
263
+ nextColIndex
264
+ ),
255
265
  };
256
266
  setFocusActiveCell(element, state, NAVIGATION_DIR.RIGHT);
257
267
  }
@@ -260,25 +270,23 @@ function reactToArrowUp(element, state, event) {
260
270
  const { rowKeyValue, colKeyValue, keyEvent } = event.detail;
261
271
  const { rowIndex } = getIndexesByKeys(state, rowKeyValue, colKeyValue);
262
272
  const nextRowIndex = getNextIndexUp(state, rowIndex);
263
- const { rows } = state;
264
-
265
- if (nextRowIndex === undefined) {
266
- return;
267
- }
268
-
269
- if (state.hideTableHeader && nextRowIndex === -1) {
273
+ if (
274
+ nextRowIndex === undefined ||
275
+ (state.hideTableHeader && nextRowIndex === -1)
276
+ ) {
270
277
  return;
271
278
  }
272
279
 
273
280
  if (keyEvent) {
274
281
  keyEvent.stopPropagation();
275
282
  }
276
-
283
+ // Tracked state change.
277
284
  setBlurActiveCell(element, state);
278
-
279
- // update activeCell
285
+ // Untracked state change.
286
+ // Update activeCell.
280
287
  state.activeCell = {
281
- rowKeyValue: nextRowIndex !== -1 ? rows[nextRowIndex].key : HEADER_ROW,
288
+ rowKeyValue:
289
+ nextRowIndex !== -1 ? state.rows[nextRowIndex].key : HEADER_ROW,
282
290
  colKeyValue,
283
291
  };
284
292
  setFocusActiveCell(element, state, NAVIGATION_DIR.USE_CURRENT);
@@ -288,25 +296,23 @@ function reactToArrowDown(element, state, event) {
288
296
  const { rowKeyValue, colKeyValue, keyEvent } = event.detail;
289
297
  const { rowIndex } = getIndexesByKeys(state, rowKeyValue, colKeyValue);
290
298
  const nextRowIndex = getNextIndexDown(state, rowIndex);
291
- const { rows } = state;
292
-
293
- if (nextRowIndex === undefined) {
294
- return;
295
- }
296
-
297
- if (state.hideTableHeader && nextRowIndex === -1) {
299
+ if (
300
+ nextRowIndex === undefined ||
301
+ (state.hideTableHeader && nextRowIndex === -1)
302
+ ) {
298
303
  return;
299
304
  }
300
305
 
301
306
  if (keyEvent) {
302
307
  keyEvent.stopPropagation();
303
308
  }
304
-
309
+ // Tracked state change.
305
310
  setBlurActiveCell(element, state);
306
-
307
- // update activeCell
311
+ // Untracked state change.
312
+ // Update activeCell.
308
313
  state.activeCell = {
309
- rowKeyValue: nextRowIndex !== -1 ? rows[nextRowIndex].key : HEADER_ROW,
314
+ rowKeyValue:
315
+ nextRowIndex !== -1 ? state.rows[nextRowIndex].key : HEADER_ROW,
310
316
  colKeyValue,
311
317
  };
312
318
  setFocusActiveCell(element, state, NAVIGATION_DIR.USE_CURRENT);
@@ -314,20 +320,26 @@ function reactToArrowDown(element, state, event) {
314
320
 
315
321
  function reactToEnter(element, state, event) {
316
322
  if (state.keyboardMode === NAVIGATION_MODE) {
323
+ // Untracked state change.
317
324
  state.keyboardMode = ACTION_MODE;
325
+ const { keyCode, keyEvent } = event.detail;
318
326
  const { rowIndex, colIndex } = getIndexesActiveCell(state);
327
+ if (keyEvent) {
328
+ keyEvent.preventDefault();
329
+ }
319
330
 
331
+ const info = { action: undefined };
332
+ if (keyCode === SPACE) {
333
+ info.action = 'space';
334
+ } else if (keyCode === ENTER) {
335
+ info.action = 'enter';
336
+ }
320
337
  const actionsMap = {};
321
338
  actionsMap[SPACE] = 'space';
322
339
  actionsMap[ENTER] = 'enter';
323
-
324
- if (event.detail.keyEvent) {
325
- event.detail.keyEvent.preventDefault();
326
- }
327
- setModeActiveCell(element, state, {
328
- action: actionsMap[event.detail.keyCode],
329
- });
330
- updateTabIndex(state, rowIndex, colIndex, -1);
340
+ // Tracked state changes.
341
+ setModeActiveCell(element, state, info);
342
+ updateCellTabIndex(state, rowIndex, colIndex, -1);
331
343
  }
332
344
  }
333
345
 
@@ -336,7 +348,9 @@ function reactToEscape(element, state, event) {
336
348
  // When the table is in action mode this event shouldn't bubble
337
349
  // because if the table in inside a modal it should prevent the modal closes
338
350
  event.detail.keyEvent.stopPropagation();
351
+ // Untracked state change.
339
352
  state.keyboardMode = NAVIGATION_MODE;
353
+ // Tracked state changes.
340
354
  setModeActiveCell(element, state);
341
355
  setFocusActiveCell(element, state, NAVIGATION_DIR.RESET);
342
356
  }
@@ -346,62 +360,74 @@ function reactToTab(element, state, event) {
346
360
  event.preventDefault();
347
361
  event.stopPropagation();
348
362
 
349
- const { shiftKey } = event.detail;
350
- const direction = getTabDirection(shiftKey);
363
+ const { detail } = event;
364
+ const direction = getTabDirection(detail.shiftKey);
351
365
  const isExitCell = isActiveCellAnExitCell(state, direction);
352
366
 
353
- // if in ACTION mode
367
+ // If in ACTION mode.
354
368
  if (state.keyboardMode === ACTION_MODE) {
355
- // if not on last or first cell, tab through each cell of the grid
369
+ // If not on last or first cell, tab through each cell of the grid.
356
370
  if (isExitCell === false) {
357
- // prevent default key event in action mode when actually moving within the grid
358
- if (event.detail.keyEvent) {
359
- event.detail.keyEvent.preventDefault();
371
+ // Prevent default key event in action mode when actually moving within the grid.
372
+ const { keyEvent } = detail;
373
+ if (keyEvent) {
374
+ keyEvent.preventDefault();
360
375
  }
361
- // tab in proper direction based on shift key press
376
+ // Tab in proper direction based on shift key press.
362
377
  if (direction === 'BACKWARD') {
363
378
  reactToTabBackward(element, state);
364
379
  } else {
365
380
  reactToTabForward(element, state);
366
381
  }
367
382
  } else {
368
- // exit ACTION mode
383
+ // Untracked state change.
384
+ // Exit ACTION mode.
369
385
  state.keyboardMode = NAVIGATION_MODE;
386
+ // Tracked state change.
370
387
  setModeActiveCell(element, state);
388
+ // Untracked state change.
371
389
  state.isExitingActionMode = true;
372
390
  }
373
391
  } else {
392
+ // Untracked state change.
374
393
  state.isExitingActionMode = true;
375
394
  }
376
395
  }
377
396
 
378
397
  export function reactToTabForward(element, state) {
379
- const { nextRowIndex, nextColIndex } = getNextIndexOnTab(state, 'FORWARD');
380
- const { columns, rows } = state;
381
-
398
+ const { nextColIndex, nextRowIndex } = getNextIndexOnTab(state, 'FORWARD');
399
+ // Tracked state change.
382
400
  setBlurActiveCell(element, state);
383
-
384
- // update activeCell
401
+ // Update activeCell.
385
402
  state.activeCell = {
386
- rowKeyValue: nextRowIndex !== -1 ? rows[nextRowIndex].key : HEADER_ROW,
387
- colKeyValue: generateColKeyValue(columns[nextColIndex], nextColIndex),
403
+ rowKeyValue:
404
+ nextRowIndex === -1 ? HEADER_ROW : state.rows[nextRowIndex].key,
405
+ colKeyValue: generateColKeyValue(
406
+ state.columns[nextColIndex],
407
+ nextColIndex
408
+ ),
388
409
  };
410
+ // Tracked state change.
389
411
  setFocusActiveCell(element, state, NAVIGATION_DIR.TAB_FORWARD, {
390
412
  action: 'tab',
391
413
  });
392
414
  }
393
415
 
394
416
  export function reactToTabBackward(element, state) {
395
- const { nextRowIndex, nextColIndex } = getNextIndexOnTab(state, 'BACKWARD');
396
- const { columns, rows } = state;
397
-
417
+ const { nextColIndex, nextRowIndex } = getNextIndexOnTab(state, 'BACKWARD');
418
+ // Tracked state change.
398
419
  setBlurActiveCell(element, state);
399
-
400
- // update activeCell
420
+ // Untracked state change.
421
+ // Update activeCell.
401
422
  state.activeCell = {
402
- rowKeyValue: nextRowIndex !== -1 ? rows[nextRowIndex].key : HEADER_ROW,
403
- colKeyValue: generateColKeyValue(columns[nextColIndex], nextColIndex),
423
+ rowKeyValue:
424
+ nextRowIndex === -1 ? HEADER_ROW : state.rows[nextRowIndex].key,
425
+ colKeyValue: generateColKeyValue(
426
+ state.columns[nextColIndex],
427
+ nextColIndex
428
+ ),
404
429
  };
430
+ // Tracked state change.
405
431
  setFocusActiveCell(element, state, NAVIGATION_DIR.TAB_BACKWARD, {
406
432
  action: 'tab',
407
433
  });
@@ -417,16 +443,12 @@ function getTabDirection(shiftKey) {
417
443
  * @param {string} direction - 'FORWARD' or 'BACKWARD'
418
444
  * @returns {object} - nextRowIndex, nextColIndex values, isExitCell boolean
419
445
  */
420
- function getNextIndexOnTab(state, direction) {
446
+ function getNextIndexOnTab(state, direction = 'FORWARD') {
421
447
  const { rowIndex, colIndex } = getIndexesActiveCell(state);
422
-
423
448
  // decide which function to use based on the value of direction
424
- const nextTabFunc = {
425
- FORWARD: getNextIndexOnTabForward,
426
- BACKWARD: getNextIndexOnTabBackward,
427
- };
428
-
429
- return nextTabFunc[direction](state, rowIndex, colIndex);
449
+ return direction === 'BACKWARD'
450
+ ? getNextIndexOnTabBackward(state, rowIndex, colIndex)
451
+ : getNextIndexOnTabForward(state, rowIndex, colIndex);
430
452
  }
431
453
 
432
454
  function getNextIndexOnTabForward(state, rowIndex, colIndex) {
@@ -471,78 +493,65 @@ function getNextIndexOnTabBackward(state, rowIndex, colIndex) {
471
493
  * and will remove the user from row mode and place them in navigation mode
472
494
  * Arrow Left: If cell is expanded, this will collapse the expanded row
473
495
  *
474
- * @param {*} datatable - The datatable component/instance
496
+ * @param {*} dt - The datatable instance
475
497
  * @param {*} state - The datatable state object
476
498
  * @param {*} event - The keydown event
477
499
  * @returns Mutated state
478
500
  */
479
- export function reactToKeyboardOnRow(datatable, state, event) {
501
+ export function reactToKeyboardOnRow(dt, state, event) {
480
502
  // TODO: Adapt this selector to also work in a role-based table once tree-grid is also migrated
503
+ const { localName } = event.target;
481
504
  if (
482
- isRowNavigationMode(state) &&
483
- event.target.localName.indexOf('tr') !== -1
505
+ localName &&
506
+ localName.indexOf('tr') !== -1 &&
507
+ isRowNavigationMode(state)
484
508
  ) {
485
- const element = datatable.template;
509
+ const element = dt.template;
486
510
  switch (event.detail.keyCode) {
487
511
  case ARROW_LEFT:
488
- return reactToArrowLeftOnRow.call(
489
- datatable,
490
- element,
491
- state,
492
- event
493
- );
512
+ reactToArrowLeftOnRow.call(dt, element, state, event);
513
+ break;
494
514
  case ARROW_RIGHT:
495
- return reactToArrowRightOnRow.call(
496
- datatable,
497
- element,
498
- state,
499
- event
500
- );
515
+ reactToArrowRightOnRow.call(dt, element, state, event);
516
+ break;
501
517
  case ARROW_UP:
502
- return reactToArrowUpOnRow.call(
503
- datatable,
504
- element,
505
- state,
506
- event
507
- );
518
+ reactToArrowUpOnRow.call(dt, element, state, event);
519
+ break;
508
520
  case ARROW_DOWN:
509
- return reactToArrowDownOnRow.call(
510
- datatable,
511
- element,
512
- state,
513
- event
514
- );
521
+ reactToArrowDownOnRow.call(dt, element, state, event);
522
+ break;
515
523
  default:
516
- return state;
524
+ break;
517
525
  }
518
526
  }
519
- return state;
520
527
  }
521
528
 
522
529
  function reactToArrowLeftOnRow(element, state, event) {
523
530
  const { rowKeyValue, rowHasChildren, rowExpanded, rowLevel } = event.detail;
524
- // check if row needs to be collapsed
525
- // if not go to parent and focus there
531
+ // Check if row needs to be collapsed.
532
+ // If not, go to parent and focus there.
526
533
  if (rowHasChildren && rowExpanded) {
527
534
  fireRowToggleEvent.call(this, rowKeyValue, rowExpanded);
528
535
  } else if (rowLevel > 1) {
529
536
  const treeColumn = getStateTreeColumn(state);
530
537
  if (treeColumn) {
531
- const colKeyValue = treeColumn.colKeyValue;
538
+ const { colKeyValue } = treeColumn;
532
539
  const { rowIndex } = getIndexesByKeys(
533
540
  state,
534
541
  rowKeyValue,
535
542
  colKeyValue
536
543
  );
537
- const parentIndex = getRowParent(state, rowLevel, rowIndex);
544
+ const parentIndex = getRowParentIndex(state, rowLevel, rowIndex);
538
545
  if (parentIndex !== -1) {
539
- const rows = state.rows;
546
+ // Tracked state change.
540
547
  setBlurActiveRow(element, state);
541
- // update activeCell for the row
548
+ // Untracked state change.
549
+ // Update activeCell for the row.
542
550
  state.activeCell = {
543
- rowKeyValue: rows[parentIndex].key,
551
+ rowKeyValue: state.rows[parentIndex].key,
544
552
  colKeyValue,
545
553
  };
554
+ // Tracked state change.
546
555
  setFocusActiveRow(element, state);
547
556
  }
548
557
  }
@@ -550,17 +559,20 @@ function reactToArrowLeftOnRow(element, state, event) {
550
559
  }
551
560
 
552
561
  function moveFromRowToCell(element, state) {
562
+ // Tracked state change.
553
563
  setBlurActiveRow(element, state);
564
+ // Untracked state change.
554
565
  unsetRowNavigationMode(state);
566
+ // Tracked state change.
555
567
  setFocusActiveCell(element, state, NAVIGATION_DIR.USE_CURRENT);
556
568
  }
557
569
 
558
570
  function reactToArrowRightOnRow(element, state, event) {
559
571
  const { rowKeyValue, rowHasChildren, rowExpanded } = event.detail;
560
- // check if row needs to be expanded
561
- // expand row if has children and is collapsed
562
- // otherwise make this.state.rowMode = false
563
- // move tabindex 0 to first cell in the row and focus there
572
+ // Check if row needs to be expanded.
573
+ // Expand row if has children and is collapsed.
574
+ // Otherwise, make this.state.rowMode = false.
575
+ // Move tabindex 0 to first cell in the row and focus there.
564
576
  if (rowHasChildren && !rowExpanded) {
565
577
  fireRowToggleEvent.call(this, rowKeyValue, rowExpanded);
566
578
  } else {
@@ -569,7 +581,7 @@ function reactToArrowRightOnRow(element, state, event) {
569
581
  }
570
582
 
571
583
  function reactToArrowUpOnRow(element, state, event) {
572
- // move tabindex 0 one row down
584
+ // Move tabindex 0 one row down.
573
585
  const { rowKeyValue, keyEvent } = event.detail;
574
586
  const treeColumn = getStateTreeColumn(state);
575
587
 
@@ -577,24 +589,26 @@ function reactToArrowUpOnRow(element, state, event) {
577
589
  keyEvent.preventDefault();
578
590
 
579
591
  if (treeColumn) {
580
- const colKeyValue = treeColumn.colKeyValue;
592
+ const { colKeyValue } = treeColumn;
581
593
  const { rowIndex } = getIndexesByKeys(state, rowKeyValue, colKeyValue);
582
594
  const prevRowIndex = getNextIndexUpWrapped(state, rowIndex);
583
- const { rows } = state;
584
595
  if (prevRowIndex !== -1) {
596
+ // Tracked state change.
585
597
  setBlurActiveRow(element, state);
586
- // update activeCell for the row
598
+ // Untracked state change.
599
+ // Update activeCell for the row.
587
600
  state.activeCell = {
588
- rowKeyValue: rows[prevRowIndex].key,
601
+ rowKeyValue: state.rows[prevRowIndex].key,
589
602
  colKeyValue,
590
603
  };
604
+ // Tracked state change.
591
605
  setFocusActiveRow(element, state);
592
606
  }
593
607
  }
594
608
  }
595
609
 
596
610
  function reactToArrowDownOnRow(element, state, event) {
597
- // move tabindex 0 one row down
611
+ // Move tabindex 0 one row down.
598
612
  const { rowKeyValue, keyEvent } = event.detail;
599
613
  const treeColumn = getStateTreeColumn(state);
600
614
 
@@ -602,17 +616,19 @@ function reactToArrowDownOnRow(element, state, event) {
602
616
  keyEvent.preventDefault();
603
617
 
604
618
  if (treeColumn) {
605
- const colKeyValue = treeColumn.colKeyValue;
619
+ const { colKeyValue } = treeColumn;
606
620
  const { rowIndex } = getIndexesByKeys(state, rowKeyValue, colKeyValue);
607
621
  const nextRowIndex = getNextIndexDownWrapped(state, rowIndex);
608
- const { rows } = state;
609
622
  if (nextRowIndex !== -1) {
623
+ // Tracked state change.
610
624
  setBlurActiveRow(element, state);
625
+ // Untracked state change.
611
626
  // update activeCell for the row
612
627
  state.activeCell = {
613
- rowKeyValue: rows[nextRowIndex].key,
628
+ rowKeyValue: state.rows[nextRowIndex].key,
614
629
  colKeyValue,
615
630
  };
631
+ // Tracked state change.
616
632
  setFocusActiveRow(element, state);
617
633
  }
618
634
  }
@@ -621,18 +637,22 @@ function reactToArrowDownOnRow(element, state, event) {
621
637
  /***************************** ACTIVE CELL *****************************/
622
638
 
623
639
  function getDefaultActiveCell(state) {
624
- const { columns, rows } = state;
625
- if (columns.length > 0) {
626
- let colIndex;
627
- const existCustomerColumn = columns.some((column, index) => {
628
- colIndex = index;
629
- return isCustomerColumn(column);
630
- });
631
-
640
+ const { columns } = state;
641
+ const { length: colCount } = columns;
642
+ if (colCount) {
643
+ let colIndex = 0;
644
+ let existCustomerColumn = false;
645
+ for (let i = 0; i < colCount; i += 1) {
646
+ colIndex = i;
647
+ if (isCustomerColumn(columns[i])) {
648
+ existCustomerColumn = true;
649
+ break;
650
+ }
651
+ }
632
652
  if (!existCustomerColumn) {
633
653
  colIndex = 0;
634
654
  }
635
-
655
+ const { rows } = state;
636
656
  return {
637
657
  rowKeyValue: rows.length > 0 ? rows[0].key : HEADER_ROW,
638
658
  colKeyValue: generateColKeyValue(columns[colIndex], colIndex),
@@ -654,11 +674,14 @@ function setDefaultActiveCell(state) {
654
674
  * @param {Object} state - A reference to the datatable's state
655
675
  */
656
676
  export function getActiveCellElement(template, state) {
657
- if (state.activeCell) {
658
- const { rowKeyValue, colKeyValue } = state.activeCell;
659
- return getCellElementByKeys(template, rowKeyValue, colKeyValue);
660
- }
661
- return null;
677
+ const { activeCell } = state;
678
+ return activeCell
679
+ ? getCellElementByKeys(
680
+ template,
681
+ activeCell.rowKeyValue,
682
+ activeCell.colKeyValue
683
+ )
684
+ : null;
662
685
  }
663
686
 
664
687
  /**
@@ -670,11 +693,12 @@ export function getActiveCellElement(template, state) {
670
693
  * @returns {boolean} - true if rowKeyValue, colKeyValue are the current activeCell values.
671
694
  */
672
695
  export function isActiveCell(state, rowKeyValue, colKeyValue) {
673
- if (state.activeCell) {
696
+ const { activeCell } = state;
697
+ if (activeCell) {
674
698
  const {
675
699
  rowKeyValue: currentRowKeyValue,
676
700
  colKeyValue: currentColKeyValue,
677
- } = state.activeCell;
701
+ } = activeCell;
678
702
  return (
679
703
  currentRowKeyValue === rowKeyValue &&
680
704
  currentColKeyValue === colKeyValue
@@ -705,19 +729,19 @@ export function updateActiveCell(state, rowKeyValue, colKeyValue) {
705
729
  * If active cell is still valid we keep it the same
706
730
  *
707
731
  * @param {object} state - datatable state
708
- * @returns {object} state - mutated datatable state
709
732
  */
710
733
  export function syncActiveCell(state) {
711
- if (!state.activeCell || !stillValidActiveCell(state)) {
712
- if (state.activeCell && state.cellToFocusNext) {
713
- // there is previously focused cell
734
+ const { activeCell } = state;
735
+ if (!activeCell || !stillValidActiveCell(state)) {
736
+ // Untracked state changes.
737
+ if (activeCell && state.cellToFocusNext) {
738
+ // There is previously focused cell.
714
739
  setNextActiveCellFromPrev(state);
715
740
  } else {
716
- // there is no active cell or there is no previously focused cell
741
+ // There is no active cell or there is no previously focused cell.
717
742
  setDefaultActiveCell(state);
718
743
  }
719
744
  }
720
- return state;
721
745
  }
722
746
 
723
747
  /**
@@ -731,24 +755,26 @@ export function syncActiveCell(state) {
731
755
  */
732
756
  function setNextActiveCellFromPrev(state) {
733
757
  const { rowIndex, colIndex } = state.cellToFocusNext;
758
+ const { columns, rows } = state;
759
+ const { length: rowCount } = rows;
734
760
  let nextRowIndex = rowIndex;
735
- let nextColIndex = colIndex;
736
- const rowsCount = state.rows ? state.rows.length : 0;
737
- const colsCount = state.columns.length ? state.columns.length : 0;
738
761
 
739
- if (nextRowIndex > rowsCount - 1) {
762
+ if (nextRowIndex > rowCount - 1) {
740
763
  // row index not existing after update to new 5 > 5-1, 6 > 5-1,
741
- nextRowIndex = rowsCount - 1;
764
+ nextRowIndex = rowCount - 1;
742
765
  }
743
- if (nextColIndex > colsCount - 1) {
766
+ const { length: colCount } = columns;
767
+ let nextColIndex = colIndex;
768
+ if (nextColIndex > colCount - 1) {
744
769
  // col index not existing after update to new
745
- nextColIndex = colsCount - 1;
770
+ nextColIndex = colCount - 1;
746
771
  }
747
772
  const nextActiveCell = getCellFromIndexes(
748
773
  state,
749
774
  nextRowIndex,
750
775
  nextColIndex
751
776
  );
777
+ // Untracked state changes.
752
778
  if (nextActiveCell) {
753
779
  state.activeCell = nextActiveCell;
754
780
  } else {
@@ -798,21 +824,25 @@ function setModeActiveCell(element, state, info) {
798
824
  }
799
825
 
800
826
  function stillValidActiveCell(state) {
801
- const {
802
- activeCell: { rowKeyValue, colKeyValue },
803
- } = state;
804
-
805
- let sortableColumns = state.columns.filter((column) => column.sortable);
806
-
827
+ const { activeCell, indexes } = state;
828
+ const { rowKeyValue, colKeyValue } = activeCell;
807
829
  if (rowKeyValue === HEADER_ROW) {
808
- if (state.rows.length && sortableColumns.length === 0) {
809
- return false;
830
+ if (state.rows.length) {
831
+ const { columns } = state;
832
+ let sortable = false;
833
+ for (let colIndex = 0; colIndex < columns.length; colIndex += 1) {
834
+ if (columns[colIndex].sortable) {
835
+ sortable = true;
836
+ break;
837
+ }
838
+ }
839
+ if (!sortable) {
840
+ return false;
841
+ }
810
842
  }
811
843
  return state.headerIndexes[colKeyValue] !== undefined;
812
844
  }
813
- return !!(
814
- state.indexes[rowKeyValue] && state.indexes[rowKeyValue][colKeyValue]
815
- );
845
+ return !!(indexes[rowKeyValue] && indexes[rowKeyValue][colKeyValue]);
816
846
  }
817
847
 
818
848
  /***************************** FOCUS MANAGEMENT *****************************/
@@ -837,13 +867,14 @@ export function setFocusActiveCell(
837
867
  ) {
838
868
  const { keyboardMode } = state;
839
869
  const { rowIndex, colIndex } = getIndexesActiveCell(state);
870
+ let cellElement = getActiveCellElement(template, state);
840
871
 
841
872
  state.activeCell.focused = !(info && isActiveCellValid(state));
842
- updateTabIndex(state, rowIndex, colIndex);
843
873
 
844
- let cellElement = getActiveCellElement(template, state);
874
+ updateCellTabIndex(state, rowIndex, colIndex);
875
+
845
876
  // if the cell wasn't found, but does exist in the table, scroll to where it should be
846
- if (!cellElement && isActiveCellValid(state) && shouldScroll) {
877
+ if (shouldScroll && !cellElement && isActiveCellValid(state)) {
847
878
  scrollToCell(state, template, rowIndex);
848
879
  }
849
880
 
@@ -913,7 +944,7 @@ export function setBlurActiveCell(template, state) {
913
944
  cellElement.parentElement.classList.remove(FOCUS_CLASS);
914
945
  }
915
946
  }, 0);
916
- updateTabIndex(state, rowIndex, colIndex, -1);
947
+ updateCellTabIndex(state, rowIndex, colIndex, -1);
917
948
  }
918
949
  }
919
950
 
@@ -934,13 +965,17 @@ export function setCellToFocusFromPrev(state, template) {
934
965
  state.indexes &&
935
966
  !state.cellToFocusNext
936
967
  ) {
968
+ const { length: rowCount } = state.rows;
969
+ const lastIndex = rowCount - 1;
970
+ const { length: colCount } = state.columns;
937
971
  let { rowIndex, colIndex } = getIndexesActiveCell(state);
938
972
  colIndex = 0; // default point to the first column
939
- if (state.rows && rowIndex === state.rows.length - 1) {
973
+ if (rowIndex === lastIndex) {
940
974
  // if it is last row, make it point to its previous row
941
- rowIndex = state.rows.length - 1;
942
- colIndex = state.columns ? state.columns.length - 1 : 0;
975
+ rowIndex = lastIndex;
976
+ colIndex = colCount ? colCount - 1 : 0;
943
977
  }
978
+ // Untracked state change.
944
979
  state.cellToFocusNext = {
945
980
  rowIndex,
946
981
  colIndex,
@@ -949,8 +984,8 @@ export function setCellToFocusFromPrev(state, template) {
949
984
  }
950
985
 
951
986
  /**
952
- * if the current new active still is valid (exists) then set the celltofocusnext to null
953
- * @param {object} state - datatable state
987
+ * If the current new active still is valid (exists) then set the celltofocusnext to null.
988
+ * @param {object} state - The datatable state
954
989
  */
955
990
  export function updateCellToFocusFromPrev(state) {
956
991
  if (
@@ -958,7 +993,7 @@ export function updateCellToFocusFromPrev(state) {
958
993
  state.cellToFocusNext &&
959
994
  stillValidActiveCell(state)
960
995
  ) {
961
- // if the previous focus is there and valid, don't set the prevActiveFocusedCell
996
+ // If the previous focus is there and valid, don't set the prevActiveFocusedCell.
962
997
  state.cellToFocusNext = null;
963
998
  }
964
999
  }
@@ -974,13 +1009,12 @@ export function resetCellToFocusFromPrev(state) {
974
1009
  /**
975
1010
  * It adds and the focus classes to the th/td or div[role=gridcell/rowheader].
976
1011
  *
977
- * @param {node} template - the custom element template `this.template`
978
- * @param {object} state - datatable state
1012
+ * @param {node} template - The custom element template `this.template`
1013
+ * @param {object} state - The datatable state
979
1014
  */
980
1015
  export function addFocusStylesToActiveCell(template, state) {
981
- const cellElement = getActiveCellElement(template, state);
982
1016
  state.activeCell.focused = true;
983
-
1017
+ const cellElement = getActiveCellElement(template, state);
984
1018
  if (cellElement) {
985
1019
  cellElement.parentElement.classList.add(FOCUS_CLASS);
986
1020
  }
@@ -996,13 +1030,13 @@ export function addFocusStylesToActiveCell(template, state) {
996
1030
  */
997
1031
  function setFocusActiveRow(template, state) {
998
1032
  const { rowIndex } = getIndexesActiveCell(state);
999
- const row = getActiveCellRow(template, state);
1033
+ const rowElement = getActiveCellRowElement(template, state);
1000
1034
 
1001
- updateTabIndexRow(state, rowIndex);
1035
+ updateRowTabIndex(state, rowIndex);
1002
1036
  // eslint-disable-next-line @lwc/lwc/no-async-operation
1003
1037
  setTimeout(() => {
1004
- row.focus({ preventScroll: true });
1005
- updateScrollTop(state, template, row);
1038
+ rowElement.focus({ preventScroll: true });
1039
+ updateScrollTop(state, template, rowElement);
1006
1040
  }, 0);
1007
1041
  }
1008
1042
 
@@ -1010,20 +1044,20 @@ function setFocusActiveRow(template, state) {
1010
1044
  * It blurs the active row, this operation implies multiple changes
1011
1045
  * - blur the active row
1012
1046
  * - update the tabindex to -1
1013
- * @param {node} template - the custom element root `this.template`
1014
- * @param {object} state - datatable state
1047
+ * @param {node} template - The custom element root `this.template`
1048
+ * @param {object} state - The datatable state
1015
1049
  */
1016
1050
  function setBlurActiveRow(template, state) {
1017
1051
  if (state.activeCell) {
1018
1052
  const { rowIndex } = getIndexesActiveCell(state);
1019
1053
  // eslint-disable-next-line @lwc/lwc/no-async-operation
1020
1054
  setTimeout(() => {
1021
- const row = getActiveCellRow(template, state);
1055
+ const row = getActiveCellRowElement(template, state);
1022
1056
  if (document.activeElement === row) {
1023
1057
  row.blur();
1024
1058
  }
1025
1059
  }, 0);
1026
- updateTabIndexRow(state, rowIndex, -1);
1060
+ updateRowTabIndex(state, rowIndex, -1);
1027
1061
  }
1028
1062
  }
1029
1063
 
@@ -1057,18 +1091,23 @@ export function datatableHasFocus(state, template) {
1057
1091
 
1058
1092
  function isFocusInside(currentTarget) {
1059
1093
  const activeElements = getShadowActiveElements();
1060
- return activeElements.some((element) => {
1061
- return currentTarget.contains(element);
1062
- });
1094
+ for (let i = 0, { length } = activeElements; i < length; i += 1) {
1095
+ if (currentTarget.contains(activeElements[i])) {
1096
+ return true;
1097
+ }
1098
+ }
1099
+ return false;
1063
1100
  }
1064
1101
 
1065
1102
  export function handleDatatableFocusIn(event) {
1066
1103
  const { state } = this;
1104
+
1105
+ // Untracked state change.
1067
1106
  state.isExitingActionMode = false;
1068
1107
 
1069
1108
  // workaround for delegatesFocus issue that focusin is called when not supposed to W-6220418
1070
1109
  if (isFocusInside(event.currentTarget)) {
1071
- if (!state.rowMode && state.activeCell) {
1110
+ if (state.activeCell && !state.rowMode) {
1072
1111
  state.activeCell.focused = true;
1073
1112
  const cellElement = getActiveCellElement(this.template, state);
1074
1113
  // we need to check because of the tree,
@@ -1085,17 +1124,17 @@ export function handleDatatableFocusIn(event) {
1085
1124
 
1086
1125
  export function handleDatatableFocusOut(event) {
1087
1126
  const { state } = this;
1127
+ const { currentTarget, relatedTarget } = event;
1128
+ const containsRelatedTarget =
1129
+ relatedTarget && currentTarget.contains(relatedTarget);
1088
1130
  // workarounds for delegatesFocus issues
1089
1131
  if (
1090
1132
  // needed for initial focus where relatedTarget is empty
1091
- !event.relatedTarget ||
1133
+ !relatedTarget ||
1092
1134
  // needed when clicked outside
1093
- (event.relatedTarget &&
1094
- !event.currentTarget.contains(event.relatedTarget)) ||
1135
+ (relatedTarget && !containsRelatedTarget) ||
1095
1136
  // needed when datatable leaves focus and related target is still within datatable W-6185154
1096
- (event.relatedTarget &&
1097
- event.currentTarget.contains(event.relatedTarget) &&
1098
- state.isExitingActionMode)
1137
+ (relatedTarget && containsRelatedTarget && state.isExitingActionMode)
1099
1138
  ) {
1100
1139
  if (state.activeCell && !state.rowMode) {
1101
1140
  const cellElement = getActiveCellElement(this.template, state);
@@ -1137,10 +1176,9 @@ function resetCellClickedForFocus(state) {
1137
1176
  * @param {number} colIndex - the column index
1138
1177
  * @param {number} [index = 0] - the value for the tabindex
1139
1178
  */
1140
- export function updateTabIndex(state, rowIndex, colIndex, index = 0) {
1179
+ export function updateCellTabIndex(state, rowIndex, colIndex, index = 0) {
1141
1180
  if (isHeaderRow(rowIndex)) {
1142
- const { columns } = state;
1143
- columns[colIndex].tabIndex = index;
1181
+ state.columns[colIndex].tabIndex = index;
1144
1182
  } else {
1145
1183
  state.rows[rowIndex].cells[colIndex].tabIndex = index;
1146
1184
  }
@@ -1155,7 +1193,7 @@ export function updateTabIndex(state, rowIndex, colIndex, index = 0) {
1155
1193
  * @param {number} rowIndex - the row index
1156
1194
  * @param {number} [index = 0] - the value for the tabindex
1157
1195
  */
1158
- export function updateTabIndexRow(state, rowIndex, index = 0) {
1196
+ export function updateRowTabIndex(state, rowIndex, index = 0) {
1159
1197
  if (!isHeaderRow(rowIndex)) {
1160
1198
  // TODO what to do when rowIndex is header row
1161
1199
  state.rows[rowIndex].tabIndex = index;
@@ -1165,16 +1203,15 @@ export function updateTabIndexRow(state, rowIndex, index = 0) {
1165
1203
  * It update the tabindex for the current activeCell.
1166
1204
  * @param {object} state - datatable state
1167
1205
  * @param {number} [index = 0] - the value for the tabindex
1168
- * @returns {object} state - mutated state
1169
1206
  */
1170
1207
  export function updateTabIndexActiveCell(state, index = 0) {
1171
1208
  if (state.activeCell && !stillValidActiveCell(state)) {
1209
+ // Untracked state change.
1172
1210
  syncActiveCell(state);
1173
1211
  }
1174
-
1175
- // we need to check again because maybe there is no active cell after sync
1212
+ // Tracked state change.
1213
+ // We need to check again because maybe there is no active cell after sync.
1176
1214
  updateActiveCellTabIndexAfterSync(state, index);
1177
- return state;
1178
1215
  }
1179
1216
 
1180
1217
  /**
@@ -1182,17 +1219,18 @@ export function updateTabIndexActiveCell(state, index = 0) {
1182
1219
  * This happens in rowMode of NAVIGATION_MODE
1183
1220
  * @param {object} state - datatable state
1184
1221
  * @param {number} [index = 0] - the value for the tabindex
1185
- * @returns {object} state - mutated state
1222
+ * @returns {object} state - The datatable state
1186
1223
  */
1187
1224
  export function updateTabIndexActiveRow(state, index = 0) {
1188
1225
  if (state.activeCell && !stillValidActiveCell(state)) {
1226
+ // Untracked state change.
1189
1227
  syncActiveCell(state);
1190
1228
  }
1191
-
1192
- // we need to check again because maybe there is no active cell after sync
1229
+ // Tracked state change.
1230
+ // We need to check again because maybe there is no active cell after sync.
1193
1231
  if (state.activeCell && isRowNavigationMode(state)) {
1194
1232
  const { rowIndex } = getIndexesActiveCell(state);
1195
- updateTabIndexRow(state, rowIndex, index);
1233
+ updateRowTabIndex(state, rowIndex, index);
1196
1234
  }
1197
1235
  return state;
1198
1236
  }
@@ -1239,28 +1277,25 @@ function getPrevColumnIndex(colIndex) {
1239
1277
  }
1240
1278
 
1241
1279
  function getNextIndexRight(state, colIndex) {
1242
- if (isRTL()) {
1243
- return getPrevColumnIndex(colIndex);
1244
- }
1245
-
1246
- return getNextColumnIndex(state.columns.length, colIndex);
1280
+ return isRTL()
1281
+ ? getPrevColumnIndex(colIndex)
1282
+ : getNextColumnIndex(state.columns.length, colIndex);
1247
1283
  }
1248
1284
 
1249
1285
  function getNextIndexLeft(state, colIndex) {
1250
- if (isRTL()) {
1251
- return getNextColumnIndex(state.columns.length, colIndex);
1252
- }
1253
- return getPrevColumnIndex(colIndex);
1286
+ return isRTL()
1287
+ ? getNextColumnIndex(state.columns.length, colIndex)
1288
+ : getPrevColumnIndex(colIndex);
1254
1289
  }
1255
1290
 
1256
1291
  function getNextIndexUpWrapped(state, rowIndex) {
1257
- const rowsCount = state.rows.length;
1258
- return rowIndex === 0 ? -1 : rowIndex === -1 ? rowsCount - 1 : rowIndex - 1;
1292
+ const { length: rowsCount } = state.rows;
1293
+ return rowIndex === -1 ? rowsCount - 1 : rowIndex - 1;
1259
1294
  }
1260
1295
 
1261
1296
  function getNextIndexDownWrapped(state, rowIndex) {
1262
- const rowsCount = state.rows.length;
1263
- return rowIndex + 1 < rowsCount ? rowIndex + 1 : -1;
1297
+ const { length: rowCount } = state.rows;
1298
+ return rowIndex + 1 < rowCount ? rowIndex + 1 : -1;
1264
1299
  }
1265
1300
 
1266
1301
  /***************************** ROW NAVIGATION MODE *****************************/
@@ -1292,7 +1327,6 @@ export function unsetRowNavigationMode(state) {
1292
1327
  * if rowMode is true and it has tree data, keep it true
1293
1328
  * @param {boolean} hadTreeDataTypePreviously - state object
1294
1329
  * @param {object} state - state object
1295
- * @returns {object} state - mutated state
1296
1330
  */
1297
1331
  export function updateRowNavigationMode(hadTreeDataTypePreviously, state) {
1298
1332
  if (!hasTreeDataType(state)) {
@@ -1300,7 +1334,6 @@ export function updateRowNavigationMode(hadTreeDataTypePreviously, state) {
1300
1334
  } else if (state.rowMode === false && !hadTreeDataTypePreviously) {
1301
1335
  state.rowMode = true;
1302
1336
  }
1303
- return state;
1304
1337
  }
1305
1338
 
1306
1339
  /***************************** HELPER FUNCTIONS *****************************/
@@ -1316,12 +1349,12 @@ function isHeaderRow(rowIndex) {
1316
1349
  return rowIndex === -1;
1317
1350
  }
1318
1351
 
1319
- export function getDataRow(rowKeyValue) {
1352
+ export function getRowDataSelector(rowKeyValue) {
1320
1353
  return `[data-row-key-value="${escapeDoubleQuotes(rowKeyValue)}"]`;
1321
1354
  }
1322
1355
 
1323
1356
  export function getCellElementByKeys(template, rowKeyValue, colKeyValue) {
1324
- const selector = `${getDataRow(
1357
+ const selector = `${getRowDataSelector(
1325
1358
  rowKeyValue
1326
1359
  )} [data-col-key-value="${escapeDoubleQuotes(
1327
1360
  colKeyValue
@@ -1329,18 +1362,18 @@ export function getCellElementByKeys(template, rowKeyValue, colKeyValue) {
1329
1362
  return template.querySelector(selector);
1330
1363
  }
1331
1364
 
1332
- function getActiveCellRow(template, state) {
1333
- if (state.activeCell) {
1334
- const { rowKeyValue } = state.activeCell;
1335
- const selector = getDataRow(rowKeyValue);
1365
+ function getActiveCellRowElement(template, state) {
1366
+ const { activeCell } = state;
1367
+ if (activeCell) {
1368
+ const selector = getRowDataSelector(activeCell.rowKeyValue);
1336
1369
  return template.querySelector(selector);
1337
1370
  }
1338
1371
  return null;
1339
1372
  }
1340
1373
 
1341
- export function getRowParent(state, rowLevel, rowIndex) {
1374
+ export function getRowParentIndex(state, rowLevel, rowIndex) {
1342
1375
  const parentIndex = rowIndex - 1;
1343
- const rows = state.rows;
1376
+ const { rows } = state;
1344
1377
  for (let i = parentIndex; i >= 0; i--) {
1345
1378
  if (rows[i].level === rowLevel - 1) {
1346
1379
  return i;
@@ -1389,10 +1422,9 @@ function scrollToCell(state, template, rowIndex) {
1389
1422
  }
1390
1423
 
1391
1424
  export function isActiveCellEditable(state) {
1392
- const { activeCell, rows, columns } = state;
1393
- if (activeCell) {
1425
+ if (state.activeCell) {
1394
1426
  const { rowIndex, colIndex } = getIndexesActiveCell(state);
1395
- return isCellEditable(rows[rowIndex], columns[colIndex]);
1427
+ return isCellEditable(state.rows[rowIndex], state.columns[colIndex]);
1396
1428
  }
1397
1429
  return false;
1398
1430
  }
@@ -1408,16 +1440,20 @@ export function isValidCell(state, rowKeyValue, colKeyValue) {
1408
1440
  }
1409
1441
 
1410
1442
  function isActiveCellValid(state) {
1411
- if (state.activeCell) {
1412
- const { rowKeyValue, colKeyValue } = state.activeCell;
1413
- return isValidCell(state, rowKeyValue, colKeyValue);
1443
+ const { activeCell } = state;
1444
+ if (activeCell) {
1445
+ return isValidCell(
1446
+ state,
1447
+ activeCell.rowKeyValue,
1448
+ activeCell.colKeyValue
1449
+ );
1414
1450
  }
1415
1451
  return false;
1416
1452
  }
1417
1453
 
1418
- export function updateActiveCellTabIndexAfterSync(state, index = 0) {
1454
+ export function updateActiveCellTabIndexAfterSync(state, tabIndex = 0) {
1419
1455
  if (state.activeCell && !isRowNavigationMode(state)) {
1420
1456
  const { rowIndex, colIndex } = getIndexesActiveCell(state);
1421
- updateTabIndex(state, rowIndex, colIndex, index);
1457
+ updateCellTabIndex(state, rowIndex, colIndex, tabIndex);
1422
1458
  }
1423
1459
  }