Skip to content

Commit f1032a6

Browse files
authored
refactor: Abstracted ElementInternals API into controller (#1749)
1 parent f077a2b commit f1032a6

File tree

23 files changed

+293
-237
lines changed

23 files changed

+293
-237
lines changed

src/components/avatar/avatar.ts

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { html, LitElement, nothing } from 'lit';
1+
import { html, LitElement, nothing, type PropertyValues } from 'lit';
22
import { property, state } from 'lit/decorators.js';
33
import { ifDefined } from 'lit/directives/if-defined.js';
4-
54
import { themes } from '../../theming/theming-decorator.js';
6-
import { watch } from '../common/decorators/watch.js';
5+
import { addInternalsController } from '../common/controllers/internals.js';
76
import { registerComponent } from '../common/definitions/register.js';
87
import type { AvatarShape } from '../types.js';
98
import { styles } from './themes/avatar.base.css.js';
@@ -29,35 +28,40 @@ export default class IgcAvatarComponent extends LitElement {
2928
public static override styles = [styles, shared];
3029

3130
/* blazorSuppress */
32-
public static register() {
31+
public static register(): void {
3332
registerComponent(IgcAvatarComponent);
3433
}
3534

36-
private __internals: ElementInternals;
35+
private readonly _internals = addInternalsController(this, {
36+
initialARIA: {
37+
role: 'image',
38+
ariaLabel: 'avatar',
39+
},
40+
});
3741

3842
@state()
39-
private hasError = false;
43+
private _hasError = false;
4044

4145
/**
4246
* The image source to use.
4347
* @attr
4448
*/
4549
@property()
46-
public src!: string;
50+
public src?: string;
4751

4852
/**
4953
* Alternative text for the image.
5054
* @attr
5155
*/
5256
@property()
53-
public alt!: string;
57+
public alt?: string;
5458

5559
/**
5660
* Initials to use as a fallback when no image is available.
5761
* @attr
5862
*/
5963
@property()
60-
public initials!: string;
64+
public initials?: string;
6165

6266
/**
6367
* The shape of the avatar.
@@ -66,27 +70,20 @@ export default class IgcAvatarComponent extends LitElement {
6670
@property({ reflect: true })
6771
public shape: AvatarShape = 'square';
6872

69-
constructor() {
70-
super();
71-
72-
this.__internals = this.attachInternals();
73-
this.__internals.role = 'img';
74-
this.__internals.ariaLabel = 'avatar';
75-
}
76-
77-
@watch('initials')
78-
@watch('alt')
79-
protected roleDescriptionChange() {
80-
this.__internals.ariaRoleDescription = this.alt ?? this.initials;
81-
}
73+
protected override willUpdate(changedProperties: PropertyValues<this>): void {
74+
if (changedProperties.has('initials') || changedProperties.has('alt')) {
75+
this._internals.setARIA({
76+
ariaRoleDescription: this.alt ?? this.initials,
77+
});
78+
}
8279

83-
@watch('src')
84-
protected handleErrorState() {
85-
this.hasError = false;
80+
if (changedProperties.has('src')) {
81+
this._hasError = false;
82+
}
8683
}
8784

88-
protected handleError() {
89-
this.hasError = true;
85+
protected _handleError(): void {
86+
this._hasError = true;
9087
}
9188

9289
protected override render() {
@@ -95,13 +92,13 @@ export default class IgcAvatarComponent extends LitElement {
9592
${this.initials
9693
? html`<span part="initials">${this.initials}</span>`
9794
: html`<slot></slot>`}
98-
${this.src && !this.hasError
95+
${this.src && !this._hasError
9996
? html`
10097
<img
10198
part="image"
10299
alt=${ifDefined(this.alt)}
103100
src=${ifDefined(this.src)}
104-
@error=${this.handleError}
101+
@error=${this._handleError}
105102
/>
106103
`
107104
: nothing}

src/components/badge/badge.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { html, LitElement } from 'lit';
1+
import { html, LitElement, type PropertyValues } from 'lit';
22
import { property } from 'lit/decorators.js';
3-
43
import { themes } from '../../theming/theming-decorator.js';
5-
import { watch } from '../common/decorators/watch.js';
4+
import { addInternalsController } from '../common/controllers/internals.js';
65
import { registerComponent } from '../common/definitions/register.js';
76
import type { BadgeShape, StyleVariant } from '../types.js';
87
import { styles } from './themes/badge.base.css.js';
@@ -25,11 +24,13 @@ export default class IgcBadgeComponent extends LitElement {
2524
public static override styles = [styles, shared];
2625

2726
/* blazorSuppress */
28-
public static register() {
27+
public static register(): void {
2928
registerComponent(IgcBadgeComponent);
3029
}
3130

32-
private __internals: ElementInternals;
31+
private readonly _internals = addInternalsController(this, {
32+
initialARIA: { role: 'status' },
33+
});
3334

3435
/**
3536
* The type of badge.
@@ -52,15 +53,10 @@ export default class IgcBadgeComponent extends LitElement {
5253
@property({ reflect: true })
5354
public shape: BadgeShape = 'rounded';
5455

55-
constructor() {
56-
super();
57-
this.__internals = this.attachInternals();
58-
this.__internals.role = 'status';
59-
}
60-
61-
@watch('variant')
62-
protected variantChange() {
63-
this.__internals.ariaRoleDescription = `badge ${this.variant}`;
56+
protected override willUpdate(changedProperties: PropertyValues<this>): void {
57+
if (changedProperties.has('variant')) {
58+
this._internals.setARIA({ ariaRoleDescription: `badge ${this.variant}` });
59+
}
6460
}
6561

6662
protected override render() {

src/components/banner/banner.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { addAnimationController } from '../../animations/player.js';
66
import { growVerIn, growVerOut } from '../../animations/presets/grow/index.js';
77
import { themes } from '../../theming/theming-decorator.js';
88
import IgcButtonComponent from '../button/button.js';
9+
import { addInternalsController } from '../common/controllers/internals.js';
910
import { registerComponent } from '../common/definitions/register.js';
1011
import type { Constructor } from '../common/mixins/constructor.js';
1112
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
@@ -46,13 +47,15 @@ export default class IgcBannerComponent extends EventEmitterMixin<
4647
public static styles = [styles];
4748

4849
/* blazorSuppress */
49-
public static register() {
50+
public static register(): void {
5051
registerComponent(IgcBannerComponent, IgcButtonComponent);
5152
}
5253

53-
private _internals: ElementInternals;
54-
private _bannerRef: Ref<HTMLElement> = createRef();
55-
private _animationPlayer = addAnimationController(this, this._bannerRef);
54+
private readonly _bannerRef: Ref<HTMLElement> = createRef();
55+
private readonly _animationPlayer = addAnimationController(
56+
this,
57+
this._bannerRef
58+
);
5659

5760
/**
5861
* Determines whether the banner is being shown/hidden.
@@ -63,10 +66,13 @@ export default class IgcBannerComponent extends EventEmitterMixin<
6366

6467
constructor() {
6568
super();
66-
this._internals = this.attachInternals();
6769

68-
this._internals.role = 'status';
69-
this._internals.ariaLive = 'polite';
70+
addInternalsController(this, {
71+
initialARIA: {
72+
role: 'status',
73+
ariaLive: 'polite',
74+
},
75+
});
7076
}
7177

7278
/** Shows the banner if not already shown. Returns `true` when the animation has completed. */

src/components/button/button-base.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { html, LitElement, nothing, type TemplateResult } from 'lit';
22
import { property, query } from 'lit/decorators.js';
33
import { ifDefined } from 'lit/directives/if-defined.js';
44
import { addKeyboardFocusRing } from '../common/controllers/focus-ring.js';
5+
import { addInternalsController } from '../common/controllers/internals.js';
56
import { blazorDeepImport } from '../common/decorators/blazorDeepImport.js';
67
import type { Constructor } from '../common/mixins/constructor.js';
78
import { EventEmitterMixin } from '../common//mixins/event-emitter.js';
@@ -27,8 +28,7 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
2728
delegatesFocus: true,
2829
};
2930

30-
protected readonly __internals: ElementInternals;
31-
31+
protected readonly _internals = addInternalsController(this);
3232
private readonly _focusRingManager = addKeyboardFocusRing(this);
3333

3434
protected _disabled = false;
@@ -49,14 +49,14 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
4949
* @attr
5050
*/
5151
@property()
52-
public href!: string;
52+
public href?: string;
5353

5454
/**
5555
* Prompts to save the linked URL instead of navigating to it.
5656
* @attr
5757
*/
5858
@property()
59-
public download!: string;
59+
public download?: string;
6060

6161
/**
6262
* Where to display the linked URL, as the name for a browsing context.
@@ -71,7 +71,7 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
7171
* @attr
7272
*/
7373
@property()
74-
public rel!: string;
74+
public rel?: string;
7575

7676
/**
7777
* The disabled state of the component
@@ -91,12 +91,7 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
9191
/* alternateType: object */
9292
/** Returns the HTMLFormElement associated with this element. */
9393
public get form(): HTMLFormElement | null {
94-
return this.__internals.form;
95-
}
96-
97-
constructor() {
98-
super();
99-
this.__internals = this.attachInternals();
94+
return this._internals.form;
10095
}
10196

10297
/* alternateName: focusComponent */

src/components/button/button.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default class IgcButtonComponent extends IgcButtonBaseComponent {
2929
protected static styles = [styles, shared];
3030

3131
/* blazorSuppress */
32-
public static register() {
32+
public static register(): void {
3333
registerComponent(IgcButtonComponent);
3434
}
3535

src/components/button/icon-button.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default class IgcIconButtonComponent extends IgcButtonBaseComponent {
2828
protected static styles = [styles, shared];
2929

3030
/* blazorSuppress */
31-
public static register() {
31+
public static register(): void {
3232
registerComponent(IgcIconButtonComponent, IgcIconComponent);
3333
}
3434

@@ -38,14 +38,14 @@ export default class IgcIconButtonComponent extends IgcButtonBaseComponent {
3838
* @attr
3939
*/
4040
@property()
41-
public name!: string;
41+
public name?: string;
4242

4343
/**
4444
* The name of the icon collection.
4545
* @attr
4646
*/
4747
@property()
48-
public collection!: string;
48+
public collection?: string;
4949

5050
/**
5151
* Whether to flip the icon button. Useful for RTL layouts.

src/components/carousel/carousel-indicator.ts

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { consume } from '@lit/context';
2-
import { html, LitElement } from 'lit';
2+
import { html, LitElement, type PropertyValues } from 'lit';
33
import { property } from 'lit/decorators.js';
44
import { styleMap } from 'lit/directives/style-map.js';
55
import { carouselContext } from '../common/context.js';
6+
import { addInternalsController } from '../common/controllers/internals.js';
67
import { registerComponent } from '../common/definitions/register.js';
78
import { formatString } from '../common/util.js';
89
import type IgcCarouselComponent from './carousel.js';
@@ -25,51 +26,45 @@ export default class IgcCarouselIndicatorComponent extends LitElement {
2526
public static override styles = styles;
2627

2728
/* blazorSuppress */
28-
public static register() {
29+
public static register(): void {
2930
registerComponent(IgcCarouselIndicatorComponent);
3031
}
3132

32-
private _active = false;
33-
private _internals: ElementInternals;
33+
private readonly _internals = addInternalsController(this, {
34+
initialARIA: { role: 'tab' },
35+
});
3436

3537
@consume({ context: carouselContext, subscribe: true })
3638
private _carousel?: IgcCarouselComponent;
3739

38-
protected get _labelFormat() {
40+
protected get _labelFormat(): string {
3941
return this._carousel ? this._carousel.indicatorsLabelFormat : '';
4042
}
4143

4244
/* blazorSuppress */
4345
@property({ attribute: false })
44-
public set active(value: boolean) {
45-
this._active = Boolean(value);
46-
this.tabIndex = this._active ? 0 : -1;
47-
this._internals.ariaSelected = this._active.toString();
48-
}
49-
50-
public get active(): boolean {
51-
return this._active;
52-
}
46+
public active = false;
5347

5448
/* blazorSuppress */
5549
@property({ attribute: false })
5650
public index = 0;
5751

58-
constructor() {
59-
super();
60-
61-
this._internals = this.attachInternals();
62-
this._internals.role = 'tab';
63-
}
64-
65-
public override connectedCallback() {
52+
/** @internal */
53+
public override connectedCallback(): void {
6654
super.connectedCallback();
6755
this.role = 'tab';
6856
this.slot = this.slot || 'indicator';
6957
}
7058

71-
protected override willUpdate(): void {
72-
this._internals.ariaLabel = formatString(this._labelFormat, this.index + 1);
59+
protected override willUpdate(changedProperties: PropertyValues<this>): void {
60+
if (changedProperties.has('active')) {
61+
this.tabIndex = this.active ? 0 : -1;
62+
this._internals.setARIA({ ariaSelected: this.active.toString() });
63+
}
64+
65+
this._internals.setARIA({
66+
ariaLabel: formatString(this._labelFormat, this.index + 1),
67+
});
7368
}
7469

7570
protected override render() {

0 commit comments

Comments
 (0)