From a22f71e1dd765930711cc94ddf16dc0e32852103 Mon Sep 17 00:00:00 2001 From: Tarryn Schantell Date: Wed, 22 Feb 2023 18:04:01 -0600 Subject: [PATCH 1/4] EA-9267 - Add toplevel placeholder for Analyst support docs rebase/squash whitespace --- components/site/analystHomePageContent.tsx | 248 ++++++++++++++++++ .../site/analystIndexListPageContent.tsx | 64 +++++ components/site/analystLayout.tsx | 23 ++ components/site/analystNavigation.tsx | 235 +++++++++++++++++ components/site/header.tsx | 5 +- components/site/layout.tsx | 5 +- components/site/layoutInnerContent.tsx | 4 +- components/site/status.tsx | 5 +- content/analyst/competitive-tracker/index.md | 5 + content/analyst/general/index.md | 5 + .../analyst/inbox-and-design-tracker/index.md | 5 + content/analyst/privacy-gdpr/index.md | 5 + content/analyst/sfmc/index.md | 5 + lib/api.ts | 30 ++- pages/analyst/[...slug].tsx | 81 ++++++ pages/analyst/index.tsx | 35 +++ 16 files changed, 752 insertions(+), 8 deletions(-) create mode 100644 components/site/analystHomePageContent.tsx create mode 100644 components/site/analystIndexListPageContent.tsx create mode 100644 components/site/analystLayout.tsx create mode 100644 components/site/analystNavigation.tsx create mode 100644 content/analyst/competitive-tracker/index.md create mode 100644 content/analyst/general/index.md create mode 100644 content/analyst/inbox-and-design-tracker/index.md create mode 100644 content/analyst/privacy-gdpr/index.md create mode 100644 content/analyst/sfmc/index.md create mode 100644 pages/analyst/[...slug].tsx create mode 100644 pages/analyst/index.tsx diff --git a/components/site/analystHomePageContent.tsx b/components/site/analystHomePageContent.tsx new file mode 100644 index 000000000..6df5ea893 --- /dev/null +++ b/components/site/analystHomePageContent.tsx @@ -0,0 +1,248 @@ +import React from 'react'; +import { Text, Stack, Box } from '@sparkpost/matchbox'; +import { Feedback, HelpOutline } from '@sparkpost/matchbox-icons'; +import Link from 'next/link'; +import styled from 'styled-components'; +import css from '@styled-system/css'; + +/* Quick Jump: + #Types + #Styles + #Content + #Compenents +*/ + +/* #Types */ +type CardProps = { + url: string; + title: string; + desc: string; + linkText: string; +}; + +type SectionContentProps = { + title: string; + desc: string; + desc2?: JSX.Element; +}; + +type HelpLinkProps = { + text: string; + url: string; + Icon: React.ElementType; +}; + +type SimpleStyledLinkProp = { + $inlineBlock?: boolean; +}; + +/* #Styles */ +const SimpleStyledLink = styled.a` + display: ${(props) => (props.$inlineBlock ? 'inline-block' : 'block')}; + text-decoration: none; + transition: color 0.3s; + ${css({ + marginY: '200', + })} + &, + &:visited { + ${css({ + color: 'gray.900', + })} + } + + &:hover { + ${css({ + color: 'blue.700', + })} + } + + ${css({ + fontWeight: 'normal', + textDecoration: 'underline', + })} +`; + +const BlueStyledLink = styled.a` + text-decoration: none; + ${css({ + paddingY: '200', + paddingX: '400', + marginRight: '200', + })} + transition: background-color .3s; + > svg { + ${css({ + marginRight: '200', + })} + } + &:hover { + ${css({ + backgroundColor: 'blue.200', + })} + } +`; + +const StyledCard = styled(Box)` + &:before { + content: ''; + display: block; + position: absolute; + top: -1px; + left: -1px; + right: 0; + width: calc(100% + 2px); + height: 4px; + ${css({ + backgroundColor: 'blue.700', + })} + } +`; + +/* #Content */ +const gettingStarted = { + title: 'Getting Started', + desc: 'Welcome to Analyst! Get familiar with our product and explore its features:', + content: [ + { + url: '/general/', + title: 'General', + desc: 'General platform definitions & common account-level inquiries', + linkText: 'Learn More', + }, + { + url: '/inbox-tracker/', + title: 'Inbox Tracker', + desc: 'A collection of How-To Guides', + linkText: 'Learn More', + }, + { + url: '/competitive-tracker/', + title: 'Competitve Tracker', + desc: 'Guides for optimizing your use of Competitive Tracker', + linkText: 'Learn More', + }, + { + url: '/design-tracker/', + title: 'Design Tracker', + desc: 'Design Tracker help docs', + linkText: 'Learn More', + }, + { + url: '/sfmc/', + title: 'Salesforce Marketing Cloud Integration', + desc: 'A set of guides on the installation and integration of the Inbox Tracker application', + linkText: 'Learn More', + }, + { + url: 'https://api.edatasource.com/docs', + title: 'API Docs', + desc: 'Check out our full API reference.', + linkText: 'View Documentation', + }, + ], +}; + +const help = { + title: "Questions? We're here to help.", + desc: 'We’re always happy to help with code or other questions you might have!', + desc2: ( + <> + To find the best resource for your question and fastest resolution, check out our + + how to get help guide + + + ), + content: [ + { text: 'Submit a ticket', url: 'mailto:support@emailanalyst.com', Icon: Feedback }, + { text: 'FAQs', url: '/docs/faq/', Icon: HelpOutline }, + ], +}; + +/* #Compenents */ +const SectionContent = (props: SectionContentProps): JSX.Element => { + const { title, desc, desc2 } = props; + return ( + <> + + {title} + + + {desc} + + {desc2 && {desc2}} + + ); +}; + +const Card = (props: CardProps): JSX.Element => { + const { url, title, desc, linkText } = props; + return ( + + + + {title} + + + {desc} + + {linkText} + + + ); +}; + +const HelpLink = (props: HelpLinkProps): JSX.Element => { + const { Icon, text, url } = props; + return ( + + + {text} + + + ); +}; + +const DocsHomePageContent = () => { + return ( + <> + + + + Help & API + + + + {gettingStarted.content.map((cc, i) => { + return ; + })} + + +
+ + + + {help.content.map((links, i) => { + return ; + })} + + +
+ + ); +}; + +export default DocsHomePageContent; diff --git a/components/site/analystIndexListPageContent.tsx b/components/site/analystIndexListPageContent.tsx new file mode 100644 index 000000000..9810ca662 --- /dev/null +++ b/components/site/analystIndexListPageContent.tsx @@ -0,0 +1,64 @@ +import React, { useState } from 'react'; +import Link from 'next/link'; +import { Box, Tag, Button } from '@sparkpost/matchbox'; +import { KeyboardArrowDown } from '@sparkpost/matchbox-icons'; +import { formatDate } from 'utils/string'; +import type { NavigationItemProps } from 'components/site/analystNavigation'; + +interface AnalystIndexListPageContentProps { + navigationData: NavigationItemProps | undefined; +} + +const AnalystIndexListPageContent = (props: AnalystIndexListPageContentProps) => { + const postsPerClick = 7; + const [paginationCount, setPaginationCount] = useState(postsPerClick); + const { navigationData } = props; + return ( + <> + {navigationData && navigationData.items ? ( + navigationData.items.slice(0, paginationCount).map((item, i) => { + return ( + + + {item.title} + {item.lastUpdated && ( + + {formatDate(item.lastUpdated)} + + )} + {item.description && ( + + {item.description} + + )} + + + + + + +
+
+ ); + }) + ) : ( + <>Whoops! This section is empty. + )} + {navigationData && navigationData.items && paginationCount < navigationData.items.length && ( + + + + )} + + ); +}; + +export default AnalystIndexListPageContent; diff --git a/components/site/analystLayout.tsx b/components/site/analystLayout.tsx new file mode 100644 index 000000000..21d2bde80 --- /dev/null +++ b/components/site/analystLayout.tsx @@ -0,0 +1,23 @@ +import Layout from './layout'; +import AnalystNavigation, { NavigationItemProps } from 'components/site/analystNavigation'; + +type LayoutProps = { + children?: React.ReactNode; + navigationData?: NavigationItemProps[]; +}; + +const AnalystLayout = (props: LayoutProps): JSX.Element => { + const { children, navigationData } = props; + return ( + + } + > + {children} + + ); +}; + +export default AnalystLayout; diff --git a/components/site/analystNavigation.tsx b/components/site/analystNavigation.tsx new file mode 100644 index 000000000..6104b7032 --- /dev/null +++ b/components/site/analystNavigation.tsx @@ -0,0 +1,235 @@ +import React from 'react'; +import { getWindow } from 'utils/ssr'; +import Link from 'next/link'; +import { Box, BoxProps, ScreenReaderOnly, styles } from '@sparkpost/matchbox'; +import { + KeyboardArrowDown, + KeyboardArrowUp, + DeviceHub, + Feedback, +} from '@sparkpost/matchbox-icons'; +import styled from 'styled-components'; +import css from '@styled-system/css'; +import { tokens } from '@sparkpost/design-tokens'; +import useStatus from 'hooks/useStatus'; + +export interface NavigationItemProps { + title: string; + link: string; + items?: this[]; + level?: number; + lastUpdated?: string; + description?: string; +} + +type NavigationProps = { + data?: NavigationItemProps[]; + title: string; + titleLink: string; +}; + +const StyledLink = styled.a<{ $active?: boolean; $level?: number }>` + display: block; + cursor: pointer; + overflow-wrap: anywhere; + + &, + &:visited, + &:active { + text-decoration: none; + ${({ $active, $level }) => + css({ + py: '200', + pr: '700', + pl: `calc(${tokens.spacing_500} + ${tokens.spacing_200} * ${$level})`, + bg: $active ? 'blue.700' : 'transparent', + color: $active ? 'white' : 'gray.900', + })} + } + + &:hover { + ${({ $active }) => { + return css({ + bg: $active ? 'blue.700' : 'gray.200', + color: $active ? 'white' : 'gray.900', + }); + }} + } +`; + +const StatusColorMap = { + none: 'green.700', + minor: 'yellow.400', + major: 'brand.orange', + critical: 'red.700', +}; + +const AnalystNavigation = (props: NavigationProps): JSX.Element | null => { + const { data = [], title, titleLink } = props; + const { status } = useStatus(); + + return ( + + + + + {title} + + + + {data.map((item, i) => ( + + ))} + + + + + + + Submit a Ticket + + + + + + + + + + + API Docs + + + + + + + + + {/* Placeholder until Circle icon is added to Matchbox */} + + + + + Service Status + + + + + + + ); +}; + +const getActiveUrl = (location: Location | undefined) => { + if (!location) { + return ''; + } + + return location.pathname + location.hash; +}; + +const findActiveChild = ( + items: NavigationItemProps[], + activeUrl: string, +): NavigationItemProps | undefined => { + return items.find((item) => { + const hasDirectChild = item.link == activeUrl; + + if (!hasDirectChild && item.items) { + return findActiveChild(item.items, activeUrl); + } + + return hasDirectChild; + }); +}; + +const Chevron = (props: { expanded: boolean }): JSX.Element => { + const { expanded } = props; + return expanded ? : ; +}; + +const StyledChevronWrapper = styled(Box)` + ${styles.buttonReset} + cursor: pointer; + &:hover { + ${({ $active }) => { + return css({ + color: $active ? 'white' : 'blue.700', + }); + }} + } + ${({ $active }) => { + return css({ + color: $active ? 'white' : 'inherit', + }); + }} +`; + +const Item = (props: NavigationItemProps): JSX.Element => { + const { link, title, items, level = 0 } = props; + const [active, setActive] = React.useState(false); + const [expanded, setExpanded] = React.useState(false); + + const environment = getWindow(); + const activeUrl = getActiveUrl(environment?.location); + const hasActiveChild = items && findActiveChild(items, activeUrl); + + React.useEffect(() => { + const isExpanded = Boolean(hasActiveChild) || activeUrl === link || activeUrl.includes(link); + const isActive = activeUrl === link; + + setExpanded(isExpanded); + setActive(isActive); + }, [link, activeUrl]); + + return ( + + + + {title} + + + + {items && ( + setExpanded(!expanded)} + aria-expanded={expanded} + aria-controls={`${link}__items`} + $active={active} + > + Expand Menu + + + )} + + {expanded && items && items.map((item, i) => )} + + + ); +}; + +export default AnalystNavigation; diff --git a/components/site/header.tsx b/components/site/header.tsx index d9b5a455a..ea9be4540 100644 --- a/components/site/header.tsx +++ b/components/site/header.tsx @@ -13,6 +13,7 @@ const StyledButton = styled.button` type HeaderProps = { getActivatorProps?: () => object; + hideDrawerButtons?: boolean; }; const Header = (props: HeaderProps) => { @@ -32,9 +33,9 @@ const Header = (props: HeaderProps) => { - + {!props.hideDrawerButtons && ( - + )} )} diff --git a/components/site/layout.tsx b/components/site/layout.tsx index 091b198e6..e60f1552c 100644 --- a/components/site/layout.tsx +++ b/components/site/layout.tsx @@ -7,6 +7,7 @@ import LayoutInnerContent from './layoutInnerContent'; type LayoutProps = { children?: React.ReactNode; navigationComponent?: React.ReactNode; + hideDrawerButtons?: boolean; }; const Layout = (props: LayoutProps): JSX.Element => { @@ -23,10 +24,10 @@ const Layout = (props: LayoutProps): JSX.Element => { {navigationComponent} - + {!props.hideDrawerButtons && ()} - + {navigationComponent} diff --git a/components/site/layoutInnerContent.tsx b/components/site/layoutInnerContent.tsx index 02223c040..baeb43830 100644 --- a/components/site/layoutInnerContent.tsx +++ b/components/site/layoutInnerContent.tsx @@ -6,6 +6,8 @@ import styled from 'styled-components'; type LayoutInnerContentProps = { children?: React.ReactNode; getActivatorProps?: () => object; + hideDrawerButtons?: boolean; + }; const StyledSkipToContent = styled(Box)` @@ -36,7 +38,7 @@ const LayoutInnerContent = (props: LayoutInnerContentProps): JSX.Element => { > Skip to main content -
+
{children} ); diff --git a/components/site/status.tsx b/components/site/status.tsx index b6c851dc2..b41c9580d 100644 --- a/components/site/status.tsx +++ b/components/site/status.tsx @@ -12,7 +12,7 @@ declare global { const Status = () => { const { setStatus } = useStatus(); - const environment = getWindow(); + const environment = getWindow(); return ( <> @@ -20,7 +20,8 @@ const Status = () => { src="https://cdn.statuspage.io/se-v2.js" onLoad={() => { if (environment && environment.StatusPage) { - const sp = new environment.StatusPage.page({ page: '7ky1q6zd3fyp' }); + const pathStartsWithAnalyst = /^\/analyst/.test(environment.location.pathname); + const sp = new environment.StatusPage.page({ page: pathStartsWithAnalyst ? 'hj13jvlxdqwx' : '7ky1q6zd3fyp' }); sp.status({ success: function (data: { diff --git a/content/analyst/competitive-tracker/index.md b/content/analyst/competitive-tracker/index.md new file mode 100644 index 000000000..9cd2d0834 --- /dev/null +++ b/content/analyst/competitive-tracker/index.md @@ -0,0 +1,5 @@ +--- +lastUpdated: '02/22/2023' +title: 'Competitive Tracker' +description: '' +--- diff --git a/content/analyst/general/index.md b/content/analyst/general/index.md new file mode 100644 index 000000000..f1ff18891 --- /dev/null +++ b/content/analyst/general/index.md @@ -0,0 +1,5 @@ +--- +lastUpdated: '02/22/2023' +title: 'General' +description: '' +--- diff --git a/content/analyst/inbox-and-design-tracker/index.md b/content/analyst/inbox-and-design-tracker/index.md new file mode 100644 index 000000000..0145708a6 --- /dev/null +++ b/content/analyst/inbox-and-design-tracker/index.md @@ -0,0 +1,5 @@ +--- +lastUpdated: '02/22/2023' +title: 'Inbox Tracker & Design Tracker' +description: '' +--- diff --git a/content/analyst/privacy-gdpr/index.md b/content/analyst/privacy-gdpr/index.md new file mode 100644 index 000000000..e3a5a43a8 --- /dev/null +++ b/content/analyst/privacy-gdpr/index.md @@ -0,0 +1,5 @@ +--- +lastUpdated: '02/22/2023' +title: 'Privacy & GDPR' +description: '' +--- diff --git a/content/analyst/sfmc/index.md b/content/analyst/sfmc/index.md new file mode 100644 index 000000000..4420e0db5 --- /dev/null +++ b/content/analyst/sfmc/index.md @@ -0,0 +1,5 @@ +--- +lastUpdated: '02/22/2023' +title: 'Salesforce Marketing Cloud' +description: '' +--- diff --git a/lib/api.ts b/lib/api.ts index 9ae6deeac..2e7277780 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -3,7 +3,7 @@ import path from 'path'; import glob from 'glob'; import matter from 'gray-matter'; -type CategoryOption = 'momentum' | 'docs'; +type CategoryOption = 'momentum' | 'docs' | 'analyst'; export const categoryPath = (category: CategoryOption): string => { return path.join( @@ -93,6 +93,34 @@ export const getSupportNavigation = () => { return navigationData; }; +/** + * Retrieves navigation data from /content/docs + */ +export const getAnalystSupportNavigation = () => { + // Get categories first + const categories = glob.sync('content/analyst/**/index.md'); + const categoryData = categories.map((file) => { + const { data } = readFile(file); + const link = file.replace(/\/index.md$/, '/').replace(/^content/, ''); + return { ...data, link }; + }); + + // Then populate category items + const navigationData = categoryData.map((category) => { + const postsInCategory = glob.sync(`content${category.link}/!(index).md`); + return { + ...category, + items: postsInCategory.map((file) => { + const { data } = readFile(file); + const link = file.replace(/.md$/, '/').replace(/^content/, ''); + return { ...data, link }; + }), + }; + }); + + return navigationData; +}; + /** * Retrieves category information for breadcrumb friendly labels */ diff --git a/pages/analyst/[...slug].tsx b/pages/analyst/[...slug].tsx new file mode 100644 index 000000000..bd848065e --- /dev/null +++ b/pages/analyst/[...slug].tsx @@ -0,0 +1,81 @@ +import { GetStaticProps, GetStaticPaths } from 'next'; +import { + getCategoryData, + getAnalystSupportNavigation, + getAllCategoryPostPaths, + getSingleCategoryPost, + categoryPath, +} from 'lib/api'; +import { CategoriesProvider, Category } from 'context/categories'; +import SEO from 'components/site/seo'; +import Markdown from 'components/markdown'; +import AnalystLayout from 'components/site/analystLayout'; +import DocumentationContent from 'components/site/documentationContent'; +import AnalystIndexListPageContent from 'components/site/analystIndexListPageContent'; +import type { NavigationItemProps } from 'components/site/analystNavigation'; +import { useRouter } from 'next/router'; + +type PostPageProps = { + content: string; + data: { + title?: string; + description?: string; + lastUpdated?: string; + }; + navigationData?: NavigationItemProps[]; + isIndex: boolean; + categoryData: Category[]; +}; + +const PostPage = (props: PostPageProps): JSX.Element => { + const { content, data, navigationData, isIndex, categoryData } = props; + const router = useRouter(); + + // This is to check for a url that either contains a trailing slash or not (since netlify will show either) + const trailingSlashOrNo = (navData: NavigationItemProps): boolean => { + const linkComponents = navData.link.split('/'); + linkComponents.pop(); + const link = linkComponents.join('/'); + + return link === router.asPath || link + '/' === router.asPath; + }; + return ( + + + + + {isIndex && navigationData ? ( + + ) : ( + {content} + )} + + + + ); +}; + +export const getStaticProps: GetStaticProps = async ({ params }) => { + if (!params?.slug) { + return { props: {} }; + } + + const { content, data, isIndex } = getSingleCategoryPost(params.slug, categoryPath('analyst')) || {}; + const navigationData = getAnalystSupportNavigation() || []; + const categoryData = getCategoryData('analyst'); + return { props: { content, data, navigationData, isIndex, categoryData } }; +}; + +export const getStaticPaths: GetStaticPaths = async () => { + return { + paths: getAllCategoryPostPaths('analyst'), + fallback: false, + }; +}; + +export default PostPage; diff --git a/pages/analyst/index.tsx b/pages/analyst/index.tsx new file mode 100644 index 000000000..41eabe7fc --- /dev/null +++ b/pages/analyst/index.tsx @@ -0,0 +1,35 @@ +import AnalystHomePageContent from 'components/site/analystHomePageContent'; +import { GetStaticProps } from 'next'; +import { getAnalystSupportNavigation, getCategoryData } from 'lib/api'; +import SEO from 'components/site/seo'; +import AnalystLayout from 'components/site/analystLayout'; +import type { NavigationItemProps } from 'components/site/navigation'; +import { CategoriesProvider, Category } from 'context/categories'; + +type IndexPageProps = { + navigationData?: NavigationItemProps[]; + categoryData: Category[]; +}; + +const IndexPage = (props: IndexPageProps): JSX.Element => { + const { categoryData, navigationData } = props; + return ( + + + + + + + ); +}; + +export const getStaticProps: GetStaticProps = async () => { + const navigationData = getAnalystSupportNavigation() || []; + const categoryData = getCategoryData('analyst'); + return { props: { categoryData, navigationData } }; +}; + +export default IndexPage; From ebba5935fcd65a2b2ff6f7bd37f51a299edb1c9a Mon Sep 17 00:00:00 2001 From: Tarryn Schantell Date: Thu, 23 Feb 2023 15:58:59 -0600 Subject: [PATCH 2/4] EA-9267 - Less copy and paste on things, make a base*.tsx file --- components/site/analystHomePageContent.tsx | 209 ++----------- .../site/analystIndexListPageContent.tsx | 64 ---- components/site/analystLayout.tsx | 3 +- components/site/analystNavigation.tsx | 178 +---------- components/site/baseHomePageContent.tsx | 279 +++++++++++++++++ components/site/baseNavigation.tsx | 185 ++++++++++++ components/site/docsHomePageContent.tsx | 282 +----------------- components/site/docsIndexListPageContent.tsx | 2 +- components/site/docsLayout.tsx | 3 +- components/site/navigation.tsx | 174 +---------- pages/analyst/[...slug].tsx | 6 +- pages/analyst/index.tsx | 2 +- pages/docs/[...slug].tsx | 2 +- pages/docs/index.tsx | 2 +- pages/submit-a-ticket.tsx | 2 +- 15 files changed, 511 insertions(+), 882 deletions(-) delete mode 100644 components/site/analystIndexListPageContent.tsx create mode 100644 components/site/baseHomePageContent.tsx create mode 100644 components/site/baseNavigation.tsx diff --git a/components/site/analystHomePageContent.tsx b/components/site/analystHomePageContent.tsx index 6df5ea893..d334686b6 100644 --- a/components/site/analystHomePageContent.tsx +++ b/components/site/analystHomePageContent.tsx @@ -1,103 +1,7 @@ import React from 'react'; -import { Text, Stack, Box } from '@sparkpost/matchbox'; -import { Feedback, HelpOutline } from '@sparkpost/matchbox-icons'; +import { Feedback } from '@sparkpost/matchbox-icons'; import Link from 'next/link'; -import styled from 'styled-components'; -import css from '@styled-system/css'; - -/* Quick Jump: - #Types - #Styles - #Content - #Compenents -*/ - -/* #Types */ -type CardProps = { - url: string; - title: string; - desc: string; - linkText: string; -}; - -type SectionContentProps = { - title: string; - desc: string; - desc2?: JSX.Element; -}; - -type HelpLinkProps = { - text: string; - url: string; - Icon: React.ElementType; -}; - -type SimpleStyledLinkProp = { - $inlineBlock?: boolean; -}; - -/* #Styles */ -const SimpleStyledLink = styled.a` - display: ${(props) => (props.$inlineBlock ? 'inline-block' : 'block')}; - text-decoration: none; - transition: color 0.3s; - ${css({ - marginY: '200', - })} - &, - &:visited { - ${css({ - color: 'gray.900', - })} - } - - &:hover { - ${css({ - color: 'blue.700', - })} - } - - ${css({ - fontWeight: 'normal', - textDecoration: 'underline', - })} -`; - -const BlueStyledLink = styled.a` - text-decoration: none; - ${css({ - paddingY: '200', - paddingX: '400', - marginRight: '200', - })} - transition: background-color .3s; - > svg { - ${css({ - marginRight: '200', - })} - } - &:hover { - ${css({ - backgroundColor: 'blue.200', - })} - } -`; - -const StyledCard = styled(Box)` - &:before { - content: ''; - display: block; - position: absolute; - top: -1px; - left: -1px; - right: 0; - width: calc(100% + 2px); - height: 4px; - ${css({ - backgroundColor: 'blue.700', - })} - } -`; +import BaseHomePageContent, {SimpleStyledLink} from './baseHomePageContent'; /* #Content */ const gettingStarted = { @@ -105,33 +9,33 @@ const gettingStarted = { desc: 'Welcome to Analyst! Get familiar with our product and explore its features:', content: [ { - url: '/general/', + url: '/analyst/general/', title: 'General', desc: 'General platform definitions & common account-level inquiries', linkText: 'Learn More', }, { - url: '/inbox-tracker/', - title: 'Inbox Tracker', + url: '/analyst/inbox-and-design-tracker/', + title: 'Inbox Tracker & Design Tracker', desc: 'A collection of How-To Guides', linkText: 'Learn More', }, { - url: '/competitive-tracker/', + url: '/analyst/competitive-tracker/', title: 'Competitve Tracker', desc: 'Guides for optimizing your use of Competitive Tracker', linkText: 'Learn More', }, { - url: '/design-tracker/', - title: 'Design Tracker', - desc: 'Design Tracker help docs', + url: '/analyst/sfmc/', + title: 'Salesforce Marketing Cloud Integration', + desc: 'A set of guides on the installation and integration of the Inbox Tracker application', linkText: 'Learn More', }, { - url: '/sfmc/', - title: 'Salesforce Marketing Cloud Integration', - desc: 'A set of guides on the installation and integration of the Inbox Tracker application', + url: '/analyst/privacy-gdpr/', + title: 'Privacy & GDPR', + desc: 'We’re committed to keeping your private information private, your data protected and secure and being transparent about our practices', linkText: 'Learn More', }, { @@ -156,93 +60,16 @@ const help = { ), content: [ { text: 'Submit a ticket', url: 'mailto:support@emailanalyst.com', Icon: Feedback }, - { text: 'FAQs', url: '/docs/faq/', Icon: HelpOutline }, ], }; -/* #Compenents */ -const SectionContent = (props: SectionContentProps): JSX.Element => { - const { title, desc, desc2 } = props; +const AnalystHomePageContent = () => { return ( - <> - - {title} - - - {desc} - - {desc2 && {desc2}} - - ); -}; - -const Card = (props: CardProps): JSX.Element => { - const { url, title, desc, linkText } = props; - return ( - - - - {title} - - - {desc} - - {linkText} - - - ); -}; - -const HelpLink = (props: HelpLinkProps): JSX.Element => { - const { Icon, text, url } = props; - return ( - - - {text} - - - ); -}; - -const DocsHomePageContent = () => { - return ( - <> - - - - Help & API - - - - {gettingStarted.content.map((cc, i) => { - return ; - })} - - -
- - - - {help.content.map((links, i) => { - return ; - })} - - -
- + ); }; -export default DocsHomePageContent; +export default AnalystHomePageContent; diff --git a/components/site/analystIndexListPageContent.tsx b/components/site/analystIndexListPageContent.tsx deleted file mode 100644 index 9810ca662..000000000 --- a/components/site/analystIndexListPageContent.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useState } from 'react'; -import Link from 'next/link'; -import { Box, Tag, Button } from '@sparkpost/matchbox'; -import { KeyboardArrowDown } from '@sparkpost/matchbox-icons'; -import { formatDate } from 'utils/string'; -import type { NavigationItemProps } from 'components/site/analystNavigation'; - -interface AnalystIndexListPageContentProps { - navigationData: NavigationItemProps | undefined; -} - -const AnalystIndexListPageContent = (props: AnalystIndexListPageContentProps) => { - const postsPerClick = 7; - const [paginationCount, setPaginationCount] = useState(postsPerClick); - const { navigationData } = props; - return ( - <> - {navigationData && navigationData.items ? ( - navigationData.items.slice(0, paginationCount).map((item, i) => { - return ( - - - {item.title} - {item.lastUpdated && ( - - {formatDate(item.lastUpdated)} - - )} - {item.description && ( - - {item.description} - - )} - - - - - - -
-
- ); - }) - ) : ( - <>Whoops! This section is empty. - )} - {navigationData && navigationData.items && paginationCount < navigationData.items.length && ( - - - - )} - - ); -}; - -export default AnalystIndexListPageContent; diff --git a/components/site/analystLayout.tsx b/components/site/analystLayout.tsx index 21d2bde80..396e65701 100644 --- a/components/site/analystLayout.tsx +++ b/components/site/analystLayout.tsx @@ -1,5 +1,6 @@ import Layout from './layout'; -import AnalystNavigation, { NavigationItemProps } from 'components/site/analystNavigation'; +import { NavigationItemProps } from './baseNavigation'; +import AnalystNavigation from 'components/site/analystNavigation'; type LayoutProps = { children?: React.ReactNode; diff --git a/components/site/analystNavigation.tsx b/components/site/analystNavigation.tsx index 6104b7032..9a39455f2 100644 --- a/components/site/analystNavigation.tsx +++ b/components/site/analystNavigation.tsx @@ -1,91 +1,19 @@ import React from 'react'; -import { getWindow } from 'utils/ssr'; import Link from 'next/link'; -import { Box, BoxProps, ScreenReaderOnly, styles } from '@sparkpost/matchbox'; +import { Box } from '@sparkpost/matchbox'; import { - KeyboardArrowDown, - KeyboardArrowUp, DeviceHub, Feedback, } from '@sparkpost/matchbox-icons'; -import styled from 'styled-components'; -import css from '@styled-system/css'; -import { tokens } from '@sparkpost/design-tokens'; import useStatus from 'hooks/useStatus'; - -export interface NavigationItemProps { - title: string; - link: string; - items?: this[]; - level?: number; - lastUpdated?: string; - description?: string; -} - -type NavigationProps = { - data?: NavigationItemProps[]; - title: string; - titleLink: string; -}; - -const StyledLink = styled.a<{ $active?: boolean; $level?: number }>` - display: block; - cursor: pointer; - overflow-wrap: anywhere; - - &, - &:visited, - &:active { - text-decoration: none; - ${({ $active, $level }) => - css({ - py: '200', - pr: '700', - pl: `calc(${tokens.spacing_500} + ${tokens.spacing_200} * ${$level})`, - bg: $active ? 'blue.700' : 'transparent', - color: $active ? 'white' : 'gray.900', - })} - } - - &:hover { - ${({ $active }) => { - return css({ - bg: $active ? 'blue.700' : 'gray.200', - color: $active ? 'white' : 'gray.900', - }); - }} - } -`; - -const StatusColorMap = { - none: 'green.700', - minor: 'yellow.400', - major: 'brand.orange', - critical: 'red.700', -}; +import BaseNavigation, {NavigationProps, StyledLink, StatusColorMap} from './baseNavigation'; const AnalystNavigation = (props: NavigationProps): JSX.Element | null => { const { data = [], title, titleLink } = props; const { status } = useStatus(); - + return ( - - - - - {title} - - - - {data.map((item, i) => ( - - ))} + @@ -133,102 +61,8 @@ const AnalystNavigation = (props: NavigationProps): JSX.Element | null => { - - - ); -}; - -const getActiveUrl = (location: Location | undefined) => { - if (!location) { - return ''; - } - - return location.pathname + location.hash; -}; - -const findActiveChild = ( - items: NavigationItemProps[], - activeUrl: string, -): NavigationItemProps | undefined => { - return items.find((item) => { - const hasDirectChild = item.link == activeUrl; - - if (!hasDirectChild && item.items) { - return findActiveChild(item.items, activeUrl); - } - - return hasDirectChild; - }); -}; - -const Chevron = (props: { expanded: boolean }): JSX.Element => { - const { expanded } = props; - return expanded ? : ; -}; - -const StyledChevronWrapper = styled(Box)` - ${styles.buttonReset} - cursor: pointer; - &:hover { - ${({ $active }) => { - return css({ - color: $active ? 'white' : 'blue.700', - }); - }} - } - ${({ $active }) => { - return css({ - color: $active ? 'white' : 'inherit', - }); - }} -`; - -const Item = (props: NavigationItemProps): JSX.Element => { - const { link, title, items, level = 0 } = props; - const [active, setActive] = React.useState(false); - const [expanded, setExpanded] = React.useState(false); - - const environment = getWindow(); - const activeUrl = getActiveUrl(environment?.location); - const hasActiveChild = items && findActiveChild(items, activeUrl); - - React.useEffect(() => { - const isExpanded = Boolean(hasActiveChild) || activeUrl === link || activeUrl.includes(link); - const isActive = activeUrl === link; - - setExpanded(isExpanded); - setActive(isActive); - }, [link, activeUrl]); - - return ( - - - - {title} - - - - {items && ( - setExpanded(!expanded)} - aria-expanded={expanded} - aria-controls={`${link}__items`} - $active={active} - > - Expand Menu - - - )} - - {expanded && items && items.map((item, i) => )} - - + + ); }; diff --git a/components/site/baseHomePageContent.tsx b/components/site/baseHomePageContent.tsx new file mode 100644 index 000000000..0159342ad --- /dev/null +++ b/components/site/baseHomePageContent.tsx @@ -0,0 +1,279 @@ +import React from 'react'; +import { Text, Stack, Box } from '@sparkpost/matchbox'; +import Link from 'next/link'; +import styled from 'styled-components'; +import css from '@styled-system/css'; + +/* Types */ + +type CardProps = { + url: string; + title: string; + desc: string; + linkText: string; +}; + +type GuideLinkProps = { + text: string; + url: string; +}; + +type SectionContentProps = { + title: string; + desc: string; + desc2?: JSX.Element; +}; + +type LibraryProps = { + name: string; + url: string; + Icon: () => JSX.Element; +}; + +type HelpLinkProps = { + text: string; + url: string; + Icon: React.ElementType; +}; + +type SimpleStyledLinkProp = { + $inlineBlock?: boolean; +}; + +export type BaseHomePageContentProps = { + gettingStarted?: {content: CardProps[]} & SectionContentProps; + migrationGuides?: {content: GuideLinkProps[]} & SectionContentProps; + clientLibraries?: {content: LibraryProps[]} & SectionContentProps; + help?: {content: HelpLinkProps[]} & SectionContentProps; +}; + +/* Styles */ +export const SimpleStyledLink = styled.a` + display: ${(props) => (props.$inlineBlock ? 'inline-block' : 'block')}; + text-decoration: none; + transition: color 0.3s; + ${css({ + marginY: '200', + })} + &, + &:visited { + ${css({ + color: 'gray.900', + })} + } + + &:hover { + ${css({ + color: 'blue.700', + })} + } + + ${css({ + fontWeight: 'normal', + textDecoration: 'underline', + })} +`; + +const BlueStyledLink = styled.a` + text-decoration: none; + ${css({ + paddingY: '200', + paddingX: '400', + marginRight: '200', + })} + transition: background-color .3s; + > svg { + ${css({ + marginRight: '200', + })} + } + &:hover { + ${css({ + backgroundColor: 'blue.200', + })} + } +`; + +const StyledCard = styled(Box)` + &:before { + content: ''; + display: block; + position: absolute; + top: -1px; + left: -1px; + right: 0; + width: calc(100% + 2px); + height: 4px; + ${css({ + backgroundColor: 'blue.700', + })} + } +`; + +const BoxStyledLink = styled.a` + display: flex; + align-items: center; + width: 30%; + text-decoration: none; + border-radius: 1px; + border-width: 1px; + border-style: solid; + background-color: transparent; + transition: background-color 0.3s; + min-width: 165px; + ${css({ + color: 'gray.900', + paddingX: '450', + paddingY: '300', + borderColor: 'gray.900', + marginBottom: '500', + fontSize: '200', + lineHeight: '200', + })} + &:hover { + ${css({ + backgroundColor: 'gray.300', + color: 'gray.900', + })} + } + > svg { + ${css({ + marginRight: '300', + })} + } +`; + + +/* Components */ +const SectionContent = (props: SectionContentProps): JSX.Element => { + const { title, desc, desc2 } = props; + return ( + <> + + {title} + + + {desc} + + {desc2 && {desc2}} + + ); +}; + +const GuideLink = (props: GuideLinkProps) => { + const { url, text } = props; + return ( + + {text} + + ); +}; + +const Card = (props: CardProps): JSX.Element => { + const { url, title, desc, linkText } = props; + return ( + + + + {title} + + + {desc} + + {linkText} + + + ); +}; + +const Library = (props: LibraryProps): JSX.Element => { + const { Icon, name, url } = props; + return ( + + + {name} + + + ); +}; + +const HelpLink = (props: HelpLinkProps): JSX.Element => { + const { Icon, text, url } = props; + return ( + + + {text} + + + ); +}; + +const BaseHomePageContent = (props: BaseHomePageContentProps) => { + const {gettingStarted, migrationGuides, clientLibraries, help} = props; + return ( + <> + + {gettingStarted && (<> + + + Help & API + + + + {gettingStarted.content.map((cc, i) => { + return ; + })} + + + )} + {migrationGuides && (<> +
+ + + + {migrationGuides.content.map((link, i) => { + return ; + })} + + + )} + {clientLibraries && (<> +
+ + + + {clientLibraries.content.map((library, i) => { + return ; + })} + + + )} + {help && (<> +
+ + + + {help.content.map((links, i) => { + return ; + })} + + + )} +
+ + ); +}; + +export default BaseHomePageContent; diff --git a/components/site/baseNavigation.tsx b/components/site/baseNavigation.tsx new file mode 100644 index 000000000..0418deba1 --- /dev/null +++ b/components/site/baseNavigation.tsx @@ -0,0 +1,185 @@ +import React from 'react'; +import { getWindow } from 'utils/ssr'; +import Link from 'next/link'; +import { Box, BoxProps, ScreenReaderOnly, styles } from '@sparkpost/matchbox'; +import { + KeyboardArrowDown, + KeyboardArrowUp, +} from '@sparkpost/matchbox-icons'; +import styled from 'styled-components'; +import css from '@styled-system/css'; +import { tokens } from '@sparkpost/design-tokens'; + +export interface NavigationItemProps { + title: string; + link: string; + items?: this[]; + level?: number; + lastUpdated?: string; + description?: string; +} + +export type NavigationProps = { + children?: React.ReactNode; + data?: NavigationItemProps[]; + title: string; + titleLink: string; +}; + +export const StyledLink = styled.a<{ $active?: boolean; $level?: number }>` + display: block; + cursor: pointer; + overflow-wrap: anywhere; + + &, + &:visited, + &:active { + text-decoration: none; + ${({ $active, $level }) => + css({ + py: '200', + pr: '700', + pl: `calc(${tokens.spacing_500} + ${tokens.spacing_200} * ${$level})`, + bg: $active ? 'blue.700' : 'transparent', + color: $active ? 'white' : 'gray.900', + })} + } + + &:hover { + ${({ $active }) => { + return css({ + bg: $active ? 'blue.700' : 'gray.200', + color: $active ? 'white' : 'gray.900', + }); + }} + } +`; + +export const StatusColorMap = { + none: 'green.700', + minor: 'yellow.400', + major: 'brand.orange', + critical: 'red.700', +}; + +const BaseNavigation = (props: NavigationProps): JSX.Element | null => { + const { data = [], title, titleLink, children } = props; + + return ( + + + + + {title} + + + + {data.map((item, i) => ( + + ))} + {children} + + ); +}; + +const getActiveUrl = (location: Location | undefined) => { + if (!location) { + return ''; + } + + return location.pathname + location.hash; +}; + +const findActiveChild = ( + items: NavigationItemProps[], + activeUrl: string, +): NavigationItemProps | undefined => { + return items.find((item) => { + const hasDirectChild = item.link == activeUrl; + + if (!hasDirectChild && item.items) { + return findActiveChild(item.items, activeUrl); + } + + return hasDirectChild; + }); +}; + +const Chevron = (props: { expanded: boolean }): JSX.Element => { + const { expanded } = props; + return expanded ? : ; +}; + +const StyledChevronWrapper = styled(Box)` + ${styles.buttonReset} + cursor: pointer; + &:hover { + ${({ $active }) => { + return css({ + color: $active ? 'white' : 'blue.700', + }); + }} + } + ${({ $active }) => { + return css({ + color: $active ? 'white' : 'inherit', + }); + }} +`; + +const Item = (props: NavigationItemProps): JSX.Element => { + const { link, title, items, level = 0 } = props; + const [active, setActive] = React.useState(false); + const [expanded, setExpanded] = React.useState(false); + + const environment = getWindow(); + const activeUrl = getActiveUrl(environment?.location); + const hasActiveChild = items && findActiveChild(items, activeUrl); + + React.useEffect(() => { + const isExpanded = Boolean(hasActiveChild) || activeUrl === link || activeUrl.includes(link); + const isActive = activeUrl === link; + + setExpanded(isExpanded); + setActive(isActive); + }, [link, activeUrl]); + + return ( + + + + {title} + + + + {items && ( + setExpanded(!expanded)} + aria-expanded={expanded} + aria-controls={`${link}__items`} + $active={active} + > + Expand Menu + + + )} + + {expanded && items && items.map((item, i) => )} + + + ); +}; + +export default BaseNavigation; diff --git a/components/site/docsHomePageContent.tsx b/components/site/docsHomePageContent.tsx index 068a9cece..bd3a32b49 100644 --- a/components/site/docsHomePageContent.tsx +++ b/components/site/docsHomePageContent.tsx @@ -1,156 +1,8 @@ import React from 'react'; -import { Text, Stack, Box } from '@sparkpost/matchbox'; import { Feedback, HelpOutline, QuestionAnswer } from '@sparkpost/matchbox-icons'; import Link from 'next/link'; -import styled from 'styled-components'; -import css from '@styled-system/css'; import { Elixir, PHP, Java, NodeMailer, Python, Node, Go } from './icons'; - -/* -████████╗██╗ ██╗██████╗ ███████╗███████╗ -╚══██╔══╝╚██╗ ██╔╝██╔══██╗██╔════╝██╔════╝ - ██║ ╚████╔╝ ██████╔╝█████╗ ███████╗ - ██║ ╚██╔╝ ██╔═══╝ ██╔══╝ ╚════██║ - ██║ ██║ ██║ ███████╗███████║ - ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝ - */ -type CardProps = { - url: string; - title: string; - desc: string; - linkText: string; -}; - -type GuideLinkProps = { - text: string; - url: string; -}; - -type SectionContentProps = { - title: string; - desc: string; - desc2?: JSX.Element; -}; - -type LibraryProps = { - name: string; - url: string; - Icon: () => JSX.Element; -}; - -type HelpLinkProps = { - text: string; - url: string; - Icon: React.ElementType; -}; - -type SimpleStyledLinkProp = { - $inlineBlock?: boolean; -}; - -/* -███████╗████████╗██╗ ██╗██╗ ███████╗███████╗ -██╔════╝╚══██╔══╝╚██╗ ██╔╝██║ ██╔════╝██╔════╝ -███████╗ ██║ ╚████╔╝ ██║ █████╗ ███████╗ -╚════██║ ██║ ╚██╔╝ ██║ ██╔══╝ ╚════██║ -███████║ ██║ ██║ ███████╗███████╗███████║ -╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝ -*/ - -const SimpleStyledLink = styled.a` - display: ${(props) => (props.$inlineBlock ? 'inline-block' : 'block')}; - text-decoration: none; - transition: color 0.3s; - ${css({ - marginY: '200', - })} - &, - &:visited { - ${css({ - color: 'gray.900', - })} - } - - &:hover { - ${css({ - color: 'blue.700', - })} - } - - ${css({ - fontWeight: 'normal', - textDecoration: 'underline', - })} -`; - -const BlueStyledLink = styled.a` - text-decoration: none; - ${css({ - paddingY: '200', - paddingX: '400', - marginRight: '200', - })} - transition: background-color .3s; - > svg { - ${css({ - marginRight: '200', - })} - } - &:hover { - ${css({ - backgroundColor: 'blue.200', - })} - } -`; - -const StyledCard = styled(Box)` - &:before { - content: ''; - display: block; - position: absolute; - top: -1px; - left: -1px; - right: 0; - width: calc(100% + 2px); - height: 4px; - ${css({ - backgroundColor: 'blue.700', - })} - } -`; - -const BoxStyledLink = styled.a` - display: flex; - align-items: center; - width: 30%; - text-decoration: none; - border-radius: 1px; - border-width: 1px; - border-style: solid; - background-color: transparent; - transition: background-color 0.3s; - min-width: 165px; - ${css({ - color: 'gray.900', - paddingX: '450', - paddingY: '300', - borderColor: 'gray.900', - marginBottom: '500', - fontSize: '200', - lineHeight: '200', - })} - &:hover { - ${css({ - backgroundColor: 'gray.300', - color: 'gray.900', - })} - } - > svg { - ${css({ - marginRight: '300', - })} - } -`; +import BaseHomePageContent, {SimpleStyledLink} from './baseHomePageContent'; /* ██████╗ ██████╗ ███╗ ██╗████████╗███████╗███╗ ██╗████████╗ @@ -243,134 +95,14 @@ const help = { ], }; -/* - ██████╗ ██████╗ ███╗ ███╗██████╗ ██████╗ ███╗ ██╗███████╗███╗ ██╗████████╗███████╗ -██╔════╝██╔═══██╗████╗ ████║██╔══██╗██╔═══██╗████╗ ██║██╔════╝████╗ ██║╚══██╔══╝██╔════╝ -██║ ██║ ██║██╔████╔██║██████╔╝██║ ██║██╔██╗ ██║█████╗ ██╔██╗ ██║ ██║ ███████╗ -██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║ ██║██║╚██╗██║██╔══╝ ██║╚██╗██║ ██║ ╚════██║ -╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ╚██████╔╝██║ ╚████║███████╗██║ ╚████║ ██║ ███████║ - ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝ -*/ - -const SectionContent = (props: SectionContentProps): JSX.Element => { - const { title, desc, desc2 } = props; - return ( - <> - - {title} - - - {desc} - - {desc2 && {desc2}} - - ); -}; - -const GuideLink = (props: GuideLinkProps) => { - const { url, text } = props; - return ( - - {text} - - ); -}; - -const Card = (props: CardProps): JSX.Element => { - const { url, title, desc, linkText } = props; - return ( - - - - {title} - - - {desc} - - {linkText} - - - ); -}; - -const Library = (props: LibraryProps): JSX.Element => { - const { Icon, name, url } = props; - return ( - - - {name} - - - ); -}; - -const HelpLink = (props: HelpLinkProps): JSX.Element => { - const { Icon, text, url } = props; - return ( - - - {text} - - - ); -}; - const DocsHomePageContent = () => { return ( - <> - - - - Help & API - - - - {gettingStarted.content.map((cc, i) => { - return ; - })} - - -
- - - - {migrationGuides.content.map((link, i) => { - return ; - })} - - -
- - - - {clientLibraries.content.map((library, i) => { - return ; - })} - - -
- - - - {help.content.map((links, i) => { - return ; - })} - - -
- + ); }; diff --git a/components/site/docsIndexListPageContent.tsx b/components/site/docsIndexListPageContent.tsx index ccdee6eda..e01e2a1a5 100644 --- a/components/site/docsIndexListPageContent.tsx +++ b/components/site/docsIndexListPageContent.tsx @@ -3,7 +3,7 @@ import Link from 'next/link'; import { Box, Tag, Button } from '@sparkpost/matchbox'; import { KeyboardArrowDown } from '@sparkpost/matchbox-icons'; import { formatDate } from 'utils/string'; -import type { NavigationItemProps } from 'components/site/navigation'; +import type { NavigationItemProps } from 'components/site/baseNavigation'; interface DocsIndexListPageContentProps { navigationData: NavigationItemProps | undefined; diff --git a/components/site/docsLayout.tsx b/components/site/docsLayout.tsx index 16317ec5f..f8c082a21 100644 --- a/components/site/docsLayout.tsx +++ b/components/site/docsLayout.tsx @@ -1,5 +1,6 @@ import Layout from './layout'; -import Navigation, { NavigationItemProps } from './navigation'; +import { NavigationItemProps } from './baseNavigation'; +import Navigation from './navigation'; type LayoutProps = { children?: React.ReactNode; diff --git a/components/site/navigation.tsx b/components/site/navigation.tsx index 1f9dd520f..d1fe42ce5 100644 --- a/components/site/navigation.tsx +++ b/components/site/navigation.tsx @@ -1,94 +1,22 @@ import React from 'react'; -import { getWindow } from 'utils/ssr'; import Link from 'next/link'; -import { Box, BoxProps, ScreenReaderOnly, styles } from '@sparkpost/matchbox'; +import { Box } from '@sparkpost/matchbox'; import { - KeyboardArrowDown, - KeyboardArrowUp, Forum, PeopleOutline, Code, DeviceHub, Feedback, } from '@sparkpost/matchbox-icons'; -import styled from 'styled-components'; -import css from '@styled-system/css'; -import { tokens } from '@sparkpost/design-tokens'; import useStatus from 'hooks/useStatus'; - -export interface NavigationItemProps { - title: string; - link: string; - items?: this[]; - level?: number; - lastUpdated?: string; - description?: string; -} - -type NavigationProps = { - data?: NavigationItemProps[]; - title: string; - titleLink: string; -}; - -const StyledLink = styled.a<{ $active?: boolean; $level?: number }>` - display: block; - cursor: pointer; - overflow-wrap: anywhere; - - &, - &:visited, - &:active { - text-decoration: none; - ${({ $active, $level }) => - css({ - py: '200', - pr: '700', - pl: `calc(${tokens.spacing_500} + ${tokens.spacing_200} * ${$level})`, - bg: $active ? 'blue.700' : 'transparent', - color: $active ? 'white' : 'gray.900', - })} - } - - &:hover { - ${({ $active }) => { - return css({ - bg: $active ? 'blue.700' : 'gray.200', - color: $active ? 'white' : 'gray.900', - }); - }} - } -`; - -const StatusColorMap = { - none: 'green.700', - minor: 'yellow.400', - major: 'brand.orange', - critical: 'red.700', -}; +import BaseNavigation, {NavigationProps, StyledLink, StatusColorMap} from './baseNavigation'; const Navigation = (props: NavigationProps): JSX.Element | null => { const { data = [], title, titleLink } = props; const { status } = useStatus(); return ( - - - - - {title} - - - - {data.map((item, i) => ( - - ))} + @@ -170,101 +98,7 @@ const Navigation = (props: NavigationProps): JSX.Element | null => { - - ); -}; - -const getActiveUrl = (location: Location | undefined) => { - if (!location) { - return ''; - } - - return location.pathname + location.hash; -}; - -const findActiveChild = ( - items: NavigationItemProps[], - activeUrl: string, -): NavigationItemProps | undefined => { - return items.find((item) => { - const hasDirectChild = item.link == activeUrl; - - if (!hasDirectChild && item.items) { - return findActiveChild(item.items, activeUrl); - } - - return hasDirectChild; - }); -}; - -const Chevron = (props: { expanded: boolean }): JSX.Element => { - const { expanded } = props; - return expanded ? : ; -}; - -const StyledChevronWrapper = styled(Box)` - ${styles.buttonReset} - cursor: pointer; - &:hover { - ${({ $active }) => { - return css({ - color: $active ? 'white' : 'blue.700', - }); - }} - } - ${({ $active }) => { - return css({ - color: $active ? 'white' : 'inherit', - }); - }} -`; - -const Item = (props: NavigationItemProps): JSX.Element => { - const { link, title, items, level = 0 } = props; - const [active, setActive] = React.useState(false); - const [expanded, setExpanded] = React.useState(false); - - const environment = getWindow(); - const activeUrl = getActiveUrl(environment?.location); - const hasActiveChild = items && findActiveChild(items, activeUrl); - - React.useEffect(() => { - const isExpanded = Boolean(hasActiveChild) || activeUrl === link || activeUrl.includes(link); - const isActive = activeUrl === link; - - setExpanded(isExpanded); - setActive(isActive); - }, [link, activeUrl]); - - return ( - - - - {title} - - - - {items && ( - setExpanded(!expanded)} - aria-expanded={expanded} - aria-controls={`${link}__items`} - $active={active} - > - Expand Menu - - - )} - - {expanded && items && items.map((item, i) => )} - - + ); }; diff --git a/pages/analyst/[...slug].tsx b/pages/analyst/[...slug].tsx index bd848065e..d51038034 100644 --- a/pages/analyst/[...slug].tsx +++ b/pages/analyst/[...slug].tsx @@ -11,8 +11,8 @@ import SEO from 'components/site/seo'; import Markdown from 'components/markdown'; import AnalystLayout from 'components/site/analystLayout'; import DocumentationContent from 'components/site/documentationContent'; -import AnalystIndexListPageContent from 'components/site/analystIndexListPageContent'; -import type { NavigationItemProps } from 'components/site/analystNavigation'; +import IndexListPageContent from 'components/site/docsIndexListPageContent'; +import type { NavigationItemProps } from 'components/site/baseNavigation'; import { useRouter } from 'next/router'; type PostPageProps = { @@ -50,7 +50,7 @@ const PostPage = (props: PostPageProps): JSX.Element => { description={data.description} > {isIndex && navigationData ? ( - + ) : ( {content} )} diff --git a/pages/analyst/index.tsx b/pages/analyst/index.tsx index 41eabe7fc..83e11b03a 100644 --- a/pages/analyst/index.tsx +++ b/pages/analyst/index.tsx @@ -3,7 +3,7 @@ import { GetStaticProps } from 'next'; import { getAnalystSupportNavigation, getCategoryData } from 'lib/api'; import SEO from 'components/site/seo'; import AnalystLayout from 'components/site/analystLayout'; -import type { NavigationItemProps } from 'components/site/navigation'; +import type { NavigationItemProps } from 'components/site/baseNavigation'; import { CategoriesProvider, Category } from 'context/categories'; type IndexPageProps = { diff --git a/pages/docs/[...slug].tsx b/pages/docs/[...slug].tsx index 99ba90c01..84719883e 100644 --- a/pages/docs/[...slug].tsx +++ b/pages/docs/[...slug].tsx @@ -12,7 +12,7 @@ import Markdown from 'components/markdown'; import DocsLayout from 'components/site/docsLayout'; import DocumentationContent from 'components/site/documentationContent'; import DocsIndexListPageContent from 'components/site/docsIndexListPageContent'; -import type { NavigationItemProps } from 'components/site/navigation'; +import type { NavigationItemProps } from 'components/site/baseNavigation'; import { useRouter } from 'next/router'; type PostPageProps = { diff --git a/pages/docs/index.tsx b/pages/docs/index.tsx index 18dfdbbe4..ae67ab96d 100644 --- a/pages/docs/index.tsx +++ b/pages/docs/index.tsx @@ -4,7 +4,7 @@ import { getCategoryData } from 'lib/api'; import SEO from 'components/site/seo'; import DocsLayout from 'components/site/docsLayout'; import { getSupportNavigation } from 'lib/api'; -import type { NavigationItemProps } from 'components/site/navigation'; +import type { NavigationItemProps } from 'components/site/baseNavigation'; import { CategoriesProvider, Category } from 'context/categories'; type IndexPageProps = { diff --git a/pages/submit-a-ticket.tsx b/pages/submit-a-ticket.tsx index dd98c691e..afb5163ab 100644 --- a/pages/submit-a-ticket.tsx +++ b/pages/submit-a-ticket.tsx @@ -3,7 +3,7 @@ import { getSupportNavigation } from 'lib/api'; import { Box, Text, Button, Stack } from '@sparkpost/matchbox'; import SEO from 'components/site/seo'; import DocsLayout from 'components/site/docsLayout'; -import type { NavigationItemProps } from 'components/site/navigation'; +import type { NavigationItemProps } from 'components/site/baseNavigation'; type SubmitATicketPageProps = { navigationData?: NavigationItemProps[]; From b23b46a5e79b4a1a5968c6320c830791108458a0 Mon Sep 17 00:00:00 2001 From: Tarryn Schantell Date: Thu, 23 Feb 2023 16:32:16 -0600 Subject: [PATCH 3/4] EA-9267 - Update algolia index build script --- .github/workflows/index.yml | 2 +- package.json | 1 + scripts/analystIndex.mjs | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 scripts/analystIndex.mjs diff --git a/.github/workflows/index.yml b/.github/workflows/index.yml index 67468196e..abf22557d 100644 --- a/.github/workflows/index.yml +++ b/.github/workflows/index.yml @@ -40,7 +40,7 @@ jobs: # Runs a single command using the runners shell - name: Index Posts - run: npm run build:index:momentum && npm run build:index:support + run: npm run build:index:momentum && npm run build:index:support && npm run build:index:analyst env: NEXT_PUBLIC_ALGOLIA_APP_ID: ${{secrets.NEXT_PUBLIC_ALGOLIA_APP_ID}} ALGOLIA_SEARCH_ADMIN_KEY: ${{secrets.ALGOLIA_SEARCH_ADMIN_KEY}} diff --git a/package.json b/package.json index 75d9a6dc7..6e90d3495 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dev": "next dev", "build": "next build", "build:ssr": "next build && next export", + "build:index:analyst": "node ./scripts/analystIndex.mjs", "build:index:momentum": "node ./scripts/momentumIndex.mjs", "build:index:support": "node ./scripts/supportIndex.mjs", "build:images": "rimraf public/content && node ./scripts/copyImages.mjs", diff --git a/scripts/analystIndex.mjs b/scripts/analystIndex.mjs new file mode 100644 index 000000000..6098ad88d --- /dev/null +++ b/scripts/analystIndex.mjs @@ -0,0 +1,23 @@ +#!/usr/bin/env node + +import fs from 'fs'; +import path from 'path'; +import glob from 'glob'; +import matter from 'gray-matter'; +import { buildIndex } from './algoliaIndex.mjs'; + +const getAllAnalystPosts = () => { + return glob.sync(`content/analyst/**/!(index).md`).map((file) => { + const withoutExtensions = file.replace(/.md$/, ''); + const url = withoutExtensions.replace(/^content\//, '/'); + const filePath = path.join(path.join(process.cwd(), 'content'), `${url}.md`); + + if (fs.existsSync(filePath)) { + return { url, file, ...matter(fs.readFileSync(filePath, 'utf8')) }; + } + }); +}; + +(async function () { + buildIndex('next_analyst_documentation', getAllAnalystPosts); +})(); From 5ec16047298794f3c14582c5c696d221a1af0b3b Mon Sep 17 00:00:00 2001 From: Tarryn Schantell Date: Fri, 24 Feb 2023 09:14:18 -0600 Subject: [PATCH 4/4] Whitespace battle - tabs vs spaces --- components/site/analystHomePageContent.tsx | 12 +- components/site/analystNavigation.tsx | 10 +- components/site/baseHomePageContent.tsx | 166 ++++++++++----------- components/site/baseNavigation.tsx | 38 ++--- components/site/docsHomePageContent.tsx | 12 +- components/site/layout.tsx | 4 +- components/site/status.tsx | 2 +- 7 files changed, 122 insertions(+), 122 deletions(-) diff --git a/components/site/analystHomePageContent.tsx b/components/site/analystHomePageContent.tsx index d334686b6..163069aca 100644 --- a/components/site/analystHomePageContent.tsx +++ b/components/site/analystHomePageContent.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Feedback } from '@sparkpost/matchbox-icons'; import Link from 'next/link'; -import BaseHomePageContent, {SimpleStyledLink} from './baseHomePageContent'; +import BaseHomePageContent, { SimpleStyledLink } from './baseHomePageContent'; /* #Content */ const gettingStarted = { @@ -14,7 +14,7 @@ const gettingStarted = { desc: 'General platform definitions & common account-level inquiries', linkText: 'Learn More', }, - { + { url: '/analyst/inbox-and-design-tracker/', title: 'Inbox Tracker & Design Tracker', desc: 'A collection of How-To Guides', @@ -60,15 +60,15 @@ const help = { ), content: [ { text: 'Submit a ticket', url: 'mailto:support@emailanalyst.com', Icon: Feedback }, - ], + ], }; const AnalystHomePageContent = () => { return ( + gettingStarted={gettingStarted} + help={help} + /> ); }; diff --git a/components/site/analystNavigation.tsx b/components/site/analystNavigation.tsx index 9a39455f2..c2ccd5bdf 100644 --- a/components/site/analystNavigation.tsx +++ b/components/site/analystNavigation.tsx @@ -6,12 +6,12 @@ import { Feedback, } from '@sparkpost/matchbox-icons'; import useStatus from 'hooks/useStatus'; -import BaseNavigation, {NavigationProps, StyledLink, StatusColorMap} from './baseNavigation'; +import BaseNavigation, { NavigationProps, StyledLink, StatusColorMap } from './baseNavigation'; const AnalystNavigation = (props: NavigationProps): JSX.Element | null => { const { data = [], title, titleLink } = props; const { status } = useStatus(); - + return ( @@ -31,7 +31,7 @@ const AnalystNavigation = (props: NavigationProps): JSX.Element | null => { - API Docs + API Docs @@ -61,8 +61,8 @@ const AnalystNavigation = (props: NavigationProps): JSX.Element | null => { - - + + ); }; diff --git a/components/site/baseHomePageContent.tsx b/components/site/baseHomePageContent.tsx index 0159342ad..67a0caea4 100644 --- a/components/site/baseHomePageContent.tsx +++ b/components/site/baseHomePageContent.tsx @@ -41,10 +41,10 @@ type SimpleStyledLinkProp = { }; export type BaseHomePageContentProps = { - gettingStarted?: {content: CardProps[]} & SectionContentProps; - migrationGuides?: {content: GuideLinkProps[]} & SectionContentProps; - clientLibraries?: {content: LibraryProps[]} & SectionContentProps; - help?: {content: HelpLinkProps[]} & SectionContentProps; + gettingStarted?: { content: CardProps[] } & SectionContentProps; + migrationGuides?: { content: GuideLinkProps[] } & SectionContentProps; + clientLibraries?: { content: LibraryProps[] } & SectionContentProps; + help?: { content: HelpLinkProps[] } & SectionContentProps; }; /* Styles */ @@ -53,44 +53,44 @@ export const SimpleStyledLink = styled.a` text-decoration: none; transition: color 0.3s; ${css({ - marginY: '200', - })} + marginY: '200', +})} &, &:visited { ${css({ - color: 'gray.900', - })} + color: 'gray.900', +})} } &:hover { ${css({ - color: 'blue.700', - })} + color: 'blue.700', +})} } ${css({ - fontWeight: 'normal', - textDecoration: 'underline', - })} + fontWeight: 'normal', + textDecoration: 'underline', +})} `; const BlueStyledLink = styled.a` text-decoration: none; ${css({ - paddingY: '200', - paddingX: '400', - marginRight: '200', - })} + paddingY: '200', + paddingX: '400', + marginRight: '200', +})} transition: background-color .3s; > svg { ${css({ - marginRight: '200', - })} + marginRight: '200', +})} } &:hover { ${css({ - backgroundColor: 'blue.200', - })} + backgroundColor: 'blue.200', +})} } `; @@ -105,8 +105,8 @@ const StyledCard = styled(Box)` width: calc(100% + 2px); height: 4px; ${css({ - backgroundColor: 'blue.700', - })} + backgroundColor: 'blue.700', +})} } `; @@ -122,24 +122,24 @@ const BoxStyledLink = styled.a` transition: background-color 0.3s; min-width: 165px; ${css({ - color: 'gray.900', - paddingX: '450', - paddingY: '300', - borderColor: 'gray.900', - marginBottom: '500', - fontSize: '200', - lineHeight: '200', - })} + color: 'gray.900', + paddingX: '450', + paddingY: '300', + borderColor: 'gray.900', + marginBottom: '500', + fontSize: '200', + lineHeight: '200', +})} &:hover { ${css({ - backgroundColor: 'gray.300', - color: 'gray.900', - })} + backgroundColor: 'gray.300', + color: 'gray.900', +})} } > svg { ${css({ - marginRight: '300', - })} + marginRight: '300', +})} } `; @@ -221,56 +221,56 @@ const HelpLink = (props: HelpLinkProps): JSX.Element => { }; const BaseHomePageContent = (props: BaseHomePageContentProps) => { - const {gettingStarted, migrationGuides, clientLibraries, help} = props; + const { gettingStarted, migrationGuides, clientLibraries, help } = props; return ( <> - {gettingStarted && (<> - - - Help & API - - - - {gettingStarted.content.map((cc, i) => { - return ; - })} - - - )} - {migrationGuides && (<> -
- - - - {migrationGuides.content.map((link, i) => { - return ; - })} - - - )} - {clientLibraries && (<> -
- - - - {clientLibraries.content.map((library, i) => { - return ; - })} - - - )} - {help && (<> -
- - - - {help.content.map((links, i) => { - return ; - })} - - - )} + {gettingStarted && (<> + + + Help & API + + + + {gettingStarted.content.map((cc, i) => { + return ; + })} + + + )} + {migrationGuides && (<> +
+ + + + {migrationGuides.content.map((link, i) => { + return ; + })} + + + )} + {clientLibraries && (<> +
+ + + + {clientLibraries.content.map((library, i) => { + return ; + })} + + + )} + {help && (<> +
+ + + + {help.content.map((links, i) => { + return ; + })} + + + )}
); diff --git a/components/site/baseNavigation.tsx b/components/site/baseNavigation.tsx index 0418deba1..4778880cd 100644 --- a/components/site/baseNavigation.tsx +++ b/components/site/baseNavigation.tsx @@ -20,7 +20,7 @@ export interface NavigationItemProps { } export type NavigationProps = { - children?: React.ReactNode; + children?: React.ReactNode; data?: NavigationItemProps[]; title: string; titleLink: string; @@ -36,22 +36,22 @@ export const StyledLink = styled.a<{ $active?: boolean; $level?: number }>` &:active { text-decoration: none; ${({ $active, $level }) => - css({ - py: '200', - pr: '700', - pl: `calc(${tokens.spacing_500} + ${tokens.spacing_200} * ${$level})`, - bg: $active ? 'blue.700' : 'transparent', - color: $active ? 'white' : 'gray.900', - })} + css({ + py: '200', + pr: '700', + pl: `calc(${tokens.spacing_500} + ${tokens.spacing_200} * ${$level})`, + bg: $active ? 'blue.700' : 'transparent', + color: $active ? 'white' : 'gray.900', + })} } &:hover { ${({ $active }) => { - return css({ - bg: $active ? 'blue.700' : 'gray.200', - color: $active ? 'white' : 'gray.900', - }); - }} + return css({ + bg: $active ? 'blue.700' : 'gray.200', + color: $active ? 'white' : 'gray.900', + }); + }} } `; @@ -83,7 +83,7 @@ const BaseNavigation = (props: NavigationProps): JSX.Element | null => { {data.map((item, i) => ( ))} - {children} + {children} ); }; @@ -116,15 +116,15 @@ const Chevron = (props: { expanded: boolean }): JSX.Element => { return expanded ? : ; }; -const StyledChevronWrapper = styled(Box)` +const StyledChevronWrapper = styled(Box) ` ${styles.buttonReset} cursor: pointer; &:hover { ${({ $active }) => { - return css({ - color: $active ? 'white' : 'blue.700', - }); - }} + return css({ + color: $active ? 'white' : 'blue.700', + }); + }} } ${({ $active }) => { return css({ diff --git a/components/site/docsHomePageContent.tsx b/components/site/docsHomePageContent.tsx index bd3a32b49..f6b82bac0 100644 --- a/components/site/docsHomePageContent.tsx +++ b/components/site/docsHomePageContent.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Feedback, HelpOutline, QuestionAnswer } from '@sparkpost/matchbox-icons'; import Link from 'next/link'; import { Elixir, PHP, Java, NodeMailer, Python, Node, Go } from './icons'; -import BaseHomePageContent, {SimpleStyledLink} from './baseHomePageContent'; +import BaseHomePageContent, { SimpleStyledLink } from './baseHomePageContent'; /* ██████╗ ██████╗ ███╗ ██╗████████╗███████╗███╗ ██╗████████╗ @@ -98,11 +98,11 @@ const help = { const DocsHomePageContent = () => { return ( + gettingStarted={gettingStarted} + help={help} + clientLibraries={clientLibraries} + migrationGuides={migrationGuides} + /> ); }; diff --git a/components/site/layout.tsx b/components/site/layout.tsx index e60f1552c..b3c70a9c3 100644 --- a/components/site/layout.tsx +++ b/components/site/layout.tsx @@ -7,7 +7,7 @@ import LayoutInnerContent from './layoutInnerContent'; type LayoutProps = { children?: React.ReactNode; navigationComponent?: React.ReactNode; - hideDrawerButtons?: boolean; + hideDrawerButtons?: boolean; }; const Layout = (props: LayoutProps): JSX.Element => { @@ -24,7 +24,7 @@ const Layout = (props: LayoutProps): JSX.Element => { {navigationComponent} - {!props.hideDrawerButtons && ()} + {!props.hideDrawerButtons && ()} diff --git a/components/site/status.tsx b/components/site/status.tsx index b41c9580d..0a271c0f8 100644 --- a/components/site/status.tsx +++ b/components/site/status.tsx @@ -12,7 +12,7 @@ declare global { const Status = () => { const { setStatus } = useStatus(); - const environment = getWindow(); + const environment = getWindow(); return ( <>