mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Auto capture
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
This commit is contained in:
parent
f162c8b6eb
commit
98b88180d8
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kube-object-event-status",
|
||||
"version": "0.0.1",
|
||||
"version": "5.5.0-alpha.0.1651664036833",
|
||||
"description": "Adds kube object status from events",
|
||||
"renderer": "dist/renderer.js",
|
||||
"lens": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-metrics-cluster-feature",
|
||||
"version": "0.0.1",
|
||||
"version": "5.5.0-alpha.0.1651664036833",
|
||||
"description": "Lens metrics cluster feature",
|
||||
"renderer": "dist/renderer.js",
|
||||
"lens": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-node-menu",
|
||||
"version": "0.0.1",
|
||||
"version": "5.5.0-alpha.0.1651664036833",
|
||||
"description": "Lens node menu",
|
||||
"renderer": "dist/renderer.js",
|
||||
"lens": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-pod-menu",
|
||||
"version": "0.0.1",
|
||||
"version": "5.5.0-alpha.0.1651664036833",
|
||||
"description": "Lens pod menu",
|
||||
"renderer": "dist/renderer.js",
|
||||
"lens": {
|
||||
|
||||
@ -8,6 +8,11 @@ import type { ButtonHTMLAttributes } from "react";
|
||||
import React from "react";
|
||||
import { cssNames } from "../../utils";
|
||||
import { withTooltip } from "../tooltip";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import trackEventInjectable from "../../telemetry/track-event.injectable";
|
||||
interface Dependencies {
|
||||
track: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<any> {
|
||||
label?: React.ReactNode;
|
||||
@ -25,10 +30,10 @@ export interface ButtonProps extends ButtonHTMLAttributes<any> {
|
||||
target?: "_blank"; // in case of using @href
|
||||
}
|
||||
|
||||
export const Button = withTooltip((props: ButtonProps) => {
|
||||
const NonInjectedButton = withTooltip((props: ButtonProps & Dependencies) => {
|
||||
const {
|
||||
waiting, label, primary, accent, plain, hidden, active, big,
|
||||
round, outlined, light, children, ...btnProps
|
||||
round, outlined, light, children, track, ...btnProps
|
||||
} = props;
|
||||
|
||||
if (hidden) return null;
|
||||
@ -37,10 +42,18 @@ export const Button = withTooltip((props: ButtonProps) => {
|
||||
waiting, primary, accent, plain, active, big, round, outlined, light,
|
||||
});
|
||||
|
||||
const onClick = (e: React.MouseEvent) => {
|
||||
track(e);
|
||||
|
||||
if (btnProps.onClick) {
|
||||
btnProps.onClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
// render as link
|
||||
if (props.href) {
|
||||
return (
|
||||
<a {...btnProps}>
|
||||
<a {...btnProps} onClick={onClick}>
|
||||
{label}
|
||||
{children}
|
||||
</a>
|
||||
@ -49,9 +62,24 @@ export const Button = withTooltip((props: ButtonProps) => {
|
||||
|
||||
// render as button
|
||||
return (
|
||||
<button type="button" {...btnProps}>
|
||||
<button
|
||||
type="button"
|
||||
{...btnProps}
|
||||
onClick={onClick}>
|
||||
{label}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
export const Button = withInjectables<Dependencies, ButtonProps>(
|
||||
NonInjectedButton,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
track: di.inject(trackEventInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@ -7,6 +7,8 @@ import "./checkbox.scss";
|
||||
import React from "react";
|
||||
import type { SingleOrMany } from "../../utils";
|
||||
import { cssNames, noop } from "../../utils";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import trackWithIdInjectable from "../../telemetry/track-with-id.injectable";
|
||||
|
||||
export interface CheckboxProps {
|
||||
className?: string;
|
||||
@ -18,7 +20,11 @@ export interface CheckboxProps {
|
||||
children?: SingleOrMany<React.ReactChild | React.ReactFragment>;
|
||||
}
|
||||
|
||||
export function Checkbox({ label, inline, className, value, children, onChange = noop, disabled, ...inputProps }: CheckboxProps) {
|
||||
interface Dependencies {
|
||||
captureClick: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
function NonInjectedCheckbox({ label, inline, className, value, children, onChange = noop, disabled, captureClick, ...inputProps }: CheckboxProps & Dependencies) {
|
||||
const componentClass = cssNames("Checkbox flex align-center", className, {
|
||||
inline,
|
||||
checked: value,
|
||||
@ -32,7 +38,13 @@ export function Checkbox({ label, inline, className, value, children, onChange =
|
||||
type="checkbox"
|
||||
checked={value}
|
||||
disabled={disabled}
|
||||
onChange={event => onChange(event.target.checked, event)}
|
||||
onChange={event => {
|
||||
if (label) {
|
||||
captureClick(`${window.location.pathname} ${label.toString()}`, `Checkbox ${event.target.checked ? "On" : "Off"}`);
|
||||
}
|
||||
|
||||
onChange(event.target.checked, event);
|
||||
}}
|
||||
/>
|
||||
<i className="box flex align-center"/>
|
||||
{label ? <span className="label">{label}</span> : null}
|
||||
@ -40,3 +52,14 @@ export function Checkbox({ label, inline, className, value, children, onChange =
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export const Checkbox = withInjectables<Dependencies, CheckboxProps>(
|
||||
NonInjectedCheckbox,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
captureClick: di.inject(trackWithIdInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@ -18,6 +18,7 @@ import { Tooltip } from "../tooltip";
|
||||
import type { NormalizeCatalogEntityContextMenu } from "../../catalog/normalize-menu-item.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import normalizeCatalogEntityContextMenuInjectable from "../../catalog/normalize-menu-item.injectable";
|
||||
import trackWithIdInjectable from "../../../renderer/telemetry/track-with-id.injectable";
|
||||
|
||||
export interface HotbarIconProps extends AvatarProps {
|
||||
uid: string;
|
||||
@ -32,6 +33,7 @@ export interface HotbarIconProps extends AvatarProps {
|
||||
|
||||
interface Dependencies {
|
||||
normalizeMenuItem: NormalizeCatalogEntityContextMenu;
|
||||
capture: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
const NonInjectedHotbarIcon = observer(({
|
||||
@ -41,7 +43,7 @@ const NonInjectedHotbarIcon = observer(({
|
||||
normalizeMenuItem,
|
||||
...props
|
||||
}: HotbarIconProps & Dependencies) => {
|
||||
const { uid, title, src, material, active, className, source, disabled, onMenuOpen, onClick, children, ...rest } = props;
|
||||
const { uid, title, src, material, active, className, source, disabled, onMenuOpen, onClick, children, capture, ...rest } = props;
|
||||
const id = `hotbarIcon-${uid}`;
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
@ -65,8 +67,13 @@ const NonInjectedHotbarIcon = observer(({
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
src={src}
|
||||
onClick={(event) => !disabled && onClick?.(event)}
|
||||
>
|
||||
onClick={(event) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
capture(title, "Click Hotbar Item");
|
||||
onClick?.(event);
|
||||
}}>
|
||||
{material && <Icon material={material} />}
|
||||
</Avatar>
|
||||
{children}
|
||||
@ -100,5 +107,6 @@ export const HotbarIcon = withInjectables<Dependencies, HotbarIconProps>(NonInje
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
normalizeMenuItem: di.inject(normalizeCatalogEntityContextMenuInjectable),
|
||||
capture: di.inject(trackWithIdInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -32,6 +32,7 @@ import userStoreInjectable from "../../../common/user-store/user-store.injectabl
|
||||
import pageFiltersStoreInjectable from "./page-filters/store.injectable";
|
||||
import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable";
|
||||
import openConfirmDialogInjectable from "../confirm-dialog/open.injectable";
|
||||
import trackWithIdInjectable from "../../../renderer/telemetry/track-with-id.injectable";
|
||||
|
||||
export interface ItemListLayoutContentProps<Item extends ItemObject, PreLoadStores extends boolean> {
|
||||
getFilters: () => Filter[];
|
||||
@ -75,6 +76,7 @@ interface Dependencies {
|
||||
userStore: UserStore;
|
||||
pageFiltersStore: PageFiltersStore;
|
||||
openConfirmDialog: OpenConfirmDialog;
|
||||
capture: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
@observer
|
||||
@ -110,7 +112,11 @@ class NonInjectedItemListLayoutContent<
|
||||
searchItem={item}
|
||||
sortItem={item}
|
||||
selected={detailsItem && detailsItem.getId() === item.getId()}
|
||||
onClick={hasDetailsView ? prevDefault(() => onDetails?.(item)) : undefined}
|
||||
onClick={hasDetailsView ? prevDefault(() => {
|
||||
this.props.capture(this.props.tableId, "Table Row Click");
|
||||
|
||||
return onDetails?.(item);
|
||||
}) : undefined}
|
||||
{...customizeTableRowProps(item)}
|
||||
>
|
||||
{isSelectable && (
|
||||
@ -381,5 +387,6 @@ export const ItemListLayoutContent = withInjectables<Dependencies, ItemListLayou
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
pageFiltersStore: di.inject(pageFiltersStoreInjectable),
|
||||
openConfirmDialog: di.inject(openConfirmDialogInjectable),
|
||||
capture: di.inject(trackWithIdInjectable),
|
||||
}),
|
||||
}) as <Item extends ItemObject, PreLoadStores extends boolean>(props: ItemListLayoutContentProps<Item, PreLoadStores>) => React.ReactElement;
|
||||
|
||||
@ -8,13 +8,26 @@ import styles from "./close-button.module.scss";
|
||||
import type { HTMLAttributes } from "react";
|
||||
import React from "react";
|
||||
import { Icon } from "../icon";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import trackWithIdInjectable from "../../telemetry/track-with-id.injectable";
|
||||
|
||||
export interface CloseButtonProps extends HTMLAttributes<HTMLDivElement> {
|
||||
}
|
||||
|
||||
export function CloseButton(props: CloseButtonProps) {
|
||||
interface Dependencies {
|
||||
capture: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
function NonInjectedCloseButton(props: CloseButtonProps & Dependencies) {
|
||||
const { capture, ...rest } = props;
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
<div
|
||||
{...rest}
|
||||
onClick={(e) => {
|
||||
capture(`${window.location.pathname}`, "Close Button Click");
|
||||
props?.onClick(e);
|
||||
}}>
|
||||
<div
|
||||
className={styles.closeButton}
|
||||
role="button"
|
||||
@ -28,3 +41,14 @@ export function CloseButton(props: CloseButtonProps) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const CloseButton = withInjectables<Dependencies, CloseButtonProps>(
|
||||
NonInjectedCloseButton,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
capture: di.inject(trackWithIdInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@ -16,9 +16,11 @@ import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { SidebarStorageState } from "./sidebar-storage/sidebar-storage.injectable";
|
||||
import sidebarStorageInjectable from "./sidebar-storage/sidebar-storage.injectable";
|
||||
import type { HierarchicalSidebarItem } from "./sidebar-items.injectable";
|
||||
import trackWithIdInjectable from "../../../renderer/telemetry/track-with-id.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
sidebarStorage: StorageLayer<SidebarStorageState>;
|
||||
capture: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
export interface SidebarItemProps {
|
||||
@ -96,6 +98,8 @@ class NonInjectedSidebarItem extends React.Component<
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.props.capture(this.registration.title.toString(), "Click Side Bar Item");
|
||||
|
||||
if (this.isExpandable) {
|
||||
this.toggleExpand();
|
||||
} else {
|
||||
@ -125,5 +129,7 @@ export const SidebarItem = withInjectables<Dependencies, SidebarItemProps>(NonIn
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
sidebarStorage: di.inject(sidebarStorageInjectable),
|
||||
capture: di.inject(trackWithIdInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -11,17 +11,23 @@ import { cssNames } from "../../utils";
|
||||
import { Tab, Tabs } from "../tabs";
|
||||
import { ErrorBoundary } from "../error-boundary";
|
||||
import type { HierarchicalSidebarItem } from "./sidebar-items.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import trackWithIdInjectable from "../../../renderer/telemetry/track-with-id.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
captureClick: (id: string, action: string) => void;
|
||||
}
|
||||
export interface TabLayoutProps {
|
||||
tabs?: HierarchicalSidebarItem[];
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const TabLayout = observer(
|
||||
const NonInjectedTabLayout = observer(
|
||||
({
|
||||
tabs = [],
|
||||
children,
|
||||
}: TabLayoutProps) => {
|
||||
captureClick,
|
||||
}: TabLayoutProps & Dependencies) => {
|
||||
const hasTabs = tabs.length > 0;
|
||||
|
||||
return (
|
||||
@ -37,7 +43,10 @@ export const TabLayout = observer(
|
||||
|
||||
return (
|
||||
<Tab
|
||||
onClick={registration.onClick}
|
||||
onClick={() => {
|
||||
captureClick(registration.title.toString(), "Tab Click");
|
||||
registration.onClick();
|
||||
}}
|
||||
key={registration.id}
|
||||
label={registration.title}
|
||||
active={active}
|
||||
@ -58,3 +67,14 @@ export const TabLayout = observer(
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const TabLayout = withInjectables<Dependencies, TabLayoutProps>(
|
||||
NonInjectedTabLayout,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
captureClick: di.inject(trackWithIdInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@ -13,6 +13,8 @@ import { Animate } from "../animate";
|
||||
import type { IconProps } from "../icon";
|
||||
import { Icon } from "../icon";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import trackWithIdInjectable from "../../../renderer/telemetry/track-with-id.injectable";
|
||||
|
||||
export const MenuContext = React.createContext<MenuContextValue | null>(null);
|
||||
export type MenuContextValue = Menu;
|
||||
@ -71,7 +73,7 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
}
|
||||
public opener: HTMLElement | null = null;
|
||||
public elem: HTMLUListElement | null = null;
|
||||
protected items: { [index: number]: MenuItem } = {};
|
||||
protected items: { [index: number]: NonInjectedMenuItem } = {};
|
||||
public state: State = {};
|
||||
|
||||
get isOpen() {
|
||||
@ -308,7 +310,7 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
this.elem = elem;
|
||||
}
|
||||
|
||||
protected bindItemRef(item: MenuItem, index: number) {
|
||||
protected bindItemRef(item: NonInjectedMenuItem, index: number) {
|
||||
this.items[index] = item;
|
||||
}
|
||||
|
||||
@ -328,7 +330,7 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
const menuItems = React.Children.toArray(children).map((item, index) => {
|
||||
if (typeof item === "object" && (item as ReactElement).type === MenuItem) {
|
||||
return React.cloneElement(item as ReactElement, {
|
||||
ref: (item: MenuItem) => this.bindItemRef(item, index),
|
||||
ref: (item: NonInjectedMenuItem) => this.bindItemRef(item, index),
|
||||
});
|
||||
}
|
||||
|
||||
@ -389,6 +391,9 @@ export function SubMenu(props: Partial<MenuProps>) {
|
||||
);
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
captureClick: (id: string, action: string) => void;
|
||||
}
|
||||
export interface MenuItemProps extends React.HTMLProps<any> {
|
||||
icon?: string | Partial<IconProps>;
|
||||
disabled?: boolean;
|
||||
@ -401,14 +406,14 @@ const defaultPropsMenuItem: Partial<MenuItemProps> = {
|
||||
onClick: noop,
|
||||
};
|
||||
|
||||
export class MenuItem extends React.Component<MenuItemProps> {
|
||||
class NonInjectedMenuItem extends React.Component<MenuItemProps & Dependencies> {
|
||||
static defaultProps = defaultPropsMenuItem as object;
|
||||
static contextType = MenuContext;
|
||||
|
||||
declare context: MenuContextValue;
|
||||
public elem: HTMLElement | null = null;
|
||||
|
||||
constructor(props: MenuItemProps) {
|
||||
constructor(props: MenuItemProps & Dependencies) {
|
||||
super(props);
|
||||
autoBind(this);
|
||||
}
|
||||
@ -431,6 +436,14 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
onClick!(evt);
|
||||
|
||||
const name = this.elem.querySelectorAll(".title")[0]?.textContent;
|
||||
|
||||
if (name) {
|
||||
const id = `${window.location.pathname.split("/").pop()} ${name}`;
|
||||
|
||||
this.props.captureClick(id, "Menu Item Click");
|
||||
}
|
||||
|
||||
if (menu.props.closeOnClickItem && !evt.defaultPrevented) {
|
||||
menu.close();
|
||||
}
|
||||
@ -441,7 +454,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, disabled, active, spacer, icon, children, ...props } = this.props;
|
||||
const { className, disabled, active, spacer, icon, children, captureClick, ...props } = this.props;
|
||||
const iconProps: Partial<IconProps> = {};
|
||||
|
||||
if (icon) {
|
||||
@ -474,3 +487,14 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||
return <li {...elemProps}/>;
|
||||
}
|
||||
}
|
||||
|
||||
export const MenuItem = withInjectables<Dependencies, MenuItemProps>(
|
||||
NonInjectedMenuItem,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
captureClick: di.inject(trackWithIdInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@ -17,6 +17,7 @@ import type { ThemeStore } from "../../themes/store";
|
||||
import { autoBind, cssNames } from "../../utils";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import themeStoreInjectable from "../../themes/store.injectable";
|
||||
import trackWithIdInjectable from "../../telemetry/track-with-id.injectable";
|
||||
|
||||
const { Menu } = components;
|
||||
|
||||
@ -81,6 +82,7 @@ const defaultFilter = createFilter({
|
||||
|
||||
interface Dependencies {
|
||||
themeStore: ThemeStore;
|
||||
capture: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
export function onMultiSelectFor<Value, Option extends SelectOption<Value>, Group extends GroupBase<Option> = GroupBase<Option>>(collection: Set<Value> | ObservableSet<Value>): SelectProps<Value, Option, true, Group>["onChange"] {
|
||||
@ -228,7 +230,13 @@ class NonInjectedSelect<
|
||||
onKeyDown={this.onKeyDown}
|
||||
className={cssNames("Select", this.themeClass, className)}
|
||||
classNamePrefix="Select"
|
||||
onChange={action(onChange)} // This is done so that all changes are actionable
|
||||
onChange={action(() => {
|
||||
if (inputId) {
|
||||
props.capture(inputId, "Select Change");
|
||||
}
|
||||
|
||||
onChange();
|
||||
})} // This is done so that all changes are actionable
|
||||
components={{
|
||||
...components,
|
||||
Menu: ({ className, ...props }) => (
|
||||
@ -249,6 +257,7 @@ export const Select = withInjectables<Dependencies, SelectProps<unknown, SelectO
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
themeStore: di.inject(themeStoreInjectable),
|
||||
capture: di.inject(trackWithIdInjectable),
|
||||
}),
|
||||
}) as <
|
||||
Value,
|
||||
|
||||
@ -8,12 +8,18 @@ import styles from "./switch.module.scss";
|
||||
import type { ChangeEvent, HTMLProps } from "react";
|
||||
import React from "react";
|
||||
import { cssNames } from "../../utils";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import trackWithIdInjectable from "../../telemetry/track-with-id.injectable";
|
||||
|
||||
export interface SwitchProps extends Omit<HTMLProps<HTMLInputElement>, "onChange"> {
|
||||
onChange?: (checked: boolean, event: ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
export function Switch({ children, disabled, onChange, ...props }: SwitchProps) {
|
||||
interface Dependencies {
|
||||
captureChange: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
function NonInjectedSwitch({ children, disabled, onChange, captureChange, ...props }: SwitchProps & Dependencies) {
|
||||
return (
|
||||
<label className={cssNames(styles.Switch, { [styles.disabled]: disabled })} data-testid="switch">
|
||||
{children}
|
||||
@ -21,9 +27,23 @@ export function Switch({ children, disabled, onChange, ...props }: SwitchProps)
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
disabled={disabled}
|
||||
onChange={(event) => onChange?.(event.target.checked, event)}
|
||||
onChange={(event) =>{
|
||||
onChange?.(event.target.checked, event);
|
||||
captureChange(children.toString(), `Switch ${props.checked ? "On" : "Off"}`);
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export const Switch = withInjectables<Dependencies, SwitchProps>(
|
||||
NonInjectedSwitch,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
captureChange: di.inject(trackWithIdInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@ -8,6 +8,8 @@ import type { DOMAttributes } from "react";
|
||||
import React from "react";
|
||||
import { autoBind, cssNames } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import trackWithIdInjectable from "../../../renderer/telemetry/track-with-id.injectable";
|
||||
|
||||
const TabsContext = React.createContext<TabsContextValue>({});
|
||||
|
||||
@ -59,12 +61,12 @@ export interface TabProps<D = any> extends DOMAttributes<HTMLElement> {
|
||||
value?: D;
|
||||
}
|
||||
|
||||
export class Tab extends React.PureComponent<TabProps> {
|
||||
class NonInjectedTab extends React.PureComponent<TabProps & Dependencies> {
|
||||
static contextType = TabsContext;
|
||||
declare context: TabsContextValue;
|
||||
public ref = React.createRef<HTMLDivElement>();
|
||||
|
||||
constructor(props: TabProps) {
|
||||
constructor(props: TabProps & Dependencies) {
|
||||
super(props);
|
||||
autoBind(this);
|
||||
}
|
||||
@ -91,6 +93,9 @@ export class Tab extends React.PureComponent<TabProps> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value?.kind) {
|
||||
this.props.capture(`${value?.kind} `, "Tab Click");
|
||||
}
|
||||
onClick?.(evt);
|
||||
this.context.onChange?.(value);
|
||||
}
|
||||
@ -142,3 +147,18 @@ export class Tab extends React.PureComponent<TabProps> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
capture: (id: string, action: string) => void;
|
||||
}
|
||||
|
||||
export const Tab = withInjectables<Dependencies, TabProps>(
|
||||
NonInjectedTab,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
capture: di.inject(trackWithIdInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
85
src/renderer/telemetry/telemetry.injectable.ts
Normal file
85
src/renderer/telemetry/telemetry.injectable.ts
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { AppEvent } from "../../common/app-event-bus/event-bus";
|
||||
import appEventBusInjectable from "../../common/app-event-bus/app-event-bus.injectable";
|
||||
import type { EventEmitter } from "../../common/event-emitter";
|
||||
|
||||
function getButtonEventName(el: HTMLElement) {
|
||||
let headers: string[] = [];
|
||||
const levels = 3;
|
||||
let parent = el;
|
||||
|
||||
for (let i = 0; i < levels; i++) {
|
||||
if (parent.parentElement) {
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
const nodelist = parent.querySelectorAll("h1, h2, h3, h4, h5, h6, .header");
|
||||
|
||||
nodelist.forEach(node => headers.push(node.textContent));
|
||||
|
||||
if (headers.length === 0) {
|
||||
const path = window.location.pathname.split("/");
|
||||
|
||||
headers.push(path[path.length-1]);
|
||||
}
|
||||
|
||||
headers = [...new Set(headers)];
|
||||
headers.push(el.textContent);
|
||||
const buttonEventName = headers.join(" ");
|
||||
|
||||
return buttonEventName;
|
||||
}
|
||||
|
||||
// foo_bar-baz => Foo Bar Baz
|
||||
function getNameFromId(id: string) {
|
||||
return id.split(/[_,-]/).map((part) => `${part[0].toUpperCase()+part.substring(1)}`).join("");
|
||||
}
|
||||
|
||||
export class Telemetry {
|
||||
private eventBus: EventEmitter<[AppEvent]>;
|
||||
private destination = "jep";
|
||||
private debug = false;
|
||||
|
||||
constructor(eventBus: EventEmitter<[AppEvent]>) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
private emitEvent(action: string, name: string) {
|
||||
console.log(`[TELEMETRY]: ${action} ${name}`);
|
||||
|
||||
if (!this.debug) {
|
||||
this.eventBus.emit({
|
||||
destination: this.destination,
|
||||
name,
|
||||
action,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buttonClickEvent(e: React.MouseEvent) {
|
||||
const el = e.target as HTMLElement;
|
||||
|
||||
this.emitEvent("Click", getButtonEventName(el));
|
||||
}
|
||||
|
||||
selectChangeEvent(id: string) {
|
||||
this.emitEvent("Select Change", getNameFromId(id));
|
||||
}
|
||||
|
||||
tableRowClick(id: string) {
|
||||
this.emitEvent("Table Row Click", getNameFromId(id));
|
||||
}
|
||||
}
|
||||
|
||||
const telemetryInjectable = getInjectable({
|
||||
id: "telemetry",
|
||||
instantiate: (di) => new Telemetry(di.inject(appEventBusInjectable)),
|
||||
});
|
||||
|
||||
export default telemetryInjectable;
|
||||
63
src/renderer/telemetry/track-event.injectable.ts
Normal file
63
src/renderer/telemetry/track-event.injectable.ts
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { AppEvent } from "../../common/app-event-bus/event-bus";
|
||||
import appEventBusInjectable from "../../common/app-event-bus/app-event-bus.injectable";
|
||||
import type { EventEmitter } from "../../common/event-emitter";
|
||||
import capitalize from "lodash/capitalize";
|
||||
|
||||
function getEventName(el: HTMLElement) {
|
||||
let headers: string[] = [];
|
||||
const levels = 3;
|
||||
let parent = el;
|
||||
|
||||
const path = window.location.pathname.split("/");
|
||||
|
||||
headers.push(capitalize(path[path.length-1]));
|
||||
|
||||
for (let i = 0; i < levels; i++) {
|
||||
if (parent.parentElement) {
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
const nodelist = parent.querySelectorAll("h1, h2, h3, h4, h5, h6, .header");
|
||||
|
||||
nodelist.forEach(node => node.textContent && headers.push(node.textContent));
|
||||
|
||||
headers = [...new Set(headers)];
|
||||
|
||||
if (el?.textContent) {
|
||||
headers.push(el.textContent);
|
||||
}
|
||||
const eventName = headers.join(" ");
|
||||
|
||||
return eventName;
|
||||
}
|
||||
|
||||
function trackEventName(eventBus: EventEmitter<[AppEvent]>, event: React.MouseEvent) {
|
||||
const name = getEventName(event.target as HTMLElement);
|
||||
const action = capitalize(event.type);
|
||||
|
||||
console.log("track event name");
|
||||
console.log(`${action} ${name}`);
|
||||
|
||||
eventBus.emit({
|
||||
destination: "MixPanel",
|
||||
name,
|
||||
action: capitalize(event.type),
|
||||
});
|
||||
}
|
||||
|
||||
const trackEventInjectable = getInjectable({
|
||||
id: "track-mouse-event",
|
||||
instantiate: (di) => {
|
||||
return (event: React.MouseEvent) => {
|
||||
return trackEventName(di.inject(appEventBusInjectable), event);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default trackEventInjectable;
|
||||
36
src/renderer/telemetry/track-with-id.injectable.ts
Normal file
36
src/renderer/telemetry/track-with-id.injectable.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { AppEvent } from "../../common/app-event-bus/event-bus";
|
||||
import appEventBusInjectable from "../../common/app-event-bus/app-event-bus.injectable";
|
||||
import type { EventEmitter } from "../../common/event-emitter";
|
||||
|
||||
// foo_bar-baz => Foo Bar Baz
|
||||
function getNameFromId(id: string) {
|
||||
return id.split(/[/,-,--]/).filter(Boolean).map((part) => `${part[0].toUpperCase()+part.substring(1)}`).join("");
|
||||
}
|
||||
|
||||
function trackWithId(eventBus: EventEmitter<[AppEvent]>, id: string, action: string) {
|
||||
const target = getNameFromId(id);
|
||||
|
||||
console.log(`[trackWithId]: ${name}`);
|
||||
|
||||
eventBus.emit({
|
||||
name: target,
|
||||
action,
|
||||
destination: "MixPanel",
|
||||
});
|
||||
}
|
||||
|
||||
const trackWithIdInjectable = getInjectable({
|
||||
id: "track-with-id",
|
||||
instantiate: (di) => {
|
||||
return (id: string, action: string) => {
|
||||
return trackWithId(di.inject(appEventBusInjectable), id, action);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default trackWithIdInjectable;
|
||||
Loading…
Reference in New Issue
Block a user