lightning-base-components 1.14.2-alpha → 1.14.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 (53) hide show
  1. package/metadata/raptor.json +1 -0
  2. package/package.json +17 -1
  3. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsPlural.js +1 -0
  4. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsSingular.js +1 -0
  5. package/scopedImports/@salesforce-label-LightningErrorMessage.validitySelectAtleastOne.js +1 -0
  6. package/scopedImports/@salesforce-label-LightningMap.titleWithAddress.js +1 -0
  7. package/src/lightning/ariaObserver/__docs__/ariaObserver.md +142 -0
  8. package/src/lightning/buttonMenu/keyboard.js +0 -10
  9. package/src/lightning/card/card.html +6 -0
  10. package/src/lightning/checkboxGroup/checkboxGroup.html +2 -2
  11. package/src/lightning/checkboxGroup/checkboxGroup.js +6 -1
  12. package/src/lightning/colorPickerCustom/colorPickerCustom.js +20 -1
  13. package/src/lightning/datatable/__docs__/datatable.md +55 -0
  14. package/src/lightning/datatable/__examples__/basic/basic.html +1 -1
  15. package/src/lightning/datatable/columns-shared.js +1 -1
  16. package/src/lightning/datatable/datatable.js +97 -24
  17. package/src/lightning/datatable/errors.js +20 -9
  18. package/src/lightning/datatable/headerActions.js +77 -49
  19. package/src/lightning/datatable/inlineEdit.js +505 -370
  20. package/src/lightning/datatable/inlineEditShared.js +24 -0
  21. package/src/lightning/datatable/keyboard.js +1 -1
  22. package/src/lightning/datatable/renderManager.js +241 -129
  23. package/src/lightning/datatable/rowLevelActions.js +17 -13
  24. package/src/lightning/datatable/rowNumber.js +54 -20
  25. package/src/lightning/datatable/rowSelection.js +760 -0
  26. package/src/lightning/datatable/rowSelectionShared.js +79 -0
  27. package/src/lightning/datatable/rows.js +16 -5
  28. package/src/lightning/datatable/state.js +10 -1
  29. package/src/lightning/datatable/templates/div/div.css +4 -0
  30. package/src/lightning/datatable/templates/div/div.html +1 -0
  31. package/src/lightning/datatable/utils.js +14 -0
  32. package/src/lightning/dualListbox/dualListbox.html +1 -1
  33. package/src/lightning/dualListbox/dualListbox.js +42 -0
  34. package/src/lightning/inputUtils/validity.js +12 -1
  35. package/src/lightning/pillContainer/__docs__/pillContainer.md +45 -1
  36. package/src/lightning/positionLibrary/positionLibrary.js +31 -43
  37. package/src/lightning/primitiveCellActions/primitiveCellActions.js +69 -12
  38. package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +13 -11
  39. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +13 -8
  40. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.html +17 -14
  41. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +167 -98
  42. package/src/lightning/primitiveDatatableIeditTypeFactory/primitiveDatatableIeditTypeFactory.js +94 -69
  43. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.html +4 -4
  44. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +4 -4
  45. package/src/lightning/primitiveHeaderActions/primitiveHeaderActions.js +99 -37
  46. package/src/lightning/progressIndicator/progressIndicator.js +1 -1
  47. package/src/lightning/progressStep/progressStep.js +1 -1
  48. package/src/lightning/staticMap/staticMap.html +1 -0
  49. package/src/lightning/staticMap/staticMap.js +39 -2
  50. package/src/lightning/utils/classSet.js +4 -1
  51. package/src/lightning/datatable/inlineEdit-shared.js +0 -14
  52. package/src/lightning/datatable/selector-shared.js +0 -38
  53. package/src/lightning/datatable/selector.js +0 -527
@@ -0,0 +1,79 @@
1
+ /**
2
+ * This file exists in order to get around circular dependencies.
3
+ * In this case, rowSelection.js has a dependency on rows.js;
4
+ * but rows.js also has a dependency on rowSelection.js for
5
+ * `isSelectedRow()` among others.
6
+ *
7
+ * We split out the functions that could cause circular dependencies with
8
+ * `rowSelection.js` into the `*Shared.js` files.
9
+ */
10
+
11
+ /**
12
+ * Returns whether or not the row is selected using the state and the rowKeyValue
13
+ * The state maintains the list of selected rows from which
14
+ * this value can be retrieved.
15
+ *
16
+ * @param {Object} state - datatable state object
17
+ * @param {String} rowKeyValue
18
+ * @returns
19
+ */
20
+ export function isSelectedRow(state, rowKeyValue) {
21
+ return !!state.selectedRowsKeys[rowKeyValue];
22
+ }
23
+
24
+ /**
25
+ * Returns whether the row (whose row key value is specified)
26
+ * should be disabled or not.
27
+ * Should not disable if the row is selected.
28
+ * If the particular row is not selected, the row should be disabled
29
+ * when max-row-selection > 1 and the selection limit is reached
30
+ *
31
+ * Note: Do not disable selection when max-row-selection = 1 and
32
+ * a row has been selected.
33
+ *
34
+ * @param {Object} state
35
+ * @param {String} rowKeyValue
36
+ * @returns {Boolean} whether the row should be disabled or not
37
+ */
38
+ export function isDisabledRow(state, rowKeyValue) {
39
+ if (!isSelectedRow(state, rowKeyValue)) {
40
+ const maxRowSelection = getMaxRowSelection(state);
41
+
42
+ // when selection is 1, we should not disable selection
43
+ return (
44
+ maxRowSelection !== 1 &&
45
+ getCurrentSelectionLength(state) === maxRowSelection
46
+ );
47
+ }
48
+
49
+ return false;
50
+ }
51
+
52
+ /**
53
+ * Returns which input type to use for row selection.
54
+ * If max-row-selection = 1, use radio buttons,
55
+ * otherwise, use checkboxes.
56
+ *
57
+ * @param {Object} state - datatable's state object
58
+ * @returns
59
+ */
60
+ export function getRowSelectionInputType(state) {
61
+ if (getMaxRowSelection(state) === 1) {
62
+ return 'radio';
63
+ }
64
+ return 'checkbox';
65
+ }
66
+
67
+ export function getMaxRowSelection(state) {
68
+ return state.maxRowSelection;
69
+ }
70
+
71
+ export function getCurrentSelectionLength(state) {
72
+ return getSelectedRowsKeys(state).length;
73
+ }
74
+
75
+ export function getSelectedRowsKeys(state) {
76
+ return Object.keys(state.selectedRowsKeys).filter(
77
+ (key) => state.selectedRowsKeys[key]
78
+ );
79
+ }
@@ -1,11 +1,11 @@
1
1
  import { assert } from 'lightning/utilsPrivate';
2
- import { classSet, isObjectLike } from './utils';
2
+ import { classSet, isObjectLike, styleToString } from './utils';
3
3
  import { isTreeType, getAttributesNames } from './types';
4
4
  import {
5
5
  isSelectedRow,
6
6
  isDisabledRow,
7
7
  getRowSelectionInputType,
8
- } from './selector-shared';
8
+ } from './rowSelectionShared';
9
9
  import { getTreeStateIndicatorFieldNames, getStateTreeColumn } from './tree';
10
10
  import {
11
11
  getColumns,
@@ -17,7 +17,8 @@ import {
17
17
  } from './columns';
18
18
  import { isRowNumberColumn, getRowNumberErrorColumnDef } from './rowNumber';
19
19
  import { getRowError } from './errors';
20
- import { getDirtyValue } from './inlineEdit-shared';
20
+ import { getDirtyValueFromCell } from './inlineEditShared';
21
+ import { DEFAULT_ROW_HEIGHT } from './renderManager';
21
22
 
22
23
  export function getData(state) {
23
24
  return state.data;
@@ -114,7 +115,7 @@ export function uniqueRowKeyGenerator(keyField) {
114
115
  * @param {object} state - the current datatable state
115
116
  */
116
117
  export function updateRowsAndCellIndexes() {
117
- const { state, privateTypes: types } = this;
118
+ const { state, privateTypes: types, _virtualize } = this;
118
119
  const { keyField, renderModeRoleBased } = state;
119
120
  const data = getData(state);
120
121
  const columns = getColumns(state);
@@ -141,11 +142,21 @@ export function updateRowsAndCellIndexes() {
141
142
  row.classnames = resolveRowClassNames(row);
142
143
  Object.assign(row, getRowStateForTree(rowData, state));
143
144
  row.tabIndex = -1;
145
+ if (_virtualize) {
146
+ row.style = styleToString({
147
+ position: 'absolute',
148
+ top: `${rowIndex * DEFAULT_ROW_HEIGHT}px`,
149
+ });
150
+ }
144
151
 
145
152
  columns.reduce((currentRow, colData, colIndex) => {
146
153
  const { fieldName } = colData;
147
154
  const colKeyValue = generateColKeyValue(colData, colIndex);
148
- const dirtyValue = getDirtyValue(state, row.key, colKeyValue);
155
+ const dirtyValue = getDirtyValueFromCell(
156
+ state,
157
+ row.key,
158
+ colKeyValue
159
+ );
149
160
  const cellHasErrors = hasCellErrors(
150
161
  rowErrors,
151
162
  colData.fieldName,
@@ -17,8 +17,9 @@ export const getDefaultState = function () {
17
17
  rows: [],
18
18
  indexes: {},
19
19
 
20
- // selector
20
+ // row selection
21
21
  selectedRowsKeys: {},
22
+ lastSelectedRowKey: undefined,
22
23
  maxRowSelection: undefined,
23
24
 
24
25
  headerIndexes: {},
@@ -59,7 +60,15 @@ export const getDefaultState = function () {
59
60
 
60
61
  // inline edit
61
62
  inlineEdit: {
63
+ rowKeyValue: undefined,
64
+ colKeyValue: undefined,
65
+ columnDef: {},
62
66
  dirtyValues: {},
67
+ editedValue: undefined,
68
+ isPanelVisible: false,
69
+ massEditEnabled: false,
70
+ massEditSelectedRows: undefined,
71
+ resolvedAttributeTypes: {},
63
72
  },
64
73
 
65
74
  // errors
@@ -22,6 +22,10 @@
22
22
  border-top: 1px solid #dddbda;
23
23
  }
24
24
 
25
+ .slds-table_header-fixed [data-row-key-value]:first-child {
26
+ border-top: 0;
27
+ }
28
+
25
29
  /* Required for row numbers */
26
30
  [role="row"] {
27
31
  counter-increment: row-number;
@@ -120,6 +120,7 @@
120
120
  aria-setsize={row.setSize}
121
121
  aria-posinset={row.posInSet}
122
122
  aria-rowindex={row.ariaRowIndex}
123
+ style={row.style}
123
124
  tabindex={row.tabIndex}>
124
125
  <template for:each={row.cells} for:item="cell">
125
126
  <template if:true={cell.isCheckbox}>
@@ -109,3 +109,17 @@ export function getScrollOffsetFromTableEnd(el) {
109
109
  el.scrollHeight - el.parentNode.scrollTop - el.parentNode.clientHeight
110
110
  );
111
111
  }
112
+
113
+ /**
114
+ * Utility for converting arrays and plain objects to style strings
115
+ * @param {Array|Object} style
116
+ * @returns {string} representing array/object as a string
117
+ */
118
+ export function styleToString(style) {
119
+ if (!Array.isArray(style)) {
120
+ return Object.entries(style)
121
+ .map(([key, value]) => `${key}:${value}`)
122
+ .join(';');
123
+ }
124
+ return style.join(';');
125
+ }
@@ -9,7 +9,7 @@
9
9
  <lightning-helptext if:true={fieldLevelHelp} content={fieldLevelHelp}></lightning-helptext>
10
10
  <div class="slds-form-element__control">
11
11
  <div class="slds-dueling-list" onfocusin={handleFocus} onfocusout={handleBlur}>
12
- <div class="slds-assistive-text" id="assertive-thing" aria-live="assertive"></div>
12
+ <div class="slds-assistive-text" id="assertive-thing" aria-live="assertive">{_messageToDisplay}</div>
13
13
  <div class="slds-assistive-text" id="keyboard-interacton">{i18n.componentAssistiveText}</div>
14
14
  <div class={computedLeftColumnClass}>
15
15
  <span class="slds-form-element__label" id="source-list-label">{sourceLabel}</span>
@@ -14,6 +14,8 @@ import labelRequiredOptionError from '@salesforce/label/LightningDualListbox.req
14
14
  import labelUpButtonAssistiveText from '@salesforce/label/LightningDualListbox.upButtonAssistiveText';
15
15
  import labelMoveSelectionToAssistiveText from '@salesforce/label/LightningDualListbox.moveSelectionToAssistiveText';
16
16
  import labelLoadingText from '@salesforce/label/LightningCombobox.loadingText';
17
+ import labelMovedOptionsSingular from '@salesforce/label/LightningDualListbox.movedOptionsSingular';
18
+ import labelMovedOptionsPlural from '@salesforce/label/LightningDualListbox.movedOptionsPlural';
17
19
  import { LightningElement, api, track } from 'lwc';
18
20
  import { handleKeyDownOnOption } from './keyboard';
19
21
  import { classSet, formatLabel } from 'lightning/utils';
@@ -47,6 +49,8 @@ const i18n = {
47
49
  upButtonAssistiveText: labelUpButtonAssistiveText,
48
50
  moveSelectionToAssistiveText: labelMoveSelectionToAssistiveText,
49
51
  loadingText: labelLoadingText,
52
+ movedOptionsSingular: labelMovedOptionsSingular,
53
+ movedOptionsPlural: labelMovedOptionsPlural,
50
54
  };
51
55
 
52
56
  /**
@@ -117,6 +121,9 @@ export default class LightningDualListbox extends LightningElement {
117
121
  @track highlightedOptions = [];
118
122
  @track focusableInSource;
119
123
  @track focusableInSelected;
124
+ @track highlightedOptionsLabel = [];
125
+
126
+ _messageToDisplay = '';
120
127
 
121
128
  isFocusOnList = false;
122
129
 
@@ -705,6 +712,8 @@ export default class LightningDualListbox extends LightningElement {
705
712
  );
706
713
  }
707
714
 
715
+ this.movedOptions(addToSelect);
716
+
708
717
  const oldSelectedValues = this._selectedValues;
709
718
  this._selectedValues = newValues;
710
719
  const invalidMove =
@@ -730,6 +739,7 @@ export default class LightningDualListbox extends LightningElement {
730
739
  this.interactingState.leave();
731
740
  this.isFocusOnList = false;
732
741
  this.highlightedOptions = [];
742
+ this.highlightedOptionsLabel = [];
733
743
  this.optionToFocus = null;
734
744
  }
735
745
 
@@ -785,6 +795,7 @@ export default class LightningDualListbox extends LightningElement {
785
795
  const start = all ? options.length : this.lastSelected;
786
796
  let val, select;
787
797
  this.highlightedOptions = [];
798
+ this.highlightedOptionsLabel = [];
788
799
  for (let i = 0; i < options.length; i++) {
789
800
  select = (i - start) * (i - end) <= 0;
790
801
  if (select) {
@@ -889,6 +900,7 @@ export default class LightningDualListbox extends LightningElement {
889
900
  if (this.selectedList !== currentList || !isMultiple) {
890
901
  if (this.selectedList) {
891
902
  this.highlightedOptions = [];
903
+ this.highlightedOptionsLabel = [];
892
904
  this.lastSelected = -1;
893
905
  }
894
906
  this.selectedList = currentList;
@@ -1013,6 +1025,36 @@ export default class LightningDualListbox extends LightningElement {
1013
1025
  }
1014
1026
  if (!isSame) {
1015
1027
  this.highlightedOptions = [];
1028
+ this.highlightedOptionsLabel = [];
1029
+ }
1030
+ }
1031
+
1032
+ movedOptions(addToSelect) {
1033
+ const listName = addToSelect ? this.selectedLabel : this.sourceLabel;
1034
+
1035
+ for (let i = 0; i < this.highlightedOptions.length; i++) {
1036
+ let selectedOption = addToSelect
1037
+ ? this.computedSourceList.filter(
1038
+ (item) => item.value === this.highlightedOptions[i]
1039
+ )
1040
+ : this.computedSelectedList.filter(
1041
+ (item) => item.value === this.highlightedOptions[i]
1042
+ );
1043
+ this.highlightedOptionsLabel.push(selectedOption[0].label);
1044
+ }
1045
+
1046
+ if (this.highlightedOptions.length) {
1047
+ const strToFormat =
1048
+ this.highlightedOptions.length > 1
1049
+ ? i18n.movedOptionsPlural
1050
+ : i18n.movedOptionsSingular;
1051
+ this._messageToDisplay = formatLabel(
1052
+ strToFormat,
1053
+ this.highlightedOptionsLabel.join(', '),
1054
+ listName
1055
+ );
1056
+ } else {
1057
+ this._messageToDisplay = '';
1016
1058
  }
1017
1059
  }
1018
1060
  }
@@ -7,6 +7,7 @@ import labelTooLong from '@salesforce/label/LightningErrorMessage.validityTooLon
7
7
  import labelTooShort from '@salesforce/label/LightningErrorMessage.validityTooShort';
8
8
  import labelTypeMismatch from '@salesforce/label/LightningErrorMessage.validityTypeMismatch';
9
9
  import labelValueMissing from '@salesforce/label/LightningErrorMessage.validityValueMissing';
10
+ import labelSelectAtleastOneValue from '@salesforce/label/LightningErrorMessage.validitySelectAtleastOne';
10
11
  import { assert } from 'lightning/utilsPrivate';
11
12
 
12
13
  const constraintsSortedByPriority = [
@@ -20,6 +21,7 @@ const constraintsSortedByPriority = [
20
21
  'tooShort',
21
22
  'typeMismatch',
22
23
  'valueMissing',
24
+ 'selectAtleastOneValue',
23
25
  ];
24
26
 
25
27
  const defaultLabels = {
@@ -33,6 +35,7 @@ const defaultLabels = {
33
35
  tooShort: labelTooShort,
34
36
  typeMismatch: labelTypeMismatch,
35
37
  valueMissing: labelValueMissing,
38
+ selectAtleastOneValue: labelSelectAtleastOneValue,
36
39
  };
37
40
 
38
41
  function resolveBestMatch(validity) {
@@ -103,6 +106,12 @@ function newValidityState(constraintsProvider) {
103
106
  get badInput() {
104
107
  return computeConstraint(constraintsProvider, 'badInput');
105
108
  }
109
+ get selectAtleastOneValue() {
110
+ return computeConstraint(
111
+ constraintsProvider,
112
+ 'validitySelectAtleastOneValue'
113
+ );
114
+ }
106
115
 
107
116
  get valid() {
108
117
  return !(
@@ -115,7 +124,8 @@ function newValidityState(constraintsProvider) {
115
124
  this.rangeOverflow ||
116
125
  this.stepMismatch ||
117
126
  this.customError ||
118
- this.badInput
127
+ this.badInput ||
128
+ this.selectAtleastOneValue
119
129
  );
120
130
  }
121
131
  }
@@ -200,6 +210,7 @@ export class FieldConstraintApi {
200
210
  tooLong: this.inputComponent.messageWhenTooLong,
201
211
  typeMismatch: this.inputComponent.messageWhenTypeMismatch,
202
212
  valueMissing: this.inputComponent.messageWhenValueMissing,
213
+ selectAtleastOneValue: this.inputComponent.messageWhenValueMissing,
203
214
  });
204
215
  }
205
216
 
@@ -14,6 +14,7 @@ selections when filtering a list, such as from a multi-select picklist.
14
14
  To specify the pills, set the `items` attribute to an array of values in your component's JavaScript.
15
15
 
16
16
  By default, all pills in the container are displayed and wrap to additional lines if they can't fit on one line.
17
+ For information about changing the behavior, see **Managing Pill Layout in the Container**.
17
18
 
18
19
  This example creates three pills: a text-only pill, a pill with an avatar, and
19
20
  a pill with an icon.
@@ -70,7 +71,7 @@ To create a pill with an avatar, use the following attributes.
70
71
  To create a pill with an icon, use the following attributes.
71
72
 
72
73
  - `type`: The media type. Use `icon`.
73
- - `iconName`: Required. The Lightning Design System name of the icon. Names are written in the format '\utility:down\' where 'utility' is the category, and 'down' is the specific icon to be displayed. Only utility icons can be used in this component.
74
+ - `iconName`: Required. The Lightning Design System name of the icon. Names are written in the format 'utility:down' where 'utility' is the category, and 'down' is the specific icon to be displayed. Only utility icons can be used for the `iconName`.
74
75
  - `alternativeText`: The alternative text used to describe the icon. Describe what happens when you click the button, for example 'Upload File', not what the icon looks like, 'Paperclip'.
75
76
 
76
77
  `lightning-pill-container` provides two variants: `bare` and `standard` (default). They are visually the same. However, the `standard` variant renders pills in an unordered list element. For more information, see the **Accessibility** section.
@@ -135,9 +136,12 @@ set to false by default, which makes all pills display and wrap to multiple line
135
136
  - `single-line`: Specifies that the pill container can display one line of pills. By default, if pills can't fit on one line, they are wrapped to additional lines to fit the container. Set `single-line` to true to limit pill display to one line. This attribute overrides `is-collapsible` and `is-expanded`.
136
137
 
137
138
  If all pills aren't displayed, the component shows a text button indicating how many more pills there are.
139
+ For example, if there are five more pills that aren’t displayed, the text button shows `+5 more`. The text button fires the `focus` event when you click it.
138
140
 
139
141
  To display a long list of pills as collapsed, set `is-collapsible` to true and optionally set `is-expanded` to false. Otherwise, pills are displayed expanded.
140
142
 
143
+ ##### Expand and Collapse Pills Programmatically
144
+
141
145
  Use `is-collapsible` and `is-expanded` to programmatically expand and collapse the pills.
142
146
 
143
147
  This example sets `is-collapsible` and uses a button to change the value of `is-expanded`.
@@ -175,6 +179,46 @@ export default class PillContainerCanCollapse extends LightningElement {
175
179
  ];
176
180
  }
177
181
  ```
182
+ ##### Display All Pills With the `+n more` Button
183
+
184
+ If all pills aren't displayed, the component shows a text button labeled `+n more` to indicate more pills can be displayed. By default, `lightning-pill-container` doesn’t handle the `focus` event that’s fired when you click the button. You can handle the event to display more pills or write logic to do something else when the button is clicked.
185
+
186
+ This example sets the pills to be collapsible but not expanded and handles the focus event.
187
+
188
+ ```html
189
+ <template>
190
+ <div style="width: 600px">
191
+ <lightning-pill-container
192
+ items={items}
193
+ is-collapsible={collapsible}
194
+ is-expanded={expanded}
195
+ onfocus={handlePillExpansion}
196
+ >
197
+ </lightning-pill-container>
198
+ </div>
199
+ </template>
200
+ ```
201
+
202
+ The list of pills is initially collapsed. When there are too many pills to be displayed, the text button labeled `+n more` displays. The handler for the `focus` event enables all the pills to display.
203
+
204
+ ```javascript
205
+ import { LightningElement } from 'lwc';
206
+
207
+ export default class PillContainerMoreButtonExpands extends LightningElement {
208
+ collapsible = true;
209
+ expanded = false;
210
+
211
+ handlePillExpansion(){
212
+ this.expanded = true;
213
+ }
214
+
215
+ items = [
216
+ {
217
+ //define the pills
218
+ }
219
+ ];
220
+ }
221
+ ```
178
222
 
179
223
  #### Component Styling
180
224
 
@@ -131,50 +131,38 @@ function createRelationship(
131
131
  );
132
132
 
133
133
  const autoShrink = config.autoShrink.height || config.autoShrink.width;
134
- const modal = new OverlayDetector(originalConfig.target);
135
- if (
136
- (config.scrollableParentBound && scrollableParent) ||
137
- (modal.isInsideModal && modal.overlay && autoShrink)
138
- ) {
139
- let parent;
140
- if (config.scrollableParentBound && scrollableParent) {
141
- parent = normalizeElement(scrollableParent);
142
- } else if (modal.isInsideModal && modal.overlay) {
143
- parent = normalizeElement(modal.overlay);
144
- }
145
- if (parent) {
146
- const boxConfig = {
147
- element: config.element,
148
- enabled: config.enabled,
149
- target: createProxy(parent),
150
- align: {},
151
- targetAlign: {},
152
- pad: 3,
153
- boxDirections: {
154
- top: true,
155
- bottom: true,
156
- left: true,
157
- right: true,
158
- },
159
- };
160
-
161
- if (autoShrink) {
162
- const style = boxConfig.element.getNode().style;
163
- if (!style.minHeight) {
164
- style.minHeight = config.minHeight;
165
- boxConfig.element._removeMinHeight = true;
166
- }
167
-
168
- boxConfig.boxDirections = {
169
- top: !!config.autoShrink.height,
170
- bottom: !!config.autoShrink.height,
171
- left: !!config.autoShrink.width,
172
- right: !!config.autoShrink.width,
173
- };
174
- constraintList.push(new Constraint('shrinking box', boxConfig));
175
- } else {
176
- constraintList.push(new Constraint('bounding box', boxConfig));
134
+ if (config.scrollableParentBound && scrollableParent) {
135
+ const parent = normalizeElement(scrollableParent);
136
+ const boxConfig = {
137
+ element: config.element,
138
+ enabled: config.enabled,
139
+ target: createProxy(parent),
140
+ align: {},
141
+ targetAlign: {},
142
+ pad: 3,
143
+ boxDirections: {
144
+ top: true,
145
+ bottom: true,
146
+ left: true,
147
+ right: true,
148
+ },
149
+ };
150
+ if (autoShrink) {
151
+ const style = boxConfig.element.getNode().style;
152
+ if (!style.minHeight) {
153
+ style.minHeight = config.minHeight;
154
+ boxConfig.element._removeMinHeight = true;
177
155
  }
156
+
157
+ boxConfig.boxDirections = {
158
+ top: !!config.autoShrink.height,
159
+ bottom: !!config.autoShrink.height,
160
+ left: !!config.autoShrink.width,
161
+ right: !!config.autoShrink.width,
162
+ };
163
+ constraintList.push(new Constraint('shrinking box', boxConfig));
164
+ } else {
165
+ constraintList.push(new Constraint('bounding box', boxConfig));
178
166
  }
179
167
  }
180
168
 
@@ -21,28 +21,33 @@ const i18n = {
21
21
  showActions: labelShowActions,
22
22
  };
23
23
 
24
+ /**
25
+ * A cell-level action.
26
+ */
24
27
  export default class PrimitiveCellActions extends LightningElement {
25
- static delegatesFocus = true;
26
-
27
- @api rowKeyValue;
28
- @api colKeyValue;
29
- @api rowActions;
30
-
28
+ // Tracked objects
31
29
  @track containerRect;
32
30
  @track _actions = [];
33
31
 
32
+ // Private variables
33
+ static delegatesFocus = true;
34
34
  _isLoadingActions;
35
35
  _menuAlignment = DEFAULT_MENU_ALIGNMENT;
36
36
  _internalTabIndex = false;
37
37
 
38
- connectedCallback() {
39
- this._connected = true;
40
- }
38
+ /************************** PUBLIC ATTRIBUTES ***************************/
41
39
 
42
- disconnectedCallback() {
43
- this._connected = false;
44
- }
40
+ @api rowKeyValue;
41
+ @api colKeyValue;
42
+ @api rowActions;
45
43
 
44
+ /**
45
+ * Defines the current menu alignment
46
+ * See `VALID_MENU_ALIGNMENT` for valid menu alignment values
47
+ * See `DEFAULT_MENU_ALIGNMENT` for the default menu alignment
48
+ *
49
+ * @type {string}
50
+ */
46
51
  @api
47
52
  get menuAlignment() {
48
53
  return this._menuAlignment;
@@ -55,6 +60,11 @@ export default class PrimitiveCellActions extends LightningElement {
55
60
  });
56
61
  }
57
62
 
63
+ /************************** PUBLIC METHODS ***************************/
64
+
65
+ /**
66
+ * Sets focus on a lightning-button-menu
67
+ */
58
68
  @api
59
69
  focus() {
60
70
  if (this._connected) {
@@ -62,6 +72,9 @@ export default class PrimitiveCellActions extends LightningElement {
62
72
  }
63
73
  }
64
74
 
75
+ /**
76
+ * Clicks a lightning-button-menu
77
+ */
65
78
  @api
66
79
  click() {
67
80
  if (this._connected) {
@@ -73,18 +86,42 @@ export default class PrimitiveCellActions extends LightningElement {
73
86
  }
74
87
  }
75
88
 
89
+ /************************** PRIVATE GETTERS **************************/
90
+
91
+ /**
92
+ * Returns the computed menu alignment value
93
+ *
94
+ * @return {string} Current computed menu alignment
95
+ */
76
96
  get computedMenuAlignment() {
77
97
  return this.menuAlignment;
78
98
  }
79
99
 
100
+ /**
101
+ * Returns the button alternative text in the appropriate language
102
+ *
103
+ * @return {string} Language-specific button alternative text
104
+ */
80
105
  get buttonAlternateText() {
81
106
  return `${i18n.showActions}`;
82
107
  }
83
108
 
109
+ /**
110
+ * Returns the spinner alternative text in the appropriate language
111
+ *
112
+ * @return {string} Language-specific spinner alternative text
113
+ */
84
114
  get spinnerAlternateText() {
85
115
  return `${i18n.loadingActions}`;
86
116
  }
87
117
 
118
+ /************************ EVENT DISPATCHERS **************************/
119
+
120
+ /**
121
+ * Handles selecting an action
122
+ *
123
+ * @param {Event} event
124
+ */
88
125
  handleActionSelect(event) {
89
126
  this.dispatchEvent(
90
127
  new CustomEvent('privatecellactiontriggered', {
@@ -100,6 +137,9 @@ export default class PrimitiveCellActions extends LightningElement {
100
137
  );
101
138
  }
102
139
 
140
+ /**
141
+ * Handles the opening of an action menu
142
+ */
103
143
  handleMenuOpen() {
104
144
  this.elementRect = this.template
105
145
  .querySelector('lightning-button-menu')
@@ -135,6 +175,23 @@ export default class PrimitiveCellActions extends LightningElement {
135
175
  }
136
176
  }
137
177
 
178
+ /************************** LIFECYCLE HOOKS **************************/
179
+
180
+ connectedCallback() {
181
+ this._connected = true;
182
+ }
183
+
184
+ disconnectedCallback() {
185
+ this._connected = false;
186
+ }
187
+
188
+ /************************* HELPER FUNCTIONS **************************/
189
+
190
+ /**
191
+ * Resets loading state when all actions have been loaded
192
+ *
193
+ * @param {object} actions - Actions displayed in the menu
194
+ */
138
195
  finishLoadingActions(actions) {
139
196
  this._isLoadingActions = false;
140
197
  this._actions = actions;