diff --git a/shared/types/src/Study.ts b/shared/types/src/Study.ts new file mode 100644 index 00000000..3f3ce826 --- /dev/null +++ b/shared/types/src/Study.ts @@ -0,0 +1,20 @@ +export interface Study { + studyId: string; + name: string; + version: string; + description: string; + studyDetailsLink: string; + authors: { + name: string; + }; + icons: Record; + firefoxAddonId: string; + chromeExtensionId: string; + schemaNamespace: string; + downloadLink: string; + endDate: string | "Ongoing"; + studyEnded: boolean; + studyPaused: boolean; + dataCollectionDetails: string[]; + tags: string[]; +} diff --git a/shared/types/src/index.ts b/shared/types/src/index.ts index 8e6d35d3..18a65f71 100644 --- a/shared/types/src/index.ts +++ b/shared/types/src/index.ts @@ -1,3 +1,4 @@ +export * from "./Study"; export * from "./UserDocument"; export * from "./UserDemographicsData"; export * from "./UserStudyRecord"; diff --git a/web-platform/website/__mocks__/firebase/firestore.js b/web-platform/website/__mocks__/firebase/firestore.js index 9937ec95..bc0e2a65 100644 --- a/web-platform/website/__mocks__/firebase/firestore.js +++ b/web-platform/website/__mocks__/firebase/firestore.js @@ -1,8 +1,11 @@ // Manual mock required as jest does not handle ESM modules. module.exports = { + collection: jest.fn(), doc: jest.fn(), getDoc: jest.fn(), + getDocs: jest.fn(), getFirestore: jest.fn(), onSnapshot: jest.fn(), + setDoc: jest.fn(), updateDoc: jest.fn() }; \ No newline at end of file diff --git a/web-platform/website/public/img/cards1-bg.png b/web-platform/website/public/img/cards1-bg.png new file mode 100644 index 00000000..d759b11f Binary files /dev/null and b/web-platform/website/public/img/cards1-bg.png differ diff --git a/web-platform/website/public/img/cards2-bg.png b/web-platform/website/public/img/cards2-bg.png new file mode 100644 index 00000000..e0870b06 Binary files /dev/null and b/web-platform/website/public/img/cards2-bg.png differ diff --git a/web-platform/website/public/img/caret.svg b/web-platform/website/public/img/caret.svg new file mode 100644 index 00000000..1b541894 --- /dev/null +++ b/web-platform/website/public/img/caret.svg @@ -0,0 +1,16 @@ + + + diff --git a/web-platform/website/public/img/check-circle.svg b/web-platform/website/public/img/check-circle.svg new file mode 100644 index 00000000..370434fe --- /dev/null +++ b/web-platform/website/public/img/check-circle.svg @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/web-platform/website/public/img/data1-bg.png b/web-platform/website/public/img/data1-bg.png new file mode 100644 index 00000000..cc9d99e2 Binary files /dev/null and b/web-platform/website/public/img/data1-bg.png differ diff --git a/web-platform/website/public/img/data2-bg.png b/web-platform/website/public/img/data2-bg.png new file mode 100644 index 00000000..f26da9a5 Binary files /dev/null and b/web-platform/website/public/img/data2-bg.png differ diff --git a/web-platform/website/public/img/default-study-icon.png b/web-platform/website/public/img/default-study-icon.png new file mode 100644 index 00000000..eeca73c5 Binary files /dev/null and b/web-platform/website/public/img/default-study-icon.png differ diff --git a/web-platform/website/public/img/icon-mozilla-128x128.png b/web-platform/website/public/img/icon-mozilla-128x128.png new file mode 100644 index 00000000..c21922b0 Binary files /dev/null and b/web-platform/website/public/img/icon-mozilla-128x128.png differ diff --git a/web-platform/website/public/img/icon-mozilla-32x32.png b/web-platform/website/public/img/icon-mozilla-32x32.png new file mode 100644 index 00000000..1f36866d Binary files /dev/null and b/web-platform/website/public/img/icon-mozilla-32x32.png differ diff --git a/web-platform/website/public/img/icon-mozilla-64x64.png b/web-platform/website/public/img/icon-mozilla-64x64.png new file mode 100644 index 00000000..d3f440a2 Binary files /dev/null and b/web-platform/website/public/img/icon-mozilla-64x64.png differ diff --git a/web-platform/website/public/img/moz-rally-logo-inverted.svg b/web-platform/website/public/img/moz-rally-logo-inverted.svg new file mode 100644 index 00000000..9eb047e5 --- /dev/null +++ b/web-platform/website/public/img/moz-rally-logo-inverted.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web-platform/website/public/img/overflow-ellipsis.svg b/web-platform/website/public/img/overflow-ellipsis.svg new file mode 100644 index 00000000..6c7e7517 --- /dev/null +++ b/web-platform/website/public/img/overflow-ellipsis.svg @@ -0,0 +1,16 @@ + + + > + diff --git a/web-platform/website/public/img/twitter.svg b/web-platform/website/public/img/twitter.svg new file mode 100644 index 00000000..8754d6df --- /dev/null +++ b/web-platform/website/public/img/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/web-platform/website/public/img/warning.svg b/web-platform/website/public/img/warning.svg new file mode 100644 index 00000000..d14b2942 --- /dev/null +++ b/web-platform/website/public/img/warning.svg @@ -0,0 +1,14 @@ + diff --git a/web-platform/website/src/__tests__/IndexPage.tests.tsx b/web-platform/website/src/__tests__/IndexPage.tests.tsx new file mode 100644 index 00000000..b7727506 --- /dev/null +++ b/web-platform/website/src/__tests__/IndexPage.tests.tsx @@ -0,0 +1,32 @@ +import { render } from "@testing-library/react"; +import Head from "next/head"; + +import { AuthenticatedPage } from "../components/AuthenticatedPage"; +import { StudiesPageContent } from "../components/pages/studies/StudiesPageContent"; +import { default as IndexPage } from "../pages/index"; +import { Strings } from "../resources/Strings"; + +jest.mock("next/head"); +jest.mock("../components/AuthenticatedPage"); +jest.mock("../components/Layout"); +jest.mock("../components/pages/studies/StudiesPageContent"); + +const strings = Strings.pages.index; + +describe("IndexPage tests", () => { + beforeEach(() => { + (Head as jest.Mock).mockImplementation(({ children }) => children); + (AuthenticatedPage as jest.Mock).mockImplementation( + ({ children }) => children + ); + }); + + it("renders studies content", () => { + render(); + + expect(AuthenticatedPage).toHaveBeenCalled(); + expect(StudiesPageContent).toHaveBeenCalled(); + + expect(document.title).toBe(strings.title); + }); +}); diff --git a/web-platform/website/src/__tests__/PrivacyPolicyPage.tests.tsx b/web-platform/website/src/__tests__/PrivacyPolicyPage.tests.tsx index 6490b475..b0f56cbc 100644 --- a/web-platform/website/src/__tests__/PrivacyPolicyPage.tests.tsx +++ b/web-platform/website/src/__tests__/PrivacyPolicyPage.tests.tsx @@ -88,7 +88,10 @@ describe("privacy policy page tests", () => { render(); - expect(PrivacyPolicyPageContent).toHaveBeenCalled(); + expect(PrivacyPolicyPageContent).toHaveBeenCalledWith( + { readOnly: false }, + expect.anything() + ); expect(replace).not.toHaveBeenCalled(); diff --git a/web-platform/website/src/__tests__/TermsPage.tests.tsx b/web-platform/website/src/__tests__/TermsPage.tests.tsx new file mode 100644 index 00000000..3ae55ea3 --- /dev/null +++ b/web-platform/website/src/__tests__/TermsPage.tests.tsx @@ -0,0 +1,32 @@ +import { render } from "@testing-library/react"; +import Head from "next/head"; + +import { Layout } from "../components/Layout"; +import { PrivacyPolicyPageContent } from "../components/pages/privacy-policy/PrivacyPolicyPageContent"; +import { default as TermsPage } from "../pages/terms"; +import { Strings } from "../resources/Strings"; + +jest.mock("next/head"); +jest.mock("../components/Layout"); +jest.mock("../components/pages/privacy-policy/PrivacyPolicyPageContent"); + +const strings = Strings.pages.privacyPolicy; + +describe("TermsPage tests", () => { + beforeEach(() => { + (Layout as jest.Mock).mockImplementation(({ children }) => children); + (Head as jest.Mock).mockImplementation(({ children }) => children); + }); + + it("renders terms content", () => { + render(); + + expect(Layout).toHaveBeenCalled(); + expect(PrivacyPolicyPageContent).toHaveBeenCalledWith( + { readOnly: true }, + expect.anything() + ); + + expect(document.title).toBe(strings.title); + }); +}); diff --git a/web-platform/website/src/components/Footer.tsx b/web-platform/website/src/components/Footer.tsx new file mode 100644 index 00000000..9b32b11a --- /dev/null +++ b/web-platform/website/src/components/Footer.tsx @@ -0,0 +1,115 @@ +import { Col, Container, Row } from "reactstrap"; +import { style } from "typestyle"; + +import { Strings } from "../resources/Strings"; +import { Colors } from "../styles/Colors"; +import { ContainerStyles } from "../styles/ContainerStyles"; +import { FontSize } from "../styles/Fonts"; +import { LinkStyles } from "../styles/LinkStyles"; +import { Spacing } from "../styles/Spacing"; + +const strings = Strings.components.footer; + +export function Footer() { + return ( + + + + + + + + {strings.sections.map((section, i) => ( + + + {section.heading.text} + + + + ))} + + + +
+ +
+ + +

{strings.copyright}

+ +
+ + {strings.bottomLinks.map((l, i) => ( + + + {l.text} + + + ))} + + + + + + +
+ ); +} + +const styles = { + container: style({ + backgroundColor: Colors.ColorBlack, + color: Colors.ColorWhite, + maxWidth: "unset", + + paddingTop: Spacing.xxxLarge, + paddingBottom: Spacing.xxxLarge, + + $nest: { + ".logo-large": { + width: 200, + marginRight: Spacing.xxLarge, + paddingBottom: Spacing.Medium * 6, + }, + }, + }), +}; diff --git a/web-platform/website/src/components/Layout.tsx b/web-platform/website/src/components/Layout.tsx index 79b80174..908662f4 100644 --- a/web-platform/website/src/components/Layout.tsx +++ b/web-platform/website/src/components/Layout.tsx @@ -1,3 +1,4 @@ +import { Footer } from "./Footer"; import { NavigationBar } from "./navigation-bar"; export function Layout(props: { children?: JSX.Element }) { @@ -5,6 +6,7 @@ export function Layout(props: { children?: JSX.Element }) { <> {props.children} +