lightning-base-components 1.17.2-alpha → 1.17.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.
@@ -1537,8 +1537,10 @@
1537
1537
  "iconUtils": {},
1538
1538
  "industriesActionPlanApi": {},
1539
1539
  "industriesActionablelistApi": {},
1540
+ "industriesAssessmentApi": {},
1540
1541
  "industriesCibApi": {},
1541
1542
  "industriesClmApi": {},
1543
+ "industriesDataloadingApi": {},
1542
1544
  "industriesDecisionMatrixDesignerApi": {},
1543
1545
  "industriesDocgenApi": {},
1544
1546
  "industriesEinsteinAIAcceleratorApi": {},
@@ -2315,13 +2317,13 @@
2315
2317
  "name": "disabled"
2316
2318
  },
2317
2319
  {
2318
- "name": "errorMessage"
2320
+ "name": "entityOptions"
2319
2321
  },
2320
2322
  {
2321
- "name": "fieldLevelHelp"
2323
+ "name": "errorMessage"
2322
2324
  },
2323
2325
  {
2324
- "name": "filterItems"
2326
+ "name": "fieldLevelHelp"
2325
2327
  },
2326
2328
  {
2327
2329
  "name": "items"
@@ -2365,7 +2367,7 @@
2365
2367
  "slotNames": [],
2366
2368
  "properties": [
2367
2369
  {
2368
- "name": "filterItems"
2370
+ "name": "entityOptions"
2369
2371
  },
2370
2372
  {
2371
2373
  "name": "items"
@@ -3671,6 +3673,7 @@
3671
3673
  "uiGraphQLApi": {
3672
3674
  "minVersion": "57.0"
3673
3675
  },
3676
+ "uiGraphQLBatchApi": {},
3674
3677
  "uiLayoutApi": {},
3675
3678
  "uiLearningContentPlatformApi": {},
3676
3679
  "uiListApi": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightning-base-components",
3
- "version": "1.17.2-alpha",
3
+ "version": "1.17.3-alpha",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "external",
@@ -506,6 +506,10 @@
506
506
  "name": "@salesforce/label/LightningDatatable.chooseARow",
507
507
  "path": "scopedImports/@salesforce-label-LightningDatatable.chooseARow.js"
508
508
  },
509
+ {
510
+ "name": "@salesforce/label/LightningDatatable.chooseARowSelectAll",
511
+ "path": "scopedImports/@salesforce-label-LightningDatatable.chooseARowSelectAll.js"
512
+ },
509
513
  {
510
514
  "name": "@salesforce/label/LightningDatatable.save",
511
515
  "path": "scopedImports/@salesforce-label-LightningDatatable.save.js"
@@ -1082,6 +1086,10 @@
1082
1086
  "name": "@salesforce/label/Global_Entity.last_modified_by",
1083
1087
  "path": "scopedImports/@salesforce-label-Global_Entity.last_modified_by.js"
1084
1088
  },
1089
+ {
1090
+ "name": "@salesforce/label/AddressAutocomplete.LookupButton",
1091
+ "path": "scopedImports/@salesforce-label-AddressAutocomplete.LookupButton.js"
1092
+ },
1085
1093
  {
1086
1094
  "name": "@salesforce/i18n/lang",
1087
1095
  "path": "scopedImports/@salesforce-i18n-lang.js"
@@ -0,0 +1 @@
1
+ export default 'Search Address';
@@ -0,0 +1 @@
1
+ export default 'Choose a Row';
@@ -69,7 +69,6 @@ export default class LightningBaseCombobox extends LightningElement {
69
69
  @api inputMaxlength;
70
70
  @api showInputActivityIndicator = false;
71
71
  @api required = false;
72
- @api dropdownAlignment = 'left';
73
72
  @api placeholder = i18n.placeholder;
74
73
  @api inputLabel;
75
74
 
@@ -93,6 +92,7 @@ export default class LightningBaseCombobox extends LightningElement {
93
92
  _inputAriaControls;
94
93
  _activeElementDomId;
95
94
  _autocomplete = 'off';
95
+ _dropdownAlignment = 'left';
96
96
  originDisableDefaultHighlight;
97
97
  privateDisableDefaultHighlight;
98
98
  _editingMode = false;
@@ -115,6 +115,7 @@ export default class LightningBaseCombobox extends LightningElement {
115
115
  }
116
116
 
117
117
  connectedCallback() {
118
+ this.overrideDropdownAlignment();
118
119
  this.classList.add('slds-combobox_container');
119
120
  this._connected = true;
120
121
  this._keyboardInterface = this.dropdownKeyboardInterface();
@@ -125,6 +126,15 @@ export default class LightningBaseCombobox extends LightningElement {
125
126
  this._listBoxElementCache = undefined;
126
127
  }
127
128
 
129
+ @api
130
+ get dropdownAlignment() {
131
+ return this._dropdownAlignment;
132
+ }
133
+
134
+ set dropdownAlignment(value) {
135
+ this._dropdownAlignment = value;
136
+ }
137
+
128
138
  @api
129
139
  get inputControlsElement() {
130
140
  return this._inputAriaControls;
@@ -896,6 +906,7 @@ export default class LightningBaseCombobox extends LightningElement {
896
906
  this._selectableItems < 3
897
907
  ? SMALL_MIN_HEIGHT
898
908
  : MEDIUM_MIN_HEIGHT,
909
+ keepInViewport: true,
899
910
  });
900
911
  }
901
912
 
@@ -1064,6 +1075,33 @@ export default class LightningBaseCombobox extends LightningElement {
1064
1075
  this.template.host.getAttribute('data-aria-invalid');
1065
1076
  return computeAriaInvalid(ariaInvalid, true);
1066
1077
  }
1078
+
1079
+ isShadowRoot(node) {
1080
+ return node && node.nodeType === 11;
1081
+ }
1082
+
1083
+ parentNodeContainsClass(host, className) {
1084
+ let element = host;
1085
+ while (element.parentNode) {
1086
+ element = this.isShadowRoot(element.parentNode)
1087
+ ? element.parentNode.host
1088
+ : element.parentNode;
1089
+ if (element.classList && element.classList.contains(className)) {
1090
+ return true;
1091
+ }
1092
+ }
1093
+ return false;
1094
+ }
1095
+
1096
+ overrideDropdownAlignment() {
1097
+ let isModal = this.parentNodeContainsClass(
1098
+ this.template.host,
1099
+ 'slds-modal'
1100
+ );
1101
+ if (isModal) {
1102
+ this._dropdownAlignment = 'auto';
1103
+ }
1104
+ }
1067
1105
  }
1068
1106
 
1069
1107
  function scrollIntoViewIfNeeded(element, scrollingParent) {
@@ -23,11 +23,9 @@ Here is an example.
23
23
 
24
24
  #### Creating Links Using Breadcrumbs
25
25
 
26
- The behavior of a breadcrumb is similar to a link. If a link is not provided
27
- via the `href` attribute, the value defaults to `javascript:void(0);`. To
28
- provide custom navigation, use an `onclick` handler with `lightning-navigation`. If you provide a link in the `href` attribute,
29
- calling `event.preventDefault()` enables you to bypass the link and use your
30
- custom navigation instead.
26
+ The behavior of a breadcrumb is similar to a link for the purpose of navigation. If a link is not provided via the `href` attribute, the value defaults to `#`. Since a breadcrumb is used as navigation, we don't recommend leaving out the `href` attribute since `#` links to the same page when middle-clicked or opened in a new tab.
27
+
28
+ To provide custom navigation, use an `onclick` handler with `lightning-navigation`. If you provide a link in the `href` attribute, calling `event.preventDefault()` enables you to bypass the link and use your custom navigation instead.
31
29
 
32
30
  ```html
33
31
  <template>
@@ -67,7 +65,8 @@ handleNavigateToCustomPage2(event) {
67
65
 
68
66
  #### Generating Breadcrumbs with Iteration
69
67
 
70
- Iterate over a list of items using `for:each` to generate breadcrumbs.
68
+ Iterate over a list of items using `for:each` to generate breadcrumbs. If you don't provide a link with an `onclick` handler, `href` defaults to `#`.
69
+
71
70
  For example, you can create an array of breadcrumbs with label and name
72
71
  values.
73
72
 
@@ -58,7 +58,7 @@ For a View All link, set the href value of the tag to a URL to take the user to
58
58
  <lightning-button label="Old" slot="actions"></lightning-button>
59
59
  <p class="slds-p-horizontal_small">Card Body (custom component)</p>
60
60
  <div slot="footer">
61
- <a class="slds-card__footer-action" href="javascript:void(0);"
61
+ <a class="slds-card__footer-action" href="#"
62
62
  >View All
63
63
  <span class="slds-assistive-text">Accounts</span>
64
64
  </a>
@@ -140,7 +140,7 @@ export default class LightningCard extends LightningElement {
140
140
  get computedHidden() {
141
141
  if (!this.label && this.hideHeader) {
142
142
  console.warn(
143
- 'Setting header without a label can cause accessibility issues'
143
+ 'A `lightning-card` with `hide-header` requires `label` to be set.'
144
144
  );
145
145
  }
146
146
  return !this.hideHeader;
@@ -213,6 +213,7 @@ export default class LightningDatatable extends LightningElement {
213
213
  _renderMode = 'table';
214
214
  _shouldResetFocus = false; // used to ensure focus isn't lost from changes in renderedRows
215
215
  _suppressBottomBar = false;
216
+ _checkboxColumnHeaderId;
216
217
 
217
218
  /************************* PUBLIC PROPERTIES *************************/
218
219
 
@@ -864,27 +865,13 @@ export default class LightningDatatable extends LightningElement {
864
865
  return styleToString(styles);
865
866
  }
866
867
 
867
- /**
868
- * Private method to get datatable header element
869
- * used when getting checkboxColumnHeaderId for
870
- * aria-labelledby in checkbox column
871
- * @returns {(HTMLElement|null)}
872
- */
873
- get checkboxColumnHeaderElement() {
874
- return this.template.querySelector(
875
- 'lightning-primitive-header-factory'
876
- );
877
- }
878
-
879
868
  /**
880
869
  * Private method to get computedCheckboxColumnHeaderId
881
870
  * from checkboxColumnHeaderElement for
882
871
  * aria-labelledby in checkbox column
883
872
  */
884
873
  get computedCheckboxColumnHeaderId() {
885
- return this.checkboxColumnHeaderElement
886
- ? this.checkboxColumnHeaderElement.computedColumnHeaderId
887
- : '';
874
+ return this._checkboxColumnHeaderId;
888
875
  }
889
876
 
890
877
  get computedAriaLiveClassForNavMode() {
@@ -1344,6 +1331,10 @@ export default class LightningDatatable extends LightningElement {
1344
1331
  this.fireSortedColumnChange(fieldName, columnKey, sortDirection);
1345
1332
  }
1346
1333
 
1334
+ handleCheckboxHeaderId(event) {
1335
+ this._checkboxColumnHeaderId = event.detail;
1336
+ }
1337
+
1347
1338
  /**
1348
1339
  * Handles the `resizecol` event on lightning-datatable
1349
1340
  *
@@ -84,7 +84,8 @@
84
84
  sorted-direction={def.sortedDirection}
85
85
  column-width={def.columnWidth}
86
86
  show-checkbox={showSelectAllCheckbox}
87
- hide-header={hideTableHeader}>
87
+ hide-header={hideTableHeader}
88
+ onprivatecolumnheaderid={handleCheckboxHeaderId}>
88
89
  </lightning-primitive-header-factory>
89
90
  </template>
90
91
  <template if:false={def.fixedWidth}>
@@ -105,7 +106,8 @@
105
106
  column-width={def.columnWidth}
106
107
  resizable={hasResizebleColumns}
107
108
  resizestep={widthsData.resizeStep}
108
- hide-header={hideTableHeader}>
109
+ hide-header={hideTableHeader}
110
+ onprivatecolumnheaderid={handleCheckboxHeaderId}>
109
111
  </lightning-primitive-header-factory>
110
112
  </template>
111
113
  </div>
@@ -73,7 +73,8 @@
73
73
  sorted-direction={def.sortedDirection}
74
74
  column-width={def.columnWidth}
75
75
  show-checkbox={showSelectAllCheckbox}
76
- hide-header={hideTableHeader}>
76
+ hide-header={hideTableHeader}
77
+ onprivatecolumnheaderid={handleCheckboxHeaderId}>
77
78
  </lightning-primitive-header-factory>
78
79
  </template>
79
80
  <template if:false={def.fixedWidth}>
@@ -94,7 +95,8 @@
94
95
  column-width={def.columnWidth}
95
96
  resizable={hasResizebleColumns}
96
97
  resizestep={widthsData.resizeStep}
97
- hide-header={hideTableHeader}>
98
+ hide-header={hideTableHeader}
99
+ onprivatecolumnheaderid={handleCheckboxHeaderId}>
98
100
  </lightning-primitive-header-factory>
99
101
  </template>
100
102
  </th>
@@ -1,4 +1,4 @@
1
- # Dialog
1
+ # Dialog (Deprecated)
2
2
 
3
3
  ## Important:
4
4
  * **For any new modal work, starting in release 236, please use `lightning-modal`**
@@ -336,7 +336,7 @@ When `multiple` is used, the email field expects a single email address or a com
336
336
 
337
337
  #### File
338
338
 
339
- An input field for selecting files to upload using an `Upload Files` button or a drag-and-drop zone.
339
+ An input field for selecting files to upload using an `Upload Files` button or a drag-and-drop zone. This field accepts files up to 3.5 MB.
340
340
 
341
341
  To retrieve the list of selected files, use
342
342
  `event.target.files` in the `onchange` event handler. Your selected files are returned in a `FileList` object, each specified as a `File` object with the `size` and `type` attributes.
@@ -574,7 +574,7 @@ export default class LightningInput extends LightningElement {
574
574
  }
575
575
 
576
576
  /**
577
- * A Boolean value for aria-invalid.
577
+ * A boolean value that controls whether accessibility tools read empty required textboxes as invalid. Default value is false.
578
578
  * @type {boolean}
579
579
  */
580
580
  @api
@@ -1,17 +1,17 @@
1
- A `lightningModal` component overlays a message modal on top of the current app window. A modal interrupts a user’s workflow and draws attention to the message.
1
+ A `lightningModal` component overlays a message modal on top of the current app window. A modal interrupts a user’s workflow and draws attention to the message.
2
2
 
3
3
  `LightningModal` implements the SLDS [modals](https://www.lightningdesignsystem.com/components/modals/) blueprint.
4
4
 
5
5
  Create a modal component in response to a user action, such as clicking a button or link. The modal blocks interaction with everything else on the page until the user acts upon or dismisses the modal.
6
6
 
7
- Unlike other components, this component doesn't use a `lightning-modal` tag or extend `LightningElement`. There is no `lightning-modal` component. Instead, you create a modal by extending `LightningModal` and using these helper `lightning-modal-*` components to provide a header, footer and the body of the modal.
7
+ Unlike other components, this component doesn't use a `lightning-modal` tag or extend `LightningElement`. There is no `lightning-modal` component. Instead, you create a modal by extending `LightningModal` and using these helper `lightning-modal-*` components to provide a header, footer and the body of the modal.
8
8
  - `lightning-modal-body`
9
9
  - `lightning-modal-header`
10
10
  - `lightning-modal-footer`
11
11
 
12
12
  To create a modal component, import `LightningModal` from `lightning/modal`. The component has access to the normal LWC resources as well as the special container, helper components, methods, and events of the `lightning/modal` module.
13
13
 
14
- In this example, the `content` property passes data to the modal from the component that invokes it.
14
+ In this example, the `content` property passes data to the modal from the component that invokes it.
15
15
 
16
16
  ```js
17
17
  /* c/myModal.js */
@@ -28,7 +28,7 @@ export default class MyModal extends LightningModal {
28
28
  }
29
29
  ```
30
30
 
31
- The modal’s HTML template uses helper `lightning-modal-*` components to provide a header, footer, and the body of the modal. In this example, the content for the modal body comes from the `content` property we defined in the modal's JavaScript file.
31
+ The modal’s HTML template uses helper `lightning-modal-*` components to provide a header, footer, and the body of the modal. In this example, the content for the modal body comes from the `content` property we defined in the modal's JavaScript file.
32
32
 
33
33
  ```html
34
34
  <!-- c/myModal.html -->
@@ -42,19 +42,19 @@ The modal’s HTML template uses helper `lightning-modal-*` components to provid
42
42
  </template>
43
43
  ```
44
44
 
45
- The `lightning-modal-body` component is required for the modal template.
45
+ The `lightning-modal-body` component is required for the modal template.
46
46
 
47
- The `lightning-modal-header` and `lightning-modal-footer` components are optional, but recommended. The `lightning-modal-*` components render in the order they appear in the template. Place the `lightning-modal-body` component after `lightning-modal-header` and before the `lightning-modal-footer` component.
47
+ The `lightning-modal-header` and `lightning-modal-footer` components are optional, but recommended. The `lightning-modal-*` components render in the order they appear in the template. Place the `lightning-modal-body` component after `lightning-modal-header` and before the `lightning-modal-footer` component.
48
48
 
49
49
  #### Open a Modal Instance
50
50
 
51
51
  `LightningModal` provides an `.open()` method which opens a modal and returns a promise that asynchronously resolves with the result of the user’s interaction with the modal.
52
52
 
53
- Each invocation of a modal component’s `.open()` method creates a unique instance of the modal. You can think of a modal as a self-contained application that starts from scratch when it opens. It displays the content you pass in through the `.open()` method or that you set within the modal's HTML template.
53
+ Each invocation of a modal component’s `.open()` method creates a unique instance of the modal. You can think of a modal as a self-contained application that starts from scratch when it opens. It displays the content you pass in through the `.open()` method or that you set within the modal's HTML template.
54
54
 
55
- When you close a modal, the modal instance is destroyed, not hidden. On close, the modal must save the user’s input data or pass it to the invoking component as the promise result. Otherwise, the data is lost when the modal instance is closed.
55
+ When you close a modal, the modal instance is destroyed, not hidden. On close, the modal must save the user’s input data or pass it to the invoking component as the promise result. Otherwise, the data is lost when the modal instance is closed.
56
56
 
57
- The `.open()` method lets you assign values to the modal's properties. `LightningModal` provides these properties.
57
+ The `.open()` method lets you assign values to the modal's properties. `LightningModal` provides these properties.
58
58
 
59
59
  * `label` - Required. Sets the modal's title and assistive device label. If the modal has a header, set `label` in the `lightning-modal-header` component. If the modal doesn't have a header, set the `label` property when opening the modal.
60
60
 
@@ -95,14 +95,14 @@ The HTML template for this app contains a button that opens the modal and displa
95
95
  <template>
96
96
  <lightning-button
97
97
  onclick={handleClick}
98
- aria-haspopup="modal"
98
+ aria-haspopup="dialog"
99
99
  label="Open My Modal">
100
- </lightning-button>
100
+ </lightning-button>
101
101
  <p>Result: {result}</p>
102
102
  </template>
103
103
  ```
104
104
 
105
- You can also use `.open()` to pass data from your invoking component into the modal with custom properties decorated with `@api`. These properties can be any type, such as a string or an object that’s an array of key/value pairs to be assigned to the new modal instance.
105
+ You can also use `.open()` to pass data from your invoking component into the modal with custom properties decorated with `@api`. These properties can be any type, such as a string or an object that’s an array of key/value pairs to be assigned to the new modal instance.
106
106
 
107
107
  For example, this app component sets an `options` property to a set of key/value pairs in `MyModal.open()`. The promise is handled using an arrow function that logs the result to the console.
108
108
 
@@ -155,14 +155,14 @@ export default class MyModal extends LightningModal {
155
155
  >
156
156
  {option.label}
157
157
  </lightning-button>
158
- </template>
158
+ </template>
159
159
  </lightning-modal-body>
160
160
  </template>
161
161
  ```
162
162
 
163
163
  #### Close a Modal Instance
164
164
 
165
- Use `this.close(result)` to close the modal, where `result` is anything you want to return from the modal. The `.close()` operation is asynchronous to display a brief fade out animation before the modal is destroyed. The `result` data can’t be modified after the close operation begins.
165
+ Use `this.close(result)` to close the modal, where `result` is anything you want to return from the modal. The `.close()` operation is asynchronous to display a brief fade out animation before the modal is destroyed. The `result` data can’t be modified after the close operation begins.
166
166
 
167
167
  You can also close the modal with the default close button, the X at the top right corner. Closing a modal like this is the same as calling `this.close()` with an `undefined` result, so any data input is lost.
168
168
 
@@ -320,7 +320,7 @@ export default class MyModalForm extends LightningModal {
320
320
 
321
321
  A modal can only fire events captured by the component that opened it, not the modal itself. Normal techniques can't catch events that fire from the modal, because the events bubble up to a top root element outside of the component that opened the modal.
322
322
 
323
- To capture modal events, attach them in the `.open()` method invoked by the component that opens the modal.
323
+ To capture modal events, attach them in the `.open()` method invoked by the component that opens the modal.
324
324
 
325
325
  For example, here's a custom `select` event dispatched from `MyModal`.
326
326
 
@@ -374,18 +374,185 @@ handleOpenModal() {
374
374
 
375
375
  See [Create and Dispatch Events](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/events_create_dispatch) in the LWC Dev Guide for more information about events.
376
376
 
377
+ #### Modal Events with Aura
378
+
379
+ For `LightningModal`, only the top level LWC component or application can communicate to the parent Aura component or application layer with eventing. This topmost component is usually the one that opens the `LightningModal`. `LightningModal`'s child components can't event to a parent Aura component or application layer.
380
+
381
+ All required eventing that should occur during the `LightningModal`'s life cycle must be passed in when you call `Modal.open()`. See `onselect` within `.open()` in the **Modal Events** section.
382
+
383
+ If you want the Aura layer to respond to events within child components embedded in the `LightningModal`, use event bubbling to move any data that you want to make available to the Aura layer into the topmost LWC component that opened the modal. Then, send the event from the LWC component.
384
+
385
+ With this in mind, there are two suggested methods for extracting data from the `LightningModal` to the Aura layer.
386
+
387
+ 1. To only communicate data out of modal after it's closed, first, close the modal, and pass the data out with `this.close({ data })`, and handle the data received in `.then((result) { ... })` where the Modal is initially opened.
388
+ 2. To continuously communicate data out of the `LightningModal` while the modal remains open, use events created and passed in when opening the modal from `Modal.open({ size, title, onmyevent })`.
389
+
390
+ These extracting methods fit into the larger LWC Modal-to-Aura event workflow.
391
+
392
+ 1. Pass any events that should occur within the modal through `LightningModal`'s `.open()` method
393
+ 2. Have the `LightningModal` JavaScript code fire any custom events
394
+ 3. In the topmost LWC component that opened the modal, create an event handler to process the events, including stopping propagation.
395
+ 4. Fire a separate event containing the LWC-processed event details and send it to the Aura parent component.
396
+ 5. Use an Aura-based event handler to handle and process the event.
397
+
398
+ For more information, see [Send Events to an Enclosing Aura Component](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.events_sending_to_aura_components) and [Events Best Practices](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/events_best_practices).
399
+
400
+ Let's see this workflow in action. In this example, we'll create a button (`lightning-button`) that launches a modal (`LightningModal`) containing a tree grid component (`lightning-tree-grid`) with a button in each record row that automatically navigates our user to that record's page (`lightning-navigation`). This use case requires data passing between our LWC components and a parent Aura component.
401
+
402
+ Because `lightning-navigation` requires you to pass in a `recordId` to construct a page reference, we need to pass the record's data out of `LightningModal`, and then use the methods provided by `lightning-navigation`. With modal's limitations for event bubbling to the Aura layer, we can't just wrap the button or treegrid with `lightning-navigation`. We need to pull that data up to the topmost LWC component, where the `LightningModal` `.open()` is called, for our Aura wrapper to handle the navigation.
403
+
404
+ So, we `import` the `lightning-navigation` component into the topmost LWC component that sits within the Aura layer by passing the data through `LightningModal`'s close method, `this.close({ data })`. Then the parent LWC component invokes the methods provided by `lightning-navigation`, which handles the navigation away from the page after the modal exits. From a UX perspective, this also lets us close the modal first, since `lightning-navigation` takes the user away from the current page.
405
+
406
+ Note that this sample code isn't fully functional. We're only showing the relevant code that makes the `lightning-navigation` use case work with `LightningModal`.
407
+
408
+ Here's our Aura component, containing our topmost LWC component, `myLwcAppModalLauncher`.
409
+
410
+ ```html
411
+ /* c/myApp.cmp */
412
+ <aura:component>
413
+ <!-- <c:myLwcTreeGridWithNavigation/> component is a child component
414
+ inside of <c:myLwcAppModalLauncher/>
415
+ -->
416
+ <c:myLwcAppModalLauncher/>
417
+ </aura:component>
418
+ ```
419
+
420
+ The topmost component, `myLwcAppModalLauncher`, launches and handles events that occur within our modal (`MyModal`). `myLwcAppModalLauncher` is also where we import the `lightning-navigation` component and use the `rowId` handed up through modal's `this.close({ ..., rowId })` function.
421
+
422
+ ```html
423
+ <!-- c/myLwcAppModalLauncher.html -->
424
+
425
+ <template>
426
+ <lightning-button
427
+ onclick={handleModalOpen}
428
+ aria-haspopup="dialog"
429
+ label="Launch Navigation Modal">
430
+ </lightning-button>
431
+ </template>
432
+ ```
433
+
434
+ ```js
435
+ /* c/myLwcAppModalLauncher.js */
436
+
437
+ import { LightningElement, wire } from 'lwc';
438
+ import { NavigationMixin } from 'lightning/navigation';
439
+ import MyModal from 'c/myModalWithTreeGridNavigation';
440
+
441
+ export default class MyLwcAppModalLauncher extends NavigationMixin(LightningElement) {
442
+
443
+ /// ... other modal code
444
+
445
+ handleModalOpen() {
446
+ // open up the Modal
447
+ MyModal.open({
448
+ size: 'medium',
449
+ heading: 'Navigate to Record Page',
450
+ description: 'Navigate to a record page by clicking the row button',
451
+ options: [{ data }, { data }],
452
+ }).then((result) => {
453
+ // when the LightningModal closes, result is whatever
454
+ // data that was passed out using this.close({ data })
455
+ if (result === null) {
456
+ // do something else
457
+ } else {
458
+ // shouldNavigate is boolean, arbitrary
459
+ // rowId is what's needed for lightning-navigation
460
+ const { shouldNavigate, rowId } = result;
461
+ if (shouldNavigate) {
462
+ this.navigateToObjectHome(rowId);
463
+ }
464
+ }
465
+ });
466
+ }
467
+
468
+ navigateToObjectHome(rowId) {
469
+ // construct the page ref
470
+ const pageRef = {
471
+ type: 'standard__recordPage',
472
+ attributes: {
473
+ recordId: rowId,
474
+ actionName: 'view'
475
+ }
476
+ }
477
+ // then call the .Navigate method with the pageRef
478
+ this[NavigationMixin.Navigate](pageRef);
479
+ }
480
+
481
+ /* ... other modal code */
482
+ ```
483
+
484
+ This `template` is our modal, containing a tree grid with the navigation buttons, named `c-my-lwc-tree-grid-with-navigation`. The `handleNavSelection` function in this component processes the `rowId` data for the modal component to pass to its parent.
485
+
486
+ ```html
487
+ <!-- c/myModalWithTreeGridNavigation.html -->
488
+ <template>
489
+ <lightning-modal-header label="My Modal Heading">
490
+ The modal tagline.
491
+ </lightning-modal-header>
492
+ <lightning-modal-body>
493
+ <h2>Choose a record to navigate to:</h2>
494
+ <!-- custom event listener when button clicked inside tree grid -->
495
+ <div onnavselection={handleNavSelection}>
496
+ <c-my-lwc-tree-grid-with-navigation></c-my-lwc-tree-grid-with-navigation>
497
+ </div>
498
+ </lightning-modal-body>
499
+ <!-- no <lightning-modal-footer> in this example -->
500
+ </template>
501
+ ```
502
+
503
+ ```js
504
+ /* c/myModalWithTreeGridNavigation.js */
505
+ handleNavSelection(event) {
506
+ event.stopPropagation();
507
+ const { detail: { rowId } } = event;
508
+ if (rowId) {
509
+ // rowId value is important to lightning-navigation
510
+ // passing this data out through the LightningModal's
511
+ // .close() method, then handled in the .then((result) {})
512
+ // see code above: c/myLwcAppModalLauncher.js
513
+ this.close({ shouldNavigate: true, rowId });
514
+ }
515
+ }
516
+ ```
517
+
518
+ ```js
519
+ /* c/myLwcTreeGridWithNavigation.js */
520
+
521
+ dispatchNavSelectionEvent(rowId) {
522
+ // add rowId to event.detail
523
+ // this custom event listener exists in
524
+ // LightningModal body code, see code above:
525
+ // c/myModalWithTreeGridNavigation.html
526
+ const eventToFire = new CustomEvent('navselection', {
527
+ detail: { rowId },
528
+ bubbles: true,
529
+ cancelable: false
530
+ });
531
+ // dispatch custom event
532
+ // see event listener added to lightning-modal-body
533
+ this.dispatchEvent(eventToFire);
534
+ }
535
+
536
+ handleRowButtonClick(event) {
537
+ // simplified handler to get row.Id
538
+ // from the tree grid row button that was clicked
539
+ const row = event.detail.row;
540
+ this.dispatchNavSelectionEvent(row.Id);
541
+ }
542
+ ```
543
+
377
544
  #### Styling Variants
378
545
 
379
- The `lightning-modal-header` and `lightning-modal-footer` child components are optional, and you can choose to not include one or the other in your modal.
546
+ The `lightning-modal-header` and `lightning-modal-footer` child components are optional, and you can choose to not include one or the other in your modal.
380
547
 
381
548
  The headerless variant of `LightningModal` has these additional requirements.
382
549
  - Must set a descriptive modal title with the `label` property using `Modal.open({ label })` or you’ll get a console error.
383
- - The `label` property is required for all variants of `LightningModal`. Assistive devices read the `label` value, even though the headerless modal variant doesn't display the label.
550
+ - The `label` property is required for all variants of `LightningModal`. Assistive devices read the `label` value, even though the headerless modal variant doesn't display the label.
384
551
  - Because this variant doesn't use `lightning-modal-header`, you have to manually create an `<h1>` heading in `lightning-modal-body`. Provide accessible structure by starting with heading level `<h1>` and using levels up to `<h6>` appropriately. For more information, see [Semantic Structure, Headings on WebAim.org](https://webaim.org/techniques/semanticstructure/#headings).
385
552
 
386
553
  The `LightningModal` component also supports the SLDS [Directional variant](https://www.lightningdesignsystem.com/components/modals/#Directional) modal blueprint pattern.
387
554
 
388
- To achieve the directional button layout, place the buttons in a `div` with the `slds-modal__footer_directional` class.
555
+ To achieve the directional button layout, place the buttons in a `div` with the `slds-modal__footer_directional` class.
389
556
 
390
557
  ```html
391
558
  <!-- c/modalDirectional.html -->
@@ -403,7 +570,7 @@ To achieve the directional button layout, place the buttons in a `div` with the
403
570
 
404
571
  #### Styling Hooks
405
572
 
406
- The `lightning-modal-*` helper components support [style hooks](https://www.lightningdesignsystem.com/components/modals/#Styling-Hooks-Overview). The styling hooks for the template that invokes the helper components doesn't carry over to them, so you must style each helper component individually.
573
+ The `lightning-modal-*` helper components support [style hooks](https://www.lightningdesignsystem.com/components/modals/#Styling-Hooks-Overview). The styling hooks for the template that invokes the helper components doesn't carry over to them, so you must style each helper component individually.
407
574
 
408
575
  Customizing the styling on the white modal frame and background, close button, or gray backdrop isn't supported.
409
576
 
@@ -411,4 +578,4 @@ Customizing the styling on the white modal frame and background, close button, o
411
578
 
412
579
  The `lightning-modal-header` component renders the `label` value in an `<h1>` element. If your modal uses the header, begin any additional heading levels in the modal with `<h2>` for accessibility. Provide accessible structure by using heading levels up to `<h6>` appropriately. For more information, see [Semantic Structure, Headings on WebAim.org](https://webaim.org/techniques/semanticstructure/#headings).
413
580
 
414
- To include tagline text or link content for the header section, add it between the `<lightning-modal-header>` tags.
581
+ To include tagline text or link content for the header section, add it between the `<lightning-modal-header>` tags.
@@ -6,6 +6,7 @@ export const OVERLAY_TYPE = {
6
6
  DIALOG: 'lightning-dialog',
7
7
  POPOVER: 'lightning-popover',
8
8
  PANEL: 'uiPanel',
9
+ SLDSMODAL: 'slds-modal',
9
10
  };
10
11
 
11
12
  export function isOverlay(element) {
@@ -30,6 +31,13 @@ export function isOverlay(element) {
30
31
  if (isPanel) {
31
32
  return OVERLAY_TYPE.PANEL;
32
33
  }
34
+
35
+ const isSldsModal =
36
+ element.classList && element.classList.contains(OVERLAY_TYPE.SLDSMODAL);
37
+ if (isSldsModal) {
38
+ return OVERLAY_TYPE.SLDSMODAL;
39
+ }
40
+
33
41
  return OVERLAY_TYPE.NONE;
34
42
  }
35
43
 
@@ -80,7 +88,8 @@ export class OverlayDetector {
80
88
  return (
81
89
  this.isInside &&
82
90
  (this._detection.type === OVERLAY_TYPE.MODAL ||
83
- this._detection.type === OVERLAY_TYPE.DIALOG)
91
+ this._detection.type === OVERLAY_TYPE.DIALOG ||
92
+ this._detection.type === OVERLAY_TYPE.SLDSMODAL)
84
93
  );
85
94
  }
86
95
 
@@ -11,12 +11,26 @@ const i18n = {
11
11
  };
12
12
 
13
13
  export default class PrimitiveCellCheckbox extends PrimitiveDatatableCell {
14
+ _columnHeaderId = '';
14
15
  @api rowIndex = 0;
15
16
  @api isSelected = false;
16
17
  @api isDisabled = false;
17
18
  @api type = 'checkbox';
18
19
  @api dtContextId;
19
- @api columnHeaderId = ''; //required as a part for aria-labelledby
20
+ @api
21
+ get columnHeaderId() {
22
+ return this._columnHeaderId;
23
+ }
24
+
25
+ set columnHeaderId(id) {
26
+ this._columnHeaderId = id || '';
27
+ const labelId = this.computedLabelId;
28
+ if (labelId) {
29
+ synchronizeAttrs(this.template.querySelector('input'), {
30
+ 'aria-labelledby': `${labelId} ${this._columnHeaderId}`,
31
+ });
32
+ }
33
+ }
20
34
 
21
35
  render() {
22
36
  if (this.type === 'radio') {
@@ -1,4 +1,5 @@
1
1
  import labelChooseARow from '@salesforce/label/LightningDatatable.chooseARow';
2
+ import labelChooseARowSelectAll from '@salesforce/label/LightningDatatable.chooseARowSelectAll';
2
3
  import labelSelectAll from '@salesforce/label/LightningDatatable.selectAll';
3
4
  import labelSort from '@salesforce/label/LightningDatatable.sort';
4
5
  import labelSortAsc from '@salesforce/label/LightningDatatable.sortAsc';
@@ -15,6 +16,7 @@ import nonsortable from './nonsortableHeader.html';
15
16
 
16
17
  const i18n = {
17
18
  chooseARow: labelChooseARow,
19
+ chooseARowSelectAll: labelChooseARowSelectAll,
18
20
  selectAll: labelSelectAll,
19
21
  sort: labelSort,
20
22
  sortAsc: labelSortAsc,
@@ -201,6 +203,17 @@ export default class PrimitiveHeaderFactory extends PrimitiveDatatableCell {
201
203
  );
202
204
  }
203
205
 
206
+ /**
207
+ * Computes column header label
208
+ *
209
+ * @return {string} The computed column header label
210
+ */
211
+ get computedColumnHeaderLabel() {
212
+ return this.showCheckbox
213
+ ? this.i18n.chooseARowSelectAll
214
+ : this.i18n.chooseARow;
215
+ }
216
+
204
217
  /**
205
218
  * Returns the header's aria role
206
219
  *
@@ -316,6 +329,22 @@ export default class PrimitiveHeaderFactory extends PrimitiveDatatableCell {
316
329
  if (this.isSelectableHeader && this.showCheckbox) {
317
330
  this.updateBulkSelectionCheckbox();
318
331
  }
332
+ if (this.isSelectableHeader) {
333
+ const columnHeaderId = this.computedColumnHeaderId;
334
+ const columnHeaderEvent = new CustomEvent('privatecolumnheaderid', {
335
+ detail: columnHeaderId,
336
+ });
337
+ this.dispatchEvent(columnHeaderEvent);
338
+ }
339
+ }
340
+
341
+ disconnectedCallback() {
342
+ if (this.isSelectableHeader) {
343
+ const columnHeaderEvent = new CustomEvent('privatecolumnheaderid', {
344
+ detail: null,
345
+ });
346
+ this.dispatchEvent(columnHeaderEvent);
347
+ }
319
348
  }
320
349
 
321
350
  /************************** EVENT HANDLERS ***************************/
@@ -1,9 +1,11 @@
1
1
  <template>
2
+ <span id="column-group-header" class="slds-assistive-text" data-column-header>
3
+ {computedColumnHeaderLabel}
4
+ </span>
2
5
  <!-- Header Content -->
3
6
  <div class="slds-th__action slds-th__action_form slds-cell-fixed" style={columnStyles}>
4
7
  <template if:true={showCheckbox}>
5
8
  <span class="slds-checkbox">
6
-
7
9
  <!-- Selectable Checkbox -->
8
10
  <input
9
11
  type="checkbox"
@@ -23,10 +25,5 @@
23
25
  </label>
24
26
  </span>
25
27
  </template>
26
- <template if:false={showCheckbox}>
27
- <span id="column-group-header" class="slds-assistive-text" data-column-header>
28
- {i18n.chooseARow}
29
- </span>
30
- </template>
31
28
  </div>
32
29
  </template>
@@ -33,7 +33,7 @@ export default class LightningProgressIndicator extends LightningElement {
33
33
 
34
34
  /**
35
35
  * Changes the appearance of the progress indicator for the base type only.
36
- * Valid values are base or shaded. The shaded variant adds a light gray border to the step indicators.
36
+ * Valid values are base or shade. The shade variant adds a light gray border to the step indicators.
37
37
  * The default is base.
38
38
  * @type {string}
39
39
  * @default base
@@ -36,6 +36,9 @@ include:
36
36
  - Selecting of rows
37
37
  - Text wrapping and clipping
38
38
 
39
+ This component provides styling for up to 20 nested levels. For tree grids that require more than 20 nested levels,
40
+ build your own component.
41
+
39
42
  A checkbox is displayed by default in the first column. The `hide-checkbox-column` attribute
40
43
  removes the checkbox.
41
44
 
@@ -272,39 +272,34 @@ export default class LightningTreeGrid extends LightningElement {
272
272
 
273
273
  handleRowSelection(event) {
274
274
  event.stopPropagation();
275
- let selectedRowKeys;
276
275
  // pass the event through
277
- switch (event.detail.config.action) {
278
- case ROWS_ACTION.ROW_SELECT:
279
- selectedRowKeys = this._selectedRowKeys.slice();
280
- this._selectedRowKeys.push(event.detail.config.value);
281
- break;
282
- case ROWS_ACTION.ROW_DESELECT: {
283
- const index = this._selectedRowKeys.indexOf(
284
- event.detail.config.value
285
- );
286
- selectedRowKeys = this._selectedRowKeys.slice();
287
- this._selectedRowKeys.splice(index, 1);
288
- break;
276
+ if (event.detail.config) {
277
+ switch (event.detail.config.action) {
278
+ case ROWS_ACTION.ROW_SELECT:
279
+ this._selectedRowKeys.push(event.detail.config.value);
280
+ break;
281
+ case ROWS_ACTION.ROW_DESELECT: {
282
+ const index = this._selectedRowKeys.indexOf(
283
+ event.detail.config.value
284
+ );
285
+ this._selectedRowKeys.splice(index, 1);
286
+ break;
287
+ }
288
+ case ROWS_ACTION.SELECT_ALL_ROWS:
289
+ this._selectedRowKeys = this.getSelectedRowKeys(
290
+ event.detail.selectedRows
291
+ );
292
+ break;
293
+ case ROWS_ACTION.DESELECT_ALL_ROWS:
294
+ this._selectedRowKeys = this.getSelectedRowKeys(
295
+ event.detail.selectedRows
296
+ );
297
+ break;
298
+ default:
299
+ break;
289
300
  }
290
- case ROWS_ACTION.SELECT_ALL_ROWS:
291
- selectedRowKeys = this._selectedRowKeys.slice();
292
- this._selectedRowKeys = this.getSelectedRowKeys(
293
- event.detail.selectedRows
294
- );
295
- break;
296
- case ROWS_ACTION.DESELECT_ALL_ROWS:
297
- selectedRowKeys = this._selectedRowKeys.slice();
298
- this._selectedRowKeys = this.getSelectedRowKeys(
299
- event.detail.selectedRows
300
- );
301
- break;
302
- default:
303
- this._selectedRowKeys.push(event.detail.config.value);
304
- selectedRowKeys = this._selectedRowKeys.slice();
305
- break;
301
+ event.detail.config.selectedRowKeys = this._selectedRowKeys.slice();
306
302
  }
307
- event.detail.config.selectedRowKeys = selectedRowKeys;
308
303
  this.fireSelectedRowsChange(event.detail);
309
304
  }
310
305
 
@@ -25,6 +25,23 @@ export function normalizeBoolean(value) {
25
25
  return typeof value === 'string' || !!value;
26
26
  }
27
27
 
28
+ const isNotNumber = (value) => {
29
+ // Need to make sure it is a number than check isNaN
30
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN#difference_between_number.isnan_and_global_isnan
31
+ if (
32
+ Number.isNaN(value) ||
33
+ value === null ||
34
+ value === undefined ||
35
+ value === '' ||
36
+ Array.isArray(value)
37
+ ) {
38
+ return true;
39
+ }
40
+ // to eliminate non numeric string or other non numeric-typed objects
41
+ const convertedNumber = Number(value);
42
+ return Number.isNaN(convertedNumber);
43
+ };
44
+
28
45
  /**
29
46
  * A number normalization utility for attributes.
30
47
  * @param {number} value - The value to normalize.
@@ -39,18 +56,17 @@ export function normalizeNumber(value, config = {}) {
39
56
  const returnValueIfInvalid =
40
57
  (typeof fallbackValue === 'number' && fallbackValue) || undefined;
41
58
 
42
- // Need to make sure it is a number than check isNaN
43
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN#difference_between_number.isnan_and_global_isnan
44
- if (typeof value !== 'number' || Number.isNaN(value)) {
59
+ if (isNotNumber(value)) {
45
60
  return returnValueIfInvalid;
46
61
  }
47
- if (typeof minValue === 'number' && value < minValue) {
62
+ if (!isNotNumber(value) && value < minValue) {
48
63
  return returnValueIfInvalid;
49
64
  }
50
- if (typeof maxValue === 'number' && value > maxValue) {
65
+ if (!isNotNumber(value) && value > maxValue) {
51
66
  return returnValueIfInvalid;
52
67
  }
53
- return value;
68
+ // multiplying 1 is to make sure to convert from a numeric string to a number
69
+ return value * 1;
54
70
  }
55
71
 
56
72
  export function normalizeArray(value) {