Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8cae01f
refactor: add setFocus controller
jcfranco Mar 29, 2025
2e3f14f
merge dev
jcfranco May 6, 2025
ae7873e
drop duplicate identifiers
jcfranco May 6, 2025
21ee384
fix infinite loop
jcfranco May 7, 2025
fe80ba1
fix combobox and chip-group tests
jcfranco May 7, 2025
d2213c1
merge dev
jcfranco May 7, 2025
674a231
merge dev
jcfranco Jul 2, 2025
3e56437
merge dev
jcfranco Jul 3, 2025
65714de
restore missing import
jcfranco Jul 3, 2025
d45ada5
fix link test
jcfranco Jul 3, 2025
d8257ce
restore missing import
jcfranco Jul 4, 2025
1c7e55c
fix(action-menu): ensure children are loaded before updating actions …
jcfranco Jul 7, 2025
a73c9c2
allow passing focus behavior to focusElementInGroup
jcfranco Jul 9, 2025
df12932
enhance controller to address list requirements
jcfranco Jul 11, 2025
87b69d0
restore h import
jcfranco Jul 11, 2025
dd06b2d
fix card-group, chip-group, table & tile-group tests
jcfranco Jul 12, 2025
3ff85db
fix list tests
jcfranco Jul 15, 2025
9504de5
merge dev
jcfranco Jul 16, 2025
b02ff63
merge branch 'dev' into jcfranco/add-setFocus-controller
jcfranco Jul 18, 2025
77f3134
tidy up
jcfranco Jul 18, 2025
a36322c
roll back unrelated upstream changes
jcfranco Jul 18, 2025
f3ccf86
avoid focusing disabled components
jcfranco Jul 18, 2025
056b38a
tidy up
jcfranco Jul 18, 2025
76deef6
fix unintentionally requiring disabled in focusable components
jcfranco Jul 18, 2025
9b3b9d0
fix test type assertions
jcfranco Jul 18, 2025
388d3ae
add disabled component test
jcfranco Jul 18, 2025
d1776ea
revisit focusElement signature
jcfranco Jul 18, 2025
f8bcbff
update focusElementInGroup tests
jcfranco Jul 18, 2025
42a511f
add focusElement tests
jcfranco Jul 19, 2025
eeb7d01
tidy up
jcfranco Jul 19, 2025
4cfe0ce
add coverage for context arg
jcfranco Jul 19, 2025
5ec449a
tidy up
jcfranco Jul 19, 2025
7b002e2
update useSetFocus controller with updated focusElement options
jcfranco Jul 19, 2025
af0db40
add tests for useSetFocus config
jcfranco Jul 19, 2025
360e129
fix FocusConfig type guard bug
jcfranco Jul 19, 2025
6a11728
fix chip-group test
jcfranco Jul 20, 2025
89d81a4
tidy up
jcfranco Jul 20, 2025
467dd9e
generalize test custom element registration
jcfranco Jul 20, 2025
52fb945
tidy up
jcfranco Jul 21, 2025
326c427
merge dev
jcfranco Jul 22, 2025
22026aa
tidy up
jcfranco Jul 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
import { CSS_UTILITY } from "../../utils/resources";
import { getIconScale } from "../../utils/component";
import { FlipContext, Position, Scale, SelectionMode, IconType, Appearance } from "../interfaces";
import { componentFocusable } from "../../utils/component";
import { IconNameOrString } from "../icon/interfaces";
import type { Accordion } from "../accordion/accordion";
import { useSetFocus } from "../../controllers/useSetFocus";
import { Heading, HeadingLevel } from "../functional/Heading";
import { SLOTS, CSS, IDS, ICONS } from "./resources";
import { RequestedItem } from "./interfaces";
Expand Down Expand Up @@ -38,6 +38,8 @@ export class AccordionItem extends LitElement {

private headerEl: HTMLDivElement;

private focusSetter = useSetFocus<this>()(this);

// #endregion

// #region State Properties
Expand Down Expand Up @@ -113,8 +115,9 @@ export class AccordionItem extends LitElement {
/** Sets focus on the component. */
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);
this.headerEl.focus();
return this.focusSetter(() => {
return this.headerEl;
});
}

// #endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
import { debounce } from "lodash-es";
import { PropertyValues } from "lit";
import { LitElement, property, createEvent, h, method, state, JsxNode } from "@arcgis/lumina";
import {
focusFirstTabbable,
slotChangeGetAssignedElements,
slotChangeHasAssignedElement,
} from "../../utils/dom";
import { componentFocusable } from "../../utils/component";
import { slotChangeGetAssignedElements, slotChangeHasAssignedElement } from "../../utils/dom";
import { createObserver } from "../../utils/observers";
import { ExpandToggle, toggleChildActionText } from "../functional/ExpandToggle";
import { Layout, Position, Scale } from "../interfaces";
Expand All @@ -17,6 +12,7 @@ import { useT9n } from "../../controllers/useT9n";
import { useCancelable } from "../../controllers/useCancelable";
import type { Tooltip } from "../tooltip/tooltip";
import type { ActionGroup } from "../action-group/action-group";
import { useSetFocus } from "../../controllers/useSetFocus";
import { Action } from "../action/action";
import { getOverflowCount } from "../../utils/overflow";
import T9nStrings from "./assets/t9n/messages.en.json";
Expand Down Expand Up @@ -107,6 +103,8 @@ export class ActionBar extends LitElement {
*/
messages = useT9n<typeof T9nStrings>();

private focusSetter = useSetFocus<this>()(this);

//#endregion

//#region State Properties
Expand Down Expand Up @@ -177,9 +175,9 @@ export class ActionBar extends LitElement {
/** Sets focus on the component's first focusable element. */
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);

focusFirstTabbable(this.el);
return this.focusSetter(() => {
return this.el;
});
}

//#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// @ts-strict-ignore
import { PropertyValues } from "lit";
import { LitElement, property, h, method, state, JsxNode, ToEvents } from "@arcgis/lumina";
import { componentFocusable } from "../../utils/component";
import { SLOTS as ACTION_MENU_SLOTS } from "../action-menu/resources";
import { Layout, Scale } from "../interfaces";
import { FlipPlacement, LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui";
import { focusFirstTabbable, slotChangeHasAssignedElement } from "../../utils/dom";
import { slotChangeHasAssignedElement } from "../../utils/dom";
import { useT9n } from "../../controllers/useT9n";
import type { ActionMenu } from "../action-menu/action-menu";
import { useSetFocus } from "../../controllers/useSetFocus";
import { Columns } from "./interfaces";
import T9nStrings from "./assets/t9n/messages.en.json";
import { CSS, ICONS, SLOTS } from "./resources";
Expand Down Expand Up @@ -42,6 +42,8 @@ export class ActionGroup extends LitElement {
*/
messages = useT9n<typeof T9nStrings>();

private focusSetter = useSetFocus<this>()(this);

//#endregion

//#region State Properties
Expand Down Expand Up @@ -99,8 +101,9 @@ export class ActionGroup extends LitElement {
/** Sets focus on the component's first focusable element. */
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);
focusFirstTabbable(this.el);
return this.focusSetter(() => {
return this.el;
});
}

//#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import {
JsxNode,
} from "@arcgis/lumina";
import { getRoundRobinIndex } from "../../utils/array";
import { focusElement, toAriaBoolean } from "../../utils/dom";
import { toAriaBoolean } from "../../utils/dom";
import { FlipPlacement, LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui";
import { guid } from "../../utils/guid";
import { isActivationKey } from "../../utils/key";
import { componentFocusable } from "../../utils/component";
import { Appearance, Scale } from "../interfaces";
import type { Action } from "../action/action";
import type { Tooltip } from "../tooltip/tooltip";
import { Popover } from "../popover/popover";
import { CSS, ICONS, SLOTS, IDS } from "./resources";
import { useSetFocus } from "../../controllers/useSetFocus";
import { CSS, ICONS, IDS, SLOTS } from "./resources";
import { styles } from "./action-menu.scss";

declare global {
Expand Down Expand Up @@ -119,6 +119,8 @@ export class ActionMenu extends LitElement {
action.activeDescendant = index === activeMenuItemIndex;
};

private focusSetter = useSetFocus<this>()(this);

// #endregion

// #region State Properties
Expand Down Expand Up @@ -152,7 +154,6 @@ export class ActionMenu extends LitElement {
get open(): boolean {
return this._open;
}

set open(open: boolean) {
const oldOpen = this._open;
if (open !== oldOpen) {
Expand Down Expand Up @@ -182,9 +183,9 @@ export class ActionMenu extends LitElement {
/** Sets focus on the component. */
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);

return focusElement(this.menuButtonEl);
return this.focusSetter(() => {
return this.menuButtonEl;
});
}

// #endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// @ts-strict-ignore
import { PropertyValues } from "lit";
import { LitElement, property, createEvent, h, method, state, JsxNode } from "@arcgis/lumina";
import { focusFirstTabbable, slotChangeGetAssignedElements } from "../../utils/dom";
import { componentFocusable } from "../../utils/component";
import { slotChangeGetAssignedElements } from "../../utils/dom";
import { ExpandToggle, toggleChildActionText } from "../functional/ExpandToggle";
import { Layout, Position, Scale } from "../interfaces";
import { createObserver } from "../../utils/observers";
import { OverlayPositioning } from "../../utils/floating-ui";
import { useT9n } from "../../controllers/useT9n";
import type { Tooltip } from "../tooltip/tooltip";
import type { ActionGroup } from "../action-group/action-group";
import { useSetFocus } from "../../controllers/useSetFocus";
import { logger } from "../../utils/logger";
import T9nStrings from "./assets/t9n/messages.en.json";
import { CSS, SLOTS } from "./resources";
Expand Down Expand Up @@ -53,6 +53,8 @@ export class ActionPad extends LitElement {
*/
messages = useT9n<typeof T9nStrings>();

private focusSetter = useSetFocus<this>()(this);

//#endregion

//#region State Properties
Expand Down Expand Up @@ -101,8 +103,9 @@ export class ActionPad extends LitElement {
/** Sets focus on the component's first focusable element. */
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);
focusFirstTabbable(this.el);
return this.focusSetter(() => {
return this.el;
});
}

//#endregion
Expand Down
9 changes: 6 additions & 3 deletions packages/calcite-components/src/components/action/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
InteractiveContainer,
updateHostInteraction,
} from "../../utils/interactive";
import { componentFocusable } from "../../utils/component";
import { createObserver } from "../../utils/observers";
import { getIconScale } from "../../utils/component";
import { Alignment, Appearance, Scale, Width } from "../interfaces";
import { IconNameOrString } from "../icon/interfaces";
import { useT9n } from "../../controllers/useT9n";
import type { Tooltip } from "../tooltip/tooltip";
import { useSetFocus } from "../../controllers/useSetFocus";
import T9nStrings from "./assets/t9n/messages.en.json";
import { CSS, SLOTS, IDS } from "./resources";
import { styles } from "./action.scss";
Expand Down Expand Up @@ -54,6 +54,8 @@ export class Action extends LitElement implements InteractiveComponent {
*/
messages = useT9n<typeof T9nStrings>({ blocking: true });

private focusSetter = useSetFocus<this>()(this);

//#endregion

//#region Public Properties
Expand Down Expand Up @@ -135,8 +137,9 @@ export class Action extends LitElement implements InteractiveComponent {
/** Sets focus on the component. */
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);
this.buttonEl.value?.focus();
return this.focusSetter(() => {
return this.buttonEl.value;
});
}

//#endregion
Expand Down
15 changes: 7 additions & 8 deletions packages/calcite-components/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,16 @@ import {
JsxNode,
stringOrBoolean,
} from "@arcgis/lumina";
import {
focusFirstTabbable,
setRequestedIcon,
slotChangeHasAssignedElement,
} from "../../utils/dom";
import { setRequestedIcon, slotChangeHasAssignedElement } from "../../utils/dom";
import { MenuPlacement } from "../../utils/floating-ui";
import { getIconScale } from "../../utils/component";
import { componentFocusable } from "../../utils/component";
import { NumberingSystem, NumberStringFormat } from "../../utils/locale";
import { toggleOpenClose, OpenCloseComponent } from "../../utils/openCloseComponent";
import { Kind, Scale } from "../interfaces";
import { KindIcons } from "../resources";
import { IconNameOrString } from "../icon/interfaces";
import { useT9n } from "../../controllers/useT9n";
import { useSetFocus } from "../../controllers/useSetFocus";
import T9nStrings from "./assets/t9n/messages.en.json";
import { AlertDuration, AlertQueue } from "./interfaces";
import { CSS, DURATIONS, SLOTS } from "./resources";
Expand Down Expand Up @@ -79,6 +75,8 @@ export class Alert extends LitElement implements OpenCloseComponent {
*/
messages = useT9n<typeof T9nStrings>();

private focusSetter = useSetFocus<this>()(this);

//#endregion

//#region State Properties
Expand Down Expand Up @@ -174,8 +172,9 @@ export class Alert extends LitElement implements OpenCloseComponent {
*/
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);
focusFirstTabbable(this.el);
return this.focusSetter(() => {
return this.el;
});
}

//#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import type { AutocompleteItemGroup } from "../autocomplete-item-group/autocompl
import type { Label } from "../label/label";
import { Validation } from "../functional/Validation";
import { createObserver } from "../../utils/observers";
import { componentFocusable } from "../../utils/component";
import { useSetFocus } from "../../controllers/useSetFocus";
import { styles } from "./autocomplete.scss";
import T9nStrings from "./assets/t9n/messages.en.json";
import { CSS, IDS, SLOTS } from "./resources";
Expand Down Expand Up @@ -131,6 +131,8 @@ export class Autocomplete

private mutationObserver = createObserver("mutation", () => this.getAllItemsDebounced());

private focusSetter = useSetFocus<this>()(this);

private resizeObserver = createObserver("resize", () => {
this.setFloatingElSize();
});
Expand Down Expand Up @@ -389,9 +391,9 @@ export class Autocomplete
*/
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);

return this.referenceEl.setFocus();
return this.focusSetter(() => {
return this.referenceEl;
});
}

//#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
disconnectSortableComponent,
SortableComponent,
} from "../../utils/sortableComponent";
import { componentFocusable } from "../../utils/component";
import { MoveEventDetail, MoveTo, ReorderEventDetail } from "../sort-handle/interfaces";
import { DEBOUNCE } from "../../utils/resources";
import { Block } from "../block/block";
import { focusFirstTabbable, getRootNode } from "../../utils/dom";
import { getRootNode } from "../../utils/dom";
import { guid } from "../../utils/guid";
import { isBlock } from "../block/utils";
import { useSetFocus } from "../../controllers/useSetFocus";
import { useCancelable } from "../../controllers/useCancelable";
import { blockGroupSelector, blockSelector, CSS } from "./resources";
import { styles } from "./block-group.scss";
Expand Down Expand Up @@ -61,6 +61,8 @@ export class BlockGroup extends LitElement implements InteractiveComponent, Sort

private updateBlockItemsDebounced = debounce(this.updateBlockItems, DEBOUNCE.nextTick);

private focusSetter = useSetFocus<this>()(this);

// #endregion

// #region State Properties
Expand Down Expand Up @@ -115,9 +117,9 @@ export class BlockGroup extends LitElement implements InteractiveComponent, Sort
*/
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);

focusFirstTabbable(this.el);
return this.focusSetter(() => {
return this.el;
});
}

// #endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// @ts-strict-ignore
import { LitElement, property, createEvent, Fragment, h, method, JsxNode } from "@arcgis/lumina";
import { focusFirstTabbable } from "../../utils/dom";
import { isActivationKey } from "../../utils/key";
import { FlipContext, Status } from "../interfaces";
import { componentFocusable } from "../../utils/component";
import { IconNameOrString } from "../icon/interfaces";
import { useT9n } from "../../controllers/useT9n";
import { logger } from "../../utils/logger";
import { useSetFocus } from "../../controllers/useSetFocus";
import T9nStrings from "./assets/t9n/messages.en.json";
import { BlockSectionToggleDisplay } from "./interfaces";
import { CSS, ICONS, IDS } from "./resources";
Expand Down Expand Up @@ -35,6 +34,8 @@ export class BlockSection extends LitElement {
*/
messages = useT9n<typeof T9nStrings>();

private focusSetter = useSetFocus<this>()(this);

//#endregion

//#region Public Properties
Expand Down Expand Up @@ -98,8 +99,9 @@ export class BlockSection extends LitElement {
/** Sets focus on the component's first tabbable element. */
@method()
async setFocus(): Promise<void> {
await componentFocusable(this);
focusFirstTabbable(this.el);
return this.focusSetter(() => {
return this.el;
});
}

//#endregion
Expand Down
Loading
Loading