Skip to content

Commit

Permalink
fix: use Stencil watchers instead of global attributes util (#8407)
Browse files Browse the repository at this point in the history
**Related Issue:** #8193 

## Summary

Stencil v4 allows [watching global
attributes](https://stenciljs.com/docs/reactive-data#watching-native-html-attributes)
on components, so we no longer need our custom util for this.
  • Loading branch information
jcfranco authored Dec 13, 2023
1 parent ff0720f commit c531d81
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 271 deletions.
40 changes: 1 addition & 39 deletions packages/calcite-components/conventions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,45 +366,7 @@ There are utilities for common workflows in [`src/utils`](../src/utils).

### Global attributes

The [`globalAttributes`](../src/utils/globalAttributes.ts) util was specifically made to access the `lang` global attribute when set on a Calcite component. However, it can be extended to allow additional [global attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) by adding to the [`allowedGlobalAttributes`](https://github.com/Esri/calcite-design-system/blob/a33aa0df0c5bf103f91187826e6b12b8ff266d90/src/utils/globalAttributes.ts#L4-L5) array. The util is used in [`calcite-pagination`](../src/components/pagination/pagination.tsx), which you can use as a reference.

#### Usage steps

1. Import the interface and watch/unwatch methods

```js
import { GlobalAttrComponent, watchGlobalAttributes, unwatchGlobalAttributes } from "../../utils/globalAttributes";
```

2. Implement the interface

```js
export class ComponentName implements GlobalAttrComponent {
```
3. Add `globalAttributes` state
```js
@State() globalAttributes = {};
```
4. Add connect/disconnect callbacks
```js
connectedCallback(): void {
watchGlobalAttributes(this, ["lang"]);
}

disconnectedCallback(): void {
unwatchGlobalAttributes(this);
}
```
5. Use the state to access `lang` (or another global attribute that may be allowed in the future).
```js
const lang = this.globalAttributes["lang"] || document.documentElement.lang || "en";
```
Watching global attributes on components is now possible with Stencil v4. Please refer to the [documentation page](https://stenciljs.com/docs/reactive-data#watching-native-html-attributes) for more information.

### BigDecimal

Expand Down
38 changes: 24 additions & 14 deletions packages/calcite-components/src/components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import { Build, Component, Element, h, Method, Prop, State, VNode, Watch } from "@stencil/core";
import {
Build,
Component,
Element,
forceUpdate,
h,
Method,
Prop,
State,
VNode,
Watch,
} from "@stencil/core";
import { findAssociatedForm, FormOwner, resetForm, submitForm } from "../../utils/form";
import {
connectInteractive,
Expand Down Expand Up @@ -27,11 +38,6 @@ import { Appearance, FlipContext, Kind, Scale, Width } from "../interfaces";
import { ButtonMessages } from "./assets/button/t9n";
import { ButtonAlignment } from "./interfaces";
import { CSS } from "./resources";
import {
GlobalAttrComponent,
unwatchGlobalAttributes,
watchGlobalAttributes,
} from "../../utils/globalAttributes";
import { toAriaBoolean } from "../../utils/dom";

/** Passing a 'href' will render an anchor link, instead of a button. Role will be set to link, or button, depending on this. */
Expand All @@ -46,7 +52,6 @@ import { toAriaBoolean } from "../../utils/dom";
})
export class Button
implements
GlobalAttrComponent,
LabelableComponent,
InteractiveComponent,
FormOwner,
Expand All @@ -56,6 +61,17 @@ export class Button
{
//--------------------------------------------------------------------------
//
// Global attributes
//
//--------------------------------------------------------------------------

@Watch("aria-expanded")
handleGlobalAttributesChanged(): void {
forceUpdate(this);
}

// --------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
Expand Down Expand Up @@ -183,7 +199,6 @@ export class Button
connectInteractive(this);
connectLocalized(this);
connectMessages(this);
watchGlobalAttributes(this, ["aria-expanded"]);
this.hasLoader = this.loading;
this.setupTextContentObserver();
connectLabel(this);
Expand All @@ -198,7 +213,6 @@ export class Button
disconnectMessages(this);
this.resizeObserver?.disconnect();
this.formEl = null;
unwatchGlobalAttributes(this);
}

async componentWillLoad(): Promise<void> {
Expand Down Expand Up @@ -260,6 +274,7 @@ export class Button
return (
<Tag
aria-disabled={childElType === "a" ? toAriaBoolean(this.disabled || this.loading) : null}
aria-expanded={this.el.getAttribute("aria-expanded")}
aria-label={!this.loading ? getLabelText(this) : this.messages.loading}
aria-live="polite"
class={{
Expand All @@ -278,7 +293,6 @@ export class Button
target={childElType === "a" && this.target}
title={this.tooltipText}
type={childElType === "button" && this.type}
{...this.globalAttributes}
// eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)
ref={this.setChildEl}
>
Expand Down Expand Up @@ -357,10 +371,6 @@ export class Button

resizeObserver = createObserver("resize", () => this.setTooltipText());

@State() globalAttributes = {
ariaExpanded: undefined,
};

//--------------------------------------------------------------------------
//
// Private Methods
Expand Down
38 changes: 21 additions & 17 deletions packages/calcite-components/src/components/menu/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Watch,
Method,
VNode,
forceUpdate,
} from "@stencil/core";
import { focusElement, focusElementInGroup, slotChangeGetAssignedElements } from "../../utils/dom";
import {
Expand All @@ -26,11 +27,6 @@ import {
updateMessages,
} from "../../utils/t9n";
import { MenuMessages } from "./assets/menu/t9n";
import {
GlobalAttrComponent,
unwatchGlobalAttributes,
watchGlobalAttributes,
} from "../../utils/globalAttributes";

type Layout = "horizontal" | "vertical";

Expand All @@ -42,12 +38,22 @@ type Layout = "horizontal" | "vertical";
},
assetsDirs: ["assets"],
})
export class CalciteMenu
implements GlobalAttrComponent, LocalizedComponent, T9nComponent, LoadableComponent
{
export class CalciteMenu implements LocalizedComponent, T9nComponent, LoadableComponent {
//--------------------------------------------------------------------------
//
// Public Properties
// Global attributes
//
//--------------------------------------------------------------------------

@Watch("role")
handleGlobalAttributesChanged(): void {
forceUpdate(this);
this.setMenuItemLayout(this.menuItems, this.layout);
}

//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------

Expand Down Expand Up @@ -102,10 +108,6 @@ export class CalciteMenu
updateMessages(this, this.effectiveLocale);
}

@State() globalAttributes = {
role: "menubar",
};

menuItems: HTMLCalciteMenuItemElement[] = [];

//--------------------------------------------------------------------------
Expand All @@ -117,7 +119,6 @@ export class CalciteMenu
connectedCallback(): void {
connectLocalized(this);
connectMessages(this);
watchGlobalAttributes(this, ["role"]);
}

async componentWillLoad(): Promise<void> {
Expand All @@ -132,7 +133,6 @@ export class CalciteMenu
disconnectedCallback(): void {
disconnectLocalized(this);
disconnectMessages(this);
unwatchGlobalAttributes(this);
}

//--------------------------------------------------------------------------
Expand Down Expand Up @@ -220,13 +220,17 @@ export class CalciteMenu
setMenuItemLayout(items: HTMLCalciteMenuItemElement[], layout: Layout): void {
items.forEach((item) => {
item.layout = layout;
if (this.globalAttributes.role === "menubar") {
if (this.getEffectiveRole() === "menubar") {
item.isTopLevelItem = true;
item.topLevelMenuLayout = this.layout;
}
});
}

private getEffectiveRole(): string {
return this.el.getAttribute("role") || "menubar";
}

// --------------------------------------------------------------------------
//
// Render Methods
Expand All @@ -236,7 +240,7 @@ export class CalciteMenu
render(): VNode {
return (
<Host>
<ul aria-label={this.label} {...this.globalAttributes}>
<ul aria-label={this.label} role={this.getEffectiveRole()}>
<slot onSlotchange={this.handleMenuSlotChange} />
</ul>
</Host>
Expand Down
97 changes: 0 additions & 97 deletions packages/calcite-components/src/utils/globalAttributes.spec.ts

This file was deleted.

Loading

0 comments on commit c531d81

Please sign in to comment.