Skip to content

Commit 98869cb

Browse files
committed
import
1 parent 1125ba9 commit 98869cb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1867
-0
lines changed
Loading
Loading
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const assertions = {
2+
'bf-cache': 'off',
3+
'color-contrast': 'off',
4+
'inspector-issues': 'off',
5+
'offscreen-images': ['error', {minScore: 0.5, maxLength: 1}],
6+
'total-byte-weight': ['error', {minScore: 0.5}],
7+
'unused-css-rules': ['error', {maxLength: 5}],
8+
'unused-javascript': ['error', {maxLength: 10}],
9+
'uses-text-compression': ['error', {maxLength: 5}],
10+
'third-party-cookies': 'off',
11+
'uses-rel-preconnect': 'off',
12+
};
13+
14+
if (process.env.STAGE !== 'production') {
15+
// Not crawlable in preprod
16+
assertions['is-crawlable'] = 'off';
17+
}
18+
19+
module.exports = {
20+
ci: {
21+
assert: {
22+
preset: 'lighthouse:recommended',
23+
assertions,
24+
},
25+
upload: {
26+
target: 'temporary-public-storage',
27+
},
28+
},
29+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {GoogleAnalytics} from '@next/third-parties/google';
2+
import {headers} from 'next/headers';
3+
4+
import {getBrandFromHostname} from '@/config/brand';
5+
import {getGoogleAnalyticsMeasurementId} from '@/config/ga4';
6+
import {getStage} from '@/config/stage';
7+
import {generateBootstrapValues} from '@/providers/statsig/statsig-backend';
8+
import StatsigProvider from '@/providers/statsig/StatsigProvider';
9+
10+
/**
11+
* Nested asynchronous layout to temporarily workaround Font Awesome imports going out of order due to CSS Chunking
12+
*
13+
* Long term fix: https://codedotorg.atlassian.net/browse/CMS-413
14+
*/
15+
export default async function Layout({
16+
children,
17+
}: Readonly<{
18+
children: React.ReactNode;
19+
}>) {
20+
const hostname = (await headers()).get('Host');
21+
const brand = getBrandFromHostname(hostname);
22+
const googleAnalyticsMeasurementId = getGoogleAnalyticsMeasurementId(brand);
23+
const statsigBootstrapValues = await generateBootstrapValues();
24+
const statsigClientKey = process.env.STATSIG_CLIENT_KEY;
25+
26+
return (
27+
<>
28+
{googleAnalyticsMeasurementId && (
29+
<GoogleAnalytics gaId={googleAnalyticsMeasurementId} />
30+
)}
31+
<StatsigProvider
32+
stage={getStage()}
33+
clientKey={statsigClientKey}
34+
values={statsigBootstrapValues}
35+
>
36+
{children}
37+
</StatsigProvider>
38+
</>
39+
);
40+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type {MetadataRoute} from 'next';
2+
3+
import {getStage} from '@/config/stage';
4+
5+
const DISALLOW_ALL = {
6+
userAgent: '*',
7+
disallow: '/',
8+
};
9+
10+
export default function robots(): MetadataRoute.Robots {
11+
const rules = [];
12+
13+
if (getStage() !== 'production') {
14+
rules.push(DISALLOW_ALL);
15+
}
16+
17+
return {
18+
rules,
19+
};
20+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {EntryFields, BaseEntry} from 'contentful';
2+
import {useMemo} from 'react';
3+
4+
import FAQAccordion, {
5+
FAQAccordionItem,
6+
} from '@code-dot-org/component-library/accordrion/faqAccordion';
7+
8+
type FAQAccordionContentfulProps = {
9+
faqs?: (BaseEntry & {
10+
fields: {
11+
question: EntryFields.Text | EntryFields.RichText;
12+
answer: EntryFields.Text | EntryFields.RichText;
13+
};
14+
})[];
15+
};
16+
17+
const checkIfEntryFieldIsRichText = (
18+
entry: BaseEntry & {
19+
fields: {[key: string]: EntryFields.Text | EntryFields.RichText};
20+
},
21+
fieldName: string,
22+
) =>
23+
typeof entry.fields[fieldName] !== 'string' &&
24+
'content' in entry.fields[fieldName];
25+
26+
const FAQAccordionContentful: React.FunctionComponent<
27+
FAQAccordionContentfulProps
28+
> = ({faqs}) => {
29+
const faqItems = useMemo(
30+
() =>
31+
faqs?.filter(Boolean).map(faq => {
32+
let id, question, questionString, answer, answerString;
33+
34+
if (checkIfEntryFieldIsRichText(faq, 'question')) {
35+
question =
36+
'Rich Text is not supported yet. Please use Text type instead';
37+
questionString = question;
38+
id = 'rich-text-not-supported-yet';
39+
} else {
40+
question = faq.fields.question as string;
41+
questionString = question;
42+
id = question.replace(' ', '-').toLowerCase();
43+
}
44+
45+
if (checkIfEntryFieldIsRichText(faq, 'answer')) {
46+
answer =
47+
'Rich Text is not supported yet. Please use Text type instead';
48+
answerString = answer;
49+
} else {
50+
answer = faq.fields.answer as string;
51+
answerString = answer;
52+
}
53+
54+
return {
55+
id,
56+
label: question,
57+
questionString,
58+
content: answer,
59+
answerString,
60+
} as FAQAccordionItem;
61+
}) || [],
62+
[faqs],
63+
);
64+
65+
// Show placeholder text until a content entry is added
66+
if (!faqItems.length) {
67+
return (
68+
<div style={{color: 'var(--text-neutral-primary)'}}>
69+
<em>
70+
<strong>❓ FAQ Accordion placeholder.</strong> Please add a "FAQs"
71+
content type entry in the FAQ Accordion sidebar, save, and open the
72+
preview tab to see the accordions in action.
73+
</em>
74+
</div>
75+
);
76+
}
77+
78+
return <FAQAccordion items={faqItems} />;
79+
};
80+
81+
export default FAQAccordionContentful;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Creates a definition for the FAQ Accordion component to be used in Contentful Studio
2+
import {ComponentDefinition} from '@contentful/experiences-sdk-react';
3+
4+
export const FAQAccordionContentfulComponentDefinition: ComponentDefinition = {
5+
id: 'faqAccordion',
6+
name: 'FAQ Accordion',
7+
category: '04: Advanced',
8+
thumbnailUrl:
9+
'https://images.ctfassets.net/90t6bu6vlf76/2T9oZuMVAKZ0HHTJB80ftF/e81cf4131a6580d1edd86c98e0539bfe/component_faq_thumbnail.png',
10+
tooltip: {
11+
description:
12+
'A collapsible list for organizing frequently asked questions.',
13+
imageUrl:
14+
'https://images.ctfassets.net/90t6bu6vlf76/4ukyPBibtqgkevBelS8CzJ/c4691dd48e5cff1242591c459ee32560/component_faq_tooltip.png',
15+
},
16+
// Adding an empty array here so no default style options show in the Design tab.
17+
builtInStyles: [],
18+
children: false,
19+
variables: {
20+
faqs: {
21+
displayName: 'FAQs',
22+
type: 'Array',
23+
},
24+
},
25+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Export the Component Definition for use in Contentful Studio
2+
export {FAQAccordionContentfulComponentDefinition} from './FAQAccordionContentfulDefinition';
3+
export {default} from './FAQAccordion';
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Creates a definition for the Iframe component to be used in Contentful Studio
2+
import {ComponentDefinition} from '@contentful/experiences-sdk-react';
3+
4+
export const IframeContentfulComponentDefinition: ComponentDefinition = {
5+
id: 'iframe',
6+
name: 'iFrame Block',
7+
category: '04: Advanced',
8+
thumbnailUrl:
9+
'https://images.ctfassets.net/90t6bu6vlf76/1qy9FC9Bqb4ADrpyszIa5M/eb4c9dde9c1c90ab40036e8fa4412697/component_iframe_thumbnail.png',
10+
tooltip: {
11+
description:
12+
'Embed external content using an iframe. Ideal for embedding forms, interactive tools, or third-party widgets within a page.',
13+
imageUrl:
14+
'https://images.ctfassets.net/90t6bu6vlf76/75ulYKJrhP83vfIre5Rm88/4a64acf16b04ac82f94dc0e6cb6c8b77/component_iframe_tooltip.png',
15+
},
16+
// Adding an empty array here so no default style options show in the Design tab.
17+
builtInStyles: [],
18+
variables: {
19+
src: {
20+
displayName: 'Embedded content URL',
21+
type: 'Text',
22+
group: 'content',
23+
validations: {
24+
required: true,
25+
},
26+
},
27+
title: {
28+
displayName: 'Embedded content title (for accessibility and SEO)',
29+
type: 'Text',
30+
group: 'content',
31+
validations: {
32+
required: true,
33+
},
34+
},
35+
height: {
36+
displayName: 'Embed container height',
37+
type: 'Text',
38+
defaultValue: '500px',
39+
group: 'style',
40+
},
41+
},
42+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Export the Component Definition for use in Contentful Studio
2+
export {IframeContentfulComponentDefinition} from './iframeContentfulDefinition';
3+
export {default} from '@code-dot-org/component-library/cms/iframe';

0 commit comments

Comments
 (0)