@brightspace-ui/core 2.164.0 → 2.166.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.
@@ -0,0 +1,127 @@
1
+ import '../colors/colors.js';
2
+ import '../icons/icon.js';
3
+ import '../tooltip/tooltip.js';
4
+ import { css, html, LitElement, unsafeCSS } from 'lit';
5
+ import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
6
+ import { getFocusPseudoClass } from '../../helpers/focus.js';
7
+ import { getUniqueId } from '../../helpers/uniqueId.js';
8
+ import { ifDefined } from 'lit/directives/if-defined.js';
9
+ import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
10
+ import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
11
+
12
+ /**
13
+ * A component for quickly adding items to a specific locaiton.
14
+ */
15
+ class ButtonAdd extends PropertyRequiredMixin(FocusMixin(LocalizeCoreElement(LitElement))) {
16
+ static get properties() {
17
+ return {
18
+ /**
19
+ * When text-visible is true, the text to show in the button. When text-visible is false, the text to show in the tooltip.
20
+ * @type {string}
21
+ */
22
+ text: { type: String, required: true },
23
+ /**
24
+ * When true, show the button with icon and visible text. When false, only show icon.
25
+ * @type {boolean}
26
+ */
27
+ textVisible: { type: Boolean, reflect: true, attribute: 'text-visible' }
28
+ };
29
+ }
30
+
31
+ static get styles() {
32
+ return css`
33
+ :host {
34
+ --d2l-button-add-line-style: solid;
35
+ }
36
+ button {
37
+ align-items: center;
38
+ background-color: transparent;
39
+ border: 0;
40
+ box-shadow: none;
41
+ cursor: pointer;
42
+ display: flex;
43
+ font-family: inherit;
44
+ justify-content: center;
45
+ outline: none;
46
+ padding: 0;
47
+ position: relative;
48
+ user-select: none;
49
+ white-space: nowrap;
50
+ width: 100%;
51
+ }
52
+
53
+ .line {
54
+ border-top: 1px var(--d2l-button-add-line-style) var(--d2l-color-mica);
55
+ margin: 3px 0; /** hover/click target */
56
+ width: 100%;
57
+ }
58
+ button:hover .line,
59
+ button:${unsafeCSS(getFocusPseudoClass())} .line {
60
+ border-top-color: var(--d2l-color-celestine);
61
+ }
62
+
63
+ .content {
64
+ align-items: center;
65
+ background-color: white;
66
+ display: flex;
67
+ position: absolute;
68
+ }
69
+ :host([text-visible]) .content {
70
+ color: var(--d2l-color-celestine);
71
+ height: 1.5rem;
72
+ padding: 0 0.3rem;
73
+ }
74
+
75
+ :host([text-visible]) d2l-icon,
76
+ :host(:not([text-visible])) button:hover d2l-icon,
77
+ :host(:not([text-visible])) button:${unsafeCSS(getFocusPseudoClass())} d2l-icon {
78
+ color: var(--d2l-color-celestine);
79
+ }
80
+ :host(:not([text-visible])) d2l-icon {
81
+ color: var(--d2l-color-galena);
82
+ margin: -3px; /** hover/click target */
83
+ padding: 3px; /** hover/click target */
84
+ }
85
+ :host([text-visible]) d2l-icon {
86
+ padding-inline-end: 0.2rem;
87
+ }
88
+
89
+ span {
90
+ font-size: 0.7rem;
91
+ font-weight: 700;
92
+ letter-spacing: 0.2px;
93
+ line-height: 1rem;
94
+ }
95
+ `;
96
+ }
97
+
98
+ constructor() {
99
+ super();
100
+
101
+ this.textVisible = false;
102
+ this._buttonId = getUniqueId();
103
+ }
104
+
105
+ static get focusElementSelector() {
106
+ return 'button';
107
+ }
108
+
109
+ render() {
110
+ const text = this.text || this.localize('components.button-add.addItem');
111
+ const id = !this.textVisible ? this._buttonId : undefined;
112
+
113
+ return html`
114
+ <button class="d2l-label-text" id="${ifDefined(id)}">
115
+ <div class="line"></div>
116
+ <div class="content">
117
+ <d2l-icon icon="tier1:plus-default"></d2l-icon>
118
+ ${this.textVisible
119
+ ? html`<span>${text}</span>`
120
+ : html`<d2l-tooltip class="vdiff-target" offset="18" for="${this._buttonId}" for-type="label">${text}</d2l-tooltip>`}
121
+ </div>
122
+ </button>
123
+ `;
124
+ }
125
+ }
126
+ customElements.define('d2l-button-add', ButtonAdd);
127
+
@@ -0,0 +1,73 @@
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-add.js';
10
+ </script>
11
+ <style>
12
+ .d2l-button-add-dashed {
13
+ --d2l-button-add-line-style: dashed;
14
+ }
15
+ </style>
16
+ </head>
17
+ <body unresolved>
18
+
19
+ <d2l-demo-page page-title="d2l-button-add">
20
+
21
+ <h2>Add Button</h2>
22
+
23
+ <d2l-demo-snippet>
24
+ <template>
25
+ <d2l-button-add></d2l-button-add>
26
+ </template>
27
+ </d2l-demo-snippet>
28
+
29
+ <h2>Add Button, dashed line</h2>
30
+
31
+ <d2l-demo-snippet>
32
+ <template>
33
+ <d2l-button-add class="d2l-button-add-dashed"></d2l-button-add>
34
+ </template>
35
+ </d2l-demo-snippet>
36
+
37
+ <h2>Add Button, custom text</h2>
38
+
39
+ <d2l-demo-snippet>
40
+ <template>
41
+ <d2l-button-add text="Custom Tooltip"></d2l-button-add>
42
+ </template>
43
+ </d2l-demo-snippet>
44
+
45
+
46
+ <h2>Add Button, text-visible</h2>
47
+
48
+ <d2l-demo-snippet>
49
+ <template>
50
+ <d2l-button-add text-visible></d2l-button-add>
51
+ </template>
52
+ </d2l-demo-snippet>
53
+
54
+
55
+ <h2>Add Button, text-visible, custom text</h2>
56
+
57
+ <d2l-demo-snippet>
58
+ <template>
59
+ <d2l-button-add text-visible text="Custom Text"></d2l-button-add>
60
+ </template>
61
+ </d2l-demo-snippet>
62
+
63
+ </d2l-demo-page>
64
+
65
+ <script>
66
+ document.addEventListener('click', e => {
67
+ if (e.target.tagName !== 'D2L-BUTTON-ADD') return;
68
+ console.log('add button clicked', e.target);
69
+ });
70
+ </script>
71
+
72
+ </body>
73
+ </html>
@@ -2,13 +2,14 @@ import '../button/button.js';
2
2
  import '../button/button-subtle.js';
3
3
  import { css, html, LitElement, nothing } from 'lit';
4
4
  import { ifDefined } from 'lit/directives/if-defined.js';
5
+ import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
5
6
 
6
7
  /**
7
8
  * `d2l-empty-state-action-button` is an empty state action component that can be placed inside of the default slot of `empty-state-simple` or `empty-state-illustrated` to add a button action to the component.
8
9
  * @fires d2l-empty-state-action - Dispatched when the action button is clicked
9
10
  * @fires d2l-empty-state-illustrated-check - Internal event
10
11
  */
11
- class EmptyStateActionButton extends LitElement {
12
+ class EmptyStateActionButton extends PropertyRequiredMixin(LitElement) {
12
13
 
13
14
  static get properties() {
14
15
  return {
@@ -16,7 +17,7 @@ class EmptyStateActionButton extends LitElement {
16
17
  * REQUIRED: The action text to be used in the button
17
18
  * @type {string}
18
19
  */
19
- text: { type: String },
20
+ text: { type: String, required: true },
20
21
  /**
21
22
  * This will change the action button to use a primary button instead of the default subtle button. The primary attribute is only valid when `d2l-empty-state-action-button` is placed within `d2l-empty-state-illustrated` components
22
23
  * @type {boolean}
@@ -37,8 +38,6 @@ class EmptyStateActionButton extends LitElement {
37
38
  constructor() {
38
39
  super();
39
40
  this._illustrated = false;
40
- this._missingTextErrorHasBeenThrown = false;
41
- this._validatingTextTimeout = null;
42
41
  }
43
42
 
44
43
  connectedCallback() {
@@ -53,11 +52,6 @@ class EmptyStateActionButton extends LitElement {
53
52
  });
54
53
  }
55
54
 
56
- firstUpdated(changedProperties) {
57
- super.firstUpdated(changedProperties);
58
- this._validateText();
59
- }
60
-
61
55
  render() {
62
56
  let actionButton = nothing;
63
57
  if (this.text) {
@@ -84,20 +78,6 @@ class EmptyStateActionButton extends LitElement {
84
78
  this.dispatchEvent(new CustomEvent('d2l-empty-state-action'));
85
79
  }
86
80
 
87
- _validateText() {
88
- clearTimeout(this._validatingTextTimeout);
89
- // don't error immediately in case it doesn't get set immediately
90
- this._validatingTextTimeout = setTimeout(() => {
91
- this._validatingTextTimeout = null;
92
- const hasText = (typeof this.text === 'string') && this.text.length > 0;
93
-
94
- if (!hasText && !this._missingTextErrorHasBeenThrown) {
95
- this._missingTextErrorHasBeenThrown = true;
96
- setTimeout(() => { throw new Error('<d2l-empty-state-action-button>: missing required "text" attribute.'); });
97
- }
98
- }, 3000);
99
- }
100
-
101
81
  }
102
82
 
103
83
  customElements.define('d2l-empty-state-action-button', EmptyStateActionButton);
@@ -1,11 +1,12 @@
1
1
  import { html, LitElement, nothing } from 'lit';
2
2
  import { bodyCompactStyles } from '../typography/styles.js';
3
3
  import { linkStyles } from '../link/link.js';
4
+ import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
4
5
 
5
6
  /**
6
7
  * `d2l-empty-state-action-link` is an empty state action component that can be placed inside of the default slot of `empty-state-simple` or `empty-state-illustrated` to add a link action to the component.
7
8
  */
8
- class EmptyStateActionLink extends LitElement {
9
+ class EmptyStateActionLink extends PropertyRequiredMixin(LitElement) {
9
10
 
10
11
  static get properties() {
11
12
  return {
@@ -13,12 +14,12 @@ class EmptyStateActionLink extends LitElement {
13
14
  * REQUIRED: The action text to be used in the subtle button
14
15
  * @type {string}
15
16
  */
16
- text: { type: String },
17
+ text: { type: String, required: true },
17
18
  /**
18
19
  * REQUIRED: The action URL or URL fragment of the link
19
20
  * @type {string}
20
21
  */
21
- href: { type: String },
22
+ href: { type: String, required: true },
22
23
  };
23
24
  }
24
25
 
@@ -26,18 +27,6 @@ class EmptyStateActionLink extends LitElement {
26
27
  return [bodyCompactStyles, linkStyles];
27
28
  }
28
29
 
29
- constructor() {
30
- super();
31
- this._missingHrefErrorHasBeenThrown = false;
32
- this._missingTextErrorHasBeenThrown = false;
33
- this._validatingAttributesTimeout = null;
34
- }
35
-
36
- firstUpdated(changedProperties) {
37
- super.firstUpdated(changedProperties);
38
- this._validateAttributes();
39
- }
40
-
41
30
  render() {
42
31
  const actionLink = this.text && this.href
43
32
  ? html`
@@ -47,27 +36,6 @@ class EmptyStateActionLink extends LitElement {
47
36
  return html`${actionLink}`;
48
37
  }
49
38
 
50
- _validateAttributes() {
51
- clearTimeout(this._validatingAttributesTimeout);
52
- // don't error immediately in case it doesn't get set immediately
53
- this._validatingAttributesTimeout = setTimeout(() => {
54
- this._validatingAttributesTimeout = null;
55
-
56
- const hasHref = (typeof this.href === 'string') && this.href.length > 0;
57
- const hasText = (typeof this.text === 'string') && this.text.length > 0;
58
-
59
- if (!hasHref && !this._missingHrefErrorHasBeenThrown) {
60
- this._missingHrefErrorHasBeenThrown = true;
61
- setTimeout(() => { throw new Error('<d2l-empty-state-action-link>: missing required "href" attribute.'); });
62
- }
63
-
64
- if (!hasText && !this._missingTextErrorHasBeenThrown) {
65
- this._missingTextErrorHasBeenThrown = true;
66
- setTimeout(() => { throw new Error('<d2l-empty-state-action-link>: missing required "text" attribute.'); });
67
- }
68
- }, 3000);
69
- }
70
-
71
39
  }
72
40
 
73
41
  customElements.define('d2l-empty-state-action-link', EmptyStateActionLink);
@@ -3,6 +3,7 @@ import { html, LitElement, nothing } from 'lit';
3
3
  import { bodyCompactStyles } from '../typography/styles.js';
4
4
  import { classMap } from 'lit/directives/class-map.js';
5
5
  import { loadSvg } from '../../generated/empty-state/presetIllustrationLoader.js';
6
+ import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
6
7
  import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
7
8
  import { runAsync } from '../../directives/run-async/run-async.js';
8
9
  import { styleMap } from 'lit/directives/style-map.js';
@@ -15,7 +16,7 @@ const illustrationAspectRatio = 500 / 330;
15
16
  * @slot - Slot for empty state actions
16
17
  * @slot illustration - Slot for custom SVG content if `illustration-name` property is not set
17
18
  */
18
- class EmptyStateIllustrated extends LitElement {
19
+ class EmptyStateIllustrated extends PropertyRequiredMixin(LitElement) {
19
20
 
20
21
  static get properties() {
21
22
  return {
@@ -23,7 +24,7 @@ class EmptyStateIllustrated extends LitElement {
23
24
  * REQUIRED: A description giving details about the empty state
24
25
  * @type {string}
25
26
  */
26
- description: { type: String },
27
+ description: { type: String, required: true },
27
28
  /**
28
29
  * The name of the preset image you would like to display in the component
29
30
  * @type {string}
@@ -33,7 +34,7 @@ class EmptyStateIllustrated extends LitElement {
33
34
  * REQUIRED: A title for the empty state
34
35
  * @type {string}
35
36
  */
36
- titleText: { type: String, attribute: 'title-text' },
37
+ titleText: { type: String, attribute: 'title-text', required: true },
37
38
  _contentHeight: { state: true },
38
39
  _titleSmall: { state: true }
39
40
  };
@@ -46,11 +47,8 @@ class EmptyStateIllustrated extends LitElement {
46
47
  constructor() {
47
48
  super();
48
49
  this._contentHeight = 330;
49
- this._missingDescriptionErrorHasBeenThrown = false;
50
- this._missingTitleTextErrorHasBeenThrown = false;
51
50
  this._resizeObserver = new ResizeObserver(this._onResize.bind(this));
52
51
  this._titleSmall = false;
53
- this._validatingAttributesTimeout = null;
54
52
  }
55
53
 
56
54
  connectedCallback() {
@@ -65,11 +63,6 @@ class EmptyStateIllustrated extends LitElement {
65
63
  this._resizeObserver.disconnect();
66
64
  }
67
65
 
68
- firstUpdated(changedProperties) {
69
- super.firstUpdated(changedProperties);
70
- this._validateAttributes();
71
- }
72
-
73
66
  render() {
74
67
  const illustrationContainerStyle = this._getIllustrationContainerStyle();
75
68
  const titleClass = this._getTitleClass();
@@ -126,30 +119,6 @@ class EmptyStateIllustrated extends LitElement {
126
119
  });
127
120
  }
128
121
 
129
- _validateAttributes() {
130
- clearTimeout(this._validatingAttributesTimeout);
131
- // don't error immediately in case it doesn't get set immediately
132
- this._validatingAttributesTimeout = setTimeout(() => {
133
- this._validatingAttributesTimeout = null;
134
- const hasTitleText = (typeof this.titleText === 'string') && this.titleText.length > 0;
135
- const hasDescription = (typeof this.description === 'string') && this.description.length > 0;
136
-
137
- if (!hasTitleText && !this._missingTitleTextErrorHasBeenThrown) {
138
- this._missingTitleTextErrorHasBeenThrown = true;
139
- setTimeout(() => {
140
- throw new Error('<d2l-empty-state-illustrated>: missing required "titleText" attribute.');
141
- });
142
- }
143
-
144
- if (!hasDescription && !this._missingDescriptionErrorHasBeenThrown) {
145
- this._missingDescriptionErrorHasBeenThrown = true;
146
- setTimeout(() => {
147
- throw new Error('<d2l-empty-state-illustrated>: missing required "description" attribute.');
148
- });
149
- }
150
- }, 3000);
151
- }
152
-
153
122
  }
154
123
 
155
124
  customElements.define('d2l-empty-state-illustrated', EmptyStateIllustrated);
@@ -2,13 +2,14 @@ import '../button/button-subtle.js';
2
2
  import { emptyStateSimpleStyles, emptyStateStyles } from './empty-state-styles.js';
3
3
  import { html, LitElement } from 'lit';
4
4
  import { bodyCompactStyles } from '../typography/styles.js';
5
+ import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
5
6
  import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
6
7
 
7
8
  /**
8
9
  * The `d2l-empty-state-simple` component is an empty state component that displays a description. An empty state action component can be placed inside of the default slot to add an optional action.
9
10
  * @slot - Slot for empty state actions
10
11
  */
11
- class EmptyStateSimple extends RtlMixin(LitElement) {
12
+ class EmptyStateSimple extends PropertyRequiredMixin(RtlMixin(LitElement)) {
12
13
 
13
14
  static get properties() {
14
15
  return {
@@ -16,7 +17,7 @@ class EmptyStateSimple extends RtlMixin(LitElement) {
16
17
  * REQUIRED: A description giving details about the empty state
17
18
  * @type {string}
18
19
  */
19
- description: { type: String },
20
+ description: { type: String, required: true },
20
21
  };
21
22
  }
22
23
 
@@ -24,17 +25,6 @@ class EmptyStateSimple extends RtlMixin(LitElement) {
24
25
  return [bodyCompactStyles, emptyStateStyles, emptyStateSimpleStyles];
25
26
  }
26
27
 
27
- constructor() {
28
- super();
29
- this._missingDescriptionErrorHasBeenThrown = false;
30
- this._validatingDescriptionTimeout = null;
31
- }
32
-
33
- firstUpdated(changedProperties) {
34
- super.firstUpdated(changedProperties);
35
- this._validateDescription();
36
- }
37
-
38
28
  render() {
39
29
  return html`
40
30
  <p class="d2l-body-compact d2l-empty-state-description">${this.description}</p>
@@ -42,20 +32,6 @@ class EmptyStateSimple extends RtlMixin(LitElement) {
42
32
  `;
43
33
  }
44
34
 
45
- _validateDescription() {
46
- clearTimeout(this._validatingDescriptionTimeout);
47
- // don't error immediately in case it doesn't get set immediately
48
- this._validatingDescriptionTimeout = setTimeout(() => {
49
- this._validatingDescriptionTimeout = null;
50
- const hasDescription = (typeof this.description === 'string') && this.description.length > 0;
51
-
52
- if (!hasDescription && !this._missingDescriptionErrorHasBeenThrown) {
53
- this._missingDescriptionErrorHasBeenThrown = true;
54
- setTimeout(() => { throw new Error('<d2l-empty-state-simple>: missing required "description" attribute.'); });
55
- }
56
- }, 3000);
57
- }
58
-
59
35
  }
60
36
 
61
37
  customElements.define('d2l-empty-state-simple', EmptyStateSimple);
@@ -12,6 +12,7 @@ import { inputStyles } from './input-styles.js';
12
12
  import { LabelledMixin } from '../../mixins/labelled/labelled-mixin.js';
13
13
  import { offscreenStyles } from '../offscreen/offscreen.js';
14
14
  import { PerfMonitor } from '../../helpers/perfMonitor.js';
15
+ import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
15
16
  import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
16
17
  import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
17
18
  import { styleMap } from 'lit/directives/style-map.js';
@@ -24,7 +25,7 @@ import { styleMap } from 'lit/directives/style-map.js';
24
25
  * @fires change - Dispatched when an alteration to the value is committed (typically after focus is lost) by the user
25
26
  * @fires input - Dispatched immediately after changes by the user
26
27
  */
27
- class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(RtlMixin(LitElement))))) {
28
+ class InputText extends PropertyRequiredMixin(FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(RtlMixin(LitElement)))))) {
28
29
 
29
30
  static get properties() {
30
31
  return {
@@ -152,7 +153,18 @@ class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(
152
153
  * Accessible label for the unit which will not be visually rendered
153
154
  * @type {string}
154
155
  */
155
- unitLabel: { attribute: 'unit-label', type: String },
156
+ unitLabel: {
157
+ attribute: 'unit-label',
158
+ required: {
159
+ dependentProps: ['unit'],
160
+ message: (_value, elem) => `<d2l-input-text>: missing required attribute "unit-label" for unit "${elem.unit}"`,
161
+ validator: (_value, elem, hasValue) => {
162
+ const hasUnit = (typeof elem.unit === 'string') && elem.unit.length > 0;
163
+ return !(hasUnit && elem.unit !== '%' && !hasValue);
164
+ }
165
+ },
166
+ type: String
167
+ },
156
168
  /**
157
169
  * Value of the input
158
170
  * @type {string}
@@ -270,7 +282,6 @@ class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(
270
282
  this._intersectionObserver = null;
271
283
  this._isIntersecting = false;
272
284
  this._lastSlotWidth = 0;
273
- this._missingUnitLabelErrorHasBeenThrown = false;
274
285
  this._prevValue = '';
275
286
 
276
287
  this._handleBlur = this._handleBlur.bind(this);
@@ -278,7 +289,6 @@ class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(
278
289
  this._handleMouseEnter = this._handleMouseEnter.bind(this);
279
290
  this._handleMouseLeave = this._handleMouseLeave.bind(this);
280
291
  this._perfMonitor = new PerfMonitor(this);
281
- this._validatingUnitTimeout = null;
282
292
  }
283
293
 
284
294
  get value() { return this._value; }
@@ -355,7 +365,6 @@ class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(
355
365
  super.firstUpdated(changedProperties);
356
366
 
357
367
  this._setValue(this.value, true);
358
- this._validateUnit();
359
368
 
360
369
  const container = this.shadowRoot && this.shadowRoot.querySelector('.d2l-input-text-container');
361
370
  if (!container) return;
@@ -484,7 +493,6 @@ class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(
484
493
  changedProperties.forEach((oldVal, prop) => {
485
494
  if (prop === 'unit' || prop === 'unitLabel') {
486
495
  this._updateInputLayout();
487
- this._validateUnit();
488
496
  } else if (prop === 'validationError') {
489
497
  if (oldVal && this.validationError) {
490
498
  const tooltip = this.shadowRoot.querySelector('d2l-tooltip');
@@ -655,20 +663,5 @@ class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(
655
663
 
656
664
  }
657
665
 
658
- _validateUnit() {
659
- if (this._missingUnitLabelErrorHasBeenThrown) return;
660
- clearTimeout(this._validatingUnitTimeout);
661
- // don't error immediately in case it doesn't get set immediately
662
- this._validatingUnitTimeout = setTimeout(() => {
663
- this._validatingUnitTimeout = null;
664
- const hasUnit = (typeof this.unit === 'string') && this.unit.length > 0;
665
- const hasUnitLabel = (typeof this.unitLabel === 'string') && this.unitLabel.length > 0;
666
- if (hasUnit && this.unit !== '%' && !hasUnitLabel) {
667
- this._missingUnitLabelErrorHasBeenThrown = true;
668
- throw new Error(`<d2l-input-text>: missing required attribute "unit-label" for unit "${this.unit}"`);
669
- }
670
- }, 3000);
671
- }
672
-
673
666
  }
674
667
  customElements.define('d2l-input-text', InputText);
@@ -343,6 +343,43 @@
343
343
  }
344
344
  ]
345
345
  },
346
+ {
347
+ "name": "d2l-button-add",
348
+ "path": "./components/button/button-add.js",
349
+ "description": "A component for quickly adding items to a specific locaiton.",
350
+ "attributes": [
351
+ {
352
+ "name": "text",
353
+ "description": "When text-visible is true, the text to show in the button. When text-visible is false, the text to show in the tooltip.",
354
+ "type": "string"
355
+ },
356
+ {
357
+ "name": "text-visible",
358
+ "description": "When true, show the button with icon and visible text. When false, only show icon.",
359
+ "type": "boolean",
360
+ "default": "false"
361
+ }
362
+ ],
363
+ "properties": [
364
+ {
365
+ "name": "text",
366
+ "attribute": "text",
367
+ "description": "When text-visible is true, the text to show in the button. When text-visible is false, the text to show in the tooltip.",
368
+ "type": "string"
369
+ },
370
+ {
371
+ "name": "textVisible",
372
+ "attribute": "text-visible",
373
+ "description": "When true, show the button with icon and visible text. When false, only show icon.",
374
+ "type": "boolean",
375
+ "default": "false"
376
+ },
377
+ {
378
+ "name": "documentLocaleSettings",
379
+ "default": "\"getDocumentLocaleSettings()\""
380
+ }
381
+ ]
382
+ },
346
383
  {
347
384
  "name": "d2l-button-icon",
348
385
  "path": "./components/button/button-icon.js",
package/lang/ar.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "إغلاق التنبيه",
3
3
  "components.breadcrumbs.breadcrumb": "شريط التنقل",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "لم يتم التحديد.",
5
6
  "components.calendar.selected": "تم التحديد.",
6
7
  "components.calendar.show": "إظهار {month}",
package/lang/cy.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Cau Hysbysiad",
3
3
  "components.breadcrumbs.breadcrumb": "Briwsionyn Bara",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Heb ei Ddewis.",
5
6
  "components.calendar.selected": "Wedi'i Ddewis.",
6
7
  "components.calendar.show": "Dangos {month}",
package/lang/da.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Luk besked",
3
3
  "components.breadcrumbs.breadcrumb": "Brødkrumme",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Ikke valgt.",
5
6
  "components.calendar.selected": "Valgt.",
6
7
  "components.calendar.show": "Vis {month}",
package/lang/de.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Benachrichtigung schließen",
3
3
  "components.breadcrumbs.breadcrumb": "Brotkrümelnavigation",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Nicht ausgewählt.",
5
6
  "components.calendar.selected": "Ausgewählt.",
6
7
  "components.calendar.show": "{month} anzeigen",
package/lang/en-gb.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Close Alert",
3
3
  "components.breadcrumbs.breadcrumb": "Breadcrumb",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Not Selected.",
5
6
  "components.calendar.selected": "Selected.",
6
7
  "components.calendar.show": "Show {month}",
package/lang/en.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Close Alert",
3
3
  "components.breadcrumbs.breadcrumb": "Breadcrumb",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Not Selected.",
5
6
  "components.calendar.selected": "Selected.",
6
7
  "components.calendar.show": "Show {month}",
package/lang/es-es.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Cerrar alerta",
3
3
  "components.breadcrumbs.breadcrumb": "Ruta de navegación",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "No seleccionado.",
5
6
  "components.calendar.selected": "Seleccionado.",
6
7
  "components.calendar.show": "Mostrar {month}",
package/lang/es.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Cerrar alerta",
3
3
  "components.breadcrumbs.breadcrumb": "Ruta de navegación",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "No seleccionado.",
5
6
  "components.calendar.selected": "Seleccionado.",
6
7
  "components.calendar.show": "Mostrar {month}",
package/lang/fr-fr.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Fermer l'alerte",
3
3
  "components.breadcrumbs.breadcrumb": "Chemin de navigation",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Non sélectionné.",
5
6
  "components.calendar.selected": "Sélectionné.",
6
7
  "components.calendar.show": "Afficher {month}",
package/lang/fr.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Fermer l'alerte",
3
3
  "components.breadcrumbs.breadcrumb": "Chemin de navigation",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Non sélectionné(e)",
5
6
  "components.calendar.selected": "Sélectionné(e).",
6
7
  "components.calendar.show": "Afficher {month}",
package/lang/hi.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "अलर्ट बंद करें",
3
3
  "components.breadcrumbs.breadcrumb": "ब्रेडक्रंब",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "चयनित नहीं।",
5
6
  "components.calendar.selected": "चयनित।",
6
7
  "components.calendar.show": "{month} दिखाएँ",
package/lang/ja.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "アラートを閉じる",
3
3
  "components.breadcrumbs.breadcrumb": "階層",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "選択されていません。",
5
6
  "components.calendar.selected": "選択されています。",
6
7
  "components.calendar.show": "{month} を表示",
package/lang/ko.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "경보 닫기",
3
3
  "components.breadcrumbs.breadcrumb": "이동 경로",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "선택되지 않음.",
5
6
  "components.calendar.selected": "선택됨.",
6
7
  "components.calendar.show": "{month} 표시",
package/lang/nl.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Waarschuwing sluiten",
3
3
  "components.breadcrumbs.breadcrumb": "Kruimelpad",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Niet geselecteerd.",
5
6
  "components.calendar.selected": "Geselecteerd.",
6
7
  "components.calendar.show": "{month} weergeven",
package/lang/pt.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Fechar alerta",
3
3
  "components.breadcrumbs.breadcrumb": "Auxiliar de navegação",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Não selecionado.",
5
6
  "components.calendar.selected": "Selecionado.",
6
7
  "components.calendar.show": "Mostrar {month}",
package/lang/sv.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Stängningsvarning",
3
3
  "components.breadcrumbs.breadcrumb": "Sökväg",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Inte vald.",
5
6
  "components.calendar.selected": "Markerad.",
6
7
  "components.calendar.show": "Visa {month}",
package/lang/tr.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "Kapatma Uyarısı",
3
3
  "components.breadcrumbs.breadcrumb": "İçerik Haritası",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "Seçili Değil.",
5
6
  "components.calendar.selected": "Seçili.",
6
7
  "components.calendar.show": "{month} Göster",
package/lang/zh-cn.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "关闭提醒",
3
3
  "components.breadcrumbs.breadcrumb": "痕迹导航",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "未选择。",
5
6
  "components.calendar.selected": "已选择。",
6
7
  "components.calendar.show": "显示 {month}",
package/lang/zh-tw.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  "components.alert.close": "關閉警示",
3
3
  "components.breadcrumbs.breadcrumb": "導覽路徑",
4
+ "components.button-add.addItem": "Add Item",
4
5
  "components.calendar.notSelected": "未選取。",
5
6
  "components.calendar.selected": "已選取。",
6
7
  "components.calendar.show": "顯示{month}",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "2.164.0",
3
+ "version": "2.166.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",