lightning-base-components 1.16.9-alpha → 1.17.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.
Files changed (53) hide show
  1. package/metadata/raptor.json +25 -13
  2. package/package.json +7 -11
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/scopedImports/@salesforce-label-LightningLookup.noAccess.js +1 -0
  5. package/scopedImports/@salesforce-label-LightningToast.close.js +1 -0
  6. package/src/lightning/accordionSection/accordionSection.html +2 -2
  7. package/src/lightning/accordionSection/accordionSection.js +21 -2
  8. package/src/lightning/baseCombobox/baseCombobox.html +1 -1
  9. package/src/lightning/baseCombobox/baseCombobox.js +17 -2
  10. package/src/lightning/baseCombobox/keyboard.js +26 -0
  11. package/src/lightning/buttonGroup/buttonGroup.css +9 -0
  12. package/src/lightning/buttonGroup/buttonGroup.html +1 -1
  13. package/src/lightning/buttonMenu/buttonMenu.html +2 -5
  14. package/src/lightning/buttonMenu/buttonMenu.js +1 -4
  15. package/src/lightning/buttonStateful/buttonStateful.html +2 -1
  16. package/src/lightning/buttonStateful/buttonStateful.js +10 -0
  17. package/src/lightning/card/__docs__/card.md +1 -1
  18. package/src/lightning/card/card.html +3 -1
  19. package/src/lightning/card/card.js +51 -15
  20. package/src/lightning/card/utils.js +0 -14
  21. package/src/lightning/checkboxGroup/checkboxGroup.html +1 -1
  22. package/src/lightning/datatable/renderManager.js +14 -5
  23. package/src/lightning/datatable/rowSelection.js +4 -4
  24. package/src/lightning/datatable/rows.js +1 -0
  25. package/src/lightning/datatable/templates/table/table.html +2 -0
  26. package/src/lightning/focusUtils/focus.js +42 -0
  27. package/src/lightning/focusUtils/focusUtils.js +1 -0
  28. package/src/lightning/formattedAddress/formattedAddress.js +1 -1
  29. package/src/lightning/input/input.html +5 -2
  30. package/src/lightning/input/input.js +13 -1
  31. package/src/lightning/modalBase/modalBase.js +7 -6
  32. package/src/lightning/primitiveBubble/primitiveBubble.css +9 -0
  33. package/src/lightning/primitiveBubble/primitiveBubble.js +3 -1
  34. package/src/lightning/primitiveCellActions/primitiveCellActions.html +1 -0
  35. package/src/lightning/primitiveCellActions/primitiveCellActions.js +1 -1
  36. package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +3 -1
  37. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +1 -0
  38. package/src/lightning/primitiveTreegridCellToggle/primitiveTreegridCellToggle.js +8 -3
  39. package/src/lightning/radioGroup/radioGroup.html +1 -1
  40. package/src/lightning/textarea/textarea.js +13 -1
  41. package/src/lightning/tooltipLibrary/tooltipLibrary.js +7 -0
  42. package/src/lightning/tree/tree.html +1 -1
  43. package/src/lightning/tree/tree.js +25 -1
  44. package/src/lightning/treeGrid/__docs__/treeGrid.md +12 -0
  45. package/src/lightning/treeGrid/treeGrid.js +12 -4
  46. package/src/lightning/utilsPrivate/ariaLevelHeadingUtils.js +11 -0
  47. package/src/lightning/utilsPrivate/os.js +36 -0
  48. package/src/lightning/utilsPrivate/utilsPrivate.js +7 -0
  49. package/src/lightning/verticalNavigationSection/verticalNavigationSection.html +1 -1
  50. package/src/lightning/verticalNavigationSection/verticalNavigationSection.js +22 -1
  51. package/scopedImports/@salesforce-label-LightningToast.missingToastLabel.js +0 -1
  52. package/scopedImports/@salesforce-label-LightningToastContainer.missingToastConfig.js +0 -1
  53. package/scopedImports/@salesforce-label-LightningToastContainer.missingToastProperty.js +0 -1
@@ -20,6 +20,9 @@
20
20
  "actions"
21
21
  ],
22
22
  "properties": [
23
+ {
24
+ "name": "headingLevel"
25
+ },
23
26
  {
24
27
  "name": "label"
25
28
  },
@@ -352,6 +355,9 @@
352
355
  "minVersion": "0.0",
353
356
  "slotNames": [],
354
357
  "properties": [
358
+ {
359
+ "name": "disabled"
360
+ },
355
361
  {
356
362
  "name": "groupOrder"
357
363
  },
@@ -398,9 +404,15 @@
398
404
  {
399
405
  "name": "headingLevel"
400
406
  },
407
+ {
408
+ "name": "hideHeader"
409
+ },
401
410
  {
402
411
  "name": "iconName"
403
412
  },
413
+ {
414
+ "name": "label"
415
+ },
404
416
  {
405
417
  "name": "variant"
406
418
  }
@@ -860,6 +872,7 @@
860
872
  "minVersion": "54.0"
861
873
  },
862
874
  "experienceModelApi": {},
875
+ "f6Controller": {},
863
876
  "fieldDependencyManager": {},
864
877
  "fieldUtils": {},
865
878
  "fileDownload": {
@@ -2339,19 +2352,7 @@
2339
2352
  "slotNames": [],
2340
2353
  "properties": [
2341
2354
  {
2342
- "name": "errorMessage"
2343
- },
2344
- {
2345
- "name": "fieldLevelHelp"
2346
- },
2347
- {
2348
- "name": "label"
2349
- },
2350
- {
2351
- "name": "required"
2352
- },
2353
- {
2354
- "name": "variant"
2355
+ "name": "disabled"
2355
2356
  }
2356
2357
  ],
2357
2358
  "methods": [
@@ -3560,6 +3561,11 @@
3560
3561
  {
3561
3562
  "name": "variant"
3562
3563
  }
3564
+ ],
3565
+ "methods": [
3566
+ {
3567
+ "name": "focus"
3568
+ }
3563
3569
  ]
3564
3570
  },
3565
3571
  "toastContainer": {
@@ -3588,6 +3594,9 @@
3588
3594
  {
3589
3595
  "name": "header"
3590
3596
  },
3597
+ {
3598
+ "name": "headingLevel"
3599
+ },
3591
3600
  {
3592
3601
  "name": "items"
3593
3602
  },
@@ -3808,6 +3817,9 @@
3808
3817
  ""
3809
3818
  ],
3810
3819
  "properties": [
3820
+ {
3821
+ "name": "headingLevel"
3822
+ },
3811
3823
  {
3812
3824
  "name": "label"
3813
3825
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightning-base-components",
3
- "version": "1.16.9-alpha",
3
+ "version": "1.17.2-alpha",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "external",
@@ -910,6 +910,10 @@
910
910
  "name": "@salesforce/label/LightningLookup.messageWhenMissingInformation",
911
911
  "path": "scopedImports/@salesforce-label-LightningLookup.messageWhenMissingInformation.js"
912
912
  },
913
+ {
914
+ "name": "@salesforce/label/LightningLookup.noAccess",
915
+ "path": "scopedImports/@salesforce-label-LightningLookup.noAccess.js"
916
+ },
913
917
  {
914
918
  "name": "@salesforce/label/LightningPicklist.available",
915
919
  "path": "scopedImports/@salesforce-label-LightningPicklist.available.js"
@@ -1067,16 +1071,8 @@
1067
1071
  "path": "scopedImports/@salesforce-label-LightningToast.errorLabel.js"
1068
1072
  },
1069
1073
  {
1070
- "name": "@salesforce/label/LightningToast.missingToastLabel",
1071
- "path": "scopedImports/@salesforce-label-LightningToast.missingToastLabel.js"
1072
- },
1073
- {
1074
- "name": "@salesforce/label/LightningToastContainer.missingToastProperty",
1075
- "path": "scopedImports/@salesforce-label-LightningToastContainer.missingToastProperty.js"
1076
- },
1077
- {
1078
- "name": "@salesforce/label/LightningToastContainer.missingToastConfig",
1079
- "path": "scopedImports/@salesforce-label-LightningToastContainer.missingToastConfig.js"
1074
+ "name": "@salesforce/label/LightningToast.close",
1075
+ "path": "scopedImports/@salesforce-label-LightningToast.close.js"
1080
1076
  },
1081
1077
  {
1082
1078
  "name": "@salesforce/label/Global_Entity.created_by",
@@ -1 +1 @@
1
- export default '240';
1
+ export default '242';
@@ -0,0 +1 @@
1
+ export default 'No access';
@@ -0,0 +1 @@
1
+ export default 'Close';
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <section class={computedSectionClasses}>
3
3
  <div class="slds-accordion__summary">
4
- <h3 onkeydown={handleKeyDown} class="slds-accordion__summary-heading">
4
+ <h2 aria-level={_privateHeadingAriaLevel} onkeydown={handleKeyDown} class="slds-accordion__summary-heading">
5
5
  <button class="section-control slds-button slds-button_reset slds-accordion__summary-action"
6
6
  type="button"
7
7
  aria-expanded={computedAriaExpanded}
@@ -14,7 +14,7 @@
14
14
  </lightning-primitive-icon>
15
15
  <span class="slds-accordion__summary-content" title={label}>{label}</span>
16
16
  </button>
17
- </h3>
17
+ </h2>
18
18
  <slot name="actions"></slot>
19
19
  </div>
20
20
  <div id='lgt-accordion-section' hidden={computedHidden} aria-hidden={computedAriaHidden} class="slds-accordion__content">
@@ -1,7 +1,7 @@
1
1
  import { LightningElement, api, track } from 'lwc';
2
- import { classSet } from 'lightning/utils';
3
2
  import { generateUniqueId } from 'lightning/inputUtils';
4
- import { keyCodes } from 'lightning/utilsPrivate';
3
+ import { keyCodes, isHeadingLevelValid } from 'lightning/utilsPrivate';
4
+ import { classSet } from 'lightning/utils';
5
5
 
6
6
  /**
7
7
  * A single section that is nested in an accordion component.
@@ -30,6 +30,25 @@ export default class LightningAccordionSection extends LightningElement {
30
30
 
31
31
  @track privateIsOpen = false;
32
32
 
33
+ _privateHeadingAriaLevel;
34
+
35
+ /**
36
+ * Changes the 'aria-level' attribute value for the
37
+ * <h2> markup tag in the card's title element. Supported values
38
+ * are (1, 2, 3, 4, 5, 6).
39
+ * @type {string | number}
40
+ */
41
+ @api
42
+ get headingLevel() {
43
+ return this._privateHeadingAriaLevel;
44
+ }
45
+
46
+ set headingLevel(value) {
47
+ if (isHeadingLevelValid(value)) {
48
+ this._privateHeadingAriaLevel = value;
49
+ }
50
+ }
51
+
33
52
  /**
34
53
  * Section should have received focus, but hasn't yet.
35
54
  */
@@ -75,7 +75,7 @@
75
75
  <template if:false={hasInputPill}>
76
76
  <div class="slds-input__icon-group slds-input__icon-group_right">
77
77
  <template if:true={showInputActivityIndicator}>
78
- <div role="status" class="slds-spinner slds-spinner_brand slds-spinner_x-small slds-input__spinner">
78
+ <div class="slds-spinner slds-spinner_brand slds-spinner_x-small slds-input__spinner">
79
79
  <span class="slds-assistive-text">{i18n.loadingText}</span>
80
80
  <div class="slds-spinner__dot-a"></div>
81
81
  <div class="slds-spinner__dot-b"></div>
@@ -95,6 +95,7 @@ export default class LightningBaseCombobox extends LightningElement {
95
95
  _autocomplete = 'off';
96
96
  originDisableDefaultHighlight;
97
97
  privateDisableDefaultHighlight;
98
+ _editingMode = false;
98
99
 
99
100
  constructor() {
100
101
  super();
@@ -347,12 +348,14 @@ export default class LightningBaseCombobox extends LightningElement {
347
348
  synchronizeAttrs(input, {
348
349
  [ARIA_LABELLEDBY]: this.inputLabelledById,
349
350
  [ARIA_DESCRIBEDBY]: this.computedAriaDescribedBy,
350
- [ARIA_ACTIVEDESCENDANT]: this._activeElementDomId,
351
351
  [ARIA_CONTROLS]: this.computedInputControls,
352
352
  [ARIA_LABEL]: this.isUserInputDisabled
353
353
  ? this.computedButtonTriggerAriaLabel
354
354
  : this.inputLabel,
355
355
  [ARIA_INVALID]: this.computedAriaInvalid,
356
+ [ARIA_ACTIVEDESCENDANT]: this._editingMode
357
+ ? undefined
358
+ : this._activeElementDomId,
356
359
  });
357
360
  }
358
361
 
@@ -605,6 +608,7 @@ export default class LightningBaseCombobox extends LightningElement {
605
608
 
606
609
  handleOptionMouseEnter(event) {
607
610
  if (event.target.hasAttribute('aria-selected')) {
611
+ this._editingMode = false;
608
612
  this.highlightOption(event.target);
609
613
  }
610
614
  }
@@ -662,6 +666,10 @@ export default class LightningBaseCombobox extends LightningElement {
662
666
  if (this.dropdownDisabled) {
663
667
  return;
664
668
  }
669
+ // For details: https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event
670
+ if (event.isComposing || event.keyCode === 229) {
671
+ return;
672
+ }
665
673
  if (this.hasInputPill) {
666
674
  this.handlePillKeyDown(event);
667
675
  } else {
@@ -953,7 +961,7 @@ export default class LightningBaseCombobox extends LightningElement {
953
961
 
954
962
  highlightDefaultItem() {
955
963
  this.removeHighlight();
956
- if (!this.privateDisableDefaultHighlight) {
964
+ if (!this.privateDisableDefaultHighlight && !this._editingMode) {
957
965
  // eslint-disable-next-line @lwc/lwc/no-async-operation
958
966
  requestAnimationFrame(() => {
959
967
  this.highlightOptionAndScrollIntoView(
@@ -1041,6 +1049,13 @@ export default class LightningBaseCombobox extends LightningElement {
1041
1049
  closeDropdown() {
1042
1050
  that.closeDropdown();
1043
1051
  },
1052
+ setEditingMode(isEditing) {
1053
+ that._editingMode = isEditing;
1054
+ if (isEditing) {
1055
+ that._activeElementDomId = null;
1056
+ }
1057
+ that.synchronizeA11y();
1058
+ },
1044
1059
  };
1045
1060
  }
1046
1061
 
@@ -157,6 +157,31 @@ const eventKeyToHandlerMap = {
157
157
  Delete: handleDeletionKeys,
158
158
  };
159
159
 
160
+ const NON_EDITING_KEYS = [
161
+ 'Enter',
162
+ 'PageUp',
163
+ 'PageDown',
164
+ 'Home',
165
+ 'End',
166
+ 'Down',
167
+ 'Up',
168
+ 'ArrowUp',
169
+ 'ArrowDown',
170
+ 'Esc',
171
+ 'Escape',
172
+ 'Tab',
173
+ ];
174
+ const isAnEditingKey = (key) => {
175
+ return !NON_EDITING_KEYS.includes(key);
176
+ };
177
+ const handleEditingMode = ({ event, dropdownInterface }) => {
178
+ if (isAnEditingKey(event.key)) {
179
+ dropdownInterface.setEditingMode(true);
180
+ } else {
181
+ dropdownInterface.setEditingMode(false);
182
+ }
183
+ };
184
+
160
185
  export function handleKeyDownOnInput({
161
186
  event,
162
187
  currentIndex,
@@ -168,4 +193,5 @@ export function handleKeyDownOnInput({
168
193
  } else {
169
194
  handleTypedCharacters(parameters);
170
195
  }
196
+ handleEditingMode(parameters);
171
197
  }
@@ -1,2 +1,11 @@
1
1
  /* @import 'lightning/sldsCommon';
2
2
  @import './button-group.slds.css'; */
3
+
4
+ /* Fix for lihgtning-button-icon not being flush with lightning-button, for details see: W-11658757
5
+ *
6
+ * :host:not([data-render-mode]) - specifies that this style will only be applied in synthetic shadow mode
7
+ */
8
+
9
+ :host:not([data-render-mode]) .fix_button-group-flexbox {
10
+ display: inline-flex;
11
+ }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div part="button-group">
2
+ <div part="button-group" class="fix_button-group-flexbox">
3
3
  <slot onslotchange={handleSlotChange}></slot>
4
4
  </div>
5
5
  </template>
@@ -11,8 +11,6 @@
11
11
  onblur={handleBlur}
12
12
  onfocus={handleFocus}
13
13
  type="button"
14
- aria-controls="lgt-button-menu-dropdown"
15
- id="lgt-button-menu"
16
14
  onmousedown={handleButtonMouseDown}
17
15
  tabindex={tabIndex}
18
16
  part="button button-icon">
@@ -40,10 +38,9 @@
40
38
  <lightning-spinner size="small" alternative-text={computedLoadingStateAlternativeText}></lightning-spinner>
41
39
  </template>
42
40
  <template if:false={isLoading}>
43
- <div class="slds-dropdown__list slds-dropdown_length-with-icon-10"
44
- id="lgt-button-menu-dropdown"
41
+ <div
42
+ class="slds-dropdown__list slds-dropdown_length-with-icon-10"
45
43
  role="menu"
46
- aria-labelledby="lgt-button-menu"
47
44
  onprivateselect={handleMenuItemPrivateSelect}
48
45
  onprivateblur={handlePrivateBlur}
49
46
  onprivatefocus={handlePrivateFocus}
@@ -772,10 +772,7 @@ export default class LightningButtonMenu extends LightningElement {
772
772
  // prevent blur during a non-blurring focus change
773
773
  // set lock so that while focusing on menutitem, menu doesnt close
774
774
  this.cancelBlur();
775
- // eslint-disable-next-line @lwc/lwc/no-async-operation
776
- requestAnimationFrame(() => {
777
- menuItem.focus();
778
- });
775
+ menuItem.focus();
779
776
  }
780
777
  // allowBlur is called when the menu items receives focus
781
778
  }
@@ -4,7 +4,8 @@
4
4
  onblur={handleButtonBlur}
5
5
  onclick={handleButtonClick}
6
6
  aria-live="polite"
7
- part="button">
7
+ part="button"
8
+ disabled={disabled}>
8
9
 
9
10
  <span class="slds-text-not-selected">
10
11
  <template if:true={iconNameWhenOff}>
@@ -22,6 +22,11 @@ export default class LightningButtonStateful extends LightningElement {
22
22
  isClicked: false,
23
23
  };
24
24
 
25
+ /**
26
+ * Passthrough to pass disabled attribute onto button
27
+ */
28
+ @api disabled;
29
+
25
30
  /**
26
31
  * The name of the icon to be used in the format 'utility:check' when the state is true.
27
32
  *
@@ -104,6 +109,11 @@ export default class LightningButtonStateful extends LightningElement {
104
109
  this.state.selected = normalizeBoolean(value);
105
110
  }
106
111
 
112
+ renderedCallback() {
113
+ // change host style to disable pointer event.
114
+ this.template.host.style.pointerEvents = this.disabled ? 'none' : '';
115
+ }
116
+
107
117
  /**
108
118
  * Sets focus on the button.
109
119
  */
@@ -111,4 +111,4 @@ This component has usage differences from its Aura counterpart. See [Base Compon
111
111
 
112
112
  #### Source Code
113
113
 
114
- `lightning-card` is available in the [Base Components Recipes GitHub repository](https://github.com/salesforce/base-components-recipes#documentation). It's transpiled into the `c` namespace so that you can use it in your own projects.
114
+ `lightning-card` is available in the [Base Components Recipes GitHub repository](https://github.com/salesforce/base-components-recipes#documentation). It's transpiled into the `c` namespace so that you can use it in your own projects.
@@ -1,5 +1,6 @@
1
1
  <template>
2
- <article class={computedWrapperClassNames} part="card">
2
+ <article class={computedWrapperClassNames} aria-label={label} part="card">
3
+ <template if:true={computedHidden}>
3
4
  <div class="slds-card__header slds-grid">
4
5
  <header class="slds-media slds-media_center slds-has-flexi-truncate" part="header">
5
6
  <template if:true={hasIcon}>
@@ -32,6 +33,7 @@
32
33
  </div>
33
34
  </header>
34
35
  </div>
36
+ </template>
35
37
  <div class="slds-card__body" part="body">
36
38
  <slot></slot>
37
39
  </div>
@@ -1,11 +1,7 @@
1
1
  import { LightningElement, api } from 'lwc';
2
2
  import { classSet } from 'lightning/utils';
3
- import {
4
- isNarrow,
5
- isBase,
6
- isHeadingLevelValid,
7
- DEFAULT_HEADING_LEVEL,
8
- } from './utils';
3
+ import { isNarrow, isBase } from './utils';
4
+ import { isHeadingLevelValid } from 'lightning/utilsPrivate';
9
5
 
10
6
  /**
11
7
  * Cards apply a container around a related grouping of information.
@@ -81,15 +77,7 @@ export default class LightningCard extends LightningElement {
81
77
  }
82
78
  }
83
79
 
84
- _privateHeadingAriaLevel = DEFAULT_HEADING_LEVEL;
85
-
86
- set headingLevel(value) {
87
- if (isHeadingLevelValid(value)) {
88
- this._privateHeadingAriaLevel = value;
89
- } else {
90
- this._privateHeadingAriaLevel = DEFAULT_HEADING_LEVEL;
91
- }
92
- }
80
+ _privateHeadingAriaLevel;
93
81
 
94
82
  /**
95
83
  * The headingLevel changes the 'aria-level' attribute value of
@@ -104,6 +92,12 @@ export default class LightningCard extends LightningElement {
104
92
  return this._privateHeadingAriaLevel;
105
93
  }
106
94
 
95
+ set headingLevel(value) {
96
+ if (isHeadingLevelValid(value)) {
97
+ this._privateHeadingAriaLevel = value;
98
+ }
99
+ }
100
+
107
101
  get titleSlot() {
108
102
  return this.template.querySelector('slot[name=title]');
109
103
  }
@@ -125,4 +119,46 @@ export default class LightningCard extends LightningElement {
125
119
  get hasStringTitle() {
126
120
  return !!this.title;
127
121
  }
122
+
123
+ privateHeaderLabel;
124
+
125
+ /**
126
+ * Assistive label for the card header. Only shown if `hideHeader` attribute is set to `true`.
127
+ * @type {string}
128
+ */
129
+ @api
130
+ get label() {
131
+ if (!this._hasTitle || this.hideHeader) {
132
+ return this.privateHeaderLabel;
133
+ }
134
+ return null;
135
+ }
136
+ set label(value) {
137
+ this.privateHeaderLabel = value;
138
+ }
139
+
140
+ get computedHidden() {
141
+ if (!this.label && this.hideHeader) {
142
+ console.warn(
143
+ 'Setting header without a label can cause accessibility issues'
144
+ );
145
+ }
146
+ return !this.hideHeader;
147
+ }
148
+
149
+ hiddenHeader = false;
150
+
151
+ /**
152
+ * Hides the header chunk of the card when set to `true`.
153
+ * Requires you to set the `label` attribute to supplement a non-rendered header. If `label` isn't set, you get a `console.warn` error.
154
+ * @type {boolean}
155
+ * @default {false}
156
+ */
157
+ @api
158
+ get hideHeader() {
159
+ return this.hiddenHeader;
160
+ }
161
+ set hideHeader(value) {
162
+ this.hiddenHeader = value;
163
+ }
128
164
  }
@@ -1,10 +1,3 @@
1
- /**
2
- * These are all values that can be set to "aria-level" attribute of h2 tag for the card's title.
3
- */
4
- export const VALID_HEADING_LEVELS = ['1', '2', '3', '4', '5', '6'];
5
-
6
- export const DEFAULT_HEADING_LEVEL = '2';
7
-
8
1
  export function isNarrow(variant) {
9
2
  return typeof variant === 'string' && variant.toLowerCase() === 'narrow';
10
3
  }
@@ -12,10 +5,3 @@ export function isNarrow(variant) {
12
5
  export function isBase(variant) {
13
6
  return typeof variant === 'string' && variant.toLowerCase() === 'base';
14
7
  }
15
-
16
- export function isHeadingLevelValid(level) {
17
- return (
18
- (typeof level === 'string' || typeof level === 'number') &&
19
- VALID_HEADING_LEVELS.includes(String(level))
20
- );
21
- }
@@ -30,7 +30,7 @@
30
30
  </div>
31
31
 
32
32
  <template if:true={_helpMessage}>
33
- <div id="helptext" data-helptext class="slds-form-element__help">{_helpMessage}</div>
33
+ <div id="helptext" data-helptext role="alert" class="slds-form-element__help">{_helpMessage}</div>
34
34
  </template>
35
35
 
36
36
  </fieldset>
@@ -83,9 +83,11 @@ export class RenderManager {
83
83
  if (normalizeBoolean(viewportRendering) || state.virtualize) {
84
84
  this.initializeResizeObserver(state, getWrapperHeight);
85
85
  }
86
+ let computedBufferSize =
87
+ typeof bufferSize != 'undefined' ? bufferSize : DEFAULT_BUFFER_SIZE;
86
88
  state.bufferSize = normalizeNumberAttribute(
87
89
  'bufferSize',
88
- bufferSize || DEFAULT_BUFFER_SIZE,
90
+ computedBufferSize,
89
91
  'non-negative',
90
92
  DEFAULT_BUFFER_SIZE
91
93
  );
@@ -213,10 +215,17 @@ export class RenderManager {
213
215
  const normalizedRowCount = rowCount
214
216
  ? Math.min(rowCount, totalRows)
215
217
  : totalRows;
216
- state.renderedRowCount = normalizedRowCount;
217
-
218
- // Update our internal cache
219
- this.previousCache.renderedRowCount = normalizedRowCount;
218
+ if (isViewportRenderingEnabled(state)) {
219
+ if (state.renderedRowCount < normalizedRowCount) {
220
+ state.renderedRowCount = normalizedRowCount;
221
+ // Update our internal cache
222
+ this.previousCache.renderedRowCount = normalizedRowCount;
223
+ }
224
+ } else {
225
+ state.renderedRowCount = normalizedRowCount;
226
+ // Update our internal cache
227
+ this.previousCache.renderedRowCount = normalizedRowCount;
228
+ }
220
229
  this.previousCache.totalRowCount = totalRows;
221
230
 
222
231
  if (rows.length > 0) {
@@ -51,7 +51,7 @@ export function handleSelectAllRows(event) {
51
51
  event.stopPropagation();
52
52
  markAllRowsSelected(this.state);
53
53
  this.fireSelectedRowsChange(this.getSelectedRows(), {
54
- selection: ROWS_ACTION.SELECT_ALL_ROWS,
54
+ action: ROWS_ACTION.SELECT_ALL_ROWS,
55
55
  });
56
56
  }
57
57
 
@@ -64,7 +64,7 @@ export function handleDeselectAllRows(event) {
64
64
  event.stopPropagation();
65
65
  markAllRowsDeselected(this.state);
66
66
  this.fireSelectedRowsChange(this.getSelectedRows(), {
67
- selection: ROWS_ACTION.DESELECT_ALL_ROWS,
67
+ action: ROWS_ACTION.DESELECT_ALL_ROWS,
68
68
  });
69
69
  }
70
70
 
@@ -89,7 +89,7 @@ export function handleSelectRow(event) {
89
89
  markSelectedRowsInterval(this.state, fromRowKey, rowKeyValue);
90
90
  setLastRowSelection(this.state, rowKeyValue);
91
91
  this.fireSelectedRowsChange(this.getSelectedRows(), {
92
- selection: ROWS_ACTION.ROW_SELECT,
92
+ action: ROWS_ACTION.ROW_SELECT,
93
93
  value: rowKeyValue,
94
94
  });
95
95
  }
@@ -115,7 +115,7 @@ export function handleDeselectRow(event) {
115
115
  markDeselectedRowsInterval(this.state, fromRowKey, rowKeyValue);
116
116
  setLastRowSelection(this.state, rowKeyValue);
117
117
  this.fireSelectedRowsChange(this.getSelectedRows(), {
118
- selection: ROWS_ACTION.ROW_DESELECT,
118
+ action: ROWS_ACTION.ROW_DESELECT,
119
119
  value: rowKeyValue,
120
120
  });
121
121
  }
@@ -173,6 +173,7 @@ export function updateRowsAndCellIndexes() {
173
173
  : undefined,
174
174
  dataLabel: colData.label,
175
175
  value: computedCellValue, // value based on the fieldName
176
+ displayValue: rowData.displayValue ? rowData.displayValue : '',
176
177
  rowKeyValue: row.key, // unique row key value
177
178
  colKeyValue, // unique column key value
178
179
  tabIndex: -1, // tabindex
@@ -167,6 +167,7 @@
167
167
  row-key-value={row.key}
168
168
  col-key-value={cell.colKeyValue}
169
169
  value={cell.value}
170
+ display-value={cell.displayValue}
170
171
  icon-name={cell.iconName}
171
172
  icon-label={cell.iconLabel}
172
173
  icon-position={cell.iconPosition}
@@ -216,6 +217,7 @@
216
217
  row-key-value={row.key}
217
218
  col-key-value={cell.colKeyValue}
218
219
  value={cell.value}
220
+ display-value={cell.displayValue}
219
221
  icon-name={cell.iconName}
220
222
  icon-label={cell.iconLabel}
221
223
  icon-position={cell.iconPosition}
@@ -81,6 +81,48 @@ export function getElementWithFocus() {
81
81
  return undefined;
82
82
  }
83
83
 
84
+ /**
85
+ * Return the focus to an element if it is still attached to the DOM.
86
+ * @param {Element} element - element which will receive the focus.
87
+ * @returns {boolean} - true if the element is focused.
88
+ */
89
+ export function returnFocusToElement(element) {
90
+ const isSavedElemInDOM = document.body.contains(element);
91
+ if (element) {
92
+ if (isSavedElemInDOM) {
93
+ element.focus();
94
+ return true;
95
+ }
96
+ const isElementInDOM = isElementInDocument(element);
97
+ if (isElementInDOM) {
98
+ element.focus();
99
+ return true;
100
+ }
101
+ }
102
+ return false;
103
+ }
104
+
105
+ /**
106
+ * Return true if the given element is in document (rather it is in shadow or not)
107
+ * @param {Element} element - element to be inspected
108
+ * @returns {boolean} - true if the element is in document, otherwise false
109
+ */
110
+ function isElementInDocument(element) {
111
+ let currentElement = element;
112
+ // traverse all the way up to the document, to make sure
113
+ // the element is inside of the document.
114
+ while (currentElement && currentElement.parentNode) {
115
+ if (currentElement.parentNode === document) {
116
+ return true;
117
+ } else if (currentElement.parentNode instanceof DocumentFragment) {
118
+ currentElement = currentElement.parentNode.host;
119
+ } else {
120
+ currentElement = currentElement.parentNode;
121
+ }
122
+ }
123
+ return false;
124
+ }
125
+
84
126
  /**
85
127
  * Recursively traverse an active tree and run callback on each non-inert node element.
86
128
  *
@@ -3,4 +3,5 @@ export {
3
3
  findAllFocusableNodes,
4
4
  getElementWithFocus,
5
5
  filterTooltips,
6
+ returnFocusToElement,
6
7
  } from './focus';
@@ -220,7 +220,7 @@ export default class LightningFormattedAddress extends LightningElement {
220
220
  }
221
221
 
222
222
  get mapUrl() {
223
- return encodeURI(MAP_HOST + this.mapQuery);
223
+ return encodeURI(MAP_HOST) + encodeURIComponent(this.mapQuery);
224
224
  }
225
225
 
226
226
  get addressLink() {
@@ -75,6 +75,7 @@
75
75
  onblur={handleBlur}
76
76
  onfocus={handleFocus}
77
77
  onchange={handleChange}
78
+ onclick={handleCheckboxClick}
78
79
  name={name}
79
80
  required={required}
80
81
  readonly={readOnly}
@@ -105,7 +106,7 @@
105
106
  <abbr class="slds-required" title={i18n.required}>*</abbr>
106
107
  </template>
107
108
  </template>
108
- <input type="checkbox"
109
+ <input type="checkbox"
109
110
  part="checkbox"
110
111
  id="checkbox"
111
112
  aria-label={computedAriaLabel}
@@ -114,6 +115,7 @@
114
115
  onblur={handleBlur}
115
116
  onfocus={handleFocus}
116
117
  onchange={handleChange}
118
+ onclick={handleCheckboxClick}
117
119
  name={name}
118
120
  required={required}
119
121
  readonly={readOnly}
@@ -142,6 +144,7 @@
142
144
  onblur={handleBlur}
143
145
  onfocus={handleFocus}
144
146
  onchange={handleChange}
147
+ onclick={handleCheckboxClick}
145
148
  name={name}
146
149
  required={required}
147
150
  readonly={readOnly}
@@ -341,6 +344,6 @@
341
344
  </template>
342
345
 
343
346
  <template if:true={_helpMessage}>
344
- <div id="help-message" class="slds-form-element__help" data-help-message role="alert" part="help-text">{_helpMessage}</div>
347
+ <div id="help-message" class={computedHelpMessageClass} data-help-message role="alert" part="help-text">{_helpMessage}</div>
345
348
  </template>
346
349
  </template>
@@ -533,7 +533,7 @@ export default class LightningInput extends LightningElement {
533
533
  @api
534
534
  get ariaLabelledBy() {
535
535
  // native version returns the auto linked value
536
- if (this.isNative) {
536
+ if (this.isNative && this._rendered) {
537
537
  const ariaValues = this._inputElement.getAttribute(ARIA_LABELLEDBY);
538
538
  return filterNonAutoLink(ariaValues);
539
539
  }
@@ -1306,6 +1306,12 @@ export default class LightningInput extends LightningElement {
1306
1306
  .toString();
1307
1307
  }
1308
1308
 
1309
+ get computedHelpMessageClass() {
1310
+ return classSet('slds-form-element__help')
1311
+ .add({ 'slds-m-left_none': this.variant !== VARIANT.LABEL_INLINE })
1312
+ .toString();
1313
+ }
1314
+
1309
1315
  get normalizedMax() {
1310
1316
  return this._normalizeDateTimeString(this.max);
1311
1317
  }
@@ -1750,6 +1756,12 @@ export default class LightningInput extends LightningElement {
1750
1756
  }
1751
1757
  }
1752
1758
 
1759
+ handleCheckboxClick() {
1760
+ if (this.template.activeElement === null) {
1761
+ this.template.querySelector("[type='checkbox']").focus();
1762
+ }
1763
+ }
1764
+
1753
1765
  handleChange(event) {
1754
1766
  event.stopPropagation();
1755
1767
 
@@ -10,7 +10,10 @@ import {
10
10
  isAriaDescriptionSupported,
11
11
  } from 'lightning/utilsPrivate';
12
12
  import { instanceName, secure } from 'lightning/overlayUtils';
13
- import { getElementWithFocus } from 'lightning/focusUtils';
13
+ import {
14
+ getElementWithFocus,
15
+ returnFocusToElement,
16
+ } from 'lightning/focusUtils';
14
17
  import closeButtonAltText from '@salesforce/label/LightningModalBase.cancelandclose';
15
18
  import disableCloseBtnMessage from '@salesforce/label/LightningModalBase.waitstate';
16
19
 
@@ -147,7 +150,7 @@ export default class LightningModalBase extends LightningElement {
147
150
  // we track both in order to handle transition correctly
148
151
  const isSwitchingToDisabled =
149
152
  !this.disableCloseButton && this.disableClose;
150
- /* Future enhancement possibility - trigger setInterval to remove and
153
+ /* Future enhancement possibility - trigger setInterval to remove and
151
154
  again add back 'Processing' text, as this will indicate to the screen
152
155
  reader user that the interface continues to be busy
153
156
  */
@@ -349,10 +352,8 @@ export default class LightningModalBase extends LightningElement {
349
352
  */
350
353
  returnFocusToBackground() {
351
354
  const { savedActiveElement } = this;
352
- const isSavedElemInDOM = document.body.contains(savedActiveElement);
353
- if (savedActiveElement && isSavedElemInDOM) {
354
- savedActiveElement.focus();
355
- } else {
355
+ const elementWasFocused = returnFocusToElement(savedActiveElement);
356
+ if (!elementWasFocused) {
356
357
  // eslint-disable-next-line no-console
357
358
  console.warn('Modal :: Nothing to return focus to');
358
359
  }
@@ -1,2 +1,11 @@
1
1
  /* @import 'lightning/sldsCommon';
2
2
  @import './tooltip.slds.css'; */
3
+
4
+ /* Fix for tooltip alignment issue, for details see: W-11677142
5
+ *
6
+ * fix-tooltip_alignment - the custom class that edits min-width
7
+ */
8
+
9
+ .fix-popover_tooltip_alignment {
10
+ min-width: inherit;
11
+ }
@@ -102,7 +102,9 @@ export default class LightningPrimitiveBubble extends LightningElement {
102
102
 
103
103
  // compute class value for this bubble
104
104
  get computedPopoverClass() {
105
- const classes = classSet('slds-popover').add('slds-popover_tooltip');
105
+ const classes = classSet('slds-popover')
106
+ .add('slds-popover_tooltip')
107
+ .add('fix-popover_tooltip_alignment'); // fix for W-11677142
106
108
 
107
109
  // show or hide bubble
108
110
  classes.add({
@@ -7,6 +7,7 @@
7
7
  onopen={handleMenuOpen}
8
8
  is-loading={_isLoadingActions}
9
9
  loading-state-alternative-text={spinnerAlternateText}
10
+ tab-index={internalTabIndex}
10
11
  >
11
12
  <template for:each={_actions} for:item="action">
12
13
  <lightning-menu-item
@@ -33,13 +33,13 @@ export default class PrimitiveCellActions extends LightningElement {
33
33
  static delegatesFocus = true;
34
34
  _isLoadingActions;
35
35
  _menuAlignment = DEFAULT_MENU_ALIGNMENT;
36
- _internalTabIndex = false;
37
36
 
38
37
  /************************** PUBLIC ATTRIBUTES ***************************/
39
38
 
40
39
  @api rowKeyValue;
41
40
  @api colKeyValue;
42
41
  @api rowActions;
42
+ @api internalTabIndex;
43
43
 
44
44
  /**
45
45
  * Defines the current menu alignment
@@ -8,6 +8,7 @@
8
8
  <template if:true={hasTreeData}>
9
9
  <lightning-primitive-treegrid-cell-toggle row-key-value={rowKeyValue}
10
10
  col-key-value={colKeyValue}
11
+ display-value={displayValue}
11
12
  value={value}
12
13
  has-children={typeAttribute21}
13
14
  is-expanded={typeAttribute22}
@@ -33,6 +34,7 @@
33
34
  row-key-value={rowKeyValue}
34
35
  col-key-value={colKeyValue}
35
36
  tabindex={internalTabIndex}
37
+ internal-tab-index={internalTabIndex}
36
38
  menu-alignment={typeAttribute0}
37
39
  row-actions={typeAttribute1}>
38
40
  </lightning-primitive-cell-actions>
@@ -278,7 +280,7 @@
278
280
  </button>
279
281
  </template>
280
282
  <!-- TODO: displayReadOnlyIcon is true only when the cell's "editable" property is false and its "displayReadOnlyIcon"
281
- property is true. The markup in this template therefore cannot be rendered concurrently with the template above that checks
283
+ property is true. The markup in this template therefore cannot be rendered concurrently with the template above that checks
282
284
  the editable property. -->
283
285
  <template if:true={shouldDisplayReadOnlyIcon}>
284
286
  <!-- TODO: Update the svg-classes once the SLDS team updates their read only icon design to not use classes that have the word 'button'
@@ -35,6 +35,7 @@ export default class PrivateCellFactory extends PrimitiveDatatableCell {
35
35
  @api types;
36
36
  @api alignment;
37
37
  @api value;
38
+ @api displayValue;
38
39
  @api iconName;
39
40
  @api iconLabel;
40
41
  @api iconPosition;
@@ -2,7 +2,7 @@ import labelCollapseBranch from '@salesforce/label/LightningPrimitiveCellTree.co
2
2
  import labelExpandBranch from '@salesforce/label/LightningPrimitiveCellTree.expandBranch';
3
3
  import { LightningElement, api } from 'lwc';
4
4
  import { classSet, formatLabel } from 'lightning/utils';
5
- import { normalizeBoolean } from 'lightning/utilsPrivate';
5
+ import { normalizeString, normalizeBoolean } from 'lightning/utilsPrivate';
6
6
 
7
7
  const i18n = {
8
8
  collapseBranch: labelCollapseBranch,
@@ -13,6 +13,7 @@ export default class PrivateTreeGridCellToggle extends LightningElement {
13
13
  @api rowKeyValue;
14
14
  @api colKeyValue;
15
15
  @api value;
16
+ @api displayValue;
16
17
 
17
18
  _expanded = false;
18
19
  _hasChildren = false;
@@ -60,10 +61,14 @@ export default class PrivateTreeGridCellToggle extends LightningElement {
60
61
  }
61
62
 
62
63
  get buttonTitle() {
64
+ let title =
65
+ normalizeString(this.displayValue) !== ''
66
+ ? this.displayValue
67
+ : this.value;
63
68
  if (this.isExpanded) {
64
- return formatLabel(i18n.collapseBranch, this.value);
69
+ return formatLabel(i18n.collapseBranch, title);
65
70
  }
66
- return formatLabel(i18n.expandBranch, this.value);
71
+ return formatLabel(i18n.expandBranch, title);
67
72
  }
68
73
 
69
74
  handleChevronClick() {
@@ -58,7 +58,7 @@
58
58
  </div>
59
59
 
60
60
  <template if:true={_helpMessage}>
61
- <div data-help-message id="help-message" class="slds-form-element__help">{_helpMessage}</div>
61
+ <div data-help-message id="help-message" role="alert" class="slds-form-element__help">{_helpMessage}</div>
62
62
  </template>
63
63
 
64
64
  </fieldset>
@@ -102,6 +102,12 @@ export default class LightningTextarea extends LightningElement {
102
102
  @track _helpMessage;
103
103
  @track _fieldLevelHelp;
104
104
 
105
+ /**
106
+ * Aria Described by value on parent lighting-textarea
107
+ * @type {string}
108
+ */
109
+ @api ariaDescribedBy;
110
+
105
111
  connectedCallback() {
106
112
  this.classList.add('slds-form-element');
107
113
  this.updateClassList();
@@ -433,7 +439,13 @@ export default class LightningTextarea extends LightningElement {
433
439
 
434
440
  get computedUniqueHelpElementId() {
435
441
  const helpMessage = this.template.querySelector('[data-help-message]');
436
- return getRealDOMId(helpMessage);
442
+ let spaceSeperatedDescribedByIds = getRealDOMId(helpMessage);
443
+ if (this.ariaDescribedBy && spaceSeperatedDescribedByIds) {
444
+ spaceSeperatedDescribedByIds += ` ${this.ariaDescribedBy}`;
445
+ } else if (this.ariaDescribedBy) {
446
+ spaceSeperatedDescribedByIds = this.ariaDescribedBy;
447
+ }
448
+ return spaceSeperatedDescribedByIds;
437
449
  }
438
450
 
439
451
  _setInputValue(value) {
@@ -222,6 +222,13 @@ export class Tooltip {
222
222
  this.hideIfNotSelfCover(event)
223
223
  )
224
224
  );
225
+ target.addEventListener('keydown', (event) => {
226
+ if (event.keyCode === 13 && !this._visible) {
227
+ this.toggle();
228
+ } else {
229
+ this.hide();
230
+ }
231
+ });
225
232
  }
226
233
  }
227
234
  handleEscape(e) {
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="slds-tree_container">
3
3
  <template if:true={header}>
4
- <h4 class="slds-tree__group-header" id="treeheading">{header}</h4>
4
+ <h4 class="slds-tree__group-header" id="treeheading" aria-level={_privateHeadingAriaLevel}>{header}</h4>
5
5
  </template>
6
6
 
7
7
  <lightning-tree-item if:true={hasChildren} class="slds-tree" role="tree" aria-labelledby={computedLabelledBy}
@@ -1,6 +1,10 @@
1
1
  import { LightningElement, api, track } from 'lwc';
2
2
  import { TreeData } from './treeData';
3
- import { keyCodes, deepCopy } from 'lightning/utilsPrivate';
3
+ import {
4
+ keyCodes,
5
+ deepCopy,
6
+ isHeadingLevelValid,
7
+ } from 'lightning/utilsPrivate';
4
8
 
5
9
  /**
6
10
  * Displays a nested tree.
@@ -41,6 +45,26 @@ export default class LightningTree extends LightningElement {
41
45
  );
42
46
  }
43
47
 
48
+ _privateHeadingAriaLevel;
49
+
50
+ /**
51
+ * Changes the 'aria-level' attribute value for the
52
+ * <h2> markup tag in the card's title element. Supported values
53
+ * are (1, 2, 3, 4, 5, 6).
54
+ *
55
+ * @type {string | number}
56
+ * @default 2
57
+ */
58
+ @api
59
+ get headingLevel() {
60
+ return this._privateHeadingAriaLevel;
61
+ }
62
+
63
+ set headingLevel(value) {
64
+ if (isHeadingLevelValid(value)) {
65
+ this._privateHeadingAriaLevel = value;
66
+ }
67
+ }
44
68
  /**
45
69
  * An array of key-value pairs that describe the tree. See the Documentation tab for more information.
46
70
  * @type {array}
@@ -507,6 +507,18 @@ Use the following `aria` attributes on `lightning-tree-grid` to provide a captio
507
507
  | aria-label | string | Provides an assistive label to identify a table from other tables on a page. |
508
508
  | aria-labelledby | ID reference list | Specifies the ID or list of IDs of the element or elements that contain visible descriptive text to caption or describe the table. |
509
509
 
510
+ You can add a `displayValue` on the dropdown arrow for a row. This `displayValue` replaces the default row `title` for the dropdown arrow's label.
511
+
512
+ ```javascript
513
+ {
514
+ name: '125313-7j',
515
+ accountName: 'Dach-Welch',
516
+ displayValue: 'Display Value One',
517
+ phone: '995-523-7024',
518
+ }
519
+ ```
520
+
521
+
510
522
  ##### Provide an Accessible Label for the Table
511
523
 
512
524
  Use the `aria-label` attribute to provide a more descriptive label for the table for assistive technology. The `aria-label` attribute and its value are passed down to the rendered `table` element.
@@ -272,31 +272,39 @@ export default class LightningTreeGrid extends LightningElement {
272
272
 
273
273
  handleRowSelection(event) {
274
274
  event.stopPropagation();
275
+ let selectedRowKeys;
275
276
  // pass the event through
276
- switch (event.detail.config.selection) {
277
+ switch (event.detail.config.action) {
277
278
  case ROWS_ACTION.ROW_SELECT:
279
+ selectedRowKeys = this._selectedRowKeys.slice();
278
280
  this._selectedRowKeys.push(event.detail.config.value);
279
281
  break;
280
282
  case ROWS_ACTION.ROW_DESELECT: {
281
283
  const index = this._selectedRowKeys.indexOf(
282
284
  event.detail.config.value
283
285
  );
286
+ selectedRowKeys = this._selectedRowKeys.slice();
284
287
  this._selectedRowKeys.splice(index, 1);
285
-
286
288
  break;
287
289
  }
288
290
  case ROWS_ACTION.SELECT_ALL_ROWS:
291
+ selectedRowKeys = this._selectedRowKeys.slice();
292
+ this._selectedRowKeys = this.getSelectedRowKeys(
293
+ event.detail.selectedRows
294
+ );
295
+ break;
289
296
  case ROWS_ACTION.DESELECT_ALL_ROWS:
297
+ selectedRowKeys = this._selectedRowKeys.slice();
290
298
  this._selectedRowKeys = this.getSelectedRowKeys(
291
299
  event.detail.selectedRows
292
300
  );
293
301
  break;
294
302
  default:
295
303
  this._selectedRowKeys.push(event.detail.config.value);
304
+ selectedRowKeys = this._selectedRowKeys.slice();
296
305
  break;
297
306
  }
298
- delete event.detail.config; // to prevent further propagation of the config
299
-
307
+ event.detail.config.selectedRowKeys = selectedRowKeys;
300
308
  this.fireSelectedRowsChange(event.detail);
301
309
  }
302
310
 
@@ -0,0 +1,11 @@
1
+ /**
2
+ * These are all values that can be set to "aria-level" attribute of h2 tag for the card's title.
3
+ */
4
+ export const VALID_HEADING_LEVELS = ['1', '2', '3', '4', '5', '6'];
5
+
6
+ export function isHeadingLevelValid(level) {
7
+ return (
8
+ (typeof level === 'string' || typeof level === 'number') &&
9
+ VALID_HEADING_LEVELS.includes(level.toString())
10
+ );
11
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Verify if user is using MAC OS or not
3
+ * @returns {boolean} - true if Mac OS
4
+ */
5
+ export const isMacOSTest = ({ userAgent }) => {
6
+ return /(macintosh|macintel|macppc|mac68k|macos)/i.test(userAgent);
7
+ };
8
+
9
+ /**
10
+ * Verify if user is using iOS or not
11
+ * @returns {boolean} - true, if iOS
12
+ */
13
+ export const isiOSTest = ({ userAgent }) => {
14
+ return /(iphone|ipad|ipod)/i.test(userAgent);
15
+ };
16
+
17
+ /**
18
+ * Verify if user is using Windows OS or not
19
+ * @returns {boolean} - true, if Windows OS
20
+ */
21
+ export const isWindowsOSTest = ({ userAgent }) => {
22
+ return /(win32|win64|windows)/i.test(userAgent);
23
+ };
24
+
25
+ /**
26
+ * Verify if user is using Android OS or not
27
+ * @returns {boolean} - true, if Android OS
28
+ */
29
+ export const isAndroidOSTest = ({ userAgent }) => {
30
+ return /android/i.test(userAgent);
31
+ };
32
+
33
+ export const isMacOS = isMacOSTest(navigator);
34
+ export const isWindowsOS = isWindowsOSTest(navigator);
35
+ export const isiOS = isiOSTest(navigator);
36
+ export const isAndroidOS = isAndroidOSTest(navigator);
@@ -42,8 +42,15 @@ export {
42
42
  parseToFormattedParts,
43
43
  } from './linkify';
44
44
  export { isValidPageReference } from './pageReference';
45
+ export { isMacOS, isWindowsOS, isiOS, isAndroidOS } from './os';
46
+
45
47
  import { smartSetAttribute } from './smartSetAttribute';
46
48
 
49
+ export {
50
+ VALID_HEADING_LEVELS,
51
+ isHeadingLevelValid,
52
+ } from './ariaLevelHeadingUtils.js';
53
+
47
54
  /**
48
55
  * @param {HTMLElement} element Element to act on
49
56
  * @param {Object} values values and attributes to set, if the value is
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="slds-nav-vertical__section" onprivateoverflowregister={handleOverflowRegister}>
3
- <h2 id="vertical-navigation-section-heading" class="slds-nav-vertical__title">{label}</h2>
3
+ <h2 id="vertical-navigation-section-heading" class="slds-nav-vertical__title" aria-level={_privateHeadingAriaLevel}>{label}</h2>
4
4
  <div role="list" aria-describedby="vertical-navigation-section-heading">
5
5
  <slot></slot>
6
6
  </div>
@@ -1,5 +1,5 @@
1
1
  import { LightningElement, api, track } from 'lwc';
2
- import { guid } from 'lightning/utilsPrivate';
2
+ import { guid, isHeadingLevelValid } from 'lightning/utilsPrivate';
3
3
 
4
4
  export default class LightningVerticalNavigationSection extends LightningElement {
5
5
  headingId = guid();
@@ -28,4 +28,25 @@ export default class LightningVerticalNavigationSection extends LightningElement
28
28
  const item = event.detail;
29
29
  item.callbacks.updateAssistiveText(this.label);
30
30
  }
31
+
32
+ _privateHeadingAriaLevel;
33
+
34
+ /**
35
+ * Changes the 'aria-level' attribute value for the
36
+ * <h2> markup tag in the card's title element. Supported values
37
+ * are (1, 2, 3, 4, 5, 6).
38
+ *
39
+ * @type {string | number}
40
+ * @default 2
41
+ */
42
+ @api
43
+ get headingLevel() {
44
+ return this._privateHeadingAriaLevel;
45
+ }
46
+
47
+ set headingLevel(value) {
48
+ if (isHeadingLevelValid(value)) {
49
+ this._privateHeadingAriaLevel = value;
50
+ }
51
+ }
31
52
  }
@@ -1 +0,0 @@
1
- export default 'Provide at least the "label" property to show the toast.';
@@ -1 +0,0 @@
1
- export default 'Unable to show toast, missing toast config.';
@@ -1 +0,0 @@
1
- export default 'Unable to show toast, missing toast property\'s "{0}".';