Skip to content

Commit

Permalink
fix: improve browser check to resolve SSR errors (#9897)
Browse files Browse the repository at this point in the history
**Related Issue:** #9242

## Summary

Add additional browser checks to resolve some SSR errors that Stencil's
`Build.isBrowser` util doesn't catch.
  • Loading branch information
benelan authored and github-actions[bot] committed Jul 30, 2024
1 parent 7d05134 commit bdb225b
Show file tree
Hide file tree
Showing 17 changed files with 46 additions and 40 deletions.
4 changes: 2 additions & 2 deletions packages/calcite-components/conventions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ This generates a `hydrate` directory which exposes `renderToString()` (for the s
Since many of the same lifecycle methods are called on the client and server you may need to differentiate any code that relies on browser APIs like so:

```ts
import { Build } from "@stencil/core";
import { isBrowser } from "../utils/browser";

if (Build.isBrowser) {
if (isBrowser()) {
// client side
} else {
// server side
Expand Down
4 changes: 2 additions & 2 deletions packages/calcite-components/src/components/action/action.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Build,
Component,
Element,
forceUpdate,
Expand Down Expand Up @@ -38,6 +37,7 @@ import {
} from "../../utils/t9n";
import { Alignment, Appearance, Scale } from "../interfaces";
import { IconName } from "../icon/interfaces";
import { isBrowser } from "../../utils/browser";
import { ActionMessages } from "./assets/action/t9n";
import { CSS, SLOTS } from "./resources";

Expand Down Expand Up @@ -182,7 +182,7 @@ export class Action

async componentWillLoad(): Promise<void> {
setUpLoadableComponent(this);
if (Build.isBrowser) {
if (isBrowser()) {
await setUpMessages(this);
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/calcite-components/src/components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Build,
Component,
Element,
forceUpdate,
Expand Down Expand Up @@ -38,6 +37,7 @@ import {
import { Appearance, FlipContext, Kind, Scale, Width } from "../interfaces";
import { toAriaBoolean } from "../../utils/dom";
import { IconName } from "../icon/interfaces";
import { isBrowser } from "../../utils/browser";
import { ButtonMessages } from "./assets/button/t9n";
import { ButtonAlignment } from "./interfaces";
import { CSS } from "./resources";
Expand Down Expand Up @@ -226,7 +226,7 @@ export class Button

async componentWillLoad(): Promise<void> {
setUpLoadableComponent(this);
if (Build.isBrowser) {
if (isBrowser()) {
this.updateHasContent();
await setUpMessages(this);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/calcite-components/src/components/chip/chip.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Build,
Component,
Element,
Event,
Expand Down Expand Up @@ -39,6 +38,7 @@ import { connectLocalized, disconnectLocalized, LocalizedComponent } from "../..
import { isActivationKey } from "../../utils/key";
import { getIconScale } from "../../utils/component";
import { IconName } from "../icon/interfaces";
import { isBrowser } from "../../utils/browser";
import { ChipMessages } from "./assets/chip/t9n";
import { CSS, SLOTS, ICONS } from "./resources";

Expand Down Expand Up @@ -230,7 +230,7 @@ export class Chip

async componentWillLoad(): Promise<void> {
setUpLoadableComponent(this);
if (Build.isBrowser) {
if (isBrowser()) {
await setUpMessages(this);
this.updateHasText();
}
Expand Down
4 changes: 2 additions & 2 deletions packages/calcite-components/src/components/combobox/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Build } from "@stencil/core";
import { nodeListToArray } from "../../utils/dom";
import { isBrowser } from "../../utils/browser";
import { ComboboxChildElement } from "./interfaces";
import { ComboboxChildSelector } from "./resources";
import { Combobox } from "./combobox";
Expand All @@ -26,7 +26,7 @@ export function hasActiveChildren(node: HTMLCalciteComboboxItemElement): boolean
}

export function getDepth(element: HTMLElement): number {
if (!Build.isBrowser) {
if (!isBrowser()) {
return 0;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Build,
Component,
Element,
Event,
Expand Down Expand Up @@ -42,6 +41,7 @@ import {
updateMessages,
} from "../../utils/t9n";
import { HeadingLevel } from "../functional/Heading";
import { isBrowser } from "../../utils/browser";
import { DatePickerMessages } from "./assets/date-picker/t9n";
import { DATE_PICKER_FORMAT_OPTIONS, HEADING_LEVEL } from "./resources";
import { DateLocaleData, getLocaleData, getValueAsDateRange } from "./utils";
Expand Down Expand Up @@ -347,7 +347,7 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom

@Watch("effectiveLocale")
private async loadLocaleData(): Promise<void> {
if (!Build.isBrowser) {
if (!isBrowser()) {
return;
}

Expand Down
5 changes: 3 additions & 2 deletions packages/calcite-components/src/components/icon/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { CalciteIconPath, CalciteMultiPathEntry } from "@esri/calcite-ui-icons";
import { Build, Component, Element, h, Host, Prop, State, VNode, Watch } from "@stencil/core";
import { Component, Element, h, Host, Prop, State, VNode, Watch } from "@stencil/core";
import { getElementDir, toAriaBoolean } from "../../utils/dom";
import { createObserver } from "../../utils/observers";
import { Scale } from "../interfaces";
import { isBrowser } from "../../utils/browser";
import { CSS } from "./resources";
import { fetchIcon, getCachedIconData, scaleToPx } from "./utils";
import { IconName } from "./interfaces";
Expand Down Expand Up @@ -137,7 +138,7 @@ export class Icon {
private async loadIconPathData(): Promise<void> {
const { icon, scale, visible } = this;

if (!Build.isBrowser || !icon || !visible) {
if (!isBrowser() || !icon || !visible) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Build,
Component,
Element,
Event,
Expand Down Expand Up @@ -90,6 +89,7 @@ import { Status } from "../interfaces";
import { Validation } from "../functional/Validation";
import { IconName } from "../icon/interfaces";
import { syncHiddenFormInput } from "../input/common/input";
import { isBrowser } from "../../utils/browser";
import { normalizeToCurrentCentury, isTwoDigitYear } from "./utils";
import { InputDatePickerMessages } from "./assets/input-date-picker/t9n";
import { CSS } from "./resources";
Expand Down Expand Up @@ -984,7 +984,7 @@ export class InputDatePicker
};

private async loadLocaleData(): Promise<void> {
if (!Build.isBrowser) {
if (!isBrowser()) {
return;
}
numberStringFormatter.numberFormatOptions = {
Expand Down
4 changes: 2 additions & 2 deletions packages/calcite-components/src/components/list-item/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Build } from "@stencil/core";
import { isBrowser } from "../../utils/browser";

const listSelector = "calcite-list";
const listItemGroupSelector = "calcite-list-item-group";
Expand Down Expand Up @@ -38,7 +38,7 @@ export function updateListItemChildren(listItemChildren: HTMLCalciteListItemElem
}

export function getDepth(element: HTMLElement, includeGroup = false): number {
if (!Build.isBrowser) {
if (!isBrowser()) {
return 0;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Build,
Component,
Element,
Event,
Expand Down Expand Up @@ -39,6 +38,7 @@ import { Appearance, Layout, Scale, Status, Width } from "../interfaces";
import { createObserver } from "../../utils/observers";
import { Validation } from "../functional/Validation";
import { IconName } from "../icon/interfaces";
import { isBrowser } from "../../utils/browser";
import { CSS } from "./resources";

/**
Expand Down Expand Up @@ -390,7 +390,7 @@ export class SegmentedControl
});

this.selectedItem = match;
if (Build.isBrowser && match) {
if (isBrowser() && match) {
match.focus();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Build,
Component,
Element,
Event,
Expand Down Expand Up @@ -36,6 +35,7 @@ import {
} from "../../utils/t9n";
import { getIconScale } from "../../utils/component";
import { IconName } from "../icon/interfaces";
import { isBrowser } from "../../utils/browser";
import { TabTitleMessages } from "./assets/tab-title/t9n";
import { CSS, ICONS } from "./resources";

Expand Down Expand Up @@ -172,7 +172,7 @@ export class TabTitle implements InteractiveComponent, LocalizedComponent, T9nCo

async componentWillLoad(): Promise<void> {
await setUpMessages(this);
if (Build.isBrowser) {
if (isBrowser()) {
this.updateHasText();
}
if (this.tab && this.selected) {
Expand Down
11 changes: 10 additions & 1 deletion packages/calcite-components/src/utils/browser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Build } from "@stencil/core";

export const isBrowser = (): boolean =>
Build.isBrowser &&
typeof navigator !== "undefined" &&
typeof window !== "undefined" &&
typeof location !== "undefined" &&
typeof document !== "undefined" &&
window.location === location &&
window.document === document;

interface NavigatorUAData {
brands: Array<{ brand: string; version: string }>;
mobile: boolean;
Expand All @@ -11,7 +20,7 @@ function getUserAgentData(): NavigatorUAData | undefined {
}

export function getUserAgentString(): string {
if (!Build.isBrowser) {
if (!isBrowser()) {
return "";
}

Expand Down
6 changes: 3 additions & 3 deletions packages/calcite-components/src/utils/floating-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import {
Strategy,
VirtualElement,
} from "@floating-ui/dom";
import { Build } from "@stencil/core";
import { debounce, DebouncedFunc } from "lodash-es";
import { offsetParent } from "composed-offset-position";
import { Layout } from "../components/interfaces";
import { DEBOUNCE } from "./resources";
import { getElementDir } from "./dom";
import { isBrowser } from "./browser";

(function setUpFloatingUiForShadowDomPositioning(): void {
if (Build.isBrowser) {
if (isBrowser()) {
const originalGetOffsetParent = platform.getOffsetParent;
platform.getOffsetParent = (element: Element) => originalGetOffsetParent(element, offsetParent);
}
Expand Down Expand Up @@ -499,7 +499,7 @@ async function runAutoUpdate(
return;
}

const effectiveAutoUpdate = Build.isBrowser
const effectiveAutoUpdate = isBrowser()
? autoUpdate
: (_refEl: HTMLElement, _floatingEl: HTMLElement, updateCallback: () => void): (() => void) => {
updateCallback();
Expand Down
10 changes: 2 additions & 8 deletions packages/calcite-components/src/utils/globalScript.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { initModeChangeEvent } from "./mode";
import { stampVersion } from "./config";
import { isBrowser } from "./browser";

/**
* This file is imported in Stencil's `globalScript` config option.
*
* @see {@link https://stenciljs.com/docs/config#globalscript}
*/
export default function (): void {
const isBrowser =
typeof window !== "undefined" &&
typeof location !== "undefined" &&
typeof document !== "undefined" &&
window.location === location &&
window.document === document;

if (isBrowser) {
if (isBrowser()) {
if (document.readyState === "interactive") {
initModeChangeEvent();
} else {
Expand Down
5 changes: 3 additions & 2 deletions packages/calcite-components/src/utils/loadable.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Build, forceUpdate } from "@stencil/core";
import { forceUpdate } from "@stencil/core";
import { isBrowser } from "./browser";

/**
* This helper adds support for knowing when a component has been loaded.
Expand Down Expand Up @@ -134,7 +135,7 @@ export function componentLoaded(component: LoadableComponent): Promise<void> {
export async function componentFocusable(component: LoadableComponent): Promise<void> {
await componentLoaded(component);

if (!Build.isBrowser) {
if (!isBrowser()) {
return;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/calcite-components/src/utils/observers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Build } from "@stencil/core";
import { isBrowser } from "./browser";

export interface ExtendedMutationObserver extends MutationObserver {
new: () => ExtendedMutationObserver;
Expand Down Expand Up @@ -50,7 +50,7 @@ export function createObserver<T extends ObserverType>(
callback: ObserverCallbackType<T>,
options?: ObserverOptions<T>,
): ObserverInstanceType<T> | undefined {
if (!Build.isBrowser) {
if (!isBrowser()) {
return undefined;
}

Expand Down
5 changes: 3 additions & 2 deletions packages/calcite-components/src/utils/t9n.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Build, getAssetPath } from "@stencil/core";
import { getAssetPath } from "@stencil/core";
import { getSupportedLocale, LocalizedComponent } from "./locale";
import { isBrowser } from "./browser";

export type MessageBundle = Record<string, string>;

Expand Down Expand Up @@ -50,7 +51,7 @@ export async function setUpMessages(component: T9nComponent): Promise<void> {
}

async function fetchMessages(component: T9nComponent, lang: string): Promise<MessageBundle> {
if (!Build.isBrowser) {
if (!isBrowser()) {
return {};
}

Expand Down

0 comments on commit bdb225b

Please sign in to comment.