/integrations facelift (#858)

This commit is contained in:
Alex Johansson authored and GitHub committed 2021-10-12 11:35:44 +02:00
1 parent 7dc4a55319
commit c3dc18643e
32 files changed
+1259 -716

No files matched your search

+11 -5
View File
@@ -1,7 +1,7 @@
import * as DialogPrimitive from "@radix-ui/react-dialog";
import React from "react";
import React, { ReactNode } from "react";
type DialogProps = React.ComponentProps<typeof DialogPrimitive["Root"]>;
export type DialogProps = React.ComponentProps<typeof DialogPrimitive["Root"]>;
export function Dialog(props: DialogProps) {
const { children, ...other } = props;
return (
@@ -35,9 +35,15 @@ export function DialogHeader({ title, subtitle }: DialogHeaderProps) {
<h3 className="font-cal text-gray-900 text-lg font-bold leading-6" id="modal-title">
{title}
</h3>
<div>
<p className="text-gray-400 text-sm">{subtitle}</p>
</div>
<div className="text-gray-400 text-sm">{subtitle}</div>
</div>
);
}
export function DialogFooter(props: { children: ReactNode }) {
return (
<div>
<div className="mt-5 flex space-x-2 justify-end">{props.children}</div>
</div>
);
}
+72
View File
@@ -0,0 +1,72 @@
import Link from "next/link";
import { createElement } from "react";
import classNames from "@lib/classNames";
export function List(props: JSX.IntrinsicElements["ul"]) {
return (
<ul {...props} className={classNames("overflow-hidden rounded-sm sm:mx-0", props.className)}>
{props.children}
</ul>
);
}
export type ListItemProps = { expanded?: boolean } & ({ href?: never } & JSX.IntrinsicElements["li"]);
export function ListItem(props: ListItemProps) {
const { href, expanded, ...passThroughProps } = props;
const elementType = href ? "a" : "li";
const element = createElement(
elementType,
{
...passThroughProps,
className: classNames(
"items-center bg-white min-w-0 flex-1 flex border-gray-200",
expanded ? "my-2 border" : "border -mb-px last:mb-0",
props.className,
(props.onClick || href) && "hover:bg-neutral-50"
),
},
props.children
);
return href ? (
<Link passHref href={href}>
{element}
</Link>
) : (
element
);
}
export function ListItemTitle<TComponent extends keyof JSX.IntrinsicElements = "span">(
props: JSX.IntrinsicElements[TComponent] & { component?: TComponent }
) {
const { component = "span", ...passThroughProps } = props;
return createElement(
component,
{
...passThroughProps,
className: classNames("text-sm font-medium text-neutral-900 truncate", props.className),
},
props.children
);
}
export function ListItemText<TComponent extends keyof JSX.IntrinsicElements = "span">(
props: JSX.IntrinsicElements[TComponent] & { component?: TComponent }
) {
const { component = "span", ...passThroughProps } = props;
return createElement(
component,
{
...passThroughProps,
className: classNames("text-sm text-gray-500", props.className),
},
props.children
);
}
+31 -6
View File
@@ -19,6 +19,7 @@ import LicenseBanner from "@ee/components/LicenseBanner";
import HelpMenuItemDynamic from "@ee/lib/intercom/HelpMenuItemDynamic";
import classNames from "@lib/classNames";
import { shouldShowOnboarding } from "@lib/getting-started";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
import { trpc } from "@lib/trpc";
@@ -29,10 +30,7 @@ import Logo from "./Logo";
function useMeQuery() {
const [session] = useSession();
const meQuery = trpc.useQuery(["viewer.me"], {
// refetch max once per 5s
staleTime: 5000,
});
const meQuery = trpc.useQuery(["viewer.me"]);
useEffect(() => {
// refetch if sesion changes
@@ -59,6 +57,26 @@ function useRedirectToLoginIfUnauthenticated() {
}, [loading, session, router]);
}
export function ShellSubHeading(props: {
title: ReactNode;
subtitle?: ReactNode;
actions?: ReactNode;
className?: string;
}) {
return (
<div className={classNames("block sm:flex justify-between mb-3", props.className)}>
<div>
{/* TODO should be Roboto */}
<h2 className="text-lg font-bold text-gray-900 flex items-center content-center space-x-2">
{props.title}
</h2>
{props.subtitle && <p className="text-sm text-neutral-500 mr-4">{props.subtitle}</p>}
</div>
{props.actions && <div className="mb-4 flex-shrink-0">{props.actions}</div>}
</div>
);
}
export default function Shell(props: {
centered?: boolean;
title?: string;
@@ -74,6 +92,15 @@ export default function Shell(props: {
const telemetry = useTelemetry();
const query = useMeQuery();
useEffect(
function redirectToOnboardingIfNeeded() {
if (query.data && shouldShowOnboarding(query.data)) {
router.push("/getting-started");
}
},
[query.data, router]
);
const navigation = [
{
name: "Event Types",
@@ -209,7 +236,6 @@ export default function Shell(props: {
<div className="mb-4 flex-shrink-0">{props.CTA}</div>
</div>
<div className="px-4 sm:px-6 md:px-8">{props.children}</div>
{/* show bottom navigation for md and smaller (tablet and phones) */}
<nav className="bottom-nav md:hidden flex fixed bottom-0 bg-white w-full shadow">
{/* note(PeerRich): using flatMap instead of map to remove settings from bottom nav */}
@@ -239,7 +265,6 @@ export default function Shell(props: {
)
)}
</nav>
{/* add padding to content for mobile navigation*/}
<div className="block md:hidden pt-12" />
</div>
+63
View File
@@ -0,0 +1,63 @@
import { useId } from "@radix-ui/react-id";
import { forwardRef, ReactNode } from "react";
import { FormProvider, UseFormReturn } from "react-hook-form";
import classNames from "@lib/classNames";
export const Input = forwardRef<HTMLInputElement, JSX.IntrinsicElements["input"]>(function Input(props, ref) {
return (
<input
{...props}
ref={ref}
className={classNames(
"mt-1 block w-full border border-gray-300 rounded-sm shadow-sm py-2 px-3 focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm",
props.className
)}
/>
);
});
export function Label(props: JSX.IntrinsicElements["label"]) {
return (
<label {...props} className={classNames("block text-sm font-medium text-gray-700", props.className)}>
{props.children}
</label>
);
}
export const TextField = forwardRef<
HTMLInputElement,
{
label: ReactNode;
} & React.ComponentProps<typeof Input> & {
labelProps?: React.ComponentProps<typeof Label>;
}
>(function TextField(props, ref) {
const id = useId();
const { label, ...passThroughToInput } = props;
// TODO: use `useForm()` from RHF and get error state here too!
return (
<div>
<Label htmlFor={id} {...props.labelProps}>
{label}
</Label>
<Input id={id} {...passThroughToInput} ref={ref} />
</div>
);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const Form = forwardRef<HTMLFormElement, { form: UseFormReturn<any> } & JSX.IntrinsicElements["form"]>(
function Form(props, ref) {
const { form, ...passThrough } = props;
return (
<FormProvider {...form}>
<form ref={ref} {...passThrough}>
{props.children}
</form>
</FormProvider>
);
}
);
Whitespace-only changes.
+2
View File
@@ -5,6 +5,7 @@ import { ReactNode } from "react";
export interface AlertProps {
title?: ReactNode;
message?: ReactNode;
actions?: ReactNode;
className?: string;
severity: "success" | "warning" | "error";
}
@@ -36,6 +37,7 @@ export function Alert(props: AlertProps) {
<h3 className="text-sm font-medium">{props.title}</h3>
<div className="text-sm">{props.message}</div>
</div>
{props.actions && <div className="text-sm">{props.actions}</div>}
</div>
</div>
);
+1 -1
View File
@@ -13,7 +13,7 @@ export const Badge = function Badge(props: BadgeProps) {
<span
{...passThroughProps}
className={classNames(
"font-bold px-2 py-0.5 inline-block rounded-sm",
"font-bold px-2 py-0.5 inline-block rounded-sm text-xs",
variant === "default" && "bg-yellow-100 text-yellow-800",
variant === "success" && "bg-green-100 text-green-800",
variant === "gray" && "bg-gray-200 text-gray-800",
+7 -5
View File
@@ -4,7 +4,7 @@ import React, { forwardRef } from "react";
import classNames from "@lib/classNames";
import { SVGComponent } from "@lib/types/SVGComponent";
export type ButtonProps = {
export type ButtonBaseProps = {
color?: "primary" | "secondary" | "minimal" | "warn";
size?: "base" | "sm" | "lg" | "fab" | "icon";
loading?: boolean;
@@ -13,10 +13,12 @@ export type ButtonProps = {
StartIcon?: SVGComponent;
EndIcon?: SVGComponent;
shallow?: boolean;
} & (
| (Omit<JSX.IntrinsicElements["a"], "href"> & { href: LinkProps["href"] })
| (JSX.IntrinsicElements["button"] & { href?: never })
);
};
export type ButtonProps = ButtonBaseProps &
(
| (Omit<JSX.IntrinsicElements["a"], "href"> & { href: LinkProps["href"] })
| (JSX.IntrinsicElements["button"] & { href?: never })
);
export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonProps>(function Button(
props: ButtonProps,
+6 -2
View File
@@ -1,3 +1,4 @@
import { useId } from "@radix-ui/react-id";
import * as Label from "@radix-ui/react-label";
import * as PrimitiveSwitch from "@radix-ui/react-switch";
import { useState } from "react";
@@ -16,7 +17,7 @@ export default function Switch(props) {
}
setChecked(change);
};
const id = useId();
return (
<div className="flex items-center h-[20px]">
<PrimitiveSwitch.Root
@@ -25,6 +26,7 @@ export default function Switch(props) {
onCheckedChange={onPrimitiveCheckedChange}
{...primitiveProps}>
<PrimitiveSwitch.Thumb
id={id}
className={classNames(
"bg-white w-[16px] h-[16px] block transition-transform",
checked ? "translate-x-[16px]" : "translate-x-0"
@@ -32,7 +34,9 @@ export default function Switch(props) {
/>
</PrimitiveSwitch.Root>
{label && (
<Label.Root className="text-neutral-700 text-sm align-text-top ml-3 font-medium cursor-pointer">
<Label.Root
htmlFor={id}
className="text-neutral-700 text-sm align-text-top ml-3 font-medium cursor-pointer">
{label}
</Label.Root>
)}