lightning-base-components 1.14.3-alpha → 1.14.4-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.
@@ -1356,6 +1356,7 @@
1356
1356
  "industriesIdentityVerificationApi": {},
1357
1357
  "industriesInterestTaggingApi": {},
1358
1358
  "industriesLoyaltyEngineApi": {},
1359
+ "industriesPublicSectorApi": {},
1359
1360
  "industriesRcgTenantmanagementApi": {},
1360
1361
  "industriesRuleBuilderApi": {},
1361
1362
  "industriesSustainabilityRecalculateApi": {},
@@ -3413,6 +3414,28 @@
3413
3414
  "uiRelatedListApi": {
3414
3415
  "minVersion": "53.0"
3415
3416
  },
3417
+ "unstable_analyticsDataServiceApi": {},
3418
+ "unstable_analyticsWaveApi": {},
3419
+ "unstable_cmsAuthoringApi": {},
3420
+ "unstable_cmsDeliveryApi": {},
3421
+ "unstable_cmsTypeApi": {},
3422
+ "unstable_commerceApi": {},
3423
+ "unstable_communityNavigationMenuApi": {},
3424
+ "unstable_communityRecordSeoPropertiesApi": {},
3425
+ "unstable_communitySitesSearchApi": {},
3426
+ "unstable_experienceMarketingIntegrationApi": {},
3427
+ "unstable_industriesCibApi": {},
3428
+ "unstable_industriesDecisionMatrixDesignerApi": {},
3429
+ "unstable_industriesExplainabilityApi": {},
3430
+ "unstable_industriesHealthcloudHpiApi": {},
3431
+ "unstable_industriesInterestTaggingApi": {},
3432
+ "unstable_industriesLoyaltyEngineApi": {},
3433
+ "unstable_industriesPublicSectorApi": {},
3434
+ "unstable_industriesRcgTenantmanagementApi": {},
3435
+ "unstable_industriesRuleBuilderApi": {},
3436
+ "unstable_platformAdminSuccessGuidanceApi": {},
3437
+ "unstable_platformInteractionOrchestratorApi": {},
3438
+ "unstable_platformScaleCenterApi": {},
3416
3439
  "unstable_uiActionsApi": {},
3417
3440
  "unstable_uiAppsApi": {},
3418
3441
  "unstable_uiDuplicatesApi": {},
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "lightning-base-components",
3
- "version": "1.14.3-alpha",
3
+ "version": "1.14.4-alpha",
4
4
  "engines": {
5
- "node": ">=12.18.3"
5
+ "node": ">=14.16.0"
6
6
  },
7
7
  "files": [
8
8
  "external",
@@ -958,8 +958,8 @@
958
958
  "path": "scopedImports/@salesforce-label-LightningRating.nStars.js"
959
959
  },
960
960
  {
961
- "name": "@salesforce/label/LightningModalBase.close",
962
- "path": "scopedImports/@salesforce-label-LightningModalBase.close.js"
961
+ "name": "@salesforce/label/LightningModalBase.cancelandclose",
962
+ "path": "scopedImports/@salesforce-label-LightningModalBase.cancelandclose.js"
963
963
  },
964
964
  {
965
965
  "name": "@salesforce/label/LightningModalBase.waitstate",
@@ -0,0 +1 @@
1
+ export default 'Cancel and close';
@@ -47,6 +47,15 @@ describe('AriaObserver', () => {
47
47
  expect(testElement.labelContent).toEqual('Foo\nBar');
48
48
  });
49
49
 
50
+ it('should work with label ids appearing in the opposite of document order', async () => {
51
+ const container = createTestElement();
52
+ container.updateAriaLabelledby('alt-label-id id-label');
53
+ await Promise.resolve();
54
+
55
+ const testElement = container.testElement;
56
+ expect(testElement.labelContent).toEqual('Bar\nFoo');
57
+ });
58
+
50
59
  it('should update the internal label content when external content changes', async () => {
51
60
  const container = createTestElement();
52
61
  container.updateLabelContent();
@@ -13,11 +13,13 @@ function getAttr(elm, attr) {
13
13
  return elm.getAttribute(attr);
14
14
  }
15
15
 
16
- function extractElements(root, selector) {
17
- if (typeof selector !== 'string' || selector === '') {
16
+ function extractElements(root, ids) {
17
+ if (typeof ids !== 'string' || ids === '') {
18
18
  return [];
19
19
  }
20
- return [].slice.call(root.querySelectorAll(selector));
20
+ // We must query the elements in the order of ids, so that
21
+ // the content will be extracted in the correct order.
22
+ return splitIds(ids).map((id) => root.querySelector(`#${id}`));
21
23
  }
22
24
 
23
25
  function extractContent(elements) {
@@ -113,38 +115,28 @@ export default class AriaObserver {
113
115
  connect({ targetSelector, attribute, id, ids }) {
114
116
  ids = ids || id;
115
117
 
116
- let attrState = this.state[attribute];
117
- if (attrState) {
118
- // note: we don't support linking to a different targetSelector, attribute
119
- if (!this.isNative) {
120
- const elm = this.template.querySelector(
121
- attrState.innerSelector
122
- );
123
- if (elm) {
124
- // removing the old ids if possible before setting the new ones
125
- removeAriaRefWhenPossible(elm, attribute, attrState.ids);
126
- }
127
- attrState.ids = ids;
118
+ this.state[attribute] = this.state[attribute] || {};
119
+ const attrState = this.state[attribute];
120
+
121
+ // note: we don't support linking to a different targetSelector
122
+ attrState.innerSelector = attrState.innerSelector || targetSelector;
123
+
124
+ // removing the old ids if possible before setting the new ones
125
+ if (!this.isNative && attrState.ids) {
126
+ const elm = this.template.querySelector(attrState.innerSelector);
127
+ if (elm) {
128
+ removeAriaRefWhenPossible(elm, attribute, attrState.ids);
128
129
  }
129
- } else {
130
- attrState = this.state[attribute] = {
131
- ids,
132
- innerSelector: targetSelector,
133
- };
134
130
  }
135
- if (this.isNative) {
136
- attrState.outerSelector = (ids + '')
137
- .trim()
138
- .split(/\s+/)
139
- .map((ref) => `#${ref}`)
140
- .join(',');
141
131
 
132
+ attrState.ids = ids;
133
+
134
+ if (this.isNative && !attrState.placeholder) {
142
135
  // create placeholder element for copied content
143
- if (!attrState.placeholder) {
144
- attrState.placeholder = document.createElement('span');
145
- attrState.placeholder.id = `auto-link-${attribute}-${this.guid}`;
146
- }
136
+ attrState.placeholder = document.createElement('span');
137
+ attrState.placeholder.id = `auto-link-${attribute}-${this.guid}`;
147
138
  }
139
+
148
140
  if (this.component.isConnected) {
149
141
  this.privateUpdate(attribute);
150
142
  }
@@ -213,12 +205,9 @@ export default class AriaObserver {
213
205
  }
214
206
  let computedIds;
215
207
  if (this.isNative) {
216
- const { outerSelector, content, placeholder } =
217
- this.state[attrName];
208
+ const { ids, content, placeholder } = this.state[attrName];
218
209
 
219
- const newContent = extractContent(
220
- extractElements(this.root, outerSelector)
221
- );
210
+ const newContent = extractContent(extractElements(this.root, ids));
222
211
  if (content !== newContent) {
223
212
  this.state[attrName].content = placeholder.textContent =
224
213
  newContent;
@@ -81,6 +81,8 @@ import {
81
81
  addFocusStylesToActiveCell,
82
82
  refocusCellElement,
83
83
  isCellElement,
84
+ getActiveCellElement,
85
+ FOCUS_CLASS,
84
86
  } from './keyboard';
85
87
  import {
86
88
  getRowNumberOffset,
@@ -135,11 +137,9 @@ import {
135
137
  import {
136
138
  isViewportRenderingEnabled,
137
139
  setViewportRendering,
138
- getDTRows,
139
- getDTRenderedRowCount,
140
- setDTRenderedRowCount,
141
140
  getDTWrapperHeight,
142
- normalizeVirtualization,
141
+ setFirstVisibleIndex,
142
+ setVirtualize,
143
143
  RenderManager,
144
144
  DEFAULT_ROW_HEIGHT,
145
145
  } from './renderManager';
@@ -200,14 +200,11 @@ export default class LightningDatatable extends LightningElement {
200
200
  _customerSelectedRows = null;
201
201
  _datatableId = generateUniqueId('lgt-datatable');
202
202
  _draftValues = [];
203
- _firstVisibleIndex = 0; // first row that should be visible in viewport, used for virtualization
204
203
  _isResizing = false; // Whether resizing is in progress
205
204
  _privateTypes = {};
206
205
  _privateWidthObserver = null; // Instance of LightningDatatableResizeObserver
207
206
  _renderMode = 'table';
208
- _renderedRowCount = 0;
209
207
  _suppressBottomBar = false;
210
- _virtualize = '';
211
208
 
212
209
  /************************* PUBLIC PROPERTIES *************************/
213
210
 
@@ -519,24 +516,17 @@ export default class LightningDatatable extends LightningElement {
519
516
 
520
517
  set renderConfig(value) {
521
518
  if (typeof value === 'object' && !isIE11) {
522
- const { viewportRendering, virtualize } = value;
523
- setViewportRendering(this.state, viewportRendering);
524
- if (this.state.renderModeRoleBased) {
525
- this._virtualize = normalizeVirtualization(virtualize);
526
- }
519
+ setViewportRendering(this.state, value.viewportRendering);
527
520
 
528
521
  this._renderManager.configure(
529
- this.getRows,
522
+ this.state,
530
523
  this.getWrapperHeight,
531
- this.getRenderedRowCount,
532
- this.setRenderedRowCount,
533
524
  value
534
525
  );
535
526
  // if renderConfig already exists, update rendering
536
527
  if (this._renderConfig) {
537
528
  this._renderManager.updateViewportRendering(
538
- this.state.rows,
539
- this.setRenderedRowCount,
529
+ this.state,
540
530
  this.gridContainer,
541
531
  true
542
532
  );
@@ -565,6 +555,9 @@ export default class LightningDatatable extends LightningElement {
565
555
  validValues: ['default', 'role-based'],
566
556
  });
567
557
  this.state.renderModeRoleBased = this._renderMode === 'role-based';
558
+ if (this._renderConfig) {
559
+ setVirtualize(this.state, this._renderConfig.virtualize);
560
+ }
568
561
  updateCellClassForRoleBasedMode(this.state);
569
562
  }
570
563
 
@@ -804,16 +797,20 @@ export default class LightningDatatable extends LightningElement {
804
797
  */
805
798
  get computedTbodyStyle() {
806
799
  const style = [];
800
+ const { firstVisibleIndex, bufferSize, virtualize, rows } = this.state;
807
801
  if (
808
802
  hasRowNumberColumn(this.state) &&
809
803
  getRowNumberOffset(this.state) >= 0
810
804
  ) {
811
- style.push(
812
- 'counter-reset: row-number ' + getRowNumberOffset(this.state)
805
+ const firstRenderedRow = Math.max(
806
+ firstVisibleIndex - bufferSize,
807
+ 0
813
808
  );
809
+ const rowNumber = firstRenderedRow + getRowNumberOffset(this.state);
810
+ style.push(`counter-reset: row-number ${rowNumber}`);
814
811
  }
815
- if (this._virtualize) {
816
- const length = this.state.rows.length;
812
+ if (virtualize) {
813
+ const length = rows.length;
817
814
  style.push(
818
815
  'position: relative',
819
816
  `height:${length * DEFAULT_ROW_HEIGHT}px`
@@ -938,18 +935,16 @@ export default class LightningDatatable extends LightningElement {
938
935
  }
939
936
 
940
937
  get renderedRows() {
941
- if (this._virtualize) {
938
+ const { virtualize, rows, renderedRowCount } = this.state;
939
+ if (virtualize) {
942
940
  const { firstIndex, lastIndex } =
943
- this._renderManager.getRenderedRange(
944
- this._firstVisibleIndex,
945
- this._renderedRowCount
946
- );
947
- return this.state.rows.slice(firstIndex, lastIndex);
941
+ this._renderManager.getRenderedRange(this.state);
942
+ return rows.slice(firstIndex, lastIndex);
948
943
  }
949
944
  if (this.viewportRendering && !isIE11) {
950
- return this.state.rows.slice(0, this._renderedRowCount);
945
+ return rows.slice(0, renderedRowCount);
951
946
  }
952
- return this.state.rows;
947
+ return rows;
953
948
  }
954
949
 
955
950
  get showSelectAllCheckbox() {
@@ -985,9 +980,6 @@ export default class LightningDatatable extends LightningElement {
985
980
  this.updateRowsAndCellIndexes = updateRowsAndCellIndexes.bind(this);
986
981
 
987
982
  this._renderManager = new RenderManager();
988
- this.getRenderedRowCount = getDTRenderedRowCount.bind(this);
989
- this.setRenderedRowCount = setDTRenderedRowCount.bind(this);
990
- this.getRows = getDTRows.bind(this);
991
983
  this.getWrapperHeight = getDTWrapperHeight.bind(this);
992
984
  }
993
985
 
@@ -1147,8 +1139,19 @@ export default class LightningDatatable extends LightningElement {
1147
1139
  this._customerSelectedRows = null;
1148
1140
  // set the previous focused cell to null after render is done
1149
1141
  resetCellToFocusFromPrev(state);
1142
+ // reset focus styles on re-render
1143
+ if (state.activeCell && state.activeCell.focused) {
1144
+ const cellElement = getActiveCellElement(template, state);
1145
+ if (
1146
+ cellElement &&
1147
+ cellElement.parentElement &&
1148
+ !cellElement.parentElement.classList.contains(FOCUS_CLASS)
1149
+ ) {
1150
+ setFocusActiveCell(template, state, null, null, false);
1151
+ }
1152
+ }
1150
1153
 
1151
- if (this.viewportRendering || this._virtualize) {
1154
+ if (this.viewportRendering || state.virtualize) {
1152
1155
  const resizeTarget = this.template.querySelector(
1153
1156
  'div.dt-outer-container'
1154
1157
  );
@@ -1159,10 +1162,9 @@ export default class LightningDatatable extends LightningElement {
1159
1162
  // Reset the row count if we already had one before updating the wrapper height.
1160
1163
  // This can happen if the number of rows was calculated before the datatable
1161
1164
  // was rendered.
1162
- if (this._renderedRowCount) {
1165
+ if (this.state.renderedRowCount) {
1163
1166
  this._renderManager.updateViewportRendering(
1164
- this.state.rows,
1165
- this.setRenderedRowCount,
1167
+ this.state,
1166
1168
  this.gridContainer,
1167
1169
  true
1168
1170
  );
@@ -1248,16 +1250,10 @@ export default class LightningDatatable extends LightningElement {
1248
1250
  }
1249
1251
 
1250
1252
  handleInlineEditPanelScroll.call(this, event);
1251
- if (this._virtualize) {
1252
- this._firstVisibleIndex =
1253
- this._renderManager.getFirstVisibleIndex(event);
1253
+ if (this.state.virtualize) {
1254
+ setFirstVisibleIndex(this.state, event.target.scrollTop);
1254
1255
  } else if (this.viewportRendering) {
1255
- this._renderManager.handleScroll(
1256
- this.state.rows,
1257
- this._renderedRowCount,
1258
- this.setRenderedRowCount,
1259
- event
1260
- );
1256
+ this._renderManager.handleScroll(this.state, event);
1261
1257
  }
1262
1258
  }
1263
1259
 
@@ -1533,10 +1529,9 @@ export default class LightningDatatable extends LightningElement {
1533
1529
 
1534
1530
  this.updateRowsAndCellIndexes(state);
1535
1531
 
1536
- if (this.viewportRendering || this._virtualize) {
1532
+ if (this.viewportRendering || state.virtualize) {
1537
1533
  this._renderManager.updateViewportRendering(
1538
- this.state.rows,
1539
- this.setRenderedRowCount,
1534
+ this.state,
1540
1535
  this.gridContainer
1541
1536
  );
1542
1537
  }
@@ -1578,7 +1573,7 @@ export default class LightningDatatable extends LightningElement {
1578
1573
  setDirtyValues(state, this._draftValues);
1579
1574
  updateRowNavigationMode(hadTreeDataTypePreviously, state);
1580
1575
  state.headerIndexes = generateHeaderIndexes(getColumns(state));
1581
- // Updates state.wrapText and when isWrapableType, sets internal header actions
1576
+ // Updates state.wrapText and when isWrappableType, sets internal header actions
1582
1577
  updateHeaderActions(state);
1583
1578
  this.updateRowsAndCellIndexes(state);
1584
1579
  updateBulkSelectionState(state);
@@ -1,34 +1,67 @@
1
1
  import { normalizeBoolean } from 'lightning/utilsPrivate';
2
2
  import { getScrollOffsetFromTableEnd, isNonNegativeInteger } from './utils';
3
3
 
4
- const SCROLLABLE_CONTAINER_SEL = '.slds-scrollable_y';
5
4
  const SCROLL_ALLOWANCE = 2;
5
+ export const DEFAULT_LOAD_MORE_OFFSET = 20;
6
6
 
7
- export function getInfiniteLoadingDefaultState() {
8
- return {
9
- enableInfiniteLoading: false,
10
- loadMoreOffset: 20,
11
- isLoading: false,
12
- };
13
- }
7
+ /*********************** STATE MANAGEMENT ************************/
14
8
 
9
+ /**
10
+ * Returns whether the datatable is in a loading state
11
+ *
12
+ * @param {Object} state The datatable state object
13
+ * @returns {Boolean} The loading state
14
+ */
15
15
  export function isLoading(state) {
16
16
  return state.isLoading;
17
17
  }
18
+
19
+ /**
20
+ * Sets the loading state of the datatable
21
+ *
22
+ * @param {Object} state The datatable state object
23
+ * @param {Boolean} value The loading state to set
24
+ */
18
25
  export function setLoading(state, value) {
19
26
  state.isLoading = normalizeBoolean(value);
20
27
  }
21
28
 
29
+ /**
30
+ * Returns whether infinite loading is enabled on the datatable
31
+ *
32
+ * @param {Object} state The datatable state object
33
+ * @returns {Boolean} The infinite loading state
34
+ */
22
35
  export function isInfiniteLoadingEnabled(state) {
23
36
  return state.enableInfiniteLoading;
24
37
  }
38
+
39
+ /**
40
+ * Sets the infinite loading option on the datatable
41
+ *
42
+ * @param {Object} state The datatable state object
43
+ * @param {Boolean} value The infinite loading state to set
44
+ */
25
45
  export function setInfiniteLoading(state, value) {
26
46
  state.enableInfiniteLoading = normalizeBoolean(value);
27
47
  }
28
48
 
49
+ /**
50
+ * Returns the load more offset
51
+ *
52
+ * @param {Object} state The datatable state object
53
+ * @returns {Number} The currently configured load more offset value
54
+ */
29
55
  export function getLoadMoreOffset(state) {
30
56
  return state.loadMoreOffset;
31
57
  }
58
+
59
+ /**
60
+ * Sets the load more offset value. Must be a number >= 0.
61
+ *
62
+ * @param {Object} state The datatable state object
63
+ * @param {Boolean} value The load more offset value to set
64
+ */
32
65
  export function setLoadMoreOffset(state, value) {
33
66
  if (!isNonNegativeInteger(value)) {
34
67
  // eslint-disable-next-line no-console
@@ -40,9 +73,17 @@ export function setLoadMoreOffset(state, value) {
40
73
 
41
74
  state.loadMoreOffset = isNonNegativeInteger(value)
42
75
  ? parseInt(value, 10)
43
- : getInfiniteLoadingDefaultState().loadMoreOffset;
76
+ : DEFAULT_LOAD_MORE_OFFSET;
44
77
  }
45
78
 
79
+ /************************** PUBLIC METHODS ***************************/
80
+
81
+ /**
82
+ * Checks whether the datatable should begin loading more content
83
+ * and then dispatches the `loadmore` event indicating that directive.
84
+ *
85
+ * @param {Event} event
86
+ */
46
87
  export function handleLoadMoreCheck(event) {
47
88
  if (isLoading(this.state)) {
48
89
  return;
@@ -60,24 +101,13 @@ export function handleLoadMoreCheck(event) {
60
101
  }
61
102
  }
62
103
 
63
- function isScrollable(element) {
64
- // scrollHeight should be greater than clientHeight by some allowance
65
- return (
66
- element &&
67
- element.scrollHeight > element.clientHeight + SCROLL_ALLOWANCE
68
- );
69
- }
70
-
71
- function isScrollerVisible(elem) {
72
- return (
73
- elem && !!(elem.offsetParent || elem.offsetHeight || elem.offsetWidth)
74
- );
75
- }
76
-
77
- function hasData(root) {
78
- return root.querySelectorAll('tbody > tr').length > 0;
79
- }
80
-
104
+ /**
105
+ * Determines whether or not to prefetch data. If so,
106
+ * dispatches the `loadmore` event.
107
+ *
108
+ * @param {Object} root The datatable
109
+ * @param {Object} state The datatable state object
110
+ */
81
111
  export function handlePrefetch(root, state) {
82
112
  if (
83
113
  !isInfiniteLoadingEnabled(state) ||
@@ -91,9 +121,51 @@ export function handlePrefetch(root, state) {
91
121
  return;
92
122
  }
93
123
 
94
- const elem = root.querySelector(SCROLLABLE_CONTAINER_SEL);
124
+ const elem = root.querySelector('.slds-scrollable_y');
95
125
 
96
126
  if (isScrollerVisible(elem) && !isScrollable(elem)) {
97
127
  this.dispatchEvent(new CustomEvent('loadmore'));
98
128
  }
99
129
  }
130
+
131
+ /************************** PRIVATE METHODS ***************************/
132
+
133
+ /**
134
+ * Determines if a DOM element is scrollable
135
+ *
136
+ * @param {Element} element The DOM element to check
137
+ * @returns {Boolean} Whether or not the element is scrollable
138
+ */
139
+ function isScrollable(element) {
140
+ // scrollHeight should be greater than clientHeight by some allowance
141
+ return (
142
+ element &&
143
+ element.scrollHeight > element.clientHeight + SCROLL_ALLOWANCE
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Determines if a DOM element's scroll bars are visible
149
+ *
150
+ * @param {Element} element The DOM element to check
151
+ * @returns {Boolean} Whether or not the element's scroll bars are visible
152
+ */
153
+ function isScrollerVisible(element) {
154
+ return (
155
+ element &&
156
+ !!(element.offsetParent || element.offsetHeight || element.offsetWidth)
157
+ );
158
+ }
159
+
160
+ /**
161
+ * Determines if a root element has data
162
+ *
163
+ * @param {Element} root The parent element to check
164
+ * @returns {Boolean} Whether or not the element contains any data
165
+ */
166
+ function hasData(root) {
167
+ return (
168
+ root.querySelectorAll('tbody > tr, [role="rowgroup"] > [role="row"]')
169
+ .length > 0
170
+ );
171
+ }
@@ -8,14 +8,16 @@ import {
8
8
  reactToTabBackward,
9
9
  reactToTabForward,
10
10
  getActiveCellElement,
11
- getCellElementByIndexes,
12
11
  updateActiveCell,
12
+ isActiveCellEditable,
13
+ isValidCell,
13
14
  } from './keyboard';
14
15
  import {
15
16
  updateRowsAndCellIndexes,
16
17
  getRowByKey,
17
18
  getKeyField,
18
19
  getUserRowByCellKeys,
20
+ isCellEditable,
19
21
  } from './rows';
20
22
  import {
21
23
  getColumnIndexByColumnKey,
@@ -315,6 +317,11 @@ function openInlineEdit(dt, target) {
315
317
 
316
318
  const { rowKeyValue, colKeyValue } = target;
317
319
 
320
+ // ensure that focus remains on inline edit panel instead of active cell
321
+ if (state.activeCell) {
322
+ state.activeCell.focused = false;
323
+ }
324
+
318
325
  inlineEdit.isPanelVisible = true;
319
326
  inlineEdit.rowKeyValue = rowKeyValue;
320
327
  inlineEdit.colKeyValue = colKeyValue;
@@ -378,11 +385,7 @@ function openInlineEdit(dt, target) {
378
385
  export function openInlineEditOnActiveCell(dt) {
379
386
  const hasData = dt.state.data && dt.state.data.length > 0;
380
387
  if (hasData) {
381
- const activeCellElement = getActiveCellElement(dt.template, dt.state);
382
- const isEditable = activeCellElement.editable;
383
- if (isEditable) {
384
- setFocusAndOpenInlineEdit(dt, activeCellElement);
385
- } else {
388
+ if (!isActiveCellEditable(dt.state)) {
386
389
  const firstEditableCell = getFirstEditableCell(dt);
387
390
  if (firstEditableCell) {
388
391
  updateActiveCell(
@@ -390,8 +393,10 @@ export function openInlineEditOnActiveCell(dt) {
390
393
  firstEditableCell.rowKeyValue,
391
394
  firstEditableCell.colKeyValue
392
395
  );
393
- setFocusAndOpenInlineEdit(dt, firstEditableCell);
396
+ setFocusAndOpenInlineEdit(dt, dt.state.activeCell);
394
397
  }
398
+ } else {
399
+ setFocusAndOpenInlineEdit(dt, dt.state.activeCell);
395
400
  }
396
401
  }
397
402
  }
@@ -400,11 +405,11 @@ export function openInlineEditOnActiveCell(dt) {
400
405
  * Async function to await setting focus on an editable cell before opening inline-edit panel
401
406
  *
402
407
  * @param {Object} dt - The datatable instance
403
- * @param {Object} cell - editable cell to be focused before open inline-edit panel
404
408
  */
405
409
  // eslint-disable-next-line @lwc/lwc/no-async-await
406
- async function setFocusAndOpenInlineEdit(dt, cell) {
410
+ async function setFocusAndOpenInlineEdit(dt) {
407
411
  await setFocusActiveCell(dt.template, dt.state, 0);
412
+ const cell = getActiveCellElement(dt.template, dt.state);
408
413
  openInlineEdit(dt, cell);
409
414
  }
410
415
 
@@ -716,8 +721,8 @@ function resolveNestedTypeAttributesHelper(rowData, typeAttributesValue) {
716
721
  /************************** HELPER FUNCTIONS **************************/
717
722
 
718
723
  /**
719
- * Returns a reference to the first editable cell in the table. If no editable cells exist in the table
720
- * then undefined is returned.
724
+ * Returns the row and column keys of the first editable cell in the table.
725
+ * If no editable cells exist in the table then undefined is returned.
721
726
  *
722
727
  * @param {Object} dt - The datatable instance. Must be a truthy and valid datatable reference.
723
728
  */
@@ -732,18 +737,11 @@ function getFirstEditableCell(dt) {
732
737
  // Loop through the editable columns in order and examine the corresponding cells
733
738
  // in the current row for editability, returning the first such cell that is editable
734
739
  const editableColumn = editableColumns[i];
735
- const editableColumnIndex = getStateColumnIndex(
736
- dt.state,
737
- editableColumn.colKeyValue
738
- );
739
- const cell = getCellElementByIndexes(
740
- dt.template,
741
- rowIndex,
742
- editableColumnIndex,
743
- dt.state
744
- );
745
- if (cell.editable) {
746
- return cell;
740
+ if (isCellEditable(rows[rowIndex], editableColumn)) {
741
+ return {
742
+ rowKeyValue: rows[rowIndex].key,
743
+ colKeyValue: editableColumn.colKeyValue,
744
+ };
747
745
  }
748
746
  }
749
747
  }
@@ -768,13 +766,6 @@ function getCellValue(state, rowKeyValue, colKeyValue) {
768
766
  return row.cells[colIndex].value;
769
767
  }
770
768
 
771
- function isValidCell(state, rowKeyValue, colKeyValue) {
772
- const row = getRowByKey(state, rowKeyValue);
773
- const colIndex = getStateColumnIndex(state, colKeyValue);
774
-
775
- return row && row.cells[colIndex];
776
- }
777
-
778
769
  /**
779
770
  * Sets `aria-selected` to true on cells whose rows are selected
780
771
  * and are in the same column as the cell being currently edited