Skip to content

Commit

Permalink
adds component-level documentation (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
arvind0598 committed Sep 24, 2021
1 parent bf87e0d commit 0ddd04b
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 6 deletions.
14 changes: 14 additions & 0 deletions components/head/head.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ type PageHeadProps = {
title: string;
};

/**
* @summary Component for rendering the <head/> tag of the HTML page.
*
* @description
* This component renders the header on top of the HTML. This currently takes one
* parameter, the title of the page. Note that we should not be including fonts or
* stylesheets here, as that means it'll end up reloading that for each page.
*
* @todo
* Improve the tags here for better SEO.
*
* @param {PageHeadProps} props props
* @returns the component
*/
const PageHead: FC<PageHeadProps> = ({ title }) => (
<Head>
<title>{title}</title>
Expand Down
11 changes: 11 additions & 0 deletions components/issue/issue.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/**
* @summary Component to render a card displaying an issue.
*
* @description
* This component currently doesn't do much.
*
* @todo
* Literally everything.
*
* @returns the component
*/
const IssueCard = () => (
<p> Temp </p>
);
Expand Down
43 changes: 43 additions & 0 deletions components/navbar/navbar-mobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,58 @@ import {
import styles from './navbar.module.scss';

type NavbarIconProps = {
/**
* The Grommet Icon that is to be rendered.
*
* @todo
* We should consider using an Icon here instead of the element, just to allow for
* consistency with how we use it within the badge of a repository card.
*/
icon: JSX.Element;

/**
* The URL that will be passed as the HREF for the anchor tag.
*/
href: string;
};

/**
* @summary Component to render the Mobile Navbar with Icons on them.
*
* @description
* This component is rendered conditionally on mobiles only. It shows up at the bottom
* and contains icons to redirect to all of the other pages.
*
* @todo
* Determine if lazy loading even matters in Next.
*
* @returns the component.
*/
const MobileNavbar: FC<{}> = () => {
const router = useRouter();

/**
* @summary This function allows for shallow routing to a relative URL.
*
* @description
* This is just a wrapper over the inbuilt router push to allow for shallow routing.
*
* @param {string} href The relative URL to direct to.
* @returns a promise to do so
*/
const followLink = (href: string) => router.push(href, undefined, { shallow: true });

/**
* @summary Component to render individual icons on the Mobile Navbar.
*
* @description
* This component exists for the simple reason that I didn't want to pass the same props
* over and over again for each Anchor. This is also why its not a different file and instead
* sits inside another component.
*
* @param {NavbarIconProps} props props
* @returns the component.
*/
const NavbarIcon: FC<NavbarIconProps> = ({ icon, href }) => (
<Anchor icon={icon} color="white" onClick={() => followLink(href)} />
);
Expand Down
70 changes: 67 additions & 3 deletions components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,92 @@ import {
} from 'grommet';
import useResponsiveContext from '../../hooks/use-responsive-context';

type NavbarAnchorProps = {
/**
* The label that will be rendered as the link on the navbar.
*/
label: string;

/**
* The URL that will be passed as the HREF for the anchor tag.
*/
href: string;
};

/**
* @summary Component to render the Desktop Navbar.
*
* @description
* This component is used to show the text links on the top of the screen. It
* accepts no props, instead resorting to the responsive context to make sure that
* everything is sized and aligned appropriately.
*
* @returns the component.
*/
const Navbar: FC<{}> = () => {
const { isMobile } = useResponsiveContext();
const headingLevel = isMobile ? '1' : '2';
const textAlign = isMobile ? 'center' : 'start';
const router = useRouter();

/**
* @see "navbar-mobile.tsx"
*
* @todo
* This function exists in both the navbars. While its minimum code duplication,
* we could look at moving this to a hook of its own to abstract away some code.
*
* @param {string} href The relative URL to direct to.
* @returns a promise to do so
*/
const followLink = (href: string) => router.push(href, undefined, { shallow: true });

/**
* @summary Component to render individual label on the desktop Navbar.
*
* @description
* This component exists to abstract away common props like the margin and the onClick.
* @param {NavbarAnchorProps} props props
* @returns the component.
*/
const NavbarAnchor: FC<NavbarAnchorProps> = ({ label, href }) => (
<Anchor
label={label}
onClick={() => followLink(href)}
margin={{ horizontal: 'medium' }}
/>
);

/**
* @summary This function conditionally renders the links in the navbar on a desktop.
*
* @description
* We return nothing if we're on a mobile device, otherwise we return the standard navbar.
*
* @returns null, or the navbar, depending on the device.
*/
const renderDesktopLinks = (): React.ReactNode | null => {
if (isMobile) return null;
return (
<Box flex direction="row" justify="end" align="center">
<Anchor label="Random" onClick={() => { }} margin={{ horizontal: 'medium' }} />
<Anchor label="About" onClick={() => followLink('/about')} margin={{ horizontal: 'medium' }} />
<NavbarAnchor label="Random" href="#" />
<NavbarAnchor label="About" href="/about" />
</Box>
);
};

return (
<Header pad="medium" justify="evenly" direction="row">
<Box flex>
<Heading level={headingLevel} textAlign={textAlign} onClick={() => followLink('/')} style={{ cursor: 'pointer' }}>Better First Issues</Heading>
<Heading
level={headingLevel}
textAlign={textAlign}
onClick={() => followLink('/')}
style={{ cursor: 'pointer' }}
>
Better First Issues
</Heading>
</Box>
{
renderDesktopLinks()
Expand Down
55 changes: 52 additions & 3 deletions components/repository/repository.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable react/require-default-props */
import {
Box, Heading, Paragraph, Text,
} from 'grommet';
Expand All @@ -6,17 +7,56 @@ import { AlignSelfType } from 'grommet/utils';
import { Repository } from '../../types/repository-search';
import classes from './repository.module.scss';

/**
* The Repository Card Props accepts the Repository Type directly as props.
* Refer to the type to see what it contains.
*
* @see "repository-search.ts"
*/
type Props = Repository;

type BadgeProps = {
// eslint-disable-next-line react/require-default-props
/**
* The icon to be rendered on the badge.
*/
BadgeIcon?: Icon;

/**
* The text to be displayed next to the badge.
*/
text: string | number | null;
// eslint-disable-next-line react/require-default-props

/**
* This is used to align the badge within the card that displays it.
*
* @todo
* Get this to work. This currently does not make any difference.
*/
align?: AlignSelfType;
};

/**
* @summary Component to render badges on top of cards.
*
* @description
* This shows some useful information, kind of like a notification. It is
* currently used only within the Repository Card, but once the Issue component
* is developed we can move this to its own directory.
*
* @param {BadgeProps} props props
* @returns the component.
*/
const Badge = ({ BadgeIcon, text, align }: BadgeProps): JSX.Element => {
/**
* @summary This function accepts the ICON type and makes an element out of it.
*
* @description
* BadgeIcon is of the Icon type, which is different from the JSX.Element type,
* which is what we need to render the actual icon. We do that so that we can
* pass our own props to it before rendering it.
*
* @returns a JSX.Element, but NULL if nothing is passed to it.
*/
const renderedIcon = () => {
if (!BadgeIcon) return null;
return <BadgeIcon color="#FFFCF5" />;
Expand All @@ -40,14 +80,23 @@ const Badge = ({ BadgeIcon, text, align }: BadgeProps): JSX.Element => {
);
};

/**
* @summary Component to render an individual repository card.
*
* @description
* This component accepts ALL the data related to a repository and then creates
* a card that the user can interact with to get data related to it.
*
* @param {Props} props the repository object
* @returns the component.
*/
const RepositoryCard = ({
fullName,
language,
description,
stars,
forks,
}: Props) => (

<Box pad="medium" background="light-2" margin="small" direction="column" className={classes.card}>
<Box direction="row" justify="end">
<Badge text={language} />
Expand Down
14 changes: 14 additions & 0 deletions components/search/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,23 @@ import { useState } from 'react';
import useResponsiveContext from '../../hooks/use-responsive-context';

type Props = {
/**
* This is a function that is called when the search is triggered.
*/
updateSearchTerm: (term: string) => void;
};

/**
* @summary Component to render the Search Box and accept user input.
*
* @description
* This component has a text box and a button, and that's the end of that story.
* It renders sizes slightly differently based on the screen size. It also has internal
* state to represent the text in the search input.
*
* @param {Props} props props
* @returns the component.
*/
const SearchForm = ({ updateSearchTerm }: Props) => {
const { isMobile, isTablet } = useResponsiveContext();
const [searchTerm, setSearchTerm] = useState('');
Expand Down
30 changes: 30 additions & 0 deletions components/viewer/viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,24 @@ import { ScreenWidth } from '../../types/responsive';
import RepositoryCard from '../repository/repository';
import styles from './viewer.module.scss';

/**
* The Viewer Props accepts the RepositorySearchData Type directly as props.
* Refer to the type to see what it contains.
*
* @see "repository-search.ts"
*/
type Props = RepositorySearchData;

/**
* So this exists to make sure that we enforce the breakpoints to be similar
* throughout the codebase. Kinda hacky, but works.
*/
type MasonryBreakpoints = Record<ScreenWidth, number> & { default: number };

/**
* Implementing breakpoints. This is a for width in pixels and determines the
* number of columns that the layout will have under a given width.
*/
const masonryBreakpoints: MasonryBreakpoints = {
600: 1,
900: 2,
Expand All @@ -17,7 +31,23 @@ const masonryBreakpoints: MasonryBreakpoints = {
default: 5,
};

/**
* @summary Parent component that displays search results.
*
* @description
* This is currently extremely useless. Once we add filters and sorting
* options, this component will start making way more sense. As it is right now,
* it just serves as a very fancy for-loop.
*
* @param {Props} props props
* @returns the component.
*/
const Viewer = ({ data }: Props) => {
/**
* @summary This function really should not need documentation
*
* @returns an array of cards to render.
*/
// eslint-disable-next-line react/jsx-props-no-spreading
const renderCards = () => data.map((repo) => <RepositoryCard {...repo} key={repo.fullName} />);

Expand Down

1 comment on commit 0ddd04b

@vercel
Copy link

@vercel vercel bot commented on 0ddd04b Sep 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.