@brightspace-ui/core 2.14.3 → 2.14.6

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.
@@ -111,9 +111,6 @@ export const htmlBlockContentStyles = css`
111
111
  mjx-assistive-mml math {
112
112
  position: absolute;
113
113
  }
114
- :host([dir="rtl"]) {
115
- text-align: right;
116
- }
117
114
  `;
118
115
 
119
116
  let renderers;
@@ -164,6 +161,9 @@ class HtmlBlock extends RtlMixin(LitElement) {
164
161
  :host([no-deferred-rendering]) div.d2l-html-block-rendered {
165
162
  display: none;
166
163
  }
164
+ :host([dir="rtl"]) {
165
+ text-align: right;
166
+ }
167
167
  `];
168
168
  }
169
169
 
@@ -203,6 +203,8 @@ class HtmlBlock extends RtlMixin(LitElement) {
203
203
  super.firstUpdated(changedProperties);
204
204
 
205
205
  if (this._renderContainer) return;
206
+
207
+ // The d2l-html-block-rendered class is used to apply CSS outside of the html-block component. Do not change lightly.
206
208
  this.shadowRoot.innerHTML += '<div class="d2l-html-block-rendered'
207
209
  + `${this.compact ? ' d2l-html-block-compact' : ''}`
208
210
  + '"></div><slot'
@@ -73,7 +73,7 @@
73
73
 
74
74
  <h2>Tag List with Interactive</h2>
75
75
  <d2l-demo-snippet fullscreen>
76
- <div role="grid">
76
+ <div grid>
77
77
  <d2l-tag-list description="A bunch of example tags" clearable>
78
78
  <d2l-tag-list-item text="Example Tag"></d2l-tag-list-item>
79
79
  <d2l-tag-list-item text="Longer Example Tag - much much much much much longer"></d2l-tag-list-item>
@@ -49,7 +49,7 @@ class TagList extends LocalizeCoreElement(InteractiveMixin(ArrowKeysMixin(LitEle
49
49
  }
50
50
 
51
51
  static get styles() {
52
- return [super.styles, css`
52
+ return [...super.styles, css`
53
53
  :host {
54
54
  display: block;
55
55
  }
@@ -1,8 +1,10 @@
1
1
  import { css, html } from 'lit';
2
2
  import { findComposedAncestor, isComposedAncestor } from '../helpers/dom.js';
3
- import { forceFocusVisible, getNextFocusable } from '../helpers/focus.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { getNextFocusable } from '../helpers/focus.js';
4
5
  import { ifDefined } from 'lit/directives/if-defined.js';
5
6
  import { LocalizeCoreElement } from '../helpers/localize-core-element.js';
7
+ import { offscreenStyles } from '../components/offscreen/offscreen.js';
6
8
 
7
9
  const keyCodes = {
8
10
  ENTER: 13,
@@ -13,24 +15,25 @@ export const InteractiveMixin = superclass => class extends LocalizeCoreElement(
13
15
 
14
16
  static get properties() {
15
17
  return {
18
+ _focusingToggle: { state: true },
16
19
  _hasInteractiveAncestor: { state: true },
17
20
  _interactive: { state: true }
18
21
  };
19
22
  }
20
23
 
21
24
  static get styles() {
22
- return css`
23
- .interactive-container.focus-visible,
24
- .interactive-container:focus-visible {
25
+ return [offscreenStyles, css`
26
+ .interactive-focusing-toggle {
25
27
  border-radius: 6px;
26
28
  outline: 2px solid var(--d2l-color-celestine);
27
29
  outline-offset: 2px;
28
30
  }
29
- `;
31
+ `];
30
32
  }
31
33
 
32
34
  constructor() {
33
35
  super();
36
+ this._focusingToggle = false;
34
37
  this._hasInteractiveAncestor = false;
35
38
  this._interactive = false;
36
39
  }
@@ -39,14 +42,14 @@ export const InteractiveMixin = superclass => class extends LocalizeCoreElement(
39
42
  super.connectedCallback();
40
43
 
41
44
  const parentGrid = findComposedAncestor(this.parentNode, node => {
42
- return ((node.tagName === 'D2L-LIST' && node.grid) || (node.nodeType === Node.ELEMENT_NODE && node.getAttribute('role') === 'grid'));
45
+ return (node.nodeType === Node.ELEMENT_NODE && (node.hasAttribute('grid') || node.getAttribute('role') === 'grid'));
43
46
  });
44
47
  this._hasInteractiveAncestor = (parentGrid !== null);
45
48
  }
46
49
 
47
50
  focus() {
48
51
  if (!this.shadowRoot) return;
49
- if (this._hasInteractiveAncestor && !this._interactive) forceFocusVisible(this.shadowRoot.querySelector('.interactive-container'));
52
+ if (this._hasInteractiveAncestor && !this._interactive) this.shadowRoot.querySelector('.interactive-toggle').focus();
50
53
  else this._focusDelegate();
51
54
  }
52
55
 
@@ -57,18 +60,27 @@ export const InteractiveMixin = superclass => class extends LocalizeCoreElement(
57
60
  if (!focusDelegate) {
58
61
  throw new Error(`InteractiveMixin: no focus delegate provided for "${this.tagName}"`);
59
62
  }
63
+
60
64
  this._focusDelegate = focusDelegate;
61
65
  if (!this._hasInteractiveAncestor) return inner;
66
+
67
+ const classes = {
68
+ 'interactive-focusing-toggle': this._focusingToggle
69
+ };
70
+
62
71
  return html`
63
- <div class="interactive-container"
64
- aria-label="${label}"
65
- aria-description="${this.localize('components.interactive.instructions')}"
66
- @keydown="${this._handleInteractiveKeyDown}"
67
- role="button"
68
- tabindex="${ifDefined(this._hasInteractiveAncestor && !this._interactive ? '0' : undefined)}">
69
- <span class="interactive-trap-start" @focus="${this._handleInteractiveStartFocus}" tabindex="${ifDefined(this._hasInteractiveAncestor ? '0' : undefined)}"></span>
72
+ <div class="${classMap(classes)}" @keydown="${this._handleInteractiveKeyDown}">
73
+ <button
74
+ class="interactive-toggle d2l-offscreen"
75
+ @blur="${this._handleInteractiveToggleBlur}"
76
+ @click="${this._handleInteractiveToggleClick}"
77
+ @focus="${this._handleInteractiveToggleFocus}"
78
+ tabindex="${ifDefined(this._hasInteractiveAncestor && !this._interactive ? '0' : '-1')}">
79
+ ${`${label}, ${this.localize('components.interactive.instructions')}`}
80
+ </button>
81
+ <span class="interactive-trap-start" @focus="${this._handleInteractiveTrapStartFocus}" tabindex="${ifDefined(this._hasInteractiveAncestor ? '0' : undefined)}"></span>
70
82
  <div class="interactive-container-content" @focusin="${this._handleInteractiveContentFocusIn}" @focusout="${this._handleInteractiveContentFocusOut}">${inner}</div>
71
- <span class="interactive-trap-end" @focus="${this._handleInteractiveEndFocus}" tabindex="${ifDefined(this._hasInteractiveAncestor ? '0' : undefined)}"></span>
83
+ <span class="interactive-trap-end" @focus="${this._handleInteractiveTrapEndFocus}" tabindex="${ifDefined(this._hasInteractiveAncestor ? '0' : undefined)}"></span>
72
84
  </div>
73
85
  `;
74
86
  }
@@ -83,27 +95,37 @@ export const InteractiveMixin = superclass => class extends LocalizeCoreElement(
83
95
  this._interactive = false;
84
96
  }
85
97
 
86
- async _handleInteractiveEndFocus() {
87
- // focus moved to trap-end either forwards from contents or backwards from outside - focus interactive toggle
88
- this._interactive = false;
89
- await this.updateComplete;
90
- this.shadowRoot.querySelector('.interactive-container').focus();
91
- }
92
-
93
98
  async _handleInteractiveKeyDown(e) {
94
- if (!this._interactive && e.keyCode === keyCodes.ENTER) {
95
- this._interactive = true;
96
- await this.updateComplete;
97
- this.focus();
98
- } else if (this._interactive && e.keyCode === keyCodes.ESCAPE) {
99
+ if (this._interactive && e.keyCode === keyCodes.ESCAPE) {
99
100
  this._interactive = false;
100
101
  await this.updateComplete;
101
- this.shadowRoot.querySelector('.interactive-container').focus();
102
+ this.shadowRoot.querySelector('.interactive-toggle').focus();
102
103
  }
103
104
  }
104
105
 
105
- async _handleInteractiveStartFocus(e) {
106
- if (e.relatedTarget === this.shadowRoot.querySelector('.interactive-container')) {
106
+ _handleInteractiveToggleBlur() {
107
+ this._focusingToggle = false;
108
+ }
109
+
110
+ async _handleInteractiveToggleClick() {
111
+ this._interactive = true;
112
+ await this.updateComplete;
113
+ this.focus();
114
+ }
115
+
116
+ _handleInteractiveToggleFocus() {
117
+ this._focusingToggle = true;
118
+ }
119
+
120
+ async _handleInteractiveTrapEndFocus() {
121
+ // focus moved to trap-end either forwards from contents or backwards from outside - focus interactive toggle
122
+ this._interactive = false;
123
+ await this.updateComplete;
124
+ this.shadowRoot.querySelector('.interactive-toggle').focus();
125
+ }
126
+
127
+ async _handleInteractiveTrapStartFocus(e) {
128
+ if (e.relatedTarget === this.shadowRoot.querySelector('.interactive-toggle')) {
107
129
  // focus moved to trap-start while non-interactive - focus next focusable after this component
108
130
  const nextFocusable = getNextFocusable(this.shadowRoot.querySelector('.interactive-trap-end'));
109
131
  if (nextFocusable) nextFocusable.focus();
@@ -111,7 +133,7 @@ export const InteractiveMixin = superclass => class extends LocalizeCoreElement(
111
133
  // focus moved to trap-start backwards from within contents - toggle to non-interactive and apply focus
112
134
  this._interactive = false;
113
135
  await this.updateComplete;
114
- this.shadowRoot.querySelector('.interactive-container').focus();
136
+ this.shadowRoot.querySelector('.interactive-toggle').focus();
115
137
  }
116
138
  }
117
139
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "2.14.3",
3
+ "version": "2.14.6",
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",