@salesforcedevs/dx-components 1.40.5-topic-alpha.2 → 1.40.5-topic-alpha.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/dx-components",
3
- "version": "1.40.5-topic-alpha.2",
3
+ "version": "1.40.5-topic-alpha.4",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -20,6 +20,11 @@
20
20
  --dx-c-button-line-height: inherit;
21
21
  --dx-c-button-letter-spacing: inherit;
22
22
  --dx-c-button-display: flex;
23
+
24
+ /* Keyboard focus ring (WES): a 2px white band, then a 2px blue outline. */
25
+ --dx-c-button-focus-outline: 2px solid var(--dx-g-blue-vibrant-60);
26
+ --dx-c-button-focus-offset: 2px;
27
+ --dx-c-button-focus-shadow: 0 0 0 2px white;
23
28
  }
24
29
 
25
30
  .button {
@@ -32,7 +37,8 @@
32
37
  user-select: none;
33
38
  white-space: nowrap;
34
39
  font-size: var(--dx-c-button-font-size);
35
- transition: var(--dx-g-transition-hue-1x);
40
+ transition: var(--dx-g-transition-hue-1x),
41
+ var(--dx-g-transition-box-shadow-1x);
36
42
  padding: var(--dx-c-button-vertical-spacing, 0)
37
43
  var(--dx-c-button-horizontal-spacing, var(--dx-g-spacing-md));
38
44
  border-radius: var(--dx-g-spacing-xs);
@@ -60,7 +66,11 @@
60
66
  display: inline;
61
67
  vertical-align: var(--dx-c-button-icon-vertical-align, unset);
62
68
 
63
- --dx-c-icon-color: var(--dx-g-button-icon-color, currentcolor);
69
+ /* Component hook first; the global stays as the fallback for dark mode. */
70
+ --dx-c-icon-color: var(
71
+ --dx-c-button-icon-color,
72
+ var(--dx-g-button-icon-color, currentcolor)
73
+ );
64
74
  }
65
75
 
66
76
  .button.variant_inline dx-icon,
@@ -191,6 +201,7 @@
191
201
  }
192
202
 
193
203
  .button.variant_primary:hover,
204
+ .button.variant_primary:focus-visible,
194
205
  :host([aria-expanded="true"]) .button.variant_primary {
195
206
  background: var(--dx-c-button-primary-color-hover);
196
207
  border-color: var(--dx-c-button-primary-color-hover);
@@ -215,6 +226,8 @@
215
226
 
216
227
  .button.variant_secondary:hover,
217
228
  .button.variant_quaternary:hover,
229
+ .button.variant_secondary:focus-visible,
230
+ .button.variant_quaternary:focus-visible,
218
231
  :host([aria-expanded="true"]) .button.variant_secondary,
219
232
  :host([aria-expanded="true"]) .button.variant_quaternary {
220
233
  background: var(--dx-c-button-secondary-color-hover);
@@ -234,6 +247,7 @@
234
247
  }
235
248
 
236
249
  .button.variant_tertiary:hover,
250
+ .button.variant_tertiary:focus-visible,
237
251
  :host([aria-expanded="true"]) .button.variant_tertiary {
238
252
  background: var(--dx-c-button-secondary-color-hover);
239
253
  border-color: var(--dx-c-button-secondary-color-hover);
@@ -250,6 +264,8 @@
250
264
 
251
265
  .button.variant_inline:hover,
252
266
  .button.variant_inline-inherit:hover,
267
+ .button.variant_inline:focus-visible,
268
+ .button.variant_inline-inherit:focus-visible,
253
269
  :host([aria-expanded="true"])
254
270
  :is(.button.variant_inline, .button.variant_inline-inherit) {
255
271
  color: var(--dx-c-button-inline-color-hover);
@@ -277,10 +293,40 @@
277
293
  }
278
294
 
279
295
  .button.variant_custom:hover,
296
+ .button.variant_custom:focus-visible,
280
297
  .button.variant_icon-only:hover,
298
+ .button.variant_icon-only:focus-visible,
281
299
  :host([aria-expanded="true"]) .button.variant_custom,
282
300
  :host([aria-expanded="true"]) .button.variant_icon-only {
283
301
  color: var(--dx-c-button-custom-color-hover);
284
302
  background: var(--dx-c-button-custom-background-hover);
285
303
  border-color: var(--dx-c-button-custom-border-hover);
304
+
305
+ /* An inset shadow can thicken the edge without resizing the button. */
306
+ box-shadow: var(--dx-c-button-custom-border-shadow-hover, none);
307
+ }
308
+
309
+ /* While its menu is open, a custom button can show the focus ring. Off unless
310
+ the active hooks are set (a select-style trigger, such as the version picker). */
311
+ :host([aria-expanded="true"]) .button.variant_custom {
312
+ outline: var(--dx-c-button-custom-focus-outline-active, none);
313
+ outline-offset: var(--dx-c-button-custom-focus-offset-active, 0);
314
+ box-shadow: var(
315
+ --dx-c-button-custom-focus-shadow-active,
316
+ var(--dx-c-button-custom-border-shadow-hover, none)
317
+ );
318
+ }
319
+
320
+ /* Keyboard focus. Keep last so it wins over hover and the open state (:host
321
+ lifts it above the custom and icon-only hover rules). The
322
+ outline survives Windows high-contrast mode; the white band does not. */
323
+ :host .button:focus-visible,
324
+ :host .button:focus-visible:hover,
325
+ :host([aria-expanded="true"]) .button:focus-visible {
326
+ outline: var(--dx-c-button-focus-outline);
327
+ outline-offset: var(--dx-c-button-focus-offset);
328
+ box-shadow: var(--dx-c-button-focus-shadow);
329
+
330
+ /* The ring appears at once instead of fading in. */
331
+ transition: var(--dx-g-transition-hue-1x);
286
332
  }
@@ -1,6 +1,11 @@
1
1
  @import "dxHelpers/reset";
2
2
 
3
3
  fieldset {
4
+ /* No browser fieldset padding, border, or margin; the container sets the spacing. */
5
+ margin: 0;
6
+ padding: 0;
7
+ border: 0;
8
+ min-width: 0;
4
9
  display: grid;
5
10
  grid-template-rows: auto;
6
11
  grid-gap: 20px;
@@ -3,7 +3,10 @@
3
3
  /* Options run edge to edge; the menu adds space above the first and below
4
4
  the last. */
5
5
  dx-popover {
6
- --dx-c-popover-padding: var(--dx-g-spacing-sm) 0;
6
+ --dx-c-popover-padding: var(
7
+ --dx-c-dropdown-padding,
8
+ var(--dx-g-spacing-sm) 0
9
+ );
7
10
  }
8
11
 
9
12
  .menu-nested:not(.menu-fit-content)::part(content) {
@@ -35,7 +35,6 @@
35
35
  nav-item-label={navItemLabel}
36
36
  variant={variant}
37
37
  width-mode={widthMode}
38
- show-icon={showOptionIcons}
39
38
  ></dx-dropdown-option>
40
39
  <div
41
40
  key={option.id}
@@ -56,7 +55,6 @@
56
55
  option={suboption}
57
56
  nav-item-label={navItemLabel}
58
57
  width-mode={widthMode}
59
- show-icon={showOptionIcons}
60
58
  ></dx-dropdown-option>
61
59
  </template>
62
60
  <dx-button
@@ -39,7 +39,6 @@ export default class Dropdown extends LightningElement {
39
39
  @api navItemLabel: string | null = null;
40
40
  @api variant: DropdownVariant = "base";
41
41
  @api widthMode: DropdownWidthMode = "fixed";
42
- @api showOptionIcons?: boolean = false; // right-aligned option icons (opt-in)
43
42
 
44
43
  // props forwarded to dropdown option
45
44
  @api analyticsEvent?: string;
@@ -51,8 +51,6 @@
51
51
  }
52
52
 
53
53
  .option-active {
54
- --dx-c-dropdown-option-label-color: var(--dx-g-blue-vibrant-50);
55
-
56
54
  background: var(--dx-g-cloud-blue-vibrant-95);
57
55
  }
58
56
 
@@ -76,7 +74,7 @@
76
74
  }
77
75
 
78
76
  .option_label {
79
- color: var(--dx-c-dropdown-option-label-color, var(--dx-g-gray-10));
77
+ color: var(--dx-c-dropdown-option-label-color, var(--dx-g-text-body-color));
80
78
 
81
79
  /* Opt-in: set to block so text-overflow:ellipsis applies (ignored on flex). */
82
80
  display: var(--dx-c-dropdown-option-label-display, flex);
@@ -114,19 +112,26 @@
114
112
  line-height: 18px;
115
113
  }
116
114
 
117
- /* Only rendered when the dropdown sets show-option-icons. */
115
+ /* An option can have a start icon (iconSymbol), an end icon (endIconSymbol),
116
+ or both. */
118
117
  .option_icon {
119
118
  --dx-c-icon-size: var(--dx-g-icon-size-md);
120
119
  --dx-c-icon-color: var(--dx-g-gray-50);
121
120
 
122
- order: 1;
123
121
  flex-shrink: 0;
122
+ }
123
+
124
+ .option_icon-start {
125
+ margin-right: var(--dx-g-spacing-md);
126
+ }
127
+
128
+ .option_icon-end {
124
129
  margin-left: var(--dx-g-spacing-2xl);
125
130
  }
126
131
 
127
- .option-indented {
128
- padding-top: var(--dx-g-spacing-xs);
129
- padding-bottom: var(--dx-g-spacing-xs);
132
+ /* Indented (breadcrumb hierarchy) menus keep their own selected label color. */
133
+ .option-indented.option-active {
134
+ --dx-c-dropdown-option-label-color: var(--dx-g-blue-vibrant-50);
130
135
  }
131
136
 
132
137
  /* fit-content (toolbar): one line per label, truncated with an ellipsis. */
@@ -146,7 +151,11 @@
146
151
  --base-indentation: calc(var(--dx-g-spacing-md) + var(--shape-width));
147
152
  --shape-width: 8px;
148
153
 
149
- padding-left: calc(var(--indentation, 0px) + var(--dx-g-spacing-md));
154
+ /* One sm step past the first option's lg left padding. */
155
+ padding-left: calc(
156
+ var(--indentation, 0px) + var(--dx-g-spacing-lg) +
157
+ var(--dx-g-spacing-sm)
158
+ );
150
159
  }
151
160
 
152
161
  [data-index="2"] {
@@ -11,10 +11,11 @@
11
11
  onclick={onClick}
12
12
  >
13
13
  <dx-icon
14
- class="option_icon"
14
+ class="option_icon option_icon-start"
15
15
  symbol={option.iconSymbol}
16
16
  sprite={option.iconSprite}
17
- lwc:if={renderIcon}
17
+ color={option.iconColor}
18
+ lwc:if={option.iconSymbol}
18
19
  size="override"
19
20
  ></dx-icon>
20
21
  <div class="option_details">
@@ -30,6 +31,14 @@
30
31
  {option.description}
31
32
  </div>
32
33
  </div>
34
+ <dx-icon
35
+ class="option_icon option_icon-end"
36
+ symbol={option.endIconSymbol}
37
+ sprite={option.endIconSprite}
38
+ color={option.endIconColor}
39
+ lwc:if={option.endIconSymbol}
40
+ size="override"
41
+ ></dx-icon>
33
42
  </a>
34
43
  <button
35
44
  if:false={option.link}
@@ -40,11 +49,11 @@
40
49
  onclick={onClick}
41
50
  >
42
51
  <dx-icon
43
- class="option_icon"
52
+ class="option_icon option_icon-start"
44
53
  symbol={option.iconSymbol}
45
54
  sprite={option.iconSprite}
46
55
  color={option.iconColor}
47
- lwc:if={renderIcon}
56
+ lwc:if={option.iconSymbol}
48
57
  size="override"
49
58
  ></dx-icon>
50
59
  <div class="option_details">
@@ -54,5 +63,13 @@
54
63
  {option.description}
55
64
  </div>
56
65
  </div>
66
+ <dx-icon
67
+ class="option_icon option_icon-end"
68
+ symbol={option.endIconSymbol}
69
+ sprite={option.endIconSprite}
70
+ color={option.endIconColor}
71
+ lwc:if={option.endIconSymbol}
72
+ size="override"
73
+ ></dx-icon>
57
74
  </button>
58
75
  </template>
@@ -18,7 +18,6 @@ export default class DropdownOption extends LightningElement {
18
18
  @api indexPosition: number = 0;
19
19
  @api variant: DropdownVariant = "base";
20
20
  @api widthMode: DropdownWidthMode = "fixed";
21
- @api showIcon: boolean = false;
22
21
 
23
22
  @api focus() {
24
23
  const option = this.template.querySelector(".option");
@@ -41,11 +40,6 @@ export default class DropdownOption extends LightningElement {
41
40
  return this.variant === "indented" && this.indexPosition >= 1;
42
41
  }
43
42
 
44
- // Icons are opt-in (right-aligned); without show-icon there is no icon or spacing.
45
- private get renderIcon() {
46
- return this.showIcon && !!this.option.iconSymbol;
47
- }
48
-
49
43
  private onClick(e: PointerEvent) {
50
44
  this.dispatchEvent(
51
45
  new CustomEvent("select", { detail: this.keyValue })
@@ -5,8 +5,6 @@
5
5
  --button-primary-color-hover: var(--dx-g-cloud-blue-vibrant-95);
6
6
  --popover-container-open-transform: translateX(-4px) translateY(-8px);
7
7
  --popover-transition: none;
8
- --dx-c-dropdown-option-font-weight: normal;
9
- --dx-c-dropdown-option-label-color: var(--dx-g-gray-10);
10
8
  }
11
9
 
12
10
  .footer-item {
@@ -12,20 +12,6 @@
12
12
  border-bottom: 1px solid var(--dx-g-gray-80);
13
13
  }
14
14
 
15
- dx-popover {
16
- --popover-padding: none;
17
- }
18
-
19
- .dropdown-item {
20
- padding: var(--dx-g-spacing-sm);
21
- }
22
-
23
- .dropdown-item:hover {
24
- cursor: pointer;
25
- font-weight: var(--dx-g-font-bold);
26
- background-color: var(--dx-g-blue-vibrant-95);
27
- }
28
-
29
15
  .more-btn {
30
16
  display: flex;
31
17
  align-items: center;
@@ -15,11 +15,18 @@
15
15
  </dx-tab-panel-item>
16
16
  </template>
17
17
  </div>
18
- <dx-popover offset="medium" placement="bottom-center">
18
+ <dx-dropdown
19
+ class="more-dropdown"
20
+ offset="medium"
21
+ placement="bottom-end"
22
+ width-mode="fit-content"
23
+ options={dropdownOptions}
24
+ value={activeOptionId}
25
+ onchange={onDropdownChange}
26
+ >
19
27
  <dx-tab-panel-item
20
28
  role="button"
21
29
  class="more-btn-container"
22
- slot="control"
23
30
  tab-label="More"
24
31
  >
25
32
  <div class="more-btn">
@@ -31,23 +38,7 @@
31
38
  ></dx-icon>
32
39
  </div>
33
40
  </dx-tab-panel-item>
34
- <div slot="content">
35
- <template for:each={dropdownItems} for:item="dropdownItem">
36
- <div
37
- role="button"
38
- onclick={onDropdownItemClick}
39
- onkeydown={onDropdownItemClick}
40
- data-value={dropdownItem.index}
41
- key={dropdownItem.label}
42
- class="dropdown-item"
43
- tabindex="0"
44
- tab-label={dropdownItem.label}
45
- >
46
- {dropdownItem.label}
47
- </div>
48
- </template>
49
- </div>
50
- </dx-popover>
41
+ </dx-dropdown>
51
42
  </div>
52
43
  <div part="tabpanels">
53
44
  <slot onslotchange={onSlotChange}></slot>
@@ -1,5 +1,5 @@
1
1
  import { LightningElement, api } from "lwc";
2
- import { LightningSlotElement, Tab } from "typings/custom";
2
+ import { LightningSlotElement, Option, Tab } from "typings/custom";
3
3
  import { toJson } from "dxUtils/normalizers";
4
4
  import { uniqueId } from "./uniqueId";
5
5
  export default class TabPanelList extends LightningElement {
@@ -33,7 +33,6 @@ export default class TabPanelList extends LightningElement {
33
33
  private _activeIndex?: number;
34
34
  private groupId: string = uniqueId();
35
35
  private tabSlotPanels?: LightningSlotElement;
36
- private showPopover: boolean = true;
37
36
  private dropdownItems: Tab[] = [];
38
37
  private hasRendered: boolean = false;
39
38
 
@@ -74,8 +73,25 @@ export default class TabPanelList extends LightningElement {
74
73
  this.activeIndex = e.detail;
75
74
  }
76
75
 
77
- private onDropdownItemClick(e: any) {
78
- this.onTabClick({ detail: +e.currentTarget.dataset.value });
76
+ // Tabs that don't fit are listed in the "More" dropdown. Option ids are the
77
+ // tab index as a string.
78
+ private get dropdownOptions(): Option[] {
79
+ return this.dropdownItems.map((item) => ({
80
+ id: String(item.index),
81
+ label: item.label || ""
82
+ }));
83
+ }
84
+
85
+ private get activeOptionId(): string {
86
+ return String(this.activeIndex);
87
+ }
88
+
89
+ private onDropdownChange(e: CustomEvent<string>) {
90
+ this.onTabClick({ detail: +e.detail });
91
+ // The menu closes after a pick; keep keyboard focus on its trigger.
92
+ (
93
+ this.template.querySelector(".more-btn-container") as HTMLElement
94
+ )?.focus();
79
95
  }
80
96
 
81
97
  private onSlotChange(e: Event) {
@@ -111,6 +127,10 @@ export default class TabPanelList extends LightningElement {
111
127
  }
112
128
 
113
129
  private onKeydown = (e: KeyboardEvent) => {
130
+ // Arrow keys inside the open "More" menu belong to the dropdown.
131
+ if ((e.target as HTMLElement).tagName === "DX-DROPDOWN") {
132
+ return;
133
+ }
114
134
  switch (e.key) {
115
135
  case "ArrowLeft": {
116
136
  e.preventDefault();
@@ -2,7 +2,9 @@
2
2
  @import "dxHelpers/text";
3
3
 
4
4
  :host {
5
- --dx-c-sidebar-width: 336px;
5
+ /* Width of the sidebar content (search field, version pickers) on tablet
6
+ and desktop. The sidebar adds its left padding and a 24px right inset. */
7
+ --dx-c-sidebar-content-width: 296px;
6
8
  --dx-c-sidebar-button-color: var(--dx-g-blue-vibrant-50);
7
9
  --dx-c-sidebar-button-background: white;
8
10
  --dx-c-sidebar-button-border: white;
@@ -12,12 +14,8 @@
12
14
  --dx-c-sidebar-left-padding: var(--dx-g-global-header-padding-horizontal);
13
15
  --dx-c-sidebar-vertical-padding: var(--dx-g-spacing-md);
14
16
 
15
- /* Width of the sidebar content (search field, version pickers): the
16
- sidebar minus its 16px left and 24px right insets. 296px on desktop. */
17
- --doc-version-picker-width: calc(
18
- var(--dx-c-sidebar-width) - var(--dx-g-spacing-md) -
19
- var(--dx-g-spacing-lg)
20
- );
17
+ /* Widest a small version picker menu can grow. */
18
+ --doc-version-picker-width: var(--dx-c-sidebar-content-width);
21
19
  }
22
20
 
23
21
  .header {
@@ -53,7 +51,7 @@
53
51
  color: var(--dx-g-blue-vibrant-30);
54
52
  }
55
53
 
56
- .container.collapsed {
54
+ .container.collapsed:not(.container-mobile) {
57
55
  width: var(--dx-g-spacing-xl);
58
56
  }
59
57
 
@@ -88,15 +86,19 @@
88
86
  display: flex;
89
87
  flex-direction: column;
90
88
  height: var(--dx-c-sidebar-height);
91
- width: calc(var(--dx-c-sidebar-width) + var(--left-spacing));
89
+
90
+ /* Mobile (container-mobile, set at 768px and below) fills the viewport. */
91
+ width: 100%;
92
92
  }
93
93
 
94
+ /* Tablet and desktop: left padding + content + right inset. */
94
95
  .container:not(.container-mobile) {
95
96
  --border-color: transparent;
96
- --left-spacing: calc(
97
- var(--dx-c-sidebar-left-padding) - var(--dx-g-spacing-md)
98
- );
99
97
 
98
+ width: calc(
99
+ var(--dx-c-sidebar-left-padding) + var(--dx-c-sidebar-content-width) +
100
+ var(--dx-g-spacing-lg)
101
+ );
100
102
  box-shadow: 2px 0 0 0 var(--border-color);
101
103
  min-height: var(--dx-c-sidebar-min-height);
102
104
  padding: var(--dx-c-sidebar-vertical-padding) 0 var(--dx-g-spacing-2xs) 0;
@@ -112,7 +114,9 @@
112
114
  }
113
115
 
114
116
  .container.container-mobile {
115
- width: 100%;
117
+ --doc-version-picker-width: calc(
118
+ 100vw - var(--dx-c-sidebar-left-padding) - var(--dx-g-spacing-lg)
119
+ );
116
120
  }
117
121
 
118
122
  .container.container-mobile .header {
@@ -175,11 +179,3 @@
175
179
  .sidebar-content-hasfooter {
176
180
  margin-bottom: var(--dx-g-spacing-2xl);
177
181
  }
178
-
179
- @media screen and (max-width: 767px) {
180
- :host {
181
- --doc-version-picker-width: calc(
182
- 100vw - var(--dx-c-sidebar-left-padding) - var(--dx-g-spacing-lg)
183
- );
184
- }
185
- }