lightning-base-components 1.19.7-alpha → 1.19.8-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 (43) hide show
  1. package/metadata/raptor.json +78 -0
  2. package/package.json +5 -1
  3. package/scopedImports/@salesforce-label-LightningProgressRing.progressRing.js +1 -0
  4. package/src/lightning/baseCombobox/baseCombobox.css +8 -0
  5. package/src/lightning/baseCombobox/baseCombobox.js +1 -0
  6. package/src/lightning/datatable/README.md +2 -1
  7. package/src/lightning/datatable/columnResizer.js +22 -18
  8. package/src/lightning/datatable/columnWidthManager.js +30 -22
  9. package/src/lightning/datatable/errors.js +2 -2
  10. package/src/lightning/datatable/infiniteLoading.js +13 -14
  11. package/src/lightning/datatable/sort.js +15 -16
  12. package/src/lightning/datatable/tree.js +10 -6
  13. package/src/lightning/datatable/types.js +158 -129
  14. package/src/lightning/datatable/utils.js +26 -24
  15. package/src/lightning/datatable/wrapText.js +15 -8
  16. package/src/lightning/input/input.html +23 -2
  17. package/src/lightning/input/input.js +54 -2
  18. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +74 -69
  19. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +8 -2
  20. package/src/lightning/primitiveHeaderFactory/primitiveHeaderFactory.js +62 -87
  21. package/src/lightning/primitiveInputCheckbox/primitiveInputCheckbox.html +3 -0
  22. package/src/lightning/primitiveInputCheckbox/primitiveInputCheckbox.js +3 -0
  23. package/src/lightning/primitiveInputCheckboxButton/primitiveInputCheckboxButton.html +3 -0
  24. package/src/lightning/primitiveInputCheckboxButton/primitiveInputCheckboxButton.js +3 -0
  25. package/src/lightning/primitiveInputFile/primitiveInputFile.html +3 -0
  26. package/src/lightning/primitiveInputFile/primitiveInputFile.js +3 -0
  27. package/src/lightning/primitiveInputRadio/primitiveInputRadio.html +3 -0
  28. package/src/lightning/primitiveInputRadio/primitiveInputRadio.js +3 -0
  29. package/src/lightning/primitiveInputSimple/primitiveInputSimple.html +6 -0
  30. package/src/lightning/primitiveInputSimple/primitiveInputSimple.js +7 -19
  31. package/src/lightning/primitiveInputToggle/primitiveInputToggle.html +4 -1
  32. package/src/lightning/primitiveInputToggle/primitiveInputToggle.js +3 -0
  33. package/src/lightning/progressRing/progressRing.html +9 -7
  34. package/src/lightning/progressRing/progressRing.js +15 -0
  35. package/src/lightning/tabBar/tabBar.html +5 -1
  36. package/src/lightning/tabBar/tabBar.js +25 -9
  37. package/src/lightning/utils/classSet.js +19 -19
  38. package/src/lightning/utilsPrivate/aria.js +11 -8
  39. package/src/lightning/utilsPrivate/classListMutation.js +5 -3
  40. package/src/lightning/utilsPrivate/classSet.js +11 -0
  41. package/src/lightning/utilsPrivate/eventEmitter.js +4 -4
  42. package/src/lightning/utilsPrivate/inert.js +6 -4
  43. package/src/lightning/utilsPrivate/utilsPrivate.js +40 -8
@@ -12,6 +12,9 @@ export default class LightningPrimitiveInputColor extends LightningElement {
12
12
  @api required;
13
13
  @api readOnly;
14
14
  @api label;
15
+ @api ariaKeyShortcuts;
16
+ @api ariaDisabled;
17
+ @api ariaRoleDescription;
15
18
 
16
19
  _checked = false;
17
20
  _disabled = false;
@@ -10,6 +10,9 @@
10
10
  <lightning-primitive-file-droppable-zone multiple={multiple} disabled={disabled}>
11
11
  <input type="file" id="input-file"
12
12
  aria-label={ariaLabel}
13
+ aria-keyshortcuts={ariaKeyShortcuts}
14
+ aria-disabled={ariaDisabled}
15
+ aria-roledescription={ariaRoleDescription}
13
16
  aria-invalid={ariaInvalid}
14
17
  accesskey={accessKey}
15
18
  class="slds-file-selector__input slds-assistive-text"
@@ -27,6 +27,9 @@ export default class LightningPrimitiveInputFile extends LightningElement {
27
27
  @api readOnly;
28
28
  @api required;
29
29
  @api ariaLabel;
30
+ @api ariaKeyShortcuts;
31
+ @api ariaDisabled;
32
+ @api ariaRoleDescription;
30
33
 
31
34
  @api
32
35
  get files() {
@@ -2,6 +2,9 @@
2
2
  <div class="slds-form-element__control">
3
3
  <span class="slds-radio">
4
4
  <input type="radio" id="radio"
5
+ aria-keyshortcuts={ariaKeyShortcuts}
6
+ aria-disabled={ariaDisabled}
7
+ aria-roledescription={ariaRoleDescription}
5
8
  aria-invalid={ariaInvalid}
6
9
  accesskey={accessKey}
7
10
  onblur={handleBlur}
@@ -6,6 +6,9 @@ import { isNativeComponent } from 'lightning/utilsPrivate';
6
6
  export default class LightningPrimitiveInputRadio extends LightningElement {
7
7
  @api variant;
8
8
  @api ariaInvalid;
9
+ @api ariaKeyShortcuts;
10
+ @api ariaDisabled;
11
+ @api ariaRoleDescription;
9
12
  @api accessKey;
10
13
  @api name;
11
14
  @api required;
@@ -16,6 +16,11 @@
16
16
  aria-haspopup={ariaHasPopup}
17
17
  aria-invalid={ariaInvalid}
18
18
  aria-label={ariaLabel}
19
+ aria-keyshortcuts={ariaKeyShortcuts}
20
+ aria-disabled={ariaDisabled}
21
+ aria-roledescription={ariaRoleDescription}
22
+ aria-autocomplete={ariaAutoComplete}
23
+ aria-expanded={ariaExpanded}
19
24
  autocomplete={autocomplete}
20
25
  class="slds-input"
21
26
  disabled={disabled}
@@ -31,6 +36,7 @@
31
36
  onfocus={handleFocus}
32
37
  oninput={handleInput}
33
38
  onkeydown={handleKeyDown}
39
+ role={role}
34
40
  part="input"
35
41
  pattern={pattern}
36
42
  placeholder={placeholder}
@@ -80,6 +80,13 @@ export default class LightningPrimitiveInputSimple extends LightningElement {
80
80
  @api ariaInvalid;
81
81
  @api timezone;
82
82
 
83
+ @api ariaKeyShortcuts;
84
+ @api ariaDisabled;
85
+ @api ariaRoleDescription;
86
+ @api ariaExpanded;
87
+ @api ariaAutoComplete;
88
+ @api role;
89
+
83
90
  @api
84
91
  get ariaDescribedByElements() {
85
92
  return this.template.querySelector('[data-help-message]');
@@ -189,29 +196,10 @@ export default class LightningPrimitiveInputSimple extends LightningElement {
189
196
  if (this._value !== previousValue) {
190
197
  this.updateNumberValue(value);
191
198
  }
192
-
193
- // If updated value is invalid or not null, it should be updated
194
- // Invalid numeric values should stay as-is to avoid programatically wiping a user-provided invalid
195
- // value, such as '2e' from the input element. See @W-14121203
196
- if (!badInput || !!value) {
197
- this.setInputValueIfNeeded();
198
- }
199
- } else {
200
- this.setInputValueIfNeeded();
201
199
  }
202
200
  }
203
201
  }
204
202
 
205
- setInputValueIfNeeded() {
206
- // Again, due to the interop layer we need to check whether the value being set
207
- // is different, otherwise we're duplicating the sets on the input, which result
208
- // in different bugs like Japanese IME duplication of characters in Safari (likely a browser bug) or
209
- // character position re-set in IE11.
210
- if (this.inputElement.value !== this.displayedValue) {
211
- this.setInputValue(this.getDisplayedValue());
212
- }
213
- }
214
-
215
203
  @api
216
204
  get inputElement() {
217
205
  if (!this.cachedInputElement) {
@@ -11,6 +11,9 @@
11
11
  part="checkbox"
12
12
  accesskey={accessKey}
13
13
  aria-label={ariaLabel}
14
+ aria-keyshortcuts={ariaKeyShortcuts}
15
+ aria-disabled={ariaDisabled}
16
+ aria-roledescription={ariaRoleDescription}
14
17
  aria-invalid={ariaInvalid}
15
18
  disabled={disabled}
16
19
  name={name}
@@ -21,7 +24,7 @@
21
24
  readonly={readOnly}
22
25
  required={required}
23
26
  >
24
- <span id="toggle-description" data-toggle-description class="slds-checkbox_faux_container" aria-live="assertive">
27
+ <span id="toggle-description" data-toggle-description class="slds-checkbox_faux_container">
25
28
  <span class="slds-checkbox_faux" part="indicator"></span>
26
29
  <span class="slds-checkbox_on">{messageToggleActive}</span>
27
30
  <span class="slds-checkbox_off">{messageToggleInactive}</span>
@@ -30,6 +30,9 @@ export default class LightningPrimitiveInputToggle extends LightningElement {
30
30
  @api readOnly;
31
31
  @api required;
32
32
  @api ariaInvalid;
33
+ @api ariaKeyShortcuts;
34
+ @api ariaDisabled;
35
+ @api ariaRoleDescription;
33
36
 
34
37
  /**
35
38
  * Text shown for the active state of a toggle. The default is "Active".
@@ -6,13 +6,10 @@
6
6
  aria-valuemin="0"
7
7
  aria-valuemax="100"
8
8
  aria-valuenow={value}
9
+ aria-label={computeAriaLabel}
9
10
  >
10
11
  <svg viewBox="-1 -1 2 2">
11
- <path
12
- if:true={d}
13
- class="slds-progress-ring__path"
14
- d={d}
15
- ></path>
12
+ <path if:true={d} class="slds-progress-ring__path" d={d}></path>
16
13
  </svg>
17
14
  </div>
18
15
  <div class="slds-progress-ring__content">
@@ -31,8 +28,13 @@
31
28
  </div>
32
29
  <div class="slds-progress-ring__progress-head">
33
30
  <svg viewBox="-1 -1 2 2">
34
- <circle if:true={isProgressHeadVisible} class="slds-progress-ring__path" cx={progressHeadCenter.x} cy={progressHeadCenter.y} r="0.2">
35
- </circle>
31
+ <circle
32
+ if:true={isProgressHeadVisible}
33
+ class="slds-progress-ring__path"
34
+ cx={progressHeadCenter.x}
35
+ cy={progressHeadCenter.y}
36
+ r="0.2"
37
+ ></circle>
36
38
  </svg>
37
39
  </div>
38
40
  </div>
@@ -1,10 +1,13 @@
1
1
  import { LightningElement, api } from 'lwc';
2
2
  import { classSet } from 'lightning/utils';
3
+ import { isUndefinedOrNull } from 'lightning/utilsPrivate';
4
+ import labelProgressRing from '@salesforce/label/LightningProgressRing.progressRing';
3
5
  import labelWarningAltText from '@salesforce/label/LightningProgressRing.warning';
4
6
  import labelExpiredAltText from '@salesforce/label/LightningProgressRing.expired';
5
7
  import labelCompleteAltText from '@salesforce/label/LightningProgressRing.complete';
6
8
 
7
9
  const i18n = {
10
+ progress: labelProgressRing,
8
11
  warning: labelWarningAltText,
9
12
  expired: labelExpiredAltText,
10
13
  complete: labelCompleteAltText,
@@ -48,6 +51,12 @@ export default class progressRing extends LightningElement {
48
51
  */
49
52
  @api direction = 'fill';
50
53
 
54
+ /**
55
+ * Descriptive label provided for assistive technologies.
56
+ * @type {string}
57
+ */
58
+ @api ariaLabel;
59
+
51
60
  /**
52
61
  * The size of the progress ring. Valid values include medium and large.
53
62
  *
@@ -213,4 +222,10 @@ export default class progressRing extends LightningElement {
213
222
  this.variant === 'base-autocomplete' && Number(this.value) === 100
214
223
  );
215
224
  }
225
+
226
+ get computeAriaLabel() {
227
+ return isUndefinedOrNull(this.ariaLabel)
228
+ ? i18n.progress
229
+ : this.ariaLabel;
230
+ }
216
231
  }
@@ -49,7 +49,11 @@
49
49
  onselect={handleOverflowSelect}
50
50
  icon-name="utility:chevrondown"
51
51
  label={i18n.more}
52
- menu-alignment="right">
52
+ menu-alignment="right"
53
+ onfocus={reflectOverflowFocusState}
54
+ onblur={reflectOverflowFocusState}
55
+ >
56
+
53
57
  <template for:each={_allTabs} for:item="tab">
54
58
  <template if:false={tab.visible}>
55
59
  <lightning-menu-item key={tab.value} label={tab.label} value={tab.value} data-target-selection-name={tab.targetSelectionName}></lightning-menu-item>
@@ -70,6 +70,12 @@ export default class LightningTabBar extends LightningElement {
70
70
  }
71
71
  }
72
72
 
73
+ overflowFocused = false;
74
+
75
+ reflectOverflowFocusState(e) {
76
+ this.overflowFocused = e.type === 'focus';
77
+ }
78
+
73
79
  @api
74
80
  get variant() {
75
81
  return this._variant;
@@ -329,22 +335,32 @@ export default class LightningTabBar extends LightningElement {
329
335
  handleKeyDown(event) {
330
336
  let currentFocusedIndex = 0;
331
337
 
332
- if (this._selectedTab) {
338
+ if (this._hasOverflow && this.overflowFocused) {
339
+ currentFocusedIndex = this._visibleTabs.length;
340
+ } else {
333
341
  currentFocusedIndex = this._visibleTabs.findIndex(
334
- (tab) => tab.value === this._selectedTab.value
342
+ (tab) => tab.value === this._selectedTab?.value
335
343
  );
336
344
  }
337
345
  handleKeyDownOnTabList(event, currentFocusedIndex, {
338
346
  isVerticalOrientation: () => this.isVerticalVariant,
339
- totalTabs: () => this._visibleTabs.length,
347
+ // add + 1 to account for the "more" tab
348
+ totalTabs: () =>
349
+ this._visibleTabs.length + (this._hasOverflow ? 1 : 0),
340
350
  selectTabIndex: (index) => {
341
351
  const tab = this._visibleTabs[index];
342
- this._selectTabAndFireSelectEvent(tab.value, {
343
- hasFocus: true,
344
- });
345
- this.template
346
- .querySelector(`a[data-tab-value="${tab.value}"]`)
347
- .focus();
352
+ if (index === this._visibleTabs.length) {
353
+ this.template
354
+ .querySelector('lightning-button-menu')
355
+ .focus();
356
+ } else {
357
+ this._selectTabAndFireSelectEvent(tab.value, {
358
+ hasFocus: true,
359
+ });
360
+ this.template
361
+ .querySelector(`a[data-tab-value="${tab.value}"]`)
362
+ .focus();
363
+ }
348
364
  },
349
365
  });
350
366
  }
@@ -1,33 +1,33 @@
1
- const classNamesHash = (classes) => {
2
- return typeof classes === 'string'
3
- ? classes
4
- .trim()
5
- .split(/\s+/)
6
- .reduce((acc, cn) => {
7
- acc[cn] = true;
8
- return acc;
9
- }, {})
10
- : classes;
11
- };
1
+ import { classSetToString } from 'lightning/utilsPrivate';
2
+
3
+ function classNamesHash(hash, classes) {
4
+ if (typeof classes === 'string') {
5
+ const array = classes.trim().split(/\s+/);
6
+ for (let i = 0, { length } = array; i < length; i += 1) {
7
+ hash[array[i]] = true;
8
+ }
9
+ return hash;
10
+ }
11
+ return Object.assign(hash, classes);
12
+ }
12
13
 
13
14
  const proto = {
14
15
  add(className) {
15
- Object.assign(this, classNamesHash(className));
16
- return this;
16
+ return classNamesHash(this, className);
17
17
  },
18
18
  invert() {
19
- Object.keys(this).forEach((key) => {
19
+ const keys = Object.keys(this);
20
+ for (let i = 0, { length } = keys; i < length; i += 1) {
21
+ const key = keys[i];
20
22
  this[key] = !this[key];
21
- });
23
+ }
22
24
  return this;
23
25
  },
24
26
  toString() {
25
- return Object.keys(this)
26
- .filter((key) => this[key])
27
- .join(' ');
27
+ return classSetToString(this);
28
28
  },
29
29
  };
30
30
 
31
31
  export function classSet(config) {
32
- return Object.assign(Object.create(proto), classNamesHash(config));
32
+ return classNamesHash({ __proto__: proto }, config);
33
33
  }
@@ -65,28 +65,31 @@ const ARIA_PROP_LIST = [
65
65
  * @returns {Object} A lookup object for ARIA properties in (modified) kebab-case or camel-case
66
66
  */
67
67
  const getAriaLookup = (list, type = 'default') => {
68
- if (!list || list.length === 0) {
68
+ const length = list ? list.length : 0;
69
+ if (length === 0) {
69
70
  throw new Error('List of aria properties is required');
70
71
  }
71
72
  const lookupObj = {};
72
73
  if (type === 'default') {
73
- list.forEach((name) => {
74
+ for (let i = 0; i < length; i += 1) {
75
+ const name = list[i];
74
76
  const nameUpperCase = name.toUpperCase();
75
77
  if (!lookupObj[nameUpperCase]) {
76
78
  lookupObj[nameUpperCase] = `aria-${name.toLowerCase()}`;
77
79
  }
78
- });
80
+ }
79
81
  return lookupObj;
80
82
  }
81
- list.forEach((name) => {
83
+ for (let i = 0; i < length; i += 1) {
84
+ const name = list[i];
82
85
  const ariaPropertyLowerCase = `aria-${name.toLowerCase()}`;
83
- const ariaPropertyCamelCase = `aria${name
84
- .charAt(0)
85
- .toUpperCase()}${name.slice(1)}`;
86
86
  if (!lookupObj[ariaPropertyLowerCase]) {
87
+ const ariaPropertyCamelCase = `aria${name[0].toUpperCase()}${name.slice(
88
+ 1
89
+ )}`;
87
90
  lookupObj[ariaPropertyLowerCase] = ariaPropertyCamelCase;
88
91
  }
89
- });
92
+ }
90
93
  return lookupObj;
91
94
  };
92
95
 
@@ -1,11 +1,13 @@
1
1
  export function classListMutation(classList, config) {
2
- Object.keys(config).forEach((key) => {
3
- if (typeof key === 'string' && key.length) {
2
+ const keys = Object.keys(config);
3
+ for (let i = 0, { length } = keys[i]; i < length; i += 1) {
4
+ const key = keys[i];
5
+ if (typeof key === 'string' && key.length > 0) {
4
6
  if (config[key]) {
5
7
  classList.add(key);
6
8
  } else {
7
9
  classList.remove(key);
8
10
  }
9
11
  }
10
- });
12
+ }
11
13
  }
@@ -0,0 +1,11 @@
1
+ export function classSetToString(classes) {
2
+ let string = '';
3
+ const keys = Object.keys(classes);
4
+ for (let i = 0, { length } = keys; i < length; i += 1) {
5
+ const key = keys[i];
6
+ if (classes[key]) {
7
+ string += (string.length ? ' ' : '') + key;
8
+ }
9
+ }
10
+ return string;
11
+ }
@@ -43,15 +43,15 @@ export class EventEmitter {
43
43
  @return {Boolean} - Returns `true` if the event had listeners, `false` otherwise
44
44
  **/
45
45
  emit(name) {
46
- const args = Array.prototype.slice.call(arguments, 1);
47
46
  const listeners = this.registry[name];
48
47
  let count = 0;
49
48
 
50
49
  if (listeners) {
51
- listeners.forEach((listener) => {
50
+ const args = Array.prototype.slice.call(arguments, 1);
51
+ for (let i = 0, { length } = listeners; i < length; i += 1) {
52
52
  count += 1;
53
- listener.apply(null, args);
54
- });
53
+ listeners[i].apply(null, args);
54
+ }
55
55
  }
56
56
  return count > 0;
57
57
  }
@@ -48,9 +48,11 @@ export function makeEverythingExceptElementInert(blockingElement) {
48
48
  }
49
49
 
50
50
  export function restoreInertness(savedInertElements) {
51
- savedInertElements.forEach((element) => {
52
- if (element.node) {
53
- element.node.ariaHidden = element.ariaHidden;
51
+ for (let i = 0, { length } = savedInertElements; i < length; i += 1) {
52
+ const element = savedInertElements[i];
53
+ const { node } = element;
54
+ if (node) {
55
+ node.ariaHidden = element.ariaHidden;
54
56
  }
55
- });
57
+ }
56
58
  }
@@ -18,6 +18,7 @@ export {
18
18
  export { deepCopy, arraysEqual, ArraySlice } from './utility';
19
19
  export { guid } from './guid';
20
20
  export { classListMutation } from './classListMutation';
21
+ export { classSetToString } from './classSet';
21
22
  export { makeEverythingExceptElementInert, restoreInertness } from './inert';
22
23
  export { hasAnimation } from './animation';
23
24
  export {
@@ -64,9 +65,10 @@ export function synchronizeAttrs(element, values) {
64
65
  return;
65
66
  }
66
67
  const attributes = Object.keys(values);
67
- attributes.forEach((attribute) => {
68
- smartSetAttribute(element, attribute, values[attribute]);
69
- });
68
+ for (let i = 0, { length } = attributes; i < length; i += 1) {
69
+ const attr = attributes[i];
70
+ smartSetAttribute(element, attr, values[attr]);
71
+ }
70
72
  }
71
73
 
72
74
  /**
@@ -100,12 +102,10 @@ export function reflectAttribute(element, attrName, value) {
100
102
  * @returns {String} The DOM id or null
101
103
  */
102
104
  export function getRealDOMId(el) {
103
- if (el && typeof el === 'string') {
104
- return el;
105
- } else if (el) {
106
- return el.getAttribute('id');
105
+ if (typeof el === 'string') {
106
+ return el.length > 0 ? el : null;
107
107
  }
108
- return null;
108
+ return typeof el === 'object' && el !== null ? el.getAttribute('id') : null;
109
109
  }
110
110
 
111
111
  /**
@@ -281,3 +281,35 @@ export function isNativeComponent(cmp) {
281
281
  export function isDesktopBrowser() {
282
282
  return formFactor === 'Large';
283
283
  }
284
+
285
+ const { hasOwn: ObjectHasOwn } = Object;
286
+ const { hasOwnProperty: ObjectProtoHasOwnProperty } = Object.prototype;
287
+
288
+ /**
289
+ * Determines if a given object has the specified key as a direct property.
290
+ *
291
+ * @param {Object} object The object to check
292
+ * @param {string} key The property key
293
+ * @returns {Boolean} Whether the given key is a direct property of the object.
294
+ */
295
+ export const hasOwn =
296
+ typeof ObjectHasOwn === 'function'
297
+ ? ObjectHasOwn
298
+ : function hasOwn(object, key) {
299
+ return ObjectProtoHasOwnProperty.call(object, key);
300
+ };
301
+
302
+ /**
303
+ * Determines if a given object has any direct properties.
304
+ *
305
+ * @param {Object} object The object to check
306
+ * @returns {Boolean} Whether the object has direct properties.
307
+ */
308
+ export function hasOwnProperties(object) {
309
+ for (let key in object) {
310
+ if (hasOwn(object, key)) {
311
+ return true;
312
+ }
313
+ }
314
+ return false;
315
+ }