lightning-base-components 1.13.10-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.
Files changed (69) hide show
  1. package/metadata/raptor.json +24 -0
  2. package/package.json +20 -4
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsPlural.js +1 -0
  5. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsSingular.js +1 -0
  6. package/scopedImports/@salesforce-label-LightningErrorMessage.validitySelectAtleastOne.js +1 -0
  7. package/scopedImports/@salesforce-label-LightningMap.titleWithAddress.js +1 -0
  8. package/scopedImports/@salesforce-label-LightningModalBase.cancelandclose.js +1 -0
  9. package/src/lightning/ariaObserver/__component__/ariaObserver.spec.js +112 -0
  10. package/src/lightning/ariaObserver/__docs__/ariaObserver.md +142 -0
  11. package/src/lightning/{utilsPrivate/contentMutation.js → ariaObserver/ariaObserver.js} +60 -98
  12. package/src/lightning/buttonMenu/keyboard.js +0 -10
  13. package/src/lightning/card/card.html +6 -0
  14. package/src/lightning/checkboxGroup/checkboxGroup.html +2 -2
  15. package/src/lightning/checkboxGroup/checkboxGroup.js +6 -1
  16. package/src/lightning/colorPickerCustom/colorPickerCustom.js +20 -1
  17. package/src/lightning/datatable/__docs__/datatable.md +55 -0
  18. package/src/lightning/datatable/__examples__/basic/basic.html +1 -1
  19. package/src/lightning/datatable/columns-shared.js +1 -1
  20. package/src/lightning/datatable/datatable.js +98 -30
  21. package/src/lightning/datatable/errors.js +20 -9
  22. package/src/lightning/datatable/headerActions.js +77 -49
  23. package/src/lightning/datatable/infiniteLoading.js +100 -28
  24. package/src/lightning/datatable/inlineEdit.js +505 -379
  25. package/src/lightning/datatable/inlineEditShared.js +24 -0
  26. package/src/lightning/datatable/keyboard.js +162 -127
  27. package/src/lightning/datatable/renderManager.js +201 -133
  28. package/src/lightning/datatable/rowLevelActions.js +17 -13
  29. package/src/lightning/datatable/rowNumber.js +54 -20
  30. package/src/lightning/datatable/rowSelection.js +760 -0
  31. package/src/lightning/datatable/rowSelectionShared.js +79 -0
  32. package/src/lightning/datatable/rows.js +17 -6
  33. package/src/lightning/datatable/state.js +16 -2
  34. package/src/lightning/datatable/templates/div/div.css +4 -0
  35. package/src/lightning/datatable/templates/div/div.html +6 -0
  36. package/src/lightning/datatable/templates/table/table.html +5 -0
  37. package/src/lightning/datatable/utils.js +14 -0
  38. package/src/lightning/datatable/wrapText.js +77 -47
  39. package/src/lightning/dualListbox/dualListbox.html +1 -1
  40. package/src/lightning/dualListbox/dualListbox.js +42 -0
  41. package/src/lightning/formattedDateTime/__docs__/formattedDateTime.md +36 -3
  42. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.html +2 -2
  43. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.js +3 -1
  44. package/src/lightning/formattedDateTime/__examples__/time/time.html +1 -1
  45. package/src/lightning/formattedDateTime/__examples__/time/time.js +3 -1
  46. package/src/lightning/formattedDateTime/formattedDateTime.js +1 -0
  47. package/src/lightning/input/input.html +1 -5
  48. package/src/lightning/input/input.js +69 -48
  49. package/src/lightning/inputUtils/validity.js +12 -1
  50. package/src/lightning/pillContainer/__docs__/pillContainer.md +45 -1
  51. package/src/lightning/primitiveCellActions/primitiveCellActions.js +69 -12
  52. package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +13 -11
  53. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +13 -8
  54. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.html +17 -14
  55. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +167 -98
  56. package/src/lightning/primitiveDatatableIeditTypeFactory/primitiveDatatableIeditTypeFactory.js +94 -69
  57. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.html +4 -4
  58. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +4 -4
  59. package/src/lightning/primitiveHeaderActions/primitiveHeaderActions.js +99 -37
  60. package/src/lightning/progressIndicator/progressIndicator.js +1 -1
  61. package/src/lightning/progressStep/progressStep.js +30 -22
  62. package/src/lightning/staticMap/staticMap.html +1 -0
  63. package/src/lightning/staticMap/staticMap.js +39 -2
  64. package/src/lightning/utils/classSet.js +4 -1
  65. package/src/lightning/utilsPrivate/utilsPrivate.js +12 -1
  66. package/scopedImports/@salesforce-label-LightningModalBase.close.js +0 -1
  67. package/src/lightning/datatable/inlineEdit-shared.js +0 -14
  68. package/src/lightning/datatable/selector-shared.js +0 -38
  69. package/src/lightning/datatable/selector.js +0 -527
@@ -1352,9 +1352,11 @@
1352
1352
  "industriesCibApi": {},
1353
1353
  "industriesDecisionMatrixDesignerApi": {},
1354
1354
  "industriesExplainabilityApi": {},
1355
+ "industriesHealthcloudHpiApi": {},
1355
1356
  "industriesIdentityVerificationApi": {},
1356
1357
  "industriesInterestTaggingApi": {},
1357
1358
  "industriesLoyaltyEngineApi": {},
1359
+ "industriesPublicSectorApi": {},
1358
1360
  "industriesRcgTenantmanagementApi": {},
1359
1361
  "industriesRuleBuilderApi": {},
1360
1362
  "industriesSustainabilityRecalculateApi": {},
@@ -3412,6 +3414,28 @@
3412
3414
  "uiRelatedListApi": {
3413
3415
  "minVersion": "53.0"
3414
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": {},
3415
3439
  "unstable_uiActionsApi": {},
3416
3440
  "unstable_uiAppsApi": {},
3417
3441
  "unstable_uiDuplicatesApi": {},
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "lightning-base-components",
3
- "version": "1.13.10-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",
@@ -57,6 +57,10 @@
57
57
  "name": "@salesforce/label/LightningErrorMessage.validityTooShort",
58
58
  "path": "scopedImports/@salesforce-label-LightningErrorMessage.validityTooShort.js"
59
59
  },
60
+ {
61
+ "name": "@salesforce/label/LightningErrorMessage.validitySelectAtleastOne",
62
+ "path": "scopedImports/@salesforce-label-LightningErrorMessage.validitySelectAtleastOne.js"
63
+ },
60
64
  {
61
65
  "name": "@salesforce/label/LightningCarousel.tabString",
62
66
  "path": "scopedImports/@salesforce-label-LightningCarousel.tabString.js"
@@ -369,6 +373,14 @@
369
373
  "name": "@salesforce/label/LightningDualListbox.requiredOptionError",
370
374
  "path": "scopedImports/@salesforce-label-LightningDualListbox.requiredOptionError.js"
371
375
  },
376
+ {
377
+ "name": "@salesforce/label/LightningDualListbox.movedOptionsSingular",
378
+ "path": "scopedImports/@salesforce-label-LightningDualListbox.movedOptionsSingular.js"
379
+ },
380
+ {
381
+ "name": "@salesforce/label/LightningDualListbox.movedOptionsPlural",
382
+ "path": "scopedImports/@salesforce-label-LightningDualListbox.movedOptionsPlural.js"
383
+ },
372
384
  {
373
385
  "name": "@salesforce/label/LightningProgressIndicator.stageComplete",
374
386
  "path": "scopedImports/@salesforce-label-LightningProgressIndicator.stageComplete.js"
@@ -861,6 +873,10 @@
861
873
  "name": "@salesforce/label/LightningMap.iframeTitle",
862
874
  "path": "scopedImports/@salesforce-label-LightningMap.iframeTitle.js"
863
875
  },
876
+ {
877
+ "name": "@salesforce/label/LightningMap.titleWithAddress",
878
+ "path": "scopedImports/@salesforce-label-LightningMap.titleWithAddress.js"
879
+ },
864
880
  {
865
881
  "name": "@salesforce/label/LightningPrimitiveCoordinate.selected",
866
882
  "path": "scopedImports/@salesforce-label-LightningPrimitiveCoordinate.selected.js"
@@ -942,8 +958,8 @@
942
958
  "path": "scopedImports/@salesforce-label-LightningRating.nStars.js"
943
959
  },
944
960
  {
945
- "name": "@salesforce/label/LightningModalBase.close",
946
- "path": "scopedImports/@salesforce-label-LightningModalBase.close.js"
961
+ "name": "@salesforce/label/LightningModalBase.cancelandclose",
962
+ "path": "scopedImports/@salesforce-label-LightningModalBase.cancelandclose.js"
947
963
  },
948
964
  {
949
965
  "name": "@salesforce/label/LightningModalBase.waitstate",
@@ -1 +1 @@
1
- export default '234';
1
+ export default '236';
@@ -0,0 +1 @@
1
+ export default 'Items {0} moved to the list {1}';
@@ -0,0 +1 @@
1
+ export default 'Item {0} moved to the list {1}';
@@ -0,0 +1 @@
1
+ export default 'You must select at least one choice from this set.';
@@ -0,0 +1 @@
1
+ export default 'Map of {0}';
@@ -0,0 +1 @@
1
+ export default 'Cancel and close';
@@ -0,0 +1,112 @@
1
+ import { createElement } from 'lwc';
2
+ import AriaObserverContainer from 'lightningtest/ariaObserverContainer';
3
+
4
+ function createTestElement(props = {}) {
5
+ const element = createElement('lightningtest-aria-observer-container', {
6
+ is: AriaObserverContainer,
7
+ });
8
+ Object.assign(element, props);
9
+ document.body.appendChild(element);
10
+
11
+ return element;
12
+ }
13
+
14
+ describe('AriaObserver', () => {
15
+ if (process.env.NATIVE_SHADOW) {
16
+ describe('native shadow', () => {
17
+ it('should copy the label content over and set aria-labelledby to be the internal label element', () => {
18
+ const container = createTestElement();
19
+ const testElement = container.testElement;
20
+
21
+ expect(container.labelContent).toEqual(
22
+ testElement.labelContent
23
+ );
24
+ });
25
+
26
+ it('should react to label id changes', async () => {
27
+ const container = createTestElement();
28
+ const oldLabelContent = container.labelContent;
29
+
30
+ container.updateAriaLabelledby('alt-label-id');
31
+ await Promise.resolve();
32
+
33
+ const testElement = container.testElement;
34
+
35
+ expect(container.labelContent).not.toEqual(oldLabelContent);
36
+ expect(container.labelContent).toEqual(
37
+ testElement.labelContent
38
+ );
39
+ });
40
+
41
+ it('should work with multiple label ids', async () => {
42
+ const container = createTestElement();
43
+ container.updateAriaLabelledby('id-label alt-label-id');
44
+ await Promise.resolve();
45
+
46
+ const testElement = container.testElement;
47
+ expect(testElement.labelContent).toEqual('Foo\nBar');
48
+ });
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
+
59
+ it('should update the internal label content when external content changes', async () => {
60
+ const container = createTestElement();
61
+ container.updateLabelContent();
62
+
63
+ // wait for component rehydration
64
+ await Promise.resolve();
65
+ // wait for mutation observer callback
66
+ await Promise.resolve();
67
+
68
+ const testElement = container.testElement;
69
+ expect(container.labelContent).toEqual(
70
+ testElement.labelContent
71
+ );
72
+ });
73
+ });
74
+ } else {
75
+ describe('synthetic shadow', () => {
76
+ it('should set aria-labelledby to be the external label id', () => {
77
+ const container = createTestElement();
78
+ const testElement = container.testElement;
79
+
80
+ expect(container.labelId).toEqual(testElement.labelId);
81
+ });
82
+
83
+ it('should react to label id changes', async () => {
84
+ const container = createTestElement();
85
+ const oldLabelId = container.labelId;
86
+
87
+ container.updateAriaLabelledby('alt-label-id');
88
+ await Promise.resolve();
89
+
90
+ const testElement = container.testElement;
91
+
92
+ expect(container.labelId).not.toEqual(oldLabelId);
93
+ expect(container.labelId).toEqual(testElement.labelId);
94
+ });
95
+
96
+ it('should keep the label id unchanged when the label content changes', async () => {
97
+ const container = createTestElement();
98
+ container.updateLabelContent();
99
+ await Promise.resolve();
100
+
101
+ const testElement = container.testElement;
102
+ expect(container.labelId).toEqual(testElement.labelId);
103
+ });
104
+
105
+ it('should keep track of the live id', () => {
106
+ const container = createTestElement();
107
+ const testElement = container.testElement;
108
+ expect(testElement.labelId).toEqual(testElement.liveId);
109
+ });
110
+ });
111
+ }
112
+ });
@@ -0,0 +1,142 @@
1
+ The `lightning/ariaObserver` module provides an easy way for users to write accessible component that works in both synthetic and native shadow.
2
+
3
+ ## Aria ID referencing in native shadow
4
+ Use the` AriaObserver` library to write accessible component that works where `ariaLabelledBy` would break native shadow.
5
+
6
+ Here's an example that won't work with native shadow. In the following code, we support attribute `ariaLabelledBy` in our component `c-foo`, so the `input` element is labelled by external elements.
7
+
8
+ ``` html
9
+ <template>
10
+ <input aria-labelledby={ariaLabelledBy}>
11
+ </template>
12
+ ```
13
+
14
+ ```
15
+ class Foo extends LightningElement {
16
+ @api ariaLabelledBy;
17
+ }
18
+ ```
19
+
20
+ This example uses the `aria-labelledby` attribute to use the internal input as an external label in `c-foo`.
21
+
22
+ ``` html
23
+ <span id="my-label">Input field</span>
24
+ <c-foo aria-labelledby="my-label"></c-foo>
25
+ ```
26
+
27
+ The above example works fine in synthetic shadow, but in native shadow mode, the `aria-labelledby` ID reference is broken. The `input` element is isolated in its own shadow DOM, so the label with id `my-label` isn't in the same shadow boundary.
28
+
29
+ ## Creating AriaObserver
30
+
31
+ To use `AriaObserver` in your component, first import it from `lightning/ariaObserver`. Then, instantiate the `AriaObserver` within your component.
32
+
33
+ The `AriaObserver` constructor takes one parameter:
34
+ - `cmpReference` The reference of the current component (`this`).
35
+
36
+ ``` js
37
+ import AriaObserver from 'lightning/ariaObserver';
38
+
39
+ class Foo extends LightningElement {
40
+ constructor() {
41
+ super();
42
+ this.ariaObserver = new AriaObserver(this);
43
+ }
44
+ }
45
+ ```
46
+
47
+ Next, use the `connect(options)` method to connect between the internal element and the external reference. It takes an options object with the following keys:
48
+ - `targetSelector` The selector to the internal element where the aria attribute should be attached.
49
+ - `attribute` The name of the aria attribute. Two supported options: `aria-labelledby`and `aria-describedby`.
50
+ - `id` The ID of the external element. Alternatively, you can use `ids` for multiple IDs.
51
+
52
+ This example uses `connect(options)` to display an aria label for the internal `input` element.
53
+ ``` js
54
+ @api
55
+ get ariaLabelledBy() {
56
+ return this._ariaLabelledBy;
57
+ }
58
+ set ariaLabelledBy(refs) {
59
+ this._ariaLabelledBy = refs;
60
+
61
+ this.ariaObserver.connect({
62
+ targetSelector: 'input',
63
+ attribute: 'aria-labelledby',
64
+ id: refs
65
+ });
66
+ }
67
+ ```
68
+
69
+ Then use the `sync()` method to synchronize the ID references when the template is re-rendered.
70
+
71
+ ``` js
72
+ renderedCallback() {
73
+ this.ariaObserver.sync();
74
+ }
75
+ ```
76
+
77
+ Finally, disconnect the aria observer and free the resources at the end of the component lifecycle.
78
+
79
+ ``` js
80
+ disconnectedCallback() {
81
+ if (this.ariaObserver) {
82
+ this.ariaObserver.disconnect();
83
+ this.ariaObserver = undefined;
84
+ }
85
+ }
86
+ ```
87
+
88
+ Here is all these steps combined into a complete example of a component using `AriaObserver`.
89
+
90
+ ``` html
91
+ <template>
92
+ <!-- element where the aria attribute is attached -->
93
+ <input>
94
+ </template>
95
+ ```
96
+
97
+ ``` js
98
+ import {api, LightningElement} from 'lwc';
99
+ import AriaObserver from 'lightning/ariaObserver';
100
+
101
+ export default class Foo extends LightningElement {
102
+ constructor() {
103
+ super();
104
+ this.ariaObserver = new AriaObserver(this);
105
+ }
106
+
107
+ _ariaLabelledBy = '';
108
+
109
+ @api
110
+ get ariaLabelledBy() {
111
+ return this._ariaLabelledBy;
112
+ }
113
+ set ariaLabelledBy(refs) {
114
+ this._ariaLabelledBy = refs;
115
+
116
+ /* Establish the connection between input and the external label */
117
+ this.ariaObserver.connect({
118
+ targetSelector: 'input',
119
+ attribute: 'aria-labelledby',
120
+ id: refs
121
+ });
122
+ }
123
+
124
+ renderedCallback() {
125
+ this.ariaObserver.sync();
126
+ }
127
+
128
+ disconnectedCallback() {
129
+ if (this.ariaObserver) {
130
+ this.ariaObserver.disconnect();
131
+ this.ariaObserver = undefined;
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ ## Limitations
138
+ `AriaObserver` only works with text-only aria ID references.
139
+
140
+ Supported attributes:
141
+ - `aria-labelledby`
142
+ - `aria-describedby`
@@ -1,50 +1,11 @@
1
- import { guid } from './guid';
2
- import { smartSetAttribute } from './smartSetAttribute';
1
+ import {
2
+ guid,
3
+ synchronizeAttrs,
4
+ isNativeComponent,
5
+ } from 'lightning/utilsPrivate';
3
6
 
4
7
  const CONTENT_SEPARATOR = '\n';
5
8
 
6
- /**
7
- <template>
8
- <span lwc:dom="manual" class="visually-hidden"></span>
9
- <input>
10
- </template>
11
-
12
- class Foo extends LightningElement {
13
- constructor() {
14
- super();
15
- this.ariaObserver = new ContentMutation(this);
16
- }
17
-
18
- connectedCallback() {
19
- if (!this.ariaObserver) {
20
- this.ariaObserver = new ContentMutation(this);
21
- }
22
- }
23
-
24
- disconnectedCallback() {
25
- if (this.ariaObserver) {
26
- this.ariaObserver.disconnect();
27
- this.ariaObserver = undefined;
28
- }
29
- }
30
-
31
- @track ariaLabeledbyValue = '';
32
-
33
- @api
34
- get ariaLabeledby() {
35
- return this.ariaLabeledbyValue; // whatever they set, is what they get back.
36
- }
37
- set ariaLabeledby(refs) {
38
- this.ariaLabeledbyValue = refs;
39
- this.ariaObserver.link('input', 'aria-labeledby', refs, 'span.visually-hidden');
40
- }
41
-
42
- renderedCallback() {
43
- this.ariaObserver.sync();
44
- }
45
- }
46
- **/
47
-
48
9
  function getAttr(elm, attr) {
49
10
  if (elm.tagName.match(/lightning/i)) {
50
11
  return elm[attr];
@@ -52,11 +13,13 @@ function getAttr(elm, attr) {
52
13
  return elm.getAttribute(attr);
53
14
  }
54
15
 
55
- function extractElements(root, selector) {
56
- if (typeof selector !== 'string' || selector === '') {
16
+ function extractElements(root, ids) {
17
+ if (typeof ids !== 'string' || ids === '') {
57
18
  return [];
58
19
  }
59
- 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}`));
60
23
  }
61
24
 
62
25
  function extractContent(elements) {
@@ -95,11 +58,10 @@ function addAriaRefWhenNeeded(elm, attrName, computedIds) {
95
58
  }
96
59
 
97
60
  if (suffix.length !== 0) {
98
- smartSetAttribute(
99
- elm,
100
- attrName,
101
- oldIds + (oldIds.length === 0 ? '' : ' ') + suffix.join(' ')
102
- );
61
+ synchronizeAttrs(elm, {
62
+ [attrName]:
63
+ oldIds + (oldIds.length === 0 ? '' : ' ') + suffix.join(' '),
64
+ });
103
65
  }
104
66
  }
105
67
 
@@ -116,19 +78,28 @@ function removeAriaRefWhenPossible(elm, attrName, computedIds) {
116
78
  newValues.push(newIds[i]);
117
79
  }
118
80
  }
119
- smartSetAttribute(elm, attrName, newValues.join(' '));
81
+
82
+ synchronizeAttrs(elm, {
83
+ [attrName]: newValues.join(' '),
84
+ });
85
+ }
86
+
87
+ function createPlaceholderContainer() {
88
+ const container = document.createElement('span');
89
+ container.style.display = 'none';
90
+ container.setAttribute('placeholder-container', '');
91
+ return container;
120
92
  }
121
93
 
122
- export class ContentMutation {
94
+ export default class AriaObserver {
123
95
  constructor(component) {
124
96
  this.component = component;
125
97
  this.template = component.template;
126
- this.isNative = this.template.constructor
127
- .toString()
128
- .match(/\[native code\]/);
98
+ this.isNative = isNativeComponent(component);
129
99
  this.state = {};
130
100
  this.liveIds = {};
131
101
  this.guid = guid();
102
+ this.placeholderContainer = null;
132
103
  }
133
104
 
134
105
  connectLiveIdRef(refs, callback) {
@@ -141,37 +112,33 @@ export class ContentMutation {
141
112
  this.liveIds[refs] = liveId;
142
113
  }
143
114
 
144
- link(innerSelector, attrName, ids, placeholderContainerSelector) {
145
- let attrState = this.state[attrName];
146
- if (attrState) {
147
- // note: we don't support linking to a different innerSelector,
148
- // attrName, or placeholderContainerSelector
149
- if (!this.isNative) {
150
- const elm = this.template.querySelector(innerSelector);
151
- if (elm) {
152
- // removing the old ids if possible before setting the new ones
153
- removeAriaRefWhenPossible(elm, attrName, attrState.ids);
154
- }
155
- attrState.ids = ids;
115
+ connect({ targetSelector, attribute, id, ids }) {
116
+ ids = ids || id;
117
+
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);
156
129
  }
157
- } else {
158
- attrState = this.state[attrName] = {
159
- ids,
160
- innerSelector,
161
- placeholderContainerSelector,
162
- };
163
130
  }
164
- if (this.isNative) {
165
- attrState.outerSelector = (ids + '')
166
- .trim()
167
- .split(/\s+/)
168
- .map((ref) => `#${ref}`)
169
- .join(',');
131
+
132
+ attrState.ids = ids;
133
+
134
+ if (this.isNative && !attrState.placeholder) {
135
+ // create placeholder element for copied content
170
136
  attrState.placeholder = document.createElement('span');
171
- attrState.placeholder.id = `auto-link-${attrName}-${this.guid}`;
137
+ attrState.placeholder.id = `auto-link-${attribute}-${this.guid}`;
172
138
  }
139
+
173
140
  if (this.component.isConnected) {
174
- this.privateUpdate(attrName);
141
+ this.privateUpdate(attribute);
175
142
  }
176
143
  }
177
144
 
@@ -238,27 +205,22 @@ export class ContentMutation {
238
205
  }
239
206
  let computedIds;
240
207
  if (this.isNative) {
241
- const {
242
- outerSelector,
243
- content,
244
- placeholder,
245
- placeholderContainerSelector,
246
- } = this.state[attrName];
247
- const newContent = extractContent(
248
- extractElements(this.root, outerSelector)
249
- );
208
+ const { ids, content, placeholder } = this.state[attrName];
209
+
210
+ const newContent = extractContent(extractElements(this.root, ids));
250
211
  if (content !== newContent) {
251
212
  this.state[attrName].content = placeholder.textContent =
252
213
  newContent;
253
214
  }
254
215
  if (!placeholder.parentNode) {
255
- // inserting the placeholder once
256
- const container = this.template.querySelector(
257
- placeholderContainerSelector
258
- );
259
- if (container) {
260
- container.appendChild(placeholder);
216
+ // create placeholder container at template root, if not already exist
217
+ if (!this.placeholderContainer) {
218
+ this.placeholderContainer = createPlaceholderContainer();
219
+ this.template.appendChild(this.placeholderContainer);
261
220
  }
221
+
222
+ // inserting the placeholder once
223
+ this.placeholderContainer.appendChild(placeholder);
262
224
  }
263
225
  computedIds = placeholder.id;
264
226
  } else {
@@ -113,16 +113,6 @@ export function handleKeyDownOnMenuTrigger(event, menuInterface) {
113
113
  menuInterface.toggleMenuVisibility();
114
114
  }
115
115
  break;
116
- // W3: Home and End: If arrow key wrapping is not supported, move focus to first and last item
117
- // Note: We do support wrapping, but it doesn't hurt to support these keys anyway.
118
- case keyCodes.home:
119
- preventDefaultAndStopPropagation(event);
120
- menuInterface.focusOnIndex(0);
121
- break;
122
- case keyCodes.end:
123
- preventDefaultAndStopPropagation(event);
124
- menuInterface.focusOnIndex(menuInterface.getTotalMenuItems() - 1);
125
- break;
126
116
  // W3: Escape: Close the menu and return focus to the element or context, e.g., menu button or
127
117
  // parent menu item, from which the menu was opened
128
118
  case keyCodes.escape:
@@ -40,5 +40,11 @@
40
40
  <slot name="footer"></slot>
41
41
  </div>
42
42
  </template>
43
+ <!-- This dummy placeholder slot is required because
44
+ a slot's presence is verified by assignedElements count
45
+ and not whether the slot itself is present or not -->
46
+ <template if:false={showFooter}>
47
+ <slot name="footer" class="slds-hide"></slot>
48
+ </template>
43
49
  </article>
44
50
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <fieldset aria-required={required}>
2
+ <fieldset aria-describedby={computearaiDescriedBy}>
3
3
 
4
4
  <legend class={computedLegendClass}>
5
5
  <template if:true={required}>
@@ -32,6 +32,6 @@
32
32
  <template if:true={_helpMessage}>
33
33
  <div id="helptext" data-helptext class="slds-form-element__help">{_helpMessage}</div>
34
34
  </template>
35
-
35
+
36
36
  </fieldset>
37
37
  </template>
@@ -295,7 +295,7 @@ export default class LightningCheckboxGroup extends LightningElement {
295
295
  get _constraint() {
296
296
  if (!this._constraintApi) {
297
297
  this._constraintApi = new FieldConstraintApi(() => this, {
298
- valueMissing: () =>
298
+ validitySelectAtleastOneValue: () =>
299
299
  !this.disabled && this.required && this.value.length === 0,
300
300
  });
301
301
  }
@@ -313,4 +313,9 @@ export default class LightningCheckboxGroup extends LightningElement {
313
313
  })
314
314
  .toString();
315
315
  }
316
+
317
+ computearaiDescriedBy() {
318
+ const helpMessage = this.template.querySelector('[data-helptext]');
319
+ return getRealDOMId(helpMessage);
320
+ }
316
321
  }