Ensure topbanner is always on top of page layout rather than inside main content (#6098)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
Jeroen Reumkens
2022-12-19 17:37:20 +00:00
committed by GitHub
co-authored by Peer Richelsen
parent 48611d5c99
commit 795574ef88
5 changed files with 108 additions and 98 deletions
@@ -1458,6 +1458,8 @@
"event_type_duplicate_copy_text": "{{slug}}-copy",
"set_as_default": "Set as default",
"hide_eventtype_details": "Hide EventType Details",
"show_navigation": "Show navigation",
"hide_navigation": "Hide navigation",
"verification_code_sent": "Verification code sent",
"verified_successfully": "Verified successfully",
"wrong_code": "Wong verification code",
@@ -16,9 +16,11 @@ export function TeamsUpgradeBanner() {
showToast(error.message, "error");
},
});
if (!data) return null;
const [membership] = data;
if (!membership) return null;
return (
<TopBanner
text={t("team_upgrade_banner_description", { teamName: membership.team.name })}
@@ -4,6 +4,8 @@ import classNames from "classnames";
import noop from "lodash/noop";
import { ReactNode } from "react";
import { Icon } from "@calcom/ui";
export type TopBannerProps = {
text: string;
variant?: keyof typeof variantClassName;
@@ -23,17 +25,20 @@ export function TopBanner(props: TopBannerProps) {
<div
data-testid="banner"
className={classNames(
"flex min-h-[40px] w-full items-start justify-between gap-8 bg-gray-50 py-2 px-4 text-center lg:items-center",
"flex min-h-[40px] w-full items-start justify-between gap-8 py-2 px-4 text-center lg:items-center",
variantClassName[variant]
)}>
<div className="flex flex-1 flex-col items-start justify-center gap-2 p-1 lg:flex-row lg:items-center">
<p className="flex flex-col items-start justify-center gap-2 text-left font-sans text-sm font-medium leading-4 text-gray-900 lg:flex-row lg:items-center">
{["warning", "error"].includes(variant) && (
<ExclamationIcon className="h-5 w-5 text-black" aria-hidden="true" />
{variant === "error" && (
<Icon.FiAlertTriangle className="h-4 w-4 stroke-[2.5px] text-black" aria-hidden="true" />
)}
{variant === "warning" && (
<Icon.FiInfo className="h-4 w-4 stroke-[2.5px] text-black" aria-hidden="true" />
)}
{text}
</p>
{actions && <div className="text-sm">{actions}</div>}
{actions && <div className="text-sm font-medium">{actions}</div>}
</div>
{typeof onClose === "function" && (
<button
+59 -79
View File
@@ -153,12 +153,14 @@ const Layout = (props: LayoutProps) => {
{/* todo: only run this if timezone is different */}
<TimezoneChangeDialog />
<div className="h-screen overflow-hidden">
<div className="flex h-screen overflow-hidden" data-testid="dashboard-shell">
<div className="flex min-h-screen flex-col">
<div className="divide-y divide-black">
<TeamsUpgradeBanner />
<ImpersonatingBanner />
</div>
<div className="flex flex-1" data-testid="dashboard-shell">
{props.SidebarContainer || <SideBarContainer />}
<div className="flex w-0 flex-1 flex-col overflow-hidden">
<TeamsUpgradeBanner />
<ImpersonatingBanner />
<div className="flex w-0 flex-1 flex-col">
<MainContainer {...props} />
</div>
</div>
@@ -177,7 +179,6 @@ type LayoutProps = {
children: ReactNode;
CTA?: ReactNode;
large?: boolean;
SettingsSidebarContainer?: ReactNode;
MobileNavigationContainer?: ReactNode;
SidebarContainer?: ReactNode;
TopNavContainer?: ReactNode;
@@ -540,7 +541,7 @@ const { desktopNavigationItems, mobileNavigationBottomItems, mobileNavigationMor
Record<string, NavigationItemType[]>
>(
(items, item, index) => {
// We filter out the "more" separator in desktop navigation
// We filter out the "more" separator in` desktop navigation
if (item.name !== MORE_SEPARATOR_NAME) items.desktopNavigationItems.push(item);
// Items for mobile bottom navigation
if (index < moreSeparatorIndex + 1 && !item.onlyDesktop) items.mobileNavigationBottomItems.push(item);
@@ -714,9 +715,6 @@ const MobileNavigationMoreItem: React.FC<{
};
function DeploymentInfo() {
const query = useMeQuery();
const user = query.data;
return (
<small
style={{
@@ -742,59 +740,59 @@ function SideBarContainer() {
function SideBar() {
return (
<aside className="desktop-transparent hidden w-14 flex-col border-r border-gray-100 bg-gray-50 md:flex lg:w-56 lg:flex-shrink-0 lg:px-4">
<div className="flex h-0 flex-1 flex-col overflow-y-auto pt-3 pb-4 lg:pt-5">
<header className="items-center justify-between md:hidden lg:flex">
<div className="relative">
<aside className="desktop-transparent top-0 hidden h-full max-h-screen w-14 flex-col overflow-y-auto border-r border-gray-100 bg-gray-50 md:sticky md:flex lg:w-56 lg:px-4">
<div className="flex flex-col pt-3 pb-32 lg:pt-5">
<header className="items-center justify-between md:hidden lg:flex">
<Link href="/event-types">
<a className="px-4">
<Logo small />
</a>
</Link>
<div className="flex space-x-2">
<button
color="minimal"
onClick={() => window.history.back()}
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
<Icon.FiArrowLeft className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
</button>
<button
color="minimal"
onClick={() => window.history.forward()}
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
<Icon.FiArrowRight className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
</button>
<KBarTrigger />
</div>
</header>
<hr className="desktop-only absolute -left-3 -right-3 mt-4 block w-full border-gray-200" />
{/* logo icon for tablet */}
<Link href="/event-types">
<a className="px-4">
<Logo small />
<a className="text-center md:inline lg:hidden">
<Logo small icon />
</a>
</Link>
<div className="flex space-x-2">
<button
color="minimal"
onClick={() => window.history.back()}
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
<Icon.FiArrowLeft className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
</button>
<button
color="minimal"
onClick={() => window.history.forward()}
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
<Icon.FiArrowRight className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
</button>
<KBarTrigger />
<Navigation />
</div>
{isCalcom && <Tips />}
<div className="fixed left-1 bottom-0 w-4 bg-gray-50 before:absolute before:left-0 before:-top-20 before:h-20 before:w-48 before:bg-gradient-to-t before:from-gray-50 before:to-transparent lg:left-4 lg:w-48">
<div data-testid="user-dropdown-trigger">
<span className="hidden lg:inline">
<UserDropdown />
</span>
<span className="hidden md:inline lg:hidden">
<UserDropdown small />
</span>
</div>
</header>
<hr className="desktop-only absolute -left-3 -right-3 mt-4 block w-full border-gray-200" />
{/* logo icon for tablet */}
<Link href="/event-types">
<a className="text-center md:inline lg:hidden">
<Logo small icon />
</a>
</Link>
<Navigation />
</div>
{isCalcom && <Tips />}
{/* Save it for next preview version
<div className="hidden mb-4 lg:block">
<UserV2OptInBanner />
</div> */}
<div data-testid="user-dropdown-trigger">
<span className="hidden lg:inline">
<UserDropdown />
</span>
<span className="hidden md:inline lg:hidden">
<UserDropdown small />
</span>
</div>
<DeploymentInfo />
</aside>
<DeploymentInfo />
</div>
</aside>
</div>
);
}
@@ -855,35 +853,17 @@ export function ShellMain(props: LayoutProps) {
);
}
const SettingsSidebarContainerDefault = () => null;
function MainContainer({
SettingsSidebarContainer: SettingsSidebarContainerProp = <SettingsSidebarContainerDefault />,
MobileNavigationContainer: MobileNavigationContainerProp = <MobileNavigationContainer />,
TopNavContainer: TopNavContainerProp = <TopNavContainer />,
...props
}: LayoutProps) {
const [sideContainerOpen, setSideContainerOpen] = props.drawerState || [false, noop];
return (
<main className="relative z-0 flex flex-1 flex-col overflow-y-auto bg-white focus:outline-none">
<main className="relative z-0 flex-1 bg-white focus:outline-none">
{/* show top navigation for md and smaller (tablet and phones) */}
{TopNavContainerProp}
{/* The following is used for settings navigation on medium and smaller screens */}
<div
className={classNames(
"overflow-none fixed z-40 m-0 h-screen w-screen overscroll-none bg-black opacity-50",
sideContainerOpen ? "" : "hidden"
)}
onClick={() => {
setSideContainerOpen(false);
}}
/>
{SettingsSidebarContainerProp}
<div className="max-w-full px-4 py-2 lg:py-8 lg:px-12">
<ErrorBoundary>
{/* add padding to top for mobile when App Bar is fixed */}
<div className="pt-14 sm:hidden" />
{!props.withoutMain ? <ShellMain {...props}>{props.children}</ShellMain> : props.children}
</ErrorBoundary>
{/* show bottom navigation for md and smaller (tablet and phones) on pages where back button doesn't exist */}
@@ -906,7 +886,7 @@ function TopNav() {
<>
<nav
style={isEmbed ? { display: "none" } : {}}
className="fixed z-40 flex w-full items-center justify-between border-b border-gray-200 bg-gray-50 bg-opacity-50 py-1.5 px-4 backdrop-blur-lg sm:relative sm:p-4 md:hidden">
className="sticky top-0 z-40 flex w-full items-center justify-between border-b border-gray-200 bg-gray-50 bg-opacity-50 py-1.5 px-4 backdrop-blur-lg sm:p-4 md:hidden">
<Link href="/event-types">
<a>
<Logo />
+36 -15
View File
@@ -100,7 +100,15 @@ const useTabs = () => {
});
};
const SettingsSidebarContainer = ({ className = "" }) => {
interface SettingsSidebarContainerProps {
className?: string;
navigationIsOpenedOnMobile?: boolean;
}
const SettingsSidebarContainer = ({
className = "",
navigationIsOpenedOnMobile,
}: SettingsSidebarContainerProps) => {
const { t } = useLocale();
const router = useRouter();
const tabsWithPermissions = useTabs();
@@ -127,7 +135,13 @@ const SettingsSidebarContainer = ({ className = "" }) => {
return (
<nav
className={`no-scrollbar flex w-56 flex-col space-y-1 overflow-x-hidden overflow-y-scroll py-3 px-2 ${className}`}
className={classNames(
"no-scrollbar fixed left-0 top-0 z-10 flex max-h-screen w-56 flex-col space-y-1 overflow-x-hidden overflow-y-scroll bg-gray-50 py-3 px-2 transition-transform lg:sticky lg:flex",
className,
navigationIsOpenedOnMobile
? "translate-x-0 opacity-100"
: "-translate-x-full opacity-0 lg:translate-x-0 lg:opacity-100"
)}
aria-label="Tabs">
<>
<div className="desktop-only pt-4" />
@@ -295,9 +309,12 @@ const MobileSettingsContainer = (props: { onSideContainerOpen?: () => void }) =>
return (
<>
<nav className="fixed z-20 flex w-full items-center justify-between border-b border-gray-100 bg-gray-50 p-4 sm:relative lg:hidden">
<nav className="sticky top-0 z-20 flex w-full items-center justify-between border-b border-gray-100 bg-gray-50 p-4 sm:relative lg:hidden">
<div className="flex items-center space-x-3 ">
<Button StartIcon={Icon.FiMenu} color="minimal" size="icon" onClick={props.onSideContainerOpen} />
<Button StartIcon={Icon.FiMenu} color="minimal" size="icon" onClick={props.onSideContainerOpen}>
<span className="sr-only">{t("show_navigation")}</span>
</Button>
<a href="/" className="flex items-center space-x-2 rounded-md px-3 py-1 hover:bg-gray-200">
<Icon.FiArrowLeft className="text-gray-700" />
<p className="font-semibold text-black">{t("settings")}</p>
@@ -314,6 +331,7 @@ export default function SettingsLayout({
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
const router = useRouter();
const state = useState(false);
const { t } = useLocale();
const [sideContainerOpen, setSideContainerOpen] = state;
useEffect(() => {
@@ -340,18 +358,21 @@ export default function SettingsLayout({
withoutSeo={true}
flexChildrenContainer
{...rest}
SidebarContainer={<SettingsSidebarContainer className="hidden lg:flex" />}
SidebarContainer={
<>
{/* Mobile backdrop */}
{sideContainerOpen && (
<button
onClick={() => setSideContainerOpen(false)}
className="fixed top-0 left-0 z-10 h-full w-full bg-black/50">
<span className="sr-only">{t("hide_navigation")}</span>
</button>
)}
<SettingsSidebarContainer navigationIsOpenedOnMobile={sideContainerOpen} />
</>
}
drawerState={state}
MobileNavigationContainer={null}
SettingsSidebarContainer={
<div
className={classNames(
"fixed inset-y-0 z-50 m-0 h-screen w-56 transform overflow-x-hidden overflow-y-scroll border-gray-100 bg-gray-50 transition duration-200 ease-in-out",
sideContainerOpen ? "translate-x-0" : "-translate-x-full"
)}>
<SettingsSidebarContainer />
</div>
}
TopNavContainer={
<MobileSettingsContainer onSideContainerOpen={() => setSideContainerOpen(!sideContainerOpen)} />
}>
@@ -371,7 +392,7 @@ function ShellHeader() {
const { meta } = useMeta();
const { t, isLocaleReady } = useLocale();
return (
<header className="mx-auto block justify-between pt-12 sm:flex sm:pt-8">
<header className="mx-auto block justify-between pt-8 sm:flex">
<div className="mb-8 flex w-full items-center border-b border-gray-200 pb-8">
{meta.backButton && (
<a href="javascript:history.back()">