@brightspace-ui/core 2.165.0 → 2.167.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.
@@ -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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "2.165.0",
3
+ "version": "2.167.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",
@@ -1,28 +0,0 @@
1
- import { getComposedChildren } from '../helpers/dom.js';
2
-
3
- export function keyDown(element, keyCode) {
4
- const event = new CustomEvent('keydown', {
5
- detail: 0,
6
- bubbles: true,
7
- cancelable: true,
8
- composed: true
9
- });
10
- event.keyCode = keyCode;
11
- event.code = keyCode;
12
- element.dispatchEvent(event);
13
- }
14
-
15
- export async function getUpdateCompleteAll(node) {
16
-
17
- if (!node || (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.DOCUMENT_NODE && node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE)) {
18
- throw new TypeError('Invalid node. Must be nodeType document, element or document fragment');
19
- }
20
-
21
- const getUpdateComplete = async elem => {
22
- if (elem.updateComplete) await elem.updateComplete;
23
- const childElements = getComposedChildren(elem);
24
- await Promise.all(childElements.map(childElement => getUpdateComplete(childElement)));
25
- };
26
-
27
- return getUpdateComplete(node);
28
- }