lightning-base-components 1.14.1-alpha → 1.14.2-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/package.json +1 -1
- package/src/lightning/ariaObserver/__component__/ariaObserver.spec.js +103 -0
- package/src/lightning/ariaObserver/ariaObserver.js +268 -0
- package/src/lightning/input/input.js +51 -26
- package/src/lightning/positionLibrary/positionLibrary.js +43 -31
- package/src/lightning/progressStep/progressStep.js +29 -21
- package/src/lightning/utilsPrivate/utilsPrivate.js +4 -3
package/package.json
CHANGED
|
@@ -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,268 @@
|
|
|
1
|
+
import {
|
|
2
|
+
guid,
|
|
3
|
+
synchronizeAttrs,
|
|
4
|
+
isNativeComponent,
|
|
5
|
+
} from 'lightning/utilsPrivate';
|
|
6
|
+
|
|
7
|
+
const CONTENT_SEPARATOR = '\n';
|
|
8
|
+
|
|
9
|
+
function getAttr(elm, attr) {
|
|
10
|
+
if (elm.tagName.match(/lightning/i)) {
|
|
11
|
+
return elm[attr];
|
|
12
|
+
}
|
|
13
|
+
return elm.getAttribute(attr);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function extractElements(root, selector) {
|
|
17
|
+
if (typeof selector !== 'string' || selector === '') {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
return [].slice.call(root.querySelectorAll(selector));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function extractContent(elements) {
|
|
24
|
+
return elements
|
|
25
|
+
.map((element) => element.textContent)
|
|
26
|
+
.filter((text) => text.length)
|
|
27
|
+
.join(CONTENT_SEPARATOR);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function splitIds(ids) {
|
|
31
|
+
return (ids + '').trim().split(/\s+/);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function hashIds(ids) {
|
|
35
|
+
return (ids + '')
|
|
36
|
+
.trim()
|
|
37
|
+
.split(/\s+/)
|
|
38
|
+
.reduce((r, v) => {
|
|
39
|
+
r[v] = 1;
|
|
40
|
+
return r;
|
|
41
|
+
}, {});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// this method should check each individual id from computedIds
|
|
45
|
+
// against the existing value of the attrName on elm, and dupe
|
|
46
|
+
// them, and add the new ones.
|
|
47
|
+
function addAriaRefWhenNeeded(elm, attrName, computedIds) {
|
|
48
|
+
const newIds = splitIds(computedIds);
|
|
49
|
+
const oldIds = getAttr(elm, attrName) || '';
|
|
50
|
+
const oldIdsHash = hashIds(oldIds);
|
|
51
|
+
const suffix = [];
|
|
52
|
+
for (let i = 0; i < newIds.length; i += 1) {
|
|
53
|
+
if (!oldIdsHash[newIds[i]]) {
|
|
54
|
+
suffix.push(newIds[i]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (suffix.length !== 0) {
|
|
59
|
+
synchronizeAttrs(elm, {
|
|
60
|
+
[attrName]:
|
|
61
|
+
oldIds + (oldIds.length === 0 ? '' : ' ') + suffix.join(' '),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// this method should check each individual id from computedIds
|
|
67
|
+
// against the existing value of the attrName on elm, and remove
|
|
68
|
+
// them when possible in preparation for some new values.
|
|
69
|
+
function removeAriaRefWhenPossible(elm, attrName, computedIds) {
|
|
70
|
+
const newIds = splitIds(computedIds);
|
|
71
|
+
const oldIds = getAttr(elm, attrName) || '';
|
|
72
|
+
const oldIdsHash = hashIds(oldIds);
|
|
73
|
+
const newValues = [];
|
|
74
|
+
for (let i = 0; i < newIds.length; i += 1) {
|
|
75
|
+
if (!oldIdsHash[newIds[i]]) {
|
|
76
|
+
newValues.push(newIds[i]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
synchronizeAttrs(elm, {
|
|
81
|
+
[attrName]: newValues.join(' '),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
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 {
|
|
93
|
+
constructor(component) {
|
|
94
|
+
this.component = component;
|
|
95
|
+
this.template = component.template;
|
|
96
|
+
this.isNative = isNativeComponent(component);
|
|
97
|
+
this.state = {};
|
|
98
|
+
this.liveIds = {};
|
|
99
|
+
this.guid = guid();
|
|
100
|
+
this.placeholderContainer = null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
connectLiveIdRef(refs, callback) {
|
|
104
|
+
const selector = (refs + '')
|
|
105
|
+
.trim()
|
|
106
|
+
.split(/\s+/)
|
|
107
|
+
.map((ref) => `[id*="${ref}"]`)
|
|
108
|
+
.join(',');
|
|
109
|
+
const liveId = { selector, callback };
|
|
110
|
+
this.liveIds[refs] = liveId;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
connect({ targetSelector, attribute, id, ids }) {
|
|
114
|
+
ids = ids || id;
|
|
115
|
+
|
|
116
|
+
let attrState = this.state[attribute];
|
|
117
|
+
if (attrState) {
|
|
118
|
+
// note: we don't support linking to a different targetSelector, attribute
|
|
119
|
+
if (!this.isNative) {
|
|
120
|
+
const elm = this.template.querySelector(
|
|
121
|
+
attrState.innerSelector
|
|
122
|
+
);
|
|
123
|
+
if (elm) {
|
|
124
|
+
// removing the old ids if possible before setting the new ones
|
|
125
|
+
removeAriaRefWhenPossible(elm, attribute, attrState.ids);
|
|
126
|
+
}
|
|
127
|
+
attrState.ids = ids;
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
attrState = this.state[attribute] = {
|
|
131
|
+
ids,
|
|
132
|
+
innerSelector: targetSelector,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (this.isNative) {
|
|
136
|
+
attrState.outerSelector = (ids + '')
|
|
137
|
+
.trim()
|
|
138
|
+
.split(/\s+/)
|
|
139
|
+
.map((ref) => `#${ref}`)
|
|
140
|
+
.join(',');
|
|
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
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (this.component.isConnected) {
|
|
149
|
+
this.privateUpdate(attribute);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
sync() {
|
|
154
|
+
if (!this.component.isConnected) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Invalid sync invocation. It can only be invoked during renderedCallback().`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (this.isNative && !this.mo) {
|
|
160
|
+
this.privateConnect();
|
|
161
|
+
}
|
|
162
|
+
for (const attrName in this.state) {
|
|
163
|
+
if (Object.prototype.hasOwnProperty.call(this.state, attrName)) {
|
|
164
|
+
this.privateUpdate(attrName);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// live idRef feature is a no-op in native
|
|
169
|
+
if (!this.isNative) {
|
|
170
|
+
this.privateUpdateLiveIds();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
privateExtractIds(elements) {
|
|
175
|
+
return elements
|
|
176
|
+
.map((el) => {
|
|
177
|
+
return el.getAttribute('id');
|
|
178
|
+
})
|
|
179
|
+
.join(' ');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
privateUpdateLiveIds() {
|
|
183
|
+
const root = this.template.host.getRootNode();
|
|
184
|
+
|
|
185
|
+
// if not connected do nothing
|
|
186
|
+
if (!root) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
for (const liveId in this.liveIds) {
|
|
190
|
+
if (Object.prototype.hasOwnProperty.call(this.liveIds, liveId)) {
|
|
191
|
+
const thisId = this.liveIds[liveId];
|
|
192
|
+
if (!thisId.elements) {
|
|
193
|
+
// element refs are cached
|
|
194
|
+
thisId.elements = Array.prototype.slice.call(
|
|
195
|
+
root.querySelectorAll(thisId.selector)
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const newIds = this.privateExtractIds(thisId.elements);
|
|
199
|
+
// only fire calback if the value changed
|
|
200
|
+
if (newIds !== thisId.ids) {
|
|
201
|
+
thisId.callback(newIds);
|
|
202
|
+
thisId.ids = newIds;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
privateUpdate(attrName) {
|
|
209
|
+
const { innerSelector } = this.state[attrName];
|
|
210
|
+
const elm = this.template.querySelector(innerSelector);
|
|
211
|
+
if (!elm) {
|
|
212
|
+
return; // nothing to update
|
|
213
|
+
}
|
|
214
|
+
let computedIds;
|
|
215
|
+
if (this.isNative) {
|
|
216
|
+
const { outerSelector, content, placeholder } =
|
|
217
|
+
this.state[attrName];
|
|
218
|
+
|
|
219
|
+
const newContent = extractContent(
|
|
220
|
+
extractElements(this.root, outerSelector)
|
|
221
|
+
);
|
|
222
|
+
if (content !== newContent) {
|
|
223
|
+
this.state[attrName].content = placeholder.textContent =
|
|
224
|
+
newContent;
|
|
225
|
+
}
|
|
226
|
+
if (!placeholder.parentNode) {
|
|
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);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// inserting the placeholder once
|
|
234
|
+
this.placeholderContainer.appendChild(placeholder);
|
|
235
|
+
}
|
|
236
|
+
computedIds = placeholder.id;
|
|
237
|
+
} else {
|
|
238
|
+
computedIds = this.state[attrName].ids;
|
|
239
|
+
}
|
|
240
|
+
addAriaRefWhenNeeded(elm, attrName, computedIds);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
privateConnect() {
|
|
244
|
+
// caching root ref
|
|
245
|
+
this.root = this.template.host.getRootNode();
|
|
246
|
+
// creating the observer once
|
|
247
|
+
this.mo = new MutationObserver(() => {
|
|
248
|
+
if (!this.component.isConnected) {
|
|
249
|
+
return; // do nothing when the template is not connected
|
|
250
|
+
}
|
|
251
|
+
this.sync();
|
|
252
|
+
});
|
|
253
|
+
this.mo.observe(this.root, {
|
|
254
|
+
characterData: true,
|
|
255
|
+
childList: true,
|
|
256
|
+
subtree: true,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
disconnect() {
|
|
261
|
+
// MutationObservers must be disconnected manually when using @lwc/synthetic-shadow
|
|
262
|
+
// https://lwc.dev/guide/composition#:~:text=memory%20leak
|
|
263
|
+
if (this.mo) {
|
|
264
|
+
this.mo.disconnect();
|
|
265
|
+
this.mo = undefined;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -77,7 +77,7 @@ const i18n = {
|
|
|
77
77
|
|
|
78
78
|
const ARIA_CONTROLS = 'aria-controls';
|
|
79
79
|
const ARIA_LABEL = 'aria-label';
|
|
80
|
-
const
|
|
80
|
+
const ARIA_LABELLEDBY = 'aria-labelledby';
|
|
81
81
|
const ARIA_DESCRIBEDBY = 'aria-describedby';
|
|
82
82
|
|
|
83
83
|
/*
|
|
@@ -524,7 +524,7 @@ export default class LightningInput extends LightningElement {
|
|
|
524
524
|
this._ariaControls = references;
|
|
525
525
|
this.ariaObserver.connect({
|
|
526
526
|
targetSelector: 'input',
|
|
527
|
-
attribute:
|
|
527
|
+
attribute: ARIA_CONTROLS,
|
|
528
528
|
ids: references,
|
|
529
529
|
});
|
|
530
530
|
}
|
|
@@ -537,8 +537,7 @@ export default class LightningInput extends LightningElement {
|
|
|
537
537
|
get ariaLabelledBy() {
|
|
538
538
|
// native version returns the auto linked value
|
|
539
539
|
if (this.isNative) {
|
|
540
|
-
const ariaValues =
|
|
541
|
-
this._inputElement.getAttribute('aria-labelledby');
|
|
540
|
+
const ariaValues = this._inputElement.getAttribute(ARIA_LABELLEDBY);
|
|
542
541
|
return filterNonAutoLink(ariaValues);
|
|
543
542
|
}
|
|
544
543
|
return this._ariaLabelledBy;
|
|
@@ -548,7 +547,7 @@ export default class LightningInput extends LightningElement {
|
|
|
548
547
|
this._ariaLabelledBy = references;
|
|
549
548
|
this.ariaObserver.connect({
|
|
550
549
|
targetSelector: 'input',
|
|
551
|
-
attribute:
|
|
550
|
+
attribute: ARIA_LABELLEDBY,
|
|
552
551
|
ids: references,
|
|
553
552
|
});
|
|
554
553
|
}
|
|
@@ -562,7 +561,7 @@ export default class LightningInput extends LightningElement {
|
|
|
562
561
|
if (this.isNative) {
|
|
563
562
|
// in native case return the linked value
|
|
564
563
|
const ariaValues =
|
|
565
|
-
this._inputElement.getAttribute(
|
|
564
|
+
this._inputElement.getAttribute(ARIA_DESCRIBEDBY);
|
|
566
565
|
return filterNonAutoLink(ariaValues);
|
|
567
566
|
}
|
|
568
567
|
return this._ariaDescribedBy;
|
|
@@ -572,7 +571,7 @@ export default class LightningInput extends LightningElement {
|
|
|
572
571
|
this._ariaDescribedBy = references;
|
|
573
572
|
this.ariaObserver.connect({
|
|
574
573
|
targetSelector: 'input',
|
|
575
|
-
attribute:
|
|
574
|
+
attribute: ARIA_DESCRIBEDBY,
|
|
576
575
|
ids: references,
|
|
577
576
|
});
|
|
578
577
|
}
|
|
@@ -2033,32 +2032,58 @@ export default class LightningInput extends LightningElement {
|
|
|
2033
2032
|
return result;
|
|
2034
2033
|
}
|
|
2035
2034
|
|
|
2035
|
+
_updateInputA11y(elem) {
|
|
2036
|
+
synchronizeAttrs(elem, {
|
|
2037
|
+
[ARIA_LABELLEDBY]: this.computedAriaLabelledBy,
|
|
2038
|
+
[ARIA_DESCRIBEDBY]: this.computedAriaDescribedBy,
|
|
2039
|
+
[ARIA_CONTROLS]: this.computedAriaControls,
|
|
2040
|
+
[ARIA_LABEL]: this.computedAriaLabel,
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
_updateDateOrTimePickerA11y(elem) {
|
|
2045
|
+
synchronizeAttrs(elem, {
|
|
2046
|
+
ariaLabelledByElement: this.ariaLabelledBy,
|
|
2047
|
+
ariaDescribedByElements: this.ariaDescribedBy,
|
|
2048
|
+
ariaControlsElement: this.ariaControls,
|
|
2049
|
+
[ARIA_LABEL]: this.computedAriaLabel,
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
_updateDateTimePickerA11y(elem) {
|
|
2054
|
+
synchronizeAttrs(elem, {
|
|
2055
|
+
// datepicker aria attributes
|
|
2056
|
+
dateAriaLabelledBy: this.dateAriaLabelledBy,
|
|
2057
|
+
dateAriaDescribedBy: this.dateAriaDescribedBy,
|
|
2058
|
+
dateAriaControls: this.dateAriaControls,
|
|
2059
|
+
dateAriaLabel: this.dateAriaLabel,
|
|
2060
|
+
// timepicker aria attributes
|
|
2061
|
+
timeAriaLabelledBy: this.timeAriaLabelledBy,
|
|
2062
|
+
timeAriaDescribedBy: this.timeAriaDescribedBy,
|
|
2063
|
+
timeAriaControls: this.timeAriaControls,
|
|
2064
|
+
timeAriaLabel: this.timeAriaLabel,
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2036
2068
|
_synchronizeA11y() {
|
|
2069
|
+
// each of these templates are mutually exclusive and are selected
|
|
2070
|
+
// depending on the [type] of input.
|
|
2037
2071
|
const input = this.template.querySelector('input');
|
|
2038
2072
|
const datepicker = this.template.querySelector('lightning-datepicker');
|
|
2039
2073
|
const timepicker = this.template.querySelector('lightning-timepicker');
|
|
2040
|
-
|
|
2074
|
+
const datetimepicker = this.template.querySelector(
|
|
2075
|
+
'lightning-datetimepicker'
|
|
2076
|
+
);
|
|
2077
|
+
// determine which template type is present,
|
|
2078
|
+
// and update a11y props accordingly
|
|
2041
2079
|
if (input) {
|
|
2042
|
-
|
|
2043
|
-
[ARIA_LABELEDBY]: this.computedAriaLabelledBy,
|
|
2044
|
-
[ARIA_DESCRIBEDBY]: this.computedAriaDescribedBy,
|
|
2045
|
-
[ARIA_CONTROLS]: this.computedAriaControls,
|
|
2046
|
-
[ARIA_LABEL]: this.computedAriaLabel,
|
|
2047
|
-
});
|
|
2080
|
+
this._updateInputA11y(input);
|
|
2048
2081
|
} else if (datepicker) {
|
|
2049
|
-
|
|
2050
|
-
ariaLabelledByElement: this.ariaLabelledBy,
|
|
2051
|
-
ariaDescribedByElements: this.ariaDescribedBy,
|
|
2052
|
-
ariaControlsElement: this.ariaControls,
|
|
2053
|
-
[ARIA_LABEL]: this.computedAriaLabel,
|
|
2054
|
-
});
|
|
2082
|
+
this._updateDateOrTimePickerA11y(datepicker);
|
|
2055
2083
|
} else if (timepicker) {
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
ariaControlsElement: this.ariaControls,
|
|
2060
|
-
[ARIA_LABEL]: this.computedAriaLabel,
|
|
2061
|
-
});
|
|
2084
|
+
this._updateDateOrTimePickerA11y(timepicker);
|
|
2085
|
+
} else if (datetimepicker) {
|
|
2086
|
+
this._updateDateTimePickerA11y(datetimepicker);
|
|
2062
2087
|
}
|
|
2063
2088
|
}
|
|
2064
2089
|
}
|
|
@@ -131,38 +131,50 @@ function createRelationship(
|
|
|
131
131
|
);
|
|
132
132
|
|
|
133
133
|
const autoShrink = config.autoShrink.height || config.autoShrink.width;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
bottom: !!config.autoShrink.height,
|
|
160
|
-
left: !!config.autoShrink.width,
|
|
161
|
-
right: !!config.autoShrink.width,
|
|
134
|
+
const modal = new OverlayDetector(originalConfig.target);
|
|
135
|
+
if (
|
|
136
|
+
(config.scrollableParentBound && scrollableParent) ||
|
|
137
|
+
(modal.isInsideModal && modal.overlay && autoShrink)
|
|
138
|
+
) {
|
|
139
|
+
let parent;
|
|
140
|
+
if (config.scrollableParentBound && scrollableParent) {
|
|
141
|
+
parent = normalizeElement(scrollableParent);
|
|
142
|
+
} else if (modal.isInsideModal && modal.overlay) {
|
|
143
|
+
parent = normalizeElement(modal.overlay);
|
|
144
|
+
}
|
|
145
|
+
if (parent) {
|
|
146
|
+
const boxConfig = {
|
|
147
|
+
element: config.element,
|
|
148
|
+
enabled: config.enabled,
|
|
149
|
+
target: createProxy(parent),
|
|
150
|
+
align: {},
|
|
151
|
+
targetAlign: {},
|
|
152
|
+
pad: 3,
|
|
153
|
+
boxDirections: {
|
|
154
|
+
top: true,
|
|
155
|
+
bottom: true,
|
|
156
|
+
left: true,
|
|
157
|
+
right: true,
|
|
158
|
+
},
|
|
162
159
|
};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
|
|
161
|
+
if (autoShrink) {
|
|
162
|
+
const style = boxConfig.element.getNode().style;
|
|
163
|
+
if (!style.minHeight) {
|
|
164
|
+
style.minHeight = config.minHeight;
|
|
165
|
+
boxConfig.element._removeMinHeight = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
boxConfig.boxDirections = {
|
|
169
|
+
top: !!config.autoShrink.height,
|
|
170
|
+
bottom: !!config.autoShrink.height,
|
|
171
|
+
left: !!config.autoShrink.width,
|
|
172
|
+
right: !!config.autoShrink.width,
|
|
173
|
+
};
|
|
174
|
+
constraintList.push(new Constraint('shrinking box', boxConfig));
|
|
175
|
+
} else {
|
|
176
|
+
constraintList.push(new Constraint('bounding box', boxConfig));
|
|
177
|
+
}
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
|
|
@@ -31,6 +31,8 @@ export default class LightningProgressStep extends LightningElement {
|
|
|
31
31
|
*/
|
|
32
32
|
@api value;
|
|
33
33
|
|
|
34
|
+
_privateLabel;
|
|
35
|
+
|
|
34
36
|
@track state = {};
|
|
35
37
|
|
|
36
38
|
updateInternal(newStatus, newType, newIndex, newActive, shouldFocus) {
|
|
@@ -47,6 +49,7 @@ export default class LightningProgressStep extends LightningElement {
|
|
|
47
49
|
this.state.type = newType;
|
|
48
50
|
this.state.index = newIndex;
|
|
49
51
|
this.state.active = newActive;
|
|
52
|
+
this.initializeTooltip();
|
|
50
53
|
}
|
|
51
54
|
/**
|
|
52
55
|
* Text to display as the name or tooltip for the step.
|
|
@@ -54,30 +57,12 @@ export default class LightningProgressStep extends LightningElement {
|
|
|
54
57
|
*/
|
|
55
58
|
@api
|
|
56
59
|
set label(value) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
} else if (value && !this.isPath) {
|
|
60
|
-
// Note that because the tooltip target is a child element it may not be present in the
|
|
61
|
-
// dom during initial rendering.
|
|
62
|
-
this._tooltip = new Tooltip(value, {
|
|
63
|
-
root: this,
|
|
64
|
-
target: () => this.template.querySelector('button'),
|
|
65
|
-
type: TooltipType.Toggle,
|
|
66
|
-
align: {
|
|
67
|
-
horizontal: Direction.Center,
|
|
68
|
-
vertical: Direction.Bottom,
|
|
69
|
-
},
|
|
70
|
-
targetAlign: {
|
|
71
|
-
horizontal: Direction.Center,
|
|
72
|
-
vertical: Direction.Top,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
this._tooltip.initialize();
|
|
76
|
-
}
|
|
60
|
+
this._privateLabel = value;
|
|
61
|
+
this.initializeTooltip();
|
|
77
62
|
}
|
|
78
63
|
|
|
79
64
|
get label() {
|
|
80
|
-
return this.
|
|
65
|
+
return this._privateLabel;
|
|
81
66
|
}
|
|
82
67
|
|
|
83
68
|
computeClassSet(type, status, isActive) {
|
|
@@ -240,4 +225,27 @@ export default class LightningProgressStep extends LightningElement {
|
|
|
240
225
|
}
|
|
241
226
|
return base;
|
|
242
227
|
}
|
|
228
|
+
|
|
229
|
+
initializeTooltip() {
|
|
230
|
+
if (this._tooltip) {
|
|
231
|
+
this._tooltip.value = this._privateLabel;
|
|
232
|
+
} else if (this._privateLabel && this.state.type && !this.isPath) {
|
|
233
|
+
// Note that because the tooltip target is a child element it may not be present in the
|
|
234
|
+
// dom during initial rendering.
|
|
235
|
+
this._tooltip = new Tooltip(this._privateLabel, {
|
|
236
|
+
root: this,
|
|
237
|
+
target: () => this.template.querySelector('button'),
|
|
238
|
+
type: TooltipType.Toggle,
|
|
239
|
+
align: {
|
|
240
|
+
horizontal: Direction.Center,
|
|
241
|
+
vertical: Direction.Bottom,
|
|
242
|
+
},
|
|
243
|
+
targetAlign: {
|
|
244
|
+
horizontal: Direction.Center,
|
|
245
|
+
vertical: Direction.Top,
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
this._tooltip.initialize();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
243
251
|
}
|
|
@@ -218,7 +218,8 @@ export function buttonGroupOrderClass(groupOrder) {
|
|
|
218
218
|
* @returns {Boolean} Whether the component is native
|
|
219
219
|
*/
|
|
220
220
|
export function isNativeComponent(cmp) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
if (cmp && cmp.template && cmp.template.constructor) {
|
|
222
|
+
return !!String(cmp.template.constructor).match(/\[native code\]/);
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
224
225
|
}
|