Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tree-item): apply enhanced openCloseComponent to tree-item for expanded/collapsed events #11246

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ Calcite follows [Conventional Commits](https://www.conventionalcommits.org/en/v1
Contributions should adhere to the `<type>(<scope>): <descriptive summary>` format and include the following:

- [Commit type](#commit-type)
- [Scope of change](#scope-of-change), _optional_
- [Scope of change](#scope-of-change), *optional*
- [Descriptive commit subject](#descriptive-commit-subject)

Check out the [contribution example](#contribution-example) for a formatted example, and explore [breaking change formatting](#breaking-changes) for consideration during Calcite's breaking change releases.
Expand All @@ -240,7 +240,7 @@ Contributions must adhere to **one** of the following types:

### Scope of change

_Optional_. Most contributions will include a scope, such as a component, multiple components, test(s), or utilities. For example:
*Optional*. Most contributions will include a scope, such as a component, multiple components, test(s), or utilities. For example:

- `text-area`
- `dropdown, dropdown-group, dropdown-item`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
InteractiveContainer,
updateHostInteraction,
} from "../../utils/interactive";
import { onToggleOpenCloseComponent, OpenCloseComponent } from "../../utils/openCloseComponent";
import { CSS_UTILITY } from "../../utils/resources";
import { FlipContext, Scale, SelectionMode } from "../interfaces";
import { getIconScale } from "../../utils/component";
Expand All @@ -34,7 +35,7 @@ declare global {
* @slot children - A slot for adding nested `calcite-tree` elements.
* @slot actions-end - A slot for adding actions to the end of the component. It is recommended to use two or fewer actions.
*/
export class TreeItem extends LitElement implements InteractiveComponent {
export class TreeItem extends LitElement implements InteractiveComponent, OpenCloseComponent {
// #region Static Members

static override styles = styles;
Expand All @@ -53,6 +54,10 @@ export class TreeItem extends LitElement implements InteractiveComponent {

private userChangedValue = false;

transitionProp = "opacity";

transitionEl: HTMLDivElement;

// #endregion

// #region State Properties
Expand Down Expand Up @@ -121,6 +126,18 @@ export class TreeItem extends LitElement implements InteractiveComponent {
/** @private */
calciteInternalTreeItemSelect = createEvent<TreeItemSelectDetail>({ cancelable: false });

/** Fires when the component is requested to be collapsed and before the closing transition begins. */
calciteTreeItemBeforeCollapsed = createEvent({ cancelable: false });

/** Fires when the component is added to the DOM but not rendered, and before the opening transition begins. */
calciteTreeItemBeforeExpanded = createEvent({ cancelable: false });

/** Fires when the component is collapsed and animation is complete. */
calciteTreeItemCollapsed = createEvent({ cancelable: false });

/** Fires when the component is expanded and animation is complete. */
calciteTreeItemExpanded = createEvent({ cancelable: false });

// #endregion

// #region Lifecycle
Expand All @@ -137,6 +154,7 @@ export class TreeItem extends LitElement implements InteractiveComponent {

load(): void {
requestAnimationFrame(() => (this.updateAfterInitialRender = true));
onToggleOpenCloseComponent(this);
}

override willUpdate(changes: PropertyValues<this>): void {
Expand All @@ -147,6 +165,7 @@ export class TreeItem extends LitElement implements InteractiveComponent {
Docs: https://qawebgis.esri.com/arcgis-components/?path=/docs/lumina-transition-from-stencil--docs#watching-for-property-changes */
if (changes.has("expanded") && (this.hasUpdated || this.expanded !== false)) {
this.updateChildTree();
onToggleOpenCloseComponent(this);
}

if (changes.has("selected") && (this.hasUpdated || this.selected !== false)) {
Expand Down Expand Up @@ -208,6 +227,7 @@ export class TreeItem extends LitElement implements InteractiveComponent {
private iconClickHandler(event: MouseEvent): void {
event.stopPropagation();
this.expanded = !this.expanded;
onToggleOpenCloseComponent(this);
}

private childrenClickHandler(event: MouseEvent): void {
Expand All @@ -230,6 +250,8 @@ export class TreeItem extends LitElement implements InteractiveComponent {
break;
case "Enter": {
// activates a node, i.e., performs its default action. For parent nodes, one possible default action is to open or close the node. In single-select trees where selection does not follow focus (see note below), the default action is typically to select the focused node.
onToggleOpenCloseComponent(this);

const link = Array.from(this.el.children).find((el) =>
el.matches("a"),
) as HTMLAnchorElement;
Expand All @@ -245,6 +267,7 @@ export class TreeItem extends LitElement implements InteractiveComponent {
updateItem: true,
});
}

event.preventDefault();
}
}
Expand Down Expand Up @@ -340,6 +363,26 @@ export class TreeItem extends LitElement implements InteractiveComponent {
}
}

private setTransitionEl(el: HTMLDivElement): void {
this.transitionEl = el;
}

onBeforeExpanded(): void {
this.calciteTreeItemBeforeExpanded.emit();
}

onExpanded(): void {
this.calciteTreeItemExpanded.emit();
}

onBeforeCollapsed(): void {
this.calciteTreeItemBeforeCollapsed.emit();
}

onCollapsed(): void {
this.calciteTreeItemCollapsed.emit();
}

// #endregion

// #region Rendering
Expand Down Expand Up @@ -483,6 +526,7 @@ export class TreeItem extends LitElement implements InteractiveComponent {
}}
data-test-id="calcite-tree-children"
onClick={this.childrenClickHandler}
ref={this.setTransitionEl}
role={this.hasChildren ? "group" : undefined}
>
<slot name={SLOTS.children} onSlotChange={this.handleChildrenSlotChange} />
Expand Down
25 changes: 24 additions & 1 deletion packages/calcite-components/src/demos/tree.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ <h1 style="margin: 0 auto; text-align: center">Tree</h1>
<calcite-tree-item>
Child 2

<calcite-tree slot="children">
<calcite-tree slot="children" id="tree-item">
<calcite-tree-item> Grandchild 1 </calcite-tree-item>

<calcite-tree-item> Grandchild 2 </calcite-tree-item>
Expand All @@ -67,6 +67,29 @@ <h1 style="margin: 0 auto; text-align: center">Tree</h1>
</calcite-tree>
</calcite-tree-item>
</calcite-tree>
<script>
const treeItem = document.getElementById("tree-item");

window.onload = () => {
if (treeItem) {
treeItem.addEventListener("calciteTreeItemBeforeCollapsed", () => {
console.log("calciteTreeItemBeforeCollapsed event fired");
});

treeItem.addEventListener("calciteTreeItemBeforeExpanded", () => {
console.log("calciteTreeItemBeforeExpanded event fired");
});

treeItem.addEventListener("calciteTreeItemCollapsed", () => {
console.log("calciteTreeItemCollapsed event fired");
});

treeItem.addEventListener("calciteTreeItemExpanded", () => {
console.log("calciteTreeItemExpanded event fired");
});
}
};
</script>
</div>

<div class="child">
Expand Down
31 changes: 26 additions & 5 deletions packages/calcite-components/src/utils/openCloseComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ export interface OpenCloseComponent {
/** The host element. */
readonly el: HTMLElement;

/** When true, the component closes. */
closed?: boolean;

/** When true, the component collapses. */
collapsed?: boolean;

/** When true, the component is expanded. */
expanded?: boolean;

/**
* Specifies property on which active transition is watched for.
*
Expand All @@ -24,16 +33,28 @@ export interface OpenCloseComponent {
transitionEl: HTMLElement;

/** Defines method for `beforeOpen` event handler. */
onBeforeOpen: () => void;
onBeforeOpen?: () => void;

/** Defines method for `open` event handler: */
onOpen: () => void;
onOpen?: () => void;

/** Defines method for `beforeClose` event handler: */
onBeforeClose: () => void;
onBeforeClose?: () => void;

/** Defines method for `close` event handler: */
onClose: () => void;
onClose?: () => void;

/** Defines method for `beforeExpanded` event handler. */
onBeforeExpanded?: () => void;

/** Defines method for `expanded` event handler: */
onExpanded?: () => void;

/** Defines method for `beforeCollapsed` event handler: */
onBeforeCollapsed?: () => void;

/** Defines method for `collapsed` event handler: */
onCollapsed?: () => void;
}

function isOpen(component: OpenCloseComponent): boolean {
Expand All @@ -56,7 +77,7 @@ function isOpen(component: OpenCloseComponent): boolean {
* async toggleModal(value: boolean): Promise<void> {
* onToggleOpenCloseComponent(this);
* }
* @param component - OpenCloseComponent uses `open` prop to emit (before)open/close.
* @param component - OpenCloseComponent uses `open`/`close` or `expanded`/`collapsed` props to emit (before)open/close or (before)expanded/collapsed respectively.
*/
export function onToggleOpenCloseComponent(component: OpenCloseComponent): void {
requestAnimationFrame((): void => {
Expand Down
Loading