├── .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 => 15 | `; 16 | 17 | exports[`Button > should render with required props 1`] = ` 18 | 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 | 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 | 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 |
8 |

Column 1

9 |
, 10 |
11 |

Column 2

12 |
, 13 |
14 |

Column 3

15 |
, 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 |
31 |
{children}
32 |
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 |
7 |
10 | Test 11 |
12 |
13 | `; 14 | 15 | exports[`Drawer > should render with required props 1`] = ` 16 |
19 |
22 | Test 23 |
24 |
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(