diff --git a/src/components/button-group/toggle-button.ts b/src/components/button-group/toggle-button.ts
index 80e449132..21dee86be 100644
--- a/src/components/button-group/toggle-button.ts
+++ b/src/components/button-group/toggle-button.ts
@@ -30,14 +30,14 @@ export default class IgcToggleButtonComponent extends LitElement {
};
/* blazorSuppress */
- public static register() {
+ public static register(): void {
registerComponent(IgcToggleButtonComponent);
}
- private _kbFocus = addKeyboardFocusRing(this);
+ private readonly _focusRingManager = addKeyboardFocusRing(this);
@query('[part="toggle"]', true)
- private _nativeButton!: HTMLButtonElement;
+ private readonly _nativeButton!: HTMLButtonElement;
/**
* The value attribute of the control.
@@ -62,32 +62,33 @@ export default class IgcToggleButtonComponent extends LitElement {
/* alternateName: focusComponent */
/** Sets focus on the button. */
- public override focus(options?: FocusOptions) {
+ public override focus(options?: FocusOptions): void {
this._nativeButton.focus(options);
}
/* alternateName: blurComponent */
/** Removes focus from the button. */
- public override blur() {
+ public override blur(): void {
this._nativeButton.blur();
}
/** Simulates a mouse click on the element. */
- public override click() {
+ public override click(): void {
this._nativeButton.click();
}
protected override render() {
return html`
diff --git a/src/components/button/button-base.ts b/src/components/button/button-base.ts
index b0ca6c684..99805b745 100644
--- a/src/components/button/button-base.ts
+++ b/src/components/button/button-base.ts
@@ -28,13 +28,14 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
delegatesFocus: true,
};
- private _kbFocus = addKeyboardFocusRing(this);
+ protected readonly __internals: ElementInternals;
+
+ private readonly _focusRingManager = addKeyboardFocusRing(this);
- protected __internals: ElementInternals;
protected _disabled = false;
@query('[part="base"]', true)
- private _nativeButton!: HTMLButtonElement;
+ private readonly _nativeButton!: HTMLButtonElement;
/* alternateName: displayType */
/**
@@ -78,19 +79,19 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
* @attr [disabled=false]
*/
@property({ type: Boolean, reflect: true })
- public get disabled(): boolean {
- return this._disabled;
- }
-
public set disabled(value: boolean) {
this._disabled = value;
this.toggleAttribute('disabled', Boolean(this._disabled));
}
+ public get disabled(): boolean {
+ return this._disabled;
+ }
+
/* blazorCSSuppress */
/* alternateType: object */
/** Returns the HTMLFormElement associated with this element. */
- public get form() {
+ public get form(): HTMLFormElement | null {
return this.__internals.form;
}
@@ -101,38 +102,30 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
/* alternateName: focusComponent */
/** Sets focus in the button. */
- public override focus(options?: FocusOptions) {
+ public override focus(options?: FocusOptions): void {
this._nativeButton.focus(options);
}
/** Simulates a mouse click on the element */
- public override click() {
+ public override click(): void {
this._nativeButton.click();
}
/* alternateName: blurComponent */
/** Removes focus from the button. */
- public override blur() {
+ public override blur(): void {
this._nativeButton.blur();
}
- protected handleBlur() {
- this._kbFocus.reset();
- }
-
- protected handleClick() {
- this._kbFocus.reset();
- switch (this.type) {
- case 'submit':
- return this.form?.requestSubmit();
- case 'reset':
- return this.form?.reset();
- default:
- return;
+ protected _handleClick(): void {
+ if (this.type === 'submit') {
+ this.form?.requestSubmit();
+ } else if (this.type === 'reset') {
+ this.form?.reset();
}
}
- protected formDisabledCallback(state: boolean) {
+ protected formDisabledCallback(state: boolean): void {
this._disabled = state;
this.requestUpdate();
}
@@ -140,12 +133,11 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
private renderButton() {
return html`
@@ -155,7 +147,7 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<
private renderLinkButton() {
return html`
${this.renderContent()}
diff --git a/src/components/carousel/carousel-indicator-container.spec.ts b/src/components/carousel/carousel-indicator-container.spec.ts
index 044a9a50c..dff4ab056 100644
--- a/src/components/carousel/carousel-indicator-container.spec.ts
+++ b/src/components/carousel/carousel-indicator-container.spec.ts
@@ -2,7 +2,12 @@ import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
import { tabKey } from '../common/controllers/key-bindings.js';
import { defineComponents } from '../common/definitions/defineComponents.js';
-import { simulateClick, simulateKeyboard } from '../common/utils.spec.js';
+import { first } from '../common/util.js';
+import {
+ simulateClick,
+ simulateKeyboard,
+ simulatePointerDown,
+} from '../common/utils.spec.js';
import IgcCarouselIndicatorContainerComponent from './carousel-indicator-container.js';
import IgcCarouselIndicatorComponent from './carousel-indicator.js';
@@ -26,12 +31,8 @@ describe('Carousel Indicator Container', () => {
let buttons: HTMLButtonElement[];
beforeEach(async () => {
- container = await fixture(
- createIndicatorContainerComponent()
- );
- buttons = container.querySelectorAll(
- 'button'
- ) as unknown as HTMLButtonElement[];
+ container = await fixture(createIndicatorContainerComponent());
+ buttons = Array.from(container.querySelectorAll('button'));
});
it('is correctly initialized', () => {
@@ -56,7 +57,7 @@ describe('Carousel Indicator Container', () => {
`
);
- simulateKeyboard(buttons[0], tabKey);
+ simulateKeyboard(first(buttons), tabKey);
await elementUpdated(container);
expect(container).shadowDom.to.equal(
@@ -67,7 +68,7 @@ describe('Carousel Indicator Container', () => {
});
it('should remove `focused` part on click', async () => {
- simulateKeyboard(buttons[0], tabKey);
+ simulateKeyboard(first(buttons), tabKey);
await elementUpdated(container);
expect(container).shadowDom.to.equal(
@@ -76,7 +77,8 @@ describe('Carousel Indicator Container', () => {
`
);
- simulateClick(buttons[0]);
+ simulatePointerDown(first(buttons));
+ simulateClick(first(buttons));
await elementUpdated(container);
expect(container).shadowDom.to.equal(
@@ -87,7 +89,7 @@ describe('Carousel Indicator Container', () => {
});
it('it should remove `focused` part on focusout', async () => {
- simulateKeyboard(buttons[0], tabKey);
+ simulateKeyboard(first(buttons), tabKey);
await elementUpdated(container);
expect(container).shadowDom.to.equal(
@@ -96,7 +98,7 @@ describe('Carousel Indicator Container', () => {
`
);
- buttons[0].dispatchEvent(new FocusEvent('focusout', { bubbles: true }));
+ first(buttons).dispatchEvent(new FocusEvent('focusout', { bubbles: true }));
await elementUpdated(container);
expect(container).shadowDom.to.equal(
@@ -107,7 +109,7 @@ describe('Carousel Indicator Container', () => {
});
it('it should not remove `focused` part on focusout if the target receiving focus is an `igc-carousel-indicator`', async () => {
- simulateKeyboard(buttons[0], tabKey);
+ simulateKeyboard(first(buttons), tabKey);
await elementUpdated(container);
expect(container).shadowDom.to.equal(
@@ -116,14 +118,14 @@ describe('Carousel Indicator Container', () => {
`
);
- const indicator = await fixture(
+ const indicator = await fixture(
html`
0
1
`
);
- buttons[0].dispatchEvent(
+ first(buttons).dispatchEvent(
new FocusEvent('focusout', { bubbles: true, relatedTarget: indicator })
);
await elementUpdated(container);
diff --git a/src/components/carousel/carousel-indicator-container.ts b/src/components/carousel/carousel-indicator-container.ts
index 7d72ae163..7834e9e49 100644
--- a/src/components/carousel/carousel-indicator-container.ts
+++ b/src/components/carousel/carousel-indicator-container.ts
@@ -22,17 +22,19 @@ export default class IgcCarouselIndicatorContainerComponent extends LitElement {
public static override styles = [styles, shared];
/* blazorSuppress */
- public static register() {
+ public static register(): void {
registerComponent(IgcCarouselIndicatorContainerComponent);
}
- private _kbFocus = addKeyboardFocusRing(this);
+ private readonly _focusRingManager = addKeyboardFocusRing(this);
- private handleFocusOut(event: FocusEvent) {
+ private _handleFocusOut(event: FocusEvent): void {
const target = event.relatedTarget as Element;
- if (!target?.matches(IgcCarouselIndicatorComponent.tagName)) {
- this._kbFocus.reset();
+ if (target?.matches(IgcCarouselIndicatorComponent.tagName)) {
+ // Stop the event from hitting the _focusRingManager handler redrawing
+ // the keyboard focus styles
+ event.stopPropagation();
}
}
@@ -41,10 +43,9 @@ export default class IgcCarouselIndicatorContainerComponent extends LitElement {
diff --git a/src/components/carousel/carousel.ts b/src/components/carousel/carousel.ts
index 71c0269e4..e591dc092 100644
--- a/src/components/carousel/carousel.ts
+++ b/src/components/carousel/carousel.ts
@@ -7,12 +7,11 @@ import {
state,
} from 'lit/decorators.js';
-import { type Ref, createRef, ref } from 'lit/directives/ref.js';
+import { createRef, ref } from 'lit/directives/ref.js';
import { styleMap } from 'lit/directives/style-map.js';
import { themes } from '../../theming/theming-decorator.js';
import IgcButtonComponent from '../button/button.js';
import { carouselContext } from '../common/context.js';
-import { addKeyboardFocusRing } from '../common/controllers/focus-ring.js';
import {
type SwipeEvent,
addGesturesController,
@@ -92,7 +91,7 @@ export default class IgcCarouselComponent extends EventEmitterMixin<
public static readonly tagName = 'igc-carousel';
/* blazorSuppress */
- public static register() {
+ public static register(): void {
registerComponent(
IgcCarouselComponent,
IgcCarouselIndicatorComponent,
@@ -104,23 +103,23 @@ export default class IgcCarouselComponent extends EventEmitterMixin<
}
private static readonly increment = createCounter();
- private _carouselId = `igc-carousel-${IgcCarouselComponent.increment()}`;
- private _carouselKeyboardInteractionFocus = addKeyboardFocusRing(this);
+ private readonly _carouselId = `igc-carousel-${IgcCarouselComponent.increment()}`;
- private _internals: ElementInternals;
+ private readonly _internals: ElementInternals;
private _lastInterval!: ReturnType | null;
private _hasKeyboardInteractionOnIndicators = false;
private _hasMouseStop = false;
+ private _hasInnerFocus = false;
private _context = new ContextProvider(this, {
context: carouselContext,
initialValue: this,
});
- private _carouselSlidesContainerRef: Ref = createRef();
- private _indicatorsContainerRef: Ref = createRef();
- private _prevButtonRef: Ref = createRef();
- private _nextButtonRef: Ref = createRef();
+ private readonly _carouselSlidesContainerRef = createRef();
+ private readonly _indicatorsContainerRef = createRef();
+ private readonly _prevButtonRef = createRef();
+ private readonly _nextButtonRef = createRef();
private get hasProjectedIndicators(): boolean {
return this._projectedIndicators.length > 0;
@@ -326,9 +325,15 @@ export default class IgcCarouselComponent extends EventEmitterMixin<
this._internals.role = 'region';
this._internals.ariaRoleDescription = 'carousel';
- this.addEventListener('pointerdown', this.handlePointerDown);
this.addEventListener('pointerenter', this.handlePointerEnter);
this.addEventListener('pointerleave', this.handlePointerLeave);
+ this.addEventListener('pointerdown', () => {
+ this._hasInnerFocus = false;
+ });
+
+ this.addEventListener('keyup', () => {
+ this._hasInnerFocus = true;
+ });
addGesturesController(this, {
ref: this._carouselSlidesContainerRef,
@@ -381,15 +386,9 @@ export default class IgcCarouselComponent extends EventEmitterMixin<
this.requestUpdate();
}
- private handlePointerDown(): void {
- if (this._carouselKeyboardInteractionFocus.focused) {
- this._carouselKeyboardInteractionFocus.reset();
- }
- }
-
private handlePointerEnter(): void {
this._hasMouseStop = true;
- if (this._carouselKeyboardInteractionFocus.focused) {
+ if (this._hasInnerFocus) {
return;
}
this.handlePauseOnInteraction();
@@ -397,14 +396,14 @@ export default class IgcCarouselComponent extends EventEmitterMixin<
private handlePointerLeave(): void {
this._hasMouseStop = false;
- if (this._carouselKeyboardInteractionFocus.focused) {
+ if (this._hasInnerFocus) {
return;
}
this.handlePauseOnInteraction();
}
private handleFocusIn(): void {
- if (this._carouselKeyboardInteractionFocus.focused || this._hasMouseStop) {
+ if (this._hasInnerFocus || this._hasMouseStop) {
return;
}
this.handlePauseOnInteraction();
@@ -417,8 +416,8 @@ export default class IgcCarouselComponent extends EventEmitterMixin<
return;
}
- if (this._carouselKeyboardInteractionFocus.focused) {
- this._carouselKeyboardInteractionFocus.reset();
+ if (this._hasInnerFocus) {
+ this._hasInnerFocus = false;
if (!this._hasMouseStop) {
this.handlePauseOnInteraction();
diff --git a/src/components/checkbox/checkbox-base.ts b/src/components/checkbox/checkbox-base.ts
index adac4db69..a195eabef 100644
--- a/src/components/checkbox/checkbox-base.ts
+++ b/src/components/checkbox/checkbox-base.ts
@@ -39,18 +39,18 @@ export class IgcCheckboxBaseComponent extends FormAssociatedCheckboxRequiredMixi
return checkBoxValidators;
}
- protected _kbFocus = addKeyboardFocusRing(this);
+ protected readonly _focusRingManager = addKeyboardFocusRing(this);
protected override _formValue: FormValue;
protected _value!: string;
@query('input', true)
- protected input!: HTMLInputElement;
+ protected readonly _input!: HTMLInputElement;
@queryAssignedNodes({ flatten: true })
- protected label!: Array;
+ protected readonly _label!: Array;
@state()
- protected hideLabel = false;
+ protected _hideLabel = false;
/**
* The value attribute of the control.
@@ -99,35 +99,35 @@ export class IgcCheckboxBaseComponent extends FormAssociatedCheckboxRequiredMixi
});
}
- protected override createRenderRoot() {
+ protected override createRenderRoot(): HTMLElement | DocumentFragment {
const root = super.createRenderRoot();
- this.hideLabel = isEmpty(this.label);
+ this._hideLabel = isEmpty(this._label);
root.addEventListener('slotchange', () => {
- this.hideLabel = isEmpty(this.label);
+ this._hideLabel = isEmpty(this._label);
});
return root;
}
/** Simulates a click on the control. */
- public override click() {
- this.input.click();
+ public override click(): void {
+ this._input.click();
}
/* alternateName: focusComponent */
/** Sets focus on the control. */
- public override focus(options?: FocusOptions) {
- this.input.focus(options);
+ public override focus(options?: FocusOptions): void {
+ this._input.focus(options);
}
/* alternateName: blurComponent */
/** Removes focus from the control. */
- public override blur() {
- this.input.blur();
+ public override blur(): void {
+ this._input.blur();
}
- protected handleClick(event: PointerEvent) {
+ protected _handleClick(event: PointerEvent): void {
event.stopPropagation();
this.checked = !this.checked;
@@ -136,11 +136,7 @@ export class IgcCheckboxBaseComponent extends FormAssociatedCheckboxRequiredMixi
});
}
- protected handleBlur() {
- this._kbFocus.reset();
- }
-
- protected handleFocus() {
+ protected _handleFocus(): void {
this._dirty = true;
}
}
diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts
index d2824e9dc..92994305f 100644
--- a/src/components/checkbox/checkbox.ts
+++ b/src/components/checkbox/checkbox.ts
@@ -37,13 +37,13 @@ export default class IgcCheckboxComponent extends IgcCheckboxBaseComponent {
protected static styles = [styles, shared];
/* blazorSuppress */
- public static register() {
+ public static register(): void {
registerComponent(IgcCheckboxComponent, IgcValidationContainerComponent);
}
private static readonly increment = createCounter();
- private inputId = `checkbox-${IgcCheckboxComponent.increment()}`;
- private labelId = `checkbox-label-${this.inputId}`;
+ private readonly _inputId = `checkbox-${IgcCheckboxComponent.increment()}`;
+ private readonly _labelId = `checkbox-label-${this._inputId}`;
/**
* Draws the checkbox in indeterminate state.
@@ -56,9 +56,9 @@ export default class IgcCheckboxComponent extends IgcCheckboxBaseComponent {
return getThemeController(this)?.theme === 'indigo';
}
- protected override handleClick(event: PointerEvent) {
+ protected override _handleClick(event: PointerEvent): void {
this.indeterminate = false;
- super.handleClick(event);
+ super._handleClick(event);
}
protected renderValidatorContainer(): TemplateResult {
@@ -93,13 +93,12 @@ export default class IgcCheckboxComponent extends IgcCheckboxBaseComponent {
part=${partMap({
base: true,
checked,
- focused: this._kbFocus.focused,
+ focused: this._focusRingManager.focused,
})}
- for=${this.inputId}
- @pointerdown=${this._kbFocus.reset}
+ for=${this._inputId}
>
@@ -120,9 +118,9 @@ export default class IgcCheckboxComponent extends IgcCheckboxBaseComponent {
diff --git a/src/components/checkbox/switch.ts b/src/components/checkbox/switch.ts
index dc5124734..f34149667 100644
--- a/src/components/checkbox/switch.ts
+++ b/src/components/checkbox/switch.ts
@@ -31,27 +31,23 @@ export default class IgcSwitchComponent extends IgcCheckboxBaseComponent {
public static styles = [styles, shared];
/* blazorSuppress */
- public static register() {
+ public static register(): void {
registerComponent(IgcSwitchComponent);
}
private static readonly increment = createCounter();
- private inputId = `switch-${IgcSwitchComponent.increment()}`;
- private labelId = `switch-label-${this.inputId}`;
+ private readonly _inputId = `switch-${IgcSwitchComponent.increment()}`;
+ private readonly _labelId = `switch-label-${this._inputId}`;
protected override render() {
const labelledBy = this.getAttribute('aria-labelledby');
const checked = this.checked;
return html`
-