diff --git a/docusaurus.config.js b/docusaurus.config.js index 6033f45e3..bbc37ba0e 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -107,68 +107,6 @@ module.exports = { additionalLanguages: ["java", "ruby", "php", "bash"], }, // hideableSidebar: true, - navbar: { - hideOnScroll: false, - logo: { - alt: "Keploy logo", - src: "img/keploy-logo-dark.svg", - srcDark: "img/keploy-logo-dark.svg", - }, - items: [ - { - label: "Products", - position: "left", - items: [ - { - label: "Integration Testing", - to: "/keploy-explained/introduction", - }, - { - label: "API Testing (AI)", - to: "/running-keploy/api-test-generator", - }, - { - label: "Unit Testing", - to: "/running-keploy/utg-pr-agent", - }, - ], - }, - { - label: "Blog", - items: [ - { - label: "Tech Blogs", - href: "https://keploy.io/blog/technology", - }, - { - label: "Community Articles", - href: "https://keploy.io/blog/community", - }, - { - label: "Glossary", - href: "/concepts/reference/glossary/", - }, - ], - position: "left", - }, - { - to: "/keploy-explained/contribution-guide", - label: "Contribution Guide", - position: "left", - }, - { - type: "docsVersionDropdown", - position: "right", - dropdownActiveClassDisabled: true, - }, - { - href: "https://github.com/keploy/keploy", - position: "right", - className: "header-github-link", - "aria-label": "GitHub repository", - }, - ], - }, footer: { copyright: ` diff --git a/src/theme/Navbar/index.js b/src/theme/Navbar/index.js new file mode 100644 index 000000000..1c460d055 --- /dev/null +++ b/src/theme/Navbar/index.js @@ -0,0 +1,611 @@ +import React, { useState, useRef } from 'react'; +import Link from '@docusaurus/Link'; +import useThemeContext from '../hooks/useThemeContext'; +import Layout from '@theme/Layout'; + +const navItems = [ + { + label: 'Products', + megaMenu: [ + { + title: 'Unit Testing', + description: 'Generate unit tests with mocks in seconds', + image: 'https://keploy.io/_next/static/media/unit-test.c96bb42c.svg', + href: '/docs/server/installation/', + height: '280px', + width: '340px', + }, + { + title: 'API Testing', + description: 'Automatically record, replay, and validate APIs', + image: 'https://keploy.io/_next/static/media/api-test.c9383d11.svg', + href: '/docs/running-keploy/about-api-testing/', + height: '280px', + width: '340px', + }, + { + title: 'Integration Testing', + description: 'Open-source testing infrastructure for devs', + image: 'https://keploy.io/_next/static/media/integration-test.842196d5.svg', + href: '/docs/keploy-explained/introduction/', + height: '280px', + width: '340px', + }, + ], + }, + { + label: 'Solutions', + megaMenu: [ + { + title: 'Code Coverage', + image: 'https://keploy.io/_next/static/media/code-coverages.2e8db773.svg', + href: 'https://keploy.io/code-coverage', + height: '180px', + width: '240px', + }, + { + title: 'Developer Productivity', + image: 'https://keploy.io/_next/static/media/developer-productivity.093de83a.svg', + href: 'https://keploy.io/developer-productivity', + height: '180px', + width: '240px', + }, + { + title: 'CI Pipelines', + image: 'https://keploy.io/_next/static/media/ci-cd.aebe9002.svg', + href: 'https://keploy.io/docs/ci-cd/github/', + height: '180px', + width: '240px', + }, + ], + }, + { + label: 'Developers', + megaMenu: [ + { + title: 'Docs', + image: 'https://keploy.io/_next/static/media/doc.66b286fa.svg', + href: 'https://keploy.io/docs/', + height: '160px', + width: '200px', + }, + { + title: 'Technical Blog', + image: 'https://keploy.io/_next/static/media/blogs.38596f15.svg', + href: 'https://keploy.io/blog', + height: '160px', + width: '200px', + }, + { + title: 'Community Stories', + image: 'https://keploy.io/images/heart.svg', + href: 'https://www.g2.com/products/keploy/reviews', + height: '160px', + width: '200px', + }, + { + title: 'Tutorials', + image: 'https://keploy.io/images/book.svg', + href: 'https://www.youtube.com/playlist?list=PLuImHQnqnB_b3MbF1_873XeMhXkaZPpwV', + height: '160px', + width: '200px', + }, + { + title: 'Migration Guide', + image: 'https://keploy.io/images/user-manual.svg', + href: 'https://keploy.io/blog/technology/migration-guide-from-restassured-to-keploy', + height: '160px', + width: '200px', + }, + ], + }, + { label: 'Pricing', href: 'https://keploy.io/pricing' }, + { + label: 'Resources', + megaMenu: [ + { + title: 'About Us', + image: 'https://keploy.io/_next/static/media/about-us.e6182882.svg', + href: 'https://keploy.io/about', + height: '140px', + width: '180px', + }, + { + title: 'Events', + image: 'https://keploy.io/_next/static/media/events.8cfbfeeb.svg', + href: 'https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ2l-psdTCNCLYAJ-Jt5ESyGP7gi1_U70ySTjtFNr0Kmx5UagNJnyzg7lNjA3NKnaP6qFfpAgcdZ', + height: '140px', + width: '180px', + }, + { + title: 'Partner With Us', + image: 'https://keploy.io/_next/static/media/partner-with-us.d899cd43.svg', + href: 'https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ2l-psdTCNCLYAJ-Jt5ESyGP7gi1_U70ySTjtFNr0Kmx5UagNJnyzg7lNjA3NKnaP6qFfpAgcdZ', + height: '140px', + width: '180px', + }, + { + title: 'Contact Us', + image: 'https://keploy.io/_next/static/media/resource-1.500a7b0a.svg', + href: 'https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ2l-psdTCNCLYAJ-Jt5ESyGP7gi1_U70ySTjtFNr0Kmx5UagNJnyzg7lNjA3NKnaP6qFfpAgcdZ', + height: '140px', + width: '180px', + }, + { + title: 'Community Forum', + image: 'https://keploy.io/_next/static/media/resource-2.b09d3d88.svg', + href: 'https://github.com/keploy', + height: '140px', + width: '180px', + }, + ], + }, +]; + +const rightIcons = [ + { + icon: '/img/vscode.svg', + label: 'VS Code', + value: '543K', + href: 'https://marketplace.visualstudio.com/items?itemName=keploy.keploy', + }, + { + icon: '/img/github.svg', + label: 'GitHub', + value: '10.2K', + href: 'https://github.com/keploy/keploy', + }, +]; + +const versions = [ + { + label: '3.0.0', + href: 'https://keploy.io/docs/server/installation/', + }, + { + label: '2.0.0', + href: 'https://keploy.io/docs/2.0.0/server/installation/', + }, + { + label: '1.0.0', + href: 'https://keploy.io/docs/1.0.0/keploy-explained/introduction/', + }, +]; + +function ThemeToggle() { + const { isDarkTheme, setLightTheme, setDarkTheme } = useThemeContext(); + return ( + + ); +} + +function VersionDropdown() { + const [open, setOpen] = useState(false); + return ( +
+ + {open && ( +
+ {versions.map((v) => ( + + {v.label} + + ))} +
+ )} +
+ ); +} + +function GithubIcon() { + return ( + + ); +} + +function KeployLogo() { + return ( + + print 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default function Navbar() { + const navbarRef = useRef(null); + const [openMenu, setOpenMenu] = useState(null); + const { isDarkTheme } = useThemeContext(); + const timeoutRef = useRef(null); + + const handleMouseEnter = (label) => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + setOpenMenu(label); + }; + + const handleMouseLeave = () => { + timeoutRef.current = setTimeout(() => { + setOpenMenu(null); + }, 300); // 300ms delay before hiding + }; + + return ( + + ); +} + +// Defensive fallback: ensure #navbar always exists for Docusaurus scripts +if (typeof window !== 'undefined' && !document.getElementById('navbar')) { + const dummy = document.createElement('nav'); + dummy.id = 'navbar'; + dummy.style.display = 'none'; + document.body.appendChild(dummy); +} diff --git a/src/theme/hooks/useThemeContext.js b/src/theme/hooks/useThemeContext.js new file mode 100644 index 000000000..fada4d388 --- /dev/null +++ b/src/theme/hooks/useThemeContext.js @@ -0,0 +1,59 @@ +import { useEffect, useState } from 'react'; + +// Helper to get current theme from document or localStorage +function getCurrentTheme() { + if (typeof window !== 'undefined') { + const stored = localStorage.getItem('theme'); + if (stored === 'dark') return true; + if (stored === 'light') return false; + // fallback to document attribute + return document.documentElement.getAttribute('data-theme') === 'dark'; + } + return false; +} + +export default function useThemeContext() { + const [isDarkTheme, setIsDarkTheme] = useState(getCurrentTheme()); + + useEffect(() => { + // On mount, set theme from localStorage if available + if (typeof window !== 'undefined') { + const stored = localStorage.getItem('theme'); + if (stored === 'dark') { + document.documentElement.setAttribute('data-theme', 'dark'); + setIsDarkTheme(true); + } else if (stored === 'light') { + document.documentElement.setAttribute('data-theme', 'light'); + setIsDarkTheme(false); + } + } + const handler = () => { + setIsDarkTheme(getCurrentTheme()); + }; + window.addEventListener('storage', handler); + // Listen for theme changes + const observer = new MutationObserver(handler); + observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); + return () => { + window.removeEventListener('storage', handler); + observer.disconnect(); + }; + }, []); + + const setLightTheme = () => { + document.documentElement.setAttribute('data-theme', 'light'); + localStorage.setItem('theme', 'light'); + setIsDarkTheme(false); + }; + const setDarkTheme = () => { + document.documentElement.setAttribute('data-theme', 'dark'); + localStorage.setItem('theme', 'dark'); + setIsDarkTheme(true); + }; + + return { + isDarkTheme, + setLightTheme, + setDarkTheme, + }; +}