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

refactor: convert platform functions to constants #2677

Merged
merged 4 commits into from
May 28, 2024
Merged
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
2 changes: 1 addition & 1 deletion src/components/accordion/accordion.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe(`sbb-accordion ${fixture.name}`, () => {
let element: SbbAccordionElement;

beforeEach(async function () {
const ssrTitleLevel = isSsr() ? '4' : nothing;
const ssrTitleLevel = isSsr ? '4' : nothing;
element = await fixture(
html`
<sbb-accordion title-level="4">
Expand Down
8 changes: 4 additions & 4 deletions src/components/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe(`sbb-autocomplete`, () => {
element = testFixture.querySelector('sbb-autocomplete')!;
});

describeIf(!isSafari(), 'Chrome-Firefox', async () => {
describeIf(!isSafari, 'Chrome-Firefox', async () => {
it('Dom', async () => {
await expect(element).dom.to.be.equalSnapshot();
});
Expand All @@ -39,7 +39,7 @@ describe(`sbb-autocomplete`, () => {
});
});

describeIf(isSafari(), 'Safari', async () => {
describeIf(isSafari, 'Safari', async () => {
it('Dom', async () => {
await expect(element).dom.to.be.equalSnapshot();
});
Expand All @@ -65,7 +65,7 @@ describe(`sbb-autocomplete`, () => {
`);
});

describeIf(!isSafari(), 'Chrome-Firefox', async () => {
describeIf(!isSafari, 'Chrome-Firefox', async () => {
it('Dom', async () => {
await expect(root).dom.to.be.equalSnapshot();
});
Expand All @@ -75,7 +75,7 @@ describe(`sbb-autocomplete`, () => {
});
});

describeIf(isSafari(), 'Safari', async () => {
describeIf(isSafari, 'Safari', async () => {
it('Dom', async () => {
await expect(root).dom.to.be.equalSnapshot();
});
Expand Down
17 changes: 12 additions & 5 deletions src/components/autocomplete/autocomplete.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
import { html, LitElement, nothing } from 'lit';
import {
type CSSResultGroup,
html,
isServer,
LitElement,
nothing,
type PropertyValues,
type TemplateResult,
} from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { ref } from 'lit/directives/ref.js';

import { getNextElementIndex } from '../core/a11y.js';
import { SbbConnectedAbortController } from '../core/controllers.js';
import { hostAttributes } from '../core/decorators.js';
import { findReferencedElement, getDocumentWritingMode, isBrowser, isSafari } from '../core/dom.js';
import { findReferencedElement, getDocumentWritingMode, isSafari } from '../core/dom.js';
import { EventEmitter } from '../core/eventing.js';
import type { SbbOpenedClosedState } from '../core/interfaces.js';
import { SbbHydrationMixin, SbbNegativeMixin } from '../core/mixins.js';
Expand All @@ -27,7 +34,7 @@ let nextId = 0;
* On Safari, the aria role 'listbox' must be on the host element, or else VoiceOver won't work at all.
* On the other hand, JAWS and NVDA need the role to be "closer" to the options, or else optgroups won't work.
*/
const ariaRoleOnHost = isSafari();
const ariaRoleOnHost = isSafari;

/**
* Combined with a native input, it displays a panel with a list of available options.
Expand Down Expand Up @@ -277,7 +284,7 @@ export class SbbAutocompleteElement extends SbbNegativeMixin(SbbHydrationMixin(L
}

private _componentSetup(): void {
if (!isBrowser()) {
if (isServer) {
return;
}
this._triggerEventsController?.abort();
Expand Down
2 changes: 1 addition & 1 deletion src/components/button/button/button.snapshot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import './button.js';

describe(`sbb-button`, () => {
if (isVisualRegressionRun()) {
if (isVisualRegressionRun) {
describe('visual-regression', () => {
let root: HTMLElement;

Expand Down
6 changes: 3 additions & 3 deletions src/components/checkbox/checkbox/checkbox.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe(`sbb-checkbox with ${fixture.name}`, () => {
})) as unknown as CheckboxAccessibilitySnapshot;

// TODO: Recheck if it is working in Chromium
if (!isChromium()) {
if (!isChromium) {
expect(snapshot.required).to.be.true;
}
});
Expand Down Expand Up @@ -139,7 +139,7 @@ describe(`sbb-checkbox with ${fixture.name}`, () => {
})) as unknown as CheckboxAccessibilitySnapshot;

// TODO: Recheck if it is working in Chromium
if (!isChromium()) {
if (!isChromium) {
expect(snapshot.required).to.be.true;
}
});
Expand Down Expand Up @@ -225,7 +225,7 @@ describe(`sbb-checkbox with ${fixture.name}`, () => {
expect(snapshot.role).to.equal('checkbox');

expect(snapshot.checked, `ariaChecked in ${JSON.stringify(snapshot)}`).to.be.equal(
isFirefox() && assertions.ariaChecked === false ? undefined : assertions.ariaChecked,
isFirefox && assertions.ariaChecked === false ? undefined : assertions.ariaChecked,
);

expect(inputSpy.count, `'input' event`).to.be.equal(assertions.inputEventCount);
Expand Down
5 changes: 3 additions & 2 deletions src/components/core/a11y/input-modality-detector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// This implementation is inspired by https://github.com/angular/components/blob/main/src/cdk/a11y/input-modality/input-modality-detector.ts

import { isBrowser } from '../dom.js';
import { isServer } from 'lit';

import { getEventTarget } from '../eventing.js';

import {
Expand Down Expand Up @@ -166,7 +167,7 @@ class SbbInputModalityDetector {
};

public constructor() {
if (isBrowser()) {
if (!isServer) {
document.addEventListener('keydown', this._onKeydown, modalityEventListenerOptions);
document.addEventListener('mousedown', this._onMousedown, modalityEventListenerOptions);
document.addEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions);
Expand Down
9 changes: 4 additions & 5 deletions src/components/core/controllers/language-controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ReactiveController, ReactiveControllerHost } from 'lit';
import { isServer, type ReactiveController, type ReactiveControllerHost } from 'lit';

import { readConfig } from '../config.js';
import { isBrowser } from '../dom.js';
import { AgnosticMutationObserver } from '../observers.js';

/**
Expand Down Expand Up @@ -34,9 +33,9 @@
public static get current(): string {
const language =
(readConfig().language ??
(isBrowser()
? document.documentElement.getAttribute('lang')
: SbbLanguageController._defaultLanguage)) ||
(isServer
? SbbLanguageController._defaultLanguage

Check warning on line 37 in src/components/core/controllers/language-controller.ts

View check run for this annotation

Codecov / codecov/patch

src/components/core/controllers/language-controller.ts#L37

Added line #L37 was not covered by tests
: document.documentElement.getAttribute('lang'))) ||
SbbLanguageController._defaultLanguage;

// Support e.g. cases like `de-ch`.
Expand Down
4 changes: 2 additions & 2 deletions src/components/core/dom/breakpoint.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isBrowser } from './platform.js';
import { isServer } from 'lit';

export const breakpoints = ['zero', 'micro', 'small', 'medium', 'wide', 'large', 'ultra'] as const;
export type Breakpoint = (typeof breakpoints)[number];
Expand All @@ -16,7 +16,7 @@ export function isBreakpoint(
to?: Breakpoint,
properties?: { includeMaxBreakpoint: boolean },
): boolean {
if (!isBrowser()) {
if (isServer) {
// TODO: Remove and decide case by case what should be done on consuming end
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/core/dom/find-referenced-element.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isBrowser } from './platform.js';
import { isServer } from 'lit';

/**
* Check whether it's a string or an HTMLElement, if it's a string queries the element with the
Expand All @@ -8,7 +8,7 @@ import { isBrowser } from './platform.js';
export function findReferencedElement<T extends HTMLElement = HTMLElement>(
reference: string | HTMLElement,
): T | null {
if (!isBrowser()) {
if (isServer) {
return null;
} else if (typeof reference === 'string') {
return document.getElementById(reference) as T;
Expand Down
4 changes: 2 additions & 2 deletions src/components/core/dom/get-document-writing-mode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isBrowser } from './platform.js';
import { isServer } from 'lit';

export const getDocumentWritingMode = (): string =>
(isBrowser() && document.documentElement.getAttribute('dir')) || 'ltr';
(!isServer && document.documentElement.getAttribute('dir')) || 'ltr';
4 changes: 2 additions & 2 deletions src/components/core/dom/host-context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isBrowser } from './platform.js';
import { isServer } from 'lit';

/**
* Looks for the closest element matching the given selector starting from the given element.
Expand All @@ -9,7 +9,7 @@ import { isBrowser } from './platform.js';
* @returns The closest element matching the selector or null if none is found.
*/
export function hostContext(selector: string, element: Element): Element | null {
if (!isBrowser()) {
if (isServer) {
return null;
}
// Start with parent element in order to avoid finding element itself
Expand Down
46 changes: 19 additions & 27 deletions src/components/core/dom/platform.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// This file is freely inspired by Angular Material solution.
// Reference: https://github.com/angular/components/blob/main/src/cdk/platform/platform.ts
import { isServer } from 'lit';

// Whether the current platform supports the V8 Break Iterator. The V8 check
// is necessary to detect all Blink based browsers.
Expand All @@ -16,64 +17,55 @@ try {
hasV8BreakIterator = false;
}

/** Whether the application is being rendered in the browser. */
export const isBrowser = (): boolean => typeof document === 'object' && !!document;

/** Whether the current browser is Microsoft Edge. */
export const isEdge = (): boolean => isBrowser() && /(edge)/i.test(navigator.userAgent);
export const isEdge = !isServer && /(edge)/i.test(navigator.userAgent);

/** Whether the current rendering engine is Microsoft Trident. */
export const isTrident = (): boolean => isBrowser() && /(msie|trident)/i.test(navigator.userAgent);
export const isTrident = !isServer && /(msie|trident)/i.test(navigator.userAgent);

// EdgeHTML and Trident mock Blink specific things and need to be excluded from this check.
/** Whether the current rendering engine is Blink. */
export const isBlink = (): boolean =>
isBrowser() &&
export const isBlink =
!isServer &&
!!((window as any).chrome || hasV8BreakIterator) &&
typeof CSS !== 'undefined' &&
!isEdge() &&
!isTrident();
!isEdge &&
!isTrident;

// Webkit is part of the userAgent in EdgeHTML, Blink and Trident. Therefore, we need to
// ensure that Webkit runs standalone and is not used as another engine's base.
/** Whether the current rendering engine is WebKit. */
export const isWebkit = (): boolean =>
isBrowser() &&
/AppleWebKit/i.test(navigator.userAgent) &&
!isBlink() &&
!isEdge() &&
!isTrident();
export const isWebkit =
!isServer && /AppleWebKit/i.test(navigator.userAgent) && !isBlink && !isEdge && !isTrident;

/** Whether the current platform is Apple iOS. */
export const isIOS = (): boolean =>
isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent) && !('MSStream' in window);
export const isIOS =
!isServer && /iPad|iPhone|iPod/.test(navigator.userAgent) && !('MSStream' in window);

// It's difficult to detect the plain Gecko engine, because most of the browsers identify
// them self as Gecko-like browsers and modify the userAgent's according to that.
// Since we only cover one explicit Firefox case, we can simply check for Firefox
// instead of having an unstable check for Gecko.
/** Whether the current browser is Firefox. */
export const isFirefox = (): boolean =>
isBrowser() && /(firefox|minefield)/i.test(navigator.userAgent);
export const isFirefox = !isServer && /(firefox|minefield)/i.test(navigator.userAgent);

/** Whether the current platform is Android. */
// Trident on mobile adds the android platform to the userAgent to trick detections.
export const isAndroid = (): boolean =>
isBrowser() && /android/i.test(navigator.userAgent) && !isTrident();
export const isAndroid = !isServer && /android/i.test(navigator.userAgent) && !isTrident;

// Safari browsers will include the Safari keyword in their userAgent. Some browsers may fake
// this and just place the Safari keyword in the userAgent. To be more safe about Safari every
// Safari browser should also use Webkit as its layout engine.
/** Whether the current browser is Safari. */
export const isSafari = (): boolean =>
isBrowser() && /safari/i.test(navigator.userAgent) && isWebkit();

/** Whether the application is being rendered in a Next.js environment. */
export const isNextjs = (): boolean => !!(globalThis as { next?: object }).next;
export const isSafari = !isServer && /safari/i.test(navigator.userAgent) && isWebkit;

/**
* Note: `userAgentData` is still experimental. If possible, avoid to use it.
* https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
*/
export const isChromium = (): boolean =>
export const isChromium =
!isServer &&
(navigator as any).userAgentData?.brands?.some((data: any) => data.brand == 'Chromium');

/** Whether the application is being rendered in a Next.js environment. */
export const isNextjs = (): boolean => !!(globalThis as { next?: object }).next;
6 changes: 3 additions & 3 deletions src/components/core/testing/private/a11y-tree-snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ export function testA11yTreeSnapshot(
await waitForLitRender(document);
});

testIf(isChromium() && !exclude.chrome, 'Chrome', async () => {
testIf(isChromium && !exclude.chrome, 'Chrome', async () => {
await a11yTreeEqualSnapshot();
});

// TODO: Try if tests on CI are no longer flaky
// testIf(isSafari() && !exclude.safari, 'Safari', async () => {
// testIf(isSafari && !exclude.safari, 'Safari', async () => {
// await a11yTreeEqualSnapshot();
// });

testIf(isFirefox() && !exclude.firefox, 'Firefox', async () => {
testIf(isFirefox && !exclude.firefox, 'Firefox', async () => {
await a11yTreeEqualSnapshot();
});
});
Expand Down
8 changes: 4 additions & 4 deletions src/components/core/testing/private/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ export const fixture = Object.defineProperty(
options.modules.unshift('/src/components/core/testing/test-setup-ssr.ts');
const fixtures = await import('@lit-labs/testing/fixtures.js');
let result: T;
if (isHydratedSsr()) {
if (isHydratedSsr) {
result = await fixtures.ssrHydratedFixture<T>(template, options);
result
.parentElement!.querySelectorAll('[defer-hydration]')
.forEach((e) => e.removeAttribute('defer-hydration'));
return result;
} else if (isNonHydratedSsr()) {
} else if (isNonHydratedSsr) {
result = await fixtures.ssrNonHydratedFixture<T>(template, options);
} else {
result = await fixtures.csrFixture<T>(template, options);
Expand All @@ -62,9 +62,9 @@ export const fixture = Object.defineProperty(
'name',
{
get() {
if (isHydratedSsr()) {
if (isHydratedSsr) {
return 'ssrHydratedFixture';
} else if (isNonHydratedSsr()) {
} else if (isNonHydratedSsr) {
return 'ssrNonHydratedFixture';
} else {
return 'csrFixture';
Expand Down
14 changes: 6 additions & 8 deletions src/components/core/testing/private/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,36 @@ import { isServer } from 'lit';
* This is a custom implementation.
* In the `web-test-runner.config` we add data attributes to the body with additional information.
*/
export const isTestEnvironment = (): boolean => !isServer && 'testEnv' in globalThis;
export const isTestEnvironment = !isServer && 'testEnv' in globalThis;

/**
* This is a custom implementation.
* True if the `testEnvironment` meta tag has the `debug` attribute
*/
export const isDebugEnvironment = (): boolean =>
!isServer && (globalThis as any).testEnv === 'debug';
export const isDebugEnvironment = !isServer && (globalThis as any).testEnv === 'debug';

/**
* This is a custom implementation.
* Returns true, if this is run in the SSR with hydration test group.
*/
export const isHydratedSsr = (): boolean =>
!isServer && (globalThis as any).testGroup === 'e2e-ssr-hydrated';
export const isHydratedSsr = !isServer && (globalThis as any).testGroup === 'e2e-ssr-hydrated';

/**
* This is a custom implementation.
* Returns true, if this is run in the SSR without hydration test group.
*/
export const isNonHydratedSsr = (): boolean =>
export const isNonHydratedSsr =
!isServer && (globalThis as any).testGroup === 'e2e-ssr-non-hydrated';

/**
* This is a custom implementation.
* Returns true, if this is run in an SSR test group.
*/
export const isSsr = (): boolean => isHydratedSsr() || isNonHydratedSsr();
export const isSsr = isHydratedSsr || isNonHydratedSsr;

/**
* This is a custom implementation.
* Returns true, if this is run in the visual regression test group.
*/
export const isVisualRegressionRun = (): boolean =>
export const isVisualRegressionRun =
!isServer && (globalThis as any).testGroup === 'visual-regression';
Loading
Loading