├── .babelrc.js
├── .browserslistrc
├── .circleci
└── config.yml
├── .eslintignore
├── .eslintrc.js
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ └── chromatic.yml
├── .gitignore
├── .huskyrc
├── .lintstagedrc
├── .mergify.yml
├── .nvmrc
├── .prettierignore
├── .storybook
├── OCTheme.js
├── backgrounds.js
├── main.ts
├── manager.js
├── preview-head.html
└── preview.js
├── .stylelintrc
├── .vercelignore
├── .vscode
└── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CONTRIBUTING_TO_DESIGN.md
├── LICENSE
├── README.md
├── common
├── config
│ ├── environment.js
│ └── svgo.js
├── constants
│ ├── api.ts
│ ├── descriptions.ts
│ ├── messages.ts
│ ├── navigation.js
│ ├── partners.js
│ ├── successStories.js
│ ├── testIDs.js
│ ├── unitsOfTime.js
│ └── urls.js
├── styles
│ ├── breakpoints.js
│ ├── globals.css
│ ├── media-queries.css
│ ├── styleExports.ts
│ ├── themeMap.js
│ └── variables.css
└── utils
│ ├── __tests__
│ ├── __snapshots__
│ │ └── next-utils.test.js.snap
│ ├── api-utils.test.js
│ ├── array-utils.test.ts
│ ├── next-utils.test.js
│ ├── prop-utils.test.js
│ ├── string-utils.test.js
│ └── style-utils.test.js
│ ├── api-utils.ts
│ ├── array-utils.ts
│ ├── auth-utils.js
│ ├── cookie-utils.ts
│ ├── next-utils.js
│ ├── prop-utils.js
│ ├── string-utils.js
│ ├── style-utils.js
│ └── thirdParty
│ └── gtag.js
├── components
├── Accordion
│ ├── Accordion.module.css
│ ├── Accordion.tsx
│ ├── __stories__
│ │ └── Accordion.stories.tsx
│ └── __tests__
│ │ └── Accordion.test.tsx
├── Alert
│ ├── Alert.module.css
│ ├── Alert.tsx
│ ├── __stories__
│ │ └── Alert.stories.tsx
│ └── __tests__
│ │ ├── Alert.test.tsx
│ │ └── __snapshots__
│ │ └── Alert.test.tsx.snap
├── Badge
│ ├── Badge.tsx
│ ├── __stories__
│ │ └── Badge.stories.tsx
│ └── __tests__
│ │ ├── Badge.test.tsx
│ │ └── __snapshots__
│ │ └── Badge.test.tsx.snap
├── Branding
│ ├── ColorSection
│ │ ├── ColorSection.tsx
│ │ └── __tests__
│ │ │ ├── ColorSection.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── ColorSection.test.tsx.snap
│ ├── FontSection
│ │ ├── FontSection.tsx
│ │ └── __tests__
│ │ │ ├── FontSection.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── FontSection.test.tsx.snap
│ ├── LogoSection
│ │ ├── LogoSection.module.css
│ │ ├── LogoSection.tsx
│ │ └── __tests__
│ │ │ ├── LogoSection.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── LogoSection.test.tsx.snap
│ └── Swatch
│ │ ├── Swatch.tsx
│ │ └── __tests__
│ │ ├── Swatch.test.tsx
│ │ └── __snapshots__
│ │ └── Swatch.test.tsx.snap
├── Buttons
│ ├── Button
│ │ ├── Button.module.css
│ │ ├── Button.tsx
│ │ ├── __stories__
│ │ │ └── Button.stories.tsx
│ │ └── __tests__
│ │ │ ├── Button.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── Button.test.tsx.snap
│ ├── CloseButton
│ │ ├── CloseButton.module.css
│ │ ├── CloseButton.tsx
│ │ ├── __stories__
│ │ │ └── CloseButton.stories.tsx
│ │ └── __tests__
│ │ │ ├── CloseButton.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── CloseButton.test.tsx.snap
│ └── LinkButton
│ │ ├── LinkButton.tsx
│ │ ├── __stories__
│ │ └── LinkButton.stories.tsx
│ │ └── __tests__
│ │ ├── LinkButton.test.tsx
│ │ └── __snapshots__
│ │ └── LinkButton.test.tsx.snap
├── Cards
│ ├── Card
│ │ ├── Card.tsx
│ │ ├── __stories__
│ │ │ └── Card.stories.tsx
│ │ └── __tests__
│ │ │ ├── Card.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── Card.test.tsx.snap
│ ├── FlatCard
│ │ ├── FlatCard.js
│ │ ├── __stories__
│ │ │ └── FlatCard.stories.js
│ │ └── __tests__
│ │ │ ├── FlatCard.test.js
│ │ │ └── __snapshots__
│ │ │ └── FlatCard.test.js.snap
│ ├── ImageCard
│ │ ├── ImageCard.tsx
│ │ ├── __stories__
│ │ │ └── ImageCard.stories.tsx
│ │ └── __tests__
│ │ │ ├── ImageCard.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── ImageCard.test.tsx.snap
│ └── ValueCard
│ │ ├── ValueCard.tsx
│ │ ├── __stories__
│ │ └── ValueCard.stories.tsx
│ │ └── __tests__
│ │ ├── ValueCard.test.tsx
│ │ └── __snapshots__
│ │ └── ValueCard.test.tsx.snap
├── Container
│ ├── Container.module.css
│ ├── Container.tsx
│ ├── __stories__
│ │ └── Container.stories.tsx
│ └── __tests__
│ │ ├── Container.test.tsx
│ │ └── __snapshots__
│ │ └── Container.test.tsx.snap
├── Content
│ ├── Content.tsx
│ ├── __stories__
│ │ └── Content.stories.tsx
│ └── __tests__
│ │ ├── Content.test.tsx
│ │ └── __snapshots__
│ │ └── Content.test.tsx.snap
├── Drawer
│ ├── Drawer.tsx
│ ├── __stories__
│ │ └── Drawer.stories.tsx
│ └── __tests__
│ │ ├── Drawer.test.tsx
│ │ └── __snapshots__
│ │ └── Drawer.test.tsx.snap
├── ErrorDisplay
│ ├── ErrorDisplay.tsx
│ └── __tests__
│ │ ├── ErrorDisplay.test.tsx
│ │ └── __snapshots__
│ │ └── ErrorDisplay.test.tsx.snap
├── FeaturedJobItem
│ ├── FeaturedJobItem.tsx
│ ├── __stories__
│ │ └── FeaturedJobItem.stories.tsx
│ ├── __tests__
│ │ ├── FeaturedJobItem.test.tsx
│ │ └── __snapshots__
│ │ │ └── FeaturedJobItem.test.tsx.snap
│ └── featuredJobs.json
├── Footer
│ ├── Footer.tsx
│ └── __tests__
│ │ ├── Footer.test.tsx
│ │ └── __snapshots__
│ │ └── Footer.test.tsx.snap
├── Form
│ ├── Checkbox
│ │ ├── Checkbox.js
│ │ └── Checkbox.module.css
│ ├── Form.tsx
│ ├── Input
│ │ ├── Input.module.css
│ │ ├── Input.tsx
│ │ ├── __stories__
│ │ │ └── Input.stories.js
│ │ └── __tests__
│ │ │ ├── Input.test.tsx
│ │ │ └── __snapshots__
│ │ │ ├── Input.test.js.snap
│ │ │ └── Input.test.tsx.snap
│ ├── Label
│ │ ├── Label.module.css
│ │ ├── Label.tsx
│ │ ├── __stories__
│ │ │ └── Label.stories.js
│ │ └── __tests__
│ │ │ ├── Label.test.js
│ │ │ └── __snapshots__
│ │ │ └── Label.test.js.snap
│ ├── MultiStepForm.tsx
│ ├── Select
│ │ ├── Select.module.css
│ │ ├── SelectMulti.tsx
│ │ ├── SelectSingle.tsx
│ │ ├── ThemedReactSelect.tsx
│ │ ├── __stories__
│ │ │ ├── SelectMulti.stories.tsx
│ │ │ └── SelectSingle.stories.tsx
│ │ └── __tests__
│ │ │ ├── SelectMulti.test.tsx
│ │ │ ├── SelectSingle.test.tsx
│ │ │ ├── ThemedReactSelect.test.tsx
│ │ │ └── __snapshots__
│ │ │ ├── Select.test.js.snap
│ │ │ ├── Select.test.ts.snap
│ │ │ ├── Select.test.tsx.snap
│ │ │ ├── SelectMulti.test.tsx.snap
│ │ │ ├── SelectSingle.test.tsx.snap
│ │ │ ├── ThemedReactSelect.test.js.snap
│ │ │ └── ThemedReactSelect.test.tsx.snap
│ └── __tests__
│ │ ├── Form.test.tsx
│ │ ├── MultiStepForm.test.js
│ │ └── __snapshots__
│ │ ├── Form.test.js.snap
│ │ └── Form.test.tsx.snap
├── Forms
│ ├── RegistrationForm
│ │ ├── RegistrationForm.tsx
│ │ ├── __stories__
│ │ │ └── RegistrationForm.stories.js
│ │ └── __tests__
│ │ │ ├── RegistrationForm.test.tsx
│ │ │ └── __snapshots__
│ │ │ ├── RegistrationForm.test.js.snap
│ │ │ └── RegistrationForm.test.tsx.snap
│ └── UpdateProfileForm
│ │ ├── UpdateProfileForm.module.css
│ │ ├── UpdateProfileForm.tsx
│ │ ├── __stories__
│ │ └── UpdateProfileForm.stories.tsx
│ │ ├── __tests__
│ │ ├── UpdateProfileForm.test.tsx
│ │ └── __snapshots__
│ │ │ └── UpdateProfileForm.test.tsx.snap
│ │ └── steps
│ │ ├── MilitaryDetails.tsx
│ │ ├── MilitaryStatus.tsx
│ │ ├── PersonalDetails.tsx
│ │ ├── ProfessionalDetails.tsx
│ │ ├── __tests__
│ │ ├── MilitaryDetails.test.tsx
│ │ ├── MilitaryStatus.test.tsx
│ │ ├── PersonalDetails.test.tsx
│ │ ├── ProfessionalDetails.test.tsx
│ │ └── __snapshots__
│ │ │ ├── MilitaryDetails.test.tsx.snap
│ │ │ ├── MilitaryStatus.test.tsx.snap
│ │ │ ├── PersonalDetails.test.tsx.snap
│ │ │ └── ProfessionalDetails.test.tsx.snap
│ │ └── _steps.module.css
├── Heading
│ ├── Heading.module.css
│ ├── Heading.tsx
│ ├── __stories__
│ │ └── Heading.stories.tsx
│ └── __tests__
│ │ ├── Heading.test.tsx
│ │ └── __snapshots__
│ │ └── Heading.test.tsx.snap
├── HeroBanner
│ ├── HeroBanner.tsx
│ ├── __stories__
│ │ └── HeroBanner.stories.tsx
│ └── __tests__
│ │ ├── HeroBanner.test.tsx
│ │ └── __snapshots__
│ │ └── HeroBanner.test.tsx.snap
├── InlineLoadingSpinner.tsx
├── Modal
│ ├── Modal.tsx
│ ├── __stories__
│ │ └── Modal.stories.tsx
│ └── __tests__
│ │ ├── Modal.test.tsx
│ │ └── __snapshots__
│ │ └── Modal.test.tsx.snap
├── Nav
│ ├── Nav.module.css
│ ├── Nav.tsx
│ ├── NavListItem
│ │ ├── NavListItem.module.css
│ │ ├── NavListItem.tsx
│ │ └── __tests__
│ │ │ ├── NavListItem.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── NavListItem.test.tsx.snap
│ ├── NavMobile
│ │ ├── NavMobile.module.css
│ │ ├── NavMobile.tsx
│ │ └── __tests__
│ │ │ ├── NavMobile.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── NavMobile.test.tsx.snap
│ └── __tests__
│ │ ├── Nav.test.tsx
│ │ └── __snapshots__
│ │ └── Nav.test.tsx.snap
├── OutboundLink
│ ├── OutboundLink.tsx
│ ├── __stories__
│ │ └── OutboundLink.stories.tsx
│ └── __tests__
│ │ ├── OutboundLink.test.tsx
│ │ └── __snapshots__
│ │ └── OutboundLink.test.tsx.snap
├── PartnerLogoLink
│ ├── PartnerLogoLink.tsx
│ ├── __stories__
│ │ └── PartnerLogoLink.stories.tsx
│ └── __tests__
│ │ ├── PartnerLogoLink.test.tsx
│ │ └── __snapshots__
│ │ └── PartnerLogoLink.test.tsx.snap
├── Press
│ ├── PressLinks
│ │ ├── Articles.ts
│ │ ├── PressLinks.module.css
│ │ ├── PressLinks.tsx
│ │ └── __tests__
│ │ │ ├── PressLinks.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── PressLinks.test.tsx.snap
│ ├── PressPhotos
│ │ ├── PressPhotos.module.css
│ │ ├── PressPhotos.tsx
│ │ └── __tests__
│ │ │ ├── PressPhotos.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── PressPhotos.test.tsx.snap
│ ├── PressVideos
│ │ ├── PressVideos.module.css
│ │ ├── PressVideos.tsx
│ │ └── __tests__
│ │ │ ├── PressVideos.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── PressVideos.test.tsx.snap
│ └── index.ts
├── ProgressIndicator
│ ├── ProgressIndicator.tsx
│ ├── __stories__
│ │ └── ProgressIndicator.stories.tsx
│ └── __tests__
│ │ ├── ProgressIndicator.test.tsx
│ │ └── __snapshots__
│ │ └── ProgressIndicator.test.tsx.snap
├── ReusableSections
│ ├── DonateSection
│ │ ├── DonateSection.tsx
│ │ ├── __stories__
│ │ │ └── DonateSection.stories.tsx
│ │ └── __tests__
│ │ │ ├── DonateSection.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── DonateSection.test.tsx.snap
│ ├── JoinSection
│ │ ├── JoinSection.tsx
│ │ ├── __stories__
│ │ │ └── JoinSection.stories.tsx
│ │ └── __tests__
│ │ │ ├── JoinSection.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── JoinSection.test.tsx.snap
│ └── SponsorsSection
│ │ ├── SponsorsSection.tsx
│ │ ├── __stories__
│ │ └── SponsorSection.stories.tsx
│ │ └── __tests__
│ │ ├── SponsorsSection.test.tsx
│ │ └── __snapshots__
│ │ └── SponsorsSection.test.tsx.snap
├── ScreenReaderOnly
│ ├── ScreenReaderOnly.tsx
│ ├── __stories__
│ │ └── ScreenReaderOnly.stories.tsx
│ └── __tests__
│ │ ├── ScreenReaderOnly.test.tsx
│ │ └── __snapshots__
│ │ └── ScreenReaderOnly.test.tsx.snap
├── SocialMedia
│ ├── SocialMedia.tsx
│ ├── SocialMediaContainer
│ │ ├── SocialMediaContainer.tsx
│ │ └── __tests__
│ │ │ ├── SocialMediaContainer.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── SocialMediaContainer.test.tsx.snap
│ ├── SocialMediaItem
│ │ ├── SocialMediaItem.tsx
│ │ └── __tests__
│ │ │ ├── SocialMediaItem.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── SocialMediaItem.test.tsx.snap
│ └── __tests__
│ │ ├── SocialMedia.test.tsx
│ │ └── __snapshots__
│ │ └── SocialMedia.test.tsx.snap
├── SuccessStory
│ ├── SuccessStory.tsx
│ ├── __stories__
│ │ └── SuccessStory.stories.tsx
│ └── __tests__
│ │ ├── SuccessStory.test.tsx
│ │ └── __snapshots__
│ │ └── SuccessStory.test.tsx.snap
├── Timeline
│ ├── Timeline.module.css
│ ├── Timeline.tsx
│ ├── TimelineEvent
│ │ ├── TimelineEvent.module.css
│ │ ├── TimelineEvent.tsx
│ │ └── __tests__
│ │ │ ├── TimelineEvent.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── TimelineEvent.test.tsx.snap
│ ├── TimelineNav
│ │ ├── TimelineNav.module.css
│ │ ├── TimelineNav.tsx
│ │ └── __tests__
│ │ │ ├── TimelineNav.test.tsx
│ │ │ └── __snapshots__
│ │ │ └── TimelineNav.test.tsx.snap
│ ├── __tests__
│ │ ├── Timeline.test.tsx
│ │ └── __snapshots__
│ │ │ └── Timeline.test.tsx.snap
│ └── historyData.js
├── UpgradeBrowserOverlay
│ ├── UpgradeBrowserOverlay.module.css
│ ├── UpgradeBrowserOverlay.tsx
│ ├── __stories__
│ │ └── UpgradeBrowserOverlay.stories.tsx
│ └── __tests__
│ │ ├── UpgradeBrowserOverlay.test.tsx
│ │ └── __snapshots__
│ │ └── UpgradeBrowserOverlay.test.tsx.snap
├── ZipRecruiterJobs
│ └── ZipRecruiterJobs.js
└── head.js
├── cypress.config.js
├── cypress
├── README.md
├── e2e
│ ├── faq.spec.js
│ ├── hashlink.spec.js
│ ├── join.spec.ts
│ ├── podcast.spec.js
│ └── team.spec.js
├── fixtures
│ └── example.json
├── plugins
│ └── index.js
├── support
│ ├── commands.js
│ └── e2e.js
└── tsconfig.json
├── decorators
├── README.md
├── __tests__
│ └── getDisplayName.test.js
└── getDisplayName.js
├── internationalized-documentation
├── Arabic
│ └── CODE_OF_CONDUCT_AR.md
├── Germany
│ └── CODE_OF_CONDUCT_DE.md
├── India
│ └── CODE_OF_CONDUCT_HI.md
├── README.md
├── Spanish
│ └── CODE_OF_CONDUCT_ES.md
└── Turkey
│ └── CODE_OF_CONDUCT_TR.md
├── jsconfig.json
├── next-env.d.ts
├── next-sitemap.config.js
├── next.config.js
├── nyc.config.js
├── package.json
├── pages
├── 404.tsx
├── README.md
├── _app.tsx
├── _document.tsx
├── _error.tsx
├── about.tsx
├── api
│ ├── __coverage__.js
│ └── registration
│ │ ├── new.ts
│ │ └── update.ts
├── blog
│ └── index.tsx
├── branding.tsx
├── challenge.tsx
├── chapter_leader.tsx
├── chapters.tsx
├── contact.tsx
├── corporate-training
│ └── index.tsx
├── donate.tsx
├── faq.tsx
├── get_involved.tsx
├── history.tsx
├── index.tsx
├── jobs.tsx
├── join
│ ├── form.tsx
│ ├── index.tsx
│ └── success.tsx
├── podcast.tsx
├── policy.tsx
├── press.tsx
├── project_rebuild.tsx
├── scholarship
│ ├── code_platoon.tsx
│ └── index.tsx
├── services.tsx
├── slack_guide.tsx
├── sponsorship.tsx
├── team.tsx
├── terms.tsx
└── thank_you.tsx
├── pathAliases.js
├── postcss.config.js
├── prettier.config.js
├── public
├── browserconfig.xml
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon-96x96.png
├── favicon.ico
├── favicon.png
├── manifest.json
├── robots.txt
└── static
│ ├── apple-icon-120x120.png
│ ├── apple-icon-144x144.png
│ ├── apple-icon-152x152.png
│ ├── apple-icon-180x180.png
│ ├── apple-icon-57x57.png
│ ├── apple-icon-60x60.png
│ ├── apple-icon-72x72.png
│ ├── apple-icon-76x76.png
│ ├── apple-icon-precomposed.png
│ ├── fonts
│ ├── DINCondensed-Bold.ttf
│ ├── DINCondensed-Bold.woff
│ └── DINCondensed-Bold.woff2
│ ├── icon-144x144.png
│ ├── icon-192x192.png
│ ├── icon-36x36.png
│ ├── icon-48x48.png
│ ├── icon-72x72.png
│ ├── icon-96x96.png
│ ├── images
│ ├── AngelHack-Boston.jpg
│ ├── CodeConferenceLA.jpg
│ ├── Family1.jpg
│ ├── Node-Summit.jpg
│ ├── RedHat-Summit.jpg
│ ├── TankFlip.gif
│ ├── Utah-Meetup.jpg
│ ├── bias1.jpg
│ ├── bias2.jpg
│ ├── bias3.jpg
│ ├── bias4.jpg
│ ├── bias5.jpg
│ ├── civic-x.png
│ ├── heroImage.jpg
│ ├── icons
│ │ ├── Custom
│ │ │ ├── bullseye.svg
│ │ │ ├── campus.svg
│ │ │ ├── career_services.svg
│ │ │ ├── chart.svg
│ │ │ ├── check-circle.svg
│ │ │ ├── equipment.svg
│ │ │ ├── high_fiving.svg
│ │ │ ├── housing.svg
│ │ │ ├── man_holding_key.svg
│ │ │ ├── mentorship.svg
│ │ │ ├── networked_people.svg
│ │ │ ├── online.svg
│ │ │ ├── people_meeting.svg
│ │ │ ├── scholarships.svg
│ │ │ └── x-circle.svg
│ │ ├── FontAwesome
│ │ │ ├── README.md
│ │ │ ├── angle-left-solid.svg
│ │ │ ├── angle-right-solid.svg
│ │ │ ├── ban-solid.svg
│ │ │ ├── book-solid.svg
│ │ │ ├── building_icon.svg
│ │ │ ├── bullhorn-solid.svg
│ │ │ ├── check-solid.svg
│ │ │ ├── chevron-up-solid.svg
│ │ │ ├── clock-regular.svg
│ │ │ ├── cloud_upload_icon.svg
│ │ │ ├── desktop-solid.svg
│ │ │ ├── devices-alt-solid.svg
│ │ │ ├── envelope-solid.svg
│ │ │ ├── exclamation-triangle-solid.svg
│ │ │ ├── external-link-square-alt-solid.svg
│ │ │ ├── flag-checkered-solid.svg
│ │ │ ├── hand-peace-regular.svg
│ │ │ ├── handshake-regular.svg
│ │ │ ├── home-solid.svg
│ │ │ ├── life-ring-regular.svg
│ │ │ ├── link-solid.svg
│ │ │ ├── map_marker_icon.svg
│ │ │ ├── medal-solid.svg
│ │ │ ├── road-solid.svg
│ │ │ ├── slack-hash.svg
│ │ │ ├── thumbs-down.svg
│ │ │ ├── thumbs-up.svg
│ │ │ ├── user-solid.svg
│ │ │ ├── user.svg
│ │ │ ├── users-solid.svg
│ │ │ └── utensils-solid.svg
│ │ ├── facebook_logo.svg
│ │ ├── gi-bill-approved.svg
│ │ ├── gi-bill-unavailable.svg
│ │ ├── github_logo.svg
│ │ ├── github_logo_circle.svg
│ │ ├── hamburger.svg
│ │ ├── instagram_logo.svg
│ │ ├── javascript_logo.svg
│ │ ├── linkedin_logo.svg
│ │ ├── linkedin_logo_circle.svg
│ │ ├── minus.svg
│ │ ├── operation_code_logo.svg
│ │ ├── pinterest_logo.svg
│ │ ├── plus.svg
│ │ ├── slack_logo.svg
│ │ ├── twitter_logo.svg
│ │ └── youtube_logo.svg
│ ├── logo.svg
│ ├── logrocket.svg
│ ├── moocs
│ │ ├── edx.jpg
│ │ ├── treehouse.jpg
│ │ └── udacity.jpg
│ ├── platinum-transparency.png
│ ├── sentry.svg
│ └── vercel.svg
│ ├── ms-icon-144x144.png
│ ├── ms-icon-150x150.png
│ ├── ms-icon-310x310.png
│ ├── ms-icon-70x70.png
│ └── operationcode_challenge
│ └── names.js
├── scripts
├── README.md
├── createComponent
│ ├── builders.js
│ └── createComponent.js
└── createPage
│ ├── builders.js
│ └── createPage.js
├── sentry.client.config.js
├── sentry.properties
├── sentry.server.config.js
├── styles
├── about.module.css
├── branding.module.css
├── challenge.module.css
├── chapter_leader.module.css
├── contact.module.css
├── donate.module.css
├── faq.module.css
├── get_involved.module.css
├── history.module.css
├── index.module.css
├── kc.module.css
├── password_reset.module.css
├── podcast.module.css
├── policy.module.css
├── press.module.css
├── profile.module.css
├── project_rebuild.module.css
├── services.module.css
├── slack_guide.module.css
├── sponsorship.module.css
├── team.module.css
└── thank_you.module.css
├── tailwind.config.js
├── test-utils
├── createComponentInstance.js
├── createShallowSnapshotTest.js
├── createSnapshotTest.js
├── flushPromises.js
├── identifiers.js
├── jest-next-image.js
├── mockGenerators
│ └── mockUser.ts
├── mocks
│ ├── apiMock.js
│ ├── existingUser.js
│ ├── jwtMock.js
│ ├── nextContextMock.js
│ ├── nextRouterMock.js
│ ├── passwordResetMock.js
│ ├── svgMock.js
│ └── testFileMock.js
├── transforms
│ └── file.js
└── wait.js
├── tsconfig.json
├── tsconfig.test.json
├── types
├── global.d.ts
└── index.d.ts
├── utils
└── types.ts
├── vitest.config.mts
├── vitest.setup.tsx
└── yarn.lock
/.babelrc.js:
--------------------------------------------------------------------------------
1 | const pathAliases = require('./pathAliases');
2 |
3 | module.exports = {
4 | env: {
5 | production: {
6 | presets: ['next/babel'],
7 | plugins: ['add-react-displayname'],
8 | },
9 | development: {
10 | presets: ['next/babel'],
11 | plugins: ['istanbul'],
12 | },
13 | test: {
14 | presets: [
15 | [
16 | 'next/babel',
17 | {
18 | 'preset-env': {
19 | modules: 'commonjs',
20 | },
21 | },
22 | ],
23 | '@babel/preset-typescript',
24 | ],
25 | },
26 | },
27 | plugins: [
28 | [
29 | 'module-resolver',
30 | {
31 | root: ['./'],
32 | alias: pathAliases,
33 | },
34 | ],
35 | 'macros',
36 | ],
37 | };
38 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not IE > 0
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package.json
3 | yarn.lock
4 | .babelrc
5 | **/.babelrc
6 | .next
7 | .github
8 | bin
9 | static
10 | cypress-coverage
11 | vitest-coverage
12 | .storybook-dist
13 | common/styles/themeMap.js
14 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | custom: operationcode.org/donate
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | # Bug Report
8 | ## What is the current behavior?
9 | e.g. After logging in, my whole page is upside down!
10 |
11 | ## What is the expected behavior?
12 | e.g. After logging in, my whole page should be right-side up.
13 |
14 | ## What steps did you take to get this behavior?
15 | e.g. I logged in...
16 |
17 | ## Additional Info
18 | ### Operating System
19 |
20 | ### Browser
21 |
22 | ### Is This A Third-Party Issue?
23 |
24 |
25 | ### Screenshots
26 |
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for Operation Code's website.
4 |
5 | ---
6 |
7 | # Feature request
8 |
9 |
10 | ## Proposed solution
11 |
12 |
13 | ## Potential alternative solutions
14 |
15 |
16 | ## Additional context
17 |
18 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Description of changes
2 |
3 |
4 | # Issue Resolved
5 |
6 |
7 | Fixes #NA
8 |
9 | ## Screenshots/GIFs
10 |
11 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: monthly
7 | time: "07:00"
8 | timezone: America/Los_Angeles
9 | open-pull-requests-limit: 0
10 | versioning-strategy: increase
11 |
12 |
--------------------------------------------------------------------------------
/.huskyrc:
--------------------------------------------------------------------------------
1 | {
2 | "hooks": {
3 | "pre-commit": "yarn lint-staged",
4 | "pre-push": "yarn test:changes"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix"],
3 | "*.css": ["prettier --write", "stylelint --fix"]
4 | }
5 |
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | queue_rules:
2 | - name: default
3 | queue_conditions:
4 | - author=dependabot[bot]
5 | - '#changes-requested-reviews-by=0'
6 | - 'status-success=Vercel – operation-code'
7 | - 'status-success=Vercel – storybook'
8 | - 'status-success=ci/circleci: lint'
9 | - 'status-success=ci/circleci: unit_tests'
10 | - 'status-success=cypress: all tests'
11 | - status-success=codeclimate/diff-coverage
12 | - status-success=codeclimate/total-coverage
13 | merge_conditions:
14 | - 'status-success=Vercel – operation-code'
15 | - 'status-success=Vercel – storybook'
16 | - 'status-success=ci/circleci: lint'
17 | - 'status-success=ci/circleci: unit_tests'
18 | - 'status-success=cypress: all tests'
19 | - status-success=codeclimate/diff-coverage
20 | - status-success=codeclimate/total-coverage
21 | merge_method: rebase
22 |
23 | pull_request_rules:
24 | - name: refactored queue action rule
25 | conditions: []
26 | actions:
27 | queue:
28 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package.json
3 | yarn.lock
4 | .babelrc
5 | **/.babelrc
6 | .next
7 | .github
8 | bin
9 | static
10 | cypress-coverage
11 | vitest-coverage
12 | .storybook-dist
13 | common/styles/themeMap.js
14 | **/*.snap
15 | **/*.mp4
16 | **/*.png
17 | **/*.jpeg
18 | **/*.jpg
19 | **/*.txt
20 | **/*.xml
21 | **/*.ico
22 |
--------------------------------------------------------------------------------
/.storybook/OCTheme.js:
--------------------------------------------------------------------------------
1 | import { create } from '@storybook/theming/create';
2 |
3 | export default create({
4 | base: 'dark',
5 | brandTitle: 'Operation Code',
6 | brandUrl: 'storybook.operationcode.org',
7 | });
8 |
--------------------------------------------------------------------------------
/.storybook/backgrounds.js:
--------------------------------------------------------------------------------
1 | import { brandColorsObject } from 'common/styles/styleExports';
2 | import { capitalizeFirstLetter } from 'common/utils/string-utils';
3 |
4 | const backgroundsPaletteArray = Object.keys(brandColorsObject).map(name => ({
5 | name: capitalizeFirstLetter(name),
6 | value: brandColorsObject[name],
7 | }));
8 |
9 | export default backgroundsPaletteArray;
10 |
--------------------------------------------------------------------------------
/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import { addons } from '@storybook/addons';
2 | import OCTheme from './OCTheme';
3 |
4 | addons.setConfig({
5 | theme: OCTheme,
6 | });
7 |
--------------------------------------------------------------------------------
/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import backgroundsPalleteArray from './backgrounds';
2 | import 'common/styles/globals.css';
3 | import * as viewports from '@storybook/addon-viewport';
4 |
5 | export const decorators = [
6 | Story => (
7 |
8 |
9 |
10 | ),
11 | ];
12 |
13 | const preview = {
14 | parameters: {
15 | actions: { argTypesRegex: '^on[A-Z].*' },
16 | backgrounds: {
17 | values: backgroundsPalleteArray,
18 | default: 'White',
19 | },
20 | viewport: {
21 | viewports: {
22 | ...viewports.MINIMAL_VIEWPORTS,
23 | ...viewports.INITIAL_VIEWPORTS,
24 | },
25 | },
26 | },
27 | };
28 |
29 | export default preview;
30 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard", "stylelint-prettier/recommended"],
3 | "plugins": ["stylelint-prettier"],
4 | "ignoreFiles": [
5 | "**/*.js",
6 | "coverage/**/*",
7 | "cypress-coverage/**/*",
8 | "vitest-coverage/**/*",
9 | "node_modules/**/*"
10 | ],
11 | "rules": {
12 | "prettier/prettier": true,
13 | "alpha-value-notation": null,
14 | "color-hex-length": "long",
15 | "custom-property-pattern": null,
16 | "hue-degree-notation": null,
17 | "keyframes-name-pattern": null,
18 | "color-function-notation": "legacy",
19 | "no-duplicate-selectors": true,
20 | "property-no-unknown": [true, { "ignoreProperties": ["composes", "composes-with"] }],
21 | "selector-id-pattern": null,
22 | "selector-class-pattern": null,
23 | "selector-pseudo-class-no-unknown": [true, { "ignorePseudoClasses": ["global"] }],
24 | "shorthand-property-no-redundant-values": null
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.vercelignore:
--------------------------------------------------------------------------------
1 | .next
2 | cypress
3 | internationalized-documentation
4 | node_modules
5 | scripts
6 | *.md
7 | src/**/*.test.js
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2014-2019 Operation Code
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/common/constants/api.ts:
--------------------------------------------------------------------------------
1 | import { patch, post } from 'common/utils/api-utils';
2 | import type { RegistrationFormValues } from 'components/Forms/RegistrationForm/RegistrationForm';
3 | import type { MilitaryStatusFormShape } from 'components/Forms/UpdateProfileForm/steps/MilitaryStatus';
4 | import type { ProfessionalDetailsFormShape } from 'components/Forms/UpdateProfileForm/steps/ProfessionalDetails';
5 |
6 | export const registerUser = (values: RegistrationFormValues) =>
7 | post('/api/registration/new', values);
8 |
9 | export const updateUser = (values: ProfessionalDetailsFormShape | MilitaryStatusFormShape) =>
10 | patch('/api/registration/update', values);
11 |
--------------------------------------------------------------------------------
/common/constants/descriptions.ts:
--------------------------------------------------------------------------------
1 | /** Provides lorem ipsum descriptions of length
2 | * short: 1 sentence,
3 | * medium: 3 sentences,
4 | * long: 5 sentences
5 | */
6 | export const descriptions = {
7 | short: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
8 |
9 | medium:
10 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut in semper justo, quis efficitur urna. Aliquam et pretium est, nec viverra risus. Curabitur facilisis est in nibh luctus condimentum.',
11 |
12 | long: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut in semper justo, quis efficitur urna. Aliquam et pretium est, nec viverra risus. Curabitur facilisis est in nibh luctus condimentum. Maecenas sed urna sed est dapibus venenatis. Quisque aliquet velit eu ligula pellentesque, non pretium massa blandit. Suspendisse vitae viverra ante, eget sodales arcu. Aliquam erat volutpat.',
13 | };
14 |
--------------------------------------------------------------------------------
/common/constants/messages.ts:
--------------------------------------------------------------------------------
1 | export const validationErrorMessages = {
2 | required: 'Required',
3 | email: 'Must be a valid email',
4 | emailsMatch: 'Emails must match',
5 | emailExists: 'This email has already been registered with an application.',
6 | codeOfConduct: 'You must agree to our Code of Conduct',
7 | slackGuidelines: 'You must agree to our Slack workspace guidelines',
8 | };
9 |
10 | export const networkErrorMessages = {
11 | serverDown: 'Something is wrong on our end. Please try again later.',
12 | };
13 |
--------------------------------------------------------------------------------
/common/constants/unitsOfTime.js:
--------------------------------------------------------------------------------
1 | // In seconds
2 | export const ONE_DAY = 86400;
3 | export const ONE_WEEK = 604800;
4 | export const TWO_WEEKS = 1209600;
5 |
--------------------------------------------------------------------------------
/common/constants/urls.js:
--------------------------------------------------------------------------------
1 | const s3hostName = 'operation-code-assets.s3.us-east-2.amazonaws.com';
2 | const s3 = `https://${s3hostName}/`;
3 | const leadershipCircleLink = 'https://secure.lglforms.com/form_engine/s/L428AQ2rrsFJQyy5Fbglvg';
4 | const codeOfConduct = `https://github.com/OperationCode/operationcode_docs/blob/master/community/code_of_conduct.md`;
5 | const slackGuidelines = `https://github.com/OperationCode/START_HERE/blob/master/community_guidelines.md`;
6 |
7 | module.exports = { s3hostName, s3, leadershipCircleLink, codeOfConduct, slackGuidelines };
8 |
--------------------------------------------------------------------------------
/common/styles/breakpoints.js:
--------------------------------------------------------------------------------
1 | import { breakpointsObject } from 'common/styles/styleExports';
2 | import { getBreakpoints } from 'common/utils/style-utils';
3 |
4 | const breakpoints = getBreakpoints(Object.values(breakpointsObject));
5 |
6 | export default breakpoints;
7 |
--------------------------------------------------------------------------------
/common/styles/media-queries.css:
--------------------------------------------------------------------------------
1 | @custom-media --extra-small-viewport (max-width: 350px);
2 |
3 | /* NOTE: Any changes made to these values should also be reflected in `../variables.css` under "Breakpoint" */
4 | @custom-media --small-viewport (max-width: 576px);
5 | @custom-media --medium-viewport (max-width: 768px);
6 | @custom-media --large-viewport (max-width: 992px);
7 | @custom-media --extra-large-viewport (max-width: 1200px);
8 | @custom-media --mobile-viewport (--large-viewport);
9 | @custom-media --desktop-viewport (min-width: 992px);
10 |
--------------------------------------------------------------------------------
/common/styles/styleExports.ts:
--------------------------------------------------------------------------------
1 | import { isHexColor } from 'common/utils/style-utils';
2 | import * as themeMap from './themeMap';
3 |
4 | const themeMapValues = Object.entries(themeMap);
5 |
6 | type StyleObjectType = Record;
7 |
8 | export const breakpointsObject: StyleObjectType = themeMapValues.reduce((object, [key, value]) => {
9 | const isBreakpoint = key.includes('ViewportWidth');
10 |
11 | if (isBreakpoint) {
12 | object[key] = value; // eslint-disable-line no-param-reassign
13 | }
14 |
15 | return object;
16 | }, {} as StyleObjectType);
17 |
18 | export const brandColorsObject: StyleObjectType = themeMapValues.reduce((object, [key, value]) => {
19 | if (isHexColor(value)) {
20 | object[key] = value; // eslint-disable-line no-param-reassign
21 | }
22 |
23 | return object;
24 | }, {} as StyleObjectType);
25 |
26 | export const fontsObject: StyleObjectType = themeMapValues.reduce((object, [key, value]) => {
27 | if (key.includes('Font')) {
28 | // Remove extra quotes from font name
29 | object[key] = value.replace(/^"(.*)"$/, '$1'); // eslint-disable-line no-param-reassign
30 | }
31 |
32 | return object;
33 | }, {} as StyleObjectType);
34 |
--------------------------------------------------------------------------------
/common/styles/themeMap.js:
--------------------------------------------------------------------------------
1 | export const primary = "#3ed6f0";
2 | export const secondary = "#252e3e";
3 | export const gray = "#e2e2e2";
4 | export const white = "#f7f7f7";
5 | export const burntOrange500 = "hsl(14, 55%, 45%, 1)";
6 | export const rgbValuesPrimary = "62, 214, 240";
7 | export const rgbValuesSecondary = "37, 46, 62";
8 | export const success = "hsl(132, 35%, 88%, 0.6)";
9 | export const successDeep = "hsl(132, 60%, 23%, 1)";
10 | export const warning = "hsl(46, 100%, 90%, 0.6)";
11 | export const warningDeep = "hsl(39, 80%, 31%, 1)";
12 | export const error = "hsl(355, 70%, 91%, 0.6)";
13 | export const errorDeep = "hsl(355, 63%, 34%, 1)";
14 | export const primaryFontFamily = "\"DIN Condensed Bold\"";
15 | export const secondaryFontFamily = "\"Encode Sans\"";
16 | export const smallViewportWidth = "576px";
17 | export const mediumViewportWidth = "768px";
18 | export const largeViewportWidth = "992px";
19 | export const extraLargeViewportWidth = "1200px";
20 | export const borderRadius = "3px";
21 | export const navBarHeight = "65px";
22 | export const navBarTopGutterHeight = "20px";
23 | export const typicalMaxWidth = "65ch";
24 |
--------------------------------------------------------------------------------
/common/utils/__tests__/api-utils.test.js:
--------------------------------------------------------------------------------
1 | import { getServerErrorMessage } from 'common/utils/api-utils';
2 | import { networkErrorMessages } from 'common/constants/messages';
3 |
4 | describe('API Utilities', () => {
5 | describe('getServerErrorMessage', () => {
6 | it('returns server down if not passed an object', () => {
7 | expect(getServerErrorMessage()).toStrictEqual(networkErrorMessages.serverDown);
8 | });
9 |
10 | it('returns server down if not passed an object with an expected shape', () => {
11 | expect(getServerErrorMessage({})).toStrictEqual(networkErrorMessages.serverDown);
12 | });
13 |
14 | it('returns the passed error message from an expected server error object', () => {
15 | const fakeTestMessage = 'Yoooooo!';
16 | expect(
17 | getServerErrorMessage({ response: { data: { error: fakeTestMessage } } }),
18 | ).toStrictEqual(fakeTestMessage);
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/common/utils/__tests__/next-utils.test.js:
--------------------------------------------------------------------------------
1 | import { getPlaceholder, getShimmerSVG } from '../next-utils';
2 |
3 | describe('Next.js Utilities', () => {
4 | describe('getPlaceholder', () => {
5 | it('defines a valid base64 string', () => {
6 | expect(getPlaceholder(200, 200)).toMatchSnapshot();
7 | });
8 | });
9 |
10 | describe('getShimmerSVG', () => {
11 | it('renders a valid SVG', () => {
12 | expect(getShimmerSVG(200, 200)).toMatchSnapshot();
13 | });
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/common/utils/__tests__/string-utils.test.js:
--------------------------------------------------------------------------------
1 | import { capitalizeFirstLetter, coerceEmptyStringToUndefined } from '../string-utils';
2 |
3 | describe('String Utilities', () => {
4 | describe('capitalizeFirstLetter', () => {
5 | it('should capitalize the first letter of a string with a lower-case first letter', () => {
6 | expect(capitalizeFirstLetter('testString')).toStrictEqual('TestString');
7 | });
8 |
9 | it('should return the same string when passed a string with a capitalized first letter', () => {
10 | expect(capitalizeFirstLetter('TestString')).toStrictEqual('TestString');
11 | });
12 |
13 | it('should return an empty string when passed undefined', () => {
14 | expect(capitalizeFirstLetter(undefined)).toStrictEqual('');
15 | });
16 | });
17 |
18 | describe('coerceEmptyStringToUndefined', () => {
19 | it('should return undefined when passed string is empty', () => {
20 | expect(coerceEmptyStringToUndefined('')).toBeUndefined();
21 | });
22 |
23 | it('should return the string itself if it is not empty', () => {
24 | const theString = 'yo';
25 | expect(coerceEmptyStringToUndefined(theString)).toStrictEqual(theString);
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/common/utils/array-utils.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 |
3 | /** used to identify if an array is filled with real values */
4 | export function isFilledArray(potentialArray: unknown): potentialArray is any[] {
5 | return Boolean(Array.isArray(potentialArray) && potentialArray.length > 0 && potentialArray[0]);
6 | }
7 |
8 | export function mapStringsToSelectOptions(strings: string[]) {
9 | return strings.map(string => ({ label: string, value: string }));
10 | }
11 |
--------------------------------------------------------------------------------
/common/utils/cookie-utils.ts:
--------------------------------------------------------------------------------
1 | import cookie from 'js-cookie';
2 | import jwtDecode from 'jwt-decode';
3 |
4 | export const setAuthCookies = ({ token }: { token: string }) => {
5 | cookie.set('token', token);
6 | };
7 |
8 | export const removeAuthCookies = () => {
9 | cookie.remove('token');
10 | };
11 |
12 | export const setAuthorizationHeader = (token = getAuthToken()) => {
13 | if (hasValidAuthToken(token)) {
14 | return { Authorization: `Bearer ${token}` };
15 | }
16 |
17 | return {};
18 | };
19 |
20 | export const getAuthToken = () => {
21 | return cookie.get('token');
22 | };
23 |
24 | export const hasValidAuthToken = (token = cookie.get('token')) => {
25 | if (token === undefined) {
26 | return false;
27 | }
28 |
29 | const jwt = jwtDecode<{ exp: number }>(token);
30 | const currentTime = new Date().getTime() / 1000;
31 |
32 | // Valid if jwt expiry is in the future
33 | return currentTime < jwt.exp;
34 | };
35 |
--------------------------------------------------------------------------------
/common/utils/prop-utils.js:
--------------------------------------------------------------------------------
1 | // General utilities for dealing with component prop types
2 | import pickBy from 'lodash/pickBy';
3 |
4 | export function getPropertiesStartingWith(string, props) {
5 | return pickBy(props, (value, key) => key.startsWith(string));
6 | }
7 |
8 | export function getDataAttributes(props) {
9 | return getPropertiesStartingWith('data-', props);
10 | }
11 |
12 | export function getAriaAttributes(props) {
13 | return getPropertiesStartingWith('aria-', props);
14 | }
15 |
--------------------------------------------------------------------------------
/common/utils/string-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Functions are being exported using module.exports, so we can use the methods in
3 | * node.js and ES6 client side.
4 | * */
5 |
6 | /**
7 | * @description Capitalize the first letter in a string
8 | *
9 | * @export
10 | * @param {string} someString
11 | * @returns {string} Returns string with the first character capitalized
12 | */
13 | function capitalizeFirstLetter(someString = '') {
14 | const stringCopy = [...someString].join('');
15 |
16 | return stringCopy.charAt(0).toUpperCase() + stringCopy.slice(1);
17 | }
18 |
19 | /**
20 | * @description Return undefined if passed string is empty, otherwise pass the string thru
21 | *
22 | * @export
23 | * @param {string} someString
24 | * @returns {(undefined|string)} undefined or string
25 | */
26 | function coerceEmptyStringToUndefined(someString) {
27 | if (someString === '') {
28 | return undefined;
29 | }
30 |
31 | return someString;
32 | }
33 |
34 | module.exports = {
35 | capitalizeFirstLetter,
36 | coerceEmptyStringToUndefined,
37 | };
38 |
--------------------------------------------------------------------------------
/components/Accordion/__stories__/Accordion.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import Accordion from '../Accordion';
3 |
4 | type AccordionStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'Accordion',
8 | component: Accordion,
9 | args: {
10 | accessibilityId: '1',
11 | content: {
12 | headingChildren: Can be JSX ,
13 | bodyChildren: Can also be JSX
,
14 | },
15 | },
16 | };
17 |
18 | export default meta;
19 |
20 | export const Default: AccordionStoryType = {
21 | render: args => ,
22 | };
23 |
--------------------------------------------------------------------------------
/components/Alert/Alert.module.css:
--------------------------------------------------------------------------------
1 | .Alert {
2 | border: 1px solid rgba(0, 0, 0, 0);
3 | border-radius: 3px;
4 | box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.12);
5 | font-size: 0.825rem;
6 | padding: 0.75rem;
7 | }
8 |
9 | .error {
10 | background-color: var(--error);
11 | border-color: var(--errorDeep);
12 | color: var(--errorDeep);
13 | }
14 |
15 | .success {
16 | background-color: var(--success);
17 | border-color: var(--successDeep);
18 | color: var(--successDeep);
19 | }
20 |
21 | .warning {
22 | background-color: var(--warning);
23 | border-color: var(--warningDeep);
24 | color: var(--warningDeep);
25 | }
26 |
27 | .alertCloseButton {
28 | border: none;
29 | color: inherit;
30 | background-color: rgba(0, 0, 0, 0);
31 | font-size: 1.5rem;
32 | padding: 0 0.5rem;
33 | margin: 0 0.25rem;
34 | vertical-align: middle;
35 | transform: scale(1);
36 | transition:
37 | 0.2s color linear,
38 | 0.2s transform linear;
39 | }
40 |
41 | .alertCloseButton:hover {
42 | color: black;
43 | cursor: pointer;
44 | transform: scale(1.1);
45 | transition:
46 | 0.2s color linear,
47 | 0.2s transform linear;
48 | }
49 |
--------------------------------------------------------------------------------
/components/Alert/__stories__/Alert.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import Alert from '../Alert';
3 |
4 | type AlertStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'Alert',
8 | component: Alert,
9 | };
10 |
11 | export default meta;
12 |
13 | const AlertStoryTemplate: AlertStoryType = {
14 | render: args => ,
15 | };
16 |
17 | export const ErrorAlert: AlertStoryType = {
18 | ...AlertStoryTemplate,
19 | args: {
20 | children: 'Error Alert JSX or Text',
21 | type: 'error',
22 | },
23 | };
24 |
25 | export const SuccessAlert: AlertStoryType = {
26 | ...AlertStoryTemplate,
27 | args: {
28 | children: 'Success Alert JSX or Text',
29 | type: 'success',
30 | },
31 | };
32 |
33 | export const WarningAlert: AlertStoryType = {
34 | ...AlertStoryTemplate,
35 | args: {
36 | children: 'Warning Alert JSX or Text',
37 | type: 'warning',
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/components/Alert/__tests__/__snapshots__/Alert.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Alert > should render error alert with required props 1`] = `
4 |
9 |
10 | Error Alert JSX or Text
11 |
12 |
13 | `;
14 |
--------------------------------------------------------------------------------
/components/Branding/ColorSection/__tests__/ColorSection.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import ColorSection from '../ColorSection';
4 |
5 | describe('ColorSection', () => {
6 | it('should render with required props', () => {
7 | createShallowSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/components/Branding/ColorSection/__tests__/__snapshots__/ColorSection.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`ColorSection > should render with required props 1`] = `
4 |
7 |
11 |
12 | Primary
13 |
14 |
18 |
,
19 |
20 |
21 | Secondary
22 |
23 |
27 | ,
28 | ]
29 | }
30 | hasTitleUnderline={true}
31 | theme="white"
32 | title="Colors"
33 | />
34 | ,
41 | ,
45 | ]
46 | }
47 | theme="white"
48 | title="Other On-Brand Colors"
49 | />
50 |
51 | `;
52 |
--------------------------------------------------------------------------------
/components/Branding/FontSection/__tests__/FontSection.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import FontSection from '../FontSection';
4 |
5 | describe('FontSection', () => {
6 | it('should render with required props', () => {
7 | createShallowSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/components/Branding/LogoSection/LogoSection.module.css:
--------------------------------------------------------------------------------
1 | .logoSizeList {
2 | margin: 0;
3 | padding: 0;
4 | list-style: none;
5 | width: 100%;
6 | }
7 |
8 | .logoSizeList img {
9 | height: auto;
10 | max-width: 100%;
11 | }
12 |
13 | .logoSizeList ul {
14 | margin-bottom: 1.5rem;
15 | }
16 |
17 | .logoSizeListItem {
18 | margin: 0;
19 | padding: 0;
20 | width: 100%;
21 | }
22 |
23 | .logoTypeListItem {
24 | margin-top: 1rem;
25 | }
26 |
27 | .badgeList {
28 | display: flex;
29 | justify-content: space-around;
30 | align-items: center;
31 | flex-wrap: wrap;
32 | list-style: none;
33 | margin: 0;
34 | padding: 0;
35 | padding-bottom: 1rem;
36 | border-bottom: 1px solid #bbbbbb;
37 | }
38 |
39 | .largeLogos .badgeList {
40 | flex-flow: column nowrap;
41 | }
42 |
43 | .badgeList span {
44 | font-size: 1.5rem;
45 | font-weight: bold;
46 | }
47 |
--------------------------------------------------------------------------------
/components/Branding/LogoSection/__tests__/LogoSection.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import LogoSection from '../LogoSection';
4 |
5 | describe('LogoSection', () => {
6 | it('should render with required props', () => {
7 | createShallowSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/components/Branding/Swatch/Swatch.tsx:
--------------------------------------------------------------------------------
1 | import { string } from 'prop-types';
2 | import ScreenReaderOnly from 'components/ScreenReaderOnly/ScreenReaderOnly';
3 |
4 | Swatch.propTypes = {
5 | colorName: string.isRequired,
6 | hexCode: string.isRequired,
7 | };
8 |
9 | export interface Swatch {
10 | colorName: string;
11 | hexCode: string;
12 | }
13 |
14 | function Swatch({ colorName, hexCode }: Swatch) {
15 | return (
16 |
17 | {`A block of the color ${colorName}`}
18 |
19 |
20 |
21 |
22 | {colorName.toUpperCase()}
23 | {hexCode.toUpperCase()}
24 |
25 |
26 | );
27 | }
28 |
29 | export default Swatch;
30 |
--------------------------------------------------------------------------------
/components/Branding/Swatch/__tests__/Swatch.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import Swatch from '../Swatch';
4 |
5 | describe('Swatch', () => {
6 | it('should render with required props', () => {
7 | createShallowSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/components/Branding/Swatch/__tests__/__snapshots__/Swatch.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Swatch > should render with required props 1`] = `
4 |
7 |
8 | A block of the color Blue
9 |
10 |
18 |
19 |
20 | BLUE
21 |
22 | #0000FF
23 |
24 |
25 | `;
26 |
--------------------------------------------------------------------------------
/components/Buttons/Button/__stories__/Button.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import Button from '../Button';
3 |
4 | type ButtonStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'Buttons/Button',
8 | component: Button,
9 | parameters: {
10 | actions: {
11 | handles: ['click'],
12 | },
13 | },
14 | };
15 |
16 | export default meta;
17 |
18 | export const Default: ButtonStoryType = {
19 | render: args => ,
20 | args: {
21 | children: 'Button',
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/components/Buttons/Button/__tests__/__snapshots__/Button.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Button > should render with many props assigned 1`] = `
4 |
13 | Test
14 |
15 | `;
16 |
17 | exports[`Button > should render with required props 1`] = `
18 |
26 | Test
27 |
28 | `;
29 |
--------------------------------------------------------------------------------
/components/Buttons/CloseButton/CloseButton.module.css:
--------------------------------------------------------------------------------
1 | .CloseButton {
2 | background-color: rgba(0, 0, 0, 0);
3 | position: absolute;
4 | top: 1rem;
5 | right: 1rem;
6 | border: none;
7 | margin: 0.5rem;
8 | z-index: 1;
9 | }
10 |
11 | .CloseButton:disabled {
12 | cursor: not-allowed;
13 | opacity: 0.5;
14 | }
15 |
16 | .CloseButton:hover:not(:disabled) {
17 | cursor: pointer;
18 | }
19 |
20 | .icon {
21 | padding: 0.25rem;
22 | width: 20px;
23 | transform: rotate(45deg);
24 | transition: 0.2s linear transform;
25 | }
26 |
27 | .primary {
28 | fill: var(--primary);
29 | }
30 |
31 | .secondary {
32 | fill: var(--secondary);
33 | }
34 |
35 | .white {
36 | fill: var(--white);
37 | }
38 |
39 | .CloseButton:hover:not(:disabled) .icon {
40 | transform: rotate(135deg);
41 | transition: 0.2s linear transform;
42 | }
43 |
--------------------------------------------------------------------------------
/components/Buttons/CloseButton/CloseButton.tsx:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames';
2 | import { CLOSE_BUTTON } from 'common/constants/testIDs';
3 | import ScreenReaderOnly from 'components/ScreenReaderOnly/ScreenReaderOnly';
4 | import PlusIcon from 'static/images/icons/plus.svg';
5 | import styles from './CloseButton.module.css';
6 |
7 | export type CloseButtonProps = {
8 | /**
9 | * Sets the button color theme.
10 | */
11 | theme?: 'primary' | 'secondary' | 'white';
12 | } & React.ButtonHTMLAttributes;
13 |
14 | export default function CloseButton({
15 | disabled = false,
16 | onClick,
17 | theme = 'primary',
18 | }: CloseButtonProps) {
19 | return (
20 |
27 | Close
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/components/Buttons/CloseButton/__stories__/CloseButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import CloseButton from '../CloseButton';
3 |
4 | type CloseButtonStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'Buttons/Closebutton',
8 | component: CloseButton,
9 | };
10 |
11 | export default meta;
12 |
13 | export const Default: CloseButtonStoryType = {
14 | render: args => ,
15 | args: {
16 | theme: 'primary',
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/components/Buttons/CloseButton/__tests__/CloseButton.test.tsx:
--------------------------------------------------------------------------------
1 | import { fireEvent, render } from '@testing-library/react';
2 | import { CLOSE_BUTTON } from 'common/constants/testIDs';
3 | import createSnapshotTest from 'test-utils/createSnapshotTest';
4 | import CloseButton from '../CloseButton';
5 |
6 | describe('CloseButton', () => {
7 | it('should render with just required props passed', () => {
8 | createSnapshotTest( );
9 | });
10 |
11 | it('should not be clickable when disabled', () => {
12 | const onClickMock = vi.fn();
13 | const { queryByTestId } = render( );
14 |
15 | fireEvent.click(queryByTestId(CLOSE_BUTTON)!);
16 |
17 | expect(onClickMock).toHaveBeenCalledTimes(0);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/components/Buttons/CloseButton/__tests__/__snapshots__/CloseButton.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`CloseButton > should render with just required props passed 1`] = `
4 |
11 |
15 | Close
16 |
17 | Plus Symbol ",
22 | }
23 | }
24 | viewBox="0 0 500 500"
25 | />
26 |
27 | `;
28 |
--------------------------------------------------------------------------------
/components/Buttons/LinkButton/__stories__/LinkButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import LinkButton from '../LinkButton';
3 |
4 | type LinkButtonStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'Buttons/LinkButton',
8 | component: LinkButton,
9 | };
10 |
11 | export default meta;
12 |
13 | export const Default: LinkButtonStoryType = {
14 | render: args => ,
15 | args: {
16 | children: 'Link',
17 | href: '#',
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/components/Buttons/LinkButton/__tests__/__snapshots__/LinkButton.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`LinkButton > should render with many props assigned 1`] = `
4 |
12 | Test
13 |
17 | Opens in new window
18 |
19 |
20 | `;
21 |
22 | exports[`LinkButton > should render with required props 1`] = `
23 |
30 | Test
31 |
32 | `;
33 |
--------------------------------------------------------------------------------
/components/Cards/Card/Card.tsx:
--------------------------------------------------------------------------------
1 | import { twMerge } from 'tailwind-merge';
2 | import { getDataAttributes } from 'common/utils/prop-utils';
3 |
4 | export interface CardPropsType {
5 | children: React.ReactNode;
6 | className?: string;
7 | hasAnimationOnHover?: boolean;
8 | }
9 |
10 | function Card({ children, className, hasAnimationOnHover, ...props }: CardPropsType) {
11 | const customDataAttributes = getDataAttributes(props);
12 |
13 | return (
14 |
23 | {children}
24 |
25 | );
26 | }
27 |
28 | export default Card;
29 |
--------------------------------------------------------------------------------
/components/Cards/Card/__stories__/Card.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { descriptions } from 'common/constants/descriptions';
3 | import Card from '../Card';
4 |
5 | type CardStoryType = StoryObj;
6 |
7 | const meta: Meta = {
8 | title: 'Cards/Card',
9 | component: Card,
10 | args: {
11 | children: descriptions.medium,
12 | },
13 | };
14 |
15 | export default meta;
16 |
17 | export const Default: CardStoryType = {
18 | render: args => ,
19 | };
20 |
--------------------------------------------------------------------------------
/components/Cards/Card/__tests__/Card.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import Card from '../Card';
3 |
4 | describe('Card', () => {
5 | it('should render with required props', () => {
6 | createSnapshotTest(Test );
7 | });
8 |
9 | it('should render with many props assigned', () => {
10 | createSnapshotTest(
11 |
12 | Test
13 | ,
14 | );
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/components/Cards/Card/__tests__/__snapshots__/Card.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Card > should render with many props assigned 1`] = `
4 |
7 | Test
8 |
9 | `;
10 |
11 | exports[`Card > should render with required props 1`] = `
12 |
15 | Test
16 |
17 | `;
18 |
--------------------------------------------------------------------------------
/components/Cards/FlatCard/__stories__/FlatCard.stories.js:
--------------------------------------------------------------------------------
1 | import { descriptions } from 'common/constants/descriptions';
2 | import FlatCard from '../FlatCard';
3 |
4 | export default {
5 | component: FlatCard,
6 | title: 'Cards/FlatCard',
7 | };
8 |
9 | const Template = arguments_ => ;
10 |
11 | // Default FlatCard supplied with only required args
12 | export const Default = Template.bind({});
13 | Default.args = {
14 | children: descriptions.long,
15 | image: {
16 | source: '',
17 | alt: '',
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/components/Cards/ImageCard/__stories__/ImageCard.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { descriptions } from 'common/constants/descriptions';
3 | import { s3 } from 'common/constants/urls';
4 | import ImageCard from '../ImageCard';
5 |
6 | type ImageCardPropsType = StoryObj;
7 |
8 | const meta: Meta = {
9 | title: 'Cards/ImageCard',
10 | component: ImageCard,
11 | args: {
12 | alt: 'Image Card',
13 | children: descriptions.long,
14 | imageSource: `${s3}redesign/heroBanners/about.jpg`,
15 | },
16 | };
17 |
18 | export default meta;
19 |
20 | export const Default: ImageCardPropsType = {
21 | render: args => ,
22 | };
23 |
--------------------------------------------------------------------------------
/components/Cards/ImageCard/__tests__/ImageCard.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 | import ImageCard from '../ImageCard';
3 |
4 | describe('ImageCard', () => {
5 | it('should render with required props', () => {
6 | createShallowSnapshotTest(
7 |
8 | Testing!
9 | ,
10 | );
11 | });
12 |
13 | it('should render with many props assigned', () => {
14 | createShallowSnapshotTest(
15 |
21 | Testing!
22 | ,
23 | );
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/components/Cards/ValueCard/ValueCard.tsx:
--------------------------------------------------------------------------------
1 | import Card from 'components/Cards/Card/Card';
2 |
3 | export interface ValueCardPropsType {
4 | name: string;
5 | description: string;
6 | }
7 |
8 | function ValueCard({ description, name }: ValueCardPropsType) {
9 | return (
10 |
14 | {name}
15 | {description}
16 |
17 | );
18 | }
19 |
20 | export default ValueCard;
21 |
--------------------------------------------------------------------------------
/components/Cards/ValueCard/__stories__/ValueCard.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { descriptions } from 'common/constants/descriptions';
3 | import ValueCard from '../ValueCard';
4 |
5 | type ValueCardStoryType = StoryObj;
6 |
7 | const meta: Meta = {
8 | title: 'Cards/ValueCard',
9 | component: ValueCard,
10 | args: {
11 | name: 'Card name',
12 | description: descriptions.short,
13 | },
14 | };
15 |
16 | export default meta;
17 |
18 | export const Default: ValueCardStoryType = {
19 | render: args => ,
20 | };
21 |
22 | export const WithLongDescription: ValueCardStoryType = {
23 | ...Default,
24 | args: {
25 | description: descriptions.long,
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/components/Cards/ValueCard/__tests__/ValueCard.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import ValueCard from '../ValueCard';
3 |
4 | describe('ValueCard', () => {
5 | it('should render with required props', () => {
6 | createSnapshotTest(
7 | ,
11 | );
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/components/Cards/ValueCard/__tests__/__snapshots__/ValueCard.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`ValueCard > should render with required props 1`] = `
4 |
7 |
10 | Testing
11 |
12 |
13 | Testing is good for the soul and scientifically proven to make puppies happy.
14 |
15 |
16 | `;
17 |
--------------------------------------------------------------------------------
/components/Container/__stories__/Container.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { descriptions } from 'common/constants/descriptions';
3 | import Container from '../Container';
4 |
5 | type ContainerStoryType = StoryObj;
6 |
7 | const meta: Meta = {
8 | title: 'Container',
9 | component: Container,
10 | args: {
11 | children: descriptions.long,
12 | },
13 | };
14 |
15 | export default meta;
16 |
17 | export const Default: ContainerStoryType = {
18 | render: args => ,
19 | };
20 |
--------------------------------------------------------------------------------
/components/Container/__tests__/Container.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 |
3 | import { s3 } from 'common/constants/urls';
4 | import Container from '../Container';
5 |
6 | describe('Container', () => {
7 | const testImageUrl = `${s3}heroBanner/stock_family-2.jpg`;
8 |
9 | it('should render with many props assigned', () => {
10 | createSnapshotTest(
11 |
17 | Test Children
18 | ,
19 | );
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/components/Container/__tests__/__snapshots__/Container.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Container > should render with many props assigned 1`] = `
4 |
12 |
15 | Test Children
16 |
17 |
18 | `;
19 |
--------------------------------------------------------------------------------
/components/Content/__stories__/Content.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import Content from '../Content';
3 |
4 | type ContentStoryType = StoryObj;
5 |
6 | const multiColumnArray = [
7 | ,
10 | ,
13 | ,
16 | ];
17 |
18 | const meta: Meta = {
19 | title: 'Content',
20 | component: Content,
21 | args: {
22 | columns: multiColumnArray.slice(0, 1),
23 | title: 'One column content',
24 | },
25 | };
26 |
27 | export default meta;
28 |
29 | /**
30 | * Default Content supplied with only one column
31 | */
32 | export const Default: ContentStoryType = {
33 | render: args => ,
34 | };
35 |
36 | /**
37 | * Content supplied with multiple columns
38 | */
39 | export const MultipleColumns: ContentStoryType = {
40 | ...Default,
41 | args: {
42 | columns: multiColumnArray,
43 | title: 'Multiple column content',
44 | },
45 | };
46 |
--------------------------------------------------------------------------------
/components/Content/__tests__/Content.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 |
3 | import Content from '../Content';
4 |
5 | // eslint-disable-next-line unicorn/prevent-abbreviations
6 | const requiredProps = {
7 | columns: [
8 |
9 |
some test content
10 |
,
11 | ],
12 | id: undefined,
13 | };
14 |
15 | describe('Content', () => {
16 | it('should render with required props', () => {
17 | createSnapshotTest( );
18 | });
19 |
20 | it('should render with many props assigned', () => {
21 | createSnapshotTest(
22 |
25 | some multi-column test content
26 | ,
27 | OH YEAH!! ,
28 | ,
29 | ]}
30 | hasTitleUnderline
31 | id="test-id"
32 | isFullViewportHeight
33 | title="Testing!"
34 | />,
35 | );
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/components/Drawer/Drawer.tsx:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames';
2 |
3 | export interface DrawerPropsType {
4 | /**
5 | * Content to be rendered in the Drawer.
6 | */
7 | children: React.ReactNode;
8 | /**
9 | * Applies class names to the container element.
10 | */
11 | className?: string;
12 | /**
13 | * Sets if the content is visible.
14 | * @default false
15 | */
16 | isVisible?: boolean;
17 | }
18 |
19 | function Drawer({ children, className, isVisible = false }: DrawerPropsType) {
20 | return (
21 |
33 | );
34 | }
35 |
36 | export default Drawer;
37 |
--------------------------------------------------------------------------------
/components/Drawer/__stories__/Drawer.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import Drawer from '../Drawer';
3 |
4 | type DrawerStorytype = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'Drawer',
8 | component: Drawer,
9 | parameters: {
10 | viewport: {
11 | defaultViewport: 'tablet',
12 | },
13 | },
14 | };
15 |
16 | export default meta;
17 |
18 | /**
19 | * Default Drawer supplied with only required args.
20 | */
21 | export const Default: DrawerStorytype = {
22 | render: args => ,
23 | args: {
24 | children: 'Drawer content will only display on display size of Tablet or smaller',
25 | isVisible: true,
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/components/Drawer/__tests__/Drawer.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 |
3 | import Drawer from '../Drawer';
4 |
5 | describe('Drawer', () => {
6 | it('should render with required props', () => {
7 | createSnapshotTest(Test );
8 | });
9 |
10 | it('should render with many props assigned', () => {
11 | createSnapshotTest(
12 |
13 | Test
14 | ,
15 | );
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/components/Drawer/__tests__/__snapshots__/Drawer.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Drawer > should render with many props assigned 1`] = `
4 |
13 | `;
14 |
15 | exports[`Drawer > should render with required props 1`] = `
16 |
25 | `;
26 |
--------------------------------------------------------------------------------
/components/ErrorDisplay/ErrorDisplay.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'components/head';
2 |
3 | export interface ErrorDisplayPropsType {
4 | /**
5 | * Displasy a status code instead of 'Error'.
6 | */
7 | statusCode?: number;
8 | }
9 |
10 | function ErrorDisplay({ statusCode }: ErrorDisplayPropsType) {
11 | return (
12 | <>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
{statusCode || 'Oh no'}!
20 |
21 | We're so ashamed. You definitely weren't supposed to see this...
22 |
23 |
24 |
25 | >
26 | );
27 | }
28 |
29 | export default ErrorDisplay;
30 |
--------------------------------------------------------------------------------
/components/ErrorDisplay/__tests__/ErrorDisplay.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import createSnapshotTest from 'test-utils/createSnapshotTest';
3 | import ErrorDisplay from '../ErrorDisplay';
4 |
5 | describe('ErrorDisplay', () => {
6 | it('should render with just required props', () => {
7 | createSnapshotTest( );
8 | });
9 |
10 | it('should render h1, even when no statusCode is passed', () => {
11 | const { container } = render( );
12 | expect(container.querySelector('h1')!.textContent).toStrictEqual('Oh no!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/components/ErrorDisplay/__tests__/__snapshots__/ErrorDisplay.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`ErrorDisplay > should render with just required props 1`] = `
4 |
7 |
10 |
13 | 404
14 | !
15 |
16 |
19 | We're so ashamed. You definitely weren't supposed to see this...
20 |
21 |
22 |
23 | `;
24 |
--------------------------------------------------------------------------------
/components/FeaturedJobItem/__stories__/FeaturedJobItem.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { descriptions } from 'common/constants/descriptions';
3 | import FeaturedJobItem from '../FeaturedJobItem';
4 |
5 | type FeaturedJobItemStoryType = StoryObj;
6 |
7 | const meta: Meta = {
8 | title: 'FeaturedJobItem',
9 | component: FeaturedJobItem,
10 | };
11 |
12 | export default meta;
13 |
14 | /**
15 | * Default FeaturedJobItem supplied with only required args.
16 | */
17 | export const Default: FeaturedJobItemStoryType = {
18 | render: args => ,
19 | args: {
20 | title: 'Job Title',
21 | source: 'Company Name',
22 | sourceUrl: '#',
23 | description: descriptions.long,
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/components/FeaturedJobItem/__tests__/FeaturedJobItem.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import FeaturedJobItem from '../FeaturedJobItem';
3 |
4 | describe('FeaturedJobItem', () => {
5 | it('should render with required props', () => {
6 | createSnapshotTest(
7 | ,
13 | );
14 | });
15 |
16 | it('should render with many props assigned', () => {
17 | createSnapshotTest(
18 | ,
28 | );
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/components/FeaturedJobItem/featuredJobs.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Software Engineer",
4 | "source": "Lockheed Martin",
5 | "sourceUrl": "https://www.lockheedmartinjobs.com/job/boulder/software-engineer/694/8061488",
6 | "city": "Boulder",
7 | "state": "Colorado",
8 | "country": "United States",
9 | "description": "We are seeking a Software Engineer to join our dynamic and growing team! The selected candidate will design, develop, and test software solutions across our contracted efforts, supporting multiple baselines in defect resolution as well as enhancements, special projects, new business initiatives, and modernization efforts. The successful candidate will be a passionate coder, someone who loves a challenge and always rises to the occasion to ensure success. They embrace modern development practices and insist that others do as well. Only a Secret Clearance is required to start, and there are multiple openings for each position.",
10 | "status": "active",
11 | "remote": false,
12 | "tags": ["Java", "C++"]
13 | }
14 | ]
15 |
--------------------------------------------------------------------------------
/components/Footer/__tests__/Footer.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import Footer from '../Footer';
4 |
5 | describe('Footer', () => {
6 | it('should render with no props passed', () => {
7 | vi.useFakeTimers().setSystemTime(new Date('2023-03-03'));
8 | createShallowSnapshotTest();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/components/Form/Checkbox/Checkbox.module.css:
--------------------------------------------------------------------------------
1 | .field {
2 | margin: 1rem;
3 | position: relative;
4 | }
5 |
6 | .Checkbox {
7 | border: 1px solid rgba(var(--rgbValuesSecondary), 0.5);
8 | border-radius: 3px;
9 | font-size: 1.125rem;
10 | padding: 0.5rem;
11 | transform: scale(1.5);
12 | margin-right: 0.75rem;
13 | }
14 |
15 | .Checkbox:disabled {
16 | opacity: 0.6;
17 | }
18 |
19 | .Checkbox:disabled:hover {
20 | cursor: not-allowed;
21 | }
22 |
23 | .errorMessage {
24 | margin-top: 0.5rem;
25 | flex: 1;
26 | text-align: center;
27 | }
28 |
--------------------------------------------------------------------------------
/components/Form/Form.tsx:
--------------------------------------------------------------------------------
1 | import { connect } from 'formik';
2 | import type { DetailedHTMLProps, FormHTMLAttributes, FunctionComponent } from 'react';
3 |
4 | const FormikConnectedForm = connect(({ formik: { handleReset, handleSubmit }, ...props }) => (
5 |
6 | )) as FunctionComponent, HTMLFormElement>>;
7 |
8 | FormikConnectedForm.displayName = 'FormikConnectedForm';
9 |
10 | export default FormikConnectedForm;
11 |
--------------------------------------------------------------------------------
/components/Form/Input/__tests__/__snapshots__/Input.test.js.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Input > should render with required props 1`] = `
4 |
8 |
13 | Some Input:
14 |
15 |
19 |
28 |
29 |
30 | `;
31 |
--------------------------------------------------------------------------------
/components/Form/Input/__tests__/__snapshots__/Input.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Input > should render with required props 1`] = `
4 |
8 |
13 | Some Input:
14 |
15 |
19 |
29 |
30 |
31 | `;
32 |
--------------------------------------------------------------------------------
/components/Form/Label/Label.module.css:
--------------------------------------------------------------------------------
1 | .Label {
2 | color: rgba(var(--rgbValuesSecondary), 0.75);
3 | font-size: 0.825rem;
4 | }
5 |
--------------------------------------------------------------------------------
/components/Form/Label/__stories__/Label.stories.js:
--------------------------------------------------------------------------------
1 | import Label from '../Label';
2 |
3 | export default {
4 | component: Label,
5 | title: 'Form/Label',
6 | };
7 |
8 | const pairedInputName = 'pairedInputName';
9 |
10 | const Template = arguments_ => {
11 | return (
12 | <>
13 | NOTE: This component is always paired with an input
14 |
15 |
16 |
17 | >
18 | );
19 | };
20 |
21 | // Default Label supplied with only required args
22 | export const Default = Template.bind({});
23 | Default.args = {
24 | children: 'Label',
25 | for: pairedInputName,
26 | };
27 |
--------------------------------------------------------------------------------
/components/Form/Label/__tests__/Label.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/label-has-associated-control */
2 | import { render } from '@testing-library/react';
3 | import createSnapshotTest from 'test-utils/createSnapshotTest';
4 | import { LABEL, SCREEN_READER_ONLY } from 'common/constants/testIDs';
5 |
6 | import Label from '../Label';
7 |
8 | describe('Label', () => {
9 | it('should render with required props', () => {
10 | createSnapshotTest(Test );
11 | });
12 |
13 | it('should be visually hidden with isHidden passed', () => {
14 | const component = render(
15 |
16 | Visually Hidden?
17 | ,
18 | );
19 |
20 | const ScreenReaderOnly = component.queryByTestId(SCREEN_READER_ONLY);
21 | const ActualLabelElement = component.queryByTestId(LABEL);
22 |
23 | expect(ScreenReaderOnly.childNodes[0]).toBe(ActualLabelElement);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/components/Form/Label/__tests__/__snapshots__/Label.test.js.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Label > should render with required props 1`] = `
4 |
9 | Test
10 |
11 | `;
12 |
--------------------------------------------------------------------------------
/components/Form/Select/Select.module.css:
--------------------------------------------------------------------------------
1 | .field {
2 | margin: 1rem;
3 | }
4 |
5 | .errorMessage {
6 | /* Alert's border is only 1px - not 2px (account for Select's left and right border) */
7 | max-width: calc(100% + 2px);
8 | }
9 |
10 | @media screen and (--desktop-viewport) {
11 | .selectFeedbackGrouping {
12 | position: relative;
13 | }
14 |
15 | .errorMessage {
16 | margin-top: 0;
17 | margin-left: 1rem;
18 | position: absolute;
19 | top: -2px;
20 | left: 100%;
21 | min-width: 150px;
22 | max-width: 300px;
23 | width: auto;
24 | padding: 0 10px;
25 | height: 100%;
26 | display: flex;
27 | align-items: center;
28 | justify-content: center;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/components/Form/__tests__/Form.test.tsx:
--------------------------------------------------------------------------------
1 | import { Formik, Field } from 'formik';
2 | import noop from 'lodash/noop';
3 | import createSnapshotTest from 'test-utils/createSnapshotTest';
4 | import Form from '../Form';
5 |
6 | describe('Form', () => {
7 | it('should render within the context of Formik', () => {
8 | createSnapshotTest(
9 |
10 |
13 | ,
14 | );
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/components/Form/__tests__/__snapshots__/Form.test.js.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Form > should render within the context of Formik 1`] = `
4 |
18 | `;
19 |
--------------------------------------------------------------------------------
/components/Form/__tests__/__snapshots__/Form.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`Form > should render within the context of Formik 1`] = `
4 |
18 | `;
19 |
--------------------------------------------------------------------------------
/components/Forms/RegistrationForm/__stories__/RegistrationForm.stories.js:
--------------------------------------------------------------------------------
1 | import { RegistrationForm } from '../RegistrationForm';
2 |
3 | export default {
4 | component: RegistrationForm,
5 | title: 'Forms/RegistrationForm',
6 | };
7 |
8 | const Template = arguments_ => {
9 | return (
10 | <>
11 | Registration form
12 |
13 | >
14 | );
15 | };
16 |
17 | // Default Input supplied with only required args
18 | export const Default = Template.bind({});
19 | Default.args = {
20 | initialValues: {
21 | email: '',
22 | 'confirm-email': '',
23 | password: '',
24 | 'confirm-password': '',
25 | firstName: '',
26 | lastName: '',
27 | zipcode: '',
28 | },
29 | onSuccess: () => {},
30 | };
31 |
--------------------------------------------------------------------------------
/components/Forms/UpdateProfileForm/UpdateProfileForm.module.css:
--------------------------------------------------------------------------------
1 | .UpdateProfileForm {
2 | width: 100%;
3 | }
4 |
5 | .row {
6 | display: flex;
7 | flex-wrap: wrap;
8 | justify-content: center;
9 | }
10 |
11 | .topMargin {
12 | margin-top: 1rem;
13 | }
14 |
--------------------------------------------------------------------------------
/components/Forms/UpdateProfileForm/__tests__/UpdateProfileForm.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import UpdateProfileForm from '../UpdateProfileForm';
3 |
4 | describe('UpdateProfileForm', () => {
5 | beforeEach(() => {
6 | vi.mock('next/router', () => require('next-router-mock')); // eslint-disable-line global-require
7 | });
8 |
9 | it('should render with required props', () => {
10 | createSnapshotTest( );
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/components/Forms/UpdateProfileForm/steps/__tests__/MilitaryDetails.test.tsx:
--------------------------------------------------------------------------------
1 | import { Formik } from 'formik';
2 | import OperationCodeAPIMock from 'test-utils/mocks/apiMock';
3 | import createSnapshotTest from 'test-utils/createSnapshotTest';
4 | import Form from 'components/Form/Form';
5 |
6 | import noop from 'lodash/noop';
7 | import { MilitaryDetails } from '../MilitaryDetails';
8 |
9 | describe('UpdateProfileForm/Steps/MilitaryDetails', () => {
10 | afterEach(() => {
11 | OperationCodeAPIMock.reset();
12 | });
13 |
14 | it('should render in context of Formik', () => {
15 | createSnapshotTest(
16 |
21 |
24 | ,
25 | );
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/Forms/UpdateProfileForm/steps/__tests__/MilitaryStatus.test.tsx:
--------------------------------------------------------------------------------
1 | import { Formik } from 'formik';
2 | import createSnapshotTest from 'test-utils/createSnapshotTest';
3 | import Form from 'components/Form/Form';
4 | import noop from 'lodash/noop';
5 | import { MilitaryStatus } from '../MilitaryStatus';
6 |
7 | describe('UpdateProfileForm/Steps/MilitaryStatus', () => {
8 | it('should render in context of Formik', () => {
9 | createSnapshotTest(
10 |
15 |
18 | ,
19 | );
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/components/Forms/UpdateProfileForm/steps/__tests__/PersonalDetails.test.tsx:
--------------------------------------------------------------------------------
1 | import { Formik } from 'formik';
2 | import createSnapshotTest from 'test-utils/createSnapshotTest';
3 | import Form from 'components/Form/Form';
4 |
5 | import noop from 'lodash/noop';
6 | import { PersonalDetails } from '../PersonalDetails';
7 |
8 | describe('UpdateProfileForm/Steps/PersonalDetails', () => {
9 | it('should render in context of Formik', () => {
10 | createSnapshotTest(
11 |
16 |
19 | ,
20 | );
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/components/Forms/UpdateProfileForm/steps/__tests__/ProfessionalDetails.test.tsx:
--------------------------------------------------------------------------------
1 | import { Formik } from 'formik';
2 | import createSnapshotTest from 'test-utils/createSnapshotTest';
3 | import Form from 'components/Form/Form';
4 |
5 | import noop from 'lodash/noop';
6 | import { ProfessionalDetails } from '../ProfessionalDetails';
7 |
8 | describe('UpdateProfileForm/Steps/ProfessionalDetails', () => {
9 | it('should render in context of Formik', () => {
10 | createSnapshotTest(
11 |
16 |
19 | ,
20 | );
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/components/Forms/UpdateProfileForm/steps/_steps.module.css:
--------------------------------------------------------------------------------
1 | /* File has _ in front of name so that it appears first on file explorer 🤷♂️ */
2 | .row {
3 | display: flex;
4 | flex-direction: column;
5 | align-items: stretch;
6 | gap: 1rem;
7 | }
8 |
9 | .row > * {
10 | margin: 0;
11 | }
12 |
13 | .pulledLeft {
14 | justify-content: flex-start;
15 | }
16 |
17 | .fullWidth {
18 | width: 100%;
19 | }
20 |
--------------------------------------------------------------------------------
/components/Heading/__stories__/Heading.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import Heading from '../Heading';
3 |
4 | type HeadingStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'Heading',
8 | component: Heading,
9 | args: {
10 | text: 'Heading text',
11 | },
12 | };
13 |
14 | export default meta;
15 |
16 | export const Default: HeadingStoryType = {
17 | render: args => ,
18 | };
19 |
--------------------------------------------------------------------------------
/components/Heading/__tests__/Heading.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import Heading from '../Heading';
3 |
4 | describe('Heading', () => {
5 | const requiredProps = {
6 | text: 'Test',
7 | };
8 |
9 | it('should render with required props', () => {
10 | createSnapshotTest( );
11 | });
12 |
13 | it('should render with many props assigned', () => {
14 | createSnapshotTest(
15 | ,
16 | );
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/components/HeroBanner/__stories__/HeroBanner.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import HeroBanner from '../HeroBanner';
3 |
4 | type HeroBannerStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'HeroBanner',
8 | component: HeroBanner,
9 | args: {
10 | title: 'Banner Title',
11 | },
12 | };
13 |
14 | export default meta;
15 |
16 | export const Default: HeroBannerStoryType = {
17 | render: args => ,
18 | };
19 |
--------------------------------------------------------------------------------
/components/HeroBanner/__tests__/HeroBanner.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 |
3 | import { s3 } from 'common/constants/urls';
4 | import HeroBanner from '../HeroBanner';
5 |
6 | describe('HeroBanner', () => {
7 | it('should render with required props', () => {
8 | createSnapshotTest( );
9 | });
10 |
11 | it('should render with many props assigned', () => {
12 | createSnapshotTest(
13 |
19 | Testing 123
20 | ,
21 | );
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/components/HeroBanner/__tests__/__snapshots__/HeroBanner.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`HeroBanner > should render with many props assigned 1`] = `
4 |
12 |
15 |
19 | Test
20 |
21 | Testing 123
22 |
23 |
24 | `;
25 |
26 | exports[`HeroBanner > should render with required props 1`] = `
27 |
30 |
33 |
37 | Test
38 |
39 |
40 |
41 | `;
42 |
--------------------------------------------------------------------------------
/components/InlineLoadingSpinner.tsx:
--------------------------------------------------------------------------------
1 | import { twMerge } from 'tailwind-merge';
2 |
3 | interface InlineLoadingSpinnerProps {
4 | className?: string;
5 | }
6 |
7 | export const InlineLoadingSpinner = ({ className }: InlineLoadingSpinnerProps) => (
8 |
15 | );
16 |
--------------------------------------------------------------------------------
/components/Nav/NavListItem/__tests__/__snapshots__/NavListItem.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`NavListItem > should render with required props passed 1`] = `
4 |
7 |
10 |
18 |
21 | Test
22 |
23 |
24 |
25 |
26 | `;
27 |
--------------------------------------------------------------------------------
/components/Nav/NavMobile/__tests__/__snapshots__/NavMobile.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`NavMobile > should render 1`] = `
4 |
8 |
11 |
16 |
19 |
25 |
26 |
27 |
28 |
35 |
36 | Open Menu
37 |
38 |
41 |
42 |
43 | `;
44 |
--------------------------------------------------------------------------------
/components/OutboundLink/__stories__/OutboundLink.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { descriptions } from 'common/constants/descriptions';
3 | import OutboundLink from '../OutboundLink';
4 |
5 | type OutboudLinkStoryType = StoryObj;
6 |
7 | const meta: Meta = {
8 | title: 'OutboundLink',
9 | component: OutboundLink,
10 | args: {
11 | analyticsEventLabel: 'Event label',
12 | children: descriptions.short,
13 | href: '#',
14 | },
15 | };
16 |
17 | export default meta;
18 |
19 | export const Default: OutboudLinkStoryType = {
20 | render: args => ,
21 | };
22 |
--------------------------------------------------------------------------------
/components/OutboundLink/__tests__/__snapshots__/OutboundLink.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`OutboundLink > should render with required props 1`] = `
4 |
11 | Test
12 |
16 | Opens in new window
17 |
18 | External Link Symbol ",
23 | }
24 | }
25 | viewBox="0 0 448 512"
26 | />
27 |
28 | `;
29 |
--------------------------------------------------------------------------------
/components/PartnerLogoLink/__stories__/PartnerLogoLink.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { s3 } from 'common/constants/urls';
3 | import PartnerLogoLink from '../PartnerLogoLink';
4 |
5 | type PartnerLogoLinkStoryType = StoryObj;
6 |
7 | const meta: Meta = {
8 | title: 'PartnerLogoLink',
9 | component: PartnerLogoLink,
10 | args: {
11 | logoSource: `${s3}partnerLogos/github.png`,
12 | name: 'Partner Name',
13 | url: '#',
14 | },
15 | };
16 |
17 | export default meta;
18 |
19 | export const Default: PartnerLogoLinkStoryType = {
20 | render: args => ,
21 | };
22 |
--------------------------------------------------------------------------------
/components/PartnerLogoLink/__tests__/PartnerLogoLink.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import PartnerLogoLink from '../PartnerLogoLink';
3 |
4 | describe('PartnerLogoLink', () => {
5 | it('should render with required props', () => {
6 | createSnapshotTest(
7 | ,
12 | );
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/components/PartnerLogoLink/__tests__/__snapshots__/PartnerLogoLink.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`PartnerLogoLink > should render with required props 1`] = `
4 |
26 | `;
27 |
--------------------------------------------------------------------------------
/components/Press/PressLinks/__tests__/PressLinks.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import PressLinks from '../PressLinks';
4 |
5 | describe('PressLinks', () => {
6 | it('should render with no props passed', () => createShallowSnapshotTest( ));
7 | });
8 |
--------------------------------------------------------------------------------
/components/Press/PressPhotos/PressPhotos.module.css:
--------------------------------------------------------------------------------
1 | .photos {
2 | padding-top: 15px;
3 | display: flex;
4 | flex-flow: row wrap;
5 | justify-content: space-around;
6 | align-items: center;
7 | }
8 |
9 | .photos > img {
10 | max-width: 350px;
11 | width: auto;
12 | padding: 0.75rem;
13 | }
14 |
15 | @media screen and (--small-viewport) {
16 | .photos > img {
17 | width: 100%;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/components/Press/PressPhotos/__tests__/PressPhotos.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import PressPhotos from '../PressPhotos';
4 |
5 | describe('PressPhotos', () => {
6 | it('should render with no props passed', () => createShallowSnapshotTest( ));
7 | });
8 |
--------------------------------------------------------------------------------
/components/Press/PressVideos/PressVideos.module.css:
--------------------------------------------------------------------------------
1 | .pressVideos {
2 | padding-top: 15px;
3 | display: flex;
4 | flex-flow: row wrap;
5 | justify-content: space-around;
6 | align-items: center;
7 | }
8 |
9 | .pressVideos iframe {
10 | padding: 25px;
11 | }
12 |
13 | /* Video iFrame Media Queries */
14 | @media screen and (--large-viewport) {
15 | .pressVideos iframe {
16 | width: 100%;
17 | height: 375px;
18 | }
19 | }
20 |
21 | @media screen and (--medium-viewport) {
22 | .pressVideos iframe {
23 | height: 250px;
24 | }
25 | }
26 |
27 | @media screen and (--small-viewport) {
28 | .pressVideos iframe {
29 | height: 187.5px;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/components/Press/PressVideos/PressVideos.tsx:
--------------------------------------------------------------------------------
1 | import styles from './PressVideos.module.css';
2 |
3 | function PressVideos() {
4 | return (
5 |
6 | VIDEO
14 |
22 |
23 | );
24 | }
25 |
26 | export default PressVideos;
27 |
--------------------------------------------------------------------------------
/components/Press/PressVideos/__tests__/PressVideos.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import PressVideos from '../PressVideos';
4 |
5 | describe('PressVideos', () => {
6 | it('should render with no props passed', () => createShallowSnapshotTest( ));
7 | });
8 |
--------------------------------------------------------------------------------
/components/Press/PressVideos/__tests__/__snapshots__/PressVideos.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`PressVideos > should render with no props passed 1`] = `
4 |
7 | VIDEO
15 |
23 |
24 | `;
25 |
--------------------------------------------------------------------------------
/components/Press/index.ts:
--------------------------------------------------------------------------------
1 | import Photos from './PressPhotos/PressPhotos';
2 | import Videos from './PressVideos/PressVideos';
3 | import Links from './PressLinks/PressLinks';
4 |
5 | export { Photos, Videos, Links };
6 |
--------------------------------------------------------------------------------
/components/ProgressIndicator/__tests__/ProgressIndicator.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 |
3 | import ProgressIndicator, { developmentErrors } from '../ProgressIndicator';
4 |
5 | describe('ProgressIndicator', () => {
6 | it('should render with required props', () => {
7 | createSnapshotTest( );
8 | });
9 |
10 | it('should throw an error if given value of currentStep is less than 0', () => {
11 | expect(() => ProgressIndicator({ stepNumber: -5, totalSteps: 5 })).toThrow(
12 | developmentErrors.currentStepTooLow,
13 | );
14 | });
15 |
16 | it('should throw an error if given value of currentStep is greater than totalSteps', () => {
17 | expect(() => ProgressIndicator({ stepNumber: 6, totalSteps: 3 })).toThrow(
18 | developmentErrors.currentStepTooHigh,
19 | );
20 | });
21 |
22 | it('should throw an error if given value of totalSteps is less than 1', () => {
23 | expect(() => ProgressIndicator({ stepNumber: 1, totalSteps: -3 })).toThrow(
24 | developmentErrors.totalStepsTooLow,
25 | );
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/ProgressIndicator/__tests__/__snapshots__/ProgressIndicator.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`ProgressIndicator > should render with required props 1`] = `
4 |
7 |
10 | 1
11 | /
12 | 3
13 | Complete
14 |
15 |
20 | 33.33333333333333
21 | %
22 |
23 |
24 | `;
25 |
--------------------------------------------------------------------------------
/components/ReusableSections/DonateSection/DonateSection.tsx:
--------------------------------------------------------------------------------
1 | import { s3 } from 'common/constants/urls';
2 | import Container from 'components/Container/Container';
3 | import LinkButton from 'components/Buttons/LinkButton/LinkButton';
4 | import Heading from 'components/Heading/Heading';
5 |
6 | function DonateSection() {
7 | return (
8 |
9 |
10 |
11 |
12 | As a 501(c)(3) veteran-led nonprofit organization, our programs and services are maintained
13 | through the efforts of our volunteer staff. Your financial support allows us to continue
14 | helping the military community learn software development, enter the tech industry, and code
15 | the future.
16 |
17 |
18 | Thank you for supporting our mission!
19 |
20 |
21 | Donate Now
22 |
23 |
24 | );
25 | }
26 |
27 | export default DonateSection;
28 |
--------------------------------------------------------------------------------
/components/ReusableSections/DonateSection/__stories__/DonateSection.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import type { Meta, StoryObj } from '@storybook/react';
3 | import DonateSection from '../DonateSection';
4 |
5 | type DonateSectionStoryType = StoryObj;
6 |
7 | const meta: Meta = {
8 | title: 'ReusableSections/DonateSection',
9 | component: DonateSection,
10 | };
11 |
12 | export default meta;
13 |
14 | export const Default: DonateSectionStoryType = {
15 | render: args => ,
16 | };
17 |
--------------------------------------------------------------------------------
/components/ReusableSections/DonateSection/__tests__/DonateSection.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 | import DonateSection from '../DonateSection';
3 |
4 | describe('DonateSection', () => {
5 | it('should render with no props passed passed', () =>
6 | createShallowSnapshotTest( ));
7 | });
8 |
--------------------------------------------------------------------------------
/components/ReusableSections/DonateSection/__tests__/__snapshots__/DonateSection.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`DonateSection > should render with no props passed passed 1`] = `
4 |
7 |
11 |
12 | As a 501(c)(3) veteran-led nonprofit organization, our programs and services are maintained through the efforts of our volunteer staff. Your financial support allows us to continue helping the military community learn software development, enter the tech industry, and code the future.
13 |
14 |
15 | Thank you for supporting our mission!
16 |
17 |
22 | Donate Now
23 |
24 |
25 | `;
26 |
--------------------------------------------------------------------------------
/components/ReusableSections/JoinSection/JoinSection.tsx:
--------------------------------------------------------------------------------
1 | import Container from 'components/Container/Container';
2 | import OutboundLink from 'components/OutboundLink/OutboundLink';
3 | import LinkButton from 'components/Buttons/LinkButton/LinkButton';
4 | import Heading from 'components/Heading/Heading';
5 |
6 | function JoinSection() {
7 | return (
8 |
9 |
10 |
11 |
12 | Are you ready to begin your journey towards a career in tech? Get the support you need by
13 | joining our members-only Slack community!
14 |
15 |
16 |
17 | Register Now
18 |
19 |
20 | Slack is a community based collaboration tool where all the magic happens!
21 |
22 |
26 | Never heard of Slack before?
27 |
28 |
29 | );
30 | }
31 |
32 | export default JoinSection;
33 |
--------------------------------------------------------------------------------
/components/ReusableSections/JoinSection/__stories__/JoinSection.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import JoinSection from '../JoinSection';
3 |
4 | type JoinSectionStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'ReusableSections/JoinSection',
8 | component: JoinSection,
9 | };
10 |
11 | export default meta;
12 |
13 | export const Default: JoinSectionStoryType = {
14 | render: () => ,
15 | };
16 |
--------------------------------------------------------------------------------
/components/ReusableSections/JoinSection/__tests__/JoinSection.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import createSnapshotTest from 'test-utils/createSnapshotTest';
3 | import JoinSection from '../JoinSection';
4 |
5 | describe('JoinSection', () => {
6 | it('should render', () => {
7 | const { queryByTestId } = render( );
8 | expect(queryByTestId('Join Section')).not.toBeNull();
9 |
10 | createSnapshotTest( );
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/components/ReusableSections/SponsorsSection/__stories__/SponsorSection.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import SponsorsSection from '../SponsorsSection';
3 |
4 | type SponsorsSectionStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'ReusableSections/SponsorsSection',
8 | component: SponsorsSection,
9 | };
10 |
11 | export default meta;
12 |
13 | export const Default: SponsorsSectionStoryType = {
14 | render: () => ,
15 | };
16 |
--------------------------------------------------------------------------------
/components/ReusableSections/SponsorsSection/__tests__/SponsorsSection.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import createSnapshotTest from 'test-utils/createSnapshotTest';
3 | import partners from 'common/constants/partners';
4 | import SponsorsSection from '../SponsorsSection';
5 |
6 | describe('SponsorsSection', () => {
7 | it('should render', () => {
8 | const { queryByTestId } = render( );
9 | expect(queryByTestId('Sponsors Section')).not.toBeNull();
10 |
11 | createSnapshotTest( );
12 | });
13 |
14 | it('should render a secure link and image for each partner', () => {
15 | const component = render( );
16 |
17 | partners.forEach(partner => {
18 | const image = component.queryByAltText(`${partner.name} logo`)!;
19 | const link = image.parentNode as HTMLAnchorElement;
20 |
21 | expect(image).toBeInTheDocument();
22 | expect(link.href.startsWith('https://')).toStrictEqual(true);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/components/ScreenReaderOnly/ScreenReaderOnly.tsx:
--------------------------------------------------------------------------------
1 | import { SCREEN_READER_ONLY } from 'common/constants/testIDs';
2 |
3 | export const toggleMessages = {
4 | open: 'Show more',
5 | close: 'Hide expanded',
6 | };
7 |
8 | function ScreenReaderOnly({ children }: React.PropsWithChildren) {
9 | return (
10 |
11 | {children}
12 |
13 | );
14 | }
15 |
16 | export default ScreenReaderOnly;
17 |
--------------------------------------------------------------------------------
/components/ScreenReaderOnly/__stories__/ScreenReaderOnly.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import ScreenReaderOnly from '../ScreenReaderOnly';
3 |
4 | type ScreenReaderOnlyStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'ScreenReaderOnly',
8 | component: ScreenReaderOnly,
9 | args: {
10 | children: 'ScreenReader content',
11 | },
12 | decorators: [
13 | ScreenReaderOnlyStory => (
14 | <>
15 | Content never displayed, but it is rendered
16 |
17 | >
18 | ),
19 | ],
20 | };
21 |
22 | export default meta;
23 |
24 | export const Default: ScreenReaderOnlyStoryType = {
25 | render: args => ,
26 | };
27 |
--------------------------------------------------------------------------------
/components/ScreenReaderOnly/__tests__/ScreenReaderOnly.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import { composeStory } from '@storybook/react';
3 | import meta, { Default } from '../__stories__/ScreenReaderOnly.stories';
4 |
5 | const ScreenReaderOnlyStory = composeStory(Default, meta);
6 |
7 | describe('ScreenReaderOnly', () => {
8 | it('should render with required props', () => {
9 | createSnapshotTest( );
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/components/ScreenReaderOnly/__tests__/__snapshots__/ScreenReaderOnly.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`ScreenReaderOnly > should render with required props 1`] = `
4 | [
5 |
6 | Content never displayed, but it is rendered
7 | ,
8 |
12 | ScreenReader content
13 | ,
14 | ]
15 | `;
16 |
--------------------------------------------------------------------------------
/components/SocialMedia/SocialMediaContainer/SocialMediaContainer.tsx:
--------------------------------------------------------------------------------
1 | export interface SocialMediaContainer {
2 | /**
3 | * Child content of
4 | */
5 | children: React.ReactElement[];
6 | }
7 |
8 | function SocialMediaContainer({ children }: SocialMediaContainer) {
9 | return {children}
;
10 | }
11 |
12 | export default SocialMediaContainer;
13 |
--------------------------------------------------------------------------------
/components/SocialMedia/SocialMediaContainer/__tests__/SocialMediaContainer.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 |
3 | import SocialMediaContainer from '../SocialMediaContainer';
4 |
5 | describe('SocialMediaContainer', () => {
6 | it('should render with required props', () => {
7 | createSnapshotTest(
8 |
9 | Testing 1
10 | Testing 2
11 | ,
12 | );
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/components/SocialMedia/SocialMediaContainer/__tests__/__snapshots__/SocialMediaContainer.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`SocialMediaContainer > should render with required props 1`] = `
4 |
7 |
8 | Testing 1
9 |
10 |
11 | Testing 2
12 |
13 |
14 | `;
15 |
--------------------------------------------------------------------------------
/components/SocialMedia/SocialMediaItem/SocialMediaItem.tsx:
--------------------------------------------------------------------------------
1 | import OutboundLink from 'components/OutboundLink/OutboundLink';
2 | import ScreenReaderOnly from 'components/ScreenReaderOnly/ScreenReaderOnly';
3 |
4 | export interface SocialMediaItemPropsType {
5 | /**
6 | * Name of the social media item.
7 | */
8 | name: string;
9 | /**
10 | * URL to the social media site.
11 | */
12 | href: string;
13 | /**
14 | * Icon to be used.
15 | */
16 | svg: React.ReactNode;
17 | }
18 |
19 | function SocialMediaItem({ name, href, svg }: SocialMediaItemPropsType) {
20 | return (
21 |
22 |
27 | Operation Code's {name}
28 | {svg}
29 |
30 |
31 | );
32 | }
33 |
34 | export default SocialMediaItem;
35 |
--------------------------------------------------------------------------------
/components/SocialMedia/SocialMediaItem/__tests__/SocialMediaItem.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 | import FacebookLogo from 'static/images/icons/facebook_logo.svg';
3 | import SocialMediaItem from '../SocialMediaItem';
4 |
5 | describe('SocialMediaItem', () => {
6 | it('should render with required props', () => {
7 | createSnapshotTest(
8 | } />,
9 | );
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/components/SocialMedia/__tests__/SocialMedia.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import SocialMedia from '../SocialMedia';
4 |
5 | describe('SocialMedia', () => {
6 | it('should render with no props passed passed', () => {
7 | createShallowSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/components/SuccessStory/SuccessStory.tsx:
--------------------------------------------------------------------------------
1 | import FlatCard from 'components/Cards/FlatCard/FlatCard';
2 |
3 | export interface SuccessStoryPropsType {
4 | /**
5 | * Path to image used on the card.
6 | */
7 | imageSource: string;
8 | /**
9 | * String applied tot he block quote element.
10 | */
11 | quote: string;
12 | /**
13 | * String applied to the card header.
14 | */
15 | title: string;
16 | }
17 |
18 | function SuccessStory({ imageSource, quote, title }: SuccessStoryPropsType) {
19 | return (
20 |
21 | {`“${quote}”`}
22 |
23 | );
24 | }
25 |
26 | export default SuccessStory;
27 |
--------------------------------------------------------------------------------
/components/SuccessStory/__stories__/SuccessStory.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { descriptions } from 'common/constants/descriptions';
3 | import { s3 } from 'common/constants/urls';
4 | import SuccessStory from '../SuccessStory';
5 |
6 | type SuccessStoryType = StoryObj;
7 |
8 | const meta: Meta = {
9 | title: 'SuccesStory',
10 | component: SuccessStory,
11 | args: {
12 | title: 'Name of Person',
13 | imageSource: `${s3}headshots/david_molina.jpg`,
14 | quote: descriptions.long,
15 | },
16 | };
17 |
18 | export default meta;
19 |
20 | export const Default: SuccessStoryType = {
21 | render: args => ,
22 | };
23 |
--------------------------------------------------------------------------------
/components/SuccessStory/__tests__/SuccessStory.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 | import SuccessStory from '../SuccessStory';
3 |
4 | describe('SuccessStory', () => {
5 | it('should render with required props', () => {
6 | createShallowSnapshotTest(
7 | ,
12 | );
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/components/SuccessStory/__tests__/__snapshots__/SuccessStory.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`SuccessStory > should render with required props 1`] = `
4 |
14 |
15 | “Best test of my life!”
16 |
17 |
18 | `;
19 |
--------------------------------------------------------------------------------
/components/Timeline/Timeline.module.css:
--------------------------------------------------------------------------------
1 | .timeline {
2 | width: 90%;
3 | margin: 50px auto;
4 | }
5 |
6 | .segment {
7 | display: flex;
8 | }
9 |
10 | /* Date (left column) */
11 | .date {
12 | flex: 1;
13 | position: relative;
14 | text-align: right;
15 | }
16 |
17 | .date > h3 {
18 | position: absolute;
19 | width: 100%;
20 | top: 14px;
21 | padding-right: 10px;
22 | }
23 |
24 | /* Vertical Line w/ Bubbles (middle column) */
25 | .vertLine {
26 | position: relative;
27 | padding: 0 20px;
28 | }
29 |
30 | .line {
31 | position: absolute;
32 | transform: translateX(-50%);
33 | width: 2px;
34 | top: 0;
35 | height: 100%;
36 | background-color: #d7d7d7;
37 | }
38 |
39 | .bubble {
40 | width: 15px;
41 | height: 15px;
42 | border-radius: 50%;
43 | border: 2px solid #7d7d7d;
44 | background-color: white;
45 | position: absolute;
46 | transform: translateX(-50%);
47 | top: 25px;
48 | }
49 |
50 | /* Events (right column) */
51 | .timelineEvent {
52 | flex: 8;
53 | }
54 |
55 | @media screen and (--medium-viewport) {
56 | .date > h3 {
57 | top: 24px;
58 | font-size: 1.1rem;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/components/Timeline/Timeline.tsx:
--------------------------------------------------------------------------------
1 | import { objectKeys } from 'utils/types';
2 | import historyData from './historyData';
3 | import styles from './Timeline.module.css';
4 | import TimelineEvent from './TimelineEvent/TimelineEvent';
5 |
6 | function Timeline() {
7 | return (
8 |
9 | {objectKeys(historyData).map(year => (
10 |
11 |
12 |
{year}
13 |
14 |
15 |
19 |
20 |
21 | {historyData[year].map(({ title, content }) => (
22 |
23 | ))}
24 |
25 |
26 | ))}
27 |
28 | );
29 | }
30 |
31 | export default Timeline;
32 |
--------------------------------------------------------------------------------
/components/Timeline/TimelineEvent/TimelineEvent.module.css:
--------------------------------------------------------------------------------
1 | .eventContainer {
2 | margin: 0 0 30px 0;
3 | border: 1px solid #c5c5c5;
4 | padding: 20px;
5 | border-radius: 5px;
6 | }
7 |
8 | .eventTitle {
9 | text-transform: capitalize;
10 | }
11 |
12 | @media screen and (--medium-viewport) {
13 | .eventTitle {
14 | font-size: 1.5rem;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/components/Timeline/TimelineEvent/TimelineEvent.tsx:
--------------------------------------------------------------------------------
1 | import styles from './TimelineEvent.module.css';
2 |
3 | export interface TimelineEventPropsType {
4 | content: React.ReactNode | React.ReactNode[];
5 | title: string;
6 | }
7 |
8 | function TimelineEvent({ content, title }: TimelineEventPropsType) {
9 | return (
10 |
11 |
{title}
12 |
13 |
{content}
14 |
15 | );
16 | }
17 |
18 | export default TimelineEvent;
19 |
--------------------------------------------------------------------------------
/components/Timeline/TimelineEvent/__tests__/TimelineEvent.test.tsx:
--------------------------------------------------------------------------------
1 | import createSnapshotTest from 'test-utils/createSnapshotTest';
2 |
3 | import TimelineEvent from '../TimelineEvent';
4 |
5 | describe('TimelineEvent', () => {
6 | it('should render with required props', () => {
7 | createSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/components/Timeline/TimelineEvent/__tests__/__snapshots__/TimelineEvent.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`TimelineEvent > should render with required props 1`] = `
4 |
7 |
10 | test title
11 |
12 |
13 | here is some test content
14 |
15 |
16 | `;
17 |
--------------------------------------------------------------------------------
/components/Timeline/TimelineNav/TimelineNav.module.css:
--------------------------------------------------------------------------------
1 | .timelineNavLink:not(:hover) {
2 | opacity: 0.6;
3 | }
4 |
--------------------------------------------------------------------------------
/components/Timeline/TimelineNav/TimelineNav.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-scroll';
2 | import historyData from '../historyData';
3 |
4 | const TimelineNav = () => {
5 | return (
6 |
7 |
8 | {Object.keys(historyData).map(year => (
9 |
15 | {year}
16 |
17 | ))}
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default TimelineNav;
25 |
--------------------------------------------------------------------------------
/components/Timeline/TimelineNav/__tests__/TimelineNav.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 | import TimelineNav from '../TimelineNav';
3 |
4 | describe('TimelineNav', () => {
5 | it('should render with no props passed passed', () => {
6 | createShallowSnapshotTest( );
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/components/Timeline/__tests__/Timeline.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import Timeline from '../Timeline';
4 |
5 | describe('Timeline', () => {
6 | it('should render with no props passed passed', () => {
7 | createShallowSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/components/UpgradeBrowserOverlay/__stories__/UpgradeBrowserOverlay.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import UpgradeBrowserOverlay from '../UpgradeBrowserOverlay';
3 |
4 | type UpgradeBrowserOverlayStoryType = StoryObj;
5 |
6 | const meta: Meta = {
7 | title: 'UpgradeBrowserOverlay',
8 | component: UpgradeBrowserOverlay,
9 | };
10 |
11 | export default meta;
12 |
13 | export const Default: UpgradeBrowserOverlayStoryType = {
14 | render: () => ,
15 | };
16 |
--------------------------------------------------------------------------------
/components/UpgradeBrowserOverlay/__tests__/UpgradeBrowserOverlay.test.tsx:
--------------------------------------------------------------------------------
1 | import createShallowSnapshotTest from 'test-utils/createShallowSnapshotTest';
2 |
3 | import UpgradeBrowserOverlay from '../UpgradeBrowserOverlay';
4 |
5 | describe('UpgradeBrowserOverlay', () => {
6 | it('should render with no props passed', () => {
7 | createShallowSnapshotTest( );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/cypress.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | const { defineConfig } = require('cypress');
3 |
4 | module.exports = defineConfig({
5 | projectId: 'dbquo6',
6 | viewportWidth: 1600,
7 | viewportHeight: 1200,
8 | requestTimeout: 10000,
9 | env: {
10 | RETRIES: 2,
11 | codeCoverage: {
12 | url: '/api/__coverage__',
13 | },
14 | },
15 | retries: {
16 | runMode: 2,
17 | openMode: 2,
18 | },
19 | e2e: {
20 | // We've imported your old cypress plugins here.
21 | // You may want to clean this up later by importing these.
22 | setupNodeEvents(on, config) {
23 | return require('./cypress/plugins/index')(on, config);
24 | },
25 | baseUrl: 'http://localhost:3000',
26 | specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/cypress/README.md:
--------------------------------------------------------------------------------
1 | # Cypress
2 |
3 | We use Cypress for end-to-end (e2e) and integration testing.
4 |
5 | It's awesome.
6 |
7 | We don't want to spend time re-listing documentation, but if you'd like to learn how to write solid e2e tests with Cypress, we recommend using the following resources:
8 |
9 | 1. [Brian Mann speaks about Cypress at Assert.js Conference](https://www.youtube.com/watch?v=5XQOK0v_YRE). See a demo of Cypress, plus watch the creator of the library walk through implementing sturdy e2e testing of a complex web application.
10 |
11 | 2. [Best Practices with Cypress](https://docs.cypress.io/guides/references/best-practices.html). The entire Cypress documentation is well-written and thorough, but this particular section is very important.
12 |
13 | 3. [Cypress Example Recipes](https://github.com/cypress-io/cypress-example-recipes). Learn from a bunch of examples!
14 |
--------------------------------------------------------------------------------
/cypress/e2e/faq.spec.js:
--------------------------------------------------------------------------------
1 | import { ACCORDION_CONTENT, ACCORDION_TOGGLE_BUTTON } from 'common/constants/testIDs';
2 |
3 | describe('faq', () => {
4 | beforeEach(() => {
5 | cy.visitAndWaitFor('/faq');
6 | cy.get('h1').should('have.text', 'Frequently Asked Questions');
7 | });
8 |
9 | it('reveals text after clicking "SHOW"', () => {
10 | cy.findByTestId(ACCORDION_CONTENT).should('not.exist');
11 | cy.findAllByTestId(ACCORDION_TOGGLE_BUTTON).then(([firstButton]) => firstButton.click());
12 | cy.findAllByTestId(ACCORDION_CONTENT).should('be.visible');
13 | });
14 |
15 | // TODO: Enable when native keyboard events are supported by Cypress
16 | // it('should be keyboard navigable', () => {
17 | // cy.get('article')
18 | // .first()
19 | // .find('[data-testid="Accordion Content"]')
20 | // .should('not.be.visible');
21 |
22 | // cy.get('article')
23 | // .first()
24 | // .click('{tab}{enter}'); // tab isnt supported yet 😭
25 |
26 | // cy.get('article')
27 | // .first()
28 | // .find('[data-testid="Accordion Content"]')
29 | // .should('be.visible');
30 | // });
31 | });
32 |
--------------------------------------------------------------------------------
/cypress/e2e/podcast.spec.js:
--------------------------------------------------------------------------------
1 | describe('podcast', () => {
2 | beforeEach(() => {
3 | cy.server();
4 | cy.visitAndWaitFor('/podcast');
5 | cy.get('h1').should('have.text', 'Podcast');
6 | });
7 |
8 | it('renders many podcast cards', () => {
9 | cy.findAllByTestId('Podcast Card').should('have.length.greaterThan', 0);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/cypress/e2e/team.spec.js:
--------------------------------------------------------------------------------
1 | describe('team', () => {
2 | beforeEach(() => {
3 | cy.server();
4 | cy.visitAndWaitFor('/team');
5 | cy.get('h1').should('have.text', 'The Team');
6 | });
7 |
8 | it('renders many board members', () => {
9 | // 3 is arbitrary, but it confirms that the API is working
10 | cy.get('article').should('have.length.greaterThan', 3);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 | import '@cypress/code-coverage/support';
19 |
20 | // Alternatively you can use CommonJS syntax:
21 | // require('./commands')
22 |
23 | // beforeEach(() => {
24 | // });
25 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "common/*": ["../common/*"],
5 | "components/*": ["../components/*"],
6 | "pages/*": ["../pages/*"],
7 | "public/*": ["../public/*"],
8 | "scripts/*": ["../scripts/*"],
9 | "static/*": ["../public/static/*"],
10 | "styles/*": ["../styles/*"],
11 | "test-utils/*": ["../test-utils/*"]
12 | },
13 | "types": ["cypress", "@testing-library/cypress"]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/decorators/README.md:
--------------------------------------------------------------------------------
1 | # Decorators
2 |
3 | These files are to be used to compose or enhance pages or components via acting as a higher-order component (HOC).
4 |
5 | See [the React docs](https://reactjs.org/docs/higher-order-components.html) for more on HOCs.
6 |
7 | ## ⚠️ Rule specific to HOCs in Next.js ⚠️
8 |
9 | ALWAYS supply `getInitialProps` as a static property of the wrapped component. If not, we'll break
10 | the entire app.
11 |
12 | - Source of information: https://github.com/vercel/next.js/issues/3899
13 |
--------------------------------------------------------------------------------
/decorators/getDisplayName.js:
--------------------------------------------------------------------------------
1 | // Gets the display name of a JSX component for dev tools
2 | const getDisplayName = Component => Component.displayName || Component.name || 'Component';
3 |
4 | export default getDisplayName;
5 |
--------------------------------------------------------------------------------
/internationalized-documentation/README.md:
--------------------------------------------------------------------------------
1 | # Internationalized Documentation
2 |
3 | ## Files Which Need Translations
4 |
5 | - [CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md)
6 |
7 | ## Files Which DO NOT Need Translations
8 |
9 | These files simply change to frequently.
10 |
11 | - [README.md](../README.md)
12 | - [CONTRIBUTING.md](../CONTRIBUTING.md)
13 |
14 | ## Steps To Add
15 |
16 | - Create the translated file in it's appropriate folder.
17 | - Suffix the file name with your [2-letter language code](https://www.sitepoint.com/iso-2-letter-language-codes/). (CODE_OF_CONDUCT_ES.md for spanish)
18 | - Link to that file at the top of the original file.
19 |
20 | Example, inside [CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md):
21 |
22 | ```
23 | [German](./internationalized-documentation/Germany/CODE_OF_CONDUCT_DE.md)
24 | ```
25 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "checkJs": false,
5 | "target": "esnext",
6 | "allowSyntheticDefaultImports": true,
7 | "experimentalDecorators": true,
8 | "baseUrl": ".",
9 | "paths": {
10 | // Ensure intellisense continues to operate - match with webpack aliases
11 | "common/*": ["./common/*"],
12 | "components/*": ["./components/*"],
13 | "pages/*": ["./pages/*"],
14 | "public/*": ["./public/*"],
15 | "scripts/*": ["./scripts/*"],
16 | "static/*": ["./public/static/*"],
17 | "styles/*": ["./styles/*"],
18 | "test-utils/*": ["./test-utils/*"]
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | const priorities = { '/': '1.00', '/join': '1.00' };
2 |
3 | /** @type {import('next-sitemap').IConfig} */
4 | module.exports = {
5 | siteUrl: 'https://www.operationcode.org',
6 | generateIndexSitemap: false, // Simplification to make robots.txt "Sitemap" easier
7 | priority: 0.8,
8 | changefreq: 'weekly',
9 | // Modified default transform function
10 | transform: async (config, path) => {
11 | return {
12 | loc: path,
13 | changefreq: config.changefreq,
14 | priority: priorities[path] ?? config.priority,
15 | lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
16 | alternateRefs: config.alternateRefs ?? [],
17 | };
18 | },
19 | exclude: [
20 | '/api/__coverage__',
21 | '/profile/change_password',
22 | '/profile',
23 | '/profile/update',
24 | '/thank_you',
25 | '/404',
26 | '/_app',
27 | '/_document',
28 | '/_error',
29 | '/join/form',
30 | '/join/success',
31 | '/confirm_email',
32 | ],
33 | };
34 |
--------------------------------------------------------------------------------
/nyc.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | include: [
3 | 'pages/**/*.{js,ts,tsx}',
4 | 'common/**/*.{js,ts,tsx}',
5 | 'components/**/*.{js,ts,tsx}',
6 | 'decorators/**/*.{js,ts,tsx}',
7 | ],
8 | exclude: ['pages/api/__coverage__.{js,ts}'],
9 | };
10 |
--------------------------------------------------------------------------------
/pages/404.tsx:
--------------------------------------------------------------------------------
1 | import ErrorDisplay from 'components/ErrorDisplay/ErrorDisplay';
2 |
3 | export default function Custom404() {
4 | return ;
5 | }
6 |
--------------------------------------------------------------------------------
/pages/README.md:
--------------------------------------------------------------------------------
1 | # Pages
2 |
3 | Every JavaScript file in this folder represents a route on the website. Note: The way the file is named is exactly as the route will appear. Please use underscores between words.
4 |
5 | # Homepage
6 |
7 | `index.js` represents the homepage.
8 |
--------------------------------------------------------------------------------
/pages/api/__coverage__.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@cypress/code-coverage/middleware/nextjs');
2 |
--------------------------------------------------------------------------------
/pages/blog/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useRouter } from 'next/router';
3 | import Head from 'components/head';
4 | import HeroBanner from 'components/HeroBanner/HeroBanner';
5 |
6 | const pageTitle = 'Blog';
7 |
8 | function BlogIndex() {
9 | const router = useRouter();
10 |
11 | useEffect(() => {
12 | router.push({ pathname: '/blog', query: router.query });
13 | }, []);
14 |
15 | return (
16 | <>
17 |
18 |
19 | >
20 | );
21 | }
22 |
23 | export default BlogIndex;
24 |
--------------------------------------------------------------------------------
/pages/jobs.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'components/head';
2 | import HeroBanner from 'components/HeroBanner/HeroBanner';
3 | import Content from 'components/Content/Content';
4 | import FeaturedJobsData from 'components/FeaturedJobItem/featuredJobs.json';
5 | import FeaturedJobItem from 'components/FeaturedJobItem/FeaturedJobItem';
6 | import ZipRecruiterJobs from 'components/ZipRecruiterJobs/ZipRecruiterJobs';
7 |
8 | const pageTitle = 'Jobs';
9 |
10 | function Jobs() {
11 | return (
12 | <>
13 |
14 |
15 |
16 |
17 | job.status === 'active').map(job => (
22 |
23 | ))}
24 | />
25 |
26 | ]}
31 | />
32 | >
33 | );
34 | }
35 |
36 | export default Jobs;
37 |
--------------------------------------------------------------------------------
/pages/join/form.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'components/head';
2 | import HeroBanner from 'components/HeroBanner/HeroBanner';
3 | import Content from 'components/Content/Content';
4 | import UpdateProfileForm from 'components/Forms/UpdateProfileForm/UpdateProfileForm';
5 | import type { NextPage } from 'next';
6 | import nextCookie from 'next-cookies';
7 |
8 | const pageTitle = 'Update Profile';
9 |
10 | const UpdateProfile: NextPage = () => {
11 | return (
12 | <>
13 |
14 |
15 |
16 |
17 | ]} />
18 | >
19 | );
20 | };
21 |
22 | UpdateProfile.getInitialProps = async ctx => {
23 | const { opCodeApplicantEmail } = nextCookie(ctx);
24 |
25 | // Redirect if server cookie is missing applicant email!
26 | if (!opCodeApplicantEmail) {
27 | if (ctx.res) {
28 | ctx.res.writeHead(302, { Location: '/' });
29 | ctx.res.end();
30 | }
31 | }
32 |
33 | return {};
34 | };
35 |
36 | export default UpdateProfile;
37 |
--------------------------------------------------------------------------------
/pages/join/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useRouter } from 'next/router';
3 | import { gtag } from 'common/utils/thirdParty/gtag';
4 | import Head from 'components/head';
5 | import HeroBanner from 'components/HeroBanner/HeroBanner';
6 | import Content from 'components/Content/Content';
7 | import { RegistrationForm } from 'components/Forms/RegistrationForm/RegistrationForm';
8 |
9 | const pageTitle = 'Join';
10 |
11 | const profileUpdateURL = '/join/form';
12 |
13 | export default function Join() {
14 | const { prefetch, push } = useRouter();
15 |
16 | useEffect(() => {
17 | prefetch(profileUpdateURL);
18 | }, []);
19 |
20 | const handleSuccess = () => {
21 | gtag.conversionEvent({ adId: '9ZvVCOOFmrkBEK-Rnp4D', category: 'sign_up' });
22 | push(profileUpdateURL);
23 | };
24 |
25 | return (
26 | <>
27 |
28 |
29 |
30 |
31 | ]} />
32 | >
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/pages/join/success.tsx:
--------------------------------------------------------------------------------
1 | import { SUCCESS_PAGE_MESSAGE } from 'common/constants/testIDs';
2 | import LinkButton from 'components/Buttons/LinkButton/LinkButton';
3 | import Head from 'components/head';
4 | import HeroBanner from 'components/HeroBanner/HeroBanner';
5 | import OutboundLink from 'components/OutboundLink/OutboundLink';
6 |
7 | const pageTitle = `Successful Registration!`;
8 |
9 | export default function JoinSuccess() {
10 | return (
11 | <>
12 |
13 |
14 |
15 |
16 | We will review your application and send an invite to our Slack team as soon as possible.
17 | If you do not receive an invite within a week, please email us at{' '}
18 |
23 | staff@operationcode.org
24 |
25 | .
26 |
27 |
28 |
29 | Go Home
30 |
31 |
32 | >
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/pages/thank_you.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import Head from 'components/head';
3 | import HeroBanner from 'components/HeroBanner/HeroBanner';
4 | import { gtag } from 'common/utils/thirdParty/gtag';
5 | import styles from 'styles/thank_you.module.css';
6 |
7 | const pageTitle = 'Thank You';
8 |
9 | function ThankYou() {
10 | useEffect(() => {
11 | gtag.conversionEvent({ adId: 'h6epCOC_os4BEK-Rnp4D' });
12 | }, []);
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | Thank you so much for your donation!
21 |
22 |
23 | We have received your contribution, and you should be receiving an email which will serve
24 | as your donation receipt.
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | export default ThankYou;
32 |
--------------------------------------------------------------------------------
/pathAliases.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | common: path.resolve('./common'),
5 | components: path.resolve('./components'),
6 | pages: path.resolve('./pages'),
7 | public: path.resolve('./public'),
8 | scripts: path.resolve('./scripts'),
9 | static: path.resolve('./public/static'),
10 | 'test-utils': path.resolve('./test-utils'),
11 | };
12 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | 'postcss-prepend-imports': {
6 | path: 'common/styles',
7 | files: ['media-queries.css'],
8 | },
9 | 'postcss-import': {},
10 | 'postcss-custom-media': {},
11 | 'postcss-custom-properties': {
12 | importFrom: './common/styles/variables.css',
13 | preserve: false,
14 | disableDeprecationNotice: true,
15 | },
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'avoid',
3 | printWidth: 100,
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | };
7 |
--------------------------------------------------------------------------------
/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 | #f0f2f2
3 |
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/favicon-96x96.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/favicon.ico
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/favicon.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Operation Code",
3 | "short_name": "Operation Code",
4 | "start_url": "/",
5 | "display": "standalone",
6 | "fallback": "index.html",
7 | "theme_color": "#249cbc",
8 | "background_color": "#f0f2f2",
9 | "orientation": "any",
10 | "icons": [
11 | {
12 | "src": "/icon-36x36.png",
13 | "sizes": "36x36",
14 | "type": "image/png",
15 | "density": "0.75"
16 | },
17 | {
18 | "src": "/icon-48x48.png",
19 | "sizes": "48x48",
20 | "type": "image/png",
21 | "density": "1.0"
22 | },
23 | {
24 | "src": "/icon-72x72.png",
25 | "sizes": "72x72",
26 | "type": "image/png",
27 | "density": "1.5"
28 | },
29 | {
30 | "src": "/icon-96x96.png",
31 | "sizes": "96x96",
32 | "type": "image/png",
33 | "density": "2.0"
34 | },
35 | {
36 | "src": "/icon-144x144.png",
37 | "sizes": "144x144",
38 | "type": "image/png",
39 | "density": "3.0"
40 | },
41 | {
42 | "src": "/icon-192x192.png",
43 | "sizes": "192x192",
44 | "type": "image/png",
45 | "density": "4.0"
46 | }
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /404.html
3 | Disallow: /employers.html
4 | Disallow: /password_reset.html
5 | Disallow: /thank_you.html
6 |
7 | Sitemap: https://www.operationcode.org/sitemap.xml
8 |
--------------------------------------------------------------------------------
/public/static/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-120x120.png
--------------------------------------------------------------------------------
/public/static/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-144x144.png
--------------------------------------------------------------------------------
/public/static/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-152x152.png
--------------------------------------------------------------------------------
/public/static/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-180x180.png
--------------------------------------------------------------------------------
/public/static/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-57x57.png
--------------------------------------------------------------------------------
/public/static/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-60x60.png
--------------------------------------------------------------------------------
/public/static/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-72x72.png
--------------------------------------------------------------------------------
/public/static/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-76x76.png
--------------------------------------------------------------------------------
/public/static/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/public/static/fonts/DINCondensed-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/fonts/DINCondensed-Bold.ttf
--------------------------------------------------------------------------------
/public/static/fonts/DINCondensed-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/fonts/DINCondensed-Bold.woff
--------------------------------------------------------------------------------
/public/static/fonts/DINCondensed-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/fonts/DINCondensed-Bold.woff2
--------------------------------------------------------------------------------
/public/static/icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/icon-144x144.png
--------------------------------------------------------------------------------
/public/static/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/icon-192x192.png
--------------------------------------------------------------------------------
/public/static/icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/icon-36x36.png
--------------------------------------------------------------------------------
/public/static/icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/icon-48x48.png
--------------------------------------------------------------------------------
/public/static/icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/icon-72x72.png
--------------------------------------------------------------------------------
/public/static/icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/icon-96x96.png
--------------------------------------------------------------------------------
/public/static/images/AngelHack-Boston.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/AngelHack-Boston.jpg
--------------------------------------------------------------------------------
/public/static/images/CodeConferenceLA.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/CodeConferenceLA.jpg
--------------------------------------------------------------------------------
/public/static/images/Family1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/Family1.jpg
--------------------------------------------------------------------------------
/public/static/images/Node-Summit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/Node-Summit.jpg
--------------------------------------------------------------------------------
/public/static/images/RedHat-Summit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/RedHat-Summit.jpg
--------------------------------------------------------------------------------
/public/static/images/TankFlip.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/TankFlip.gif
--------------------------------------------------------------------------------
/public/static/images/Utah-Meetup.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/Utah-Meetup.jpg
--------------------------------------------------------------------------------
/public/static/images/bias1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/bias1.jpg
--------------------------------------------------------------------------------
/public/static/images/bias2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/bias2.jpg
--------------------------------------------------------------------------------
/public/static/images/bias3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/bias3.jpg
--------------------------------------------------------------------------------
/public/static/images/bias4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/bias4.jpg
--------------------------------------------------------------------------------
/public/static/images/bias5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/bias5.jpg
--------------------------------------------------------------------------------
/public/static/images/civic-x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/civic-x.png
--------------------------------------------------------------------------------
/public/static/images/heroImage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/heroImage.jpg
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/bullseye.svg:
--------------------------------------------------------------------------------
1 | Bullseye Icon
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/chart.svg:
--------------------------------------------------------------------------------
1 | Chart Icon
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/check-circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/equipment.svg:
--------------------------------------------------------------------------------
1 | Equipment Icon
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/housing.svg:
--------------------------------------------------------------------------------
1 | Housing Icon
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/man_holding_key.svg:
--------------------------------------------------------------------------------
1 | Man Holding Key
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/networked_people.svg:
--------------------------------------------------------------------------------
1 | Networked People Symbol
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/online.svg:
--------------------------------------------------------------------------------
1 | Online Icon
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/people_meeting.svg:
--------------------------------------------------------------------------------
1 | Three People Meeting
--------------------------------------------------------------------------------
/public/static/images/icons/Custom/x-circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/README.md:
--------------------------------------------------------------------------------
1 | Assets in this folder are thanks to Font Awesome and are licensed via attribution as required: https://fontawesome.com/license/free
2 |
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/angle-left-solid.svg:
--------------------------------------------------------------------------------
1 | Angle Left
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/angle-right-solid.svg:
--------------------------------------------------------------------------------
1 | Angle Right
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/ban-solid.svg:
--------------------------------------------------------------------------------
1 | Not Allowed
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/book-solid.svg:
--------------------------------------------------------------------------------
1 | Book
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/building_icon.svg:
--------------------------------------------------------------------------------
1 | Building
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/bullhorn-solid.svg:
--------------------------------------------------------------------------------
1 | Bullhorn
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/check-solid.svg:
--------------------------------------------------------------------------------
1 | Checkmark
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/chevron-up-solid.svg:
--------------------------------------------------------------------------------
1 | Double Chevron Up
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/clock-regular.svg:
--------------------------------------------------------------------------------
1 | Clock
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/cloud_upload_icon.svg:
--------------------------------------------------------------------------------
1 | Cloud With Up Arrow Inside
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/desktop-solid.svg:
--------------------------------------------------------------------------------
1 | Desktop Device
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/devices-alt-solid.svg:
--------------------------------------------------------------------------------
1 | Devices
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/envelope-solid.svg:
--------------------------------------------------------------------------------
1 | Envelope
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/exclamation-triangle-solid.svg:
--------------------------------------------------------------------------------
1 | Warning Sign
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/external-link-square-alt-solid.svg:
--------------------------------------------------------------------------------
1 | External Link Symbol
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/flag-checkered-solid.svg:
--------------------------------------------------------------------------------
1 | Checkered Flag
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/hand-peace-regular.svg:
--------------------------------------------------------------------------------
1 | Hand Holding Up Peace Sign
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/handshake-regular.svg:
--------------------------------------------------------------------------------
1 | Handshake
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/home-solid.svg:
--------------------------------------------------------------------------------
1 | House
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/life-ring-regular.svg:
--------------------------------------------------------------------------------
1 | Life Ring
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/map_marker_icon.svg:
--------------------------------------------------------------------------------
1 | Map Marker
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/medal-solid.svg:
--------------------------------------------------------------------------------
1 | Medal Solid
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/road-solid.svg:
--------------------------------------------------------------------------------
1 | Road
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/slack-hash.svg:
--------------------------------------------------------------------------------
1 | Slack Logo
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/thumbs-down.svg:
--------------------------------------------------------------------------------
1 | Thumbs Down
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/thumbs-up.svg:
--------------------------------------------------------------------------------
1 | Thumbs Up
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/user-solid.svg:
--------------------------------------------------------------------------------
1 | Person
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/users-solid.svg:
--------------------------------------------------------------------------------
1 | People
--------------------------------------------------------------------------------
/public/static/images/icons/FontAwesome/utensils-solid.svg:
--------------------------------------------------------------------------------
1 | Utensils
--------------------------------------------------------------------------------
/public/static/images/icons/facebook_logo.svg:
--------------------------------------------------------------------------------
1 | Facebook Logo
--------------------------------------------------------------------------------
/public/static/images/icons/gi-bill-approved.svg:
--------------------------------------------------------------------------------
1 | GI Bill Approved
--------------------------------------------------------------------------------
/public/static/images/icons/gi-bill-unavailable.svg:
--------------------------------------------------------------------------------
1 | GI Bill Unavailable
--------------------------------------------------------------------------------
/public/static/images/icons/hamburger.svg:
--------------------------------------------------------------------------------
1 | Hamburger Icon
--------------------------------------------------------------------------------
/public/static/images/icons/javascript_logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/static/images/icons/linkedin_logo.svg:
--------------------------------------------------------------------------------
1 | LinkedIn Logo
--------------------------------------------------------------------------------
/public/static/images/icons/linkedin_logo_circle.svg:
--------------------------------------------------------------------------------
1 | LinkedIn Logo
--------------------------------------------------------------------------------
/public/static/images/icons/minus.svg:
--------------------------------------------------------------------------------
1 | Minus Symbol
--------------------------------------------------------------------------------
/public/static/images/icons/operation_code_logo.svg:
--------------------------------------------------------------------------------
1 | Operation Code Logo
--------------------------------------------------------------------------------
/public/static/images/icons/pinterest_logo.svg:
--------------------------------------------------------------------------------
1 | Pinterest Logo
--------------------------------------------------------------------------------
/public/static/images/icons/plus.svg:
--------------------------------------------------------------------------------
1 | Plus Symbol
--------------------------------------------------------------------------------
/public/static/images/icons/slack_logo.svg:
--------------------------------------------------------------------------------
1 | Slack Logo
--------------------------------------------------------------------------------
/public/static/images/icons/twitter_logo.svg:
--------------------------------------------------------------------------------
1 | Twitter Logo
--------------------------------------------------------------------------------
/public/static/images/icons/youtube_logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/static/images/moocs/edx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/moocs/edx.jpg
--------------------------------------------------------------------------------
/public/static/images/moocs/treehouse.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/moocs/treehouse.jpg
--------------------------------------------------------------------------------
/public/static/images/moocs/udacity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/moocs/udacity.jpg
--------------------------------------------------------------------------------
/public/static/images/platinum-transparency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/images/platinum-transparency.png
--------------------------------------------------------------------------------
/public/static/images/vercel.svg:
--------------------------------------------------------------------------------
1 | Vercel Logo
--------------------------------------------------------------------------------
/public/static/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/ms-icon-144x144.png
--------------------------------------------------------------------------------
/public/static/ms-icon-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/ms-icon-150x150.png
--------------------------------------------------------------------------------
/public/static/ms-icon-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/ms-icon-310x310.png
--------------------------------------------------------------------------------
/public/static/ms-icon-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/public/static/ms-icon-70x70.png
--------------------------------------------------------------------------------
/scripts/README.md:
--------------------------------------------------------------------------------
1 | # Scripts
2 |
3 | This should probably be called `bin`, but this folder is for local development scripts only. They
4 | will not be available for deployments as they ignored in `.vercelignore`, so don't consume the files
5 | within via source code.
6 |
--------------------------------------------------------------------------------
/scripts/createPage/builders.js:
--------------------------------------------------------------------------------
1 | const camelCase = require('lodash/camelCase');
2 | const upperFirst = require('lodash/upperFirst');
3 |
4 | // example LikeThisCase
5 | const pascalCase = someString => upperFirst(camelCase(someString));
6 |
7 | const builder = {
8 | buildJS: pageTitle => {
9 | const componentName = pascalCase(pageTitle);
10 |
11 | return `import Head from 'components/head';
12 | import HeroBanner from 'components/HeroBanner/HeroBanner';
13 | // customize styles by creating and importing a stylesheet in the ~/styles folder
14 |
15 | const pageTitle = '${pageTitle}';
16 |
17 | function ${componentName}() {
18 | return (
19 | <>
20 |
21 |
22 |
23 | {/* Don't forget to define the imageSource prop in the HeroBanner Component */}
24 | {/* Call-to-action goes here */}
25 |
26 |
27 | {/* Rest of page content goes in here */}
28 | >
29 | );
30 | }
31 |
32 | export default ${componentName};
33 | `;
34 | },
35 | };
36 |
37 | module.exports = builder;
38 |
--------------------------------------------------------------------------------
/sentry.client.config.js:
--------------------------------------------------------------------------------
1 | // This file configures the initialization of Sentry on the browser.
2 | // The config you add here will be used whenever a page is visited.
3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/
4 |
5 | import * as Sentry from '@sentry/nextjs';
6 |
7 | const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
8 |
9 | Sentry.init({
10 | dsn: SENTRY_DSN || 'https://90edfb8d1d9640cf86d8aefd57218d71@o68895.ingest.sentry.io/1443656',
11 | // Adjust this value in production, or use tracesSampler for greater control
12 | tracesSampleRate: 1,
13 | release: 'client_2.0.0',
14 | enabled: !!process.env.SENTRY_AUTH_TOKEN,
15 |
16 | // ...
17 | // Note: if you want to override the automatic release value, do not set a
18 | // `release` value here - use the environment variable `SENTRY_RELEASE`, so
19 | // that it will also get attached to your source maps
20 | });
21 |
--------------------------------------------------------------------------------
/sentry.properties:
--------------------------------------------------------------------------------
1 | defaults.url=https://sentry.io/
2 | defaults.org=operation-code
3 | defaults.project=front-end
4 | cli.executable=../../../.npm/_npx/a8388072043b4cbc/node_modules/@sentry/cli/bin/sentry-cli
5 |
--------------------------------------------------------------------------------
/sentry.server.config.js:
--------------------------------------------------------------------------------
1 | // This file configures the initialization of Sentry on the server.
2 | // The config you add here will be used whenever the server handles a request.
3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/
4 |
5 | import * as Sentry from '@sentry/nextjs';
6 |
7 | const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
8 |
9 | Sentry.init({
10 | dsn: SENTRY_DSN || 'https://90edfb8d1d9640cf86d8aefd57218d71@o68895.ingest.sentry.io/1443656',
11 | // Adjust this value in production, or use tracesSampler for greater control
12 | tracesSampleRate: 1,
13 | release: 'server_2.0.0',
14 | enabled: !!process.env.SENTRY_AUTH_TOKEN,
15 |
16 | // ...
17 | // Note: if you want to override the automatic release value, do not set a
18 | // `release` value here - use the environment variable `SENTRY_RELEASE`, so
19 | // that it will also get attached to your source maps
20 | });
21 |
--------------------------------------------------------------------------------
/styles/about.module.css:
--------------------------------------------------------------------------------
1 | .About .hero {
2 | background-position: top center;
3 | }
4 |
5 | .About .imageCard h6 {
6 | font-size: 1.125rem;
7 | }
8 |
9 | .About .imageCard p {
10 | font-size: 85%;
11 | }
12 |
--------------------------------------------------------------------------------
/styles/branding.module.css:
--------------------------------------------------------------------------------
1 | .Branding .topMargin {
2 | margin-top: 1rem;
3 | }
4 |
5 | .Branding p {
6 | margin: 1rem 0;
7 | }
8 |
--------------------------------------------------------------------------------
/styles/challenge.module.css:
--------------------------------------------------------------------------------
1 | /* On this component, all non-classed images are inline */
2 | .Challenge .instructionList img {
3 | max-width: 100%;
4 | height: auto;
5 | padding: 0 10px;
6 | }
7 |
8 | .Challenge .instructionList {
9 | line-height: 1.5;
10 | padding: 0 0 0 5px;
11 | margin: 0;
12 | }
13 |
14 | .Challenge .instructionList li {
15 | padding: 0 0 25px 0;
16 | }
17 |
18 | .Challenge .blockImage {
19 | padding: 0 !important;
20 | }
21 |
22 | .Challenge .centerText {
23 | text-align: center;
24 | }
25 |
26 | .Challenge .challengerListColumn {
27 | padding: 0;
28 | align-self: flex-start;
29 | flex-basis: 225px;
30 | list-style-position: inside;
31 | }
32 |
33 | .Challenge .challengerListContainer div {
34 | width: 85vw;
35 | margin: 0;
36 | }
37 |
--------------------------------------------------------------------------------
/styles/chapter_leader.module.css:
--------------------------------------------------------------------------------
1 | .ChapterLeader .fullWidth {
2 | width: 100%;
3 | max-width: 100%;
4 | }
5 |
--------------------------------------------------------------------------------
/styles/contact.module.css:
--------------------------------------------------------------------------------
1 | .Contact .verticalSpacing {
2 | margin: 1rem 0;
3 | }
4 |
5 | .Contact .contactHero a {
6 | transition: color 0.2s linear;
7 | }
8 |
9 | .Contact .contactHero a:focus-visible,
10 | .Contact .contactHero a:hover {
11 | text-shadow: none;
12 | color: var(--primary);
13 | }
14 |
15 | .Contact .contactMethod {
16 | margin: 0 auto 2rem;
17 | max-width: var(--typicalMaxWidth);
18 | font-style: normal;
19 | }
20 |
21 | .Contact .forceWidth {
22 | width: 100%;
23 | }
24 |
--------------------------------------------------------------------------------
/styles/donate.module.css:
--------------------------------------------------------------------------------
1 | .explainer {
2 | border-bottom: 4px solid var(--secondary);
3 | margin-bottom: 1.6rem;
4 | }
5 |
6 | .donateForm {
7 | border: 1px solid transparent;
8 | outline: 1px solid transparent;
9 | max-width: 70ch;
10 | height: 1500px;
11 | width: 100%;
12 | }
13 |
--------------------------------------------------------------------------------
/styles/faq.module.css:
--------------------------------------------------------------------------------
1 | .FAQ .FAQAccordion {
2 | background-color: rgba(0, 0, 0, 0);
3 | border-radius: 5px;
4 | border: 1px solid #c5c5c5;
5 | box-shadow: none;
6 | color: var(--white);
7 | display: block;
8 | padding: 1.125rem;
9 | width: 100%;
10 | }
11 |
12 | .FAQ .FAQAccordion > button > svg {
13 | fill: currentcolor;
14 | top: 1.25rem;
15 | }
16 |
--------------------------------------------------------------------------------
/styles/history.module.css:
--------------------------------------------------------------------------------
1 | .History .hero {
2 | background-position: right center;
3 | }
4 |
5 | .History .quote {
6 | font-size: 1.5rem;
7 | }
8 |
9 | .History .author {
10 | font-size: 1.75rem;
11 | float: right;
12 | }
13 |
--------------------------------------------------------------------------------
/styles/index.module.css:
--------------------------------------------------------------------------------
1 | .Home .hero {
2 | padding: 0;
3 | }
4 |
5 | .heroText {
6 | padding: 0 1rem;
7 | }
8 |
9 | .Home .ctaContainer {
10 | display: flex;
11 | width: 100%;
12 | max-width: var(--typicalMaxWidth);
13 | justify-content: space-evenly;
14 | flex-wrap: wrap;
15 | column-gap: 0.5rem;
16 | }
17 |
18 | .Home .ctaContainer > * {
19 | margin-top: 1rem;
20 | }
21 |
22 | .Home .mission {
23 | display: flex;
24 | flex-direction: column;
25 | }
26 |
27 | .Home .successStories {
28 | display: flex;
29 | flex-flow: row wrap;
30 | justify-content: space-around;
31 | }
32 |
33 | .Home .partnerLogos {
34 | display: flex;
35 | align-self: center;
36 | justify-content: center;
37 | flex-flow: row wrap;
38 | max-width: var(--typicalMaxWidth);
39 | }
40 |
41 | .Home .joinThrivingSection {
42 | display: flex;
43 | align-self: center;
44 | justify-content: space-between;
45 | flex-direction: column;
46 | }
47 |
--------------------------------------------------------------------------------
/styles/kc.module.css:
--------------------------------------------------------------------------------
1 | .KC .hero {
2 | min-height: 0;
3 | padding-top: 6rem;
4 | }
5 |
6 | .KC .info {
7 | text-align: center;
8 | width: 100%;
9 | }
10 |
11 | .KC .podcastThumbnailList {
12 | display: grid;
13 | grid-template-columns: 1fr 1fr;
14 | grid-template-rows: 1fr;
15 | gap: 3rem;
16 | list-style: none;
17 | padding: 0;
18 | margin: 0;
19 | }
20 |
21 | @media screen and (--small-viewport) {
22 | .KC .podcastThumbnailList {
23 | grid-template-columns: 1fr;
24 | }
25 | }
26 |
27 | .KC .podcastThumbnailListItem {
28 | display: flex;
29 | flex-direction: column;
30 | align-items: center;
31 | justify-content: center;
32 | text-align: center;
33 | }
34 |
35 | .KC .podcastThumbnailListItem a {
36 | text-decoration: none;
37 | }
38 |
39 | .KC .podcastThumbnailListItem a:hover {
40 | text-decoration: underline;
41 | }
42 |
--------------------------------------------------------------------------------
/styles/password_reset.module.css:
--------------------------------------------------------------------------------
1 | .PasswordReset .centerAlign {
2 | text-align: center;
3 | }
4 |
--------------------------------------------------------------------------------
/styles/podcast.module.css:
--------------------------------------------------------------------------------
1 | .Podcast .hero {
2 | min-height: 0;
3 | padding-top: 7rem;
4 | margin-bottom: -4rem;
5 | }
6 |
7 | .Podcast .podcastCards {
8 | display: flex;
9 | justify-content: center;
10 | align-items: flex-start;
11 | flex-wrap: wrap;
12 | }
13 |
14 | .Podcast .podcastCard {
15 | align-items: center;
16 | display: flex;
17 | flex-flow: column;
18 | justify-content: center;
19 | overflow: hidden; /* prevent devs from bloating content */
20 | margin: 1.5rem;
21 | width: 500px;
22 | }
23 |
24 | .Podcast .podcastCard p {
25 | overflow-y: scroll;
26 | height: 250px;
27 | }
28 |
29 | @media screen and (--small-viewport) {
30 | .Podcast .podcastCard {
31 | width: 100%;
32 | }
33 | }
34 |
35 | .Podcast .img {
36 | max-height: 200px;
37 | max-width: 100%;
38 | object-fit: cover;
39 | }
40 |
--------------------------------------------------------------------------------
/styles/policy.module.css:
--------------------------------------------------------------------------------
1 | .Policy .statisticContainer {
2 | display: flex;
3 | flex-direction: column;
4 | font-family: var(--primaryFontFamily), sans-serif;
5 | margin-top: 3em;
6 | }
7 |
8 | .Policy .statistic {
9 | background-color: rgba(0, 5, 30, 0.7);
10 | display: flex;
11 | margin: 0;
12 | padding: 0.5rem;
13 | max-width: 500px;
14 | }
15 |
16 | .Policy .statistic + .statistic {
17 | margin-top: 1rem;
18 | }
19 |
20 | .Policy .shortenedStatistic {
21 | max-width: 370px;
22 | }
23 |
24 | .Policy .statisticNumber {
25 | display: flex;
26 | align-items: center;
27 | flex-grow: 1;
28 | flex-shrink: 0;
29 | color: var(--primary);
30 | font-size: 2.5rem;
31 | line-height: 2.5rem;
32 | padding: 0 1rem 0 0.5rem;
33 | }
34 |
35 | @media screen and (--large-viewport) {
36 | .Policy .statisticNumber {
37 | font-size: 2rem;
38 | line-height: 2rem;
39 | }
40 | }
41 |
42 | @media screen and (--medium-viewport) {
43 | .Policy .statisticNumber {
44 | flex-grow: 0;
45 | }
46 | }
47 |
48 | .Policy .statisticDescription {
49 | color: var(--white);
50 | font-size: 1.5rem;
51 | line-height: 1.7rem;
52 | }
53 |
--------------------------------------------------------------------------------
/styles/press.module.css:
--------------------------------------------------------------------------------
1 | .Press .row {
2 | display: flex;
3 | flex-wrap: wrap;
4 | justify-content: center;
5 | }
6 |
7 | .Press .column {
8 | flex: 33%;
9 | }
10 |
11 | @media screen and (--large-viewport) {
12 | .Press .column {
13 | flex: 50%;
14 | }
15 | }
16 |
17 | @media screen and (--medium-viewport) {
18 | .Press .column {
19 | flex: 100%;
20 | }
21 | }
22 |
23 | .Press .textGrouping {
24 | margin: 1rem;
25 | }
26 |
27 | .Press .logos {
28 | padding-top: 15px;
29 | display: flex;
30 | flex-flow: row wrap;
31 | justify-content: space-around;
32 | align-items: center;
33 | }
34 |
35 | .Press .logos > .imgBox {
36 | padding: 25px;
37 | height: auto;
38 | width: 45%;
39 | max-width: 560px;
40 | }
41 |
42 | .Press .logos a {
43 | color: var(--primary);
44 | text-decoration: none;
45 | }
46 |
47 | @media screen and (--extra-large-viewport) {
48 | .Press .logos > .imgBox {
49 | width: 350px;
50 | }
51 | }
52 |
53 | @media screen and (--extra-small-viewport) {
54 | .Press .logos > .imgBox {
55 | width: 300px;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/styles/profile.module.css:
--------------------------------------------------------------------------------
1 | .Profile .actionItems {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | width: 100%;
6 | margin: auto;
7 | text-align: center;
8 | }
9 |
10 | .Profile .actionItems > a {
11 | margin: 1rem;
12 | }
13 |
--------------------------------------------------------------------------------
/styles/project_rebuild.module.css:
--------------------------------------------------------------------------------
1 | .logosSection {
2 | margin-bottom: 0;
3 | }
4 |
5 | .logos {
6 | display: flex;
7 | flex-wrap: wrap;
8 | align-items: center;
9 | justify-content: center;
10 | gap: 4rem;
11 | margin-bottom: -2rem;
12 | }
13 |
14 | @media screen and (--large-viewport) {
15 | .logos {
16 | gap: 3rem;
17 | }
18 |
19 | .logos > *:nth-child(3) {
20 | order: 3;
21 | }
22 |
23 | .logos > *:nth-child(2) {
24 | order: 1;
25 | }
26 |
27 | .logos > *:nth-child(1) {
28 | order: 2;
29 | }
30 | }
31 |
32 | .bold {
33 | font-weight: bold;
34 | }
35 |
36 | .donateLinkContainer {
37 | display: flex;
38 | width: 300px;
39 | }
40 |
41 | .donateLinkButtonContainer {
42 | width: 100%;
43 | margin: 2rem auto 0;
44 | text-align: center;
45 | }
46 |
--------------------------------------------------------------------------------
/styles/services.module.css:
--------------------------------------------------------------------------------
1 | .Services .centeredText {
2 | text-align: center;
3 | }
4 |
5 | .Services .topMargin {
6 | margin-top: 2.5rem;
7 | }
8 |
9 | .Services .badgeGroupings {
10 | display: flex;
11 | flex-wrap: wrap;
12 | justify-content: center;
13 | margin-top: -1rem;
14 | }
15 |
16 | :global(#__next) .Services .badgeGroupings .badge {
17 | fill: var(--secondary);
18 | margin: 1rem 4rem;
19 | }
20 |
--------------------------------------------------------------------------------
/styles/slack_guide.module.css:
--------------------------------------------------------------------------------
1 | .FAQ .FAQAccordion {
2 | background-color: rgba(0, 0, 0, 0);
3 | border-radius: 5px;
4 | border: 1px solid #c5c5c5;
5 | box-shadow: none;
6 | color: var(--white);
7 | display: block;
8 | padding: 1.125rem;
9 | width: 100%;
10 | }
11 |
12 | .FAQ .FAQAccordion > button > svg {
13 | fill: currentcolor;
14 | top: 0.825rem;
15 | }
16 |
17 | @media screen and (--small-viewport) {
18 | .slackVideo {
19 | height: 100%;
20 | width: 100%;
21 | }
22 |
23 | .exploreChannels,
24 | .joinChannels {
25 | width: 100%;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/styles/sponsorship.module.css:
--------------------------------------------------------------------------------
1 | .Sponsorship .constrainDimensions {
2 | justify-content: flex-start;
3 | max-width: 350px;
4 | height: 250px;
5 | }
6 |
7 | .Sponsorship .badgeFormat {
8 | font-size: 1.5rem;
9 | font-weight: 550;
10 | margin-top: 1.5rem;
11 | }
12 |
13 | .Sponsorship .gold {
14 | color: #ffaa22;
15 | }
16 |
17 | .Sponsorship .silver {
18 | color: silver;
19 | }
20 |
21 | .Sponsorship .bronze {
22 | color: #cd7f32;
23 | }
24 |
25 | .Sponsorship .paragraphFormat {
26 | padding: 0 2rem;
27 | }
28 |
--------------------------------------------------------------------------------
/styles/team.module.css:
--------------------------------------------------------------------------------
1 | .Team .hero {
2 | background-position: center 30%;
3 | }
4 |
5 | .Team .teamMembers {
6 | display: flex;
7 | align-items: flex-start;
8 | justify-content: center;
9 | flex-wrap: wrap;
10 | width: 100%;
11 | }
12 |
13 | .Team .teamMembers img {
14 | object-fit: cover;
15 | }
16 |
17 | .Team .foundingMembers {
18 | padding: 2rem 0 0 0;
19 | }
20 |
21 | .textAlignCenter {
22 | text-align: center;
23 | }
24 |
--------------------------------------------------------------------------------
/styles/thank_you.module.css:
--------------------------------------------------------------------------------
1 | .ThankYou .main {
2 | padding-bottom: 2.25rem;
3 | }
4 |
5 | .ThankYou .main p {
6 | text-align: left;
7 | }
8 |
--------------------------------------------------------------------------------
/test-utils/createComponentInstance.js:
--------------------------------------------------------------------------------
1 | import TestRenderer from 'react-test-renderer';
2 |
3 | export default function createComponentInstance(Component) {
4 | const root = TestRenderer.create(Component);
5 | return root.getInstance();
6 | }
7 |
--------------------------------------------------------------------------------
/test-utils/createShallowSnapshotTest.js:
--------------------------------------------------------------------------------
1 | import ShallowRenderer from 'react-test-renderer/shallow';
2 |
3 | /**
4 | * Used to create a snapshot test off of surface leaves of the tree.
5 | * Useful for regression tests on containers, components utilizing something that requires large
6 | * mocking, or very large components.
7 | *
8 | * @export
9 | * @param {*} Component
10 | */
11 | export default Component => {
12 | const shallowRenderer = new ShallowRenderer();
13 | const tree = shallowRenderer.render(Component);
14 |
15 | expect(tree).toMatchSnapshot();
16 | };
17 |
--------------------------------------------------------------------------------
/test-utils/createSnapshotTest.js:
--------------------------------------------------------------------------------
1 | import renderer from 'react-test-renderer';
2 |
3 | /**
4 | * Used to create fully-rendered snapshot test.
5 | * Useful for reusable component smoke tests.
6 | *
7 | * @export
8 | * @param {*} Component
9 | */
10 | export default Component => {
11 | const tree = renderer.create(Component).toJSON();
12 | expect(tree).toMatchSnapshot();
13 | };
14 |
--------------------------------------------------------------------------------
/test-utils/flushPromises.js:
--------------------------------------------------------------------------------
1 | // Flush all promsises in a test so that Jest won't finish before they are handled
2 | // Taken from: https://github.com/facebook/jest/issues/2157#issuecomment-279171856
3 | const flushPromises = () => new Promise(resolve => setImmediate(resolve));
4 |
5 | export default flushPromises;
6 |
--------------------------------------------------------------------------------
/test-utils/identifiers.js:
--------------------------------------------------------------------------------
1 | const tabKeyCode = { key: 'Tab', keycode: 9 };
2 |
3 | export const KEY_CODES = {
4 | ENTER: { key: 'Enter', keyCode: 13 },
5 | BACKSPACE: { key: 'Backspace', keyCode: 8 },
6 | TAB: tabKeyCode,
7 | TAB_AND_SHIFT: { ...tabKeyCode, shiftKey: true },
8 | DOWN_ARROW: { key: 'ArrowDown', keyCode: 40 },
9 | };
10 |
--------------------------------------------------------------------------------
/test-utils/jest-next-image.js:
--------------------------------------------------------------------------------
1 | const { s3hostName } = require('../common/constants/urls');
2 |
3 | process.env = {
4 | ...process.env,
5 | __NEXT_IMAGE_OPTS: {
6 | deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
7 | imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
8 | domains: [s3hostName, 'user-images.githubusercontent.com'],
9 | path: '/',
10 | loader: 'default',
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/test-utils/mocks/apiMock.js:
--------------------------------------------------------------------------------
1 | import MockAdapter from 'axios-mock-adapter';
2 | import { OperationCodeAPI } from 'common/utils/api-utils';
3 |
4 | const OperationCodeAPIMock = new MockAdapter(OperationCodeAPI);
5 |
6 | export default OperationCodeAPIMock;
7 |
--------------------------------------------------------------------------------
/test-utils/mocks/existingUser.js:
--------------------------------------------------------------------------------
1 | export default {
2 | email: 'kylemh.email12@gmail.com',
3 | firstName: 'Kyle',
4 | lastName: 'Holmberg',
5 | zipcode: '97214',
6 | };
7 |
--------------------------------------------------------------------------------
/test-utils/mocks/jwtMock.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 | /* eslint-disable max-len */
3 |
4 | // JWT with an expiration set to Fri Jan 11 2143
5 | export const VALID_AUTH_TOKEN =
6 | 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InRlc3Q1NUB0ZXN0LnRlc3QiLCJmaXJzdE5hbWUiOiJUZXN0IiwibGFzdE5hbWUiOiJUZXN0ZXJzb24iLCJ6aXBjb2RlIjoiMTExMTEiLCJpc01lbnRvciI6ZmFsc2UsImV4cCI6NTQ2MDI2MDA0NCwib3JpZ19pYXQiOjE1NjAyNTY0NDR9._XI7rnMB1X1zpj960LFKYQZZWjJSxur1sgMYFlav9Bc';
7 |
--------------------------------------------------------------------------------
/test-utils/mocks/nextRouterMock.js:
--------------------------------------------------------------------------------
1 | import { action } from '@storybook/addon-actions';
2 |
3 | /* Necessary to mock Next's router */
4 | // https://github.com/vercel/next.js/issues/1827#issuecomment-323721221
5 | const actionWithPromise = () => {
6 | action('clicked link')();
7 | // we need to return promise because it is needed by Link.linkClicked
8 | return new Promise((resolve, reject) => reject());
9 | };
10 |
11 | const mockedRouter = {
12 | pathname: 'mock-path',
13 | prefetch: () => {},
14 | push: actionWithPromise,
15 | replace: actionWithPromise,
16 | route: '/mock-route',
17 | };
18 |
19 | export default mockedRouter;
20 |
--------------------------------------------------------------------------------
/test-utils/mocks/passwordResetMock.js:
--------------------------------------------------------------------------------
1 | export const uid = 'Mjg';
2 | export const token = '56m-53bcc9ef792551752cd3';
3 |
--------------------------------------------------------------------------------
/test-utils/mocks/svgMock.js:
--------------------------------------------------------------------------------
1 | const { createElement } = require('react');
2 |
3 | const Svg = props => createElement('svg', props, null);
4 |
5 | module.exports = {
6 | __esModule: true,
7 | default: Svg,
8 | };
9 |
--------------------------------------------------------------------------------
/test-utils/mocks/testFileMock.js:
--------------------------------------------------------------------------------
1 | module.exports = 'test-file-mock';
2 |
--------------------------------------------------------------------------------
/test-utils/transforms/file.js:
--------------------------------------------------------------------------------
1 | 'use strict'; // eslint-disable-line strict, lines-around-directive
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 | module.exports = {
8 | process(source, filename) {
9 | const assetFilename = JSON.stringify(path.basename(filename));
10 |
11 | return `module.exports = ${assetFilename};`;
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/test-utils/wait.js:
--------------------------------------------------------------------------------
1 | export default (ms = 0) => new Promise(resolve => setTimeout(resolve, ms));
2 |
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OperationCode/front-end/310e3c409e6db848d2f76fda9be6d24ebdfb5b82/tsconfig.test.json
--------------------------------------------------------------------------------
/types/global.d.ts:
--------------------------------------------------------------------------------
1 | import type { ReactElement } from 'react';
2 |
3 | declare global {
4 | type RenderableChild = string | number | ReactElement;
5 | }
6 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'react-scroll-up-button';
2 | declare module 'faker';
3 | declare module 'react-scroll';
4 |
--------------------------------------------------------------------------------
/utils/types.ts:
--------------------------------------------------------------------------------
1 | export const objectKeys = (value: Type) =>
2 | Object.keys(value) as (keyof Type)[];
3 |
--------------------------------------------------------------------------------
/vitest.setup.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { vi, beforeAll } from 'vitest';
3 |
4 | const MockedNextImage = ({ src, alt }: { src: string; alt: string }) => ;
5 |
6 | /* MOCKS */
7 | vi.mock('next/image', () => ({ default: MockedNextImage }));
8 | vi.importMock('common/utils/thirdParty/gtag');
9 |
10 | beforeAll(async () => {
11 | const IntersectionObserverMock = vi.fn(() => ({
12 | disconnect: vi.fn(),
13 | observe: vi.fn(),
14 | takeRecords: vi.fn(),
15 | unobserve: vi.fn(),
16 | }));
17 |
18 | vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);
19 | });
20 |
21 | beforeEach(() => {
22 | vi.clearAllMocks();
23 | });
24 |
--------------------------------------------------------------------------------