lightning-base-components 1.13.9-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 (83) hide show
  1. package/metadata/raptor.json +1 -0
  2. package/package.json +21 -1
  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-LightningLookup.recentItems.js +1 -0
  8. package/scopedImports/@salesforce-label-LightningMap.titleWithAddress.js +1 -0
  9. package/src/lightning/ariaObserver/__component__/ariaObserver.spec.js +103 -0
  10. package/src/lightning/ariaObserver/__docs__/ariaObserver.md +142 -0
  11. package/src/lightning/{utilsPrivate/contentMutation.js → ariaObserver/ariaObserver.js} +51 -78
  12. package/src/lightning/baseCombobox/baseCombobox.html +1 -0
  13. package/src/lightning/baseCombobox/baseCombobox.js +14 -1
  14. package/src/lightning/buttonMenu/keyboard.js +0 -10
  15. package/src/lightning/card/card.html +6 -0
  16. package/src/lightning/checkboxGroup/checkboxGroup.html +2 -2
  17. package/src/lightning/checkboxGroup/checkboxGroup.js +6 -1
  18. package/src/lightning/colorPickerCustom/colorPickerCustom.js +20 -1
  19. package/src/lightning/combobox/combobox.css +12 -0
  20. package/src/lightning/combobox/combobox.html +1 -0
  21. package/src/lightning/datatable/__docs__/datatable.md +55 -0
  22. package/src/lightning/datatable/__examples__/basic/basic.html +1 -1
  23. package/src/lightning/datatable/columnWidthManager.js +7 -3
  24. package/src/lightning/datatable/columns-shared.js +1 -1
  25. package/src/lightning/datatable/datatable.js +122 -40
  26. package/src/lightning/datatable/errors.js +20 -9
  27. package/src/lightning/datatable/headerActions.js +77 -49
  28. package/src/lightning/datatable/inlineEdit.js +520 -373
  29. package/src/lightning/datatable/inlineEditShared.js +24 -0
  30. package/src/lightning/datatable/keyboard.js +1077 -933
  31. package/src/lightning/datatable/renderManager.js +241 -129
  32. package/src/lightning/datatable/resizer.js +91 -108
  33. package/src/lightning/datatable/rowLevelActions.js +17 -13
  34. package/src/lightning/datatable/rowNumber.js +54 -20
  35. package/src/lightning/datatable/rowSelection.js +760 -0
  36. package/src/lightning/datatable/rowSelectionShared.js +79 -0
  37. package/src/lightning/datatable/rows.js +16 -5
  38. package/src/lightning/datatable/state.js +10 -10
  39. package/src/lightning/datatable/templates/div/div.css +23 -0
  40. package/src/lightning/datatable/templates/div/div.html +6 -5
  41. package/src/lightning/datatable/templates/table/table.html +4 -4
  42. package/src/lightning/datatable/utils.js +14 -0
  43. package/src/lightning/datatable/widthManagerShared.js +1 -1
  44. package/src/lightning/dualListbox/dualListbox.html +1 -1
  45. package/src/lightning/dualListbox/dualListbox.js +42 -0
  46. package/src/lightning/iconSvgTemplates/buildTemplates/templates.js +2 -1
  47. package/src/lightning/iconSvgTemplates/buildTemplates/utility/contract_alt.html +1 -2
  48. package/src/lightning/iconSvgTemplates/buildTemplates/utility/contract_doc.html +8 -0
  49. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/templates.js +2 -1
  50. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/contract_alt.html +1 -2
  51. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/contract_doc.html +8 -0
  52. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/templates.js +2 -1
  53. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/contract_alt.html +1 -2
  54. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/contract_doc.html +8 -0
  55. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/templates.js +2 -1
  56. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/contract_alt.html +1 -2
  57. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/contract_doc.html +8 -0
  58. package/src/lightning/input/input.html +0 -1
  59. package/src/lightning/input/input.js +69 -48
  60. package/src/lightning/inputUtils/validity.js +12 -1
  61. package/src/lightning/pillContainer/__docs__/pillContainer.md +45 -1
  62. package/src/lightning/primitiveCellActions/primitiveCellActions.js +69 -12
  63. package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +13 -11
  64. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +13 -8
  65. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.html +17 -14
  66. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +169 -80
  67. package/src/lightning/primitiveDatatableIeditTypeFactory/primitiveDatatableIeditTypeFactory.js +97 -62
  68. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.html +4 -4
  69. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +4 -4
  70. package/src/lightning/primitiveHeaderActions/primitiveHeaderActions.js +99 -37
  71. package/src/lightning/primitiveHeaderFactory/nonsortableHeader.html +5 -4
  72. package/src/lightning/primitiveHeaderFactory/primitiveHeaderFactory.js +46 -46
  73. package/src/lightning/primitiveHeaderFactory/selectableHeader.html +25 -23
  74. package/src/lightning/primitiveHeaderFactory/sortableHeader.html +13 -9
  75. package/src/lightning/progressIndicator/progressIndicator.js +4 -6
  76. package/src/lightning/progressStep/progressStep.js +31 -22
  77. package/src/lightning/staticMap/staticMap.html +1 -0
  78. package/src/lightning/staticMap/staticMap.js +39 -2
  79. package/src/lightning/utils/classSet.js +4 -1
  80. package/src/lightning/utilsPrivate/utilsPrivate.js +12 -1
  81. package/src/lightning/datatable/inlineEdit-shared.js +0 -14
  82. package/src/lightning/datatable/selector-shared.js +0 -38
  83. package/src/lightning/datatable/selector.js +0 -527
@@ -1352,6 +1352,7 @@
1352
1352
  "industriesCibApi": {},
1353
1353
  "industriesDecisionMatrixDesignerApi": {},
1354
1354
  "industriesExplainabilityApi": {},
1355
+ "industriesHealthcloudHpiApi": {},
1355
1356
  "industriesIdentityVerificationApi": {},
1356
1357
  "industriesInterestTaggingApi": {},
1357
1358
  "industriesLoyaltyEngineApi": {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightning-base-components",
3
- "version": "1.13.9-alpha",
3
+ "version": "1.14.3-alpha",
4
4
  "engines": {
5
5
  "node": ">=12.18.3"
6
6
  },
@@ -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"
@@ -793,6 +805,10 @@
793
805
  "name": "@salesforce/label/LightningLookup.recentObject",
794
806
  "path": "scopedImports/@salesforce-label-LightningLookup.recentObject.js"
795
807
  },
808
+ {
809
+ "name": "@salesforce/label/LightningLookup.recentItems",
810
+ "path": "scopedImports/@salesforce-label-LightningLookup.recentItems.js"
811
+ },
796
812
  {
797
813
  "name": "@salesforce/label/LightningLookup.resultsListHeaderMobile",
798
814
  "path": "scopedImports/@salesforce-label-LightningLookup.resultsListHeaderMobile.js"
@@ -857,6 +873,10 @@
857
873
  "name": "@salesforce/label/LightningMap.iframeTitle",
858
874
  "path": "scopedImports/@salesforce-label-LightningMap.iframeTitle.js"
859
875
  },
876
+ {
877
+ "name": "@salesforce/label/LightningMap.titleWithAddress",
878
+ "path": "scopedImports/@salesforce-label-LightningMap.titleWithAddress.js"
879
+ },
860
880
  {
861
881
  "name": "@salesforce/label/LightningPrimitiveCoordinate.selected",
862
882
  "path": "scopedImports/@salesforce-label-LightningPrimitiveCoordinate.selected.js"
@@ -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 'Recent Items';
@@ -0,0 +1 @@
1
+ export default 'Map of {0}';
@@ -0,0 +1,103 @@
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 update the internal label content when external content changes', async () => {
51
+ const container = createTestElement();
52
+ container.updateLabelContent();
53
+
54
+ // wait for component rehydration
55
+ await Promise.resolve();
56
+ // wait for mutation observer callback
57
+ await Promise.resolve();
58
+
59
+ const testElement = container.testElement;
60
+ expect(container.labelContent).toEqual(
61
+ testElement.labelContent
62
+ );
63
+ });
64
+ });
65
+ } else {
66
+ describe('synthetic shadow', () => {
67
+ it('should set aria-labelledby to be the external label id', () => {
68
+ const container = createTestElement();
69
+ const testElement = container.testElement;
70
+
71
+ expect(container.labelId).toEqual(testElement.labelId);
72
+ });
73
+
74
+ it('should react to label id changes', async () => {
75
+ const container = createTestElement();
76
+ const oldLabelId = container.labelId;
77
+
78
+ container.updateAriaLabelledby('alt-label-id');
79
+ await Promise.resolve();
80
+
81
+ const testElement = container.testElement;
82
+
83
+ expect(container.labelId).not.toEqual(oldLabelId);
84
+ expect(container.labelId).toEqual(testElement.labelId);
85
+ });
86
+
87
+ it('should keep the label id unchanged when the label content changes', async () => {
88
+ const container = createTestElement();
89
+ container.updateLabelContent();
90
+ await Promise.resolve();
91
+
92
+ const testElement = container.testElement;
93
+ expect(container.labelId).toEqual(testElement.labelId);
94
+ });
95
+
96
+ it('should keep track of the live id', () => {
97
+ const container = createTestElement();
98
+ const testElement = container.testElement;
99
+ expect(testElement.labelId).toEqual(testElement.liveId);
100
+ });
101
+ });
102
+ }
103
+ });
@@ -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];
@@ -95,11 +56,10 @@ function addAriaRefWhenNeeded(elm, attrName, computedIds) {
95
56
  }
96
57
 
97
58
  if (suffix.length !== 0) {
98
- smartSetAttribute(
99
- elm,
100
- attrName,
101
- oldIds + (oldIds.length === 0 ? '' : ' ') + suffix.join(' ')
102
- );
59
+ synchronizeAttrs(elm, {
60
+ [attrName]:
61
+ oldIds + (oldIds.length === 0 ? '' : ' ') + suffix.join(' '),
62
+ });
103
63
  }
104
64
  }
105
65
 
@@ -116,19 +76,28 @@ function removeAriaRefWhenPossible(elm, attrName, computedIds) {
116
76
  newValues.push(newIds[i]);
117
77
  }
118
78
  }
119
- smartSetAttribute(elm, attrName, newValues.join(' '));
79
+
80
+ synchronizeAttrs(elm, {
81
+ [attrName]: newValues.join(' '),
82
+ });
120
83
  }
121
84
 
122
- export class ContentMutation {
85
+ function createPlaceholderContainer() {
86
+ const container = document.createElement('span');
87
+ container.style.display = 'none';
88
+ container.setAttribute('placeholder-container', '');
89
+ return container;
90
+ }
91
+
92
+ export default class AriaObserver {
123
93
  constructor(component) {
124
94
  this.component = component;
125
95
  this.template = component.template;
126
- this.isNative = this.template.constructor
127
- .toString()
128
- .match(/\[native code\]/);
96
+ this.isNative = isNativeComponent(component);
129
97
  this.state = {};
130
98
  this.liveIds = {};
131
99
  this.guid = guid();
100
+ this.placeholderContainer = null;
132
101
  }
133
102
 
134
103
  connectLiveIdRef(refs, callback) {
@@ -141,24 +110,26 @@ export class ContentMutation {
141
110
  this.liveIds[refs] = liveId;
142
111
  }
143
112
 
144
- link(innerSelector, attrName, ids, placeholderContainerSelector) {
145
- let attrState = this.state[attrName];
113
+ connect({ targetSelector, attribute, id, ids }) {
114
+ ids = ids || id;
115
+
116
+ let attrState = this.state[attribute];
146
117
  if (attrState) {
147
- // note: we don't support linking to a different innerSelector,
148
- // attrName, or placeholderContainerSelector
118
+ // note: we don't support linking to a different targetSelector, attribute
149
119
  if (!this.isNative) {
150
- const elm = this.template.querySelector(innerSelector);
120
+ const elm = this.template.querySelector(
121
+ attrState.innerSelector
122
+ );
151
123
  if (elm) {
152
124
  // removing the old ids if possible before setting the new ones
153
- removeAriaRefWhenPossible(elm, attrName, attrState.ids);
125
+ removeAriaRefWhenPossible(elm, attribute, attrState.ids);
154
126
  }
155
127
  attrState.ids = ids;
156
128
  }
157
129
  } else {
158
- attrState = this.state[attrName] = {
130
+ attrState = this.state[attribute] = {
159
131
  ids,
160
- innerSelector,
161
- placeholderContainerSelector,
132
+ innerSelector: targetSelector,
162
133
  };
163
134
  }
164
135
  if (this.isNative) {
@@ -167,11 +138,15 @@ export class ContentMutation {
167
138
  .split(/\s+/)
168
139
  .map((ref) => `#${ref}`)
169
140
  .join(',');
170
- attrState.placeholder = document.createElement('span');
171
- attrState.placeholder.id = `auto-link-${attrName}-${this.guid}`;
141
+
142
+ // create placeholder element for copied content
143
+ if (!attrState.placeholder) {
144
+ attrState.placeholder = document.createElement('span');
145
+ attrState.placeholder.id = `auto-link-${attribute}-${this.guid}`;
146
+ }
172
147
  }
173
148
  if (this.component.isConnected) {
174
- this.privateUpdate(attrName);
149
+ this.privateUpdate(attribute);
175
150
  }
176
151
  }
177
152
 
@@ -238,12 +213,9 @@ export class ContentMutation {
238
213
  }
239
214
  let computedIds;
240
215
  if (this.isNative) {
241
- const {
242
- outerSelector,
243
- content,
244
- placeholder,
245
- placeholderContainerSelector,
246
- } = this.state[attrName];
216
+ const { outerSelector, content, placeholder } =
217
+ this.state[attrName];
218
+
247
219
  const newContent = extractContent(
248
220
  extractElements(this.root, outerSelector)
249
221
  );
@@ -252,13 +224,14 @@ export class ContentMutation {
252
224
  newContent;
253
225
  }
254
226
  if (!placeholder.parentNode) {
255
- // inserting the placeholder once
256
- const container = this.template.querySelector(
257
- placeholderContainerSelector
258
- );
259
- if (container) {
260
- container.appendChild(placeholder);
227
+ // create placeholder container at template root, if not already exist
228
+ if (!this.placeholderContainer) {
229
+ this.placeholderContainer = createPlaceholderContainer();
230
+ this.template.appendChild(this.placeholderContainer);
261
231
  }
232
+
233
+ // inserting the placeholder once
234
+ this.placeholderContainer.appendChild(placeholder);
262
235
  }
263
236
  computedIds = placeholder.id;
264
237
  } else {
@@ -46,6 +46,7 @@
46
46
  name={name}
47
47
  data-value={computedInputValue}
48
48
  disabled={disabled}
49
+ aria-label={computedButtonTriggerAriaLabel}
49
50
  onfocus={handleFocus}
50
51
  onkeydown={handleInputKeyDown}
51
52
  onblur={handleBlur}>
@@ -328,7 +328,9 @@ export default class LightningBaseCombobox extends LightningElement {
328
328
  [ARIA_DESCRIBEDBY]: this.computedAriaDescribedBy,
329
329
  [ARIA_ACTIVEDESCENDANT]: this._activeElementDomId,
330
330
  [ARIA_CONTROLS]: this.computedInputControls,
331
- [ARIA_LABEL]: this.inputLabel,
331
+ [ARIA_LABEL]: this.isUserInputDisabled
332
+ ? this.computedButtonTriggerAriaLabel
333
+ : this.inputLabel,
332
334
  });
333
335
  }
334
336
 
@@ -441,6 +443,17 @@ export default class LightningBaseCombobox extends LightningElement {
441
443
  return this.hasInputPill ? this.inputPill.label : this.inputText;
442
444
  }
443
445
 
446
+ get computedButtonTriggerAriaLabel() {
447
+ const label = this.inputLabel;
448
+ const value = this.computedInputValue || this.computedPlaceholder;
449
+
450
+ if (!label) {
451
+ return value;
452
+ }
453
+
454
+ return `${label}, ${value}`;
455
+ }
456
+
444
457
  handleListboxScroll(event) {
445
458
  // We don't want this to bubble up to the modal which due to event retargeting wouldn't be able
446
459
  // to know what is actually being scrolled and thus may lead to the scrolling of the modal
@@ -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
  }
@@ -8,6 +8,7 @@ import labelHexLabel from '@salesforce/label/LightningColorPicker.hexLabel';
8
8
  import labelHueInput from '@salesforce/label/LightningColorPicker.hueInput';
9
9
  import labelRInput from '@salesforce/label/LightningColorPicker.rInput';
10
10
  import labelRedAbbr from '@salesforce/label/LightningColorPicker.redAbbr';
11
+ import formFactorPropertyName from '@salesforce/client/formFactor';
11
12
  import { LightningElement, api, track } from 'lwc';
12
13
  import { keyCodes } from 'lightning/utilsPrivate';
13
14
  import { generateUniqueId, getErrorMessage } from 'lightning/inputUtils';
@@ -58,7 +59,25 @@ export default class LightningColorPickerCustom extends LightningElement {
58
59
  if (!this._initialized) {
59
60
  // eslint-disable-next-line @lwc/lwc/no-async-operation
60
61
  requestAnimationFrame(() => {
61
- this.focus();
62
+ // (*1*)
63
+ if (formFactorPropertyName !== 'Large') {
64
+ /**
65
+ * We need to wait for one more animation frame and invoke .focus()
66
+ * in iOS. This is because the positionLibray.js initially sets the position
67
+ * of this color-picker element to "top: 0px" and then later repositions it asynchronously
68
+ * it the next animation frames. The first (*1*) rAF callback is fired between
69
+ * setting "top: 0px" and then later repositioning it. Calling .focus() in this callback
70
+ * triggers a re-paint step and the page is scrolled to the top due to "top: 0px" being
71
+ * present in the styles. To avoid this, we can delay the next re-paint after color-picker
72
+ * is repositioned correctly by positionLibrary.js. Hence we wait for next animation frame
73
+ * and then call .focus() to trigger the next re-paint after the color-picker is repositioned correctly
74
+ * which was initially positioned with "top: 0px" by positionLibrary.js
75
+ */
76
+ // eslint-disable-next-line @lwc/lwc/no-async-operation
77
+ requestAnimationFrame(() => this.focus());
78
+ } else {
79
+ this.focus();
80
+ }
62
81
  });
63
82
  this.gradient();
64
83
  this.handleUpdateAnchor();