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.
- package/metadata/raptor.json +1 -0
- package/package.json +21 -1
- package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
- package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsPlural.js +1 -0
- package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsSingular.js +1 -0
- package/scopedImports/@salesforce-label-LightningErrorMessage.validitySelectAtleastOne.js +1 -0
- package/scopedImports/@salesforce-label-LightningLookup.recentItems.js +1 -0
- package/scopedImports/@salesforce-label-LightningMap.titleWithAddress.js +1 -0
- package/src/lightning/ariaObserver/__component__/ariaObserver.spec.js +103 -0
- package/src/lightning/ariaObserver/__docs__/ariaObserver.md +142 -0
- package/src/lightning/{utilsPrivate/contentMutation.js → ariaObserver/ariaObserver.js} +51 -78
- package/src/lightning/baseCombobox/baseCombobox.html +1 -0
- package/src/lightning/baseCombobox/baseCombobox.js +14 -1
- package/src/lightning/buttonMenu/keyboard.js +0 -10
- package/src/lightning/card/card.html +6 -0
- package/src/lightning/checkboxGroup/checkboxGroup.html +2 -2
- package/src/lightning/checkboxGroup/checkboxGroup.js +6 -1
- package/src/lightning/colorPickerCustom/colorPickerCustom.js +20 -1
- package/src/lightning/combobox/combobox.css +12 -0
- package/src/lightning/combobox/combobox.html +1 -0
- package/src/lightning/datatable/__docs__/datatable.md +55 -0
- package/src/lightning/datatable/__examples__/basic/basic.html +1 -1
- package/src/lightning/datatable/columnWidthManager.js +7 -3
- package/src/lightning/datatable/columns-shared.js +1 -1
- package/src/lightning/datatable/datatable.js +122 -40
- package/src/lightning/datatable/errors.js +20 -9
- package/src/lightning/datatable/headerActions.js +77 -49
- package/src/lightning/datatable/inlineEdit.js +520 -373
- package/src/lightning/datatable/inlineEditShared.js +24 -0
- package/src/lightning/datatable/keyboard.js +1077 -933
- package/src/lightning/datatable/renderManager.js +241 -129
- package/src/lightning/datatable/resizer.js +91 -108
- package/src/lightning/datatable/rowLevelActions.js +17 -13
- package/src/lightning/datatable/rowNumber.js +54 -20
- package/src/lightning/datatable/rowSelection.js +760 -0
- package/src/lightning/datatable/rowSelectionShared.js +79 -0
- package/src/lightning/datatable/rows.js +16 -5
- package/src/lightning/datatable/state.js +10 -10
- package/src/lightning/datatable/templates/div/div.css +23 -0
- package/src/lightning/datatable/templates/div/div.html +6 -5
- package/src/lightning/datatable/templates/table/table.html +4 -4
- package/src/lightning/datatable/utils.js +14 -0
- package/src/lightning/datatable/widthManagerShared.js +1 -1
- package/src/lightning/dualListbox/dualListbox.html +1 -1
- package/src/lightning/dualListbox/dualListbox.js +42 -0
- package/src/lightning/iconSvgTemplates/buildTemplates/templates.js +2 -1
- package/src/lightning/iconSvgTemplates/buildTemplates/utility/contract_alt.html +1 -2
- package/src/lightning/iconSvgTemplates/buildTemplates/utility/contract_doc.html +8 -0
- package/src/lightning/iconSvgTemplatesRtl/buildTemplates/templates.js +2 -1
- package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/contract_alt.html +1 -2
- package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/contract_doc.html +8 -0
- package/src/lightning/iconSvgTemplatesUtility/buildTemplates/templates.js +2 -1
- package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/contract_alt.html +1 -2
- package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/contract_doc.html +8 -0
- package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/templates.js +2 -1
- package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/contract_alt.html +1 -2
- package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/contract_doc.html +8 -0
- package/src/lightning/input/input.html +0 -1
- package/src/lightning/input/input.js +69 -48
- package/src/lightning/inputUtils/validity.js +12 -1
- package/src/lightning/pillContainer/__docs__/pillContainer.md +45 -1
- package/src/lightning/primitiveCellActions/primitiveCellActions.js +69 -12
- package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +13 -11
- package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +13 -8
- package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.html +17 -14
- package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +169 -80
- package/src/lightning/primitiveDatatableIeditTypeFactory/primitiveDatatableIeditTypeFactory.js +97 -62
- package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.html +4 -4
- package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +4 -4
- package/src/lightning/primitiveHeaderActions/primitiveHeaderActions.js +99 -37
- package/src/lightning/primitiveHeaderFactory/nonsortableHeader.html +5 -4
- package/src/lightning/primitiveHeaderFactory/primitiveHeaderFactory.js +46 -46
- package/src/lightning/primitiveHeaderFactory/selectableHeader.html +25 -23
- package/src/lightning/primitiveHeaderFactory/sortableHeader.html +13 -9
- package/src/lightning/progressIndicator/progressIndicator.js +4 -6
- package/src/lightning/progressStep/progressStep.js +31 -22
- package/src/lightning/staticMap/staticMap.html +1 -0
- package/src/lightning/staticMap/staticMap.js +39 -2
- package/src/lightning/utils/classSet.js +4 -1
- package/src/lightning/utilsPrivate/utilsPrivate.js +12 -1
- package/src/lightning/datatable/inlineEdit-shared.js +0 -14
- package/src/lightning/datatable/selector-shared.js +0 -38
- package/src/lightning/datatable/selector.js +0 -527
package/metadata/raptor.json
CHANGED
|
@@ -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.
|
|
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 '
|
|
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 {
|
|
2
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
79
|
+
|
|
80
|
+
synchronizeAttrs(elm, {
|
|
81
|
+
[attrName]: newValues.join(' '),
|
|
82
|
+
});
|
|
120
83
|
}
|
|
121
84
|
|
|
122
|
-
|
|
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 =
|
|
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
|
-
|
|
145
|
-
|
|
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
|
|
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(
|
|
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,
|
|
125
|
+
removeAriaRefWhenPossible(elm, attribute, attrState.ids);
|
|
154
126
|
}
|
|
155
127
|
attrState.ids = ids;
|
|
156
128
|
}
|
|
157
129
|
} else {
|
|
158
|
-
attrState = this.state[
|
|
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
|
-
|
|
171
|
-
|
|
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(
|
|
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
|
-
|
|
243
|
-
|
|
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
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
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 {
|
|
@@ -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.
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|