diff --git a/apps/iris-e2e/.eslintrc.json b/apps/iris-e2e/.eslintrc.json new file mode 100644 index 0000000..696cb8b --- /dev/null +++ b/apps/iris-e2e/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/iris-e2e/cypress.json b/apps/iris-e2e/cypress.json new file mode 100644 index 0000000..521cade --- /dev/null +++ b/apps/iris-e2e/cypress.json @@ -0,0 +1,12 @@ +{ + "fileServerFolder": ".", + "fixturesFolder": "./src/fixtures", + "integrationFolder": "./src/integration", + "modifyObstructiveCode": false, + "supportFile": "./src/support/index.ts", + "pluginsFile": false, + "video": true, + "videosFolder": "../../dist/cypress/apps/iris-e2e/videos", + "screenshotsFolder": "../../dist/cypress/apps/iris-e2e/screenshots", + "chromeWebSecurity": false +} diff --git a/apps/iris-e2e/project.json b/apps/iris-e2e/project.json new file mode 100644 index 0000000..616aa70 --- /dev/null +++ b/apps/iris-e2e/project.json @@ -0,0 +1,28 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/iris-e2e/src", + "projectType": "application", + "targets": { + "e2e": { + "executor": "@nrwl/cypress:cypress", + "options": { + "cypressConfig": "apps/iris-e2e/cypress.json", + "devServerTarget": "iris:serve:development" + }, + "configurations": { + "production": { + "devServerTarget": "iris:serve:production" + } + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/iris-e2e/**/*.{js,ts}"] + } + } + }, + "tags": [], + "implicitDependencies": ["iris"] +} diff --git a/apps/iris-e2e/src/fixtures/example.json b/apps/iris-e2e/src/fixtures/example.json new file mode 100644 index 0000000..294cbed --- /dev/null +++ b/apps/iris-e2e/src/fixtures/example.json @@ -0,0 +1,4 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io" +} diff --git a/apps/iris-e2e/src/integration/app.spec.ts b/apps/iris-e2e/src/integration/app.spec.ts new file mode 100644 index 0000000..0736853 --- /dev/null +++ b/apps/iris-e2e/src/integration/app.spec.ts @@ -0,0 +1,13 @@ +import { getGreeting } from '../support/app.po'; + +describe('iris', () => { + beforeEach(() => cy.visit('/')); + + it('should display welcome message', () => { + // Custom command example, see `../support/commands.ts` file + cy.login('my-email@something.com', 'myPassword'); + + // Function helper example, see `../support/app.po.ts` file + getGreeting().contains('Welcome iris'); + }); +}); diff --git a/apps/iris-e2e/src/support/app.po.ts b/apps/iris-e2e/src/support/app.po.ts new file mode 100644 index 0000000..3293424 --- /dev/null +++ b/apps/iris-e2e/src/support/app.po.ts @@ -0,0 +1 @@ +export const getGreeting = () => cy.get('h1'); diff --git a/apps/iris-e2e/src/support/commands.ts b/apps/iris-e2e/src/support/commands.ts new file mode 100644 index 0000000..310f1fa --- /dev/null +++ b/apps/iris-e2e/src/support/commands.ts @@ -0,0 +1,33 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +// eslint-disable-next-line @typescript-eslint/no-namespace +declare namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + } +} +// +// -- This is a parent command -- +Cypress.Commands.add('login', (email, password) => { + console.log('Custom command example: Login', email, password); +}); +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/iris-e2e/src/support/index.ts b/apps/iris-e2e/src/support/index.ts new file mode 100644 index 0000000..3d469a6 --- /dev/null +++ b/apps/iris-e2e/src/support/index.ts @@ -0,0 +1,17 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; diff --git a/apps/iris-e2e/tsconfig.json b/apps/iris-e2e/tsconfig.json new file mode 100644 index 0000000..c4f818e --- /dev/null +++ b/apps/iris-e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "sourceMap": false, + "outDir": "../../dist/out-tsc", + "allowJs": true, + "types": ["cypress", "node"] + }, + "include": ["src/**/*.ts", "src/**/*.js"] +} diff --git a/apps/iris/.eslintrc.json b/apps/iris/.eslintrc.json new file mode 100644 index 0000000..5b9886b --- /dev/null +++ b/apps/iris/.eslintrc.json @@ -0,0 +1,31 @@ +{ + "extends": [ + "plugin:@nrwl/nx/react-typescript", + "next", + "next/core-web-vitals", + "../../.eslintrc.json" + ], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@next/next/no-html-link-for-pages": ["error", "apps/iris/pages"] + } + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ], + "rules": { + "@next/next/no-html-link-for-pages": "off" + }, + "env": { + "jest": true + } +} diff --git a/apps/iris/components/ActiveLink.js b/apps/iris/components/ActiveLink.js new file mode 100644 index 0000000..c0a8fb8 --- /dev/null +++ b/apps/iris/components/ActiveLink.js @@ -0,0 +1,36 @@ +import { useRouter } from 'next/router'; +import Link from 'next/link'; +import React, { Children } from 'react'; + +const ActiveLink = ({ children, activeClassName, ...props }) => { + const { asPath } = useRouter(); + const child = Children.only(children); + const childClassName = child.props.className || ''; + + // pages/index.js will be matched via props.href + // pages/about.js will be matched via props.href + // pages/[slug].js will be matched via props.as + + const addTrailingSlash = (url) => url.replace(/\/?$/, '/'); + const href = props.href && addTrailingSlash(props.href); + const as = props.as && addTrailingSlash(props.as); + const className = + asPath === href || asPath === as + ? `${childClassName} ${activeClassName}`.trim() + : childClassName; + + return ( + + {React.cloneElement(child, { + className: className || null, + })} + + ); +}; + +export default ActiveLink; + + + + + diff --git a/apps/iris/components/BlogCard.js b/apps/iris/components/BlogCard.js new file mode 100644 index 0000000..7af9ebc --- /dev/null +++ b/apps/iris/components/BlogCard.js @@ -0,0 +1,213 @@ +/** @jsx jsx */ +/** @jsxRuntime classic */ +import React from 'react'; +import parseDate from 'src/helpers/parseDate'; +import { jsx } from 'theme-ui'; +import Link from 'next/link'; +import generateFluidObject from 'src/helpers/generateFluidObject'; + +const BlogCard = ({ post, type = 'basic' }) => { + return ( + <> + {(type === 'featured' || type === 'basic') && ( +
+
+
+
+ + + {post.medium && ( + + + )} + + +
+
+
+
+
+ {post?.categories.length > 0 && + {post.categories[0]?.name} + + } +
+
+ + +

+ {post.title.length < 50 ? post.title : post.title.slice(0, 50) + '...'} +

+
+ +
+
+ {post.excerpt.length < 80 ? post.excerpt : post.excerpt.slice(0, 80) + '...'} +
+
+
+
+ +
+ {parseDate(post.published_date)} . 3 min read +
+
+
+
+
+ )} + + {type === 'sidebar' && ( +
+
+
+
+ + + {post.medium && ( + + )} + + +
+
+
+
+
+ + + {post.categories[0].name} + + +
+
+ + +

+ {post.title} +

+
+ +
+
+
+ )} + + ); +}; + +export default BlogCard; + + diff --git a/apps/iris/components/Layout/Footer.js b/apps/iris/components/Layout/Footer.js new file mode 100644 index 0000000..f11ccd8 --- /dev/null +++ b/apps/iris/components/Layout/Footer.js @@ -0,0 +1,116 @@ +/** @jsx jsx */ +/** @jsxRuntime classic */ +import React from 'react'; // eslint-disable-line no-unused-vars +import { jsx } from 'theme-ui'; + + +const FooterTwo = () => ( + +); + +export default FooterTwo; diff --git a/apps/iris/components/Layout/Navbar.js b/apps/iris/components/Layout/Navbar.js new file mode 100644 index 0000000..bed1409 --- /dev/null +++ b/apps/iris/components/Layout/Navbar.js @@ -0,0 +1,182 @@ +/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ +/** @jsx jsx */ +/** @jsxRuntime classic */ +import React, { useState, useEffect } from 'react'; +import Link from 'next/link'; +import { jsx } from 'theme-ui'; +import { FaHome, FaBars } from 'react-icons/fa'; +import isBrowser from 'src/helpers/isBrowser'; + +/** + * @component Navbar + * @typedef Props + * @prop {string} logo - url for logo + * @param {Props} props - arguments for Navbar with logo and menu properties + * @param {string} props.logo - url for logo + * @param {Object} props.menu - menu item + */ + +const Navbar = ({ logo, menu }) => { + const [showMenu, setShowMenu] = useState(false); + const [width, setWidth] = useState(0); + + /** + * Updates width when resized for responsiveness of menu item + */ + const updateWidth = () => { + const windowWidth = isBrowser && window.innerWidth; + setWidth(windowWidth); + }; + + useEffect(() => { + updateWidth(); + isBrowser && window.addEventListener('resize', updateWidth); + if (width >= 1080) { + setShowMenu(true); + } else { + setShowMenu(false); + } + return () => isBrowser && window.removeEventListener('resize', updateWidth); + }, [width]); + + const handleClick = () => { + setShowMenu((prevState) => !prevState); + }; + return ( + +
`${theme.colors.bgLight}`, + borderBottomWidth: '1px', + }} + > + +
+
+ ); +}; + +export default Navbar; \ No newline at end of file diff --git a/apps/iris/components/Layout/index.js b/apps/iris/components/Layout/index.js new file mode 100644 index 0000000..327ce7a --- /dev/null +++ b/apps/iris/components/Layout/index.js @@ -0,0 +1,58 @@ +/** @jsx jsx */ +/** @jsxRuntime classic */ +import React from 'react'; // eslint-disable-line no-unused-vars +import { graphql } from 'graphql'; +import PropTypes from 'prop-types'; +import { jsx } from 'theme-ui'; +import Navbar from './Navbar'; +import Footer from './Footer'; + + +const Layout = ({ children, data }) => { + + + return ( + <> + {/* + {space.fav_icon && } + + */} + + {data && data.menu && } +
`${theme.fontSizes.h6}`, null, (theme) => `${theme.fontSizes.h5}`], + color: (theme) => `${theme.colors.textPrimary}`, + lineHeight: 'normal', + pt: [(theme) => `${theme.space.spacing5}`, 0, 0], + mt: '60px', + minHeight: 'calc(100vh - 60px)', + mx: 'auto', + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + }} + > + {children} +
+