@brightspace-ui/core 2.101.3 → 2.103.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 CHANGED
@@ -71,7 +71,7 @@ npm install @brightspace-ui/core
71
71
  * [FormElementMixin](components/form/docs/form-element-mixin.md): allow components to participate in forms and validation
72
72
  * [InteractiveMixin](mixins/interactive-mixin.md): enables toggling interactive elements inside of nested grids
73
73
  * [LabelledMixin](mixins/labelled-mixin.md): label custom elements by referencing elements across DOM scopes
74
- * [LocalizeMixin](mixins/localize-mixin.md): localize text in your components
74
+ * [LocalizeMixin](mixins/localize/): localize text in your components
75
75
  * [ProviderMixin](mixins/provider-mixin.md): provide and consume data across elements in a DI-like fashion
76
76
  * [RtlMixin](mixins/rtl-mixin.md): enable components to define RTL styles
77
77
  * [SkeletonMixin](components/skeleton/): make components skeleton-aware
@@ -167,7 +167,6 @@ class ButtonIcon extends ThemeMixin(ButtonMixin(VisibleOnAncestorMixin(RtlMixin(
167
167
  aria-haspopup="${ifDefined(this.ariaHaspopup)}"
168
168
  aria-label="${this.ariaLabel ? this.ariaLabel : ifDefined(this.text)}"
169
169
  ?autofocus="${this.autofocus}"
170
- class="d2l-label-text"
171
170
  ?disabled="${this.disabled && !this.disabledTooltip}"
172
171
  form="${ifDefined(this.form)}"
173
172
  formaction="${ifDefined(this.formaction)}"
@@ -0,0 +1,245 @@
1
+ import '../colors/colors.js';
2
+ import '../icons/icon.js';
3
+ import '../tooltip/tooltip.js';
4
+ import { css, html, LitElement } from 'lit';
5
+ import { buttonStyles } from './button-styles.js';
6
+ import { FocusMixin } from '../../mixins/focus-mixin.js';
7
+ import { getUniqueId } from '../../helpers/uniqueId.js';
8
+ import { ifDefined } from 'lit/directives/if-defined.js';
9
+ import { RtlMixin } from '../../mixins/rtl-mixin.js';
10
+
11
+ const keyCodes = Object.freeze({
12
+ DOWN: 40,
13
+ END: 35,
14
+ HOME: 36,
15
+ LEFT: 37,
16
+ RIGHT: 39,
17
+ UP: 38
18
+ });
19
+
20
+ export const moveActions = Object.freeze({
21
+ up: 'up',
22
+ down: 'down',
23
+ left: 'left',
24
+ right: 'right',
25
+ rootHome: 'root-home',
26
+ home: 'home',
27
+ rootEnd: 'root-end',
28
+ end: 'end'
29
+ });
30
+
31
+ /**
32
+ * A button component that provides a move action via a single button.
33
+ */
34
+ class ButtonMove extends FocusMixin(RtlMixin(LitElement)) {
35
+
36
+ static get properties() {
37
+ return {
38
+ /**
39
+ * @ignore
40
+ */
41
+ autofocus: { type: Boolean, reflect: true },
42
+ /**
43
+ * A description to be added to the button for accessibility when text on button does not provide enough context
44
+ * @type {string}
45
+ */
46
+ description: { type: String },
47
+ /**
48
+ * Disables the button
49
+ * @type {boolean}
50
+ */
51
+ disabled: { type: Boolean, reflect: true },
52
+ /**
53
+ * Tooltip text when disabled
54
+ * @type {string}
55
+ */
56
+ disabledTooltip: { type: String, attribute: 'disabled-tooltip' },
57
+ /**
58
+ * REQUIRED: Accessible text for the button
59
+ * @type {string}
60
+ */
61
+ text: { type: String, reflect: true }
62
+
63
+ };
64
+ }
65
+
66
+ static get styles() {
67
+ return [ buttonStyles,
68
+ css`
69
+ :host {
70
+ display: inline-block;
71
+ line-height: 0;
72
+ }
73
+ :host([hidden]) {
74
+ display: none;
75
+ }
76
+ button {
77
+ background-color: transparent;
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: 2px;
81
+ margin: 0;
82
+ min-height: 1.8rem;
83
+ padding: 0;
84
+ position: relative;
85
+ width: 0.9rem;
86
+ }
87
+ d2l-icon {
88
+ border-radius: 0.1rem;
89
+ height: 0.85rem;
90
+ width: 0.9rem;
91
+ }
92
+ button:focus {
93
+ background-color: #ffffff;
94
+ }
95
+ button:hover > d2l-icon,
96
+ button:focus > d2l-icon {
97
+ background-color: var(--d2l-color-mica);
98
+ }
99
+ .up-icon {
100
+ border-top-left-radius: 0.3rem;
101
+ border-top-right-radius: 0.3rem;
102
+ }
103
+ .down-icon {
104
+ border-bottom-left-radius: 0.3rem;
105
+ border-bottom-right-radius: 0.3rem;
106
+ }
107
+
108
+ .up-layer,
109
+ .down-layer {
110
+ height: 1.1rem;
111
+ left: -0.2rem;
112
+ position: absolute;
113
+ width: 1.3rem;
114
+ }
115
+ .up-layer {
116
+ top: -0.25rem;
117
+ }
118
+ .down-layer {
119
+ bottom: -0.25rem;
120
+ }
121
+ :host([dir="rtl"]) .up-layer,
122
+ :host([dir="rtl"]) .down-layer {
123
+ left: auto;
124
+ right: -0.2rem;
125
+ }
126
+
127
+
128
+ /* Firefox includes a hidden border which messes up button dimensions */
129
+ button::-moz-focus-inner {
130
+ border: 0;
131
+ }
132
+ :host([disabled]) button {
133
+ cursor: default;
134
+ opacity: 0.5;
135
+ }
136
+ button[disabled]:hover > d2l-icon {
137
+ background-color: transparent;
138
+ }
139
+ `
140
+ ];
141
+ }
142
+
143
+ constructor() {
144
+ super();
145
+ this.disabled = false;
146
+ /** @ignore */
147
+ this.autofocus = false;
148
+ /** @internal */
149
+ this._buttonId = getUniqueId();
150
+ /** @internal */
151
+ this._describedById = getUniqueId();
152
+ }
153
+
154
+ static get focusElementSelector() {
155
+ return 'button';
156
+ }
157
+
158
+ connectedCallback() {
159
+ super.connectedCallback();
160
+ this.addEventListener('click', this._handleClick, true);
161
+ }
162
+
163
+ disconnectedCallback() {
164
+ super.disconnectedCallback();
165
+ this.removeEventListener('click', this._handleClick, true);
166
+ }
167
+
168
+ render() {
169
+ return html`
170
+ <button
171
+ aria-describedby="${ifDefined(this.description ? this._describedById : undefined)}"
172
+ aria-disabled="${ifDefined(this.disabled && this.disabledTooltip ? 'true' : undefined)}"
173
+ aria-label="${ifDefined(this.text)}"
174
+ ?autofocus="${this.autofocus}"
175
+ ?disabled="${this.disabled && !this.disabledTooltip}"
176
+ id="${this._buttonId}"
177
+ @keydown="${this._handleKeydown}"
178
+ title="${ifDefined(this.text)}"
179
+ type="button">
180
+ <d2l-icon icon="tier1:arrow-toggle-up" class="up-icon"></d2l-icon>
181
+ <d2l-icon icon="tier1:arrow-toggle-down" class="down-icon"></d2l-icon>
182
+ <div class="up-layer" @click="${this._handleUpClick}"></div>
183
+ <div class="down-layer" @click="${this._handleDownClick}"></div>
184
+ </button>
185
+ ${this.description ? html`<span id="${this._describedById}" hidden>${this.description}</span>` : null}
186
+ ${this.disabled && this.disabledTooltip ? html`<d2l-tooltip for="${this._buttonId}">${this.disabledTooltip}</d2l-tooltip>` : ''}
187
+ `;
188
+ }
189
+
190
+ _dispatchAction(action) {
191
+ if (!action) return;
192
+ this.dispatchEvent(new CustomEvent('d2l-button-move-action', {
193
+ detail: { action },
194
+ bubbles: false
195
+ }));
196
+ }
197
+
198
+ _handleClick(e) {
199
+ if (this.disabled) {
200
+ e.stopPropagation();
201
+ }
202
+ }
203
+
204
+ _handleDownClick() {
205
+ this._dispatchAction(moveActions.down);
206
+ }
207
+
208
+ _handleKeydown(e) {
209
+ let action;
210
+ switch (e.keyCode) {
211
+ case keyCodes.UP:
212
+ action = moveActions.up;
213
+ break;
214
+ case keyCodes.DOWN:
215
+ action = moveActions.down;
216
+ break;
217
+ case keyCodes.LEFT:
218
+ action = moveActions.left;
219
+ break;
220
+ case keyCodes.RIGHT:
221
+ action = moveActions.right;
222
+ break;
223
+ case keyCodes.HOME:
224
+ action = (e.ctrlKey ? moveActions.rootHome : moveActions.home);
225
+ break;
226
+ case keyCodes.END:
227
+ action = (e.ctrlKey ? moveActions.rootEnd : moveActions.end);
228
+ break;
229
+ default:
230
+ return;
231
+ }
232
+
233
+ this._dispatchAction(action);
234
+ e.preventDefault();
235
+ e.stopPropagation();
236
+
237
+ }
238
+
239
+ _handleUpClick() {
240
+ this._dispatchAction(moveActions.up);
241
+ }
242
+
243
+ }
244
+
245
+ customElements.define('d2l-button-move', ButtonMove);
@@ -0,0 +1,46 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="UTF-8">
6
+ <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
7
+ <script type="module">
8
+ import '../../demo/demo-page.js';
9
+ import '../button-move.js';
10
+ </script>
11
+ </head>
12
+ <body unresolved>
13
+
14
+ <d2l-demo-page page-title="d2l-button-move">
15
+
16
+ <h2>Move Button</h2>
17
+
18
+ <d2l-demo-snippet>
19
+ <template>
20
+ <d2l-button-move id="move-button" text="Reorder Item"></d2l-button-move>
21
+ <script>
22
+ document.querySelector('#move-button').addEventListener('d2l-button-move-action', e => {
23
+ console.log('d2l-button-move-action', e.target, e.detail);
24
+ });
25
+ </script>
26
+ </template>
27
+ </d2l-demo-snippet>
28
+
29
+ <h2>Move Button Disabled</h2>
30
+
31
+ <d2l-demo-snippet>
32
+ <template>
33
+ <d2l-button-move text="Reorder Item" disabled></d2l-button-move>
34
+ </template>
35
+ </d2l-demo-snippet>
36
+
37
+ <h2>Move Button Disabled with Tooltip</h2>
38
+
39
+ <d2l-demo-snippet>
40
+ <template>
41
+ <d2l-button-move text="Reorder Item" disabled disabled-tooltip="Optional disabled tooltip"></d2l-button-move>
42
+ </template>
43
+ </d2l-demo-snippet>
44
+
45
+ </body>
46
+ </html>
@@ -1,12 +1,12 @@
1
- import '../button/button-icon.js';
2
1
  import '../icons/icon.js';
3
2
  import '../tooltip/tooltip.js';
4
3
  import { css, html, LitElement } from 'lit';
5
4
  import { buttonStyles } from '../button/button-styles.js';
6
5
  import { findComposedAncestor } from '../../helpers/dom.js';
7
- import { getFirstFocusableDescendant } from '../../helpers/focus.js';
6
+ import { FocusMixin } from '../../mixins/focus-mixin.js';
8
7
  import { getUniqueId } from '../../helpers/uniqueId.js';
9
8
  import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
9
+ import { moveActions } from '../button/button-move.js';
10
10
  import { RtlMixin } from '../../mixins/rtl-mixin.js';
11
11
 
12
12
  const keyCodes = Object.freeze({
@@ -43,7 +43,7 @@ let hasDisplayedKeyboardTooltip = false;
43
43
  /**
44
44
  * @fires d2l-list-item-drag-handle-action - Dispatched when an action performed on the drag handle
45
45
  */
46
- class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
46
+ class ListItemDragHandle extends LocalizeCoreElement(FocusMixin(RtlMixin(LitElement))) {
47
47
 
48
48
  static get properties() {
49
49
  return {
@@ -79,33 +79,6 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
79
79
  .d2l-list-item-drag-handle-dragger-button {
80
80
  background-color: unset;
81
81
  display: block;
82
- }
83
- .d2l-list-item-drag-handle-keyboard-button {
84
- display: grid;
85
- gap: 2px;
86
- grid-auto-rows: 1fr 1fr;
87
- position: relative;
88
- }
89
- .d2l-list-item-drag-handle-keyboard-button-up,
90
- .d2l-list-item-drag-handle-keyboard-button-down {
91
- height: 1.1rem;
92
- left: -0.2rem;
93
- position: absolute;
94
- width: 1.3rem;
95
- }
96
- .d2l-list-item-drag-handle-keyboard-button-up {
97
- top: -0.25rem;
98
- }
99
- .d2l-list-item-drag-handle-keyboard-button-down {
100
- bottom: -0.25rem;
101
- }
102
- :host([dir="rtl"]) .d2l-list-item-drag-handle-keyboard-button-up,
103
- :host([dir="rtl"]) .d2l-list-item-drag-handle-keyboard-button-down {
104
- left: auto;
105
- right: -0.2rem;
106
- }
107
- .d2l-list-item-drag-handle-dragger-button,
108
- .d2l-list-item-drag-handle-keyboard-button {
109
82
  margin: 0;
110
83
  min-height: 1.8rem;
111
84
  padding: 0;
@@ -119,20 +92,6 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
119
92
  height: 0.9rem;
120
93
  width: 0.9rem;
121
94
  }
122
- .d2l-button-up-icon,
123
- .d2l-button-down-icon {
124
- border-radius: 0.1rem;
125
- height: 0.85rem;
126
- width: 0.9rem;
127
- }
128
- .d2l-button-up-icon {
129
- border-top-left-radius: 0.3rem;
130
- border-top-right-radius: 0.3rem;
131
- }
132
- .d2l-button-down-icon {
133
- border-bottom-left-radius: 0.3rem;
134
- border-bottom-right-radius: 0.3rem;
135
- }
136
95
  button,
137
96
  button[disabled]:hover,
138
97
  button[disabled]:focus {
@@ -143,14 +102,6 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
143
102
  .d2l-list-item-drag-handle-dragger-button:focus {
144
103
  background-color: var(--d2l-color-mica);
145
104
  }
146
- .d2l-list-item-drag-handle-keyboard-button:hover,
147
- .d2l-list-item-drag-handle-keyboard-button:focus {
148
- background-color: transparent;
149
- }
150
- .d2l-list-item-drag-handle-keyboard-button:hover > d2l-icon,
151
- .d2l-list-item-drag-handle-keyboard-button:focus > d2l-icon {
152
- background-color: var(--d2l-color-mica);
153
- }
154
105
  button[disabled] {
155
106
  cursor: default;
156
107
  opacity: 0.5;
@@ -178,6 +129,10 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
178
129
  this._movingElement = false;
179
130
  }
180
131
 
132
+ static get focusElementSelector() {
133
+ return '.d2l-list-item-drag-handle-button';
134
+ }
135
+
181
136
  render() {
182
137
  return this._keyboardActive && !this.disabled ? this._renderKeyboardDragging() : this._renderDragger();
183
138
  }
@@ -192,11 +147,6 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
192
147
  this._keyboardActive = true;
193
148
  }
194
149
 
195
- focus() {
196
- const node = getFirstFocusableDescendant(this);
197
- if (node) node.focus();
198
- }
199
-
200
150
  get _defaultLabel() {
201
151
  const namespace = 'components.list-item-drag-handle';
202
152
  const defaultLabel = this.localize(`${namespace}.${'default'}`, 'name', this.text);
@@ -212,37 +162,96 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
212
162
  }));
213
163
  }
214
164
 
215
- _dispatchActionDown() {
216
- this._dispatchAction(dragActions.down);
165
+ async _doAction(action) {
166
+ this._dispatchAction(action);
167
+ const cell = findComposedAncestor(this, (parent) => parent.hasAttribute && parent.hasAttribute('draggable'));
168
+ if (cell) await cell.updateComplete;
169
+ await this.updateComplete;
170
+ this.focus();
171
+ this._movingElement = false;
217
172
  }
218
173
 
219
- _dispatchActionUp() {
220
- this._dispatchAction(dragActions.up);
174
+ _onDraggerButtonClick() {
175
+ this.activateKeyboardMode();
221
176
  }
222
177
 
223
- async _onActiveKeyboard(e) {
224
- if (!this._keyboardActive) {
178
+ _onDraggerButtonKeydown(e) {
179
+ if (e.keyCode !== keyCodes.ENTER && e.keyCode !== keyCodes.SPACE) return;
180
+ e.preventDefault();
181
+ this.activateKeyboardMode();
182
+ }
183
+
184
+ _onKeyboardButtonFocusIn() {
185
+ if (hasDisplayedKeyboardTooltip) return;
186
+ this._displayKeyboardTooltip = true;
187
+ hasDisplayedKeyboardTooltip = true;
188
+ }
189
+
190
+ _onKeyboardButtonFocusOut(e) {
191
+ this._displayKeyboardTooltip = false;
192
+ if (this._movingElement) {
193
+ this._movingElement = false;
194
+ e.stopPropagation();
195
+ e.preventDefault();
225
196
  return;
226
197
  }
198
+ this._keyboardActive = false;
199
+ this._dispatchAction(dragActions.save);
200
+ e.stopPropagation();
201
+ }
202
+
203
+ async _onMoveButtonAction(e) {
204
+
227
205
  let action = null;
228
- switch (e.keyCode) {
229
- case keyCodes.UP:
206
+ switch (e.detail.action) {
207
+ case moveActions.up:
230
208
  this._movingElement = true;
231
209
  action = dragActions.up;
232
210
  this.updateComplete.then(() => this.blur()); // tell screenreaders to refocus
233
211
  break;
234
- case keyCodes.DOWN:
212
+ case moveActions.down:
235
213
  this._movingElement = true;
236
214
  action = dragActions.down;
237
215
  break;
238
- case keyCodes.HOME:
216
+ case moveActions.right:
217
+ this._movingElement = true;
218
+ action = (this.dir === 'rtl' ? dragActions.unnest : dragActions.nest);
219
+ break;
220
+ case moveActions.left:
221
+ this._movingElement = true;
222
+ action = (this.dir === 'rtl' ? dragActions.nest : dragActions.unnest) ;
223
+ break;
224
+ case moveActions.rootHome:
225
+ this._movingElement = true;
226
+ action = dragActions.rootFirst;
227
+ break;
228
+ case moveActions.home:
239
229
  this._movingElement = true;
240
- action = (e.ctrlKey ? dragActions.rootFirst : dragActions.first);
230
+ action = dragActions.first;
241
231
  break;
242
- case keyCodes.END:
232
+ case moveActions.rootEnd:
243
233
  this._movingElement = true;
244
- action = (e.ctrlKey ? dragActions.rootLast : dragActions.last);
234
+ action = dragActions.rootLast;
245
235
  break;
236
+ case moveActions.end:
237
+ this._movingElement = true;
238
+ action = dragActions.last;
239
+ break;
240
+ default:
241
+ return;
242
+ }
243
+
244
+ this._doAction(action);
245
+
246
+ }
247
+
248
+ async _onMoveButtonKeydown(e) {
249
+ if (!this._keyboardActive) {
250
+ return;
251
+ }
252
+
253
+ let action = null;
254
+ switch (e.keyCode) {
246
255
  case keyCodes.TAB:
247
256
  action = e.shiftKey ? dragActions.previousElement : dragActions.nextElement;
248
257
  break;
@@ -250,14 +259,6 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
250
259
  action = dragActions.cancel;
251
260
  this.updateComplete.then(() => this._keyboardActive = false);
252
261
  break;
253
- case keyCodes.RIGHT:
254
- this._movingElement = true;
255
- action = (this.dir === 'rtl' ? dragActions.unnest : dragActions.nest);
256
- break;
257
- case keyCodes.LEFT:
258
- this._movingElement = true;
259
- action = (this.dir === 'rtl' ? dragActions.nest : dragActions.unnest) ;
260
- break;
261
262
  case keyCodes.ENTER:
262
263
  case keyCodes.SPACE:
263
264
  action = dragActions.save;
@@ -266,49 +267,19 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
266
267
  default:
267
268
  return;
268
269
  }
269
- this._dispatchAction(action);
270
- e.preventDefault();
271
- e.stopPropagation();
272
- const cell = findComposedAncestor(this, (parent) => parent.hasAttribute && parent.hasAttribute('draggable'));
273
- if (cell) await cell.updateComplete;
274
- await this.updateComplete;
275
- this.focus();
276
- this._movingElement = false;
277
- }
278
270
 
279
- _onFocusInKeyboardButton() {
280
- if (hasDisplayedKeyboardTooltip) return;
281
- this._displayKeyboardTooltip = true;
282
- hasDisplayedKeyboardTooltip = true;
283
- }
284
-
285
- _onFocusOutKeyboardButton(e) {
286
- this._displayKeyboardTooltip = false;
287
- if (this._movingElement) {
288
- this._movingElement = false;
289
- e.stopPropagation();
290
- e.preventDefault();
291
- return;
292
- }
293
- this._keyboardActive = false;
294
- this._dispatchAction(dragActions.save);
271
+ e.preventDefault();
295
272
  e.stopPropagation();
296
- }
273
+ this._doAction(action);
297
274
 
298
- _onInactiveKeyboard(e) {
299
- if (e.type === 'click' || e.keyCode === keyCodes.ENTER || e.keyCode === keyCodes.SPACE) {
300
- this._dispatchAction(dragActions.active);
301
- this._keyboardActive = true;
302
- e.preventDefault();
303
- }
304
275
  }
305
276
 
306
277
  _renderDragger() {
307
278
  return html`
308
279
  <button
309
- class="d2l-list-item-drag-handle-dragger-button"
310
- @click="${this._onInactiveKeyboard}"
311
- @keydown="${this._onInactiveKeyboard}"
280
+ class="d2l-list-item-drag-handle-dragger-button d2l-list-item-drag-handle-button"
281
+ @click="${this._onDraggerButtonClick}"
282
+ @keydown="${this._onDraggerButtonKeydown}"
312
283
  aria-label="${this._defaultLabel}"
313
284
  ?disabled="${this.disabled}">
314
285
  <d2l-icon icon="tier1:dragger" class="d2l-button-dragger-icon"></d2l-icon>
@@ -318,19 +289,16 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
318
289
 
319
290
  _renderKeyboardDragging() {
320
291
  return html`
321
- <button
322
- aria-label="${this._defaultLabel}"
323
- class="d2l-list-item-drag-handle-keyboard-button"
324
- @focusin="${this._onFocusInKeyboardButton}"
325
- @focusout="${this._onFocusOutKeyboardButton}"
292
+ <d2l-button-move
293
+ class="d2l-list-item-drag-handle-button"
294
+ @d2l-button-move-action="${this._onMoveButtonAction}"
295
+ @focusin="${this._onKeyboardButtonFocusIn}"
296
+ @focusout="${this._onKeyboardButtonFocusOut}"
326
297
  id="${this._buttonId}"
327
- @keydown="${this._onActiveKeyboard}">
328
- <d2l-icon icon="tier1:arrow-toggle-up" class="d2l-button-up-icon"></d2l-icon>
329
- <d2l-icon icon="tier1:arrow-toggle-down" class="d2l-button-down-icon"></d2l-icon>
330
- <div class="d2l-list-item-drag-handle-keyboard-button-up" @click="${this._dispatchActionUp}"></div>
331
- <div class="d2l-list-item-drag-handle-keyboard-button-down" @click="${this._dispatchActionDown}"></div>
332
- </button>
333
- ${this._displayKeyboardTooltip ? html`<d2l-tooltip align="start" for="${this._buttonId}" for-type="descriptor">${this._renderTooltipContent()}</d2l-tooltip>` : ''}
298
+ @keydown="${this._onMoveButtonKeydown}"
299
+ text="${this._defaultLabel}">
300
+ </d2l-button-move>
301
+ ${this._displayKeyboardTooltip ? html`<d2l-tooltip align="start" announced for="${this._buttonId}" for-type="descriptor">${this._renderTooltipContent()}</d2l-tooltip>` : ''}
334
302
  `;
335
303
  }
336
304
 
@@ -438,6 +438,66 @@
438
438
  }
439
439
  ]
440
440
  },
441
+ {
442
+ "name": "d2l-button-move",
443
+ "path": "./components/button/button-move.js",
444
+ "description": "A button component that provides a move action via a single button.",
445
+ "attributes": [
446
+ {
447
+ "name": "description",
448
+ "description": "A description to be added to the button for accessibility when text on button does not provide enough context",
449
+ "type": "string"
450
+ },
451
+ {
452
+ "name": "disabled-tooltip",
453
+ "description": "Tooltip text when disabled",
454
+ "type": "string"
455
+ },
456
+ {
457
+ "name": "text",
458
+ "description": "REQUIRED: Accessible text for the button",
459
+ "type": "string"
460
+ },
461
+ {
462
+ "name": "disabled",
463
+ "description": "Disables the button",
464
+ "type": "boolean",
465
+ "default": "false"
466
+ }
467
+ ],
468
+ "properties": [
469
+ {
470
+ "name": "description",
471
+ "attribute": "description",
472
+ "description": "A description to be added to the button for accessibility when text on button does not provide enough context",
473
+ "type": "string"
474
+ },
475
+ {
476
+ "name": "disabledTooltip",
477
+ "attribute": "disabled-tooltip",
478
+ "description": "Tooltip text when disabled",
479
+ "type": "string"
480
+ },
481
+ {
482
+ "name": "text",
483
+ "attribute": "text",
484
+ "description": "REQUIRED: Accessible text for the button",
485
+ "type": "string"
486
+ },
487
+ {
488
+ "name": "disabled",
489
+ "attribute": "disabled",
490
+ "description": "Disables the button",
491
+ "type": "boolean",
492
+ "default": "false"
493
+ }
494
+ ],
495
+ "events": [
496
+ {
497
+ "name": "d2l-button-move-action"
498
+ }
499
+ ]
500
+ },
441
501
  {
442
502
  "name": "d2l-button-subtle",
443
503
  "path": "./components/button/button-subtle.js",