@brightspace-ui/core 2.93.3 → 2.95.0
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/README.md +0 -1
- package/components/count-badge/count-badge-icon.js +8 -5
- package/components/count-badge/count-badge.js +9 -6
- package/components/dialog/dialog-mixin.js +2 -2
- package/components/html-block/html-block.js +1 -0
- package/components/inputs/docs/input-search.md +1 -1
- package/components/link/link.js +1 -0
- package/components/menu/menu-item-styles.js +3 -0
- package/components/paging/pager-load-more.js +1 -2
- package/components/scroll-wrapper/scroll-wrapper.js +5 -4
- package/components/switch/switch-mixin.js +8 -7
- package/components/table/table-col-sort-button.js +4 -2
- package/components/table/table-controls.js +4 -0
- package/components/tabs/tab-internal.js +4 -2
- package/components/tabs/tabs.js +9 -6
- package/components/tooltip/tooltip-help.js +5 -4
- package/directives/animate/animate.js +2 -2
- package/helpers/README.md +7 -0
- package/helpers/focus.js +6 -0
- package/lang/ar.js +1 -0
- package/lang/cy.js +1 -0
- package/lang/da.js +1 -0
- package/lang/de.js +1 -0
- package/lang/en-gb.js +1 -0
- package/lang/en.js +1 -0
- package/lang/es-es.js +1 -0
- package/lang/es.js +1 -0
- package/lang/fr-fr.js +1 -0
- package/lang/fr.js +1 -0
- package/lang/hi.js +1 -0
- package/lang/ja.js +1 -0
- package/lang/ko.js +1 -0
- package/lang/nl.js +1 -0
- package/lang/pt.js +1 -0
- package/lang/sv.js +1 -0
- package/lang/tr.js +1 -0
- package/lang/zh-cn.js +1 -0
- package/lang/zh-tw.js +1 -0
- package/package.json +1 -2
- package/templates/primary-secondary/primary-secondary.js +18 -9
- package/tools/web-test-runner-helpers.js +12 -0
- package/mixins/focus-visible-polyfill-mixin.js +0 -29
- package/mixins/focus-visible-polyfill-mixin.md +0 -50
package/README.md
CHANGED
|
@@ -68,7 +68,6 @@ npm install @brightspace-ui/core
|
|
|
68
68
|
* [ArrowKeysMixin](mixins/arrow-keys-mixin.md): manage focus with arrow keys
|
|
69
69
|
* [AsyncContainerMixin](mixins/async-container/): manage collective async state
|
|
70
70
|
* [FocusMixin](mixins/focus-mixin.md): delegate focus to a nested element when `focus()` is called
|
|
71
|
-
* [FocusVisiblePolyfillMixin](mixins/focus-visible-polyfill-mixin.md): components can use the `:focus-visible` pseudo-class polyfill
|
|
72
71
|
* [FormElementMixin](components/form/docs/form-element-mixin.md): allow components to participate in forms and validation
|
|
73
72
|
* [InteractiveMixin](mixins/interactive-mixin.md): enables toggling interactive elements inside of nested grids
|
|
74
73
|
* [LabelledMixin](mixins/labelled-mixin.md): label custom elements by referencing elements across DOM scopes
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import '../colors/colors.js';
|
|
2
2
|
import '../icons/icon.js';
|
|
3
|
-
import { css, html, LitElement } from 'lit';
|
|
3
|
+
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
4
4
|
import { CountBadgeMixin } from './count-badge-mixin.js';
|
|
5
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
5
6
|
import { getUniqueId } from '../../helpers/uniqueId.js';
|
|
6
7
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
7
8
|
|
|
@@ -22,9 +23,11 @@ class CountBadgeIcon extends CountBadgeMixin(LitElement) {
|
|
|
22
23
|
|
|
23
24
|
static get styles() {
|
|
24
25
|
return [super.styles, css`
|
|
25
|
-
:host(.focus-visible) d2l-icon,
|
|
26
26
|
:host([focus-ring]) d2l-icon,
|
|
27
|
-
|
|
27
|
+
:host(.focus-visible) d2l-icon,
|
|
28
|
+
d2l-icon.focus-visible,
|
|
29
|
+
:host(:${unsafeCSS(getFocusPseudoClass())}) d2l-icon,
|
|
30
|
+
d2l-icon:${unsafeCSS(getFocusPseudoClass())} {
|
|
28
31
|
box-shadow: 0 0 0 2px var(--d2l-color-celestine);
|
|
29
32
|
outline: none;
|
|
30
33
|
}
|
|
@@ -105,8 +108,8 @@ class CountBadgeIcon extends CountBadgeMixin(LitElement) {
|
|
|
105
108
|
${this.renderCount(numberStyles)}
|
|
106
109
|
<div class="d2l-skeletize d2l-count-badge-wrapper">
|
|
107
110
|
<d2l-icon id="${this._badgeId}"
|
|
108
|
-
icon="${this.icon}"
|
|
109
|
-
tabindex="${ifDefined(tabbable ? '0' : undefined)}"
|
|
111
|
+
icon="${this.icon}"
|
|
112
|
+
tabindex="${ifDefined(tabbable ? '0' : undefined)}"
|
|
110
113
|
aria-labelledby="${ifDefined(this.getAriaLabelId())}"
|
|
111
114
|
role="img">
|
|
112
115
|
</d2l-icon>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '../colors/colors.js';
|
|
2
|
-
import { css, html, LitElement } from 'lit';
|
|
2
|
+
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
3
3
|
import { CountBadgeMixin } from './count-badge-mixin.js';
|
|
4
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
4
5
|
import { getUniqueId } from '../../helpers/uniqueId.js';
|
|
5
6
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
6
7
|
|
|
@@ -8,9 +9,11 @@ class CountBadge extends CountBadgeMixin(LitElement) {
|
|
|
8
9
|
|
|
9
10
|
static get styles() {
|
|
10
11
|
return [super.styles, css`
|
|
11
|
-
:host(.focus-visible) .d2l-count-badge-wrapper,
|
|
12
12
|
:host([focus-ring]) .d2l-count-badge-wrapper,
|
|
13
|
-
.d2l-count-badge-wrapper
|
|
13
|
+
:host(.focus-visible) .d2l-count-badge-wrapper,
|
|
14
|
+
.d2l-count-badge-wrapper.focus-visible,
|
|
15
|
+
:host(:${unsafeCSS(getFocusPseudoClass())}) .d2l-count-badge-wrapper,
|
|
16
|
+
.d2l-count-badge-wrapper:${unsafeCSS(getFocusPseudoClass())} {
|
|
14
17
|
box-shadow: 0 0 0 2px var(--d2l-color-celestine);
|
|
15
18
|
}
|
|
16
19
|
|
|
@@ -22,7 +25,7 @@ class CountBadge extends CountBadgeMixin(LitElement) {
|
|
|
22
25
|
border-radius: 0.65rem;
|
|
23
26
|
outline: none;
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
:host([size="large"]) .d2l-count-badge-wrapper {
|
|
27
30
|
border-radius: 0.8rem;
|
|
28
31
|
outline: none;
|
|
@@ -38,10 +41,10 @@ class CountBadge extends CountBadgeMixin(LitElement) {
|
|
|
38
41
|
render() {
|
|
39
42
|
const tabbable = (this.tabStop || this.hasTooltip) && !(this.hideZero && this.number === 0) && !this.skeleton;
|
|
40
43
|
const innerHtml = html`
|
|
41
|
-
<div
|
|
44
|
+
<div
|
|
42
45
|
class="d2l-count-badge-wrapper d2l-skeletize"
|
|
43
46
|
id="${this._badgeId}"
|
|
44
|
-
tabindex="${ifDefined(tabbable ? '0' : undefined)}"
|
|
47
|
+
tabindex="${ifDefined(tabbable ? '0' : undefined)}"
|
|
45
48
|
aria-labelledby="${ifDefined(this.getAriaLabelId())}"
|
|
46
49
|
role="img">
|
|
47
50
|
${this.renderCount()}
|
|
@@ -3,7 +3,7 @@ import '../../helpers/viewport-size.js';
|
|
|
3
3
|
import { allowBodyScroll, preventBodyScroll } from '../backdrop/backdrop.js';
|
|
4
4
|
import { clearDismissible, setDismissible } from '../../helpers/dismissible.js';
|
|
5
5
|
import { findComposedAncestor, isComposedAncestor } from '../../helpers/dom.js';
|
|
6
|
-
import { forceFocusVisible, getComposedActiveElement, getNextFocusable, tryApplyFocus } from '../../helpers/focus.js';
|
|
6
|
+
import { forceFocusVisible, getComposedActiveElement, getNextFocusable, isFocusVisibleApplied, tryApplyFocus } from '../../helpers/focus.js';
|
|
7
7
|
import { classMap } from 'lit/directives/class-map.js';
|
|
8
8
|
import { getUniqueId } from '../../helpers/uniqueId.js';
|
|
9
9
|
import { html } from 'lit';
|
|
@@ -411,7 +411,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
|
411
411
|
const activeElement = getComposedActiveElement();
|
|
412
412
|
if (!activeElement
|
|
413
413
|
|| !isComposedAncestor(dialog, activeElement)
|
|
414
|
-
|| !activeElement
|
|
414
|
+
|| !isFocusVisibleApplied(activeElement)) {
|
|
415
415
|
// wait till the dialog is visible for Safari
|
|
416
416
|
requestAnimationFrame(() => this._focusInitial());
|
|
417
417
|
}
|
|
@@ -101,6 +101,7 @@ export const htmlBlockContentStyles = css`
|
|
|
101
101
|
color: var(--d2l-color-celestine-minus-1, #004489);
|
|
102
102
|
text-decoration: underline;
|
|
103
103
|
}
|
|
104
|
+
a.focus-visible,
|
|
104
105
|
a:${unsafeCSS(getFocusPseudoClass())} {
|
|
105
106
|
border-radius: 2px;
|
|
106
107
|
outline: 2px solid var(--d2l-color-celestine, #006fbf);
|
|
@@ -85,4 +85,4 @@ To make your usage of `d2l-input-search` accessible, use the following property
|
|
|
85
85
|
| Attribute | Description |
|
|
86
86
|
|---|---|
|
|
87
87
|
| `description` | Use when label on input does not provide enough context. |
|
|
88
|
-
| label | **REQUIRED** [Acts as a primary label on the input](https://www.w3.org/WAI/tutorials/forms/labels/). Not visible. |
|
|
88
|
+
| `label` | **REQUIRED** [Acts as a primary label on the input](https://www.w3.org/WAI/tutorials/forms/labels/). Not visible. |
|
package/components/link/link.js
CHANGED
|
@@ -20,6 +20,7 @@ export const linkStyles = css`
|
|
|
20
20
|
color: var(--d2l-color-celestine-minus-1);
|
|
21
21
|
text-decoration: underline;
|
|
22
22
|
}
|
|
23
|
+
.d2l-link.focus-visible,
|
|
23
24
|
.d2l-link:${unsafeCSS(getFocusPseudoClass())} {
|
|
24
25
|
border-radius: 2px;
|
|
25
26
|
outline: 2px solid var(--d2l-color-celestine);
|
|
@@ -21,6 +21,8 @@ export const menuItemStyles = css`
|
|
|
21
21
|
color: var(--d2l-menu-foreground-color-hover);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
:host(.focus-visible),
|
|
25
|
+
:host([first].focus-visible),
|
|
24
26
|
:host(:${unsafeCSS(getFocusPseudoClass())}),
|
|
25
27
|
:host([first]:${unsafeCSS(getFocusPseudoClass())}) {
|
|
26
28
|
border-radius: 6px;
|
|
@@ -36,6 +38,7 @@ export const menuItemStyles = css`
|
|
|
36
38
|
opacity: 0.75;
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
:host([disabled].focus-visible),
|
|
39
42
|
:host([disabled]:${unsafeCSS(getFocusPseudoClass())}) {
|
|
40
43
|
cursor: default;
|
|
41
44
|
opacity: 0.75;
|
|
@@ -4,7 +4,6 @@ import { css, html, LitElement, nothing } from 'lit';
|
|
|
4
4
|
import { buttonStyles } from '../button/button-styles.js';
|
|
5
5
|
import { findComposedAncestor } from '../../helpers/dom.js';
|
|
6
6
|
import { FocusMixin } from '../../mixins/focus-mixin.js';
|
|
7
|
-
import { FocusVisiblePolyfillMixin } from '../../mixins/focus-visible-polyfill-mixin.js';
|
|
8
7
|
import { formatNumber } from '@brightspace-ui/intl/lib/number.js';
|
|
9
8
|
import { getFirstFocusableDescendant } from '../../helpers/focus.js';
|
|
10
9
|
import { getSeparator } from '@brightspace-ui/intl/lib/list.js';
|
|
@@ -18,7 +17,7 @@ const nativeFocus = document.createElement('div').focus;
|
|
|
18
17
|
* A pager component for load-more paging.
|
|
19
18
|
* @fires d2l-pager-load-more - Dispatched when the user clicks the load-more button. Consumers must call the provided "complete" method once items have been loaded.
|
|
20
19
|
*/
|
|
21
|
-
class LoadMore extends FocusMixin(
|
|
20
|
+
class LoadMore extends FocusMixin(LocalizeCoreElement(LitElement)) {
|
|
22
21
|
|
|
23
22
|
static get properties() {
|
|
24
23
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../colors/colors.js';
|
|
2
2
|
import '../icons/icon.js';
|
|
3
|
-
import { css, html, LitElement } from 'lit';
|
|
4
|
-
import {
|
|
3
|
+
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
4
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
5
5
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
6
6
|
import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
|
|
7
7
|
import { RtlMixin } from '../../mixins/rtl-mixin.js';
|
|
@@ -14,7 +14,7 @@ const SCROLL_AMOUNT = 0.8;
|
|
|
14
14
|
* Wraps content which may overflow its horizontal boundaries, providing left/right scroll buttons.
|
|
15
15
|
* @slot - User provided content to wrap
|
|
16
16
|
*/
|
|
17
|
-
class ScrollWrapper extends
|
|
17
|
+
class ScrollWrapper extends RtlMixin(LitElement) {
|
|
18
18
|
|
|
19
19
|
static get properties() {
|
|
20
20
|
return {
|
|
@@ -59,7 +59,8 @@ class ScrollWrapper extends FocusVisiblePolyfillMixin(RtlMixin(LitElement)) {
|
|
|
59
59
|
overflow-x: auto;
|
|
60
60
|
overflow-y: var(--d2l-scroll-wrapper-overflow-y, visible);
|
|
61
61
|
}
|
|
62
|
-
.d2l-scroll-wrapper-container.focus-visible
|
|
62
|
+
.d2l-scroll-wrapper-container.focus-visible,
|
|
63
|
+
.d2l-scroll-wrapper-container:${unsafeCSS(getFocusPseudoClass())} {
|
|
63
64
|
box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px var(--d2l-color-celestine), 0 2px 12px 0 rgba(0, 0, 0, 0.15);
|
|
64
65
|
}
|
|
65
66
|
:host([h-scrollbar]) .d2l-scroll-wrapper-container {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import '../colors/colors.js';
|
|
2
|
-
import { css, html } from 'lit';
|
|
2
|
+
import { css, html, unsafeCSS } from 'lit';
|
|
3
3
|
import { classMap } from 'lit/directives/class-map.js';
|
|
4
4
|
import { FocusMixin } from '../../mixins/focus-mixin.js';
|
|
5
|
-
import {
|
|
5
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
6
6
|
import { getUniqueId } from '../../helpers/uniqueId.js';
|
|
7
7
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
8
8
|
import { RtlMixin } from '../../mixins/rtl-mixin.js';
|
|
9
9
|
|
|
10
|
-
export const SwitchMixin = superclass => class extends FocusMixin(RtlMixin(
|
|
10
|
+
export const SwitchMixin = superclass => class extends FocusMixin(RtlMixin(superclass)) {
|
|
11
11
|
|
|
12
12
|
static get properties() {
|
|
13
13
|
return {
|
|
@@ -59,7 +59,8 @@ export const SwitchMixin = superclass => class extends FocusMixin(RtlMixin(Focus
|
|
|
59
59
|
padding: 0.1rem;
|
|
60
60
|
vertical-align: middle;
|
|
61
61
|
}
|
|
62
|
-
.d2l-switch-container.focus-visible
|
|
62
|
+
.d2l-switch-container.focus-visible,
|
|
63
|
+
.d2l-switch-container:${unsafeCSS(getFocusPseudoClass())} {
|
|
63
64
|
border-color: var(--d2l-color-celestine);
|
|
64
65
|
}
|
|
65
66
|
:host([disabled]) .d2l-switch-container {
|
|
@@ -221,9 +222,9 @@ export const SwitchMixin = superclass => class extends FocusMixin(RtlMixin(Focus
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
get _labelContent() {
|
|
224
|
-
return html`<span
|
|
225
|
-
@click='${this._handleClick}'
|
|
226
|
-
@mouseenter='${this._handleSwitchHover}'
|
|
225
|
+
return html`<span
|
|
226
|
+
@click='${this._handleClick}'
|
|
227
|
+
@mouseenter='${this._handleSwitchHover}'
|
|
227
228
|
@mouseleave='${this._handleSwitchHoverLeave}'>
|
|
228
229
|
${this.text}
|
|
229
230
|
</span>`;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import '../colors/colors.js';
|
|
2
2
|
import '../icons/icon.js';
|
|
3
|
-
import { css, html, LitElement } from 'lit';
|
|
3
|
+
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
4
4
|
import { FocusMixin } from '../../mixins/focus-mixin.js';
|
|
5
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Button for sorting a table column in ascending/descending order.
|
|
@@ -55,7 +56,8 @@ export class TableColSortButton extends FocusMixin(LitElement) {
|
|
|
55
56
|
button:hover {
|
|
56
57
|
text-decoration: underline;
|
|
57
58
|
}
|
|
58
|
-
button:focus-visible
|
|
59
|
+
button:focus-visible,
|
|
60
|
+
button:${unsafeCSS(getFocusPseudoClass())} {
|
|
59
61
|
border-radius: 0.2rem;
|
|
60
62
|
box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px var(--d2l-color-celestine);
|
|
61
63
|
outline-style: none;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../colors/colors.js';
|
|
2
|
-
import { css, html, LitElement } from 'lit';
|
|
2
|
+
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
3
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
3
4
|
import { RtlMixin } from '../../mixins/rtl-mixin.js';
|
|
4
5
|
|
|
5
6
|
const keyCodes = {
|
|
@@ -62,7 +63,8 @@ class Tab extends RtlMixin(LitElement) {
|
|
|
62
63
|
margin-left: 0.6rem;
|
|
63
64
|
margin-right: 0;
|
|
64
65
|
}
|
|
65
|
-
:host(.focus-visible) > .d2l-tab-text
|
|
66
|
+
:host(.focus-visible) > .d2l-tab-text,
|
|
67
|
+
:host(:${unsafeCSS(getFocusPseudoClass())}) > .d2l-tab-text {
|
|
66
68
|
border-radius: 0.3rem;
|
|
67
69
|
box-shadow: 0 0 0 2px var(--d2l-color-celestine);
|
|
68
70
|
color: var(--d2l-color-celestine);
|
package/components/tabs/tabs.js
CHANGED
|
@@ -2,12 +2,12 @@ import '../colors/colors.js';
|
|
|
2
2
|
import '../icons/icon.js';
|
|
3
3
|
import '../../helpers/queueMicrotask.js';
|
|
4
4
|
import './tab-internal.js';
|
|
5
|
-
import { css, html, LitElement } from 'lit';
|
|
5
|
+
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
6
6
|
import { cssEscape, findComposedAncestor } from '../../helpers/dom.js';
|
|
7
7
|
import { ArrowKeysMixin } from '../../mixins/arrow-keys-mixin.js';
|
|
8
8
|
import { bodyCompactStyles } from '../typography/styles.js';
|
|
9
9
|
import { classMap } from 'lit/directives/class-map.js';
|
|
10
|
-
import {
|
|
10
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
11
11
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
12
12
|
import { repeat } from 'lit/directives/repeat.js';
|
|
13
13
|
import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
|
|
@@ -52,7 +52,7 @@ if (!Array.prototype.findIndex) {
|
|
|
52
52
|
* @slot ext - Additional content (e.g., a button) positioned at right
|
|
53
53
|
* @fires d2l-tabs-initialized - Dispatched when the component is initialized
|
|
54
54
|
*/
|
|
55
|
-
class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(
|
|
55
|
+
class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(LitElement))) {
|
|
56
56
|
|
|
57
57
|
static get properties() {
|
|
58
58
|
return {
|
|
@@ -182,14 +182,17 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
|
|
|
182
182
|
border: 0;
|
|
183
183
|
}
|
|
184
184
|
.d2l-tabs-scroll-button[disabled]:hover,
|
|
185
|
-
.d2l-tabs-scroll-button[disabled].focus-visible
|
|
185
|
+
.d2l-tabs-scroll-button[disabled].focus-visible,
|
|
186
|
+
.d2l-tabs-scroll-button[disabled]:${unsafeCSS(getFocusPseudoClass())} {
|
|
186
187
|
background-color: transparent;
|
|
187
188
|
}
|
|
188
189
|
.d2l-tabs-scroll-button:hover,
|
|
189
|
-
.d2l-tabs-scroll-button.focus-visible
|
|
190
|
+
.d2l-tabs-scroll-button.focus-visible,
|
|
191
|
+
.d2l-tabs-scroll-button:${unsafeCSS(getFocusPseudoClass())} {
|
|
190
192
|
background-color: var(--d2l-color-gypsum);
|
|
191
193
|
}
|
|
192
|
-
.d2l-tabs-scroll-button.focus-visible
|
|
194
|
+
.d2l-tabs-scroll-button.focus-visible,
|
|
195
|
+
.d2l-tabs-scroll-button:${unsafeCSS(getFocusPseudoClass())} {
|
|
193
196
|
box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px var(--d2l-color-celestine);
|
|
194
197
|
}
|
|
195
198
|
.d2l-panels-container-no-whitespace ::slotted(*) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import '../colors/colors.js';
|
|
2
2
|
import '../tooltip/tooltip.js';
|
|
3
|
-
import { css, html, LitElement, nothing } from 'lit';
|
|
3
|
+
import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
|
|
4
4
|
import { bodySmallStyles } from '../typography/styles.js';
|
|
5
5
|
import { classMap } from 'lit/directives/class-map.js';
|
|
6
6
|
import { FocusMixin } from '../../mixins/focus-mixin.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
8
8
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
9
9
|
import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
|
|
|
12
12
|
* A component used to display additional information when users focus or hover over some text.
|
|
13
13
|
* @slot - Default content placed inside of the tooltip
|
|
14
14
|
*/
|
|
15
|
-
class TooltipHelp extends SkeletonMixin(FocusMixin(
|
|
15
|
+
class TooltipHelp extends SkeletonMixin(FocusMixin(LitElement)) {
|
|
16
16
|
|
|
17
17
|
static get properties() {
|
|
18
18
|
return {
|
|
@@ -60,7 +60,8 @@ class TooltipHelp extends SkeletonMixin(FocusMixin(FocusVisiblePolyfillMixin(Lit
|
|
|
60
60
|
#d2l-tooltip-help-text:focus {
|
|
61
61
|
outline-style: none;
|
|
62
62
|
}
|
|
63
|
-
#d2l-tooltip-help-text.focus-visible
|
|
63
|
+
#d2l-tooltip-help-text.focus-visible,
|
|
64
|
+
#d2l-tooltip-help-text:${unsafeCSS(getFocusPseudoClass())} {
|
|
64
65
|
border-radius: 0.05rem;
|
|
65
66
|
outline: 2px solid var(--d2l-color-celestine);
|
|
66
67
|
outline-offset: 0.05rem;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { directive, PartType } from 'lit/directive.js';
|
|
2
|
-
import { getComposedActiveElement, getNextFocusable } from '../../helpers/focus.js';
|
|
2
|
+
import { getComposedActiveElement, getNextFocusable, isFocusVisibleApplied } from '../../helpers/focus.js';
|
|
3
3
|
import { AsyncDirective } from 'lit/async-directive.js';
|
|
4
4
|
import { isComposedAncestor } from '../../helpers/dom.js';
|
|
5
5
|
import { noChange } from 'lit';
|
|
@@ -180,7 +180,7 @@ class AnimationState {
|
|
|
180
180
|
|
|
181
181
|
// if focus is inside and was placed by keyboard, move it to next focusable
|
|
182
182
|
const activeElem = getComposedActiveElement();
|
|
183
|
-
if (activeElem && activeElem
|
|
183
|
+
if (activeElem && isFocusVisibleApplied(activeElem)) {
|
|
184
184
|
const focusIsInside = isComposedAncestor(this.elem, activeElem);
|
|
185
185
|
if (focusIsInside) {
|
|
186
186
|
const nextFocusable = getNextFocusable(activeElem);
|
package/helpers/README.md
CHANGED
|
@@ -130,6 +130,10 @@ getComposedActiveElement()
|
|
|
130
130
|
getFirstFocusableDescendant(node, includeHidden, predicate, includeTabbablesOnly)
|
|
131
131
|
|
|
132
132
|
// gets the focus pseudo-class to used in selectors (focus-visible if supported, or focus)
|
|
133
|
+
// Always also include `.focus-visible` as a selector, to support `forceFocusVisible`. Usage:
|
|
134
|
+
// css`
|
|
135
|
+
// some-element.focus-visible, some-element:${unsafeCSS(getFocusPseudoClass())} { ... }
|
|
136
|
+
// `
|
|
133
137
|
getFocusPseudoClass()
|
|
134
138
|
|
|
135
139
|
// gets the last focusable descendant given a node, including those within the shadow DOM
|
|
@@ -147,6 +151,9 @@ getPreviousFocusableAncestor(node, includeHidden, includeTabbablesOnly)
|
|
|
147
151
|
// returns true/false whether the element is focusable
|
|
148
152
|
isFocusable(node, includeHidden, includeTabbablesOnly, includeDisabled)
|
|
149
153
|
|
|
154
|
+
// returns true/false whether the element has :focus-visible or .focus-visible applied
|
|
155
|
+
isFocusVisibleApplied(node)
|
|
156
|
+
|
|
150
157
|
// returns true/false whether the :focus-visible is supported
|
|
151
158
|
isFocusVisibleSupported()
|
|
152
159
|
|
package/helpers/focus.js
CHANGED
|
@@ -186,6 +186,12 @@ export function isFocusable(node, includeHidden, includeTabbablesOnly, includeDi
|
|
|
186
186
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
export function isFocusVisibleApplied(node) {
|
|
190
|
+
if (!node) return false;
|
|
191
|
+
if (node.classList.contains('focus-visible')) return true;
|
|
192
|
+
return isFocusVisibleSupported() && node.parentNode?.querySelector(':focus-visible') === node;
|
|
193
|
+
}
|
|
194
|
+
|
|
189
195
|
let _isFocusVisibleSupported;
|
|
190
196
|
|
|
191
197
|
export function isFocusVisibleSupported() {
|
package/lang/ar.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "مرئي.",
|
|
107
107
|
"components.switch.hidden": "مخفي",
|
|
108
108
|
"components.switch.conditions": "يجب استيفاء الشروط",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "التمرير إلى الأمام",
|
|
110
111
|
"components.tabs.previous": "التمرير إلى الخلف",
|
|
111
112
|
"components.tag-list.clear": "انقر فوق، أو اضغط على مسافة للخلف، أو اضغط على مفتاح حذف لإزالة العنصر {value}",
|
package/lang/cy.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Gweladwy.",
|
|
107
107
|
"components.switch.hidden": "Cudd",
|
|
108
108
|
"components.switch.conditions": "Rhaid bodloni’r amodau",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Sgrolio Ymlaen",
|
|
110
111
|
"components.tabs.previous": "Sgrolio Yn Ôl",
|
|
111
112
|
"components.tag-list.clear": "Cliciwch, pwyswch yn ôl, neu pwyswch y bysell dileu i dynnu’r eitem {value}",
|
package/lang/da.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Synlig.",
|
|
107
107
|
"components.switch.hidden": "Skjult",
|
|
108
108
|
"components.switch.conditions": "Betingelserne skal være opfyldt",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Rul frem",
|
|
110
111
|
"components.tabs.previous": "Rul tilbage",
|
|
111
112
|
"components.tag-list.clear": "Klik, tryk på tilbagetasten, eller tryk på slettasten for at fjerne element {value}",
|
package/lang/de.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Sichtbar.",
|
|
107
107
|
"components.switch.hidden": "Ausgeblendet",
|
|
108
108
|
"components.switch.conditions": "Bedingungen müssen erfüllt sein",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Weiterblättern",
|
|
110
111
|
"components.tabs.previous": "Zurückblättern",
|
|
111
112
|
"components.tag-list.clear": "Klicken Sie, drücken Sie die Rücktaste, oder drücken Sie die Entfernen-Taste, um das Element {value} zu entfernen",
|
package/lang/en-gb.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Visible.",
|
|
107
107
|
"components.switch.hidden": "Hidden",
|
|
108
108
|
"components.switch.conditions": "Conditions must be met",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Scroll Forward",
|
|
110
111
|
"components.tabs.previous": "Scroll Backward",
|
|
111
112
|
"components.tag-list.clear": "Click, press backspace, or press delete key to remove item {value}",
|
package/lang/en.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Visible.",
|
|
107
107
|
"components.switch.hidden": "Hidden",
|
|
108
108
|
"components.switch.conditions": "Conditions must be met",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Scroll Forward",
|
|
110
111
|
"components.tabs.previous": "Scroll Backward",
|
|
111
112
|
"components.tag-list.clear": "Click, press backspace, or press delete key to remove item {value}",
|
package/lang/es-es.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Visible.", // mfv-translated
|
|
107
107
|
"components.switch.hidden": "Oculto",
|
|
108
108
|
"components.switch.conditions": "Deben cumplirse las condiciones",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Desplazarse hacia delante",
|
|
110
111
|
"components.tabs.previous": "Desplazarse hacia atrás",
|
|
111
112
|
"components.tag-list.clear": "Haga clic, pulse Retroceso o pulse la tecla Supr para eliminar el elemento {value}",
|
package/lang/es.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Visible.", // mfv-translated
|
|
107
107
|
"components.switch.hidden": "Oculto",
|
|
108
108
|
"components.switch.conditions": "Se deben cumplir las condiciones",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Desplazarse hacia adelante",
|
|
110
111
|
"components.tabs.previous": "Desplazarse hacia atrás",
|
|
111
112
|
"components.tag-list.clear": "Haga clic, presione Retroceso o presione la tecla Suprimir para eliminar el elemento {value}",
|
package/lang/fr-fr.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Visible.", // mfv-translated
|
|
107
107
|
"components.switch.hidden": "Masqué",
|
|
108
108
|
"components.switch.conditions": "Les conditions doivent être remplies",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Faire défiler vers l'avant",
|
|
110
111
|
"components.tabs.previous": "Faire défiler vers l'arrière",
|
|
111
112
|
"components.tag-list.clear": "Cliquez sur l’élément, appuyez sur la touche Retour arrière ou sur la touche Suppr pour supprimer l’élément {value}",
|
package/lang/fr.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Visible.", // mfv-translated
|
|
107
107
|
"components.switch.hidden": "Masqué(e)",
|
|
108
108
|
"components.switch.conditions": "Les conditions doivent être remplies",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Défilement avant",
|
|
110
111
|
"components.tabs.previous": "Défilement arrière",
|
|
111
112
|
"components.tag-list.clear": "Cliquez sur le bouton, appuyez sur retour arrière ou appuyez sur la touche de suppression pour supprimer l’élément {value}",
|
package/lang/hi.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "दृश्यमान।",
|
|
107
107
|
"components.switch.hidden": "छुपा हुआ",
|
|
108
108
|
"components.switch.conditions": "शर्तें पूरी होनी चाहिए",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "आगे स्क्रॉल करें",
|
|
110
111
|
"components.tabs.previous": "पीछे स्क्रॉल करें",
|
|
111
112
|
"components.tag-list.clear": "{value} को हटाने के लिए क्लिक करें, बैकस्पेस दबाएँ, या हटाएँ कुंजी को दबाएँ",
|
package/lang/ja.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "表示。",
|
|
107
107
|
"components.switch.hidden": "非表示",
|
|
108
108
|
"components.switch.conditions": "条件が一致する必要があります",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "前方にスクロール",
|
|
110
111
|
"components.tabs.previous": "後方にスクロール",
|
|
111
112
|
"components.tag-list.clear": "クリックする、Backspace キーを押す、または Delete キーを押すと項目 {value} が削除されます",
|
package/lang/ko.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "표시.",
|
|
107
107
|
"components.switch.hidden": "숨김",
|
|
108
108
|
"components.switch.conditions": "조건을 충족해야 합니다",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "앞으로 스크롤",
|
|
110
111
|
"components.tabs.previous": "뒤로 스크롤",
|
|
111
112
|
"components.tag-list.clear": "항목 {value}을(를) 제거하려면 클릭하거나, 백스페이스 또는 삭제 키를 누릅니다.",
|
package/lang/nl.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Zichtbaar.",
|
|
107
107
|
"components.switch.hidden": "Verborgen",
|
|
108
108
|
"components.switch.conditions": "Er moet aan de voorwaarden worden voldaan",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Naar voren scrollen",
|
|
110
111
|
"components.tabs.previous": "Naar achteren scrollen",
|
|
111
112
|
"components.tag-list.clear": "Klik, druk op Backspace of druk op de Delete-toets om item {value} te verwijderen",
|
package/lang/pt.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Visível.",
|
|
107
107
|
"components.switch.hidden": "Oculto",
|
|
108
108
|
"components.switch.conditions": "As condições devem ser atendidas",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Ir para frente",
|
|
110
111
|
"components.tabs.previous": "Ir para trás",
|
|
111
112
|
"components.tag-list.clear": "Clique em, pressione Backspace ou pressione a tecla Delete para remover o item {value}",
|
package/lang/sv.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Synlig.",
|
|
107
107
|
"components.switch.hidden": "Dold",
|
|
108
108
|
"components.switch.conditions": "Villkoren måste uppfyllas",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "Bläddra framåt",
|
|
110
111
|
"components.tabs.previous": "Bläddra bakåt",
|
|
111
112
|
"components.tag-list.clear": "Klicka, tryck på backstegstangenten eller Delete-tangenten för att ta bort objektet {value}",
|
package/lang/tr.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "Görünür.",
|
|
107
107
|
"components.switch.hidden": "Gizli",
|
|
108
108
|
"components.switch.conditions": "Koşullar karşılanmalıdır",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "İleri Kaydır",
|
|
110
111
|
"components.tabs.previous": "Geri Kaydır",
|
|
111
112
|
"components.tag-list.clear": "Öğe {value} değerini kaldırmak için tıklatın, geri al tuşuna veya sil tuşuna basın",
|
package/lang/zh-cn.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "可见。",
|
|
107
107
|
"components.switch.hidden": "隐藏",
|
|
108
108
|
"components.switch.conditions": "必须符合条件",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "向前滚动",
|
|
110
111
|
"components.tabs.previous": "向后滚动",
|
|
111
112
|
"components.tag-list.clear": "单击、按退格键或按 Delete 键以移除项目 {value}",
|
package/lang/zh-tw.js
CHANGED
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
"components.switch.visibleWithPeriod": "可見。",
|
|
107
107
|
"components.switch.hidden": "隱藏",
|
|
108
108
|
"components.switch.conditions": "必須符合條件",
|
|
109
|
+
"components.table-controls.label": "Actions for table",
|
|
109
110
|
"components.tabs.next": "向前捲動",
|
|
110
111
|
"components.tabs.previous": "向後捲動",
|
|
111
112
|
"components.tag-list.clear": "按一下、按下退格鍵或按下刪除鍵以移除項目 {value}",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightspace-ui/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.95.0",
|
|
4
4
|
"description": "A collection of accessible, free, open-source web components for building Brightspace applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": "https://github.com/BrightspaceUI/core.git",
|
|
@@ -71,7 +71,6 @@
|
|
|
71
71
|
"@brightspace-ui/intl": "^3",
|
|
72
72
|
"@formatjs/intl-pluralrules": "^1",
|
|
73
73
|
"@open-wc/dedupe-mixin": "^1",
|
|
74
|
-
"focus-visible": "^5",
|
|
75
74
|
"ifrau": "^0.40",
|
|
76
75
|
"intl-messageformat": "^7",
|
|
77
76
|
"lit": "^2",
|
|
@@ -2,9 +2,9 @@ import '../../components/colors/colors.js';
|
|
|
2
2
|
import '../../components/icons/icon-custom.js';
|
|
3
3
|
import '../../components/icons/icon.js';
|
|
4
4
|
import '../../components/offscreen/offscreen.js';
|
|
5
|
-
import { css, html, LitElement } from 'lit';
|
|
5
|
+
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
6
6
|
import { classMap } from 'lit/directives/class-map.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
|
8
8
|
import { getUniqueId } from '../../helpers/uniqueId.js';
|
|
9
9
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
10
10
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
@@ -536,7 +536,7 @@ class MobileTouchResizer extends Resizer {
|
|
|
536
536
|
* @fires d2l-template-primary-secondary-resize-start - Dispatched when a user begins moving the divider.
|
|
537
537
|
* @fires d2l-template-primary-secondary-resize-end - Dispatched when a user finishes moving the divider.
|
|
538
538
|
*/
|
|
539
|
-
class TemplatePrimarySecondary extends
|
|
539
|
+
class TemplatePrimarySecondary extends RtlMixin(LocalizeCoreElement(LitElement)) {
|
|
540
540
|
|
|
541
541
|
static get properties() {
|
|
542
542
|
return {
|
|
@@ -743,7 +743,9 @@ class TemplatePrimarySecondary extends FocusVisiblePolyfillMixin(RtlMixin(Locali
|
|
|
743
743
|
width: 0.1rem;
|
|
744
744
|
}
|
|
745
745
|
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-right,
|
|
746
|
-
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-left
|
|
746
|
+
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-left,
|
|
747
|
+
.d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} .d2l-template-primary-secondary-divider-handle-right,
|
|
748
|
+
.d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} .d2l-template-primary-secondary-divider-handle-left {
|
|
747
749
|
display: block;
|
|
748
750
|
}
|
|
749
751
|
:host(:not([dir="rtl"]):not([secondary-first])) [data-is-expanded] .d2l-template-primary-secondary-divider-handle-left,
|
|
@@ -776,7 +778,9 @@ class TemplatePrimarySecondary extends FocusVisiblePolyfillMixin(RtlMixin(Locali
|
|
|
776
778
|
box-shadow: none;
|
|
777
779
|
}
|
|
778
780
|
:host([resizable]) .d2l-template-primary-secondary-divider.focus-visible,
|
|
779
|
-
:host([resizable][dir="rtl"]) .d2l-template-primary-secondary-divider.focus-visible
|
|
781
|
+
:host([resizable][dir="rtl"]) .d2l-template-primary-secondary-divider.focus-visible,
|
|
782
|
+
:host([resizable]) .d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())},
|
|
783
|
+
:host([resizable][dir="rtl"]) .d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} {
|
|
780
784
|
background-color: var(--d2l-color-celestine);
|
|
781
785
|
}
|
|
782
786
|
.d2l-template-primary-secondary-divider:focus .d2l-template-primary-secondary-divider-handle-line::before,
|
|
@@ -786,7 +790,9 @@ class TemplatePrimarySecondary extends FocusVisiblePolyfillMixin(RtlMixin(Locali
|
|
|
786
790
|
background-color: var(--d2l-color-ferrite);
|
|
787
791
|
}
|
|
788
792
|
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-line::before,
|
|
789
|
-
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-line::after
|
|
793
|
+
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-line::after,
|
|
794
|
+
.d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} .d2l-template-primary-secondary-divider-handle-line::before,
|
|
795
|
+
.d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} .d2l-template-primary-secondary-divider-handle-line::after {
|
|
790
796
|
background-color: white;
|
|
791
797
|
}
|
|
792
798
|
|
|
@@ -913,13 +919,15 @@ class TemplatePrimarySecondary extends FocusVisiblePolyfillMixin(RtlMixin(Locali
|
|
|
913
919
|
height: 1rem;
|
|
914
920
|
width: 2.2rem;
|
|
915
921
|
}
|
|
916
|
-
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle
|
|
922
|
+
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle,
|
|
923
|
+
.d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} .d2l-template-primary-secondary-divider-handle {
|
|
917
924
|
box-shadow: none;
|
|
918
925
|
height: 1.2rem;
|
|
919
926
|
right: 17px;
|
|
920
927
|
width: 2.6rem;
|
|
921
928
|
}
|
|
922
|
-
:host([dir="rtl"]) .d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle
|
|
929
|
+
:host([dir="rtl"]) .d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle,
|
|
930
|
+
:host([dir="rtl"]) .d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} .d2l-template-primary-secondary-divider-handle {
|
|
923
931
|
left: 17px;
|
|
924
932
|
right: auto;
|
|
925
933
|
}
|
|
@@ -927,7 +935,8 @@ class TemplatePrimarySecondary extends FocusVisiblePolyfillMixin(RtlMixin(Locali
|
|
|
927
935
|
color: white;
|
|
928
936
|
display: block;
|
|
929
937
|
}
|
|
930
|
-
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-mobile
|
|
938
|
+
.d2l-template-primary-secondary-divider.focus-visible .d2l-template-primary-secondary-divider-handle-mobile,
|
|
939
|
+
.d2l-template-primary-secondary-divider:${unsafeCSS(getFocusPseudoClass())} .d2l-template-primary-secondary-divider-handle-mobile {
|
|
931
940
|
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.2rem var(--d2l-color-celestine);
|
|
932
941
|
right: 0.2rem;
|
|
933
942
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { resetMouse, sendKeys, sendMouse } from '@web/test-runner-commands';
|
|
2
|
+
|
|
3
|
+
export const focusWithKeyboard = async(element) => {
|
|
4
|
+
await sendKeys({ press: 'Tab' });
|
|
5
|
+
element.focus({ focusVisible: true });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const focusWithMouse = async(element) => {
|
|
9
|
+
const { x, y } = element.getBoundingClientRect();
|
|
10
|
+
await sendMouse({ type: 'click', position: [Math.ceil(x), Math.ceil(y)] });
|
|
11
|
+
await resetMouse();
|
|
12
|
+
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
let polyfillLoading = false;
|
|
2
|
-
|
|
3
|
-
export const FocusVisiblePolyfillMixin = superclass => class extends superclass {
|
|
4
|
-
|
|
5
|
-
connectedCallback() {
|
|
6
|
-
|
|
7
|
-
super.connectedCallback();
|
|
8
|
-
|
|
9
|
-
if (!this.shadowRoot) return;
|
|
10
|
-
|
|
11
|
-
if (window.applyFocusVisiblePolyfill) {
|
|
12
|
-
window.applyFocusVisiblePolyfill(this.shadowRoot);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
window.addEventListener('focus-visible-polyfill-ready', () => {
|
|
17
|
-
// somehow it's occasionally still not available
|
|
18
|
-
if (this.shadowRoot && window.applyFocusVisiblePolyfill) {
|
|
19
|
-
window.applyFocusVisiblePolyfill(this.shadowRoot);
|
|
20
|
-
}
|
|
21
|
-
}, { once: true });
|
|
22
|
-
|
|
23
|
-
if (!polyfillLoading) {
|
|
24
|
-
polyfillLoading = true;
|
|
25
|
-
import('focus-visible');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# FocusVisiblePolyfillMixin
|
|
2
|
-
|
|
3
|
-
From [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible):
|
|
4
|
-
|
|
5
|
-
> The `:focus-visible` pseudo-class applies while an element matches the `:focus` pseudo-class and the user agent determines via heuristics that the focus should be made evident on the element.
|
|
6
|
-
|
|
7
|
-
In order words, it allows you to provide focus styles for an element that only get applied when the user accessed it via keyboard. But today only Chrome supports `:focus-visible`, so a polyfill is required.
|
|
8
|
-
|
|
9
|
-
The `FocusVisiblePolyfillMixin` adds support to a component to use the [focus-visible polyfill](https://github.com/WICG/focus-visible), ensuring that the polyfill is lazy loaded only once and that it's properly applied to the component's shadow root.
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
Apply the mixin and target the `focus-visible` CSS class for styles you'd like to apply when focus should be visible.
|
|
14
|
-
|
|
15
|
-
```js
|
|
16
|
-
import { FocusVisiblePolyfillMixin } from '@brightspace-ui/core/mixins/focus-visible-polyfill-mixin.js';
|
|
17
|
-
|
|
18
|
-
class MyComponent extends FocusVisiblePolyfillMixin(LitElement) {
|
|
19
|
-
static get styles() {
|
|
20
|
-
return css`
|
|
21
|
-
/* styles to apply when clicked or focused with a keyboard */
|
|
22
|
-
button:hover,
|
|
23
|
-
button:focus {
|
|
24
|
-
background-color: #cccccc;
|
|
25
|
-
}
|
|
26
|
-
/* styles to apply only when focused with a keyboard */
|
|
27
|
-
button.focus-visible {
|
|
28
|
-
outline: 2px solid black;
|
|
29
|
-
}
|
|
30
|
-
`;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
**Learn More:** [focus-visible polyfill documentation](https://github.com/WICG/focus-visible)
|
|
36
|
-
|
|
37
|
-
## Forcing Visible Focus
|
|
38
|
-
|
|
39
|
-
When applying focus programatically in JavaScript via `elem.focus()`, unless a `Tab` keyboard event has just occurred, a visible focus ring will not be shown. In certain circumstances, forcing focus to be visible is desired -- like when moving focus from one element to another.
|
|
40
|
-
|
|
41
|
-
For example, opening a dialog moves the user's focus from an opener to inside the dialog and then closing the dialog moves it back to the opener. In cases like this, not being able to clearly see which element has focus can be disorienting for the user.
|
|
42
|
-
|
|
43
|
-
To force visible focus, use the `forceFocusVisible()` helper:
|
|
44
|
-
|
|
45
|
-
```js
|
|
46
|
-
import { forceFocusVisible } from '@brightspace-ui/core/helpers/focus.js';
|
|
47
|
-
|
|
48
|
-
// focus and force a visible focus ring
|
|
49
|
-
forceFocusVisible(elem);
|
|
50
|
-
```
|