diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 40401d93e5..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,9 +0,0 @@ -node_modules -build -dist -lib -esm -**/*.spec.ts -**/*spec.ts -**/*.d.ts -**/graphql-schema.ts? diff --git a/.eslintrc.base.js b/.eslintrc.base.js deleted file mode 100644 index 7bb9124520..0000000000 --- a/.eslintrc.base.js +++ /dev/null @@ -1,123 +0,0 @@ -module.exports = { - globals: { - fetchMock: true, - cy: true, - Cypress: true, - }, - settings: { - react: { - // Automatically detect the react version - version: "detect", - }, - "import/resolver": { - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - } - } - }, - env: { - browser: true, - es2021: true, - jest: true, - }, - extends: [ - "plugin:react/recommended", - "plugin:storybook/recommended", - "airbnb", - // Make this the last element so prettier config overrides other formatting rules - "plugin:prettier/recommended", - "plugin:jest/recommended", - // "plugin:import/typescript", - ], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - ecmaVersion: 13, - sourceType: "module", - }, - plugins: ["react", "jest"], - rules: { - "react/function-component-definition": "off", - "import/no-relative-packages": "off", - "jsx-a11y/control-has-associated-label": "off", - "react/jsx-filename-extension": "off", - "react/jsx-no-constructed-context-values": "off", - "no-restricted-exports": "off", - "react/jsx-no-useless-fragment": "off", - "no-unsafe-optional-chaining": "off", - "no-promise-executor-return": "off", - "default-param-last": "off", - "react/require-default-props": "off", - "react/jsx-props-no-spreading": [1, { custom: "ignore" }], - "react/jsx-props-no-spreading": "off", - // Use our .prettierrc file as source - "prettier/prettier": ["error", {}, { usePrettierrc: true }], - "dot-notation": "off", - "no-console": 1, - "import/prefer-default-export": "off", - "import/no-extraneous-dependencies": ["error", { devDependencies: true }], - "no-unused-vars": [ - "warn", - { - argsIgnorePattern: "^_", - }, - ], - "import/no-relative-path-imports": "off", - "import/order": ["error", { - "newlines-between": "always", - "pathGroupsExcludedImportTypes": ["builtin"] - }], - "jsx-a11y/label-has-associated-control": [ - "error", - { - required: { - some: ["nesting", "id"], - }, - }, - ], - "import/no-absolute-path": "off", - "import/order": "off", - }, - overrides: [ - { - settings: { - "import/extensions": [".js", ".jsx", ".ts", ".tsx"], - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"] - }, - "import/resolver": { - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - } - } - }, - files: ["*.ts", "*.tsx"], - parser: "@typescript-eslint/parser", - extends: [ - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - plugins: ["eslint-plugin-import-helpers", "@typescript-eslint"], - rules: { - "import/extensions": [ - "error", - "ignorePackages", - { - "js": "never", - "jsx": "never", - "ts": "never", - "tsx": "never" - } - ], - "import/prefer-default-export": "off", - "react/jsx-filename-extension": ["error", { extensions: [".js", ".jsx", ".ts", ".tsx"] }], - "@typescript-eslint/ban-ts-comment": ["warn"], - "no-unused-vars": "off", - "react/require-default-props": "off", - "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], - "@typescript-eslint/no-empty-function": "off" - } - }, - ], -}; diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 449fbcd4f2..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - root: true, - env: { - browser: true, - es2021: true, - }, - parserOptions: { - ecmaVersion: 12, // Allows for the parsing of modern ECMAScript features - sourceType: "module", // Allows for the use of imports - ecmaFeatures: { - jsx: true, // Allows for the parsing of JSX - }, - }, - plugins: ["eslint-plugin-import-helpers"], - settings: { - "import/resolver": "webpack", - "import/ignore": "node_modules", - }, - rules: { - // "no-unused-vars": "warn", - // "no-console": "warn", - // "no-undef": "error", - "import-helpers/order-imports": [ - "warn", - { - newlinesBetween: "always", // new line between groups - groups: [ - "module", - ["/^Components/", "/^Vendor/"], - [("parent", "sibling", "index")], - ], - alphabetize: { order: "asc", ignoreCase: true }, - }, - ], - }, -}; diff --git a/.vscode/settings.json b/.vscode/settings.json index f4f0482814..99af502c08 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,5 +15,6 @@ "titleBar.activeForeground": "#FFC627", "titleBar.inactiveBackground": "#8C1D40", "titleBar.inactiveForeground": "#D0D0D0" - } + }, + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000000..7f2b095e7e --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,190 @@ +const globals = require("globals"); +const react = require("eslint-plugin-react"); +const jest = require("eslint-plugin-jest"); +const tsParser = require("@typescript-eslint/parser"); +const typescriptEslint = require("@typescript-eslint/eslint-plugin"); +const js = require("@eslint/js"); +const importPlugin = require("eslint-plugin-import"); +const jsxA11y = require("eslint-plugin-jsx-a11y"); +const prettier = require("eslint-plugin-prettier"); + +module.exports = [ + js.configs.recommended, + { + files: ["**/*.{js,jsx}"], + languageOptions: { + globals: { + ...globals.browser, + ...globals.jest, + }, + ecmaVersion: 13, + sourceType: "module", + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + react, + jest, + import: importPlugin, + "jsx-a11y": jsxA11y, + prettier, + }, + settings: { + react: { + version: "detect", + }, + "import/resolver": { + node: { + extensions: [".js", ".jsx", ".ts", ".tsx"], + }, + }, + "import/extensions": [".js", ".jsx", ".ts", ".tsx"], + }, + rules: { + // React rules + "react/function-component-definition": "off", + "react/jsx-filename-extension": "off", + "react/jsx-no-constructed-context-values": "off", + "react/jsx-no-useless-fragment": "off", + "react/require-default-props": "off", + "react/jsx-props-no-spreading": "off", + + // Import rules + "import/no-relative-packages": "off", + "import/prefer-default-export": "off", + "import/no-extraneous-dependencies": ["error", { + devDependencies: true, + }], + "import/no-relative-path-imports": "off", + "import/no-absolute-path": "off", + + // JSX A11y rules + "jsx-a11y/control-has-associated-label": "off", + "jsx-a11y/label-has-associated-control": "off", // Disabled due to too many false positives + + // General rules + "no-restricted-exports": "off", + "no-unsafe-optional-chaining": "off", + "no-promise-executor-return": "off", + "default-param-last": "off", + "dot-notation": "off", + "no-console": 1, + "no-unused-vars": ["warn", { + argsIgnorePattern: "^_", + varsIgnorePattern: "^(React|_)", + }], + "no-undef": "off", // Disable for config files and test files + + // Import rules - disable problematic ones + "import/no-extraneous-dependencies": "off", + + // Prettier + "prettier/prettier": ["error", {}, { + usePrettierrc: true, + }], + }, + }, + { + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser: tsParser, + globals: { + ...globals.browser, + ...globals.jest, + }, + ecmaVersion: 13, + sourceType: "module", + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + react, + jest, + "@typescript-eslint": typescriptEslint, + import: importPlugin, + "jsx-a11y": jsxA11y, + prettier, + }, + settings: { + react: { + version: "detect", + }, + "import/extensions": [".js", ".jsx", ".ts", ".tsx"], + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx"], + }, + "import/resolver": { + node: { + extensions: [".js", ".jsx", ".ts", ".tsx"], + }, + }, + }, + rules: { + // Inherit JS rules + ...js.configs.recommended.rules, + + // TypeScript specific rules + "import/extensions": ["error", "ignorePackages", { + js: "never", + jsx: "never", + ts: "never", + tsx: "never", + }], + "import/prefer-default-export": "off", + "react/jsx-filename-extension": ["error", { + extensions: [".js", ".jsx", ".ts", ".tsx"], + }], + "@typescript-eslint/ban-ts-comment": ["warn"], + "no-unused-vars": "off", + "react/require-default-props": "off", + "@typescript-eslint/no-unused-vars": ["error", { + argsIgnorePattern: "^_", + varsIgnorePattern: "^(React|_)", + }], + "@typescript-eslint/no-empty-function": "off", + + // Re-apply common rules for TypeScript files + "react/function-component-definition": "off", + "import/no-relative-packages": "off", + "jsx-a11y/control-has-associated-label": "off", + "react/jsx-no-constructed-context-values": "off", + "no-restricted-exports": "off", + "react/jsx-no-useless-fragment": "off", + "no-unsafe-optional-chaining": "off", + "no-promise-executor-return": "off", + "default-param-last": "off", + "react/jsx-props-no-spreading": "off", + "prettier/prettier": ["error", {}, { + usePrettierrc: true, + }], + "dot-notation": "off", + "no-console": 1, + "import/no-extraneous-dependencies": ["error", { + devDependencies: true, + }], + "import/no-relative-path-imports": "off", + "jsx-a11y/label-has-associated-control": "off", // Disabled due to too many false positives + "import/no-absolute-path": "off", + }, + }, + { + ignores: [ + "**/node_modules", + "**/build", + "**/dist", + "**/lib", + "**/esm", + "**/*.spec.ts", + "**/*spec.ts", + "**/*.d.ts", + "**/graphql-schema.ts", + "vite.config.*" + ], + }, +]; diff --git a/package.json b/package.json index 0d56a60e93..dd9e8cdc41 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,9 @@ "@commitlint/cli": "^19.7.1", "@commitlint/config-conventional": "^19.7.1", "@commitlint/config-lerna-scopes": "^19.7.0", + "@eslint/compat": "^1.3.1", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.29.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@storybook/addons": "^7.6.14", @@ -67,17 +70,19 @@ "css-minimizer-webpack-plugin": "^2.0.0", "cz-conventional-changelog": "^3.3.0", "dompurify": "^3.2.4", - "eslint": "^8", + "eslint": "^9", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^8.2.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-import-helpers": "^1.1.0", "eslint-plugin-jest": "^28", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-prettier": "^3.4.0", + "eslint-plugin-prettier": "^5.5.1", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^4", + "eslint-plugin-storybook": "^9.0.13", "gh-pages": "^6.0.0", + "globals": "^16.2.0", "husky": "^6.0.0", "inquirer": "^8.0.0", "inquirer-autocomplete-prompt": "^2.0.0", diff --git a/packages/app-degree-pages/.eslintrc.js b/packages/app-degree-pages/.eslintrc.js deleted file mode 100644 index 4d6456d469..0000000000 --- a/packages/app-degree-pages/.eslintrc.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], - rules: { - "react/no-unstable-nested-components": "off", - "no-unused-vars": [ - "warn", - { - varsIgnorePattern: "_|tagHeadings|DegreeDataPropResolverServiceType", - argsIgnorePattern: "^_", - }, - ], - }, -}; diff --git a/packages/app-degree-pages/__mocks__/unity-react-core-mock.jsx b/packages/app-degree-pages/__mocks__/unity-react-core-mock.jsx index 3f67ae976a..21e91e79e0 100644 --- a/packages/app-degree-pages/__mocks__/unity-react-core-mock.jsx +++ b/packages/app-degree-pages/__mocks__/unity-react-core-mock.jsx @@ -1,7 +1,7 @@ /* eslint-disable react/jsx-props-no-spreading */ // @ts-check import * as asuCore from "@asu/unity-react-core"; -import * as React from "react"; +import React from "react"; import { vi } from "vitest" const { diff --git a/packages/app-degree-pages/eslint.config.js b/packages/app-degree-pages/eslint.config.js new file mode 100644 index 0000000000..55fa021f65 --- /dev/null +++ b/packages/app-degree-pages/eslint.config.js @@ -0,0 +1,17 @@ +const rootConfig = require("../../eslint.config.js"); + +module.exports = [ + ...rootConfig, + + // Package-specific overrides + { + files: ["**/*.{js,jsx,ts,tsx}"], + rules: { + "react/no-unstable-nested-components": "off", + "no-unused-vars": ["warn", { + varsIgnorePattern: "_|tagHeadings|DegreeDataPropResolverServiceType", + argsIgnorePattern: "^_", + }], + }, + }, +]; diff --git a/packages/app-degree-pages/package.json b/packages/app-degree-pages/package.json index 28942e5cbf..2784bada9b 100644 --- a/packages/app-degree-pages/package.json +++ b/packages/app-degree-pages/package.json @@ -24,7 +24,7 @@ "registry": "https://npm.pkg.github.com/" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'", "test": "vitest --watch=false", "test-update-snapshot": "yarn test -- -u", "e2e-ci": "concurrently --kill-others \"yarn storybook\" \"yarn cy:run\"", @@ -51,6 +51,7 @@ "styled-components": "^5.3.0" }, "devDependencies": { + "@asu/shared": "*", "@babel/core": "^7.13.14", "@babel/plugin-syntax-jsx": "^7.14.5", "@babel/plugin-transform-react-jsx": "^7.13.12", @@ -69,7 +70,6 @@ "concurrently": "^6.4.0", "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.2.4", - "eslint-plugin-storybook": "^0.6.15", "express": "^4.17.1", "file-loader": "^6.2.0", "http-proxy-middleware": "^2.0.0", diff --git a/packages/app-degree-pages/src/components/DetailPage/components/ApplicationRequirements/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/ApplicationRequirements/index.jsx index 9c7785d9d2..3407b77322 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/ApplicationRequirements/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/ApplicationRequirements/index.jsx @@ -1,12 +1,11 @@ -/* eslint-disable react/no-danger */ // @ts-check import { Accordion, Button } from "@asu/unity-react-core"; +import { sanitizeDangerousMarkup } from "@asu/shared"; import classNames from "classnames"; import PropTypes from "prop-types"; import React from "react"; import styled from "styled-components"; -import { sanitizeDangerousMarkup } from "../../../../../../../shared"; import { progDetailSectionIds } from "../../../../core/models"; /** diff --git a/packages/app-degree-pages/src/components/DetailPage/components/AtAGlance/GlanceItem/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/AtAGlance/GlanceItem/index.jsx index a3e3b029b5..f0511ddea8 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/AtAGlance/GlanceItem/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/AtAGlance/GlanceItem/index.jsx @@ -1,8 +1,8 @@ // @ts-check +import { idGenerator } from "@asu/shared"; import PropTypes from "prop-types"; import React, { Fragment } from "react"; -import { idGenerator } from "../../../../../../../../shared"; import { glanceItemPropShape, glanceItemsMap, diff --git a/packages/app-degree-pages/src/components/DetailPage/components/AttendOnline/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/AttendOnline/index.jsx index d825e3145f..39e3668cb5 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/AttendOnline/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/AttendOnline/index.jsx @@ -1,9 +1,9 @@ // @ts-check import { Button } from "@asu/unity-react-core"; +import { idGenerator } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; -import { idGenerator } from "../../../../../../../shared"; import { progDetailSectionIds } from "../../../../core/models"; import { imagePropShape } from "../../../../core/models/app-prop-types"; diff --git a/packages/app-degree-pages/src/components/DetailPage/components/Breadcrumbs/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/Breadcrumbs/index.jsx index 85e17c3df1..68f9d4e3ec 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/Breadcrumbs/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/Breadcrumbs/index.jsx @@ -1,8 +1,8 @@ // @ts-check +import { idGenerator } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; -import { idGenerator } from "../../../../../../../shared"; import { linkPropShape } from "../../../../core/models"; import { trackGAEvent } from "../../../../core/services/google-analytics"; diff --git a/packages/app-degree-pages/src/components/DetailPage/components/ChangeYourMajor/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/ChangeYourMajor/index.jsx index ba8461ba6c..973b62a0f5 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/ChangeYourMajor/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/ChangeYourMajor/index.jsx @@ -1,9 +1,9 @@ // @ts-check +import { sanitizeDangerousMarkup } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; import styled from "styled-components"; -import { sanitizeDangerousMarkup } from "../../../../../../../shared"; import { progDetailSectionIds } from "../../../../core/models"; const WrapperContainer = styled.div` diff --git a/packages/app-degree-pages/src/components/DetailPage/components/CustomText/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/CustomText/index.jsx index 16168d7fca..a3ba693692 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/CustomText/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/CustomText/index.jsx @@ -1,9 +1,8 @@ // @ts-check +import { sanitizeDangerousMarkup } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; -import { sanitizeDangerousMarkup } from "../../../../../../../shared"; - /** * @param {{content: string}} props * @returns {JSX.Element} @@ -13,7 +12,6 @@ function CustomText({ content = "" }) {
); diff --git a/packages/app-degree-pages/src/components/DetailPage/components/FlexibleDegreeOptions/ContentBlock/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/FlexibleDegreeOptions/ContentBlock/index.jsx index f03d36a3f6..40ef29f296 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/FlexibleDegreeOptions/ContentBlock/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/FlexibleDegreeOptions/ContentBlock/index.jsx @@ -33,7 +33,6 @@ const ContentBlock = ({ id, title, links }) => (

{links.map((link, index) => ( - // eslint-disable-next-line react/no-array-index-key
  • {link.title} diff --git a/packages/app-degree-pages/src/components/DetailPage/components/MarketText/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/MarketText/index.jsx index deae824ea3..1537fbbdf5 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/MarketText/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/MarketText/index.jsx @@ -1,11 +1,8 @@ // @ts-check +import { sanitizeDangerousMarkup, spreadClasses } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; -import { - sanitizeDangerousMarkup, - spreadClasses, -} from "../../../../../../../shared"; import { contentPropShape } from "../../../../core/models/app-prop-types"; /** @@ -24,7 +21,6 @@ function MarketText({ contents = [] }) {
    ))} diff --git a/packages/app-degree-pages/src/components/DetailPage/components/ProfessionalLicensure/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/ProfessionalLicensure/index.jsx index 115288dca1..a97efb9405 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/ProfessionalLicensure/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/ProfessionalLicensure/index.jsx @@ -1,8 +1,7 @@ +import { sanitizeDangerousMarkup } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; -import { sanitizeDangerousMarkup } from "../../../../../../../shared"; - function ProfessionalLicensure({ content = "" }) { return (
    diff --git a/packages/app-degree-pages/src/components/DetailPage/components/ProgramDescription/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/ProgramDescription/index.jsx index 694dbda25f..43d66974a5 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/ProgramDescription/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/ProgramDescription/index.jsx @@ -1,7 +1,7 @@ +import { sanitizeDangerousMarkup } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; -import { sanitizeDangerousMarkup } from "../../../../../../../shared"; import { STEM_OPT_HEADER_TEXT } from "../../../../core/constants"; /** @typedef {import('../../../../core/types/detail-page-types').ProgramDescriptionProps} ProgramDescriptionProps */ @@ -17,7 +17,6 @@ function ProgramDescription({ content, stemOptText, programNotFound }) {

    {programNotFound ? `Program not found` : `Program description`}

    {stemOptText && ( @@ -27,7 +26,6 @@ function ProgramDescription({ content, stemOptText, programNotFound }) {

    diff --git a/packages/app-degree-pages/src/components/DetailPage/components/RequiredCourse/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/RequiredCourse/index.jsx index 5f8cd26b7d..dd3eab5ca2 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/RequiredCourse/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/RequiredCourse/index.jsx @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ import PropTypes from "prop-types"; import React from "react"; diff --git a/packages/app-degree-pages/src/components/DetailPage/components/WhyChooseAsu/index.jsx b/packages/app-degree-pages/src/components/DetailPage/components/WhyChooseAsu/index.jsx index 562e911207..fc11a8d24a 100644 --- a/packages/app-degree-pages/src/components/DetailPage/components/WhyChooseAsu/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/components/WhyChooseAsu/index.jsx @@ -1,8 +1,8 @@ // @ts-check import { Card } from "@asu/unity-react-core"; +import { sanitizeDangerousMarkup } from "@asu/shared"; import React from "react"; -import { sanitizeDangerousMarkup } from "../../../../../../../shared"; import { whyChooseAsuShape, progDetailSectionIds, @@ -25,7 +25,6 @@ const WhyChooseAsu = ({ sectionIntroText, cards, defaultCards }) => { >

    Why choose ASU

    diff --git a/packages/app-degree-pages/src/components/DetailPage/index.jsx b/packages/app-degree-pages/src/components/DetailPage/index.jsx index 051242d207..01aecae145 100644 --- a/packages/app-degree-pages/src/components/DetailPage/index.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/index.jsx @@ -1,11 +1,11 @@ // @ts-check import { Hero } from "@asu/unity-react-core"; +import { useFetch, trackReactComponent } from "@asu/shared"; import PropTypes, { arrayOf } from "prop-types"; import React, { useContext, useEffect, useState } from "react"; // @ts-ignore -import { useFetch } from "../../../../../shared"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; + import { ErrorAlert, Loader, @@ -405,7 +405,7 @@ DetailPage.propTypes = { hideProgramDesc: PropTypes.bool, hideRequiredCourses: PropTypes.bool, breadcrumbs: arrayOf(linkPropShape), - // eslint-disable-next-line react/forbid-prop-types + contents: arrayOf(PropTypes.object), video: videoPropShape, image: imagePropShape, diff --git a/packages/app-degree-pages/src/components/DetailPage/index.stories.jsx b/packages/app-degree-pages/src/components/DetailPage/index.stories.jsx index a4e3e58a82..07aef5fdf0 100644 --- a/packages/app-degree-pages/src/components/DetailPage/index.stories.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/index.stories.jsx @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ /* eslint react/jsx-props-no-spreading: "off" */ // @ts-check import React from "react"; diff --git a/packages/app-degree-pages/src/components/DetailPage/index.test.jsx b/packages/app-degree-pages/src/components/DetailPage/index.test.jsx index 44417e9f9d..a715003181 100644 --- a/packages/app-degree-pages/src/components/DetailPage/index.test.jsx +++ b/packages/app-degree-pages/src/components/DetailPage/index.test.jsx @@ -65,7 +65,6 @@ describe("#DetailPage", () => { /** @param {AppProps} props */ async function renderDetailPage(props) { await act(async () => { - // eslint-disable-next-line react/jsx-props-no-spreading component = render(); // container = component.container; }); diff --git a/packages/app-degree-pages/src/components/ListingPage/components/Filters/components/index.jsx b/packages/app-degree-pages/src/components/ListingPage/components/Filters/components/index.jsx index 6472cc799b..26d1b59a7c 100644 --- a/packages/app-degree-pages/src/components/ListingPage/components/Filters/components/index.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/components/Filters/components/index.jsx @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ // @ts-check import React from "react"; diff --git a/packages/app-degree-pages/src/components/ListingPage/components/FiltersSummary/index.jsx b/packages/app-degree-pages/src/components/ListingPage/components/FiltersSummary/index.jsx index 71dd7bbfca..ea1c038c1f 100644 --- a/packages/app-degree-pages/src/components/ListingPage/components/FiltersSummary/index.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/components/FiltersSummary/index.jsx @@ -1,8 +1,8 @@ // @ts-check +import { idGenerator } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; -import { idGenerator } from "../../../../../../../shared"; import { filterValueShape, isAccelConcValid } from "../../../../core/models"; /** diff --git a/packages/app-degree-pages/src/components/ListingPage/components/IntroContent/index.jsx b/packages/app-degree-pages/src/components/ListingPage/components/IntroContent/index.jsx index fcb4e1a1c3..821f85cf8b 100644 --- a/packages/app-degree-pages/src/components/ListingPage/components/IntroContent/index.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/components/IntroContent/index.jsx @@ -1,10 +1,10 @@ // @ts-check import { Video } from "@asu/unity-react-core"; +import { spreadClasses } from "@asu/shared"; import PropTypes from "prop-types"; import React from "react"; import styled from "styled-components"; -import { spreadClasses } from "../../../../../../../shared"; import { ApplyNow, OverlapContentImage, diff --git a/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/GridView/index.jsx b/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/GridView/index.jsx index a64752044d..7cc5a3d573 100644 --- a/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/GridView/index.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/GridView/index.jsx @@ -1,4 +1,3 @@ -/* eslint-disable no-alert */ // @ts-check import { Card } from "@asu/unity-react-core"; import React, { useContext } from "react"; diff --git a/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.colums.config.jsx b/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.colums.config.jsx index ca2619a681..9d10ad93b6 100644 --- a/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.colums.config.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.colums.config.jsx @@ -1,8 +1,8 @@ /* eslint-disable no-unused-vars */ // @ts-check +import { idGenerator } from "@asu/shared"; import React from "react"; -import { idGenerator } from "../../../../../../../../shared"; import { ChevronIconButton, InfoButtonIcon, @@ -20,7 +20,6 @@ import { import { toTitleCase } from "../../../../../core/utils"; /** @typedef {import("../../../../../core/types/grid-column-types").GridColumn} GridColumn */ -/* eslint-disable react/prop-types, no-alert, no-console */ /** @type {GridColumn []} */ const columns = [ { @@ -216,6 +215,5 @@ const columns = [ // ), // }, ]; -/* eslint-enable react/prop-types, no-alert, no-console */ export { columns }; diff --git a/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.jsx b/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.jsx index 389ce39b56..2fa8af6b21 100644 --- a/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/components/ProgramList/ListView/index.jsx @@ -1,11 +1,7 @@ -/* eslint-disable react/no-danger, jsx-a11y/no-noninteractive-element-to-interactive-role */ // @ts-check +import { idGenerator, sanitizeDangerousMarkup } from "@asu/shared"; import React, { Fragment, useContext, createRef } from "react"; -import { - idGenerator, - sanitizeDangerousMarkup, -} from "../../../../../../../../shared"; import { AppContext } from "../../../../../core/context"; import { GRID_PROGRAMS_ID } from "../../../../../core/models"; import { degreeDataPropResolverService } from "../../../../../core/services"; diff --git a/packages/app-degree-pages/src/components/ListingPage/index.jsx b/packages/app-degree-pages/src/components/ListingPage/index.jsx index 2ee9c46ec8..092dc5d923 100644 --- a/packages/app-degree-pages/src/components/ListingPage/index.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/index.jsx @@ -1,11 +1,10 @@ // @ts-check import { Hero } from "@asu/unity-react-core"; +import { useFetch, trackReactComponent } from "@asu/shared"; import PropTypes from "prop-types"; import React, { useEffect, useState, useContext } from "react"; import styled, { createGlobalStyle } from "styled-components"; -import { useFetch } from "../../../../../shared"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; import { Loader, Main as MainSection, @@ -249,7 +248,9 @@ const ListingPage = ({ return ( <> + {/* @ts-ignore */} + {/* @ts-ignore */} {error && } diff --git a/packages/app-degree-pages/src/components/ListingPage/index.stories.jsx b/packages/app-degree-pages/src/components/ListingPage/index.stories.jsx index 321da36076..7172ce8947 100644 --- a/packages/app-degree-pages/src/components/ListingPage/index.stories.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/index.stories.jsx @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ // @ts-check import React from "react"; diff --git a/packages/app-degree-pages/src/components/ListingPage/index.test.jsx b/packages/app-degree-pages/src/components/ListingPage/index.test.jsx index cf7cb8c324..61e4ba9ece 100644 --- a/packages/app-degree-pages/src/components/ListingPage/index.test.jsx +++ b/packages/app-degree-pages/src/components/ListingPage/index.test.jsx @@ -85,7 +85,6 @@ describe("#ListingPage", () => { await renderListingPage(defaultArgs); }); - // eslint-disable-next-line no-return-assign afterEach(() => (globalThis.doDelay = false)); it("should define Loading component", async () => { diff --git a/packages/app-degree-pages/src/core/components/MessageAlert/index.jsx b/packages/app-degree-pages/src/core/components/MessageAlert/index.jsx index 9621eaffcb..25208c8709 100644 --- a/packages/app-degree-pages/src/core/components/MessageAlert/index.jsx +++ b/packages/app-degree-pages/src/core/components/MessageAlert/index.jsx @@ -66,8 +66,6 @@ MessageAlert.propTypes = { message: PropTypes.string.isRequired, }; -/* eslint-disable react/prop-types */ - /** * @param {{ message: string }} props */ @@ -81,6 +79,5 @@ function ErrorAlert({ message }) { function InfoAlert({ message }) { return ; } -/* eslint-enable react/prop-types */ export { ErrorAlert, InfoAlert }; diff --git a/packages/app-degree-pages/src/core/components/ParagrapList/index.jsx b/packages/app-degree-pages/src/core/components/ParagrapList/index.jsx index d3d5deba65..9ff8363a61 100644 --- a/packages/app-degree-pages/src/core/components/ParagrapList/index.jsx +++ b/packages/app-degree-pages/src/core/components/ParagrapList/index.jsx @@ -1,13 +1,12 @@ -/* eslint-disable react/no-danger */ // @ts-check -import PropTypes from "prop-types"; -import React from "react"; - import { idGenerator, sanitizeDangerousMarkup, spreadClasses, -} from "../../../../../../shared"; +} from "@asu/shared"; +import PropTypes from "prop-types"; +import React from "react"; + import { contentPropShape } from "../../models"; import { isHtml } from "../../utils"; diff --git a/packages/app-degree-pages/src/core/components/icons/InfoIconButton/index.jsx b/packages/app-degree-pages/src/core/components/icons/InfoIconButton/index.jsx index cc97fb59fc..2886989332 100644 --- a/packages/app-degree-pages/src/core/components/icons/InfoIconButton/index.jsx +++ b/packages/app-degree-pages/src/core/components/icons/InfoIconButton/index.jsx @@ -1,14 +1,11 @@ // TODO: THIS COMPONENT IS CURRENTLY DEFERRED -/* eslint-disable react/no-danger */ -/* eslint-disable react/prop-types */ // @ts-check import { createPopper } from "@popperjs/core"; +import { sanitizeDangerousMarkup } from "@asu/shared"; import React, { useEffect, useRef, useState } from "react"; import styled from "styled-components"; -import { sanitizeDangerousMarkup } from "../../../../../../../shared"; - const Tooltip = styled.div` position: absolute; top: 0; diff --git a/packages/app-degree-pages/src/core/components/icons/index.jsx b/packages/app-degree-pages/src/core/components/icons/index.jsx index 02c8f3c773..799d550ba2 100644 --- a/packages/app-degree-pages/src/core/components/icons/index.jsx +++ b/packages/app-degree-pages/src/core/components/icons/index.jsx @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ // @ts-check import React, { useState } from "react"; diff --git a/packages/app-degree-pages/src/core/context/AppContext.jsx b/packages/app-degree-pages/src/core/context/AppContext.jsx index 38113b2655..491531ed40 100644 --- a/packages/app-degree-pages/src/core/context/AppContext.jsx +++ b/packages/app-degree-pages/src/core/context/AppContext.jsx @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ // @ts-check import PropTypes from "prop-types"; import React, { createContext, useMemo } from "react"; diff --git a/packages/app-degree-pages/src/core/models/app-prop-types.js b/packages/app-degree-pages/src/core/models/app-prop-types.js index 84a10f3328..55d3b83dd3 100644 --- a/packages/app-degree-pages/src/core/models/app-prop-types.js +++ b/packages/app-degree-pages/src/core/models/app-prop-types.js @@ -65,7 +65,7 @@ const dataSourcePropShape = PropTypes.shape({ id: PropTypes.string, sourceType: PropTypes.oneOf(["api", "shared-data-source", "static-json"]), // default `api` sharedDataSourceId: PropTypes.string, // only if `dataSourceType == "shared-data-source"`` - // eslint-disable-next-line react/forbid-prop-types + data: PropTypes.arrayOf(PropTypes.object), // only if `dataSourceType == "static-json"`` apiUrl: PropTypes.string, // only if `dataSourceType == "api"`` }); diff --git a/packages/app-degree-pages/src/core/models/page-default-props.js b/packages/app-degree-pages/src/core/models/page-default-props.js index 1f26da285c..018a321392 100644 --- a/packages/app-degree-pages/src/core/models/page-default-props.js +++ b/packages/app-degree-pages/src/core/models/page-default-props.js @@ -1,5 +1,5 @@ // @ts-check -import { getCurrentScriptPath } from "../../../../../shared"; +import { getCurrentScriptPath } from "@asu/shared"; const currentScriptPath = getCurrentScriptPath(); diff --git a/packages/app-degree-pages/src/core/services/degree-http-service.js b/packages/app-degree-pages/src/core/services/degree-http-service.js index c30dd4a5cc..550853a0dc 100644 --- a/packages/app-degree-pages/src/core/services/degree-http-service.js +++ b/packages/app-degree-pages/src/core/services/degree-http-service.js @@ -1,4 +1,3 @@ -/* eslint-disable no-shadow */ // @ts-check import { progDetailSectionIds } from "../models"; diff --git a/packages/app-degree-pages/src/core/services/legacy-degree-http-service.js b/packages/app-degree-pages/src/core/services/legacy-degree-http-service.js index 38d6aac8c6..d9f96252d3 100644 --- a/packages/app-degree-pages/src/core/services/legacy-degree-http-service.js +++ b/packages/app-degree-pages/src/core/services/legacy-degree-http-service.js @@ -1,5 +1,5 @@ /* eslint-disable no-unused-vars */ -/* eslint-disable no-shadow */ + // @ts-check // ================================================================================================ diff --git a/packages/app-degree-pages/src/core/utils/dev-tools-utils.js b/packages/app-degree-pages/src/core/utils/dev-tools-utils.js index 10d4c246b1..14e2448c48 100644 --- a/packages/app-degree-pages/src/core/utils/dev-tools-utils.js +++ b/packages/app-degree-pages/src/core/utils/dev-tools-utils.js @@ -1,4 +1,3 @@ -/* eslint-disable no-underscore-dangle */ // @ts-check const KEY = "AsuDevTools"; const devTools = { diff --git a/packages/app-degree-pages/src/core/utils/init-page-degree.js b/packages/app-degree-pages/src/core/utils/init-page-degree.js index 9d8e926b62..4ca52e58be 100644 --- a/packages/app-degree-pages/src/core/utils/init-page-degree.js +++ b/packages/app-degree-pages/src/core/utils/init-page-degree.js @@ -1,4 +1,3 @@ -/* eslint-disable quote-props */ // @ts-check import React from "react"; import { createRoot } from "react-dom/client"; diff --git a/packages/app-degree-pages/vitest.config.ts b/packages/app-degree-pages/vitest.config.ts index d27a08554b..f063f973cf 100644 --- a/packages/app-degree-pages/vitest.config.ts +++ b/packages/app-degree-pages/vitest.config.ts @@ -10,10 +10,5 @@ export default defineConfig({ setupFiles: ['./vitest.setup.ts'], globals: true }, - resolve: { - alias: { - "@shared": resolve(__dirname, "./../../shared"), - } - }, }); diff --git a/packages/app-rfi/.eslintrc.js b/packages/app-rfi/.eslintrc.js deleted file mode 100644 index 20ea142f77..0000000000 --- a/packages/app-rfi/.eslintrc.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], - // TODO - /** - * This is neecessary to skip check on these lines - * app-rfi/src/components/stepper/RfiStepper.js - * next = () => t... - * prev = () => ... - */ - parser: "@babel/eslint-parser", -}; diff --git a/packages/app-rfi/eslint.config.js b/packages/app-rfi/eslint.config.js new file mode 100644 index 0000000000..01c6b0bd16 --- /dev/null +++ b/packages/app-rfi/eslint.config.js @@ -0,0 +1,21 @@ +const rootConfig = require("../../eslint.config.js"); +const babelParser = require("@babel/eslint-parser"); + +module.exports = [ + // Inherit base configurations from root + ...rootConfig, + + // Package-specific overrides for babel parser + { + files: ["**/*.{js,jsx}"], + languageOptions: { + parser: babelParser, + parserOptions: { + requireConfigFile: false, + babelOptions: { + presets: ["@babel/preset-react"] + } + } + }, + }, +]; diff --git a/packages/app-rfi/package.json b/packages/app-rfi/package.json index 2dd180251e..bbe6606aad 100644 --- a/packages/app-rfi/package.json +++ b/packages/app-rfi/package.json @@ -25,7 +25,7 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx}' ", "test": "jest --config=./jest.config.js --passWithNoTests", "start:dev": "webpack-dashboard -- webpack serve -c webpack/webpack.dev.js", "build": "vite build && cp -r src/assets dist/", @@ -42,6 +42,7 @@ "url": "https://github.com/ASU/asu-unity-stack/issues" }, "devDependencies": { + "@asu/shared": "*", "@asu/unity-bootstrap-theme": "^1.0.0", "@babel/core": "^7.13.14", "@babel/eslint-parser": "^7.13.14", @@ -58,8 +59,6 @@ "babel-loader": "^8.2.2", "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.2.0", - "eslint": "^7.25.0", - "eslint-plugin-storybook": "^0.6.15", "file-loader": "^6.2.0", "glob": "^7.1.6", "jest": "^26.6.3", diff --git a/packages/app-rfi/src/components/AsuRfi/index.js b/packages/app-rfi/src/components/AsuRfi/index.js index 85ebddf3b7..d9a47e769d 100644 --- a/packages/app-rfi/src/components/AsuRfi/index.js +++ b/packages/app-rfi/src/components/AsuRfi/index.js @@ -11,13 +11,12 @@ import React, { useEffect, useState } from "react"; import { Progress } from "reactstrap"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; import { betterPropNames, useRfiState } from "../../core/utils/appState"; import { DATA_SOURCE } from "../../core/utils/constants"; import { RfiContext } from "../../core/utils/rfiContext"; import { RfiMainForm } from "../stepper/RfiMainForm"; import "./index.css"; -import { getCurrentScriptPath } from "../../../../../shared"; +import { getCurrentScriptPath, trackReactComponent } from "@asu/shared"; import { Debug } from "../../Debug"; import { RfiStepperButtons } from "../stepper/RfiStepperButtons"; diff --git a/packages/app-rfi/src/components/controls/RfiCheckboxMulti.js b/packages/app-rfi/src/components/controls/RfiCheckboxMulti.js index 01468ebf67..6a76165ad5 100644 --- a/packages/app-rfi/src/components/controls/RfiCheckboxMulti.js +++ b/packages/app-rfi/src/components/controls/RfiCheckboxMulti.js @@ -1,6 +1,6 @@ // @ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field } from "formik"; import PropTypes from "prop-types"; import React from "react"; @@ -11,7 +11,7 @@ const RfiCheckboxMulti = ({ label, name, options }) => ( {({ field, - // eslint-disable-next-line no-unused-vars + form: { touched, errors }, meta, }) => { diff --git a/packages/app-rfi/src/components/controls/RfiDateInput.js b/packages/app-rfi/src/components/controls/RfiDateInput.js index 427302e493..e722802458 100644 --- a/packages/app-rfi/src/components/controls/RfiDateInput.js +++ b/packages/app-rfi/src/components/controls/RfiDateInput.js @@ -1,6 +1,6 @@ // @ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field, useFormikContext } from "formik"; import PropTypes from "prop-types"; import React from "react"; diff --git a/packages/app-rfi/src/components/controls/RfiEmailInput.js b/packages/app-rfi/src/components/controls/RfiEmailInput.js index ee3e593543..52347caa6a 100644 --- a/packages/app-rfi/src/components/controls/RfiEmailInput.js +++ b/packages/app-rfi/src/components/controls/RfiEmailInput.js @@ -1,6 +1,6 @@ // @ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field } from "formik"; import PropTypes from "prop-types"; import React from "react"; diff --git a/packages/app-rfi/src/components/controls/RfiPhone.js b/packages/app-rfi/src/components/controls/RfiPhone.js index 14d858dad3..2b8186aae1 100644 --- a/packages/app-rfi/src/components/controls/RfiPhone.js +++ b/packages/app-rfi/src/components/controls/RfiPhone.js @@ -1,6 +1,6 @@ // @ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field, useFormikContext } from "formik"; import PropTypes from "prop-types"; import React, { useState } from "react"; @@ -43,7 +43,7 @@ const RfiPhone = ({ disableCountryGuess={disableCountryGuess} inputProps={{ name, - // eslint-disable-next-line object-shorthand + required: required, }} {...field} diff --git a/packages/app-rfi/src/components/controls/RfiRadioGroup.js b/packages/app-rfi/src/components/controls/RfiRadioGroup.js index 1f1dcb762e..535eca30c3 100644 --- a/packages/app-rfi/src/components/controls/RfiRadioGroup.js +++ b/packages/app-rfi/src/components/controls/RfiRadioGroup.js @@ -1,6 +1,6 @@ // @ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field } from "formik"; import PropTypes from "prop-types"; import React from "react"; @@ -12,7 +12,7 @@ const RfiRadioGroup = ({ name, id, options, label, onBlur }) => { {({ field, - // eslint-disable-next-line no-unused-vars + form: { touched, errors }, meta, }) => { diff --git a/packages/app-rfi/src/components/controls/RfiSelect.js b/packages/app-rfi/src/components/controls/RfiSelect.js index 9e54a46522..8c87e8b286 100644 --- a/packages/app-rfi/src/components/controls/RfiSelect.js +++ b/packages/app-rfi/src/components/controls/RfiSelect.js @@ -1,6 +1,6 @@ // DISABLED@ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field, useField, useFormikContext } from "formik"; import PropTypes from "prop-types"; import React, { useEffect } from "react"; diff --git a/packages/app-rfi/src/components/controls/RfiTextArea.js b/packages/app-rfi/src/components/controls/RfiTextArea.js index 8a9b5933a5..d77e809b50 100644 --- a/packages/app-rfi/src/components/controls/RfiTextArea.js +++ b/packages/app-rfi/src/components/controls/RfiTextArea.js @@ -1,6 +1,6 @@ // @ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field } from "formik"; import PropTypes from "prop-types"; import React from "react"; diff --git a/packages/app-rfi/src/components/controls/RfiTextInput.js b/packages/app-rfi/src/components/controls/RfiTextInput.js index 5c871519ae..f131df9df6 100644 --- a/packages/app-rfi/src/components/controls/RfiTextInput.js +++ b/packages/app-rfi/src/components/controls/RfiTextInput.js @@ -1,6 +1,6 @@ // @ts-check /* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import { Field, useFormikContext } from "formik"; import PropTypes from "prop-types"; import React from "react"; diff --git a/packages/app-rfi/src/components/controls/controls-helpers.js b/packages/app-rfi/src/components/controls/controls-helpers.js index 4731b7aae8..050d67a2fd 100644 --- a/packages/app-rfi/src/components/controls/controls-helpers.js +++ b/packages/app-rfi/src/components/controls/controls-helpers.js @@ -1,6 +1,5 @@ // @ts-check -/* eslint-disable no-unused-vars */ -/* eslint-disable react/jsx-props-no-spreading */ + import PropTypes from "prop-types"; import React from "react"; diff --git a/packages/app-rfi/src/components/stepper/RfiStepperButtons.js b/packages/app-rfi/src/components/stepper/RfiStepperButtons.js index e6c64ef7b3..8ecdb6c0b0 100644 --- a/packages/app-rfi/src/components/stepper/RfiStepperButtons.js +++ b/packages/app-rfi/src/components/stepper/RfiStepperButtons.js @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import React from "react"; import { Button } from "reactstrap"; -import { trackGAEvent } from "../../../../../shared"; +import { trackGAEvent } from "@asu/shared"; // Note on the spans around the FA i tags below: // When the host site/app deploys FA such that it replaces the i's with svg // tags, when React tries to rewrite the DOM we get hit with the error diff --git a/packages/app-rfi/src/components/steps/CertInfo.js b/packages/app-rfi/src/components/steps/CertInfo.js index b60bd445d0..1ba5f48b99 100644 --- a/packages/app-rfi/src/components/steps/CertInfo.js +++ b/packages/app-rfi/src/components/steps/CertInfo.js @@ -1,9 +1,8 @@ -/* eslint-disable react/no-danger */ // @ts-check import PropTypes from "prop-types"; import React from "react"; -import { sanitizeDangerousMarkup } from "../../../../../shared"; +import { sanitizeDangerousMarkup } from "@asu/shared"; // Component diff --git a/packages/app-rfi/src/components/steps/Success.js b/packages/app-rfi/src/components/steps/Success.js index 6648558d3b..9b0993df6f 100644 --- a/packages/app-rfi/src/components/steps/Success.js +++ b/packages/app-rfi/src/components/steps/Success.js @@ -1,8 +1,7 @@ -/* eslint-disable react/no-danger */ // @ts-check import React from "react"; -import { sanitizeDangerousMarkup } from "../../../../../shared"; +import { sanitizeDangerousMarkup } from "@asu/shared"; import { useRfiContext } from "../../core/utils/rfiContext"; // Component diff --git a/packages/app-rfi/src/components/steps/questions/Campus.js b/packages/app-rfi/src/components/steps/questions/Campus.js index 94b655ce99..7a5e93e74a 100644 --- a/packages/app-rfi/src/components/steps/questions/Campus.js +++ b/packages/app-rfi/src/components/steps/questions/Campus.js @@ -1,14 +1,14 @@ import React, { useEffect } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { CAMPUS_OPTIONS_DEFAULT, KEY } from "../../../core/utils/constants"; import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiSelect } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ -// eslint-disable-next-line react/prop-types + export const Campus = ({ gaData, autoFocus }) => { const label = "Which applies to you?"; const name = "Campus"; diff --git a/packages/app-rfi/src/components/steps/questions/CampusProgramHasChoice.js b/packages/app-rfi/src/components/steps/questions/CampusProgramHasChoice.js index 8e7bfd097a..7daa29d5ad 100644 --- a/packages/app-rfi/src/components/steps/questions/CampusProgramHasChoice.js +++ b/packages/app-rfi/src/components/steps/questions/CampusProgramHasChoice.js @@ -1,12 +1,12 @@ import React, { useEffect } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { CAMPUS_OPTIONS_DEFAULT, KEY } from "../../../core/utils/constants"; import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiSelect } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const CampusProgramHasChoice = ({ gaData }) => { const label = "Which applies to you?"; diff --git a/packages/app-rfi/src/components/steps/questions/CareerAndStudentType.js b/packages/app-rfi/src/components/steps/questions/CareerAndStudentType.js index 529bb64758..67a089a7d7 100644 --- a/packages/app-rfi/src/components/steps/questions/CareerAndStudentType.js +++ b/packages/app-rfi/src/components/steps/questions/CareerAndStudentType.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { KEY, STUDENT, @@ -10,7 +10,7 @@ import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiSelect } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const CareerAndStudentType = ({ gaData }) => { const label = "Select your student status"; diff --git a/packages/app-rfi/src/components/steps/questions/Country.js b/packages/app-rfi/src/components/steps/questions/Country.js index 1c2056b362..2b2611c5db 100644 --- a/packages/app-rfi/src/components/steps/questions/Country.js +++ b/packages/app-rfi/src/components/steps/questions/Country.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { PII_VALUE } from "../../../core/utils/constants"; import { fetchCountries } from "../../../core/utils/fetchCountries"; import { useRfiContext } from "../../../core/utils/rfiContext"; @@ -10,7 +10,7 @@ import { RfiSelect } from "../../controls"; function getCountryOptions(resultsArrayOfObjects) { let i = 1; // TODO Resolve eslint error when dust settles. Not hurting anything for now. - // eslint-disable-next-line no-return-assign + const results = resultsArrayOfObjects.map(co => ({ key: (i += 1).toString(), value: co.countryCodeTwoChar, @@ -22,7 +22,7 @@ function getCountryOptions(resultsArrayOfObjects) { // Component /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const Country = ({ gaData }) => { const label = "Country of citizenship"; diff --git a/packages/app-rfi/src/components/steps/questions/EmailAddress.js b/packages/app-rfi/src/components/steps/questions/EmailAddress.js index 2a5b93aa58..b5f4c2b316 100644 --- a/packages/app-rfi/src/components/steps/questions/EmailAddress.js +++ b/packages/app-rfi/src/components/steps/questions/EmailAddress.js @@ -1,13 +1,13 @@ import React from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { PII_VALUE } from "../../../core/utils/constants"; import { RfiEmailInput } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ -// eslint-disable-next-line react/prop-types + export const EmailAddress = ({ gaData, autoFocus }) => { const label = "Email Address"; const name = "EmailAddress"; diff --git a/packages/app-rfi/src/components/steps/questions/EntryTerm.js b/packages/app-rfi/src/components/steps/questions/EntryTerm.js index 83162bff40..28ea8fecc7 100644 --- a/packages/app-rfi/src/components/steps/questions/EntryTerm.js +++ b/packages/app-rfi/src/components/steps/questions/EntryTerm.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { KEY } from "../../../core/utils/constants"; import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiSelect } from "../../controls"; @@ -36,7 +36,7 @@ const getGenericTermOptions = () => { }; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const EntryTerm = ({ gaData }) => { const label = "When do you anticipate starting at ASU?"; diff --git a/packages/app-rfi/src/components/steps/questions/FirstName.js b/packages/app-rfi/src/components/steps/questions/FirstName.js index 1439b3bbfe..9714852482 100644 --- a/packages/app-rfi/src/components/steps/questions/FirstName.js +++ b/packages/app-rfi/src/components/steps/questions/FirstName.js @@ -1,11 +1,11 @@ import React from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { PII_VALUE } from "../../../core/utils/constants"; import { RfiTextInput } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const FirstName = ({ gaData }) => { const label = "First name"; diff --git a/packages/app-rfi/src/components/steps/questions/GdprConsent.js b/packages/app-rfi/src/components/steps/questions/GdprConsent.js index 4b984060a8..5120e04391 100644 --- a/packages/app-rfi/src/components/steps/questions/GdprConsent.js +++ b/packages/app-rfi/src/components/steps/questions/GdprConsent.js @@ -1,13 +1,12 @@ -/* eslint-disable react/no-danger */ import React from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { KEY } from "../../../core/utils/constants"; import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiCheckboxSingle } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const GdprConsent = ({ gaData }) => { const { diff --git a/packages/app-rfi/src/components/steps/questions/Interest1.js b/packages/app-rfi/src/components/steps/questions/Interest1.js index 1bbfc64099..613cbc3a54 100644 --- a/packages/app-rfi/src/components/steps/questions/Interest1.js +++ b/packages/app-rfi/src/components/steps/questions/Interest1.js @@ -1,12 +1,12 @@ import React, { useEffect, useState } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { FAILED_OPTIONS_DEFAULT, KEY } from "../../../core/utils/constants"; import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiSelect } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const Interest1 = ({ gaData }) => { const label = "Area of interest"; diff --git a/packages/app-rfi/src/components/steps/questions/Interest2.js b/packages/app-rfi/src/components/steps/questions/Interest2.js index 60dfa1f8a5..d7b02f5e92 100644 --- a/packages/app-rfi/src/components/steps/questions/Interest2.js +++ b/packages/app-rfi/src/components/steps/questions/Interest2.js @@ -1,12 +1,12 @@ import React, { useEffect, useState } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { FAILED_OPTIONS_DEFAULT, KEY } from "../../../core/utils/constants"; import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiSelect } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const Interest2 = ({ gaData }) => { const label = "Program of interest"; diff --git a/packages/app-rfi/src/components/steps/questions/LastName.js b/packages/app-rfi/src/components/steps/questions/LastName.js index d1a460328d..11d89c1764 100644 --- a/packages/app-rfi/src/components/steps/questions/LastName.js +++ b/packages/app-rfi/src/components/steps/questions/LastName.js @@ -1,11 +1,11 @@ import React from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { PII_VALUE } from "../../../core/utils/constants"; import { RfiTextInput } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const LastName = ({ gaData }) => { const label = "Last name"; diff --git a/packages/app-rfi/src/components/steps/questions/MilitaryStatus.js b/packages/app-rfi/src/components/steps/questions/MilitaryStatus.js index 50b8af0b74..4b9f3d46cf 100644 --- a/packages/app-rfi/src/components/steps/questions/MilitaryStatus.js +++ b/packages/app-rfi/src/components/steps/questions/MilitaryStatus.js @@ -1,6 +1,6 @@ import React from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { RfiRadioGroup } from "../../controls"; /** @@ -19,7 +19,7 @@ const militaryOptions = [ ]; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const MilitaryStatus = ({ gaData }) => { const label = diff --git a/packages/app-rfi/src/components/steps/questions/Phone.js b/packages/app-rfi/src/components/steps/questions/Phone.js index 83586491c7..d137d044ab 100644 --- a/packages/app-rfi/src/components/steps/questions/Phone.js +++ b/packages/app-rfi/src/components/steps/questions/Phone.js @@ -1,11 +1,11 @@ import React from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { PII_VALUE } from "../../../core/utils/constants"; import { RfiPhone } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const Phone = ({ gaData }) => { const label = "Phone"; diff --git a/packages/app-rfi/src/components/steps/questions/ZipCode.js b/packages/app-rfi/src/components/steps/questions/ZipCode.js index e941b8afbc..878b7f4216 100644 --- a/packages/app-rfi/src/components/steps/questions/ZipCode.js +++ b/packages/app-rfi/src/components/steps/questions/ZipCode.js @@ -1,12 +1,12 @@ import React, { useEffect } from "react"; -import { gaEventPropTypes, trackGAEvent } from "../../../../../../shared"; +import { gaEventPropTypes, trackGAEvent } from "@asu/shared"; import { KEY, PII_VALUE } from "../../../core/utils/constants"; import { useRfiContext } from "../../../core/utils/rfiContext"; import { RfiTextInput } from "../../controls"; /** - * @param {{ gaData: import("../../../../../../shared/services/googleAnalytics").GAEventObject}} props + * @param {{ gaData: import("@asu/shared").GAEventObject}} props */ export const ZipCode = ({ gaData }) => { const label = "Postal code"; diff --git a/packages/app-rfi/src/core/utils/google-analytics.js b/packages/app-rfi/src/core/utils/google-analytics.js index e3237824a2..c16ed9c315 100644 --- a/packages/app-rfi/src/core/utils/google-analytics.js +++ b/packages/app-rfi/src/core/utils/google-analytics.js @@ -1,5 +1,4 @@ // @ts-nocheck -/* eslint-disable no-undef, func-names, no-unused-vars, no-plusplus */ /* dataLayer event push to GA. */ export function pushDataLayerEventToGa(eventValue) { diff --git a/packages/app-rfi/src/core/utils/submission-helpers.js b/packages/app-rfi/src/core/utils/submission-helpers.js index 5917249e45..c27a2e9cac 100644 --- a/packages/app-rfi/src/core/utils/submission-helpers.js +++ b/packages/app-rfi/src/core/utils/submission-helpers.js @@ -1,5 +1,5 @@ // @ts-check -import { deepCloner } from "../../../../../shared/utils"; +import { deepCloner } from "@asu/shared"; import { KEY } from "./constants"; import { pushDataLayerEventToGa, setClientId } from "./google-analytics"; diff --git a/packages/app-rfi/vite.config.js b/packages/app-rfi/vite.config.js index 1793f9b42c..e2ba231ee9 100644 --- a/packages/app-rfi/vite.config.js +++ b/packages/app-rfi/vite.config.js @@ -32,6 +32,11 @@ export default defineConfig({ process: {env: {NODE_ENV: process.env.NODE_ENV}}, global: {} }, + resolve: { + alias: { + "@asu/shared": resolve(__dirname, "../shared"), + } + }, plugins: [ react(), { diff --git a/packages/app-webdir-ui/.eslintrc.js b/packages/app-webdir-ui/.eslintrc.js deleted file mode 100644 index 9c07c9028d..0000000000 --- a/packages/app-webdir-ui/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], -}; diff --git a/packages/app-webdir-ui/eslint.config.js b/packages/app-webdir-ui/eslint.config.js new file mode 100644 index 0000000000..6b534f22b4 --- /dev/null +++ b/packages/app-webdir-ui/eslint.config.js @@ -0,0 +1,5 @@ +const rootConfig = require("../../eslint.config.js"); + +module.exports = [ + ...rootConfig, +]; diff --git a/packages/app-webdir-ui/package.json b/packages/app-webdir-ui/package.json index 67ee16031e..5cb280eaa4 100644 --- a/packages/app-webdir-ui/package.json +++ b/packages/app-webdir-ui/package.json @@ -23,7 +23,7 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx}' ", "test": "jest --config=./jest.config.js --passWithNoTests --silent --coverage", "test-update-snapshot": "yarn test -- -u", "prebuild": "rm -rf ./dist", @@ -47,6 +47,7 @@ "styled-components": "^5.3.0" }, "devDependencies": { + "@asu/shared": "*", "@babel/core": "^7.13.14", "@babel/plugin-syntax-jsx": "^7.14.5", "@babel/plugin-transform-react-jsx": "^7.13.12", @@ -67,7 +68,6 @@ "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.2.4", "dotenv-webpack": "^7.0.3", - "eslint-plugin-storybook": "^0.6.15", "express": "^4.17.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.3.2", diff --git a/packages/app-webdir-ui/src/FacultyRankComponent/index.stories.js b/packages/app-webdir-ui/src/FacultyRankComponent/index.stories.js index ea4af27ab3..de2f00c73e 100644 --- a/packages/app-webdir-ui/src/FacultyRankComponent/index.stories.js +++ b/packages/app-webdir-ui/src/FacultyRankComponent/index.stories.js @@ -1,6 +1,6 @@ import React from "react"; -import { FullLayout } from "../../../../shared/components/Layout"; +import { FullLayout } from "@asu/shared"; import { WebDirectory } from "../WebDirectoryComponent/index"; export default { diff --git a/packages/app-webdir-ui/src/PreSearchMessages/PreSearchMessage/index.stories.js b/packages/app-webdir-ui/src/PreSearchMessages/PreSearchMessage/index.stories.js index 32fb0b10ee..4e6539011e 100644 --- a/packages/app-webdir-ui/src/PreSearchMessages/PreSearchMessage/index.stories.js +++ b/packages/app-webdir-ui/src/PreSearchMessages/PreSearchMessage/index.stories.js @@ -1,6 +1,6 @@ import React from "react"; -import { defaultDecorator } from "../../../../../shared/components/Layout"; +import { defaultDecorator } from "@asu/shared"; import { searchMessageComponentMap } from "../index"; export default { diff --git a/packages/app-webdir-ui/src/ProfileCard/index.js b/packages/app-webdir-ui/src/ProfileCard/index.js index f89cebd88b..4d7f73b334 100644 --- a/packages/app-webdir-ui/src/ProfileCard/index.js +++ b/packages/app-webdir-ui/src/ProfileCard/index.js @@ -1,6 +1,6 @@ import React from "react"; -import { trackGAEvent } from "../../../../shared"; +import { trackGAEvent } from "@asu/shared"; import { ProfileCardLayout } from "./index.styles"; import { profileCardType } from "./models"; diff --git a/packages/app-webdir-ui/src/ProfileCard/index.stories.js b/packages/app-webdir-ui/src/ProfileCard/index.stories.js index 641821d44e..60134c147c 100644 --- a/packages/app-webdir-ui/src/ProfileCard/index.stories.js +++ b/packages/app-webdir-ui/src/ProfileCard/index.stories.js @@ -1,6 +1,6 @@ import React from "react"; -import { defaultDecorator } from "../../../../shared/components/Layout"; +import { defaultDecorator } from "@asu/shared"; import { staffConverter } from "../helpers/dataConverter"; import data from "./mock-data"; diff --git a/packages/app-webdir-ui/src/QuickLinks/index.stories.js b/packages/app-webdir-ui/src/QuickLinks/index.stories.js index 1e3badf13a..159c3fa0cb 100644 --- a/packages/app-webdir-ui/src/QuickLinks/index.stories.js +++ b/packages/app-webdir-ui/src/QuickLinks/index.stories.js @@ -2,7 +2,7 @@ import React from "react"; import { QuickLinks } from "./index"; -import { defaultDecorator } from "../../../../shared/components/Layout"; +import { defaultDecorator } from "@asu/shared"; export default { title: "Atoms/QuickLinks/Template", diff --git a/packages/app-webdir-ui/src/ResultCard/index.js b/packages/app-webdir-ui/src/ResultCard/index.js index 50c007bab8..dd14a2a682 100644 --- a/packages/app-webdir-ui/src/ResultCard/index.js +++ b/packages/app-webdir-ui/src/ResultCard/index.js @@ -1,7 +1,7 @@ import React from "react"; import { useSearchParams } from "react-router-dom"; -import { trackGAEvent } from "../../../../shared"; +import { trackGAEvent } from "@asu/shared"; import { ResultCardTemplate } from "./index.styles"; import { resultCardType } from "./models"; diff --git a/packages/app-webdir-ui/src/ResultCard/index.stories.js b/packages/app-webdir-ui/src/ResultCard/index.stories.js index 4953a6d55e..e7d2313862 100644 --- a/packages/app-webdir-ui/src/ResultCard/index.stories.js +++ b/packages/app-webdir-ui/src/ResultCard/index.stories.js @@ -1,6 +1,6 @@ import React from "react"; -import { defaultDecorator } from "../../../../shared/components/Layout"; +import { defaultDecorator } from "@asu/shared"; import { subdomainConverter } from "../helpers/dataConverter"; import data from "./mock-data"; diff --git a/packages/app-webdir-ui/src/SearchPage/components/sort.js b/packages/app-webdir-ui/src/SearchPage/components/sort.js index 734ac0d69b..0bbdaf1082 100644 --- a/packages/app-webdir-ui/src/SearchPage/components/sort.js +++ b/packages/app-webdir-ui/src/SearchPage/components/sort.js @@ -1,7 +1,7 @@ import PropTypes from "prop-types"; import React, { useState, useEffect, useId } from "react"; -import { trackGAEvent } from "../../../../../shared"; +import { trackGAEvent } from "@asu/shared"; import { SortLayout } from "./index.styles"; /** diff --git a/packages/app-webdir-ui/src/SearchPage/components/tabs/all.js b/packages/app-webdir-ui/src/SearchPage/components/tabs/all.js index 3d4899726f..68df27fc3d 100644 --- a/packages/app-webdir-ui/src/SearchPage/components/tabs/all.js +++ b/packages/app-webdir-ui/src/SearchPage/components/tabs/all.js @@ -168,7 +168,7 @@ const AllTab = ({ AllTab.propTypes = { term: PropTypes.string, - // eslint-disable-next-line react/forbid-prop-types + engines: PropTypes.object, site: PropTypes.string, goToTab: PropTypes.func, diff --git a/packages/app-webdir-ui/src/SearchPage/components/tabs/faculty.js b/packages/app-webdir-ui/src/SearchPage/components/tabs/faculty.js index 80e6986932..809b846279 100644 --- a/packages/app-webdir-ui/src/SearchPage/components/tabs/faculty.js +++ b/packages/app-webdir-ui/src/SearchPage/components/tabs/faculty.js @@ -42,7 +42,7 @@ const FacultyTab = ({ engines, term, sort, onSortChange }) => { FacultyTab.propTypes = { term: PropTypes.string, - // eslint-disable-next-line react/forbid-prop-types + engines: PropTypes.object, sort: PropTypes.string, onSortChange: PropTypes.func, diff --git a/packages/app-webdir-ui/src/SearchPage/components/tabs/local.js b/packages/app-webdir-ui/src/SearchPage/components/tabs/local.js index 178f16ab0b..0fa921aa1d 100644 --- a/packages/app-webdir-ui/src/SearchPage/components/tabs/local.js +++ b/packages/app-webdir-ui/src/SearchPage/components/tabs/local.js @@ -34,7 +34,7 @@ const LocalTab = ({ engines, term, site, loggedIn }) => { LocalTab.propTypes = { term: PropTypes.string, - // eslint-disable-next-line react/forbid-prop-types + engines: PropTypes.object, site: PropTypes.string, loggedIn: PropTypes.bool, diff --git a/packages/app-webdir-ui/src/SearchPage/components/tabs/students.js b/packages/app-webdir-ui/src/SearchPage/components/tabs/students.js index df74c304c6..c4681e358e 100644 --- a/packages/app-webdir-ui/src/SearchPage/components/tabs/students.js +++ b/packages/app-webdir-ui/src/SearchPage/components/tabs/students.js @@ -35,7 +35,7 @@ const StudentsTab = ({ engines, term, loggedIn }) => { StudentsTab.propTypes = { term: PropTypes.string, - // eslint-disable-next-line react/forbid-prop-types + engines: PropTypes.object, loggedIn: PropTypes.bool, }; diff --git a/packages/app-webdir-ui/src/SearchPage/index.js b/packages/app-webdir-ui/src/SearchPage/index.js index 344a37be03..41ebb97ee5 100644 --- a/packages/app-webdir-ui/src/SearchPage/index.js +++ b/packages/app-webdir-ui/src/SearchPage/index.js @@ -74,9 +74,8 @@ function SearchPage({ const updateSearchParams = (param, newValue) => { const newParams = {}; - // eslint-disable-next-line no-restricted-syntax + for (const entry of searchParams.entries()) { - // eslint-disable-next-line prefer-destructuring newParams[entry[0]] = entry[1]; } newParams[param] = newValue; diff --git a/packages/app-webdir-ui/src/SearchPage/index.stories.js b/packages/app-webdir-ui/src/SearchPage/index.stories.js index ebf8f02abf..e820ac7635 100644 --- a/packages/app-webdir-ui/src/SearchPage/index.stories.js +++ b/packages/app-webdir-ui/src/SearchPage/index.stories.js @@ -2,7 +2,7 @@ import React from "react"; import { SearchPage } from "./index"; -import { FullLayout } from "../../../../shared/components/Layout"; +import { FullLayout } from "@asu/shared"; export default { title: "Organisms/Search Page/Templates", diff --git a/packages/app-webdir-ui/src/SearchResultsList/dataFormatter.js b/packages/app-webdir-ui/src/SearchResultsList/dataFormatter.js index 2253daa72f..58196f784b 100644 --- a/packages/app-webdir-ui/src/SearchResultsList/dataFormatter.js +++ b/packages/app-webdir-ui/src/SearchResultsList/dataFormatter.js @@ -1,4 +1,3 @@ -/* eslint-disable camelcase */ import axios from "axios"; import { validateAndCleanURL } from "../helpers/validateUrl"; diff --git a/packages/app-webdir-ui/src/SearchResultsList/index.js b/packages/app-webdir-ui/src/SearchResultsList/index.js index 740c2e75e9..ab57e023bc 100644 --- a/packages/app-webdir-ui/src/SearchResultsList/index.js +++ b/packages/app-webdir-ui/src/SearchResultsList/index.js @@ -2,7 +2,7 @@ import { Button, Pagination } from "@asu/unity-react-core"; import PropTypes from "prop-types"; import React, { useState, useEffect, useRef } from "react"; -import { trackGAEvent } from "../../../../shared"; +import { trackGAEvent } from "@asu/shared"; import Grid from "../grid/grid"; import { performSearch } from "../helpers/search"; import { SearchMessage } from "../SearchPage/components/SearchMessage"; @@ -289,7 +289,7 @@ const ASUSearchResultsList = ({ ASUSearchResultsList.propTypes = { term: PropTypes.string, type: PropTypes.string, - // eslint-disable-next-line react/forbid-prop-types + engine: PropTypes.object, seeAllResultsText: PropTypes.string, sort: PropTypes.string, @@ -301,7 +301,7 @@ ASUSearchResultsList.propTypes = { setPromotedResult: PropTypes.func, hidePaginator: PropTypes.bool, registerResults: PropTypes.func, - // eslint-disable-next-line react/forbid-prop-types + filters: PropTypes.object, loggedIn: PropTypes.bool, profilesToFilterOut: PropTypes.string, diff --git a/packages/app-webdir-ui/src/WebDirectoryComponent/index.js b/packages/app-webdir-ui/src/WebDirectoryComponent/index.js index 6daebdc649..f33a3d2e05 100644 --- a/packages/app-webdir-ui/src/WebDirectoryComponent/index.js +++ b/packages/app-webdir-ui/src/WebDirectoryComponent/index.js @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import React, { useState, useEffect } from "react"; -import trackReactComponent from "../../../../shared/services/componentDatalayer"; +import { trackReactComponent } from "@asu/shared"; import FacultyRankTabPanels from "../FacultyRankComponent"; import { FILTER_CHOICES } from "../helpers/constants"; import { FilterComponent } from "../helpers/Filter"; diff --git a/packages/app-webdir-ui/src/WebDirectoryComponent/index.stories.js b/packages/app-webdir-ui/src/WebDirectoryComponent/index.stories.js index 18bd029d24..274c0d726d 100644 --- a/packages/app-webdir-ui/src/WebDirectoryComponent/index.stories.js +++ b/packages/app-webdir-ui/src/WebDirectoryComponent/index.stories.js @@ -2,7 +2,7 @@ import React from "react"; import { WebDirectory } from "./index"; -import { FullLayout } from "../../../../shared/components/Layout"; +import { FullLayout } from "@asu/shared"; export default { title: "Organisms/Web Directory/Templates", diff --git a/packages/app-webdir-ui/src/helpers/dataConverter.js b/packages/app-webdir-ui/src/helpers/dataConverter.js index b4eab126ef..544a622adc 100644 --- a/packages/app-webdir-ui/src/helpers/dataConverter.js +++ b/packages/app-webdir-ui/src/helpers/dataConverter.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-destructuring */ import React from "react"; import anonPic from "../assets/anon.png"; diff --git a/packages/app-webdir-ui/src/helpers/init-app-webdir-ui.js b/packages/app-webdir-ui/src/helpers/init-app-webdir-ui.js index c396639be9..0ec9f0e9b9 100644 --- a/packages/app-webdir-ui/src/helpers/init-app-webdir-ui.js +++ b/packages/app-webdir-ui/src/helpers/init-app-webdir-ui.js @@ -1,4 +1,3 @@ -/* eslint-disable quote-props */ // @ts-check import React from "react"; import { createRoot } from "react-dom/client"; diff --git a/packages/app-webdir-ui/src/helpers/search.js b/packages/app-webdir-ui/src/helpers/search.js index 71c0cf11f9..93b5912163 100644 --- a/packages/app-webdir-ui/src/helpers/search.js +++ b/packages/app-webdir-ui/src/helpers/search.js @@ -181,9 +181,8 @@ const webDirDeptsFormatter = ({ let localPage = 1; if (engines[engineName].name === engineNames.WEB_DIRECTORY_PEOPLE_AND_DEPS) { localResults = results.map(datum => { - // eslint-disable-next-line camelcase const { full_record, ...basicFields } = datum; - // eslint-disable-next-line camelcase + return { ...basicFields, ...full_record }; }); } else { diff --git a/packages/component-cookie-consent/.eslintrc.js b/packages/component-cookie-consent/.eslintrc.js deleted file mode 100644 index 9c07c9028d..0000000000 --- a/packages/component-cookie-consent/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], -}; diff --git a/packages/component-cookie-consent/.gitignore b/packages/component-cookie-consent/.gitignore deleted file mode 100644 index aa01ff727d..0000000000 --- a/packages/component-cookie-consent/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -/*.log - -package-lock.json -yarn-lock.json diff --git a/packages/component-cookie-consent/.storybook/main.js b/packages/component-cookie-consent/.storybook/main.js deleted file mode 100644 index ee8a6497d2..0000000000 --- a/packages/component-cookie-consent/.storybook/main.js +++ /dev/null @@ -1,34 +0,0 @@ -const common = require("../webpack/webpack.common"); - -const config = { - staticDirs: ['../dist'], - addons: [ - "../../../.storybook-config", - "../../../.storybook-config/dataLayerListener", - "@storybook/addon-controls", - "@storybook/addon-viewport", - "@storybook/addon-a11y", - ], - stories: ["../src/*.stories.js"], - framework: { - name: "@storybook/react-webpack5", - options: {} - }, - webpackFinal: async config => { - return { - ...config, - resolve: { - extensions: [".js", ".jsx"], - alias: { - ...common.resolve.alias, - }, - }, - }; - }, - - docs: { - autodocs: true - } -}; - -export default config; diff --git a/packages/component-cookie-consent/.storybook/manager.js b/packages/component-cookie-consent/.storybook/manager.js deleted file mode 100644 index 5676dcef41..0000000000 --- a/packages/component-cookie-consent/.storybook/manager.js +++ /dev/null @@ -1,5 +0,0 @@ -import { addons } from "@storybook/addons"; - -addons.setConfig({ - showRoots: true, -}); diff --git a/packages/component-cookie-consent/.storybook/preview-head.html b/packages/component-cookie-consent/.storybook/preview-head.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/component-cookie-consent/.storybook/preview.js b/packages/component-cookie-consent/.storybook/preview.js deleted file mode 100644 index eb6d5d5462..0000000000 --- a/packages/component-cookie-consent/.storybook/preview.js +++ /dev/null @@ -1,11 +0,0 @@ -const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, - layout: "fullscreen", -}; - -/** @type { import('@storybook/react').Preview } */ -const preview = { - parameters -}; - -export default preview; diff --git a/packages/component-cookie-consent/CHANGELOG.md b/packages/component-cookie-consent/CHANGELOG.md deleted file mode 100644 index f65d200c33..0000000000 --- a/packages/component-cookie-consent/CHANGELOG.md +++ /dev/null @@ -1,34 +0,0 @@ -# [@asu/component-cookie-consent-v2.1.0](https://github.com/asu/asu-unity-stack/compare/@asu/component-cookie-consent-v2.0.1...@asu/component-cookie-consent-v2.1.0) (2024-01-11) - - -### Bug Fixes - -* renamed shared folder to match sahred/services ([860c6f4](https://github.com/asu/asu-unity-stack/commit/860c6f44d42119956cbaa36d8c9d8798613c76fa)) - - -### Features - -* **component-cookie-consent:** added gtm global variable tracking ([9a4b51f](https://github.com/asu/asu-unity-stack/commit/9a4b51fa0eafa861147b077d559e32650bf98bed)) - -# [@asu/component-cookie-consent-v2.0.1](https://github.com/asu/asu-unity-stack/compare/@asu/component-cookie-consent-v2.0.0...@asu/component-cookie-consent-v2.0.1) (2023-05-23) - - -### Bug Fixes - -* **component-cookie-consent:** changelog cleanup ([e09c6fb](https://github.com/asu/asu-unity-stack/commit/e09c6fb852458686c833f74da87bacb72a7a74c5)) - -# [@asu/component-cookie-consent-v2.0.0](https://github.com/asu/asu-unity-stack/compare/@asu/component-cookie-consent-v1.0.2...@asu/component-cookie-consent-v2.0.0) (2023-05-23) - - -### Features - -* **unity-bootstrap-theme:** MAIN migration to BS5 PR ([#992](https://github.com/asu/asu-unity-stack/issues/992)) ([8c74ce4](https://github.com/asu/asu-unity-stack/commit/8c74ce4dc65278839b207b9ae895ea76e8e2195d)) - - -### BREAKING CHANGES - -* **unity-bootstrap-theme:** New BS5 theme is used
    -update theme dependency from bootstrap4-theme to unity-bootstrap-theme
    -see unity-bootstrap-theme UPGRADE.md for more information.
    -refers to UDS-1277
    -BREAKING CHANGE: updates theme dependency and markup for Bootstrap 5 compatibility. diff --git a/packages/component-cookie-consent/README.md b/packages/component-cookie-consent/README.md deleted file mode 100644 index 4ec30344f0..0000000000 --- a/packages/component-cookie-consent/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# ASU Cookie Consent -ASU Web Standards-based implementation of Cookie Consent. - -This is a small react component with the implementation of ASU Cookie Consent. - -## Architecture details -This component is a simple react application, that shows the cookie consent banner depending on the `localStorage` value setted. When a user accept or close the banner we set a pair, key/value on the `localStorage` to continue or not showing the banner in the future. -The initialization of this component is the same as all the packages, so it can keep uniqueness between all of them. For this, we use two methods: - - `createElement`: this is provided by React. It creates a new React element. Click to read more about [createElement](https://reactjs.org/docs/react-api.html#createelement) and [react without jsx](https://reactjs.org/docs/react-without-jsx.html). - - `render`: this is provided by ReactDOM library. It is used to render the react element in the DOM. Click to read more about [render](https://reactjs.org/docs/react-dom.html). -The way it works is: when the initializer function is called, this creates the element with the props provided and the react component.Then it is rendered on the DOM. - -## Props and settings -```JS -/** - * @typedef {{ - * enableCookieConsent: boolean - * expirationTime?: number - * }} CookieConsentProps - */ -``` - -## Component props documentation - -You can find a full list of props into the [docs/README.props.md](docs/README.props.md) - - - - - -
    - - - -
    - -## CLI Commands - -``` bash -# add component-footer -yarn add @asu/component-cookie-consent - -# run storybook -yarn storybook - -# build for production with minification -yarn build - -# run tests -yarn test - -``` - -## How to install - -1. Make sure you are set up to use the private npm registry at registry.web.asu.edu. See instructures in the 'How to use private package registry' here: [README.md](../../README.md) -2. ```yarn add @asu/component-cookie-consent@dev``` - -## Use as a JS module in React app - -### Default import -```JAVASCRIPT - import { CookieConsent } from '@asu/component-cookie-consent@dev' -``` - -### Import for use in HTML page -You can find an example of how to set `CookieConsent` props [here](/packages/component-cookie-consent/examples/cookie-consent.html) - -## Example - -```JS -{ - enableCookieConsent: true, - expirationTime: 90, // Time in days -}; -``` - -## Future improvements -All the requirements for this component were covered, so there is no need of any further enhancements at the moment this is being written. diff --git a/packages/component-cookie-consent/babel.config.js b/packages/component-cookie-consent/babel.config.js deleted file mode 100644 index 4fe412bbb5..0000000000 --- a/packages/component-cookie-consent/babel.config.js +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = api => { - api.cache(true); - - return { - presets: [ - ["@babel/preset-env", { targets: { node: "current" } }], - "@babel/preset-react", - ], - plugins: [ - "@babel/plugin-syntax-jsx", - "@babel/plugin-transform-react-jsx", - "@babel/plugin-transform-runtime", - ], - ignore: ["node_modules"], - env: { - test: { - plugins: [ - "@babel/plugin-syntax-jsx", - "babel-plugin-dynamic-import-node", - "@babel/plugin-transform-react-jsx", - ], - presets: [ - [ - "@babel/preset-env", - { - targets: { node: "current" }, - }, - ], - "@babel/preset-react", - ], - }, - }, - }; -}; diff --git a/packages/component-cookie-consent/docs/README.props.md b/packages/component-cookie-consent/docs/README.props.md deleted file mode 100644 index cfd7b2b8a3..0000000000 --- a/packages/component-cookie-consent/docs/README.props.md +++ /dev/null @@ -1,11 +0,0 @@ -
    - -## CookieConsentProps : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| enableCookieConsent | boolean | -| [expirationTime] | number | - diff --git a/packages/component-cookie-consent/examples/cookie-consent.html b/packages/component-cookie-consent/examples/cookie-consent.html deleted file mode 100644 index b6e4c75f37..0000000000 --- a/packages/component-cookie-consent/examples/cookie-consent.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - Cookie Consent - Example page - - - - - - - - - - - - - - - - diff --git a/packages/component-cookie-consent/jest.config.js b/packages/component-cookie-consent/jest.config.js deleted file mode 100644 index 333d06ad4c..0000000000 --- a/packages/component-cookie-consent/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -// @ts-check -module.exports = { - verbose: true, - testEnvironment: "jsdom", - setupFilesAfterEnv: ["./setupTests.js"], - transform: { - "^.+\\.js$": "babel-jest", - "^.+\\.(js|jsx)$": "babel-jest", - "^.+\\.css$": "jest-transform-css", - "\\.(jpg|jpeg|png|gif|webp|svg)$": "jest-transform-file", - }, -}; diff --git a/packages/component-cookie-consent/jsdoc.config.js b/packages/component-cookie-consent/jsdoc.config.js deleted file mode 100644 index 2809f4d224..0000000000 --- a/packages/component-cookie-consent/jsdoc.config.js +++ /dev/null @@ -1,11 +0,0 @@ -// @ts-check - -const coreConfig = require("../../jsdoc.config"); - -module.exports = { - ...coreConfig, - source: { - ...coreConfig.source, - include: [...coreConfig.source.include, "./src"], - }, -}; diff --git a/packages/component-cookie-consent/package.json b/packages/component-cookie-consent/package.json deleted file mode 100644 index 35b7407188..0000000000 --- a/packages/component-cookie-consent/package.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "@asu/component-cookie-consent", - "version": "2.1.0", - "description": "ASU Cookie Consent", - "main": "./dist/asuCookieConsent.cjs.js", - "browser": "./dist/asuCookieConsent.umd.js", - "module": "./dist/asuCookieConsent.es.js", - "types": "./dist/main.d.ts", - "author": "Nicolas Baudon", - "license": "MIT", - "files": [ - "examples/*", - "dist/*", - "docs/*", - "CHANGELOG.md" - ], - "repository": { - "type": "git", - "url": "https://github.com/asu/asu-unity-stack.git", - "directory": "packages/component-cookie-consent" - }, - "publishConfig": { - "access": "public", - "registry": "https://npm.pkg.github.com" - }, - "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", - "test": "jest --config=./jest.config.js --silent --coverage --passWithNoTests", - "test-update-snapshot": "yarn test -- -u", - "prebuild": "rm -rf ./dist", - "build": "webpack -c webpack/webpack.prod.js", - "build:stats": "webpack -c webpack/webpack.prod.js --profile --json=compilation-stats.json", - "postbuild": "cp ./types/main.d.ts ./dist/main.d.ts", - "start:dev": "webpack-dashboard -- webpack serve -c webpack/webpack.dev.js", - "storybook": "storybook dev -p 9050", - "build-storybook": "storybook build -o ../../build/$npm_package_name", - "predocs": "mkdir -p ./docs", - "docs": "jsdoc2md --no-cache -c jsdoc.config.js --files ./src > ./docs/README.props.md", - "postdocs": "node ../../scripts/process-readme-props.js" - }, - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.13.12", - "@fortawesome/free-solid-svg-icons": "^6.4.2", - "@fortawesome/react-fontawesome": "^0.2.0", - "prop-types": "^15.7.2", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "styled-components": "^5.3.0" - }, - "devDependencies": { - "@babel/core": "^7.13.14", - "@babel/plugin-syntax-jsx": "^7.14.5", - "@babel/plugin-transform-react-jsx": "^7.13.12", - "@babel/plugin-transform-runtime": "^7.14.5", - "@storybook/addon-a11y": "^7.6.14", - "@storybook/addon-essentials": "^7.6.14", - "@storybook/addon-links": "^7.6.14", - "@storybook/react": "^7.6.14", - "@storybook/react-webpack5": "^7.6.14", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/react": "^16.0.0", - "babel-loader": "^8.2.2", - "copy-webpack-plugin": "^9.0.1", - "css-loader": "^5.2.4", - "dotenv-webpack": "^7.0.3", - "eslint-plugin-storybook": "^0.6.15", - "express": "^4.17.1", - "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.3.2", - "http-proxy-middleware": "^2.0.0", - "jest": "^26.6.3", - "jest-image-snapshot": "^4.4.1", - "jest-transform-css": "^6.0.1", - "jest-transform-file": "^1.1.1", - "jsdoc": "^3.6.7", - "jsdoc-plugin-typescript": "^2.0.6", - "jsdoc-to-markdown": "5.0.0", - "jsdoc-ts-utils": "^2.0.1", - "jsdom-screenshot": "^4.0.0", - "postcss-loader": "^6.1.1", - "raw-loader": "^4.0.2", - "sass-loader": "^11.1.0", - "semantic-release": "^22", - "semantic-release-monorepo": "^8.0.2", - "storybook": "^7.6.14", - "style-loader": "^2.0.0", - "terser-webpack-plugin": "^5.1.1", - "webpack-filter-warnings-plugin": "^1.2.1", - "webpack-merge": "^5.8.0" - }, - "volta": { - "extends": "../../package.json" - } -} diff --git a/packages/component-cookie-consent/setupTests.js b/packages/component-cookie-consent/setupTests.js deleted file mode 100644 index 8a84d2b1b8..0000000000 --- a/packages/component-cookie-consent/setupTests.js +++ /dev/null @@ -1,7 +0,0 @@ -// @ts-check -import { toMatchImageSnapshot } from "jest-image-snapshot"; - -import "@testing-library/jest-dom"; -import "@testing-library/jest-dom/extend-expect"; - -expect.extend({ toMatchImageSnapshot }); diff --git a/packages/component-cookie-consent/src/CookieConsent.js b/packages/component-cookie-consent/src/CookieConsent.js deleted file mode 100644 index 28dc18b15c..0000000000 --- a/packages/component-cookie-consent/src/CookieConsent.js +++ /dev/null @@ -1,135 +0,0 @@ -// @ts-check -import { faTimes } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import PropTypes from "prop-types"; -import React, { useState, useEffect, useRef } from "react"; - -// eslint-disable-next-line import/no-unresolved -import { trackGAEvent } from "../../../shared"; -import trackReactComponent from "../../../shared/services/componentDatalayer"; -import { CookieConsentWrapper } from "./CookieConsent.styles"; -import { addDays } from "./core/utils/helpers"; - -const defaultGAEvent = { - event: "link", - action: "click", - name: "onclick", - type: "internal link", - region: "main content", - section: "cookie banner", -}; - -const now = new Date(); - -/** - * @typedef { import('./core/types/cookie-consent-types').CookieConsentProps } CookieConsentProps - */ - -/** - * - * @param {CookieConsentProps} props - * @returns {JSX.Element} - */ -const CookieConsent = ({ enableCookieConsent, expirationTime }) => { - const cookieConsentRef = useRef(null); - const [visible, setVisible] = useState(false); - - const updateEasingState = () => { - const elementClassList = cookieConsentRef.current.classList; - elementClassList.add("ease-closing"); - setTimeout(() => { - setVisible(false); - }, 500); - }; - - const updateLocalStorage = () => { - const { localStorage } = window; - const ttl = addDays(now, expirationTime).getTime(); - localStorage.setItem("cookieConsent", ttl.toString()); - }; - - const handleCloseConsent = () => { - updateLocalStorage(); - updateEasingState(); - }; - - useEffect(() => { - if (typeof window !== "undefined") { - trackReactComponent({ - packageName: "component-cookie-consent", - component: "CookieConsent", - type: "NA", - configuration: { - expirationTime, - enableCookieConsent, - }, - }); - } - }, []); - - useEffect(() => { - const { localStorage } = window; - if (localStorage) { - const item = localStorage.getItem("cookieConsent"); - setVisible(!item ? true : now.getTime() > parseInt(item, 10)); - } - }, []); - - return enableCookieConsent && visible ? ( - -
    -
    -
    -

    - ASU websites use cookies to enhance user experience, analyze site - usage, and assist with outreach and enrollment. By continuing to - use this site, you are giving us your consent to do this. Learn - more about cookies on ASU websites in our{" "} - - Privacy Statement. - -

    -
    - - -
    -
    -
    - ) : null; -}; - -CookieConsent.propTypes = { - /* Show the banner or not */ - enableCookieConsent: PropTypes.bool.isRequired, - /* Number of days to expire the consent */ - expirationTime: PropTypes.number, -}; - -CookieConsent.defaultProps = { - expirationTime: 90, -}; - -export { CookieConsent }; diff --git a/packages/component-cookie-consent/src/CookieConsent.stories.js b/packages/component-cookie-consent/src/CookieConsent.stories.js deleted file mode 100644 index ba80e0d1cb..0000000000 --- a/packages/component-cookie-consent/src/CookieConsent.stories.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; - -import { CookieConsent } from "./CookieConsent"; - -export default { - title: "UDS/ASU Cookie Consent", - component: CookieConsent, -}; - -const Template = args => ; - -export const Default = Template.bind({}); -Default.args = { - enableCookieConsent: true, - expirationTime: 90, // Time in days -}; - -export const ConsentDisabled = Template.bind({}); -ConsentDisabled.args = { - enableCookieConsent: false, -}; diff --git a/packages/component-cookie-consent/src/CookieConsent.styles.js b/packages/component-cookie-consent/src/CookieConsent.styles.js deleted file mode 100644 index 8078778163..0000000000 --- a/packages/component-cookie-consent/src/CookieConsent.styles.js +++ /dev/null @@ -1,129 +0,0 @@ -import styled from "styled-components"; - -const CookieConsentWrapper = styled.div` - font-family: Arial, Helvetica, "Nimbus Sans L", "Liberation Sans", FreeSans, - sans-serif; - margin: 0 auto; - max-width: 1200px; - position: relative; - z-index: 999; - font-weight: 300; - color: #191919; - .cookie-consent-component { - position: fixed; - bottom: 2rem; - margin-right: 1rem; - &.ease-closing { - transition-timing-function: ease-out; - transition: 0.2s; - transform: translateY(130%); - } - .container { - position: relative; - background: #e8e8e8; - border: 1px solid #d0d0d0; - padding: 1rem 1.5rem 1.5rem 1.5rem; - max-width: 676px; - width: 100%; - margin: 0 auto; - z-index: 999; - box-sizing: border-box; - float: left; - bottom: 0; - - .content { - width: 90%; - font-size: 0.875rem; - line-height: 1.5rem; - margin: 0; - p { - margin: 0; - a { - color: #8c1d40; - text-decoration: underline; - } - } - } - .accept-btn { - color: #ffffff; - background: #8c1d40; - text-decoration: none; - font-weight: 700; - display: inline-block; - text-align: center; - text-decoration: none; - vertical-align: middle; - user-select: none; - margin: 1rem 0 0 0; - line-height: 1rem; - font-size: 14px; - border-radius: 25px; - border: none; - padding: 0.5rem 1.2rem; - transition: 0.03s ease-in-out; - &:not(:disabled):not(.disabled) { - cursor: pointer; - } - &:hover { - transform: scale(1.05); - } - &:active { - transform: scale(1); - } - } - .close-btn { - position: absolute; - top: 1rem; - right: 1.5rem; - border-radius: 400rem; - line-height: 1rem; - transition: 0.03s ease-in-out; - padding: 0.25rem 0.25rem; - width: 2rem; - height: 2rem; - border: solid 1px #d0d0d0; - background: #ffffff; - font-size: 1rem; - &:hover { - transform: scale(1.05); - } - &:active { - transform: scale(0.95); - } - &:not(:disabled):not(.disabled) { - cursor: pointer; - } - @media (max-width: 768px) { - top: 0.5rem; - right: 0.5rem; - } - } - - // Fade animation - animation: fade 1s; - @keyframes fade { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - } - - @media (max-width: 998px) { - margin-left: 2rem; - margin-right: 2rem; - } - } - button, - a { - text-decoration: none; - &:focus { - outline: none !important; - box-shadow: 0px 0px 0px 2px #ffffff, 0px 0px 0px 4px #191919 !important; - } - } -`; - -export { CookieConsentWrapper }; diff --git a/packages/component-cookie-consent/src/core/types/cookie-consent-types.js b/packages/component-cookie-consent/src/core/types/cookie-consent-types.js deleted file mode 100644 index c461ee10af..0000000000 --- a/packages/component-cookie-consent/src/core/types/cookie-consent-types.js +++ /dev/null @@ -1,14 +0,0 @@ -// @ts-check - -/** - * @typedef {Object} CookieConsentProps - * @property {boolean} enableCookieConsent - * @property {number} [expirationTime] - */ - -/** - * This helps VSCODE and JSOC to recognize the syntax - * `import(FILE_PATH).EXPORTED_THING` - * @ignore - */ -export const JSDOC = "jsdoc"; diff --git a/packages/component-cookie-consent/src/core/utils/helpers.js b/packages/component-cookie-consent/src/core/utils/helpers.js deleted file mode 100644 index 39ce710e8d..0000000000 --- a/packages/component-cookie-consent/src/core/utils/helpers.js +++ /dev/null @@ -1,14 +0,0 @@ -// @ts-check - -/** - * @param {string | Date} date - * @param {number} days - * @returns {Date} - */ -const addDays = (date, days) => { - const dateWithDays = new Date(date); - dateWithDays.setDate(dateWithDays.getDate() + days); - return dateWithDays; -}; - -export { addDays }; diff --git a/packages/component-cookie-consent/src/core/utils/init-cookie-consent.js b/packages/component-cookie-consent/src/core/utils/init-cookie-consent.js deleted file mode 100644 index 477f585fb7..0000000000 --- a/packages/component-cookie-consent/src/core/utils/init-cookie-consent.js +++ /dev/null @@ -1,28 +0,0 @@ -// @ts-check -import React from "react"; -import { createRoot } from "react-dom/client"; - -import { CookieConsent } from "../../CookieConsent"; - -/** - * @typedef {Object} ComponentProps - * @property {string} targetSelector - The CSS selector (#id or .class) - * which identify the
    element where the footer should be either hydrated or rendered. - * @property {object} props - Properties to initialize the footer with. - * Should only be set to true if the footer has been completely rendered server-side. - * - */ - -const RenderReact = (component, props, target) => { - const root = createRoot(target); - root.render(React.createElement(component, props)); -}; - -/** - * @param {ComponentProps} props - */ -const initCookieConsent = ({ targetSelector, props }) => { - RenderReact(CookieConsent, props, document.querySelector(targetSelector)); -}; - -export { initCookieConsent }; diff --git a/packages/component-cookie-consent/src/index.js b/packages/component-cookie-consent/src/index.js deleted file mode 100644 index a9f64aa065..0000000000 --- a/packages/component-cookie-consent/src/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { CookieConsent } from "./CookieConsent"; -export { initCookieConsent } from "./core/utils/init-cookie-consent"; diff --git a/packages/component-cookie-consent/types/main.d.ts b/packages/component-cookie-consent/types/main.d.ts deleted file mode 100644 index 202e59507d..0000000000 --- a/packages/component-cookie-consent/types/main.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { FunctionComponent } from "react"; - -export interface CookieConsentProps { - enableCookieConsent: boolean - expirationTime?: number -} - -export const CookieConsent: FunctionComponent; diff --git a/packages/component-cookie-consent/webpack/webpack.common.js b/packages/component-cookie-consent/webpack/webpack.common.js deleted file mode 100644 index 3df5075e56..0000000000 --- a/packages/component-cookie-consent/webpack/webpack.common.js +++ /dev/null @@ -1,68 +0,0 @@ -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const path = require("path"); - -const PROJECT_DIR = path.resolve(__dirname, "../"); - -const common = { - context: path.join(PROJECT_DIR, "src"), - entry: { - asuCookieConsent: "./index.js", - }, - module: { - rules: [ - { - test: /\.(jsx|js)?$/, - exclude: /node_modules/, - use: [ - { - loader: "babel-loader", - options: { - presets: ["@babel/env", "@babel/preset-react"], - }, - }, - ], - }, - // Process CSS Modules on files matching '*.module.css' - { - test: /\.css$/, - use: [ - MiniCssExtractPlugin.loader, - { - loader: "css-loader", - options: { - importLoaders: 1, - modules: true, - }, - }, - ], - include: /\.module\.css$/, - }, - // Process regular global CSS styles - { - test: /\.css$/, - use: ["style-loader", "css-loader"], - - exclude: /\.module\.css$/, - }, - { - test: /\.(png|jpe?g|gif|svg)$/i, - use: [ - { - loader: "file-loader", - }, - ], - }, - ], - }, - resolve: { - extensions: [".js", ".jsx"], - }, - plugins: [ - // new CleanWebpackPlugin(), - new MiniCssExtractPlugin({ - filename: "[name].[contenthash].css", - }), - ], -}; - -module.exports = common; diff --git a/packages/component-cookie-consent/webpack/webpack.dev.js b/packages/component-cookie-consent/webpack/webpack.dev.js deleted file mode 100644 index 9304c96f7e..0000000000 --- a/packages/component-cookie-consent/webpack/webpack.dev.js +++ /dev/null @@ -1,16 +0,0 @@ -const path = require("path"); - -const common = require("./webpack.common"); - -// development bundle config -const config = { - ...common, - mode: "development", - - output: { - path: path.resolve(__dirname, "dist"), - filename: "[name].development.js", - }, -}; - -module.exports = config; diff --git a/packages/component-cookie-consent/webpack/webpack.prod.js b/packages/component-cookie-consent/webpack/webpack.prod.js deleted file mode 100644 index 4f05a85191..0000000000 --- a/packages/component-cookie-consent/webpack/webpack.prod.js +++ /dev/null @@ -1,88 +0,0 @@ -const path = require("path"); -const TerserPlugin = require("terser-webpack-plugin"); -const { merge } = require("webpack-merge"); - -const common = require("./webpack.common"); - -const PROJECT_DIR = path.resolve(__dirname, "../"); - -// production bundle -const umdConfig = merge(common, { - mode: "production", - output: { - path: path.resolve(PROJECT_DIR, "dist"), - filename: "[name].umd.js", - library: { - name: "AsuCookieConsent", - type: "umd", - umdNamedDefine: true, - }, - umdNamedDefine: true, - }, - optimization: { - splitChunks: { - cacheGroups: { - commons: { - test: /[\\/]node_modules[\\/]/, - name: "vendor", - chunks: "all", - }, - }, - }, - minimizer: [ - new TerserPlugin({ - parallel: true, - terserOptions: { - ecma: 6, - }, - }), - ], - }, - /* TODO: - this block should be uncommented and we should decide a standard way - to include React library externally, such explained in this link - https://reactjs.org/docs/add-react-to-a-website.html#step-2-add-the-script-tags - */ - externals: { - "react": "React", - "react-dom": "ReactDOM", - }, -}); - -const cjsConfig = merge(common, { - mode: "production", - output: { - path: path.resolve(PROJECT_DIR, "dist"), - filename: "[name].cjs.js", - library: { - type: "commonjs2", - }, - }, - externals: { - "react": "React", - "react-dom": "ReactDOM", - }, -}); - -const esModuleConfig = merge(common, { - mode: "production", - output: { - path: path.resolve(PROJECT_DIR, "dist"), - filename: "[name].es.js", - library: { - type: "module", - }, - environment: { module: true }, - }, - experiments: { - outputModule: true, - }, - externalsType: "module", - externals: { - // these needs to be low-case spelled - "react": "react", - "react-dom": "react-dom", - }, -}); - -module.exports = [umdConfig, cjsConfig, esModuleConfig]; diff --git a/packages/component-events/.eslintrc.js b/packages/component-events/.eslintrc.js deleted file mode 100644 index 9c07c9028d..0000000000 --- a/packages/component-events/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], -}; diff --git a/packages/component-events/eslint.config.js b/packages/component-events/eslint.config.js new file mode 100644 index 0000000000..6b534f22b4 --- /dev/null +++ b/packages/component-events/eslint.config.js @@ -0,0 +1,5 @@ +const rootConfig = require("../../eslint.config.js"); + +module.exports = [ + ...rootConfig, +]; diff --git a/packages/component-events/package.json b/packages/component-events/package.json index ae42d16eed..3a74db9be3 100644 --- a/packages/component-events/package.json +++ b/packages/component-events/package.json @@ -24,7 +24,7 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx}' ", "test": "vitest --watch=false", "test-update-snapshot": "yarn test -- -u", "build": "vite build && yarn postbuild", @@ -59,7 +59,6 @@ "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.2.4", "dotenv-webpack": "^7.0.3", - "eslint-plugin-storybook": "^0.6.15", "express": "^4.17.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.3.2", diff --git a/packages/component-events/src/components/CardsGridEvents/index.jsx b/packages/component-events/src/components/CardsGridEvents/index.jsx index 084bfee11e..5ddff92e4c 100644 --- a/packages/component-events/src/components/CardsGridEvents/index.jsx +++ b/packages/component-events/src/components/CardsGridEvents/index.jsx @@ -1,7 +1,7 @@ // @ts-check import React, { useEffect } from "react"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; +import { trackReactComponent } from "@asu/shared"; import { BaseFeed } from "../../core/components/BaseFeed"; import { eventsPropTypes } from "../../core/models/propTypes"; import { GridView } from "./GridView"; diff --git a/packages/component-events/src/components/CardsListEvents/index.jsx b/packages/component-events/src/components/CardsListEvents/index.jsx index 0789d36e70..e1ca97f272 100644 --- a/packages/component-events/src/components/CardsListEvents/index.jsx +++ b/packages/component-events/src/components/CardsListEvents/index.jsx @@ -1,7 +1,7 @@ // @ts-check import React, { useEffect } from "react"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; +import { trackReactComponent } from "@asu/shared"; import { BaseFeed } from "../../core/components/BaseFeed"; import { eventsPropTypes } from "../../core/models/propTypes"; import { ListView } from "./ListView"; diff --git a/packages/component-events/src/core/utils/story-utils.js b/packages/component-events/src/core/utils/story-utils.js index 60f537bab4..3257b2370a 100644 --- a/packages/component-events/src/core/utils/story-utils.js +++ b/packages/component-events/src/core/utils/story-utils.js @@ -1,6 +1,5 @@ // @ts-check -// eslint-disable-next-line jest/no-mocks-import import * as feeds from "../../../__mocks__/api/feeds.json"; function createMockParam() { diff --git a/packages/component-events/vitest.config.ts b/packages/component-events/vitest.config.ts index d27a08554b..f063f973cf 100644 --- a/packages/component-events/vitest.config.ts +++ b/packages/component-events/vitest.config.ts @@ -10,10 +10,5 @@ export default defineConfig({ setupFiles: ['./vitest.setup.ts'], globals: true }, - resolve: { - alias: { - "@shared": resolve(__dirname, "./../../shared"), - } - }, }); diff --git a/packages/component-footer/.eslintrc.js b/packages/component-footer/.eslintrc.js deleted file mode 100644 index 9c07c9028d..0000000000 --- a/packages/component-footer/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], -}; diff --git a/packages/component-footer/eslint.config.js b/packages/component-footer/eslint.config.js new file mode 100644 index 0000000000..6b534f22b4 --- /dev/null +++ b/packages/component-footer/eslint.config.js @@ -0,0 +1,5 @@ +const rootConfig = require("../../eslint.config.js"); + +module.exports = [ + ...rootConfig, +]; diff --git a/packages/component-footer/package.json b/packages/component-footer/package.json index ad45c3783c..a497d919ae 100644 --- a/packages/component-footer/package.json +++ b/packages/component-footer/package.json @@ -25,7 +25,7 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx}' ", "test": "jest --config=./jest.config.js --silent --coverage", "test-update-snapshot": "yarn test -- -u", "build": "vite build && yarn postbuild", @@ -46,6 +46,7 @@ "prop-types": "^15.7.2" }, "devDependencies": { + "@asu/shared": "*", "@babel/core": "^7.13.14", "@babel/plugin-syntax-jsx": "^7.14.5", "@storybook/addon-a11y": "^7.6.14", @@ -59,7 +60,6 @@ "babel-loader": "^8.2.2", "copy-webpack-plugin": "^8.1.1", "css-loader": "^5.2.4", - "eslint-plugin-storybook": "^0.6.15", "jest": "^26.6.3", "jest-image-snapshot": "^4.4.1", "jest-transform-css": "^6.0.1", diff --git a/packages/component-footer/src/components/Contact/index.js b/packages/component-footer/src/components/Contact/index.js index 02aaca2fd4..dc68249251 100644 --- a/packages/component-footer/src/components/Contact/index.js +++ b/packages/component-footer/src/components/Contact/index.js @@ -5,9 +5,9 @@ import React from "react"; import { ColumnSection } from "../ColumnSection"; /** - * @typedef {import("../../core/models/types").Contact} Contact + * @typedef {import("../../core/models/types").Contact} ContactProps * - * @param {{contact: Contact}} props + * @param {{contact: ContactProps}} props */ const Contact = ({ diff --git a/packages/component-footer/src/components/Contact/index.test.js b/packages/component-footer/src/components/Contact/index.test.js index 19677ad889..d7f9e9395b 100644 --- a/packages/component-footer/src/components/Contact/index.test.js +++ b/packages/component-footer/src/components/Contact/index.test.js @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-footer/src/components/Innovation/index.js b/packages/component-footer/src/components/Innovation/index.js index a14fd86eeb..ada3e4c265 100644 --- a/packages/component-footer/src/components/Innovation/index.js +++ b/packages/component-footer/src/components/Innovation/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../shared"; import innovationLogo from "../../assets/images/innovationLogo.png"; import { FOOTER_URLS } from "../../core/constants"; // @ts-ignore diff --git a/packages/component-footer/src/components/Legal/index.js b/packages/component-footer/src/components/Legal/index.js index 386c48c103..f0bea43d8f 100644 --- a/packages/component-footer/src/components/Legal/index.js +++ b/packages/component-footer/src/components/Legal/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../shared"; import { LEGAL_URLS } from "../../core/constants"; const DEFAULT_GA_EVENT = { diff --git a/packages/component-footer/src/components/Social/index.js b/packages/component-footer/src/components/Social/index.js index b7cfb6ec5c..ce35deb7ac 100644 --- a/packages/component-footer/src/components/Social/index.js +++ b/packages/component-footer/src/components/Social/index.js @@ -1,4 +1,5 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faFacebookSquare, faInstagramSquare, @@ -11,7 +12,6 @@ import PropTypes, { shape } from "prop-types"; import React from "react"; // @ts-ignore -import { trackGAEvent } from "../../../../../shared"; import endorsedLogo from "../../assets/images/endorsedLogo.png"; const DEFAULT_GA_EVENT = { diff --git a/packages/component-footer/src/components/Social/index.test.js b/packages/component-footer/src/components/Social/index.test.js index 2883ed0890..ab8a85101b 100644 --- a/packages/component-footer/src/components/Social/index.test.js +++ b/packages/component-footer/src/components/Social/index.test.js @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-footer/src/footer.js b/packages/component-footer/src/footer.js index df6b14b19f..71393c8927 100644 --- a/packages/component-footer/src/footer.js +++ b/packages/component-footer/src/footer.js @@ -1,8 +1,8 @@ // @ts-check +import { trackReactComponent } from "@asu/shared"; import { shape } from "prop-types"; import React, { useEffect } from "react"; -import trackReactComponent from "../../../shared/services/componentDatalayer"; import { Social, Legal, Innovation, Contact } from "./components"; const Base = () => { diff --git a/packages/component-footer/src/footer.stories.js b/packages/component-footer/src/footer.stories.js index 502e715cfd..67561799f8 100644 --- a/packages/component-footer/src/footer.stories.js +++ b/packages/component-footer/src/footer.stories.js @@ -1,6 +1,5 @@ import React from "react"; -// eslint-disable-next-line import/no-unresolved import endorsedLogo from "./assets/images/endorsedLogo.png"; import { ASUFooter } from "./footer"; @@ -9,7 +8,6 @@ export default { component: ASUFooter, }; -/* eslint-disable-next-line react/jsx-props-no-spreading */ const Template = args => ; export const Default = Template.bind({}); diff --git a/packages/component-footer/src/header.test.js b/packages/component-footer/src/header.test.js index 5a14f89fea..9ffeb94a3c 100644 --- a/packages/component-footer/src/header.test.js +++ b/packages/component-footer/src/header.test.js @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header-footer/.eslintrc.js b/packages/component-header-footer/.eslintrc.js deleted file mode 100644 index 9c07c9028d..0000000000 --- a/packages/component-header-footer/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], -}; diff --git a/packages/component-header-footer/eslint.config.js b/packages/component-header-footer/eslint.config.js new file mode 100644 index 0000000000..6b534f22b4 --- /dev/null +++ b/packages/component-header-footer/eslint.config.js @@ -0,0 +1,5 @@ +const rootConfig = require("../../eslint.config.js"); + +module.exports = [ + ...rootConfig, +]; diff --git a/packages/component-header-footer/package.json b/packages/component-header-footer/package.json index 8eb4b7da11..66693ddc21 100644 --- a/packages/component-header-footer/package.json +++ b/packages/component-header-footer/package.json @@ -32,7 +32,7 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx}' ", "test": "jest --config=./jest.config.js --silent --coverage", "test-update-snapshot": "yarn test -- -u", "build": "vite build && yarn postbuild", @@ -56,6 +56,7 @@ "react-dom": "^18.3.1" }, "devDependencies": { + "@asu/shared": "*", "@babel/core": "^7.13.14", "@babel/plugin-syntax-jsx": "^7.14.5", "@babel/plugin-transform-react-jsx": "^7.13.12", @@ -72,7 +73,6 @@ "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.2.4", "dotenv-webpack": "^7.0.3", - "eslint-plugin-storybook": "^0.6.15", "express": "^4.17.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.3.2", diff --git a/packages/component-header-footer/src/footer/components/Contact/index.test.js b/packages/component-header-footer/src/footer/components/Contact/index.test.js index e3f8e58958..1e1909e816 100644 --- a/packages/component-header-footer/src/footer/components/Contact/index.test.js +++ b/packages/component-header-footer/src/footer/components/Contact/index.test.js @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header-footer/src/footer/components/Innovation/index.js b/packages/component-header-footer/src/footer/components/Innovation/index.js index 0c89301277..8029143c56 100644 --- a/packages/component-header-footer/src/footer/components/Innovation/index.js +++ b/packages/component-header-footer/src/footer/components/Innovation/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../../shared"; import innovationLogo from "../../../../public/assets/images/innovationLogo.png"; import { FOOTER_URLS } from "../../core/constants"; diff --git a/packages/component-header-footer/src/footer/components/Legal/index.js b/packages/component-header-footer/src/footer/components/Legal/index.js index 866ebc2ed4..f0bea43d8f 100644 --- a/packages/component-header-footer/src/footer/components/Legal/index.js +++ b/packages/component-header-footer/src/footer/components/Legal/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../../shared"; import { LEGAL_URLS } from "../../core/constants"; const DEFAULT_GA_EVENT = { diff --git a/packages/component-header-footer/src/footer/components/Social/index.js b/packages/component-header-footer/src/footer/components/Social/index.js index 18522e9d84..7921834b48 100644 --- a/packages/component-header-footer/src/footer/components/Social/index.js +++ b/packages/component-header-footer/src/footer/components/Social/index.js @@ -1,4 +1,5 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faFacebookSquare, faInstagramSquare, @@ -10,7 +11,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import PropTypes, { shape } from "prop-types"; import React from "react"; -import { trackGAEvent } from "../../../../../../shared"; // @ts-expect-error import endorsedLogo from "../../../../public/assets/images/endorsedLogo.png"; diff --git a/packages/component-header-footer/src/footer/components/Social/index.test.js b/packages/component-header-footer/src/footer/components/Social/index.test.js index 1fd18cac50..015fc142b3 100644 --- a/packages/component-header-footer/src/footer/components/Social/index.test.js +++ b/packages/component-header-footer/src/footer/components/Social/index.test.js @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header-footer/src/footer/footer.js b/packages/component-header-footer/src/footer/footer.js index 905ff074d3..8657bdb9ed 100644 --- a/packages/component-header-footer/src/footer/footer.js +++ b/packages/component-header-footer/src/footer/footer.js @@ -1,8 +1,8 @@ // @ts-check +import { trackReactComponent } from "@asu/shared"; import { shape } from "prop-types"; import React, { useEffect } from "react"; -import trackReactComponent from "../../../../shared/services/componentDatalayer"; import { Social, Legal, Innovation, ContactComponent } from "./components"; import { StyledFooter } from "./index.styles"; diff --git a/packages/component-header-footer/src/footer/footer.stories.js b/packages/component-header-footer/src/footer/footer.stories.js index 7f95183a2d..8204e3d58a 100644 --- a/packages/component-header-footer/src/footer/footer.stories.js +++ b/packages/component-header-footer/src/footer/footer.stories.js @@ -8,7 +8,6 @@ export default { component: ASUFooter, }; -/* eslint-disable-next-line react/jsx-props-no-spreading */ const Template = args => ; export const Default = Template.bind({}); diff --git a/packages/component-header-footer/src/footer/footer.test.js b/packages/component-header-footer/src/footer/footer.test.js index 8a6e3e9354..f8c52820cf 100644 --- a/packages/component-header-footer/src/footer/footer.test.js +++ b/packages/component-header-footer/src/footer/footer.test.js @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header-footer/src/footer/index.styles.js b/packages/component-header-footer/src/footer/index.styles.js index 38b283b024..df5b284c6a 100644 --- a/packages/component-header-footer/src/footer/index.styles.js +++ b/packages/component-header-footer/src/footer/index.styles.js @@ -32,7 +32,8 @@ const StyledFooter = styled.footer` a:focus, button:focus { outline: none; - box-shadow: 0 0 0 2px var(--color-base-white), 0 0 0 4px var(--color-base-grey-7) !important; + box-shadow: 0 0 0 2px var(--color-base-white), + 0 0 0 4px var(--color-base-grey-7) !important; -webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } diff --git a/packages/component-header-footer/src/header/components/HeaderMain/Logo/index.js b/packages/component-header-footer/src/header/components/HeaderMain/Logo/index.js index cdba5f3243..e7df6c5ecc 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/Logo/index.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/Logo/index.js @@ -1,7 +1,6 @@ -/* eslint-disable import/no-extraneous-dependencies */ +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../../../shared"; import vertLogo from "../../../../../public/assets/img/arizona-state-university-logo-vertical.png"; import horizLogo from "../../../../../public/assets/img/arizona-state-university-logo.png"; import { useAppContext } from "../../../core/context/app-context"; @@ -25,7 +24,6 @@ const Logo = () => { width="303" height="234" decoding="async" - // eslint-disable-next-line fetchpriority="high" /> { width="400" height="72" decoding="async" - // eslint-disable-next-line fetchpriority="high" /> diff --git a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/Buttons.test.js b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/Buttons.test.js index 2d50d4c497..5fe6d4549b 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/Buttons.test.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/Buttons.test.js @@ -1,14 +1,13 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { render, fireEvent, cleanup } from "@testing-library/react"; import React from "react"; import { NavbarContainer, BUTTON_ERROR_MESSAGE } from "."; -import { trackGAEvent } from "../../../../../../../shared"; import { AppContextProvider } from "../../../core/context/app-context"; -jest.mock("../../../../../../../shared", () => ({ +jest.mock("@asu/shared", () => ({ idGenerator: jest.fn().mockImplementation(function* () { yield "test-id"; }), diff --git a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/DropdownItem/index.js b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/DropdownItem/index.js index ad104c031b..4e905c6f32 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/DropdownItem/index.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/DropdownItem/index.js @@ -1,9 +1,8 @@ // @ts-check +import { idGenerator, trackGAEvent } from "@asu/shared"; import PropTypes from "prop-types"; import React, { useState, useEffect, useRef } from "react"; -import { idGenerator, trackGAEvent } from "../../../../../../../../shared"; -// eslint-disable-next-line import/no-extraneous-dependencies import { useAppContext } from "../../../../core/context/app-context"; import { ButtonPropTypes } from "../../../../core/models/app-prop-types"; import { Button } from "../../../Button"; diff --git a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/NavItem/index.js b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/NavItem/index.js index d69afadb59..173617ddd4 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/NavItem/index.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/NavItem/index.js @@ -1,10 +1,10 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faChevronDown, faHome } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import PropTypes from "prop-types"; import React, { useRef, useEffect, useMemo } from "react"; -import { trackGAEvent } from "../../../../../../../../shared"; import { useAppContext } from "../../../../core/context/app-context"; import { useIsMobile } from "../../../../core/hooks/isMobile"; import { NavTreePropTypes } from "../../../../core/models/app-prop-types"; diff --git a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.js b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.js index 574fcf6822..eceb7adeb1 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.js @@ -1,8 +1,7 @@ // @ts-check +import { idGenerator, trackGAEvent } from "@asu/shared"; import React, { useState } from "react"; -import { idGenerator, trackGAEvent } from "../../../../../../../shared"; -// eslint-disable-next-line import/no-extraneous-dependencies import { useAppContext } from "../../../core/context/app-context"; import { useIsMobile } from "../../../core/hooks/isMobile"; import { Button } from "../../Button"; diff --git a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.test.js b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.test.js index 2573c79cb9..afb0258ba8 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.test.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/NavbarContainer/index.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, fireEvent, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header-footer/src/header/components/HeaderMain/Partner/index.js b/packages/component-header-footer/src/header/components/HeaderMain/Partner/index.js index e4ee7f1c5e..53cc8a8831 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/Partner/index.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/Partner/index.js @@ -1,7 +1,6 @@ -/* eslint-disable import/no-extraneous-dependencies */ +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../../../shared"; // @ts-check import asuVertLogo from "../../../../../public/assets/img/arizona-state-university-logo-vertical.png"; import asuHorizLogo from "../../../../../public/assets/img/arizona-state-university-logo.png"; @@ -36,7 +35,7 @@ const Partner = () => { height="81" decoding="async" // @ts-ignore - // eslint-disable-next-line + fetchpriority="high" /> @@ -52,7 +51,7 @@ const Partner = () => { height="234" decoding="async" // @ts-ignore - // eslint-disable-next-line + fetchpriority="high" /> { height="72" decoding="async" // @ts-ignore - // eslint-disable-next-line + fetchpriority="high" /> diff --git a/packages/component-header-footer/src/header/components/HeaderMain/Title/index.js b/packages/component-header-footer/src/header/components/HeaderMain/Title/index.js index d7827b46c8..ff524085ca 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/Title/index.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/Title/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React, { useEffect, useState } from "react"; -import { trackGAEvent } from "../../../../../../../shared"; import { useAppContext } from "../../../core/context/app-context"; import { TitlePropTypes } from "../../../core/models/app-prop-types"; import { checkFirstLoad } from "../../../core/utils/helpers/title"; diff --git a/packages/component-header-footer/src/header/components/HeaderMain/index.js b/packages/component-header-footer/src/header/components/HeaderMain/index.js index 95ed14a73e..da440b39d5 100644 --- a/packages/component-header-footer/src/header/components/HeaderMain/index.js +++ b/packages/component-header-footer/src/header/components/HeaderMain/index.js @@ -1,9 +1,9 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faTimes, faBars } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React, { useState } from "react"; -import { trackGAEvent } from "../../../../../../shared"; import { useAppContext } from "../../core/context/app-context"; import { useIsMobile } from "../../core/hooks/isMobile"; import { UniversalNavbar } from "../UniversalNavbar"; diff --git a/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.js b/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.js index 9038e178d1..30fb38738b 100644 --- a/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.js +++ b/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../../../shared"; import { useAppContext } from "../../../core/context/app-context"; import { LoginWrapper } from "./index.styles"; diff --git a/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.test.js b/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.test.js index c70c5872ab..67b0dca665 100644 --- a/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.test.js +++ b/packages/component-header-footer/src/header/components/UniversalNavbar/Login/index.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.js b/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.js index 81d4f34848..7c6f60f2ad 100644 --- a/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.js +++ b/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.js @@ -1,9 +1,9 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faSearch, faTimes } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React, { useState, useRef, useEffect } from "react"; -import { trackGAEvent } from "../../../../../../../shared"; import { useAppContext } from "../../../core/context/app-context"; import { useIsMobile } from "../../../core/hooks/isMobile"; import { SearchWrapper } from "./index.styles"; diff --git a/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.test.js b/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.test.js index 6f78ca2fa1..2156be9921 100644 --- a/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.test.js +++ b/packages/component-header-footer/src/header/components/UniversalNavbar/Search/index.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, fireEvent, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header-footer/src/header/components/UniversalNavbar/index.js b/packages/component-header-footer/src/header/components/UniversalNavbar/index.js index 2263b2843c..e216e59ecb 100644 --- a/packages/component-header-footer/src/header/components/UniversalNavbar/index.js +++ b/packages/component-header-footer/src/header/components/UniversalNavbar/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../../shared"; import { useAppContext } from "../../core/context/app-context"; import { Wrapper } from "./index.styles"; import { Login } from "./Login"; diff --git a/packages/component-header-footer/src/header/core/context/app-context.js b/packages/component-header-footer/src/header/core/context/app-context.js index 63204d6f0f..fb6ea5dd06 100644 --- a/packages/component-header-footer/src/header/core/context/app-context.js +++ b/packages/component-header-footer/src/header/core/context/app-context.js @@ -8,7 +8,6 @@ const breakpoints = { Lg: "992px", Xl: "1260px" }; const AppContext = createContext(); const AppContextProvider = ({ initialValue, children }) => { - // eslint-disable-next-line react/jsx-no-constructed-context-values const value = { ...initialValue, breakpoint: breakpoints[initialValue.breakpoint], diff --git a/packages/component-header-footer/src/header/header.js b/packages/component-header-footer/src/header/header.js index 0b49348381..8fd132c2d3 100644 --- a/packages/component-header-footer/src/header/header.js +++ b/packages/component-header-footer/src/header/header.js @@ -1,7 +1,7 @@ // @ts-check +import { trackReactComponent } from "@asu/shared"; import React, { useEffect, useRef } from "react"; -import trackReactComponent from "../../../../shared/services/componentDatalayer"; import { HeaderMain } from "./components/HeaderMain"; import { AppContextProvider } from "./core/context/app-context"; import { HeaderPropTypes } from "./core/models/app-prop-types"; diff --git a/packages/component-header-footer/src/header/header.test.js b/packages/component-header-footer/src/header/header.test.js index 7f2bf507cd..ea82a1c409 100644 --- a/packages/component-header-footer/src/header/header.test.js +++ b/packages/component-header-footer/src/header/header.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup, screen, act } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header/.eslintrc.js b/packages/component-header/.eslintrc.js deleted file mode 100644 index 9c07c9028d..0000000000 --- a/packages/component-header/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], -}; diff --git a/packages/component-header/eslint.config.js b/packages/component-header/eslint.config.js new file mode 100644 index 0000000000..6b534f22b4 --- /dev/null +++ b/packages/component-header/eslint.config.js @@ -0,0 +1,5 @@ +const rootConfig = require("../../eslint.config.js"); + +module.exports = [ + ...rootConfig, +]; diff --git a/packages/component-header/package.json b/packages/component-header/package.json index e4ee617d2f..aa19d3965b 100644 --- a/packages/component-header/package.json +++ b/packages/component-header/package.json @@ -25,7 +25,7 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx}' ", "test": "jest --config=./jest.config.js --silent --coverage", "test-update-snapshot": "yarn test -- -u", "build": "vite build && cp -r src/assets dist/ && yarn postbuild", @@ -46,6 +46,7 @@ "react-dom": "^18.3.1" }, "devDependencies": { + "@asu/shared": "*", "@babel/core": "^7.13.14", "@babel/plugin-syntax-jsx": "^7.14.5", "@babel/plugin-transform-react-jsx": "^7.13.12", @@ -62,7 +63,6 @@ "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.2.4", "dotenv-webpack": "^7.0.3", - "eslint-plugin-storybook": "^0.6.15", "express": "^4.17.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.3.2", diff --git a/packages/component-header/src/components/HeaderMain/Logo/index.js b/packages/component-header/src/components/HeaderMain/Logo/index.js index 19fbd443fd..0bc6e2dd7e 100644 --- a/packages/component-header/src/components/HeaderMain/Logo/index.js +++ b/packages/component-header/src/components/HeaderMain/Logo/index.js @@ -1,7 +1,6 @@ -/* eslint-disable import/no-extraneous-dependencies */ +import { getCurrentScriptPath, trackGAEvent } from "@asu/shared"; import React from "react"; -import { getCurrentScriptPath, trackGAEvent } from "../../../../../../shared"; import { useAppContext } from "../../../core/context/app-context"; import { LogoWrapper } from "./index.styles"; @@ -37,7 +36,6 @@ const Logo = () => { width="303" height="234" decoding="async" - // eslint-disable-next-line fetchpriority="high" /> { width="400" height="72" decoding="async" - // eslint-disable-next-line fetchpriority="high" /> diff --git a/packages/component-header/src/components/HeaderMain/NavbarContainer/DropdownItem/index.js b/packages/component-header/src/components/HeaderMain/NavbarContainer/DropdownItem/index.js index 47e6fd90a2..6f49e4b3eb 100644 --- a/packages/component-header/src/components/HeaderMain/NavbarContainer/DropdownItem/index.js +++ b/packages/component-header/src/components/HeaderMain/NavbarContainer/DropdownItem/index.js @@ -1,8 +1,7 @@ +import { idGenerator, trackGAEvent } from "@asu/shared"; import PropTypes from "prop-types"; import React, { useState, useEffect, useRef } from "react"; -import { idGenerator, trackGAEvent } from "../../../../../../../shared"; -// eslint-disable-next-line import/no-extraneous-dependencies import { useAppContext } from "../../../../core/context/app-context"; import { ButtonPropTypes } from "../../../../core/models/app-prop-types"; import { Button } from "../../../Button"; diff --git a/packages/component-header/src/components/HeaderMain/NavbarContainer/NavItem/index.js b/packages/component-header/src/components/HeaderMain/NavbarContainer/NavItem/index.js index 1aa28bd377..ea1cf9bce4 100644 --- a/packages/component-header/src/components/HeaderMain/NavbarContainer/NavItem/index.js +++ b/packages/component-header/src/components/HeaderMain/NavbarContainer/NavItem/index.js @@ -1,10 +1,10 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faChevronDown, faHome } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import PropTypes from "prop-types"; import React, { useRef, useEffect, useMemo } from "react"; -import { trackGAEvent } from "../../../../../../../shared"; import { useAppContext } from "../../../../core/context/app-context"; import { useIsMobile } from "../../../../core/hooks/isMobile"; import { NavTreePropTypes } from "../../../../core/models/app-prop-types"; diff --git a/packages/component-header/src/components/HeaderMain/NavbarContainer/index.js b/packages/component-header/src/components/HeaderMain/NavbarContainer/index.js index eedf674109..aef186b524 100644 --- a/packages/component-header/src/components/HeaderMain/NavbarContainer/index.js +++ b/packages/component-header/src/components/HeaderMain/NavbarContainer/index.js @@ -1,8 +1,7 @@ // @ts-check +import { idGenerator, trackGAEvent } from "@asu/shared"; import React, { useState } from "react"; -import { idGenerator, trackGAEvent } from "../../../../../../shared"; -// eslint-disable-next-line import/no-extraneous-dependencies import { useAppContext } from "../../../core/context/app-context"; import { useIsMobile } from "../../../core/hooks/isMobile"; import { Button } from "../../Button"; diff --git a/packages/component-header/src/components/HeaderMain/NavbarContainer/index.test.js b/packages/component-header/src/components/HeaderMain/NavbarContainer/index.test.js index ecc8ea5d47..b239643e5c 100644 --- a/packages/component-header/src/components/HeaderMain/NavbarContainer/index.test.js +++ b/packages/component-header/src/components/HeaderMain/NavbarContainer/index.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, fireEvent, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header/src/components/HeaderMain/Partner/index.js b/packages/component-header/src/components/HeaderMain/Partner/index.js index 91d5eb2403..15a7fc8854 100644 --- a/packages/component-header/src/components/HeaderMain/Partner/index.js +++ b/packages/component-header/src/components/HeaderMain/Partner/index.js @@ -1,7 +1,6 @@ -/* eslint-disable import/no-extraneous-dependencies */ +import { getCurrentScriptPath, trackGAEvent } from "@asu/shared"; import React from "react"; -import { getCurrentScriptPath, trackGAEvent } from "../../../../../../shared"; // @ts-check import { useAppContext } from "../../../core/context/app-context"; import { PartnerLogosWrapper } from "./index.styles"; @@ -38,7 +37,7 @@ const Partner = () => { height="81" decoding="async" // @ts-ignore - // eslint-disable-next-line + fetchpriority="high" /> @@ -54,7 +53,7 @@ const Partner = () => { height="234" decoding="async" // @ts-ignore - // eslint-disable-next-line + fetchpriority="high" /> { height="72" decoding="async" // @ts-ignore - // eslint-disable-next-line + fetchpriority="high" /> diff --git a/packages/component-header/src/components/HeaderMain/Title/index.js b/packages/component-header/src/components/HeaderMain/Title/index.js index 2d7f1b0660..ff524085ca 100644 --- a/packages/component-header/src/components/HeaderMain/Title/index.js +++ b/packages/component-header/src/components/HeaderMain/Title/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React, { useEffect, useState } from "react"; -import { trackGAEvent } from "../../../../../../shared"; import { useAppContext } from "../../../core/context/app-context"; import { TitlePropTypes } from "../../../core/models/app-prop-types"; import { checkFirstLoad } from "../../../core/utils/helpers/title"; diff --git a/packages/component-header/src/components/HeaderMain/index.js b/packages/component-header/src/components/HeaderMain/index.js index 138017b435..da440b39d5 100644 --- a/packages/component-header/src/components/HeaderMain/index.js +++ b/packages/component-header/src/components/HeaderMain/index.js @@ -1,9 +1,9 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faTimes, faBars } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React, { useState } from "react"; -import { trackGAEvent } from "../../../../../shared"; import { useAppContext } from "../../core/context/app-context"; import { useIsMobile } from "../../core/hooks/isMobile"; import { UniversalNavbar } from "../UniversalNavbar"; diff --git a/packages/component-header/src/components/UniversalNavbar/Login/index.js b/packages/component-header/src/components/UniversalNavbar/Login/index.js index c2aa279f0b..ee70d67b60 100644 --- a/packages/component-header/src/components/UniversalNavbar/Login/index.js +++ b/packages/component-header/src/components/UniversalNavbar/Login/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../../shared"; import { useAppContext } from "../../../core/context/app-context"; import { LoginWrapper } from "./index.styles"; diff --git a/packages/component-header/src/components/UniversalNavbar/Login/index.test.js b/packages/component-header/src/components/UniversalNavbar/Login/index.test.js index e9ac99272d..5411ba5fe3 100644 --- a/packages/component-header/src/components/UniversalNavbar/Login/index.test.js +++ b/packages/component-header/src/components/UniversalNavbar/Login/index.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header/src/components/UniversalNavbar/Search/index.js b/packages/component-header/src/components/UniversalNavbar/Search/index.js index 4a89db0779..7c6f60f2ad 100644 --- a/packages/component-header/src/components/UniversalNavbar/Search/index.js +++ b/packages/component-header/src/components/UniversalNavbar/Search/index.js @@ -1,9 +1,9 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import { faSearch, faTimes } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React, { useState, useRef, useEffect } from "react"; -import { trackGAEvent } from "../../../../../../shared"; import { useAppContext } from "../../../core/context/app-context"; import { useIsMobile } from "../../../core/hooks/isMobile"; import { SearchWrapper } from "./index.styles"; diff --git a/packages/component-header/src/components/UniversalNavbar/Search/index.test.js b/packages/component-header/src/components/UniversalNavbar/Search/index.test.js index c78a3e8e5c..2ab219a61f 100644 --- a/packages/component-header/src/components/UniversalNavbar/Search/index.test.js +++ b/packages/component-header/src/components/UniversalNavbar/Search/index.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, fireEvent, cleanup } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-header/src/components/UniversalNavbar/index.js b/packages/component-header/src/components/UniversalNavbar/index.js index 97e37287a2..e216e59ecb 100644 --- a/packages/component-header/src/components/UniversalNavbar/index.js +++ b/packages/component-header/src/components/UniversalNavbar/index.js @@ -1,7 +1,7 @@ // @ts-check +import { trackGAEvent } from "@asu/shared"; import React from "react"; -import { trackGAEvent } from "../../../../../shared"; import { useAppContext } from "../../core/context/app-context"; import { Wrapper } from "./index.styles"; import { Login } from "./Login"; diff --git a/packages/component-header/src/header.js b/packages/component-header/src/header.js index 8bb95efcdb..1b9c162640 100644 --- a/packages/component-header/src/header.js +++ b/packages/component-header/src/header.js @@ -1,7 +1,7 @@ // @ts-check +import { trackReactComponent } from "@asu/shared"; import React, { useEffect, useRef } from "react"; -import trackReactComponent from "../../../shared/services/componentDatalayer"; import { HeaderMain } from "./components/HeaderMain"; import { AppContextProvider } from "./core/context/app-context"; import { HeaderPropTypes } from "./core/models/app-prop-types"; diff --git a/packages/component-header/src/header.test.js b/packages/component-header/src/header.test.js index 92b5277947..b0d53c7967 100644 --- a/packages/component-header/src/header.test.js +++ b/packages/component-header/src/header.test.js @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-mocks-import */ // @ts-check import { render, cleanup, screen, act } from "@testing-library/react"; import React from "react"; diff --git a/packages/component-news/.eslintrc.js b/packages/component-news/.eslintrc.js deleted file mode 100644 index 9c07c9028d..0000000000 --- a/packages/component-news/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], -}; diff --git a/packages/component-news/__mocks__/component-carousel-mock.jsx b/packages/component-news/__mocks__/component-carousel-mock.jsx index 3f67ae976a..bfdec7cc1b 100644 --- a/packages/component-news/__mocks__/component-carousel-mock.jsx +++ b/packages/component-news/__mocks__/component-carousel-mock.jsx @@ -1,7 +1,6 @@ -/* eslint-disable react/jsx-props-no-spreading */ // @ts-check import * as asuCore from "@asu/unity-react-core"; -import * as React from "react"; +import React from "react"; import { vi } from "vitest" const { diff --git a/packages/component-news/eslint.config.js b/packages/component-news/eslint.config.js new file mode 100644 index 0000000000..6b534f22b4 --- /dev/null +++ b/packages/component-news/eslint.config.js @@ -0,0 +1,5 @@ +const rootConfig = require("../../eslint.config.js"); + +module.exports = [ + ...rootConfig, +]; diff --git a/packages/component-news/package.json b/packages/component-news/package.json index ed8dd47026..61a56cfb20 100644 --- a/packages/component-news/package.json +++ b/packages/component-news/package.json @@ -24,7 +24,7 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", + "lint": "eslint --fix 'src/**/*.{js,jsx}' ", "test": "vitest --watch=false", "test-update-snapshot": "yarn test -- -u", "build": "vite build && yarn postbuild", @@ -66,7 +66,6 @@ "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.2.4", "dotenv-webpack": "^7.0.3", - "eslint-plugin-storybook": "^0.6.15", "express": "^4.17.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.3.2", diff --git a/packages/component-news/src/components/CardCarouselNews/index.jsx b/packages/component-news/src/components/CardCarouselNews/index.jsx index 796d8cfb83..23f1e7d45d 100644 --- a/packages/component-news/src/components/CardCarouselNews/index.jsx +++ b/packages/component-news/src/components/CardCarouselNews/index.jsx @@ -2,7 +2,7 @@ import { CardCarousel, FeedContext } from "@asu/unity-react-core"; import React, { useContext, useEffect } from "react"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; +import { trackReactComponent } from "@asu/shared"; import { BaseFeed } from "../../core/components/BaseFeed"; import { defaultProps } from "../../core/constants/default-props"; import { NewsWrapper } from "./index.styles"; @@ -32,7 +32,7 @@ const cardRow = (feed, index, cardButton) => ({ /** * @param {import("../../core/types/news-types").TemplateProps} props */ -// eslint-disable-next-line react/prop-types + const CarouselTemplate = ({ cardButton }) => { const { feeds } = useContext(FeedContext); // Reading the "feeds" object from the context const cardItems = feeds?.map((feed, index) => diff --git a/packages/component-news/src/components/CardGridNews/index.jsx b/packages/component-news/src/components/CardGridNews/index.jsx index af70e844b7..01c31e58e4 100644 --- a/packages/component-news/src/components/CardGridNews/index.jsx +++ b/packages/component-news/src/components/CardGridNews/index.jsx @@ -2,7 +2,7 @@ import { Card, feedCardButtonShape, FeedContext } from "@asu/unity-react-core"; import React, { useContext, useEffect } from "react"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; +import { trackReactComponent } from "@asu/shared"; import { BaseFeed } from "../../core/components/BaseFeed"; import { defaultProps } from "../../core/constants/default-props"; import { parseInterests } from "../../core/utils"; @@ -46,14 +46,13 @@ const gridRow = (feed, cardButton) => ( /** * @param {import("../../core/types/news-types").TemplateProps} props */ -// eslint-disable-next-line react/prop-types + const GridTemplate = ({ cardButton }) => { const { feeds } = useContext(FeedContext); // Reading the "feeds" object from the context return ( {feeds?.map((feed, index) => ( - // eslint-disable-next-line react/no-array-index-key {gridRow(feed, cardButton)} ))} diff --git a/packages/component-news/src/components/CardListlNews/index.jsx b/packages/component-news/src/components/CardListlNews/index.jsx index d04e51e90e..7c7f618484 100644 --- a/packages/component-news/src/components/CardListlNews/index.jsx +++ b/packages/component-news/src/components/CardListlNews/index.jsx @@ -2,7 +2,7 @@ import { Card, feedCardButtonShape, FeedContext } from "@asu/unity-react-core"; import React, { useContext, useEffect } from "react"; -import trackReactComponent from "../../../../../shared/services/componentDatalayer"; +import { trackReactComponent } from "@asu/shared"; import { BaseFeed } from "../../core/components/BaseFeed"; import { defaultProps } from "../../core/constants/default-props"; import { parseInterests } from "../../core/utils"; @@ -43,14 +43,13 @@ const listRow = (feed, cardButton) => ( /** * @param {import("../../core/types/news-types").TemplateProps} props */ -// eslint-disable-next-line react/prop-types + const ListTemplate = ({ cardButton }) => { const { feeds } = useContext(FeedContext); // Reading the "feeds" object from the context return ( {feeds?.map((feed, index) => ( - // eslint-disable-next-line react/no-array-index-key {listRow(feed, cardButton)} ))} diff --git a/packages/component-news/src/core/utils/story-utils.js b/packages/component-news/src/core/utils/story-utils.js index 60f537bab4..3257b2370a 100644 --- a/packages/component-news/src/core/utils/story-utils.js +++ b/packages/component-news/src/core/utils/story-utils.js @@ -1,6 +1,5 @@ // @ts-check -// eslint-disable-next-line jest/no-mocks-import import * as feeds from "../../../__mocks__/api/feeds.json"; function createMockParam() { diff --git a/packages/component-news/vite.config.js b/packages/component-news/vite.config.js index 52cc25f20e..0546d68a1b 100644 --- a/packages/component-news/vite.config.js +++ b/packages/component-news/vite.config.js @@ -49,9 +49,4 @@ export default defineConfig({ }, ], - resolve: { - alias: { - "@shared": path.resolve(__dirname, "./../../shared"), - }, - }, }); diff --git a/packages/component-news/vitest.config.ts b/packages/component-news/vitest.config.ts index d27a08554b..f063f973cf 100644 --- a/packages/component-news/vitest.config.ts +++ b/packages/component-news/vitest.config.ts @@ -10,10 +10,5 @@ export default defineConfig({ setupFiles: ['./vitest.setup.ts'], globals: true }, - resolve: { - alias: { - "@shared": resolve(__dirname, "./../../shared"), - } - }, }); diff --git a/packages/components-core/.eslintrc.js b/packages/components-core/.eslintrc.js deleted file mode 100644 index 3cc1ce6adf..0000000000 --- a/packages/components-core/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.base.js"], - rules: { - "react/no-unstable-nested-components": "off", - }, -}; diff --git a/packages/components-core/.gitignore b/packages/components-core/.gitignore deleted file mode 100644 index fa2884279a..0000000000 --- a/packages/components-core/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -percy-storybook -docs/** -!docs/README.props.md diff --git a/packages/components-core/.storybook/main.js b/packages/components-core/.storybook/main.js deleted file mode 100644 index ec7cf2b01d..0000000000 --- a/packages/components-core/.storybook/main.js +++ /dev/null @@ -1,48 +0,0 @@ -const path = require("path"); -const PROJECT_DIR = path.resolve(__dirname, "../"); - -const config = { - staticDirs: ['../dist'], - stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], - addons: [ - "../../../.storybook-config", - "../../../.storybook-config/dataLayerListener", - "@whitespace/storybook-addon-html", - "@storybook/addon-links", - "@storybook/addon-essentials", - "storybook-css-modules-preset", - ], - framework: { - name: "@storybook/react-webpack5", - options: {} - }, - webpackFinal: config => { - config.module.rules.push({ - test: /\.scss$/, - use: [ - "style-loader", - { loader: "css-loader", options: { importLoaders: 1, url: false } }, - { - loader: "sass-loader", - options: {}, - }, - ], - }); - - return { - ...config, - resolve: { - ...config.resolve, - alias: { - Components: path.resolve(PROJECT_DIR, "src/components/"), - Vendor: path.resolve(PROJECT_DIR, "vendor/"), - }, - }, - }; - }, - - docs: { - autodocs: true - } -}; -export default config; diff --git a/packages/components-core/.storybook/percy/main.js b/packages/components-core/.storybook/percy/main.js deleted file mode 100644 index a2726d8543..0000000000 --- a/packages/components-core/.storybook/percy/main.js +++ /dev/null @@ -1,44 +0,0 @@ -const path = require("path"); -const PROJECT_DIR = path.resolve(__dirname, "../../"); - -const config = { - staticDirs: ['../../dist'], - stories: [ - "../../tests/**/*.percy.mdx", - "../../tests/**/*.percy.@(js|jsx|ts|tsx)", - ], - addons: [ - "@storybook/addon-links", - "@storybook/addon-essentials", - "storybook-css-modules-preset", - ], - framework: { - name: "@storybook/react-webpack5", - options: {} - }, - webpackFinal: config => { - config.module.rules.push({ - test: /\.scss$/, - use: [ - "style-loader", - { loader: "css-loader", options: { importLoaders: 1 } }, - { - loader: "sass-loader", - options: {}, - }, - ], - }); - return { - ...config, - resolve: { - ...config.resolve, - alias: { - Components: path.resolve(PROJECT_DIR, "src/components/"), - Vendor: path.resolve(PROJECT_DIR, "vendor/"), - }, - }, - }; - }, -}; - -export default config; diff --git a/packages/components-core/.storybook/percy/preview-head.html b/packages/components-core/.storybook/percy/preview-head.html deleted file mode 100644 index 75c21371ae..0000000000 --- a/packages/components-core/.storybook/percy/preview-head.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/packages/components-core/.storybook/percy/preview.js b/packages/components-core/.storybook/percy/preview.js deleted file mode 100644 index 442ec5ee5b..0000000000 --- a/packages/components-core/.storybook/percy/preview.js +++ /dev/null @@ -1,23 +0,0 @@ -import "@asu/unity-bootstrap-theme/src/scss/unity-bootstrap-theme.bundle.scss"; -const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, - backgrounds: { - values: [ - { - name: "Gray 2", - value: "#e8e8e8", - }, - { - name: "Gray 3", - value: "#d0d0d0", - }, - ], - }, -}; - -/** @type { import('@storybook/react').Preview } */ -const preview = { - parameters -}; - -export default preview; diff --git a/packages/components-core/.storybook/preview-head.html b/packages/components-core/.storybook/preview-head.html deleted file mode 100644 index 55d5b94203..0000000000 --- a/packages/components-core/.storybook/preview-head.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - diff --git a/packages/components-core/.storybook/preview.js b/packages/components-core/.storybook/preview.js deleted file mode 100644 index e373112069..0000000000 --- a/packages/components-core/.storybook/preview.js +++ /dev/null @@ -1,25 +0,0 @@ -import "@asu/unity-bootstrap-theme/src/scss/unity-bootstrap-theme.bundle.scss"; -import "../../../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"; - -const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, - backgrounds: { - values: [ - { - name: "Gray 2", - value: "#e8e8e8", - }, - { - name: "Gray 3", - value: "#d0d0d0", - }, - ], - }, -}; - -/** @type { import('@storybook/react').Preview } */ -const preview = { - parameters -}; - -export default preview; diff --git a/packages/components-core/CHANGELOG.md b/packages/components-core/CHANGELOG.md deleted file mode 100644 index ad7c155551..0000000000 --- a/packages/components-core/CHANGELOG.md +++ /dev/null @@ -1,289 +0,0 @@ -# [@asu/components-core-v4.3.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v4.2.0...@asu/components-core-v4.3.0) (2025-02-18) - - -### Features - -* **components-core:** add Deprecation ([9a35364](https://github.com/ASU/asu-unity-stack/commit/9a3536442d44efb313df66c0c2bbefd5162c0dbb)) - -# [@asu/components-core-v4.2.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v4.1.1...@asu/components-core-v4.2.0) (2025-02-11) - - -### Bug Fixes - -* **components-core:** remove deprecated message ([3628bad](https://github.com/ASU/asu-unity-stack/commit/3628bad8f8229ac2ca42be1846064fd39f00154b)) -* **unity-react-core:** update package.json ([7eabcca](https://github.com/ASU/asu-unity-stack/commit/7eabcca01058eb26a1954fd26831b7ebbf1ea161)) - - -### Features - -* **components-core:** added component-carousel to components-core ([8677db8](https://github.com/ASU/asu-unity-stack/commit/8677db8f92c15f768c35951fbbd85f254f437de0)) - -# [@asu/components-core-v4.1.1](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v4.1.0...@asu/components-core-v4.1.1) (2024-09-04) - - -### Bug Fixes - -* **components-core:** updated small ranking card for accessibility ([377fde0](https://github.com/ASU/asu-unity-stack/commit/377fde084b3ed96d0b1548dae05054f627f4f233)) - -# [@asu/components-core-v4.1.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v4.0.0...@asu/components-core-v4.1.0) (2024-08-07) - - -### Bug Fixes - -* **components-core:** just a small change to create pr ([64cde43](https://github.com/ASU/asu-unity-stack/commit/64cde43e6dad644877b2e41b198c76876a586145)) - - -### Features - -* **unity-bootstrap-theme:** added styles for focus on accordions ([145c4a1](https://github.com/ASU/asu-unity-stack/commit/145c4a10faf783a918973b7d300392d44d22d217)) - -# [@asu/components-core-v4.0.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.8...@asu/components-core-v4.0.0) (2024-08-01) - - -### Bug Fixes - -* **components-core:** switch h4 to h2 on AnchorMenu ([bfde9f2](https://github.com/ASU/asu-unity-stack/commit/bfde9f281bfffa4a62592292be6743e795cc0cd4)) - - -### BREAKING CHANGES - -* **components-core:** It is necessary to replicate the changes from the unity-bootstrap-theme in the -component-core. - -UDS-1773 - -# [@asu/components-core-v3.3.8](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.7...@asu/components-core-v3.3.8) (2024-07-31) - - -### Bug Fixes - -* **components-core:** fix A11y issue with incorrect Hero Heading structure ([bddc4bd](https://github.com/ASU/asu-unity-stack/commit/bddc4bd62a606fe2b6e0ae670688bb7587dfa426)) - -# [@asu/components-core-v3.3.7](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.6...@asu/components-core-v3.3.7) (2024-06-25) - - -### Bug Fixes - -* **components-core:** remove import of bootstrap collapse in accordion ([88ebe12](https://github.com/ASU/asu-unity-stack/commit/88ebe12c845f1ea27c28998a2f8c186e2fe5f1a3)) - -# [@asu/components-core-v3.3.6](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.5...@asu/components-core-v3.3.6) (2024-06-18) - - -### Bug Fixes - -* **components-core:** caret is now part of button ([767e932](https://github.com/ASU/asu-unity-stack/commit/767e932e173146088526b5ebf29fd1054fc6f5c3)) -* **components-core:** fixed Ranking card accessibility issues ([a7074dd](https://github.com/ASU/asu-unity-stack/commit/a7074dd37c3f9fd6c9f9c92ca395e688370a0b47)) -* **components-core:** ranking card accessibility "see more" fixed ([25afc1d](https://github.com/ASU/asu-unity-stack/commit/25afc1d95f76286504aab014ce6a3bc283901f34)) -* **components-core:** rearrange markup in ranking cards ([85a2fb9](https://github.com/ASU/asu-unity-stack/commit/85a2fb95c75b4c12c926091f7dd7cc8b58dd6db5)) -* **components-core:** remove useRef for ID generation ([85d0658](https://github.com/ASU/asu-unity-stack/commit/85d0658e21972fd8b16b00f5fb6d4afb38896fc1)) -* **components-core:** updated accessibility in Ranking Card ([53d4ec6](https://github.com/ASU/asu-unity-stack/commit/53d4ec60b81ea006993939780e9d64fc6b265386)) -* **components-core:** updated large ranking card accessibility ([183685f](https://github.com/ASU/asu-unity-stack/commit/183685fe1b68ac4d8de3c0814f8a3f64b34a3ce8)) - -# [@asu/components-core-v3.3.5](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.4...@asu/components-core-v3.3.5) (2024-06-10) - - -### Bug Fixes - -* **components-core:** add new plain white text hero option ([7edb1b3](https://github.com/ASU/asu-unity-stack/commit/7edb1b3c0409ad86e26db236bc41ace81a7209e1)) - -# [@asu/components-core-v3.3.4](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.3...@asu/components-core-v3.3.4) (2024-01-29) - - -### Bug Fixes - -* **components-core:** switched to relative import for less packages to install ([06c2869](https://github.com/ASU/asu-unity-stack/commit/06c28690150b7086e4a67357db77609c3fc077fc)) -* **components-core:** wrap accordion body with collapse class for smoother animation ([0c792d3](https://github.com/ASU/asu-unity-stack/commit/0c792d3ddfa91803895d62d4984ac27f944d11b0)) - -# [@asu/components-core-v3.3.3](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.2...@asu/components-core-v3.3.3) (2024-01-22) - - -### Bug Fixes - -* **unity-bootstrap-theme:** fix card padding inconsistancies ([4a858f8](https://github.com/ASU/asu-unity-stack/commit/4a858f8a8a646edd0255cd1e5e5c7f0e507d0e7d)) - -# [@asu/components-core-v3.3.2](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.1...@asu/components-core-v3.3.2) (2024-01-11) - - -### Bug Fixes - -* slight changes ([f07b430](https://github.com/ASU/asu-unity-stack/commit/f07b4306a01bda66e2a53d54b9aa35d821ee0c1f)) - -# [@asu/components-core-v3.3.1](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.3.0...@asu/components-core-v3.3.1) (2023-12-04) - - -### Bug Fixes - -* **components-core:** allow sanitized html in image citation ([269fa92](https://github.com/ASU/asu-unity-stack/commit/269fa92f449372666e0d307b00a65f95117cdfa6)) - -# [@asu/components-core-v3.3.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.2.0...@asu/components-core-v3.3.0) (2023-12-04) - - -### Bug Fixes - -* **components-core:** added classes to image renders in Image component ([6b7687f](https://github.com/ASU/asu-unity-stack/commit/6b7687f42f408d88555fb7e793594aa48e2769d9)) -* **components-core:** fixed rendering of wrapper div around images ([1a8a075](https://github.com/ASU/asu-unity-stack/commit/1a8a075ad22e12b05fe726cb0d837efdb94a504c)) -* **components-core:** updated news and story cards to match ws ([dc1e9b1](https://github.com/ASU/asu-unity-stack/commit/dc1e9b13ad5e991a3733a0e0e7dd7e1c4c1928a1)) - - -### Features - -* **components-core:** added new props in image component ([b70bc76](https://github.com/ASU/asu-unity-stack/commit/b70bc76d61943331f51011d1ad95065fe4e64faa)) -* **components-core:** updated stories and conditional rendering for Image component ([9d10537](https://github.com/ASU/asu-unity-stack/commit/9d10537e92e40ef5d077b22aaee2a4c7c0c68d13)) - -# [@asu/components-core-v3.2.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.1.4...@asu/components-core-v3.2.0) (2023-11-20) - - -### Bug Fixes - -* **components-core:** export initFuncs ([5da35a2](https://github.com/ASU/asu-unity-stack/commit/5da35a2b6bebd56e054754beb40a5d40df3e38b7)) -* **components-core:** fix components-core init ([38bdb42](https://github.com/ASU/asu-unity-stack/commit/38bdb42bf9243f6ffaf0a615e769388b22a2ef66)) - - -### Features - -* **components-core:** add InitImage ([4139660](https://github.com/ASU/asu-unity-stack/commit/41396609a2f483cc1ff2fc52e984b1fc551afd4f)) - -# [@asu/components-core-v3.1.4](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.1.3...@asu/components-core-v3.1.4) (2023-10-18) - - -### Bug Fixes - -* **components-core:** fixed tab panels error that did not account for negative scroll width ([185051f](https://github.com/ASU/asu-unity-stack/commit/185051f3a860195f982b90503dfedee6e1e08b61)) - -# [@asu/components-core-v3.1.3](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.1.2...@asu/components-core-v3.1.3) (2023-10-17) - - -### Bug Fixes - -* **components-core:** removed console log ([03464ab](https://github.com/ASU/asu-unity-stack/commit/03464ab6aee174b5ca64f7b0e60981421dee809c)) -* **components-core:** useEffect guards in cleanup function of TabbedPanels ([80b3ac0](https://github.com/ASU/asu-unity-stack/commit/80b3ac0c1556b7d0deb021271995f5aff564c8b8)) - -# [@asu/components-core-v3.1.2](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.1.1...@asu/components-core-v3.1.2) (2023-10-03) - - -### Bug Fixes - -* **components-core:** fix package.json exports ([4011fbe](https://github.com/ASU/asu-unity-stack/commit/4011fbe17e30aa6f293e9e5b22922c4aa8708f0a)) - -# [@asu/components-core-v3.1.1](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.1.0...@asu/components-core-v3.1.1) (2023-10-02) - - -### Bug Fixes - -* **components-core:** readMoreLink not required in ranming card ([904e8c6](https://github.com/ASU/asu-unity-stack/commit/904e8c629742f801955ebe7a58c28c7100fef04b)) - -# [@asu/components-core-v3.1.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v3.0.0...@asu/components-core-v3.1.0) (2023-09-18) - - -### Bug Fixes - -* **components-core:** ignore eslint warnings ([6304782](https://github.com/ASU/asu-unity-stack/commit/6304782438b84aabfc2d0e16e7ff8aeafcb35e60)) - - -### Features - -* **components-core:** updated events card ([05f6af4](https://github.com/ASU/asu-unity-stack/commit/05f6af48cc9bfbf42da767d0b46a994f5d1bef82)) - -# [@asu/components-core-v3.0.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.2.2...@asu/components-core-v3.0.0) (2023-09-07) - - -### Bug Fixes - -* **components-core:** improve pagination accessibility, remove unused props ([52b8c40](https://github.com/ASU/asu-unity-stack/commit/52b8c40761482251e9b267a2821f21d95c391c26)) - - -### BREAKING CHANGES - -* **components-core:** Removed unused props - -# [@asu/components-core-v2.2.2](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.2.1...@asu/components-core-v2.2.2) (2023-08-02) - - -### Bug Fixes - -* **components-core:** update README.md to trigger release for ws2-1634 ([df59a6b](https://github.com/ASU/asu-unity-stack/commit/df59a6bec2d11a4205aa4eb491bcbbfa327747a1)) - -# [@asu/components-core-v2.2.1](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.2.0...@asu/components-core-v2.2.1) (2023-07-27) - - -### Bug Fixes - -* **app-degree-pages:** Update TabbedPanels ([5b46336](https://github.com/ASU/asu-unity-stack/commit/5b46336ff4207ea0fa254752f7ac746d6c46e23a)) -* **components-core:** activeTab less aggressive ([ebd67a8](https://github.com/ASU/asu-unity-stack/commit/ebd67a810b6fb4fbcaf273bc2d474c0eca2635e4)) -* **components-core:** change TabbedPanels props ([526dca3](https://github.com/ASU/asu-unity-stack/commit/526dca3a57cc0f705965c0ef5e1714e78f786e7f)) -* **components-core:** fix TabbedPanel activeTab ([169540e](https://github.com/ASU/asu-unity-stack/commit/169540e46faf8affcf504030ba4c064b88c24fb6)) -* **components-core:** update TabbedPanels props ([d72714c](https://github.com/ASU/asu-unity-stack/commit/d72714c8a05c5e6821bfb02ee0295a4438298638)) - -# [@asu/components-core-v2.2.1](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.2.0...@asu/components-core-v2.2.1) (2023-07-27) - - -### Bug Fixes - -* **app-degree-pages:** Update TabbedPanels ([5b46336](https://github.com/ASU/asu-unity-stack/commit/5b46336ff4207ea0fa254752f7ac746d6c46e23a)) -* **components-core:** activeTab less aggressive ([ebd67a8](https://github.com/ASU/asu-unity-stack/commit/ebd67a810b6fb4fbcaf273bc2d474c0eca2635e4)) -* **components-core:** change TabbedPanels props ([526dca3](https://github.com/ASU/asu-unity-stack/commit/526dca3a57cc0f705965c0ef5e1714e78f786e7f)) -* **components-core:** fix TabbedPanel activeTab ([169540e](https://github.com/ASU/asu-unity-stack/commit/169540e46faf8affcf504030ba4c064b88c24fb6)) -* **components-core:** update TabbedPanels props ([d72714c](https://github.com/ASU/asu-unity-stack/commit/d72714c8a05c5e6821bfb02ee0295a4438298638)) - -# [@asu/components-core-v2.2.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.1.1...@asu/components-core-v2.2.0) (2023-07-24) - - -### Bug Fixes - -* **components-core:** add exports to package.json ([e4df59a](https://github.com/ASU/asu-unity-stack/commit/e4df59ac3c0d382ebdbe444c6cdc2c189bfb1d14)) -* **components-core:** align anchor menu with its content ([fb8bfed](https://github.com/ASU/asu-unity-stack/commit/fb8bfedd3e0553900c4edf22b5868340a6ca9228)) -* **components-core:** exported tabbedpanels correctly ([96049b1](https://github.com/ASU/asu-unity-stack/commit/96049b18e5ea00ea87bcb1d6e07c09517d502033)) -* **components-core:** fix typo error in readme ([aaa05b5](https://github.com/ASU/asu-unity-stack/commit/aaa05b5aac156178fd6b567410cbc8f94ae32191)) -* **components-core:** removed exports ([ce3c5fd](https://github.com/ASU/asu-unity-stack/commit/ce3c5fd178e32377c4ed5e1b69107fb61eb918fd)) -* **components-core:** updated package.json main and types ([f8859a8](https://github.com/ASU/asu-unity-stack/commit/f8859a8a1c93cc371d7e47fe4d1c4cae84448d0e)) - - -### Features - -* **components-core:** image component ([80da9f6](https://github.com/ASU/asu-unity-stack/commit/80da9f6dbcf3f2398a3098fe458b140cf0313dbc)) -* **components-core:** new Image component ([c710c5c](https://github.com/ASU/asu-unity-stack/commit/c710c5cc13209ddd4ce5b995147284995e50e216)) -* **image component:** fix linter issues ([b7bd259](https://github.com/ASU/asu-unity-stack/commit/b7bd25989278ee3570efdd645f91de97e939331f)) - -# [@asu/components-core-v2.1.1](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.1.0...@asu/components-core-v2.1.1) (2023-06-28) - - -### Bug Fixes - -* **components-core:** add cardLink prop and update story ([59ccda8](https://github.com/ASU/asu-unity-stack/commit/59ccda82f9c37dc5185b5c1b86a3c1612c9f8c64)) -* **components-core:** add missing init for ranking cards. rename folder ([ba9e653](https://github.com/ASU/asu-unity-stack/commit/ba9e6536f6101ddbfbbf111e313456624e996dff)) -* **components-core:** added prop types ([d2732b1](https://github.com/ASU/asu-unity-stack/commit/d2732b1e130ee44b4706555c42e8351994486220)) -* **components-core:** card titles are now links ([326d4bc](https://github.com/ASU/asu-unity-stack/commit/326d4bc3251c5df6ee253bb652504e538703a58e)) -* **components-core:** removed log statement ([8f5135f](https://github.com/ASU/asu-unity-stack/commit/8f5135f8dd732d85e17fbaf3414207bf2561b211)) - -# [@asu/components-core-v2.1.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.0.1...@asu/components-core-v2.1.0) (2023-06-08) - - -### Features - -* **components-core:** add Ranking cards component ([c97ad24](https://github.com/ASU/asu-unity-stack/commit/c97ad249f3f8163f917464c548fc0df21c6977a1)) -* **components-core:** fix lint ([61dc543](https://github.com/ASU/asu-unity-stack/commit/61dc5430610048fbd644c77eca45515622fedf1f)) -* **components-core:** ranking cards component ([c039dc1](https://github.com/ASU/asu-unity-stack/commit/c039dc15f0f72d94f10cb21601ba790e2e6567bb)) -* **components-core:** ranking cards component ([f31bff0](https://github.com/ASU/asu-unity-stack/commit/f31bff03e7922528f55533f1929b417950fcee84)) - -# [@asu/components-core-v2.0.1](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v2.0.0...@asu/components-core-v2.0.1) (2023-05-23) - - -### Bug Fixes - -* **components-core:** changelog cleanup ([0e183d6](https://github.com/ASU/asu-unity-stack/commit/0e183d6c3d557a46f29df532be376fcd9a37f789)) - -# [@asu/components-core-v2.0.0](https://github.com/ASU/asu-unity-stack/compare/@asu/components-core-v1.5.0...@asu/components-core-v2.0.0) (2023-05-23) - - -### Features - -* **unity-bootstrap-theme:** MAIN migration to BS5 PR ([#992](https://github.com/ASU/asu-unity-stack/issues/992)) ([8c74ce4](https://github.com/ASU/asu-unity-stack/commit/8c74ce4dc65278839b207b9ae895ea76e8e2195d)) - - -### BREAKING CHANGES - -* **unity-bootstrap-theme:** New BS5 theme is used
    -update theme dependency from bootstrap4-theme to unity-bootstrap-theme
    -see unity-bootstrap-theme UPGRADE.md for more information.
    -refers to UDS-1277
    -BREAKING CHANGE: updates theme dependency and markup for Bootstrap 5 compatibility. diff --git a/packages/components-core/README.md b/packages/components-core/README.md deleted file mode 100644 index 67546cd45a..0000000000 --- a/packages/components-core/README.md +++ /dev/null @@ -1,200 +0,0 @@ -# ASU Components Core - -ASU React Core Components. - -This component is the react implementation of some of the components of the Bootstrap 4 theme package. -All of these components are listed [below](#examples---quick-links). - -## Architecture details - -As this package is intended to be the react core components package, each of these components are built in isolation, thats mean that, each component, was thougth to be used in different scenarios in the same way. -For each component there is a `.js` file, that contains the code of that component, a `.test.js` file, that has the unit test within that component. For this last file, we use [React testing library](https://testing-library.com/docs/react-testing-library/intro), that is use in combination with [Jest](https://jestjs.io/). This provide us a nice way to test all the components in terms of what that component paints in the DOM and what user sees. There might be another file on some component folder that contains some specific styling for that component(`.styles.js`). As you may noticed, we are using css in js to code the styles. For this, we make use of [styled-components](https://styled-components.com/) that has a great integration and support in the react ecosystem. The last file included, for each component, is the `.stories.js` one, that one, has the configuration for all the stories that are shown in [Storybook](https://storybook.js.org/) page. -The initialization, for plain Javascript, of each component is the same as all the packages, so it can keep uniqueness between all of them. For this, we use two methods: - -- `createElement`: this is provided by React. It creates a new React element. Click to read more about [createElement](https://reactjs.org/docs/react-api.html#createelement) and [react without jsx](https://reactjs.org/docs/react-without-jsx.html). -- `render`: this is provided by ReactDOM library. It is used to render the react element in the DOM. Click to read more about [render](https://reactjs.org/docs/react-dom.html). - The way it works is: when the initializer function is called, this creates the element with the props provided and the react component.Then it is rendered on the DOM. - - -## Component props documentation - -You can find a full list of props into the [docs/README.props.md](docs/README.props.md) - -## CLI Commands - -```bash -# add component-carousel -yarn add @asu/components-core - -# run storybook -yarn storybook - -# build for production with minification -yarn build - -# run tests -yarn test - -``` - -## How to install - -1. Either make sure you are part of the ASU github organization and follow the instructions [here,](https://github.com/ASU/asu-unity-stack#-how-to-use-the-private-package-registry)or, if you already are, you can clone this repo and run `yarn install` and `yarn build` to build the package locally. -2. Make sure to have included [FontAwesome](https://fontawesome.com/) on your project to allow icons to be shown -3. `yarn add @asu/components-core` - -## Use as a JS module in React app - -```JAVASCRIPT - import { Card } from '@asu/components-core@dev' - - // Build out the component, providing the options depending on the card you wanna - // have. - // Example provided below - const App = (props) => { - return ( -
    - -
    - ) - }; - - export default App; - -``` - -## Use on static HTML page - -```HTML - -
    -
    - - - - - - - - - -``` - -### Examples - -The folder [packages/components-core/examples](/packages/components-core/examples) -
    contains examples to use all the core components on static HTML pages - -#### Examples - quick links - -- [Accordion](/packages/components-core/examples/accordion.html) -- [Anchor Menu](/packages/components-core/examples/anchorMenu.html) -- [Article](/packages/components-core/examples/article.html) -- [Article](/packages/components-core/examples/article.html) -- [Button](/packages/components-core/examples/button.html) -- [Button Icon Only](/packages/components-core/examples/buttonIconOnly.html) -- [Button Tag](/packages/components-core/examples/buttonTag.html) -- [Card](/packages/components-core/examples/card.html) -- [Hero](/packages/components-core/examples/hero.html) -- [Pagination](/packages/components-core/examples/pagination.html) -- [Testimonial](/packages/components-core/examples/testimonial.html) -- [Video](/packages/components-core/examples/video.html) - -#### Examples - run and test - -If you want to test the examples files you need to install an application server
    -and run it into the folder `/packages/components-core/examples`.
    -For example, if you want to use the `npm` package `lite-server` follow these steps: - -- run `npm -g i lite-server` . MAC users may need to use `sudo npm -g i lite-server` -- run `cd packages/components-core` -- run `lite-server` -- open the broweser to the url `http://localhost:3000/examples/card.html` - (port number may be different) - -## Future improvements - -All the requirements for this component were covered, so there is no need of any further enhancements at the moment this is being written. - -## Tools - -- [How to convert prop types to definition types](https://github.com/mskelton/ratchet) - - [Online tool](https://mskelton.dev/ratchet) diff --git a/packages/components-core/__mocks__/data/feed-anatomy-search.json b/packages/components-core/__mocks__/data/feed-anatomy-search.json deleted file mode 100644 index cce215e334..0000000000 --- a/packages/components-core/__mocks__/data/feed-anatomy-search.json +++ /dev/null @@ -1,876 +0,0 @@ -{ - "nodes": [ - { - "node": { - "nid": "90179", - "title": "No. 21 @SunDevilSoccer's unbeaten streaks come to an end against No. 17 LSU", - "body": "\nDevils had won five in a row to start the season and were unbeaten in last 11 regular season contests against non-conference opponents.\n]]>", - "post_date": "09/05/2021-2:54pm", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210905-no-21-sundevilsoccers-unbeaten-streaks-come-end-against-no-17-lsu", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/5/DSC_6409.jpg", - "image_url": "https://thesundevils.com/images/2021/9/5/DSC_6409.jpg", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90177", - "title": "No. 21 @SunDevilSoccer hosts No. 17 LSU in matchup of 5-0 teams", - "body": "\nThe No. 21 Sun Devil soccer team will be looking for its first 6-0 start since 2000 when it hosts No. 17 LSU on Sunday (2 p.m. MST) in the second and final day of the annual Sun Devil Classic.\n]]>", - "post_date": "09/04/2021-4:21pm", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210904-no-21-sundevilsoccer-hosts-no-17-lsu-matchup-5-0-teams", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/4/DSC_5766.jpg", - "image_url": "https://thesundevils.com/images/2021/9/4/DSC_5766.jpg", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90176", - "title": "Triathlon Sweeps Podium at Pleasant Prairie", - "body": "\nSun Devils swept podium at Pleasant Prairie and qualify for National Tournament.\n]]>", - "post_date": "09/04/2021-7:07am", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210904-triathlon-sweeps-podium-pleasant-prairie", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/4/IMG_5278.jpeg", - "image_url": "https://thesundevils.com/images/2021/9/4/IMG_5278.jpeg", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90149", - "title": "Essays explore altered social experiences from the COVID-19 pandemic", - "body": "\u201cThe Pandemic Reader\u201d is a new collection of essays edited by faculty in Arizona State University's School of Social Transformation, in The College of Liberal Arts and Sciences.The collection explores the multitude of ways in which the COVID-19 pandemic has changed life in every aspect. As people around the world try to navigate challenges and revelations that have unfolded in light of the coronavirus pandemic, the faculty involved in the project say it is still crucial to consider the societal impact at large, and what it will mean down the line.\u00a0\u201cOur collection of essays, articles and activities are designed to assist in both understanding and deconstructing the ways in which the pandemic has impacted our lives \u2014 as individuals, families and communities,\" said editor\u00a0Christine L. Holman, senior lecturer, justice and social inquiry.\u201cThe Pandemic Reader\u201d\u00a0draws from research, writings and discussions by journalists, students, community activists and academics who have formed teachable viewpoints on the world\u2019s current state of affairs. The contributors come from a wide range of specialties from economics to pediatrics to epidemiology and investigative reporting.\u00a0The editors of this collection are\u00a0Mako Fitts Ward,\u00a0Jennifer A. Sandlin,\u00a0Michelle McGibbney Vlahoulis\u00a0and\u00a0Holman.\u00a0They helped bolster the sociohistorical framework and evidence-based responses used to address issues such as: pandemic racism, coronavirus capitalism, communications surrounding exposure and protection, pandemic leadership, and social messaging.In addition to offering thought-provoking narratives, \u201cThe Pandemic Reader\u201d\u00a0strives to serve as a blueprint for new teaching strategies as communities relearn how to connect and move forward.Sandlin says the book is \u201can\u00a0attempt to expose the cracks in systems that have become too wide to ignore.\"\"It\u2019s important to provide context to understand how the multiple pandemics of 2020 \u2014 including COVID-19 and dismantling structural racism \u2014 exacerbated vast disparities that have existed and been cultivated for decades and even centuries,\u201d she said.\u00a0This book aims to provide a resource for courses on social justice and introduce critical perspectives for enlightening classroom discussions. After engaging with this material, the intended outcome is that COVID-era inequities are magnified, to inspire new perspectives and necessary change.The book is available in e-book and paperback at\u00a0diopress.com/the-pandemic-reader.]]>", - "post_date": "09/03/2021-4:04pm", - "clas_teaser": "\u201cThe Pandemic Reader\u201d is a new collection of essays edited by faculty in the School of Social Transformation. The collection explores the multitude of ways in which the COVID-19 pandemic has changed life in every aspect.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/pandemic-editors.jpg?itok=mvl-r46_", - "path": "https://news.asu.edu/20210903-essays-explore-altered-social-experiences-covid-19-pandemic", - "hide_byline": "0", - "contributor-contact-information-affiliation": "School of Social Transformation", - "contributor-contact-information-name": "Marjani Hawkins", - "contributor-contact-information-e-mail": "mvhawkin@asu.edu", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/pandemic-editors.jpg?itok=mvl-r46_", - "image_alt": "portrait collage of four women", - "image_caption": "", - "related_story": "", - "news_units": "School of Social Transformation|The College of Liberal Arts and Sciences", - "interests": "Healthy Living|Black / African American|COVID-19 research and resources|Generosity|LGBTQ|Gender|Health|Justice studies|Community involvement|Social Justice|Social science|Academics|Campus life|Nursing and Health Care", - "audiences": "Faculty|Community", - "event_types": "", - "locations": "Downtown Phoenix campus|Tempe campus|West campus|Online", - "content_type": "news", - "field_saf": "Solutions" - } - }, - { - "node": { - "nid": "90164", - "title": "Arizona PBS to air Sandra Day O'Connor documentary Sept. 13", - "body": "Arizona PBS will honor the legacy of Supreme Court Justice Sandra Day O\u2019Connor with the national premiere of\u00a0\u201cSandra Day O\u2019Connor: The First\u201d\u00a0from 8 to 10 p.m. Monday, Sept. 13, on \u201cAmerican Experience\u201d on Channel 8.1.An encore presentation is scheduled for 11 p.m. Sept. 13, with subsequent airings at 2 a.m. Tuesday, Sept. 14, and 2:30 p.m. Wednesday, Sept. 15.\u00a0In addition, Arizona PBS will host a special virtual preview of the documentary for its members at 6:30 p.m. Wednesday, Sept. 8, followed by a panel discussion led by Mi-Ai Parrish, managing director of ASU Media Enterprise and a member of the Sandra Day O\u2019Connor Institute for American Democracy\u2019s board of directors. Panelists Nicole Maroulakos Goodwin, managing shareholder of the Phoenix office of Greenberg Traurig, and Leezie Kim, chief legal officer at Fox Restaurant Concepts, will discuss O\u2019Connor\u2019s legacy on the court and her influence in Arizona.O\u2019Connor was the first woman to be appointed to the U.S. Supreme Court after she was nominated by President Ronald Reagan in 1981. She spent 25 years on the court, emerging as one of the more moderate justices of the time.O\u2019Connor was born in El Paso, Texas, and was raised on a cattle ranch near Duncan, Arizona. She graduated at the top of her class from Stanford Law School but focused on volunteer work and public service after law firms refused to interview her because she was a woman.\u00a0This led to her rise through the ranks of the Arizona Republican party from precinct captain to Arizona\u2019s assistant attorney general to the first female state Senate majority leader in the nation. O\u2019Connor was a state Court of Appeals judge when she was appointed to the U.S. Supreme Court by Reagan, who had made a campaign promise to name a woman to the highest bench. At the time of her appointment, she had never heard a federal case, but was well-regarded and well-connected within the Republican party.\u00a0Her hearings before the Senate Judiciary Committee were the first of any Supreme Court nominee to be broadcast live on television, and tens of millions tuned in. O\u2019Connor\u2019s interrogators attempted, time and again over three days, to probe her about her views on the most controversial issues of the time. Her Senate confirmation was unanimous at 99 votes to 0, and she was officially sworn in on Sept. 25, 1981.During her tenure, O\u2019Connor was frequently viewed as a swing vote due to her moderate stance on various issues, as well as a judge who could negotiate with colleagues on both sides of the aisle. She served as the critical deciding vote on some of the most controversial issues of the 20th century, including those involving race, gender and reproductive rights. O\u2019Connor was the deciding vote in Bush v. Gore in 2000, which gave the presidential election to George W. Bush.O\u2019Connor retired from the U.S. Supreme Court in 2006. This PBS biography, released on the 40th anniversary of her appointment to the highest court, recounts the life of a pioneering woman who both reflected and shaped an era.To confirm your Arizona PBS membership and register for the virtual event, please contact Paulina Sanchez at\u00a0paulina.sanchez@asu.edu.]]>", - "post_date": "09/03/2021-3:53pm", - "clas_teaser": "Arizona PBS will honor the legacy of Supreme Court Justice Sandra Day O\u2019Connor with the national premiere of \u201cSandra Day O\u2019Connor: The First\u201d from 8 to 10 p.m. Monday, Sept. 13, on \u201cAmerican Experience\u201d on Channel 8.1.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/unknown-1_3.jpeg?itok=B0AWvCvn", - "path": "https://news.asu.edu/20210903-arizona-pbs-air-sandra-day-oconnor-documentary-sept-13", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Walter Cronkite School of Journalism and Mass Communication", - "contributor-contact-information-name": "Jamar Younger", - "contributor-contact-information-e-mail": "jryounge@asu.edu", - "contributor-contact-information-campus": "Downtown Phoenix campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/unknown-1_3.jpeg?itok=B0AWvCvn", - "image_alt": "Ruth Bader Ginsburg and Sandra Day O'Connor", - "image_caption": "", - "related_story": "", - "news_units": "KAET Eight, Arizona PBS|Sandra Day O'Connor College of Law|Walter Cronkite School of Journalism and Mass Communication", - "interests": "Journalism|Law", - "audiences": "Campus student|Faculty|Graduate student|Alumni", - "event_types": "", - "locations": "Downtown Phoenix campus", - "content_type": "news", - "field_saf": "Arizona Impact" - } - }, - { - "node": { - "nid": "90161", - "title": "6 new faculty join School of Arts, Media and Engineering", - "body": "ASU School of Arts, Media and Engineering Director Pavan Turaga has set a goal to empower all current and future students in the program with technofluency, a key pillar in the school\u2019s mission statement.\u00a0\u201cWe want students to develop fluency of the tools of technology, how to apply them and ask, 'What are the implications?\u2019\u201d Turaga said. \u201cAll technology tools have certain trade-offs, and it\u2019s become more and more clear, especially in the fields of media and social media, things driven by artificial intelligence, that those methods amplify certain biases that are prevalent in media already. We are creators who make art and media and engaging products, but let\u2019s pay attention to the underlying assumptions that exist.\u201dTo help achieve the school\u2019s new goal, it has welcomed several new faculty, all with diverse, branching disciplines and backgrounds. These new faculty members will not only help arts, media and engineering students grow, but also assist in the growth of Arizona State University\u2019s new Mesa City Center project and programs.\"Among the School of Arts, Media and Engineering new faculty are several colleagues \u2014 Ana Herruzo, Laura Cechanowicz and Nicholas Pilarski \u2014 who will assist us for the ASU at Mesa City Center facility and its programs, which is an exciting beginning,\u201d said Jake Pinholster, associate dean of the Herberger Institute for Design and the Arts. \u201cWe will be searching for a number of additional positions this year, and, by the time the building is complete in 2022, we will have put together a truly world-class team in extended reality (XR) technologies and related fields.\"Meet the new facultyDB Bauer, assistant professor of games and interactive media, School of Arts, Media and Engineering\u00a0Prior to pursuing graduate studies, DB Bauer worked for many years in television, radio and film production with New Mexico PBS, Koahnic Broadcast Corporation and other freelance outlets. Bauer has broad experience in both producing and production, with a primary focus on audio engineering. In addition to media production, Bauer has a background in digital media preservation for university archival collections.Bauer draws on this professional background in both previous and current research and teaching, focusing recently on the theoretical and social impact of new media theory and practice, speculative design, hands-on, critical making, and 3D media \u2014 particularly 3D media fabrication in both physical and virtual space.\u201cFor whatever reason, I can\u2019t help but be drawn to the beeping gadgets and neon lights that transport us to elsewheres and otherwises where things exist beyond, to quote Oscar Wilde, \u2018the sordid perils of actual existence,\u2019\u201d Bauer said. \u201cNot purely escapism \u2014 though certainly escapism surely has its benefits \u2014 digital tools allow us to bring those creative visions into everyday life and reimagine our shared futures.\u201dJoseph Watson, professor of practice, School of Arts, Media and Engineering and senior adviser to the dean, Herberger Institute for Design and the Arts\u00a0Joseph Watson, better known as Joe Watson, will work with arts, media and engineering in a number of ways, including mentoring students and sharing seminars, consulting with faculty on institutional and external strategy, and working with university leadership to elevate the work happening in the school. Watson has had a long career in strategy, business consulting, entrepreneurship and leadership development and has been featured in media outlets including The Today Show, CNN, PBS, The New York Times, The Wall Street Journal, Fast Company and The Chronicle of Philanthropy. Watson has authored books such as \"Without Excuses: Unleash the Power of Diversity to Build Your Business\" and \"Where the Jobs Are Now.\"\u00a0He has been serving as senior adviser to the dean of the Herberger Institute and will continue in that role. He is also a member of The Fast Company Executive Board.\u00a0\u201cI am thrilled to be working with (the school) at the intersection of creativity and engineering \u2014 understanding its unique role in educating the next generation of robust critical thinkers,\" Watson said.Laura Cechanowicz, assistant professor, School of Arts, Media and Engineering and Graphic Information Technology programWith a jointly appointed contract with ASU's graphic information technology program and the School of Arts, Media and Engineering, Laura Cechanowicz will help the school with expanding research strengths in VR/AR/XR tech and its applications. Cechanowicz is an artist, designer and worldbuilder working in research, theory and practice across media, including extended reality and immersive technologies, VR/AR pre-visualization, animation, multimedia installation, narrative and experimental film, and production and sound design.Cechanowicz works across industry and academia, consulting, writing and conducting workshops and talks on the practice of worldbuilding. Their talks and workshops have been hosted at Disney, LASER, VRLA, in conferences and within communities around the world in the United States, Japan, Argentina, Uruguay, Chile, India, Denmark and Brazil. Many of their worldbuilding projects have resulted in virtual reality immersive experiences presented at the Sundance Film Festival and in films, critically acclaimed interactive operas, multimedia collections displayed at the Venice Biennale, design frameworks for augmented reality apps and digital multimedia books. Cechanowicz\u2019s production design talent was recognized at the 2015 Berlinale.\u00a0Luke Kautz, clinical assistant professor of digital fabrication and 3D modeling, School of Arts, Media and EngineeringLuke Kautz is transitioning to faculty as a clinical assistant professor of digital fabrication and 3D modeling, having previously served as the digital design fabrication specialist running the school\u2019s FabLab. Kautz said he believes this direct experience and knowledge as staff support to students and faculty will lead to deeper connections between all involved, helping to lift the academic and research possibilities within the school, at ASU and beyond. His current explorations look at the illusory notion of regenerative design; the cross-pollination of disciplinary techniques for new practices; and the use of design technologies to create mutual generosity between ourselves and our natural and built environments.Kautz is also a senior researcher at the Center for Philosophical Technologies at ASU, having participated in several research and design projects.Before coming to ASU, Kautz worked as a junior designer in the architecture office of Frank Gehry in Los Angeles, and prior to that he was a lecturer in the Knowlton School of Architecture at Ohio State University.Nicholas Pilarski, associate professor, School of Arts, Media and Engineering and The Sidney Poitier New American Film School\u00a0Nicholas Pilarski is an award-winning filmmaker with a background in immersive media, music, film, theater and community work. He creates interactive and emerging media stories that attempt to address issues related to historicized poverty and class-based trauma. His art is not \u201cabout\u201d communities, but rather \u201cof\u201d them \u2014 helping build ecosystems where traditional lines between subject and author, teacher and student, and spectator and producer are intentionally contested and reimagined.Pilarski co-founded and co-directs Peoples Culture, an arts collective in Chicago and New York working to reimagine shared narratives through collaborative art-making practices, whose work has been archived in the permanent collection of The Smithsonian: National Museum of African American History and Culture. His work has appeared in The New York Times, the Museum of Modern Art, Eye Institute Netherlands, U.S. Departments of Interior and Education and the Chicago International Film Festival. Pilarski holds degrees from the University of Michigan and Duke University, and his research interests include XR and computational media, film and media theory, class, geographic conflict, dialectics, utopianism, phenomenology and cognitive neuroscience.\u00a0Ana Herruzo, associate professor, School of Arts, Media and Engineering and The Design SchoolAna Herruzo holds a master's degree in design research in media-art from SCI-arc, a bachelor's degree and master's degree in architecture from ETSAM (Madrid, Spain) and is currently finishing her PhD at ETSAM, as part of the architectural communication doctoral program. Her background is in architecture, experience design and real-time interactive media. Her main focus is on incorporating lighting, audio and video with physical environments, materials and human interaction.\u00a0Herruzo has served as chair of the Applied Computer Science Department at Woodbury and won multiple industry awards for her wide-ranging work in installation, live events, film, robotics and architecture. She hopes to help the school grow its strengths in experience design as part of the Mesa programs.\u00a0As a hobby, she said she enjoys creating experimental audiovisual performances and plays in an electronic music duo called IvanaBytes. She is also the co-founder of an art collective called NaiveLaser and an interactive agency, VIRTULABS.\u00a0]]>", - "post_date": "09/03/2021-3:46pm", - "clas_teaser": "ASU's School of Arts, Media and Engineering has welcomed several new faculty, all with diverse, branching disciplines and backgrounds.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/6faculty-new.jpg?itok=zpnBELuQ", - "path": "https://news.asu.edu/20210903-6-new-faculty-join-school-arts-media-and-engineering", - "hide_byline": "0", - "contributor-contact-information-affiliation": "School of Arts, Media and Engineering", - "contributor-contact-information-name": "Megan Patzem", - "contributor-contact-information-e-mail": "mpatzem@asu.edu", - "contributor-contact-information-phone_number": "480-727-2904", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/6faculty-new.jpg?itok=zpnBELuQ", - "image_alt": "Profile shot of six new faculty. DB Bauer, Joseph Watson, Luke Kautz, Nicholas Pilarski, Ana Herruzo", - "image_caption": "", - "related_story": "", - "news_units": "School of Arts, Media and Engineering|The Design School|Sidney Poitier New American Film School|Ira A. Fulton Schools of Engineering|Herberger Institute for Design and the Arts", - "interests": "Arts|Design|Film|Engineering|Arts and Entertainment", - "audiences": "Faculty", - "event_types": "", - "locations": "", - "feed_herberger": "School of Arts, Media + Engineering, Herberger Institute for Design and the Arts", - "content_type": "news", - "field_saf": "University News" - } - }, - { - "node": { - "nid": "90160", - "title": "ASU alum receives prestigious NIH early-career award", - "body": "Arizona State University alumnus Chris Gisriel, currently a postdoctoral associate in the Department of Chemistry at Yale University, was recently awarded a Pathway to Independence Award by the National Institutes of Health.These awards are given to postdoctoral scientists with promising careers to support independent research while receiving mentoring. The mentoring phase lasts for one to two years, followed by up to three years of independent research in professorship, for a total of up to five years of support. The goal of these awards is to facilitate the transition of postdoctoral scientists into independent, tenure-track (or equivalent) faculty positions.Gisriel began his undergraduate studies in 2009 in ASU's School of Molecular Sciences. After earning his bachelor\u2019s degree in 2013, he continued his studies in the school, earning his PhD in 2017 under Professor Kevin Redding. Following this, Gisriel was a postdoctoral fellow in Professor Petra Fromme\u2019s lab until 2019.\u201cChris started out in our group as an undergrad who really wasn\u2019t too sure about what he wanted to do with his life, but he fairly quickly matured into a serious researcher, \" Redding said. \"What I remember most strongly about Chris is his fearlessness \u2014 he took on some serious challenges, but never let the difficulties deter him. He just figured out what needed to be done, and then did whatever was necessary to solve the problem. One of the greatest joys of my career was watching him become the scientist he is today. This award is well deserved, and I have no doubt that he will continue to experience great success in his future career.\u201dHere, Gisriel reflects on this experience at ASU and the training he received in the School of Molecular Sciences.Question: How have your experiences at ASU in the School of Molecular Sciences and the Biodesign Institute prepared you for future success?Answer: The research faculty and culture at SMS and Biodesign shaped my personal identity as a researcher, which continues to aid in my endeavors today. Together, they have created an environment that leads my field of interest, photosynthesis research. My first glimpse into this community atmosphere was in SMS \u2014 at that time called the Department of Chemistry \u2014 as an undergraduate researcher in the Redding Lab. The lab member\u2019s passion for research was magnetizing. As I became aware of the broader scope of my research and the people who contributed the most in my field, it became clear that many of the biggest contributors were my colleagues, advisers and mentors at ASU.Q: What skills did you learn that you continue to utilize?A: As a person whose career goals entail maintaining well-funded academic research, an important aspect of my scientific upbringing was becoming familiar with the paths that others had taken to achieve success. I found abundant experience with this at SMS and Biodesign. During my graduate experience, I was given the opportunity to participate in writing scientific manuscripts and grant applications, I learned what career strategies would enhance my visibility in a competitive field, and I was immersed in my field of research by attending conferences with my lab members. All of these have been instrumental in my success today.Q: What do you find most memorable about your time at ASU?A: What stands out to me is that the community aspect of the research at ASU is very important. More than other universities in my experience so far, my research ideas and goals were encouraged, even at the lowest levels, by giants in my field of study. As long as I approached my goals with enthusiasm and dedication, my success was thoughtfully cultivated by SMS and Biodesign faculty. Namely, Kevin Redding, Petra Fromme, Neal Woodbury, Tom Moore and Bob Blankenship \u2014 although Bob did not come back to ASU until the end of my time there \u2014 have shaped how I act as a scientist. It is because of their mentorship and guidance, along with the examples they set, that I enjoy success today. I appreciate their investment in me, and I am already in the process of investing in students around me.]]>", - "post_date": "09/03/2021-3:19pm", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/20210901_154444.jpg?itok=K8MopYtY", - "path": "https://news.asu.edu/20210903-asu-alum-receives-prestigious-nih-early-career-award", - "hide_byline": "0", - "contributor-contact-information-affiliation": "School of Molecular Sciences", - "contributor-contact-information-name": "James Klemaszewski", - "contributor-contact-information-e-mail": "jklem@asu.edu", - "contributor-contact-information-phone_number": "480-965-2729", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/20210901_154444.jpg?itok=K8MopYtY", - "image_alt": "Chris Gisriel", - "image_caption": "", - "related_story": "", - "news_units": "School of Molecular Sciences|Biodesign Institute|The College of Liberal Arts and Sciences", - "interests": "Grants / Awards|Academics|Career development", - "audiences": "Degreed alum|Faculty|Graduate student|Alumni", - "event_types": "", - "locations": "Tempe campus", - "content_type": "news", - "field_saf": "University News" - } - }, - { - "node": { - "nid": "90157", - "title": "ASU researchers bring a new twist to 2D magnets", - "body": "The discovery of graphene \u2014 a single layer of graphite, also known as charcoal \u2014 revolutionized our understanding of low-dimensional materials and unraveled their potential for applications in quantum technologies.We now have a library of 2D materials with outstanding capabilities. Thanks to their weak chemical bonding, it is also possible to combine different types of 2D materials \u2014 like 2D Lego bricks \u2014 to engineer unique properties.One of the new editions to the catalog of properties available in 2D materials is magnetism: Utilizing and manipulating magnetism is crucial for quantum applications.A team of researchers from ASU shows in a recent manuscript published in Nano Letters, titled \"Moir\u00e9 Skyrmions and Chiral Magnetic Phases in Twisted CrX3 (X = I,Br, and Cl) Bilayers,\" that twisting two-dimensional magnets such as Cr-trihallides can lead to new magnetic phenomena such as skyrmions. Skyrmions are hedgehog-like arrays of magnetic moments with potential applications in memory devices.\u201cTwisting two-dimensional materials lead to large-scale structures called moir\u00e9 patterns. Moir\u00e9 patterns can significantly modify the properties of materials and give rise to new emergent phases,\u201d said Antia Botana, an assistant professor in the Department of Physics. \u201cSkyrmions have been sought after for a long time in 2D magnets. We are excited to show that Cr-based trihalides can be platform to realize them.\u201d\u201cEmploying complementary approaches is crucial to address complex phenomena in varying length scales,\u201d said Onur Erten, an assistant professor in the Department of Physics. \u201cIt would not be possible to conduct this project without the combined expertise of both groups.\u201dBotana and Erten emphasize the key role of the graduate students in this project. This work is supported by National Science Foundation, Division of Materials Research Award No. DMR 1904716.]]>", - "post_date": "09/03/2021-1:38pm", - "clas_teaser": "A team of researchers from ASU shows in a recent manuscript published in Nano Letters that twisting two-dimensional magnets can lead to new magnetic phenomena.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/screenshot_20_0.png?itok=IkNX7QKV", - "path": "https://news.asu.edu/20210903-asu-researchers-bring-new-twist-2d-magnets", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Department of Physics", - "contributor-contact-information-name": "Kiersten Moss", - "contributor-contact-information-e-mail": "ktmoss1@asu.edu", - "contributor-contact-information-phone_number": "480-815-0891", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/screenshot_20_0.png?itok=IkNX7QKV", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Department of Physics|The College of Liberal Arts and Sciences", - "interests": "Science|Research", - "audiences": "Faculty", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Discoveries" - } - }, - { - "node": { - "nid": "90153", - "title": "New research advances clean energy solutions", - "body": "Meeting society\u2019s growing energy needs has become a daunting challenge for humanity. Demands for energy are expected to nearly double by the year 2050, while the effects of climate change, caused by the burning of fossil fuels, are already wreaking havoc in the form of droughts, wildfires, floods and other disasters.Gary Moore, a researcher at Arizona State University's\u00a0Biodesign Center for Applied Structural Discovery, thinks chemistry will play a vital role in the development of clean solutions to the world\u2019s mounting energy dilemma.In new research appearing on the cover of the journal\u00a0ChemElectroChem, Moore and his colleagues describe the use of ring-shaped molecules known as porphyrins. Such molecules, among the most abundant pigments in nature, are noted for their ability to speed up or catalyze chemical reactions, including important reactions occurring in living systems.Among these reactions is the conversion of radiant energy from the sun into chemical energy stored in molecular bonds, a process exploited by plants and photosynthetic microbes. This chemical energy can then be used to fuel the organism\u2019s metabolism, through the process of cellular respiration.Researchers like Moore hope to take a page from nature\u2019s playbook, creating synthetic analogs to natural processes of photosynthesis. The new study describes a synthetic diiron-containing porphyrin and explores its potential as an effective catalyst.Gary Moore is is a researcher at the Biodesign Center for Applied Structural Discovery. Photo by Biodesign Institute\u201cRather than exploiting the products of natural photosynthesis, we can be inspired by our knowledge of photosynthesis to pioneer new materials and technologies with properties and capabilities rivalling those of their biological counterparts,\u201d Moore said.Porphyrins, and their structurally related analogs, are found in abundance across the biological world. They act to bind a range of metal ions to perform far-flung cellular tasks. Chlorophyll molecules, for example, bind magnesium (a crucial chemical stage in plant photosynthesis), while heme \u2013 an iron-containing porphyrin \u2014 helps organize molecular oxygen and carbon-dioxide transport and provides the necessary electron-transport chains essential for cellular respiration. Because of their commanding role in life processes, porphyrin abnormalities are responsible for a range of serious diseases.Porphyrins can also be used as catalysts in synthetic devices known as electrochemical cells, which convert chemical energy into electrical energy, or vice versa. Although radiant energy from the sun may be stored within conventional types of batteries, such applications are limited by their low-energy densities compared with fuels used for modern transportation.Moore\u2019s efforts to design artificial photosynthetic systems could provide a valuable piece of the renewable energy puzzle, producing \u201cnon-fossil-based\u201d fuels as well as a range of beneficial commodities.Such devices would allow the capture and storage of solar energy for use when and where it is needed and can be constructed using chemicals that are far cheaper and more abundant than the materials currently in use for conventional solar energy applications.The paper has been selected for the cover of the current issue of the journal, with a descriptive graphic produced by\u00a0Jason Drees, multimedia developer lead at ASU, and is part of a special collection dedicated to Professor Jean-Michel Sav\u00e9ant.]]>", - "post_date": "09/03/2021-11:30am", - "clas_teaser": "In new research appearing on the cover of the journal ChemElectroChem, Moore and his colleagues describe the use of ring-shaped molecules known as porphyrins. Such molecules, among the most abundant pigments in nature, are noted for their ability to speed up or catalyze chemical reactions, including important reactions occurring in living systems.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/screen_shot_2021-06-10_at_5.48.36_pm.png?itok=iAGu4yxI", - "path": "https://news.asu.edu/20210903-new-research-advances-clean-energy-solutions", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Biodesign Institute at ASU", - "contributor-contact-information-name": "Richard Harth", - "contributor-contact-information-e-mail": "Richard.Harth@asu.edu", - "contributor-contact-information-phone_number": "480-727-0378", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/screen_shot_2021-06-10_at_5.48.36_pm.png?itok=iAGu4yxI", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "School of Molecular Sciences|Biodesign Center for Applied Structural Discovery", - "interests": "Science|Sustainability", - "audiences": "Faculty", - "event_types": "", - "locations": "Tempe campus", - "content_type": "news", - "field_saf": "Discoveries" - } - }, - { - "node": { - "nid": "90148", - "title": "ASU Law appoints alum to lead Indian gaming and tribal self-governance programs", - "body": "Derrick Beetso, a 2010 alumnus of the Sandra Day O\u2019Connor College of Law at Arizona State University, has been named to take the helm of the college\u2019s Indian gaming and tribal self-governance programs.Beetso, a citizen of the Navajo Nation, earned his JD and certificate in Indian law as part of ASU Law\u2019s nationally recognized Indian Legal Program. As director of the Indian gaming and tribal self-governance programs, he builds on the earlier leadership and work of ASU Law professors Lawrence Roberts and Ann Marie Bledsoe Downes, who helped launch the programs in 2019. Both recently went on leave from the college to serve in President Joe Biden\u2019s administration.Prior to joining ASU Law, Beetso served as the general counsel for the National Congress of American Indians, the first and only Native person to ever serve in this capacity for the organization. While there, he handled in-house legal needs and co-managed the Tribal Supreme Court Project with the Native American Rights Fund.Beetso also served as an attorney-adviser within the Office of the Solicitor\u2019s Phoenix Field Office, where he provided legal services for the Western Region of the Bureau of Indian Affairs and the San Carlos Irrigation Project. Previously, he served as counselor to Assistant Secretary-Indian Affairs Kevin Washburn during President Barack Obama\u2019s administration.With his experience in Washington, D.C., and ASU Law\u2019s growing presence there, Beetso will help to expand educational and career opportunities for Indian Legal Program students in the nation\u2019s capital.He will teach one of the program\u2019s popular traveling classes this fall, allowing ASU Law students to study on the Washington, D.C., campus. The class, Federal Advocacy for the Tribal Client, is a unique experience that introduces students to decisionmakers and facilitates networking with future employers, while providing students time to explore opportunities in the city.\u201cWe are excited to welcome one of our distinguished alums back to campus,\u201d said Patty Ferguson Bohnee, faculty director for the Indian Legal Program and director of the Indian Legal Clinic. \u201cDerrick Beetso has a deep understanding of Indian Affairs and has committed his career to advancing the rights of tribal nations. His experience working for tribes and the government, as well as his success in developing effective partnerships make him a perfect fit for our ILP team.\u201dBeetso said he is honored to return to ASU Law as faculty.\u201cThe great work the ILP has done since its inception at ASU Law has furthered the life goals of so many talented Native and non-Native professionals and has helped ensure tribal nations receive excellent representation in the legal field,\u201d he said. \u201cI am humbled to be able to share my experience, and I look forward to contributing to the ILP\u2019s mission as best I can.\u201dThe first of their kind in the nation, the\u00a0Indian gaming and tribal self-governance programs\u00a0were established thanks to an initial contribution generously given by the San Manuel Band of Mission Indians to further ASU Law\u2019s commitment to serving the educational needs of tribal nations. With the Indian gaming emphasis, ASU Law is providing in-depth courses for students on the regulation, compliance and implementation of Indian gaming. ASU Law\u2019s\u00a0Master of Legal Studies\u00a0(MLS) degree will equip professionals with the essential legal components of Indian gaming to excel in careers that intersect with Indian gaming.Similarly, the self-governance emphasis is designed to educate students, whether they desire to work for tribes, the federal government or states, in really understanding the legal framework of the tribal-federal relationship, the federal programs and statutes that promote tribal self-governance and the implementation of that legal framework.The support from the San Manuel Band of Mission Indians will help increase educational opportunities in the field of Indian law and the expansion of work experiences for students interested in Indian law, and it will continue to contribute to the development of Indian law trainings for Indian tribes and organizations. To learn more about contributing to the programs, contact\u00a0Hallie.Rexer@asu.edu.]]>", - "post_date": "09/03/2021-11:00am", - "clas_teaser": "Derrick Beetso, a 2010 alumnus of the Sandra Day O\u2019Connor College of Law at Arizona State University, has been named to take the helm of the college\u2019s Indian gaming and tribal self-governance programs.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/derrick_beetso_2021_asu_news.jpg?itok=0GFa1-f_", - "path": "https://news.asu.edu/20210903-asu-law-appoints-alum-lead-indian-gaming-and-tribal-self-governance-programs", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Sandra Day O'Connor College of Law", - "contributor-contact-information-name": "Julie Tenney", - "contributor-contact-information-e-mail": "julie.tenney@asu.edu", - "contributor-contact-information-campus": "Downtown Phoenix campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/derrick_beetso_2021_asu_news.jpg?itok=0GFa1-f_", - "image_alt": "Photo of Derrick Beetso, new director of ASU Law Indian Gaming and Tribal Self-Governance programs", - "image_caption": "", - "related_story": "", - "news_units": "Sandra Day O'Connor College of Law", - "interests": "Law|Native American", - "audiences": "Staff|Faculty|Graduate student|Alumni", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "University News" - } - }, - { - "node": { - "nid": "90144", - "title": "ASU alumnus returns home as a leader in the production of 'Hamilton' at ASU Gammage", - "body": "Arizona State University alumnus and Tempe, Arizona,\u00a0native Patrick Fanning\u00a0will return\u00a0home to\u202fASU Gammage as music director and conductor for\u00a0a national tour\u00a0of\u00a0the\u00a0world-renowned musical and Broadway hit\u00a0\u201cHamilton.\u201d\u202f\u00a0With a degree in collaborative piano from\u00a0the\u00a0Herberger Institute for Design and the Arts'\u00a0School of Music, Dance, and Theatre, Fanning will enter the doors of ASU Gammage for the first time since his graduation in 2008.\u00a0\u201cIt\u2019s a little surreal,\u201d\u00a0Fanning\u00a0said\u00a0when discussing his career and life on tour.\u202f\u00a0Since middle school, Fanning knew he wanted to pursue a career in music. And he said his time at ASU helped to refine his natural abilities.\u202f\u00a0\u201cI got a really good education here,\u201d Fanning said.\u00a0\u201cI had very good teachers in the School of Music, and I developed a strong technique in my piano playing through my private studies. The academic rigor of the music helped me in ways that I didn\u2019t realize at the time would be super beneficial.\u201d\u202f\u00a0Fanning's education and experiences aided him as he dove into the role of music director and conductor for\u00a0a\u00a0touring cast of \"Hamilton,\" just three months prior to the start of the pandemic.\u202f\u00a0\u201cI\u2019ve been with the company ('Hamilton') since September 2018, but in an associate conductor role. That really prepared me for the nuts-and-bolts\u00a0part of this job, but being a leader is a little different\u201d Fanning\u00a0said.\u202f\u00a0\"Hamilton\" is a\u00a0one-of-a-kind\u00a0musical, not only because of the show\u2019s immense popularity, but also due\u00a0to\u00a0its unique style of music with a mixture of rap and powerful ballads.\u202f\u00a0\u201cAs conductors and pianists on the show, we have to be able to rap, sing and play the entire thing ourselves; that's very different from anything I have ever done,\u201d Fanning said.\u202f\u00a0But unfortunately, just as Fanning began to conquer these new responsibilities,\u202fCOVID-19 brought the world of theater to a sudden halt.\u202fAlthough\u00a0it was tough to be away from the stage, Fanning says that the production has made performing again a smooth and enjoyable experience.\u202f\u00a0\u201cThe producers and the company have taken a lot of time to make sure that we are being integrated back into the world of professional life,\u201d Fanning said. \u201cThey are very attentive to our needs both physically and mentally.\u201d\u00a0With their safety and well-being looked after, Fanning and the rest of the \"Hamilton\" cast are ready to breathe life back into the ASU Gammage stage.\u202f\u00a0While the world is very different since the last time Fanning performed at Gammage, he wants ASU students to know that a successful future awaits them.\u202f\u00a0\u201cPut\u00a0yourself out there, don\u2019t take yourself too seriously, be kind to other people and take every opportunity you can to grow,\u201d\u202fFanning said.\u00a0\"Hamilton\" returns to\u00a0ASU\u00a0Gammage\u00a0Sept.\u00a08\u2013Oct. 10. For tickets, go to\u00a0asugammage.com.\u00a0]]>", - "post_date": "09/03/2021-10:36am", - "clas_teaser": "Arizona State University alumnus and Tempe, Arizona, native Patrick Fanning will return home to\u202fASU Gammage as music director and conductor for a national tour of the world-renowned musical and Broadway hit \u201cHamilton.\u201d\u202f", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/fanning_story_0.jpg?itok=Ci56FC2e", - "path": "https://news.asu.edu/20210903-asu-alumnus-returns-home-leader-production-hamilton-asu-gammage", - "hide_byline": "0", - "contributor-contact-information-affiliation": "ASU Gammage", - "contributor-contact-information-name": "Arianna Reyna", - "contributor-contact-information-e-mail": "amreyna6@asu.edu", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/fanning_story_0.jpg?itok=Ci56FC2e", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "School of Music, Dance and Theatre|Herberger Institute for Design and the Arts|ASU Gammage", - "interests": "Music|Theater|Arts and Entertainment", - "audiences": "Staff|Campus student|Graduate student|Gammage|Alumni|Student", - "event_types": "", - "locations": "Tempe campus", - "feed_herberger": "Herberger Institute for Design and the Arts, School of Music, Dance and Theatre", - "content_type": "news", - "field_saf": "Sun Devil Life" - } - }, - { - "node": { - "nid": "90175", - "title": "Isanovic Stellar In Doubleheader Split", - "body": "\nSun Devil Volleyball split five-set matches, falling to Georgia before beating Nebraska Omaha.\n]]>", - "post_date": "09/03/2021-9:57am", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210903-isanovic-stellar-doubleheader-split", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/3/HCK_6950.jpg", - "image_url": "https://thesundevils.com/images/2021/9/3/HCK_6950.jpg", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90156", - "title": "Getting a greener grid", - "body": "As Congress advances legislation to invest $550 billion in new funding for national infrastructure projects, the specific priorities in each industry category remain undefined. For example, the proposed framework directs $65 billion to foster greater use of renewable energy sources within America\u2019s power grid, but it doesn\u2019t exactly define how that will happen.\u201cWind is certainly well-positioned for expansion,\u201d said\u00a0Ronald Calhoun, an associate professor of civil and environmental engineering in the\u00a0Ira A. Fulton Schools of Engineering\u00a0at Arizona State University. \u201cThe technology is mature and reliable. Plus, it\u2019s highly cost effective. Actually, wind is market competitive even without financial incentives. So, there are good reasons for its further development.\u201dAccording to the\u00a0U.S. Energy Information Administration, renewable energy sources now represent 20% of the electricity generated in America. Within that sector, wind is the most significant source (8.4%), followed by hydropower (7.3%) and solar (2.3%). Additionally, wind energy continues to grow more substantially than any other renewable. Its share of that category is expected to exceed 10% this year as new facilities enter operation.\u201cWhen we talk about new wind energy sites, the Great Plains are considered the richest resource area in the United States,\u201d said Calhoun, who is director of the\u00a0Environmental Remote Sensing Group, which operates within the\u00a0School for Engineering of Matter, Transport and Energy, one of the seven Fulton Schools. His team conducts atmospheric research and technology development to advance wind energy industry systems.\u201cThe region is ideal in that it has excellent wind and also is not very populated,\u201d he said. \u201cBut whatever you build in those remote spaces needs to be connected to the grid. And that route to market is a big issue.\u201dHigh-voltage lines cross cities, counties and states, and there is no single federal authority to secure the necessary permissions to build new ones. Additionally, they cost a lot of money. To address these hurdles, the infrastructure framework now before Congress includes establishment of a new Grid Development Authority to finance and facilitate new transmission lines.Laying this literal groundwork is crucial to building new wind energy sites. But it\u2019s also important for expanding the number of commercial solar energy facilities, since solar farms operate in similarly isolated regions. For example, the Agua Caliente Solar Project outside Yuma, Arizona, and the Solana Generating Station near Gila Bend, Arizona, cover more than 4,000 acres in the open desert.\u201cInvesting in the physical grid is probably one of the most important things we can do to support burgeoning renewable energy fields such as wind and solar,\u201d Calhoun said. \u201cThe other challenge is figuring out the intermittency issue. How do we tackle the times when the wind doesn\u2019t blow and the sun doesn\u2019t shine?\u201dGigawatts through a pipeMuch attention has turned to advancing battery technology as a means to balance out the fluctuating nature of power generated by wind and solar sites. But many experts say batteries are too expensive to encourage more commercial-scale renewable energy generation.\u201cBatteries are great for short cycles of loading and unloading. They can work for storing grid energy from morning to evening. But their capital cost is too large to sit on electricity for six months,\u201d says\u00a0Klaus Lackner, a professor of environmental engineering in the\u00a0School of Sustainable Engineering and the Built Environment, also one of the Fulton Schools at ASU.By contrast, Lackner says the cost of storing and moving energy in liquid form is negligible. Think of all the energy on tap at gas stations across the country. Also consider that a single gallon of gasoline represents the daily kilowatt usage of a typical American household.\u201cThe power we can run through pipelines dwarfs what we can get through transmission lines,\u201d Lackner said. \u201cWe can move gigawatts through a single pipe.\u201dBut how do we practically transform energy generated by wind and solar farms into liquid fuel? The answer could be the technology of capturing carbon from the air.Lackner is director of the\u00a0Center for Negative Carbon Emissions, which researches technology to capture carbon dioxide from the atmosphere to both combat the adverse effects of climate change and help advance sustainable energy infrastructure.Central to their work is the development of a \u201cmechanical tree\u201d system that harvests CO2\u00a0from the air. While still at prototype stage, application of the new technology includes concentrating the carbon dioxide for commercial use in carbonating beverages, filling fire extinguishers and making dry ice.\u201cWe also can combine that CO2\u00a0with hydrogen produced through the electrolysis of water using renewable energy like solar. And through that combination, we can create gasoline or diesel or jet fuel,\u201d Lackner said. \u201cThe necessary technology already exists, but it needs to get cheaper through a little more innovation and expanding scale. My prediction is that it will happen in the next five years.\u201dFuel literally created from the air could offer the storage medium needed to solve the intermittency issue impeding greater adoption of wind and solar energy generation technology. It could also attenuate fossil fuel extraction and processing for transportation. If carbon from the atmosphere can power our planes, trains and automobiles, there may be no need to drill into the earth for more petroleum.This innovation seems like a pivotal opportunity to arrest the greenhouse gas accumulation driving climate change; and carbon capture is explicitly listed as a new technology priority in the infrastructure framework before Congress.But Lackner and colleagues at the Fulton Schools point out that averting greater ecological adversity requires more than recycling atmospheric carbon. Additional volumes need to be prevented entirely, and that likely means electrifying transportation.Gorilla in the roomAccording to the\u00a0U.S. Environmental Protection Agency, a quarter of all greenhouse gas emissions in America come from electricity generation. But transportation accounts for an even larger share of the problem: 29%.To reduce those gases, the bipartisan bill in Congress also dedicates nearly $6 billion to replace aging public transit system buses with zero-emission vehicles. Additionally, $7.5 billion is earmarked to start a national network of vehicle-charging stations to accelerate the adoption of electric cars.\u201cIt\u2019s a logical step if we really want to cut emissions,\u201d said\u00a0Vijay Vittal, a Regents Professor of electrical engineering in the\u00a0School of Electrical, Computer and Energy Engineering, also one of the Fulton Schools. \u201cBut if everybody buys an electric car, the added loads on our distribution grid suddenly become very large. How are we going to support those additions? This question is sort of an 800-pound gorilla in the room.\u201dVittal explains that the padmount transformers used in most residential neighborhoods are typically designed to supply four or five households. There are millions of these units across the country, but they were designed and deployed at a time when demand for energy was lower. So, if droves of people start driving electric vehicles, our current power distribution systems won\u2019t be able to handle charging them all after work each day.Consequently, there is an acute need for urban and suburban grid modernization to support consumers and commerce; and it parallels the need for added high-voltage transmission infrastructure to support wind and solar farms in remote corners of America. Both are long-overdue investments in the capacity and resilience of a system that is vital to the operation of society.\u201cFortunately, this is not a technology issue,\u201d said Vittal, who is a former director of the National Science Foundation\u2019s\u00a0Power Systems Engineering Research Center, a consortium of universities and industry focused on the future of electrical energy infrastructure. \u201cAt a local level, our utilities just need to pull out those old transformers put in new ones. Of course, there is significant cost to work at this scale. But the cost of not doing it may be even greater.\u201dTop photo:\u00a0Renewable energy generation technologies are capable of supplying a greater share of national power demand. But making that happen requires investment in fundamental transmission and distribution elements of the electricity grid. Image courtesy of Shutterstock]]>", - "post_date": "09/03/2021-9:32am", - "clas_teaser": "ASU engineering experts point to priorities for infrastructure investment that will support the expanded use of renewable energy sources.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/solar-panel-clouds-sky-windmill-shutterstock_91355138-1920x1080-72dpi_0.jpg?itok=GDnX7Q4u", - "path": "https://news.asu.edu/20210903-solutions-getting-greener-grid-asu-engineering-experts", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Ira A. Fulton Schools of Engineering", - "contributor-contact-information-name": "Gary Werner", - "contributor-contact-information-e-mail": "gewerner@asu.edu", - "contributor-contact-information-phone_number": "480-727-5622", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/solar-panel-clouds-sky-windmill-shutterstock_91355138-1920x1080-72dpi_0.jpg?itok=GDnX7Q4u", - "image_alt": "Wind energy turbines and solar energy panels", - "image_caption": "", - "related_story": "", - "news_units": "Ira A. Fulton Schools of Engineering", - "interests": "Engineering|Renewable energy|Politics", - "audiences": "Faculty|Corporation|Policymakers", - "event_types": "", - "locations": "", - "content_type": "asu_news_article", - "field_saf": "Solutions" - } - }, - { - "node": { - "nid": "90173", - "title": "Triathlon Set to Compete in First Race Since 2019", - "body": "\nSun Devil Triathlon is headed to Wisconsin for the Pleasant Prairie National Qualifier.\n]]>", - "post_date": "09/03/2021-5:08am", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210903-triathlon-set-compete-first-race-2019", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/3/tri_sda_3272.jpg", - "image_url": "https://thesundevils.com/images/2021/9/3/tri_sda_3272.jpg", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90162", - "title": "New ASU faculty will use their interdisciplinary research to build better futures", - "body": "This semester, five new faculty members are joining the College of Global Futures.Their unique and diverse backgrounds in sustainability, technology, innovation, education and policy will contribute to the college's vision of creating a sustainable, equitable and vibrant future for everyone on a thriving, healthy planet.Learn more about the new faculty members.Eusebio ScornavaccaProfessor in the School for the Future of Innovation in Society in the College of Global Futures, and the Thunderbird School of Global ManagementTo say that\u00a0Eusebio Scornavacca\u00a0has a diverse background is almost an understatement. He speaks seven languages, has been a visiting professor in 10 different countries and has a strong research collaboration network across six continents. He has a deep and diverse cultural appreciation that allows him to understand and collaborate with people all around the world.Scornavacca joins ASU from the University of Baltimore, where he was the Parsons Professor of Digital Innovation and director of the Center for Digital Communication, Commerce and Culture. His research focuses on the intersection of disruptive digital innovation with socioeconomic and environmental impact.\"I look at the development of digital innovation and how it can produce a smart, equitable and sustainable future,\" Scornavacca said. \"How can we use digital technologies to help reduce our carbon footprint or create more inclusive financial systems? How can we leverage the capabilities of the digital ecosystem to foster entrepreneurship that creates positive socio-economic and environmental outcomes?\"Scornavacca believes that while digital innovations are disruptive and pose risks, it also has immense potential to do some good.\"Technological development and technological innovation need to be looked at with a balance between the positive and negative impacts and always in light of the context that is being used,\" Scornavacca said. \"How can technology, instead of furthering inequalities, actually help to create a better world for everyone?\"Beza MeridAssistant professor in the School for the Future of Innovation in Society\u00a0in the College of Global FuturesWhen discussing illness and disease, stand-up comedy isn't the traditional venue. But the contradiction between comedy and tragedy can actually lead to some intimate conversations. The unlikely pair has become one of the topics of\u00a0Beza Merid's research. He studies people's experiences of illness and disease, and the types of social and cultural elements that can shape those experiences. He has found that stand-up comedy can enable people to have more difficult and often taboo conversations, and even creates a shared experience.\"Stand-up comedy operates as a place where people have conversations that bend the rules of society,\" Merid said. \"It enables us to explore and confront these difficult topics and not think of them as isolated, personalized experiences that we have to deal with on our own. Stand-up comedy can create a collective understanding that shows we're all struggling with these kinds of things. It's something we can all relate to.\"Merid joins ASU from the University of Michigan Medical School, where he also studied the technologies and innovation surrounding health and illness. Part of his research looked at whether mobile apps could be used to encourage people with high blood pressure to eat healthier and exercise more. But not everyone has access to the same resources to make the technology most effective. The technology only works if everyone has access to the same resources.\"We can design technologies, but we can't really innovate our way out of these structural problems,\" Merid said. \"One of the important things for us to do when we're thinking about the relationship between health and technology innovation is to think about the broader political and social systems that create these problems to begin with. By bringing all those things together, we have a better shot of collectivizing our solutions.\"Janna Goebel\u00a0Assistant professor in the School of Sustainability\u00a0in the College of Global FuturesJanna Goebel\u00a0is no stranger to ASU. She recently completed her PhD in educational policy and evaluation at the Mary Lou Fulton Teachers College. But her focus on sustainability is relatively new. While earning her PhD, she learned more about the sustainability work happening at ASU and started engaging with the researchers behind it. That urge to study sustainability and our interconnectedness with nature took her to Brazil for her dissertation. She studied how education can be conceptualized beyond the current approach that puts humans at the center of everything.\"I'm interested in how we can reconfigure our relationships with the Earth,\" Goebel said. \"I study how education can influence and change our perceptions so that we can see beyond our view of human exceptionalism.\"Sustainability education is more than just incorporating Sustainable Development Goals into the curriculum. Goebel's research also looks at environmental justice and how education is part of the problem and solution to the sustainability challenges we're facing.\"The power dynamics of the world create sustainability problems,\" Goebel said. \"We know that the people who suffer most from climate change do not contribute most to its causes. We have to look at how education is part of that system of oppression, but also liberation. We have to see how sustainability education can be incorporated across all different discipline areas so that we can start to chip away at some of these very persistent status quo power dynamics that are causing some to suffer much more than others.\"Ding FeiClinical assistant professor in the School of Sustainability\u00a0in the College of Global FuturesA conversation with road builders in western China helped shape\u00a0Ding Fei's research. While doing international fieldwork for her master's degree in geography, she spoke with a team of workers and learned they were headed to East Africa for their next project. She wanted to learn more about China's global presence and why these construction workers would travel to another country to work, so she followed them to East Africa to observe their operations.\"There is a big debate about China,\" Fei said. \"Some people say China is a distinctive development partner; they have good foreign policies, are very generous in providing loans and investment, and will contribute to sustainable development in Africa. But others say China is a neocolonial power and is using elite diplomacy or resource diplomacy to perpetuate African underdevelopment.\"While conducting her research in East Africa, Fei felt that China did not give a very clear picture of what it was doing overseas or how its activities influenced development in other countries. She wants to bring a more critical analysis of China's overseas activities to better understand the nation's impact and the implications of sustainable development in the Global South.\"When we talk about China or any emerging power, we often use very nationalistic terms to address it as a whole, coherent actor while operating abroad,\" Fei said. \"But in fact, there are so many institutions and actors from China, and they have different layers of interactions with the African host country. When they interact, many contingencies or complexities emerge. Also, the host country is not just a passive recipient of aid or investment. It is also trying to carve its developmental path, but its ability to do so is often limited by its own history, domestic political conditions, social conditions and government capabilities. We need to both recognize the agency or the strategies of the host countries, but also critically analyze their constraints to inform policymaking in the future.\"Danae Hernandez-Cortes\u00a0Assistant professor in the School for the Future of Innovation in Society and the School of Sustainability, both\u00a0in the College of Global FuturesDanae Hernandez-Cortes\u00a0wanted to help vulnerable communities and felt one way to do that was through the lens of economics. But when she arrived in Mexico City to earn her bachelor's degree in economics, she noticed another problem that needed solving: pollution. And she believed that environmental policies were one of the contributors.\"When I was starting my undergrad, I wanted to understand what parts of environmental policy were affecting pollution and how we can solve them,\" Hernandez-Cortes said. \"I then decided to do my PhD in economics to try to get more knowledge on how these different policies could affect pollution exposure.\"Hernandez-Cortes' research took an interdisciplinary approach with an emphasis on equity; she focused on the sources of environmental inequality and the distributional consequences of environmental policy. She found that her background in economics was instrumental in her research.\"Economics is all about scarcity and trade-offs,\" Hernandez-Cortes said. \"Recognizing that scarcity, and that the policy implications we are trying to achieve have trade-offs, is the main basis for environmental policy. There's nothing more scarce than natural resources, and trying to solve different environmental problems will always involve trade-offs. By looking at these, we're able to understand better policies that could help design efficient and equitable environmental policy.\"]]>", - "post_date": "09/02/2021-8:47pm", - "clas_teaser": "This semester, five new faculty members are joining the College of Global Futures. Their unique and diverse backgrounds in sustainability, technology, innovation, education and policy will contribute to the college's vision of creating a sustainable, equitable and vibrant future for everyone on a thriving, healthy planet.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/earth-night.jpeg?itok=nl8FU4DN", - "path": "https://news.asu.edu/20210902-university-news-new-asu-faculty-will-use-their-interdisciplinary-research-build-better", - "hide_byline": "0", - "contributor-contact-information-affiliation": "School for the Future of Innovation in Society", - "contributor-contact-information-name": "Ashley Richards", - "contributor-contact-information-e-mail": "agoelitz@asu.edu", - "contributor-contact-information-phone_number": "480-727-8828", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/earth-night.jpeg?itok=nl8FU4DN", - "image_alt": "View of Earth from space at night", - "image_caption": "", - "related_story": "", - "news_units": "College of Global Futures|Julie Ann Wrigley Global Futures Laboratory|School for the Future of Innovation in Society|School of Sustainability|Thunderbird School of Global Management", - "interests": "Innovation|Education|Health|Sustainability|Technology|Academics|Research", - "audiences": "Faculty", - "event_types": "", - "locations": "", - "content_type": "asu_news_article", - "field_saf": "University News" - } - }, - { - "node": { - "nid": "90166", - "title": "Six Rushing Touchdowns Fuels 41-14 Opening Game Win", - "body": "\nSix rushing touchdowns by four Sun Devils led to a 41-14 season opening win over Southern Utah Saturday night at Sun Devil Stadium/Frank Kush Field.\n]]>", - "post_date": "09/02/2021-4:39pm", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210903-six-rushing-touchdowns-fuels-41-14-opening-game-win", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/2/_DSC6376.JPG", - "image_url": "https://thesundevils.com/images/2021/9/2/_DSC6376.JPG", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90142", - "title": "Adding a minor can enhance what a student learns in a major", - "body": "Majors are among the stars of the academic world. Whether students keep the same one throughout their time in college, change them along the way or double them up, majors play a central role in the college experience.Rarer, though, are mentions of a minor or an academic certificate gained through completing specially arranged courses. That\u2019s often because students don\u2019t have a minor or in many cases may not even know they exist.Faculty members in Arizona State University's\u00a0Watts College of Public Service and Community Solutions say minors deepen students\u2019 academic experiences and offer complementary knowledge to what they are getting from their majors that can make a difference as they enter careers \u2014 and adding a minor often costs no additional tuition.The college offers 13 minors, nine of which are within the School of Community Resources and Development. Those nine minors range from special event management to recreation therapy to community sports management.Watts College enrollment estimates for this semester report that about 604 ASU students have minors based at the college out of more than 9,800 throughout the university.In addition, the School of Criminology and Criminal Justice offers a minor in criminology and criminal justice, the School of Social Work offers one in social welfare, and the School of Public Affairs offers one each in public service and public policy, and in urban and metropolitan studies.The college also offers 14 certificate programs for completing certain menus of courses. Find them, as well as the college\u2019s minors, on\u00a0this list of all ASU academic minors and certificates.Minors can refine broader themes in majorsMinors and certificate programs offer students ways to enhance learning from courses in their majors, say two\u00a0School of Community Resources and Development faculty members, who work with several students with minors.A student\u2019s experience in a more broadly defined major can benefit from a minor, said Lecturer Claire McWilliams, who teaches tourism development management.\u201cI see minors as smart extensions that broaden the reach of a bachelor\u2019s degree,\u201d McWilliams said. \u201cYou could have a dozen students graduate with a communications degree, but only a few of them will have an events certificate and a minor in tourism.\u201dIncluding minors and certificates on a resume can make students stand out to potential employers, McWilliams said.\u00a0\u201cI see them as a very practical thing.\u201dErin Schneiderman, a clinical assistant professor who teaches special events management, agreed based on evidence she has observed among students who take that subject as a minor.\u201cWhen they take it as a minor, it means they want to go deeper into the events industry. They believe that the additional credential will enhance their major,\u201d Schneiderman said. \u201cFor example, a (criminology and criminal justice) major will say, \u2018I\u2019m interested in special events management, and if I\u2019m working in law enforcement and assigned to a specific event, I\u2019ll want to know what\u2019s happening around me.\u2019\u201dSchneiderman said other examples include fashion majors learning about how fashion shows are created, or art students who know they will show their work in public art festivals, both of which are discussed in special events management classes.Some students don\u2019t know minors are availableThe often-large undertaking of choosing a major sometimes leaves some students forgetting or even unaware they can pick a minor or certificate program as well, McWilliams said.\u201cThere\u2019s a big challenge for them to sort out majors. We\u2019re trying to get them to be aware of minors and certificates in the larger picture in putting together their college program,\u201d McWilliams said.A more diverse menu of items on a student\u2019s academic transcript show that student has had a more well-rounded experience, McWilliams said.In addition to complementing a student\u2019s major, a minor also can afford the chance to explore a second discipline that interests them, said Joanna Lucio, Watts College associate dean for academic affairs.\u201cFor instance, minoring in a language, such as Spanish, can help a nonprofit major who wants to work for a nonprofit that serves a Spanish-speaking population,\u201d Lucio said. \u201cOr, a criminal justice or public service and public policy major might minor in social welfare if they want to focus their careers in social services.\u201dStudents gain additional knowledge that will help them on a career path, broaden their perspective and add depth in an area of their interest \u2014 and it is reflected on their academic transcript, she said.Students praise adding minors to their experienceAlexandra Monksfield, a junior supply chain management and business (public policy) major, said she uses knowledge from her special events management minor every day.\u201cEvents management is very similar to project management,\u201d Monksfield said. \u201cI use the key ideas and tools I have learned to put them in a business aspect and give me the upper hand to finding more creative ways to solve issues. Not only does my minor allow me to interact with people not in the\u00a0business program, but I am also given more opportunities across the job market.\u201dSamantha Sahagun, a junior social work major, recently added a minor in parks and protected area management that she said will provide her more resources for day-to-day living.\u201cI have always been very passionate and fascinated\u00a0with being outdoors and wanting to better the environment. I thought the perfect way to accomplish this was by furthering my knowledge by adding a minor in something that I am\u00a0passionate about and can hopefully use in my social work career,\u201d Sahagun said.Caitlin Personale, a senior marketing major, said her special events management minor has opened up several new doors, introducing her to \u201ccountless people, companies and roles never before thought of.\u201d\u201cI was able to identify key strengths of mine and strong passions within the classes that I was previously unaware of,\u201d Personale said. \u201cFrom nonprofit leadership to sports tourism, both the transferable skills and real-world application value are unbeatable.\u201d\u00a0Lucio said that while students can have more than one minor, she strongly recommends students consult with their academic advisers before enrolling. Interested students can find information about minors and certificate programs from this list.\u00a0\u00a0]]>", - "post_date": "09/02/2021-4:11pm", - "clas_teaser": "Faculty members in ASU\u2019s Watts College of Public Service and Community Solutions say minors deepen students\u2019 academic experiences and offer complementary knowledge to what they are getting from their majors that can make a difference as they enter careers \u2014 and adding a minor often costs no additional tuition.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/140404-graduation-cap-and-pitchfork-silhouette3.jpg?itok=AYC8I1Is", - "path": "https://news.asu.edu/20210902-adding-minor-can-enhance-what-student-learns-major", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Watts College of Public Service and Community Solutions", - "contributor-contact-information-name": "Mark J. Scarp", - "contributor-contact-information-e-mail": "mscarp@asu.edu", - "contributor-contact-information-phone_number": "602-496-0001", - "contributor-contact-information-campus": "Downtown Phoenix campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/140404-graduation-cap-and-pitchfork-silhouette3.jpg?itok=AYC8I1Is", - "image_alt": "Graduation, cap, pitchfork, silhouette, graduate", - "image_caption": "", - "related_story": "", - "news_units": "School of Community Resources and Development|Watts College of Public Service and Community Solutions", - "interests": "Public service|Academics|Career development", - "audiences": "Prospective student|Student", - "event_types": "", - "locations": "Downtown Phoenix campus", - "content_type": "news", - "field_saf": "Creativity" - } - }, - { - "node": { - "nid": "90127", - "title": "Buggin' around in southern Arizona", - "body": "Editor's note: Arizona State University photographer and videographer Deanna Dent recently joined researchers from the\u00a0Biodiversity Knowledge Integration Center\u00a0on a trip to southern Arizona, where they collected insects after a strong monsoon. Here's what she learned.This year may well be one of\u00a0southern\u00a0Arizona\u2019s wettest monsoons in recorded history, and that has translated into vibrant plant growth and an increase in insect and animal populations after years of extreme drought.The photo above shows a large pond in the grasslands near the U.S.-Mexico border, east of Nogales, Arizona, and is exactly what researchers from BioKIC, or the Biodiversity Knowledge Integration Center, wanted to see. The researchers planned out a monsoon collection trip this past weekend, and while it's open to researchers, students and their families, they were kind enough to invite me with them as well. As an Arizona native it felt like a rare chance to go onto our public lands and learn about the plants, insects and animals of southern Arizona.When I first arrived at the Coronado National Forest campsite on Friday night, white sheets were being set up, glowing brightly in the darkness. These white sheets are clamped to a metal frame with a mercury vapor light at top, and a black light resting in the middle of the sheet. The lights were connected to a generator and run until midnight, attracting all types of insects from the dark. Between dinner and breaks, chatting folks examined the sheets to see what had been attracted, commenting at the variety and beauty of the insects. Researchers opted between cupping the small insects in their hands and placing them in vials or jars, or using the aspirator, a kind of vaccuum that pulls in small insects with a filter protecting the operators. These methods work well, but what struck me as interesting was that experts don't know why insects are attracted to the lights. They have theories, but no one knows why they're drawn to bright lights.The students, researchers and community members all spend time together, creating a strong sense of community interest. I'm perfectly welcome to ask a series of silly questions to experts in their field about how many insect species exist in the world or why they collect this beetle but not that one. Or why are moths dusty? And thankfully there are no unwelcome questions.I ask, \"Why this particular area of the Patagonia mountains?\"\"This kind of highlands region, and to the Santa Rita mountains north of us, really contained a lot of interesting records from jaguars to a lot of reptiles and amphibians that just get into the U.S. from Mexico and extend their range into here,\" said Andrew Johnston,\u00a0invertebrates collections manager and darkling beetle researcher. \"So there were a lot of potentially interesting things for us to find in the less documented mountain range.\"As the night continues, we find the Arizona state reptile, an Arizona ridge-nosed rattlesnake, curled at the base of a tree just a few feet from one campsite. We walk the small creek looking for frogs and toads, which you can hear but not really see. One camper shares the cicada they found preparing to molt, while botanist Elizabeth Makings shares a desert stink beetle and a dragonfly rests on the white sheet.I also learned that although experts are very familiar with all the mammal species on Earth, there's a huge question mark on insect species.\u00a0\"It\u2019s likely that we\u2019ve not described even half of the diversity of insects that currently exist and alive in the world today,\"\u00a0Johnston said.A lack of knowledge of the insect life in an area adds to a lack of future understanding about our environment and ecological systems, as climate change affects our state over time.\u00a0The generators go out at midnight and folks head to tents for the evening. As the sun rises over the mountains, green stretches all across the mountains and grasslands below, a shocking green that almost seems out of place in Arizona nowadays.In the morning, Makings and researcher Ed Gilbert head up a stream\u00a0that runs through the campsite with three ASU undergraduate students \u2014 Ethan Wright, Marcus Reid and Mary Haddad. Makings spends time walking with the students as they explore the grasses, plants, insects and the environment.\"I love to do fieldwork; I mean, why wouldn't you get buggy and hot and sweaty,\" said Makings, who helps organize the trips. \"It's all about the camaraderie and, you know, interacting with scientists that are ... amazing in their field, and I just enjoy learning from everybody.\"Gilbert's son, Aven Klecker, manages to catch a fencepost lizard with his lizard lasso. While holding the lizard in his hand, he flips it upside down and gently rubs its belly, which puts it into a still state. He shows the trick to Haddad, who holds the lizard until he jumps up and runs aways.As we concluded our walk, another set of researchers jumped in four-wheel-drive vehicles to explore the mountaintop areas reachable only by some rough forest roads. As we drive through some of the most scenic areas, we look for areas of interest and finally stop at a high point with a depression that has filled with water, creating a kind of pond in the center.Researchers head out into the water with their nets to catch dragonflies or photograph botanical species. With every interesting find, everyone rushes over to take a look before the insects are released. School of Sustainability researcher Rick Overson spends time photographing the different insects with his 100mm macro lens, and environmental and\u00a0zoological collections manager Laura Steger uses her net to collect dragonflies before we finally head back to camp. After a quick lunch, another group of researchers heads down to the nearly chest-height grassland areas near the U.S.-Mexico border.\u00a0Just near Lochiel, Arizona, we pull off on a road and head to a grassy area, after being rained out of the road to Scotia Canyon in the Huachuca Mountains. We can see the monsoon storm clouds all around but somehow don't receive a hint of rain. The grasses give way to a small pond where we find a frog and a western patch-nosed snake, which apparently is a county over from where it is described as having range;\u00a0this is noted. We patch a punctured tire and someone passes around Trader Joe's cookies as we decide to head up to reach the campsite before dark. Folks are trying to bet whether the rains hit our campsite and if their sleeping bags will be soaked.\u00a0We reach the camp to find that it has not only rained but hailed, and while those who stayed behind tried their best to protect the area, everything is wet and damp. Unfortunately this means most insects won't come out to the white sheet at all, so we sit down and eat a spaghetti dinner together. Folks share photos from the day's finds. We hear jokes and stories about past trips and almost impassible roads, along with a limerick or two before everyone heads to bed.The next morning everyone sits down for breakfast and a final touch-base as the camp breaks down. I interview\u00a0Johnston and Makings and record sound of the stream, which is running with water this morning, and plan out my route back home through Patagonia to Tucson and back to Phoenix.\u00a0Makings is leaving with over 150 grass specimens \u2014 a wealth of samples after years of drought, which may take weeks to process back at the Alameda Center.Johnston\u00a0shows me the tools of the trade, including forceps, an aspirator, nets and archival vials \u2014 though takeout containers can work temporarily as well. He says sometimes he saves samples in his freezer until he's ready to process, and it makes me wonder if that ever surprises anyone.\u00a0It's a bright beautiful morning as we all head back to regular life, and I'm glad I spent this weekend out in the wilderness with such an amazing group of experts.\u00a0]]>", - "post_date": "09/02/2021-4:05pm", - "clas_teaser": "Arizona State University photographer and videographer Deanna Dent joined researchers from the Biodiversity Knowledge Integration Center on a trip to southern Arizona where they collected insects after a strong monsoon. Here's what she learned.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/article_heros/20210827biokicresearchcollectiontrip_0662.jpg?itok=Xc5X7BUK", - "path": "https://news.asu.edu/20210902-arizona-impact-buggin-around-southern-arizona", - "hide_byline": "0", - "contributor-contact-information-affiliation": "ASU News", - "contributor-contact-information-name": "Deanna Dent", - "contributor-contact-information-e-mail": "dadent@asu.edu", - "contributor-contact-information-phone_number": "480-727-5972", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/article_heros/20210827biokicresearchcollectiontrip_0662.jpg?itok=Xc5X7BUK", - "image_alt": "dragonflies on the sheet", - "image_caption": "", - "related_story": "", - "news_units": "College of Global Futures|The College of Liberal Arts and Sciences|School of Sustainability", - "interests": "Environment|Life Science|Research", - "audiences": "Staff|Faculty|Community|Student", - "event_types": "", - "locations": "", - "content_type": "featured_article", - "field_saf": "Arizona Impact" - } - }, - { - "node": { - "nid": "90155", - "title": "ASU professor researching how humans can help technology detect bad bots", - "body": "An Arizona State University professor is researching how to track malevolent social media bots by using a human touch.Victor Benjamin, who researches artificial intelligence, is looking at how people\u2019s reactions to social media posts can be mined for clues on content that may be generated by automated accounts called bots.\u201cMy co-author and I had this hunch that as bots are becoming more prolific on the internet, users are becoming more able to detect when something looks fishy,\u201d said Benjamin, an assistant professor in the Information Systems Department in the W. P. Carey School of Business at ASU.\u201cCould we apply bot-detection technology and augment that by relying on human intelligence in the crowd responses?\u201dBenjamin's most recent paper on detecting malicious bots was published in PeerJ Computer Science, an open-access journal. He worked with Raghu Santanam, who holds the McCord Chair of Business in the Information Systems Department, and that research looked at how how the same fake news translated across various regional dialects may be detected using advanced linguistic models.Bots are dangerous because they spread misinformation and conspiracy theories.\"This vaccine misinformation is scary stuff,\" he said, so finding a faster and better way of detecting bots is more important. That's why Benjamin decided to look at whether adding humans to the computer algorithms was more accurate.He used the platform Reddit because it has posts and replies that lend themselves to looking at crowd reactions. The researchers wanted to weigh the level of certainty over people\u2019s bot detection.\u201cYou could have two users and one might say, \u2018I think this is a bot\u2019 and the other might say, \u2018I know for sure this is a bot.\u2019\u201cThat\u2019s the angle of our research \u2014 how to signal different certainties. You don\u2019t want to blindly trust every crowd response.\u201dOne factor that can hamper research is the lack of public data sets from social media platforms. Reddit is maintained by volunteer moderators who share lists among themselves of which accounts might be bots. The researchers collected those lists, then analyzed the crowd responses to posts from those accounts.They applied \u201cspeech action theory,\u201d a linguistic theory, to determine the level of certainty that someone found a bot.\u201cIt\u2019s a way to quantify the intended or underlying meaning of spoken utterances,\u201d Benjamin said.In the replies, the researchers looked at whether \u201cbot\u201d was mentioned, which was a strong indicator of accuracy in finding a bot. Sentiment, such as a strong negative reaction to the topic of bots, was a less important indicator.The research, which is currently under review, found that analyzing the human reactions added to the accuracy of the computational bot-detection system.It\u2019s important to root out bots because they are a major source of misinformation spread on social media. Bots controlled by Russian trolls are a major driver of the lies that the 2020 election was stolen, researchers say. Last spring, Benjamin authored an op-ed column in the Boston Globe about how social media platforms need to step up the fight against misinformation.\u201c(Bots) hijack conversations on controversial issues to derail or inflame the discussion,\u201d he wrote. \u201cFor example, bots have posed as Black Lives Matter activists and shared divisive posts designed to stoke racial tensions. When real people try to make their voices heard online, they do so within a landscape that\u2019s increasingly poisoned and polarized by bots.\u201dNever before in human history have foreign governments been so successful in being able to target the populace of another country.\u00a0\u2014 ASU Assistant Professor Victor BenjaminOne way social media platforms can help curb bots is by releasing more data to their users, who could then decide whether a post is from a bot.\u201cWe\u2019re relying on public, open-source data \u2014 whatever the platforms make available to us.\u00a0But the platforms have a lot of data they never reveal, such as how often users log in or how many hours their activity level remains continuous. A bot might have a 16-hour session,\u201d he said.\u201cIf you see someone posting very inflammatory messages, why can\u2019t the platform reveal that data? \u2018This user posts 300 messages a day, and they\u2019re all inflammatory about America.\u2019\u201cOr if there\u2019s a hashtag that suddenly becomes popular on Twitter, Twitter has the metadata to show how that hashtag was formed. Was it organic growth or did it appear millions of times out of nowhere within a few minutes?\u201dNo private data would need to be revealed.\u201cWe just need metadata on usage of a hashtag or origin of country of where a hashtag is most frequently tweeted from.\u201cIf it\u2019s a Black Lives Matter tagline being tweeted from Russia, we should be suspicious.\u201dThe problem is that it\u2019s not in the social media platforms\u2019 best interest to do anything that would decrease engagement, he said.\u201cThe more a user engages, whether it\u2019s a bot or not, the more it helps the value of the platform.\u201cIf they say, \u201830 percent of users are bots,\u2019 what does that do to the value of the platform?\u201dScrutiny of social media platforms heightened during the divisive 2016 election.\u201cWith Facebook, I\u2019ll give them credit. They released data saying, \u2018We noticed a lot of advertising paid for by Russian state agencies,\u2019 \u201d Benjamin said.\u201cAnd Twitter put out a small grant for improving the conversational health of social media. They acknowledged some of their responsibility for maintaining the quality of online conversations, but they\u2019re still not at the level we want them to be of releasing the metadata.\u201dBenjamin called the current state of affairs \u201can arms race\u201d between bot authors and bot detection.\u201cSocial media bots out in the wild today are always listening for new instructions from their owners. They can change their behavior in real time.\u201cIf you have a static detection method, invariably the bots will learn to evade it.\u201dThat\u2019s why a system incorporating humans could be faster and more accurate. But the system would have to be adaptable, learning linguistic patterns from different languages.The next frontier for bots \u2014 and using human detection \u2014 will be video.\u201cIf you go on YouTube, there are now algorithmically generated videos that are completely generated by bots. It\u2019s a lot of the same stuff \u2014 to spread misinformation and random conspiracies and so on,\u201d he said.The bots are creating narrative videos with unique content and music.\u201cSome are of low quality, but you wouldn\u2019t be able to tell whether they are bot-generated or by someone who is new to creating videos,\u201d he said.One way to catch bot-generated videos is through applying the \u201ctheory of mind,\u201d or the ability to consider how another person would see something \u2014 a perspective that is difficult for bot-generated content. For example, a human would align the visual and audio content in a video, but a bot might not.\u201cWhere would a human content creator apply theory of mind that a bot might not, and how can we see those discrepancies?\u201d\u00a0Benjamin asked.He said that vaccine misinformation amplified by bots is especially scary.\u201cNever before in human history have foreign governments been so successful in being able to target the populace of another country,\u201d he said.Top image courtesy of Pixabay.com]]>", - "post_date": "09/02/2021-3:35pm", - "clas_teaser": "ASU Professor Victor Benjamin has been researching malevolent social media bots by using a human touch. He says people\u2019s reactions to posts can be mined for clues on content that may be generated by automated accounts.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/technology-3353701_1920.jpg?itok=pPp6zAyT", - "path": "https://news.asu.edu/20210902-discoveries-asu-research-finds-humans-can-help-detect-bad-bots", - "hide_byline": "0", - "contributor-contact-information-affiliation": "ASU News", - "contributor-contact-information-name": "Mary Beth Faller", - "contributor-contact-information-e-mail": "marybeth.faller@asu.edu", - "contributor-contact-information-phone_number": "480-727-4503", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/technology-3353701_1920.jpg?itok=pPp6zAyT", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "W. P. Carey School of Business", - "interests": "Business|Technology", - "audiences": "Faculty|Community|Student", - "event_types": "", - "locations": "Tempe campus", - "content_type": "asu_news_article", - "field_saf": "Discoveries" - } - }, - { - "node": { - "nid": "90163", - "title": "Downtown Phoenix campus turns 15", - "body": "It was once a place where people cleared out after work, where most restaurants closed by 3 p.m., where only the occasional sports game or First Fridays art walk drew a younger crowd.Now Arizona State University students live and learn on the Downtown Phoenix campus, bringing an energy and presence that have helped inject new life into the area.It\u2019s easy to forget that the Downtown Phoenix campus\u2019 vibrant collection of new and renovated buildings, residence halls, entrepreneurship spaces and streetscapes were once vacant acreage, parking lots and neglected commercial space. Now it\u2019s a place where students study health and nursing, journalism, public service and law; gather in open spaces for social events; and do outreach with various local communities.The campus marks its 15th anniversary this school year.\u201cThe ASU Downtown Phoenix campus represents the best of what is possible when a city joins forces with an innovative university to address a community need and reimagine all the ways they can collaborate to be of service,\u201d said ASU President Michael Crow. \u201cASU believes that universities should be accessible wherever there is an opportunity to help learners and the community at large to thrive, and we\u2019re proud of the growth and experiences our downtown campus creates for learners and partners alike, as well as the robust, cross-sector network it has revitalized in the heart of our metro region.\u201dPrior to the Downtown Phoenix campus, ASU had never tried to sell an urban experience. Its establishment came as result of an unprecedented referendum.Public money was involved. The basketball arena was downtown, but unless there was a game, the streets were empty in the evenings. Civic leaders knew the area needed more to push it to the next level. And that included populating the area with students who would live, work, study and shop there.In short, these students would become citizens of the city and able to pursue their careers in a more dynamic arrangement.New York City has Columbia. Los Angeles has UCLA. Phoenix needed an urban university. But there was no need to create one from scratch.\u00a0What follows is the story of how a group of very determined people turned a napkin sketch into a viable 20-acre, multimillion-dollar university campus in a short amount of time that had skeptics wondering if it would ever really happen.Two decades ago, the streets of downtown Phoenix were populated by business people and the occasional event goer. Now students bring their energy and potential to ASU's campus, where a line of families moving students into the residence halls is a common sight each August. Photo by Charlie Leight/ASU NewsBreakfast of championsThe Downtown Phoenix campus wasn\u2019t initially created on an architect\u2019s draft table or an engineer\u2019s rendering. It was sketched on a plain, white restaurant napkin.It started with two great minds \u2014 and two empty stomachs \u2014 over breakfast in 2003. Crow and then-Mayor Phil Gordon wanted more than a satellite school. They wanted bacon and eggs \u2014 and they wanted thousands to live and study in downtown Phoenix for a very long time.\u201cOur first meeting took place at The Good Egg on Central and Camelback in Phoenix when I was running for Phoenix mayor,\u201d said Gordon, who won the race in September 2003 and served as Phoenix\u2019s mayor from 2004 to 2012. \u201cBringing ASU to the downtown area was one of things I pushed during my campaign. Dr. Crow said, \u2018Great idea. Come back \u2014 win or lose \u2014 and let\u2019s move this concept forward.'\u201dThey met again at the same restaurant shortly after Gordon\u2019s victory, vowing to work together to take the university, the city and the region to new heights. Gordon, a municipal leader unafraid of taking on challenges, and Crow, the academic visionary who felt that the old college system was broken and who had declared ASU the \u201cNew American University,\u201d seemed perfectly matched.Phoenix and the university\u2019s burgeoning popularity added pressure to act swiftly to create an environment that would attract a \u201ccreative class\u201d \u2014 people who would build a knowledge industry and develop technologies to nourish Arizona in the 21st century.Despite Phoenix\u2019s designation as one of the nation\u2019s largest cities, and its growth and prosperity in the new millennium, its core had been somewhat hollow, especially after business hours. Merging students, staff and faculty with merchants, artisans, government and nonprofits would inject a much needed, livelier ambiance in the city\u2019s core.It would also help accommodate ASU\u2019s booming numbers. In order to meet the projected growth in college-bound Arizona high school graduates, the university needed capacity. ASU began shifting its focus from Tempe to other campuses in the Valley to handle the overflow: ASU\u2019s West and Polytechnic campuses, and a small satellite facility at the Mercado at Seventh and Van Buren streets. Both Crow and Gordon knew expanding ASU to a full-scale campus in downtown Phoenix made perfect sense.ASU Adjunct Professor Isaac Easley takes a headshot of Cronkite News Sports team member Mariah Graves, a\u00a0graduate student in sports journalism, at the Cronkite News set on Aug. 19. The Walter Cronkite School of Journalism and Mass Communication was one of the first colleges that moved to the Downtown Phoenix campus 15 years ago. Photo by Deanna Dent/ASU NewsA blueprint for maroon and goldThat napkin became ASU\u2019s blueprint \u2014 a campus geared toward city-minded students attracted to service-oriented careers. The area was teeming with government, media, nonprofit, legal, medical and business operations ripe for full- or part-time internships, mentoring and professional networking opportunities. The region\u2019s bevy of professional arts, dance, theater and music venues; sports stadiums; and fine and casual dining would cap off the university\u2019s unique work-study-live experience.A university design team, led by Wellington \u201cDuke\u201d Reiter, then dean of what was known as the College of Architecture and Environmental Design (now The Design School), said in early 2004 that he was pulled into Crow\u2019s office and asked if he could design an urban campus for ASU. There weren\u2019t yet too many hard-and-fast specifics, including which colleges would relocate downtown, but he was given one directive \u2014 have it done in 60 days.\u201cHow do you design a house before you know who the occupants are? How do you design a campus before you know which colleges will go there?\u201d Reiter said. \u201cThe only thing I really knew was the scope, which was, one day the campus would potentially serve 15,000 students, which was a start.\u201dReiter also enlisted noted and locally based architecture firms to think beyond the normal constraints of what might be possible. The firms included Will Bruder Architects, Architekton, SmithGroup and DeBartolo Architects.They collectively identified several universities \u2014 George Washington University in Washington, D.C.; New York University; Emerson College in Boston; and Savannah College of Arts and Design in Georgia \u2014 as excellent models of how an urban campus interacts with the community.Reiter also advocated for a civic space or multiuse park to be used by the students and general public. The idea was to bridge the city and the university, and offer community-related events, sponsored by both entities. The end result was a $34 million sustainable space that opened in 2009 called Civic Space Park.A university design team also spent several months deciding which colleges should relocate to the downtown campus. They ultimately chose: the Walter Cronkite School of Journalism and Mass Communication; Watts College of Public Service and Community Solutions; the Edson College of Nursing and Health Innovation; and University College.A few years later the campus added the Sandra Day O\u2019Connor College of Law; College of Health Solutions; Thunderbird School of Global Management and the Graduate College. The College of Integrative Sciences and Arts; Mary Lou Fulton Teachers College; and Barrett, The Honors College also established a presence on the campus, as did a sprinkling of centers, think tanks and initiatives.Fusion on First, a combination high-rise residence hall and academic-entrepreneurship space, welcomed its first residents on Aug. 16. The first floor of the new building will offer some student amenities as well a makerspace. The second and third floors will feature academic space with fashion being prominent on the second floor and music programing on third. Photo by Charlie Leight/ASU NewsNo Plan BWhen the city and ASU unveiled its master plan to the community, it was met with mixed praise from the business community, local residents and most especially the media. Landholders debated whether or not to sell their property; private developers in the area wondered if they should get involved; residents didn\u2019t want the university to change the downtown\u2019s character. Some media openly questioned the viability of the project.There were some members on the home team who wondered aloud if the city\u2019s greatest revitalization project was really going to happen, given its collapsed timeline of getting the campus up and running by late August 2006.\u201cOne of the deans asked me early in the process what was \u2018Plan B\u2019?\u201d said Mernoy Harrison, former ASU executive vice president and chief financial officer, who was selected by Crow to oversee the completion of the campus. \u201cI told this person, \u2018There is no Plan B. It\u2019s do or die. We either launch this campus or die trying.\u201dHarrison\u2019s declaration soon became the battle cry for ASU and city of Phoenix employees, who designed, renovated and constructed a university campus in record-breaking time.But where to get the money?A 2006 Phoenix bond proposal would provide the necessary funds to build the campus. The proposal totaled $878.5 million for homeland security, education, parks, libraries, streets, fire stations and other city infrastructure and amenities. Of those funds, $223 million was earmarked for the ASU Downtown Phoenix campus \u2014 almost a fourth of the total and the largest single proposed bond expenditure.But it needed voter approval.That took place on March 14, 2006, winning over 60% of the vote according to the Associated Press. That was the good news. The bad \u2014 or at least stressful \u2014 news was that ASU and city officials had less than six months to renovate several buildings, purchase office supplies, equipment and furniture, strategize operations, move 600 faculty from the Tempe campus, and hire 80 new staff members, including the four founding deans. A tall order for a short window of time.School of Social Work graduate students Brandon Falk (left) and Ronald Bookman (right) chat with Westward Ho resident Douglas Meyer during an informal meetup in Civic Space Park in downtown Phoenix last winter. The social-hour activity was started by the ASU Community Collaborative as a way for residents to safely connect and is but one of many ways ASU students connect with the downtown community. Photo by Charlie Leight/ASU NewsThe planets alignConstruction crews scrambled, working around the clock while ASU burned the midnight oil planning for the massive four-college move \u2014 all while the new light rail system along Central Avenue was getting underway. Buildings were gutted and classrooms readied, while the torn-up streets made navigating a challenge.But then trucks and shipments with televisions, computers and furnishings started to arrive and, finally, it was done.\u201cAll the planets were aligned for this project,\u201d Reiter said. \u201cWe had a mayor who was ready to do something great; a governor who was predisposed towards higher education; a university president who was ready to build; a community who supported this idea; a bond election, which could fund this and make it a reality; and a thriving economy, which gave voters the confidence to make positive change. It was the right cast of characters at the right economic time.\u201dSeveral hundred dignitaries, employees, business owners and locals joined the Aug. 15, 2006, ribbon-cutting ceremony inside the University Center lobby to listen to special remarks from Gov. Janet Napolitano, Gordon and Crow, as well as take a tour of campus facilities. ASU\u2019s Downtown Phoenix campus went from brainstorming sketch to reality in just three short years.And that fall, it welcomed more than 3,000 full-time and 6,000 part-time students. Today, that number has almost quadrupled to more than 11,000 full-time students. One of them is Noah Furtado, a Hawaiian native who came to Phoenix to study journalism.\u201cI like that everything on the Downtown Phoenix campus is close by, and the longest walk I have to make for a class is about 15 minutes,\u201d Furtado said. \u201cThere\u2019s a really down-to-earth vibe here compared to Tempe. Everything is close by, and the people are close-knit.\u201dFurtado also likes the amenities at the Cronkite School and hanging out with like-minded students.\u201cI like the fact that the Cronkite building has a lot of editing bays, and you can go in there whenever you want to learn the craft and work with all of the different photo and video applications,\u201d Furtado said. \u201cIt\u2019s refreshing to be around a lot of people who are passionate about the same thing you are. It\u2019s nice that we have a shared interest in sports journalism, and it\u2019s something that I didn\u2019t have with a lot of people back in Hawaii.\u201dAlso new to campus this year is Cronkite School Dean Battinto Batts Jr., who moved from Cincinnati this summer. He said living and working in downtown Phoenix has a vibrancy that he\u2019s enjoying.\u201cThe downtown Phoenix campus has grown tremendously over the course of the past decade and has been a powerful catalyst in the community\u2019s overall growth,\u201d Batts said. \u201cBeing new to ASU, I am currently living downtown while looking for permanent housing. The energy is so appealing that we\u2019re in no rush to leave. I am a believer in the role of higher education as transformers of communities. That role involves being a developer of buildings while also developing a vibe and a feel. Arizona State has accomplished both with its Downtown Phoenix campus, and I am excited to be a part of it.\u201dConstruction is in the final stages of the new home of the Thunderbird School of Global Management on the Downtown Phoenix campus, shown here Aug. 19. Photo by Deanna Dent/ASU NewsRoom to growThough it may seem to many that the campus has reached full maturation, it is in fact growing and still has room for expansion.This semester saw the opening of Fusion on First, a high-rise residential community at 380 N. First Ave. The building will include 13 floors of apartment-style student housing and several floors of classroom space, housing the fashion and popular music majors from the Herberger Institute for Design and the Arts.Another building nearing completion is the new home of the Thunderbird School of Global Management, which announced its relocation to the Downtown Phoenix campus in 2017. Thunderbird\u2019s move from its former westside location to downtown Phoenix provides the opportunity to evaluate the curriculum and give them proximity to other ASU schools and colleges, while continuing to provide world-class global management and business education with a unique intercultural focus.Thunderbird\u2019s new building is between First and Second streets, just north of Polk Street. It includes space for classrooms, meetings, enclave and office space and will include two levels for executive education. There will also be rooftop function space.Ongoing plans for the campus include continued growth of the existing academic programs currently on the campus, which, over time, means the need for more classroom, office and residential space. In addition, downtown has become a major center for health and bioscience research, with the opening this year of the\u00a0Wexford Innovation Center on the Phoenix Biomedical Campus, which was another collaboration between the city of Phoenix and ASU, with the help of Wexford Science + Technology.TGen and private companies \u2014 along with all three state universities \u2014 are doing cutting-edge research to advance human health in the downtown area. ASU\u2019s College of Health Solutions and Edson College of Nursing and Health Innovation are driving this research growth for ASU, and it is expected that additional university and private research activity will gravitate to and grow in the downtown area, along with the startups that emerge from this research.The Downtown Phoenix campus\u2019 launch and continued success is one of Gordon\u2019s proudest accomplishments in his eight years as Phoenix mayor.\u201cIt has succeeded way beyond anyone\u2019s expectations,\u201d said Gordon, who is now the director of the Chan Soon-Shiong Institute of Advanced Health, a data center dedicated to health information. \u201cIn addition to the campus, there\u2019s the outgrowth of apartments, houses, restaurants, bars, grocery stores, retail businesses and commercial buildings. The campus has totally redeveloped downtown Phoenix, and now other major cities want college campuses in their downtowns, too.\"Top photo:\u00a0Students make their way to classes at the Beus Center for Law and Society during the first day of fall 2021 classes on ASU's Downtown Phoenix campus Aug. 19. Photo by Deanna Dent/ASU]]>", - "post_date": "09/02/2021-2:30pm", - "clas_teaser": "ASU's urban campus turns 15 this fall \u2014 and its creation is the story of a city and a university working together with a shared vision. \u201cThe ASU Downtown Phoenix campus represents the best of what is possible when a city joins forces with an innovative university to address a community need and reimagine all the ways they can collaborate to be of service,\u201d ASU President Michael Crow said.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/20210819firstdayofclassesdowntowncampus_109.jpg?itok=be6pFsea", - "path": "https://news.asu.edu/20210902-arizona-impact-asu-downtown-phoenix-campus-turns-15", - "hide_byline": "0", - "contributor-contact-information-affiliation": "ASU News", - "contributor-contact-information-name": "Marshall Terrill", - "contributor-contact-information-e-mail": "marshall.terrill@asu.edu", - "contributor-contact-information-phone_number": "480-727-5176", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/20210819firstdayofclassesdowntowncampus_109.jpg?itok=be6pFsea", - "image_alt": "Students walk through a corridor between buildings while some sit at tables", - "image_caption": "", - "related_story": "", - "news_units": "Edson College of Nursing and Health Innovation|College of Health Solutions|Sandra Day O'Connor College of Law|Thunderbird School of Global Management|Walter Cronkite School of Journalism and Mass Communication|Watts College of Public Service and Community Solutions", - "interests": "Education|Health|Journalism|New American University|Public service|University", - "audiences": "Media|Staff|Faculty|Corporation|Policymakers|Alumni|Community|Student", - "event_types": "", - "locations": "Downtown Phoenix campus", - "content_type": "asu_news_article", - "field_saf": "Arizona Impact" - } - }, - { - "node": { - "nid": "90165", - "title": "Douglas posts third career hat trick in @SunDevilSoccer\u2019s 4-2 win over CSUN", - "body": "\nNicole Douglas tied a single-game school record with three goals as the Sun Devil soccer team improved to 5-0 following a 4-2 win over CSUN on Thursday's day one of the Sun Devil Classic.\n]]>", - "post_date": "09/02/2021-12:50pm", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210902-douglas-posts-third-career-hat-trick-sundevilsoccer%E2%80%99s-4-2-win-over-csun", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/2/DSC_5705.jpg", - "image_url": "https://thesundevils.com/images/2021/9/2/DSC_5705.jpg", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90154", - "title": "The impact of empowering student entrepreneurs", - "body": "As a registered nurse, Jasmine Bhatti knows that patients leaving the hospital may still have a long road to full recovery. For people who don\u2019t have a network of family and friends to assist them after a hospitalization or surgery, the joy of going home can be tinged with anxiety.Bhatti had an idea that could help \u2014 a marketplace that connects nurses to patients and families who need medical guidance and support after a hospital stay or outpatient surgery. But with her intense schedule as a nurse caring for COVID-19 patients and as a PhD student at Arizona State University, coupled with a lack of business experience, Bhatti needed some help to get her company,\u00a0Navi Concierge Nurses, off the ground.\u201cMy co-founder and I are nurses, so our expertise is in clinical care. It was a challenge for us to learn so many different other business-related skills like accounting, financing, marketing, creating strategic partnerships and more,\u201d she said.Bhatti found the support she needed through the\u00a0J. Orin Edson Entrepreneurship + Innovation Institute, officially established in 2020. Innovative problem solving, transdisciplinary research and inclusivity for all learners has always been at the heart of the ASU Charter, but a generous gift from J. Orin Edson set the stage for an evolution of the concept \u2014 in perpetuity.The gift was a major milestone in the Edson family\u2019s long history of support to ASU.J. Orin Edson was, himself, the living embodiment of the entrepreneurial spirit. Deeply devoted to the community, he held the belief that talented and forward-thinking people like Bhatti should have the resources to pursue their entrepreneurial dreams. Edson and his wife, Charlene, have been ASU benefactors since 2005.\u201cThe Edsons helped set ASU on a path to valuing entrepreneurship with their initial gift in 2005 establishing the Edson Student Entrepreneur Initiative. That gift enabled ASU to be the first investors, as the \u2018friends and family round,\u2019 to student-driven startups and the development of new solutions,\u201d said Ji Mi Choi, vice president for ASU\u2019s Knowledge Enterprise and founding executive director of the Edson Entrepreneurship + Innovation Institute.The Edson Student Entrepreneur Initiative has paved the way for nearly 300 student-led ventures, which have filed more than 40 patents and raised more than $46 million in external funding. In 2018, a second endowed gift supported Edson\u00a0Entrepreneurship + Innovation Institute training and development resources to accelerate innovation and entrepreneurship. It has also provided support for faculty to develop and offer extra- and co-curricular training utilizing a wide range of subject-matter expertise.Bhatti participated in the institute's\u00a0Venture Devils\u00a0and Venture Devils Plus programs, which currently include more than 700 teams. Since the start of the pandemic, both she and her co-founder have been focused on patient care in the hospital where they work. But in true entrepreneurial spirit, they have managed to grow their company in spite of trying circumstances. Bhatti said the institute's programs were influential in developing the clarity needed to design and actually create the business.\u201cWe were given the opportunity to connect with other professionals whose guidance helped us refine our strategies immensely,\u201d said Bhatti, who is now completing her dissertation in the\u00a0Edson College of Nursing and Health Innovation, named through\u00a0a generous gift to ASU health care programs\u00a0from the Edson family in 2019. \u00a0Bhatti admits that balancing school, work and business ownership was a challenge, especially when paired with the emotional, mental and physical fatigue of caring for COVID-19 patients.\u201cI had to turn those painful emotions into fuel to propel this business forward,\u201d she said. \u201cIt\u2019s like you keep adding things to your plate and you have to figure it out as you go because everything becomes important. Right now for me, balance means just making sure I\u2019m getting good sleep, I get to see my loved ones and I\u2019m engaging in a handful of activities every day that bring me joy.\u201dEdson Entrepreneurship + Innovation empowers students to concurrently pursue education and entrepreneurship. While the primary focus of the institute is ASU students, faculty, staff and alumni, it also supports a diverse range of lifelong learners and entrepreneurs of every age, interest and socioeconomic background. ASU\u2019s entrepreneurship programs have received numerous\u00a0awards\u00a0over the years, including the Deshpande Symposium for Innovation and Entrepreneurship in Higher Education, and the Global Consortium of Entrepreneurship Centers Award for Outstanding Contribution to Venture Creation.The Edsons\u2019 gracious contributions have allowed ASU to expand its portfolio of entrepreneurial programs and initiatives, as well as the depth and breadth of the programs.\u201cASU\u2019s commitment and leadership in innovation is evidenced with the establishment of the institute and enables us to materially follow through on taking fundamental responsibility for the economic and community development and well-being of the communities we serve,\u201d Choi said.\u201cIn addition to the stability of the institute serving as the foundation for entrepreneurial programming, we also galvanize support from many other philanthropic and funding sources to further the impact we have on advancing solutions \u2014 new products, new services and new processes \u2014 to address problems and unmet needs in society.\u201dOn top of collaborations with many ASU academic programs and colleges, Edson\u00a0Entrepreneurship + Innovation facilitates multiple funding opportunities, provides collaborative working spaces for ventures at all stages, and offers training and development resources, including a robust mentor network to help up-and-coming entrepreneurs find solid footing.Bhatti credits her own mentor, Rick Hall, with believing in her and encouraging her to apply to the Venture Devils program. Hall is director of the\u00a0Health Entrepreneurship Accelerator Lab\u00a0(HEALab), and assistant dean and clinical professor of health innovation programs at the Edson College of Nursing and Health Innovation.\u201cHe connected us to a professor in the college of engineering who was able to help us create a prototype of our product,\u201d Bhatti said. \u201cHe connected us with the law school so that I could save money by having students help with some of my initial legal documents. Dr. Hall also introduced me to a handful of incredible people throughout the university who have become followers of what we are building.\u201dThrough hard work, perseverance and leveraging the resources available to them, Bhatti and her co-founder were ready to make their business happen by the end of last year. They began taking their first clients in January 2021, went from zero to more than 90 nurses in their network, and have made almost $500,000 to date.What advice would Bhatti offer other student entrepreneurs?\u201cIt may sound kind of silly,\u201d she said, \u201cbut I didn\u2019t think that my idea was enough to really go and ask for help. In hindsight, I don\u2019t want any student to ever think that. If anyone ever tells you that you have a good idea, roll with it. Believe in yourself as much as others believe in you. Especially in the beginning.\u201cI promise that once you start to see what you are building is meaningful, you will understand the power of your idea. All it takes is the spark. The university has all the fuel to help the flames grow and spread.\u201dTop photo:\u00a0Jasmine Bhatti, founder of Navi Concierge Nurses and PhD student in the Edson College of Nursing and Health Innovation. Photo by Brandon Sullivan]]>", - "post_date": "09/02/2021-9:42am", - "clas_teaser": "The J. Orin Edson Entrepreneurship + Innovation Institute, which was officially established in 2020, has been helping entrepreneurs get their big ideas off the ground for the past year.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/jasmine-bhatti-navi-nuses.jpg?itok=avqI6wZN", - "path": "https://news.asu.edu/20210902-entrepreneurship-impact-empowering-student-entrepreneurs", - "hide_byline": "1", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/jasmine-bhatti-navi-nuses.jpg?itok=avqI6wZN", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "College of Health Solutions|J. Orin Edson Entrepreneurship + Innovation Institute|Knowledge Enterprise", - "interests": "Entrepreneurship|Business|Generosity|Health", - "audiences": "Donor|Staff|Faculty|Foundation|Alumni|Community|Student|Friend/Fan", - "event_types": "", - "locations": "", - "content_type": "asu_news_article", - "field_saf": "Entrepreneurship" - } - }, - { - "node": { - "nid": "90150", - "title": "ASU ranked in top 8% of universities worldwide", - "body": "Times Higher Education rankings released today place Arizona State University in the top 8% of worldwide universities, and among the top 50 in the United States.ASU tied for No. 132 out of 1,662 institutions across the globe, a jump from its 2020 ranking of No. 184 worldwide out of 1,527.The university also ranks in the top 25% of U.S. higher education institutions, coming in at No. 45 out of 183, ahead of Rice University, the University of Arizona, Northeastern University and the University of Notre Dame.\u00a0\u201cArizona State University has sharpened its focus and used its creativity and resources to meet the challenges of the past year in a way that has advanced both our standards of excellence and our commitment to student access and success,\u201d ASU President Michael Crow said. \u201cThe resiliency that has driven our progress will continue to provide the foundation for the way we advance in an economy and in a world where universities must do more than ever, and we believe our climb in the rankings is a reflection of these efforts.\u201dThe Times Higher Education World University Rankings judge research-intensive universities across all their core missions: teaching, research, knowledge transfer and international outlook.This year\u2019s rankings reflected a significant boost to citation impact for those universities that published medical sciences research related to COVID-19, but the organization said it was unclear whether the pandemic would reshape or entrench existing hierarchies in higher education. The 2022 version of the rankings, released today, draws on data on research published between 2016 and 2020, and citations made between 2016 and 2021.David Watkins, head of data science at Times Higher Education, said the citations effect was \u201cto be expected given that COVID-19 has had such an impact worldwide.\u201d\u201cResearch into the disease, and especially work on vaccines, was heavily funded and prioritized, and some papers have attracted more than 20,000 citations within a year of publication,\u201d he said in the rankings\u2019 official release.\u201cBecause (Times Higher Education) uses a five-year window for publications, we believe that this effect will remain noticeable in the rankings for some time, and it is likely that other COVID-related effects, such as reputational impact \u2014 both positive and negative \u2014 and income, will also become visible.\u201dThe rankings\u2019 performance indicators are grouped into five pillars: teaching (the learning environment); research (volume, income and reputation); citations (research influence); international outlook (staff, students and research); and industry income (knowledge transfer). ASU\u2019s top pillar is research (No. 129).Advancing research and discovery of public value is a key part of the charter of ASU, where there is an emphasis on research with real-world impact. Earlier this year, ASU moved up to sixth out of 759 universities in the nation for total research expenditures among universities without a medical school, according to the latest National Science Foundation Higher Education Research and Development (HERD) rankings. For fiscal year 2019, ASU had nearly $640 million in research expenditures.\u201cMeasuring research expenditures is a useful proxy for measuring what really matters: impact,\u201d said Sally C. Morton, executive vice president of Knowledge Enterprise at ASU. \u201cWe measure our success not by grant dollars awarded, but by lives improved, because we believe deeply in a university\u2019s moral imperative to bring radical advancement to the communities it serves \u2014 locally, nationally and globally.\u201d]]>", - "post_date": "09/01/2021-6:05pm", - "clas_teaser": "Times Higher Education rankings released today place Arizona State University in the top 8% of worldwide universities, and among the top 50 in the United States.\r\n", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/20130307asu-sign-013-cropped.jpg?itok=cZpFuXxm", - "path": "https://news.asu.edu/20210901-university-news-times-higher-education-ranks-asu-top-8-percent-universities-worldwide", - "hide_byline": "1", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/20130307asu-sign-013-cropped.jpg?itok=cZpFuXxm", - "image_alt": "An ASU sign shown with the Fulton Center building and a church behind it", - "image_caption": "", - "related_story": "", - "news_units": "Office Of The President|Knowledge Enterprise", - "interests": "Education|New American University|Research|University", - "audiences": "Staff|Faculty|Prospective student|Student", - "event_types": "", - "locations": "", - "content_type": "asu_news_article", - "field_saf": "University News" - } - }, - { - "node": { - "nid": "90152", - "title": "Q&A with Graham Winkworth as @SunDevilSoccer Hosts Sun Devil Classic", - "body": "\nSun Devil Soccer head coach Graham Winkworth offers his thoughts as No. 21 ASU prepares to host the annual Sun Devil Classic.\n]]>", - "post_date": "09/01/2021-2:19pm", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210901-qa-graham-winkworth-sundevilsoccer-hosts-sun-devil-classic", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2021/9/1/DSC09448.jpg", - "image_url": "https://thesundevils.com/images/2021/9/1/DSC09448.jpg", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - }, - { - "node": { - "nid": "90129", - "title": "Racial discrimination linked to drinking through mental health in Black college students", - "body": "Research has shown that excessive alcohol use disproportionately affects Black American college students, who experience more negative social and health consequences from drinking than their white peers.A new study from Arizona State University and Virginia Commonwealth University has found that racial discrimination can lead to depressive symptoms and to problem alcohol consumption in Black American college students. Problem drinking is more common in college students than young adults who do not attend college, but the disparity in how alcohol use affects different people is not well understood in large part because there are few studies on minority populations. This work, which was published in Psychology of Addictive Behaviors in August, examined the pathways that lead to and protect against alcohol use problems in Black American college students.\u201cBlack American college students drink less than their peers but are more likely to have problems from drinking. Understanding what the risks and protective factors are for this group is important,\u201d said\u00a0Jinni Su, assistant professor of psychology at ASU and first author on the paper. \u201cRacial discrimination was related to worse mental health outcomes, which increased the risk for alcohol use and related problems.\u201dDiscrimination is a known stressor that can lead to using alcohol as a coping mechanism.\u00a0\u201cWhen people experience stress and lack strong coping skills or resources, they can turn to alcohol or substance abuse as a coping mechanism. We know from previous research that people who experience racial and ethnic discrimination are more likely to have problems related to excessive drinking,\u201d Su said.Racial discrimination was associated with alcohol use in two groups of Black American college students. The connection between the two was changes in mental health, in the form of increased depressive symptoms.\u201cRacial discrimination increased the risk for alcohol use and related problems, and this relationship was linked to worse mental health outcomes,\u201d Su said.The research team also examined whether how people think about their identity was protective against problem alcohol use. They assessed the impact of an individual having positive feelings about being a Black American and the belief that society at large has positive views about Black people.Participants who had positive feelings about being a Black American had a weaker link between discrimination, mental health and alcohol use. But, the association between racial discrimination, mental health and alcohol use was stronger for the participants who thought that society viewed Black people positively.\u00a0\u201cThe impact of racial discrimination is not equal for everybody; it varies in part based on beliefs about identity,\u201d Su said. \u201cWe found that participants who felt good about being Black were buffered from the effects of how discrimination might impact their mental health and drinking behavior. We also found that participants who think other people view them positively were more negatively impacted by discrimination, possibly because it is harder to understand unfair treatment.\u201dEleanor Seaton, professor in the T. Denny Sanford School of Social and Family Dynamics, also contributed to the work along with Chelsea Williams, the Spit for Science Working Group and Danielle Dick from Virginia Commonwealth University.This study was funded by the National Institute on Alcohol Abuse and Alcoholism, the National Center for Research Resources, National Institutes of Health Roadmap for Medical Research, National Institute on Drug Abuse, the Center for Tobacco Products of the U.S. Food and Drug Administration and Virginia Commonwealth University.]]>", - "post_date": "09/01/2021-1:45pm", - "clas_teaser": "A new study published in Psychology of Addictive Behaviors found racial discrimination led to depressive symptoms and to problem alcohol consumption in Black college students. Positive feelings about being a Black American were associated with a weaker link between discrimination, mental health and alcohol use.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/pexels-kampus-production-5935213.jpg?itok=Tsf7XKjI", - "path": "https://news.asu.edu/20210901-racial-discrimination-linked-drinking-through-mental-health-black-college-students", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Psychology Department", - "contributor-contact-information-name": "Kimberlee D\u2019Ardenne", - "contributor-contact-information-e-mail": "kmcclur4@asu.edu", - "contributor-contact-information-phone_number": "480-965-7598", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/pexels-kampus-production-5935213.jpg?itok=Tsf7XKjI", - "image_alt": "A group of friends drink beer together.", - "image_caption": "", - "related_story": "", - "news_units": "Department of Psychology|The College of Liberal Arts and Sciences", - "interests": "Black / African American|Psychology", - "audiences": "Faculty", - "event_types": "", - "locations": "Tempe campus", - "content_type": "news", - "field_saf": "Discoveries" - } - }, - { - "node": { - "nid": "90131", - "title": "ASU professor begins term as president of History of Economics Society", - "body": "Ross Emmett, director of Arizona State University\u2019s Center for the Study of Economic Liberty and professor at the School of Civic and Economic Thought and Leadership, recently began his term as president of the History of Economics Society, an organization dedicated to \u201cencouraging interest, fostering scholarship and promoting discussion among scholars and professionals in the field of history of economics and related disciplines.\u201dFollowing the organization\u2019s constitutional guidelines, Emmett will serve for two years as president of the society, having already served two years as the elected vice president. According to Emmett, this allows the society\u2019s elected officials a reasonable time in office for accomplishment of their most important duty: planning and hosting conferences that provide a forum for established and young scholars to advance new ideas and research. Young scholars can apply for funding for the conference through the Warren and Sylvia Samuels Fund, a resource dedicated to the promotion of young academics.\u00a0Emmett sees the History of Economics Society conferences as a great opportunity to \u201cprovide mentorship to young scholars \u2026 and have senior scholars comment on their scholarship.\u201d As a young scholar, Emmett won best dissertation in 1992 for his work, \u201cThe Economist as Philosopher: Frank H. Knight and American Social Science During the Twenties and Thirties.\u201dMore than 300 international members of the History of Economics Society will be invited to meet in Vancouver, British Columbia, in January 2023 to discuss research materials and papers in the conference curated by Emmett and his team. Conference sessions and themes are organized around papers and research proposals submitted well in advance of the meeting; this approach allows for submissions from a broad range of thoughts and ideas, rather than responses to a prescriptive call.\u00a0Per Emmett: \u201cWe want everyone to attend. We get Marxists and libertarians, conservatives and liberals, historians and philosophers.\u201d Emmett, a self-described member of \"the dismal science,\" corrected the author\u2019s admiration for this egalitarian approach, insisting that pragmatism is the driving factor. He is an economist, after all.Since joining ASU in 2018, Emmett has taught three different history of economics-related courses and is regularizing a cross-listed history of economic thought course with the School of Civic and Economic Thought and Leadership and the W. P. Carey Department of Economics. As director of the Center for the Study of Economic Liberty, Emmett organized and held the Winter Institute for the History of Economic Thought in 2019 and 2020, and he is planning for the 2022 offering. Additionally, center researchers publish an annual report titled \u201cDoing Business North America,\" a project that \u201cannually provides objective measures of the scale and scope of business regulations in 130 cities across 92 states, provinces and federal districts of the U.S., Canada and Mexico. It uses these measures to score and rank cities in regard to how easy or difficult it is to set up, operate and shut down a business.\u201dA fuller description of Emmett\u2019s research and teaching can be found in his ASU profile.\u00a0Most recently, Emmett has started a new research program, Economists on the Indigenous Peoples of North America, with the first foray into the subject being a paper titled \u201cFrank Amasa Walker and the Indigenous Peoples of North America.\u201d This will be presented at the January 2022 Allied Social Science Association meeting, which is the largest gathering of economists globally, with more than 13,000 attendees.]]>", - "post_date": "09/01/2021-1:42pm", - "clas_teaser": "ASU Professor Ross Emmett recently began his term as president of the History of Economics Society, an organization dedicated to \u201cencouraging interest, fostering scholarship and promoting discussion among scholars and professionals in the field of history of economics and related disciplines.\u201d", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/3283604.png?itok=Y_6kE_ni", - "path": "https://news.asu.edu/20210901-asu-professor-begins-term-president-history-economics-society", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Center for the Study of Economic Liberty", - "contributor-contact-information-name": "Mason Hunt, MPA", - "contributor-contact-information-e-mail": "mchunt1@asu.edu", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/3283604.png?itok=Y_6kE_ni", - "image_alt": "portrait of ASU Professor Ross Emmett", - "image_caption": "", - "related_story": "", - "news_units": "School of Civic and Economic Thought and Leadership|The College of Liberal Arts and Sciences|Center for the Study of Economic Liberty|W. P. Carey School of Business", - "interests": "Entrepreneurship|Business|Humanities|Social science|Research", - "audiences": "Staff|Faculty|Alumni|Community|Student", - "event_types": "", - "locations": "Tempe campus", - "content_type": "news", - "field_saf": "University News" - } - }, - { - "node": { - "nid": "90135", - "title": "Hundreds of AZ students are college ready, thanks to ASU summer camps", - "body": "College readiness, career inspiration and Sun Devil pride were on full display at the nine Access ASU summer camps that reached 726 middle and high schoolers in June and July.\u00a0Every summer, GEAR UP, Clubes de Ciencia, Poly Summer Leadership Academy, Summer Experience at West, Inspire, Barrett Summer Scholars, First Star, the C\u00e9sar E. Ch\u00e1vez Leadership Institute and the RISE Leadership Institute share resources and information that students need to be prepared for college financially, socially and academically.\u00a0For the past two years, the format was adapted to a virtual environment, but in previous years, the events ranged from day camps to residential college experiences, which allow students to live on campus for a week. All participants get a taste of campus life while also receiving hands-on instruction in both academics and college readiness. Faculty, community and ASU student support helps make the camps enriching experiences that students don\u2019t soon forget.\u00a0Current ASU student Emilia Ba\u00f1uelos, a speech and hearing sciences and family and human development major, was a 2016 C\u00e9sar E. Ch\u00e1vez Leadership Institute participant. After her experience at the camp, she said she was inspired to serve as a facilitator.\u201cWhen I saw that Access ASU was hiring summer mentors for various programs, including (C\u00e9sar E. Ch\u00e1vez Leadership Institute), I applied within the hour because these programs not only create leaders but a supportive community,\u201d Ba\u00f1uelos said.\u00a0She remembers her time at the\u00a0C\u00e9sar E. Ch\u00e1vez Leadership Institute fondly. What particularly stuck out was learning to avoid making assumptions, how first impressions matter and the power of a firm handshake. Ba\u00f1uelos said that the camp helped her realize all the opportunities that ASU students have to make a difference in the community.\u00a0\u201cAttending these programs sets you in motion to be a high-achieving student, a leader in your community,\u201d she said.\u00a0Post-event surveys confirm that participants leave with tremendous knowledge and inspiration about what it takes to get to college and what fields they want to consider.\u00a0 Surveys reveal it\u2019s nearly unanimous that respondents feel prepared and confident in the steps they need to take to prepare for college, to pay for college and to prepare for the admissions process.\u00a0Applications open in December for the 2022 programs. Camps are available at all four ASU campuses and involve a variety of hands-on coursework and training in leadership, engineering, critical thinking, electives, financial aid presentations and much more. Tours of the four unique campuses and tips about ASU resources, such as me3, an online tool that helps students connect their interests to future careers and fields of study, are also included.One Barrett Summer Scholars participant said that they were excited for the work and also the information about becoming a Sun Devil.\u00a0\u201cBarrett Summer Scholars was an amazing experience because I learned so much in my classes, and I also learned about ASU as a school. I would like to go to ASU someday, (and I enjoyed) seeing the campus through the virtual tours. I am so glad that I got to attend Barrett Summer Scholars, and I hope I get to next year,\u201d they wrote.\u00a0Access ASU\u2019s summer camps serve many Arizona students, and recruitment includes special outreach to first-generation and underrepresented students. Scholarships are available for all camps and summer programs.The camps all offer scholarships.Summer Experience at West is a program at ASU\u2019s West campus that was launched by Jose E. N\u00e1\u00f1ez Sr., ASU President\u2019s Professor of psychology, to help prepare students for college life, inspired by his own experience of having no role models as a Mexican American student.\u00a0\n Video of PSLA - Summer 2021 Wrap-Up\n\nVideo courtesy of Educational Outreach and Student ServicesChaparral High School junior Renee Hsu was a Summer Experience at West participant in 2021 who appreciated being surrounded by people who had similar interests and motivation.\u201cI thoroughly enjoyed my professors, who have incredible experience and an amazing amount of knowledge that I am so grateful that they were willing to share with us,\u201d she said.\u00a0Some of the academic topics covered at Summer Experience at West included neuroscience, law, forensics and STEM.The Poly Summer Leadership Academy is usually held on ASU\u2019s Polytechnic campus in Mesa, and the virtual event in 2021 included panel discussions led by ASU students and faculty, in addition to college-level academic content on attachment, psychotherapy, education and sustainability. \u00a0One survey respondent said that they especially appreciated the tips to get motivated and establish good habits.\u00a0\u201cI received so much information, resources and instruction about getting to college, picking my major, finding my niche in life, earning scholarships and helping me build a career path with a solid set of goals,\u201d they said. \u201cPoly Summer Leadership Academy truly was a major stepping stone to creating future leaders of all different types of backgrounds wherever they may go.\u201dMarcelino Quin\u0303onez,\u00a0Access ASU director of community outreach and partnerships,\u00a0said that this year\u2019s camps showed the resilience and motivation of the next generation of college students.\u201cThe resources to get to college are out there, and the talent of Arizona students is something that always inspires us,\u201d Quin\u0303onez said. \u201cASU has so much to offer students to inspire and assist them in being college ready, and seeing these students\u2019 persistence after the learning disruptions we\u2019ve seen is simply amazing.\u201dSomething that is emphasized at each camp, aside from getting into college and exploring majors, is the idea of creating leaders and people who can improve their communities. As an alum of the C\u00e9sar E. Ch\u00e1vez Leadership Institute, Ba\u00f1uelos saw that idea come full circle in the students who led at the camp this year.\u00a0\u201cMy biggest takeaway from being a summer program leader was seeing the programs transform students into student leaders. I thought it was amazing how in only a week, students who were shy were leading the conversation,\u201d Ba\u00f1uelos said. \u201cThese programs are an amazing opportunity to learn more about ASU and prepare you for greatness. When you do attend the wonderful Access ASU programs, do not be afraid to put yourself out there.\u201dRegistration for 2022 camps begins in December. Follow Access ASU on Facebook, Instagram and Twitter for updates when applications open.]]>", - "post_date": "09/01/2021-1:22pm", - "clas_teaser": "College readiness, career inspiration and Sun Devil pride were on full display at the nine Access ASU summer camps that reached 726 middle and high schoolers in June and July. ", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/access_asu_summer_camps_2021_0.jpg?itok=IJ7yxI6T", - "path": "https://news.asu.edu/20210901-hundreds-az-students-are-college-ready-thanks-asu-summer-camps", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Educational Outreach and Student Services", - "contributor-contact-information-name": "Hannah Moulton Belec", - "contributor-contact-information-e-mail": "Hmoulton@asu.edu", - "contributor-contact-information-phone_number": "480-965-4255", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/access_asu_summer_camps_2021_0.jpg?itok=IJ7yxI6T", - "image_alt": "Young woman in a mask in front of a laptop with a Zoom screen visible on the wall behind her.", - "image_caption": "", - "related_story": "", - "news_units": "Educational Outreach and Student Services", - "interests": "Education|Family activities", - "audiences": "Prospective student", - "event_types": "", - "locations": "Downtown Phoenix campus|Polytechnic campus|Tempe campus|West campus", - "content_type": "news", - "field_saf": "Sun Devil Life" - } - }, - { - "node": { - "nid": "90132", - "title": "ASU Alumni Association announces new board of directors and National Alumni Council members", - "body": "The Arizona State University Alumni Association, which has been named the No. 1 largest Phoenix-area networking association for the eighth consecutive year by the Phoenix Business Journal, recently announced its 2021\u201322 Board of Directors and National Alumni Council. Terms for all members of both groups began July 1.\u201cMembers of the board and council bring their diverse professional expertise and passion for their alma mater as they provide guidance and serve as ambassadors for the Alumni Association and the university,\u201d said Christine Wilkinson, president and CEO of the organization. \u201cI look forward to working with this talented group of business and community leaders as we focus on advancing the university, enhancing the alumni experience, honoring traditions and strengthening alumni leadership.\u201dBoard/Council OfficersThe following persons were named to serve as officers of the board of directors and the National Alumni Council.Chair: Kristine Kassel, \u201991 BSA successful business owner, Kassel joined the ASU Alumni Association National Alumni Council in 2013 and the board of directors in 2016. Kassel\u2019s business, Benefits by Design, an Arizona-based insurance agency, has focused on group employee benefits for over two decades. For her business and community service she has received several awards, including Tempe Business Woman of the Year, NAWBO Business Owner of the Year and recognition as an honoree of the Sun Devil 100 two years in a row. She has served as board chair of the Tempe Chamber of Commerce, Gabriel\u2019s Angels, the Phoenix chapter of the National Association of Women Business Owners, Arizona Association of Health Underwriter\u2019s and the Arizona Small Business Association.\u201cASU continues to be innovative in academics, research, philanthropy and more,\u201d Kassel said. \u201cThe Alumni Association works hard to create programs and opportunities to engage alumni and connect them back to their alma mater and with fellow alums. My goal is to bring further awareness to the amazing programs, projects and accomplishments of the university and the association to our Sun Devil alumni around the globe.\u201dChair-elect: Gregg Brewster, \u201983 BABrewster brings a long and successful history in logistics and supply chain management and is currently regional vice president of sales and commercial services for Owens & Minor, a global health care company providing expertise in health care, supply chain management, logistics and transportation. Brewster joined the association\u2019s board of directors in August 2016 and has actively supported the mission while focusing on the board\u2019s state government engagements as the chair of the legislative committee. He and his wife, Tracy, have two daughters who are also Sun Devil alums, following a Brewster family tradition of ASU graduations dating back to 1956.Treasurer: Joya Kizer, \u201904 BSKizer has served for 20 years as president and chief executive officer of CASA Unlimited Enterprises, a family owned ACDBE-certified airport concessionaire business that owns, operates and/or serves as joint venture partnerships in four metropolitan airports. She is involved in all aspects of the airport operations, management, business development and decision-making, and she continues to build growth opportunities for the business. Her recognitions include being a Sun Devil 100 honoree for the past three years, a recipient of the Univision Radio Phoenix, and Valle del Sol \u201c40 Hispanic Leaders Under 40\u201d for her excellence in business leadership, and she is a member of the National Association of Professional Women.Past-chair: Chris Hill, \u201986 BSHill has served in numerous roles since joining the board in 2015, including being a part of the ASU Alumni Veterans Chapter and participating in the selection of scholarship award recipients and ASU Leadership Institute participants. He is a distinguished military graduate from ASU\u2019s nationally ranked ROTC program and served in the Army as a medical service corps officer for 20 years. Hill subsequently served as CEO for several large health care organizations and is currently the senior managing director of Treadstone Management. ASU Alumni Association Board of DirectorsThe following alums were elected to their first three-year term on the association\u2019s board of directors, effective July 1:Michael Angulo, \u201906 BSAngulo, a supply chain management professional at Salt River Project with more than 15 years of experience, joined the National Alumni Council in July 2019. In addition to his valued service to the council, Angulo\u2019s support of the university and alumni association includes past leadership roles with the ASU Hispanic Business Alumni chapter as scholarship chair and events committee participant, as well as a sponsorship and major events adviser to the group. Angulo serves on the board of the East Valley Hispanic Chamber of Commerce and the Associated Minority Contractors of Arizona, plus committee and operations support for St. Mary\u2019s Food Bank and the Pacific Southwest Minority Supplier Development Council.Paul Kent, \u201988 BSKent is the proprietor of Flagstaff Extreme and is dedicated to the ongoing growth, well-being and advancement of Arizona\u2019s communities. He serves on the University Park Neighborhood Association, the Northern Arizona University Curriculum Advisory Board for Parks and Recreation, the city of Tempe Mayor\u2019s Commission on Disability Concerns, All Saints Catholic Newman Center\u2019s Finance Council and the Arc of Tempe Board of Directors.\u00a0In past years, Kent has volunteered as a Tillman Scholar mentor at ASU and at Maggie\u2019s Place, a non-profit organization providing homes for homeless pregnant women.Clarence McAllister, \u201991 BS, \u201997 MSMcAllister is a multicultural, multilingual business leader with more than 30 years of experience in technology and construction. Having started several companies, he is the CEO and founder of Fortis Engineers, a world-class electrical and mechanical engineering firm based in Phoenix.\u00a0He was appointed by Gov. Doug Ducey to the Arizona Board of Technical Registration and serves on the Arizona Supreme Court Alternative Business Structure Board.\u00a0McAllister is an active member of Greater Phoenix Leadership, the Young President Organization, and the honorary board of advisors for addiction and PTSD treatment center Warriors Heart. Additionally, he chairs the board of both the small business microlender DreamSpring and Western Rare Earth.\u00a0His son is a May 2020 graduate of the Ira A. Fulton Schools of Engineering.Jen Scrivner, \u201904 MBAAs the COO of Goodmans Interior Structures, one of the first certified B Corporations in Arizona, Scrivner is passionate about education and believes it\u2019s the key to changing an individual\u2019s circumstances for the better.\u00a0Scrivner is grateful for the experience of her MBA program and sought out opportunities to further the university\u2019s charter and was accepted into the ASU Leadership Institute in 2019.\u00a0Her roles in the community include dedicating time and energy to the Northwest Maricopa Regional Council of First Things First, Valley of the Sun United Way, Conscious Capitalism Senior Leader Network, T-Gen ambassadorship, Joyride Society and KNOW Women Phoenix.\u00a0The four new board members join the following board members: Ray Artigue, \u201976 BA; Latasha Causey, \u201902 BIS; Rick Dircks, \u201982 BS; Lisa Fernandez, \u201909 BS; Tim Gertz, \u201901 BS, \u201902 MAIS; Luis Heredia, \u201998 BS; Mark Kerrigan, \u201974 BS; Jim Lodge, \u201987 MBA; Kirk McClure, \u201901 BSD, \u201910 MBA; Mike McGuirk, \u201982 BS; Stephanie Mitrovic, \u201909 MArch; and Michael Tully, \u201987 BS, \u201991 MBA.ASUAA National Alumni CouncilFive ASU alums were named to their first three-year terms on the Alumni Association\u2019s National Alumni Council:Rick Bowers, \u201992 BS, president, TTI Success Insights.Barbara Dickerson, \u201975 MA, administrator, Canyon City Foundation.Giovanna Grijalva, \u201900 MEd, \u201909 EdD, client relations director, ADM Group.Rayme Lofgren, \u201906 BIS, senior director of marketing, Arizona Diamondbacks.Bob Mulhern, \u201981 BA, \u201983 MBA, senior managing director, Greater Phoenix, Colliers International.These new members join the following on the National Alumni Council: Suzanne Durkin-Bighorn, \u201904 BS, \u201918 MNLM; Andi Fourlis, \u201992 BAE, \u201911 EdD; Samantha Hagan, \u201993 BS; Eden Higgins, \u201987 BA; Tim Huish, \u201998 BS; Valerie Jedlicki, \u201909 BS; Chad Kolodisner, \u201991 BS; Rick Nakazawa, \u201985 BS; Ira Rotenberg, \u201995 BS, \u201900 MBA; Shaney Salomon, \u201914 BIS; Ty Triplett, \u201976 BS; Andrew Vandertoorn, \u201902 BS; and Jennifer Ward, \u201903 BS, \u201903 BS.]]>", - "post_date": "09/01/2021-11:20am", - "clas_teaser": "The Arizona State University Alumni Association recently announced its 2021-2022 Board of Directors and National Alumni Council. Terms for all members of both groups began July 1.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/asu_alumni_04_-_small.jpg?itok=580Q-rXa", - "path": "https://news.asu.edu/20210901-asu-alumni-association-announces-new-board-directors-and-national-alumni-council-members", - "hide_byline": "0", - "contributor-contact-information-affiliation": "ASU Alumni Association", - "contributor-contact-information-name": "Morgan Harrison", - "contributor-contact-information-e-mail": "mhernqui@asu.edu", - "contributor-contact-information-phone_number": "480-727-7106", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/asu_alumni_04_-_small.jpg?itok=580Q-rXa", - "image_alt": "group photo of members of the ASU Alumni Association Board of Directors and National Alumni Council for the 2021-2022 academic year", - "image_caption": "", - "related_story": "", - "news_units": "Alumni Association", - "interests": "", - "audiences": "Corporation|Alumni|Community", - "event_types": "", - "locations": "Tempe campus", - "content_type": "news", - "field_saf": "University News" - } - }, - { - "node": { - "nid": "90143", - "title": "ASU engineer harnesses microwaves to improve imaging systems", - "body": "Microwaves do more than just heat up your leftovers. These invisible waves of electromagnetic radiation can also be used in a variety of imaging applications.From observing the Earth\u2019s climate and surface topography to screening baggage at airports, microwaves can help us see things that may be hidden. That is because different frequencies of microwaves can penetrate objects that would normally block our vision in the visible light spectrum \u2014 such as clouds or the outer layer of a suitcase.However, the limitations of microwave imaging are the unfavorable size, weight, power consumption and cost of the systems. These factors limit their use to applications such as bulky scanners at airports or large satellites in orbit.Mohammadreza Imani, an assistant professor of electrical engineering in the Ira A. Fulton Schools of Engineering at Arizona State University, is looking to overcome these challenges by making microwave imaging systems more portable and accessible.His research focuses on one particular area of opportunity for microwave imaging: using metamaterials \u2014 engineered combinations of elements that have unique patterns or structures not found in nature \u2014 to simplify microwave imaging hardware and make it easier to generate images.Imani shares how to improve microwave imaging using metamaterials and how this new way of \u201cseeing\u201d can help inspect buildings, aid in search and rescue efforts, speed up airport security and even assist in our own home improvement projects.Mohammadreza Imani, an assistant professor of electrical engineering in the Ira A. Fulton Schools of Engineering at Arizona State University, researches microwave imaging using metamaterials. These combinations of elements engineered to have patterns and structures not found in nature can make imaging systems smaller, lighter, cheaper and more functional. Photo by Deanna Dent/Arizona State UniversityQuestion: What is the big challenge with microwave imaging systems that you\u2019re addressing?Answer:\u00a0The complexity of current imaging systems comes from multiple factors. A crucial one is the antenna hardware.Think of a security screening system at an airport. This system has a mechanical arm equipped with many antennas that scan around the person. Though it can capture fantastic images, it is expensive, bulky and slow.Metamaterials offer a simple way to get several measurements of a particular scene of interest. In essence, they can alleviate the need for mechanical movement or using very large antenna arrays.Metamaterials consist of many inclusions, or encapsulated compounds in the material\u2019s structure, that resonate when an electromagnetic wave interacts with them. Their resonance response determines the patterns generated.By designing metamaterial structures that exhibit specific resonance responses as a function of frequency or electronic signals, we can generate diverse patterns that interrogate the scene of interest and obtain several measurements of the scene.In this manner, we can replace multiple antennas with a single one that is fashioned with metamaterial structures. When we reduce the number of antennas and mechanical motion, we can make imaging systems faster and simpler.Q: How else do metamaterials address microwave imaging challenges?A:\u00a0We can leverage metamaterials\u2019 dynamic and diverse interactions with a scene to obtain enough measurements to form an image even at a single frequency. Other imaging systems use several frequencies over a wide bandwidth.Bandwidth, or the range of electromagnetic frequencies used, is usually a huge bottleneck in microwave imaging. Many devices operate in microwaves \u2014 ranging from microwave ovens to reconnaissance radars. When using a large bandwidth for microwave imaging, we increase the chances of interfering with other devices. Wide bandwidth also usually increases the complexity and cost of the devices. Further, in the case of imaging through surfaces, using wideband electromagnetic waves can cause image distortion.Using a single frequency, or a very narrow bandwidth, solves the problems associated with wide bandwidth, which is where my research comes into play. Reconfigurable metamaterials provide a simple means to realize imaging systems that use a single frequency.Q: What is innovative about your approach to developing imaging technology?A:\u00a0The innovation at the heart of using metamaterials to obtain diverse measurements comes from the application of computational imaging. In this framework, computational post-processing is used to retrieve the desired image instead of relying solely on hardware components.This change of roles has immense benefits in terms of imaging speed and hardware complexity, and it allows us to use unconventional hardware such as metamaterials.Q: Can you \u201csee\u201d through walls with this technology?A: Seeing through layers has always been a fascinating idea in science fiction. However, that is not quite what is possible when we use microwave imaging to see through opaque layers. The resulting images may not easily be interpretable by the human eye but the measurements they produce carry a lot of information.The images obtained using microwave imaging can be used to examine structural integrity, detect structural damages, such as water leaks and cracks; locate items of interest, such as pipes or studs; or find and count the number of humans beyond walls, for example, in rescue operations. You can also use microwave imaging to inspect packages or backpacks, for example, to detect concealed threats.Q: How can microwave imaging technology be used to ensure infrastructure is safe?A: Microwave imaging enables nondestructive and usually noncontact evaluation and monitoring of different layers of infrastructure.It can even provide information about optically invisible layers. For example, it can detect cracks that may be invisible to the eye or provide information about a crack\u2019s depth. If we can make a microwave imaging system more easily deployable and portable, it can be used in a range of applications, like examining building integrity and aiding restoration and repair projects.Q: How can it be used in search and rescue operations?A: Microwaves are reflected by the human body, but they go through most other optically opaque layers, such as buildings and fabric. That means a microwave imaging device can in principle locate a person by \u201clooking through\u201d walls or wreckage in disasters such as earthquakes, building collapses or fires. To bring these potentials to widespread use, we need to improve the speed and size of microwave imaging systems, as well as associated image processing techniques.Q: How else could this imaging technology improve people\u2019s lives?A: If we can speed up data acquisition, we can eliminate the need for the stop-and-go process used currently at airports. Microwave imaging can be used in crowded places like stadiums, train stations, schools, hospitals and more as a simple and fast security screening device. As passengers pass through a corridor, the microwave imaging system can detect suspicious objects. Keep in mind that microwave imaging can also see through backpacks and luggage. Fast and affordable microwave imaging may thus detect potential threats concealed in backpacks in crowded areas, without requiring everyone to take them off.On a domestic basis, a fast and deployable microwave imaging system can be used in DIY projects, repairs, inspections or just for fun. When starting a DIY home improvement project, or doing an inspection of a house, one wants to know about the material behind walls, roofs or ceilings. For example, has the insulation settled down? Is there water leakage? Is there a pipe or wire behind the wall I am about to drill into? An affordable microwave camera can provide the answers to such problems.One can also just use the microwave camera for fun applications, such as checking how your foot fits within a shoe or discovering the contents of your gifts before opening them.Top image courtesy of Pexels.]]>", - "post_date": "09/01/2021-10:37am", - "clas_teaser": "Mohammadreza Imani is advancing microwave imaging by using uniquely patterned metamaterials for a faster and simpler way to \"see\" through barriers.", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/article_heros/imani_microwaveinfrastructurepexelssm.jpg?itok=WThAcmLP", - "path": "https://news.asu.edu/20210901-solutions-asu-engineer-harnesses-microwaves-improve-imaging-systems", - "hide_byline": "0", - "contributor-contact-information-affiliation": "Ira A. Fulton Schools of Engineering", - "contributor-contact-information-name": "Monique Clement", - "contributor-contact-information-e-mail": "mdevoe1@asu.edu", - "contributor-contact-information-phone_number": "480-727-1958", - "contributor-contact-information-campus": "Tempe campus", - "feed_image_link": "", - "image_url": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/article_heros/imani_microwaveinfrastructurepexelssm.jpg?itok=WThAcmLP", - "image_alt": "New York City skyline", - "image_caption": "", - "related_story": "", - "news_units": "School of Electrical, Computer, and Energy Engineering|Ira A. Fulton Schools of Engineering", - "interests": "Engineering|Expert Q-and-A", - "audiences": "Faculty", - "event_types": "", - "locations": "", - "content_type": "featured_article", - "field_saf": "Solutions" - } - }, - { - "node": { - "nid": "90151", - "title": "Briggs Named Diving Coach, Promoted From Assistant Role", - "body": "\nHead coach Bob Bowman has named Marc Briggs, once a Sun Devil diver and formerly a volunteer assistant, as the team's new diving coach.\n]]>", - "post_date": "09/01/2021-8:56am", - "clas_teaser": "", - "teaser": "", - "story_images": "", - "hero_image": "https://news.asu.edu/sites/default/files/styles/asu_news_article_hero/public/default_images/asu-watermark-lg-dk_0_0.png?itok=NqZCO26e", - "path": "https://news.asu.edu/20210901-briggs-named-diving-coach-promoted-assistant-role", - "hide_byline": "0", - "feed_image_link": "https://thesundevils.com/images/2019/9/5/PV6_4086_1.JPG", - "image_url": "https://thesundevils.com/images/2019/9/5/PV6_4086_1.JPG", - "image_alt": "", - "image_caption": "", - "related_story": "", - "news_units": "Sun Devil Athletics", - "interests": "", - "audiences": "", - "event_types": "", - "locations": "", - "content_type": "news", - "field_saf": "Athletics" - } - } - ] -} diff --git a/packages/components-core/__mocks__/fetch-mock.js b/packages/components-core/__mocks__/fetch-mock.js deleted file mode 100644 index 1c7932adba..0000000000 --- a/packages/components-core/__mocks__/fetch-mock.js +++ /dev/null @@ -1,10 +0,0 @@ -// @ts-check -import fetchMock from "jest-fetch-mock"; - -import * as data from "./data/feed-anatomy-search.json"; - -fetchMock.enableMocks(); -fetchMock.mockResponse(() => { - const res = { ...data }; - return Promise.resolve(JSON.stringify(res)); -}); diff --git a/packages/components-core/__mocks__/window-mock.js b/packages/components-core/__mocks__/window-mock.js deleted file mode 100644 index e602f5cf64..0000000000 --- a/packages/components-core/__mocks__/window-mock.js +++ /dev/null @@ -1,8 +0,0 @@ -// @ts-check -Object.defineProperty(window, "matchMedia", { - value: (matches = true) => ({ - matches, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - }), -}); diff --git a/packages/components-core/babel.config.js b/packages/components-core/babel.config.js deleted file mode 100644 index 4fe412bbb5..0000000000 --- a/packages/components-core/babel.config.js +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = api => { - api.cache(true); - - return { - presets: [ - ["@babel/preset-env", { targets: { node: "current" } }], - "@babel/preset-react", - ], - plugins: [ - "@babel/plugin-syntax-jsx", - "@babel/plugin-transform-react-jsx", - "@babel/plugin-transform-runtime", - ], - ignore: ["node_modules"], - env: { - test: { - plugins: [ - "@babel/plugin-syntax-jsx", - "babel-plugin-dynamic-import-node", - "@babel/plugin-transform-react-jsx", - ], - presets: [ - [ - "@babel/preset-env", - { - targets: { node: "current" }, - }, - ], - "@babel/preset-react", - ], - }, - }, - }; -}; diff --git a/packages/components-core/docs/README.props.md b/packages/components-core/docs/README.props.md deleted file mode 100644 index b1b237bbe0..0000000000 --- a/packages/components-core/docs/README.props.md +++ /dev/null @@ -1,278 +0,0 @@ -## Constants - -
    -
    AnchorMenu ⇒ JSX.Element
    -
    -
    Article ⇒ JSX.Element
    -
    -
    Button ⇒ JSX.Element
    -
    -
    ButtonIconOnly ⇒ JSX.Element
    -
    -
    ButtonTag ⇒ JSX.Element
    -
    -
    Card ⇒ JSX.Element
    -
    -
    Image ⇒ JSX.Element
    -
    -
    Pagination ⇒ JSX.Element
    -
    -
    - -## Components - -
    -
    Accordion(props) ⇒ JSX.Element
    -
    -
    Hero(props) ⇒ JSX.Element
    -
    -
    Testimonial(props) ⇒ JSX.Element
    -
    -
    Video(props) ⇒ JSX.Element
    -
    -
    - -## Typedefs - -
    -
    ArticleProps : Object
    -
    -
    CardProps : Object
    -
    -
    FeedHeader : Object
    -
    -
    FeedCtaButton : Object
    -
    -
    FeedCardButton : Object
    -
    -
    DataSource : Object
    -
    -
    FeedType : Object
    -
    -
    HeroProps : Object
    -
    -
    ImageComponentProps : Object
    -
    -
    ButtonProps : Object
    -
    -
    ButtonIconOnlyProps : Object
    -
    -
    TagsProps : Object
    -
    -
    BreadcrumbProps : Object
    -
    -
    ImageProps : Object
    -
    -
    ContentProps : Object
    -
    -
    AccordionCard : Object
    -
    -
    AccordionProps : Object
    -
    -
    ReactMouseEvent ⇒ void
    -
    -
    AccordionCardItemProps : Object
    -
    -
    AnchorMenuItem : Object
    -
    -
    AnchorMenuProps : Object
    -
    -
    PageChangeEvent ⇒ void
    -
    -
    PaginationProps : Object
    -
    -
    PageItemProps : Object
    -
    -
    TestimonialStyle : Object
    -
    -
    TestimonialQuote : Object
    -
    -
    TestimonialProps : Object
    -
    -
    VideoProps : Object
    -
    -
    - - - -## AnchorMenu ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | [AnchorMenuProps](#AnchorMenuProps) | - - - -## Article ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | [ArticleProps](#ArticleProps) | - - - -## Button ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | [ButtonProps](#ButtonProps) | - - - -## ButtonIconOnly ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | [ButtonIconOnlyProps](#ButtonIconOnlyProps) | - - - -## ButtonTag ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | ButtonTagProps | - - - -## Card ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | [CardProps](#CardProps) | - - - -## Image ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | [ImageComponentProps](#ImageComponentProps) | - - - -## Pagination ⇒ JSX.Element -**Kind**: global constant - -| Param | Type | -| --- | --- | -| props | [PaginationProps](#PaginationProps) | - - - -## Accordion(props) ⇒ JSX.Element -**Kind**: global function - -| Param | Type | -| --- | --- | -| props | [AccordionProps](#AccordionProps) | - - - -## Hero(props) ⇒ JSX.Element -**Kind**: global function - -| Param | Type | -| --- | --- | -| props | [HeroProps](#HeroProps) | - - - -## Testimonial(props) ⇒ JSX.Element -**Kind**: global function - -| Param | Type | -| --- | --- | -| props | [TestimonialProps](#TestimonialProps) | - - - -## Video(props) ⇒ JSX.Element -**Kind**: global function - -| Param | Type | -| --- | --- | -| props | [VideoProps](#VideoProps) | - - - -## ArticleProps : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| type | "event" \| "news" | -| [articleUrl] | string | -| [publicationDate] | string | -| [title] | string | -| [body] | string | -| [authorEmail] | string | -| [authorName] | string | -| [authorPhone] | string | -| [authorTitle] | string | -| [breadcrumbs] | [Array.<BreadcrumbProps>](#BreadcrumbProps) | -| [calendarUrl] | string | -| [eventLocation] | string | -| [eventTime] | string | -| [headerImageUrl] | string | -| [registrationUrl] | string | -| [zoomUrl] | string | - - - -## CardProps : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| [type] | string | -| [horizontal] | boolean | -| [image] | string | -| [imageAltText] | string | -| title | string | -| [icon] | Array.<string> | -| [body] | string | -| [eventLocation] | string | -| [eventTime] | string | -| [linkLabel] | string | -| [linkUrl] | string | -| [buttons] | [Array.<ButtonProps>](#ButtonProps) | -| [eventFormat] | "stack" \| "inline" | -| [width] | "25%" \| "50%" \| "75%" \| "100%" | -| [tags] | [Array.<TagsProps>](#TagsProps) | -| [showBorders] | boolean | -| [cardLink] | string | - - - -## FeedHeader : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| [color] | "white" \| "dark" | -| [text] | string | - - - -## FeedCtaButton : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| [color] | "gold" \| "maroon" \| "gray" \| "dark" | -| [text] | string | -| [url] | string | - - diff --git a/packages/components-core/examples/accordion.html b/packages/components-core/examples/accordion.html deleted file mode 100644 index 27e9caa87d..0000000000 --- a/packages/components-core/examples/accordion.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - Anchor Menu Component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    -
    - - - - - - diff --git a/packages/components-core/examples/anchorMenu.html b/packages/components-core/examples/anchorMenu.html deleted file mode 100644 index a8993da247..0000000000 --- a/packages/components-core/examples/anchorMenu.html +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - - - Anchor Menu Component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    - -
    -

    First container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. - Nulla ullamcorper odio id risus pretium, sed ornare turpis luctus. - Integer ac semper ex, ac convallis lorem. Orci varius natoque - penatibus et magnis dis parturient montes, nascetur ridiculus mus. - In eleifend lobortis lacus sed efficitur. Ut rhoncus consectetur - accumsan. Nulla massa lorem, maximus eu venenatis in, ultrices sit - amet purus. Duis et rutrum mi, id dictum risus. Fusce suscipit metus - congue, fermentum sem non, molestie sem. Donec tortor quam, - imperdiet vel dui at, finibus vestibulum neque. Nulla facilisi. Sed - fermentum vulputate enim, at aliquam diam tincidunt vitae. Nunc - eleifend volutpat pellentesque. Aliquam consectetur enim ac lectus - dapibus bibendum. Cras eget erat magna. Donec turpis quam, efficitur - in leo in, elementum interdum lorem. Duis tempor tempor nulla, at - faucibus leo commodo vel. Nunc in tincidunt diam, ac ultrices - mauris. Nunc quis vehicula tellus. Mauris vehicula gravida eros, - eget facilisis elit viverra faucibus. Sed viverra rhoncus erat, eget - dignissim orci tincidunt et. Nullam sodales elit eu metus lobortis, - ut posuere lectus convallis. Maecenas id lectus id ligula bibendum - congue ac ut risus. Integer eget neque volutpat, mollis nisi sed, - lobortis mauris. In est metus, rhoncus in tristique nec, blandit sed - mauris. Maecenas non risus mauris. Nunc pretium urna sit amet sapien - venenatis mollis. Sed facilisis libero nisi, fermentum malesuada - magna commodo at. Phasellus sed hendrerit ipsum. Donec ultrices - sodales posuere. Proin a finibus mauris, non ornare arcu. Donec eget - cursus augue. Nulla vel porta massa. Ut hendrerit mauris a suscipit - ullamcorper. Donec sagittis tellus non ultrices feugiat. Mauris - sodales, eros id vulputate porttitor, nunc augue aliquet tortor, - eget mattis turpis mauris nec enim. Proin ac rhoncus lacus. Aliquam - venenatis lorem ac arcu commodo tristique id eget dolor. Nam gravida - nisi vel purus interdum, faucibus suscipit arcu placerat. Phasellus - congue velit et quam elementum, a fermentum velit efficitur. Nunc - lobortis, lacus at faucibus vestibulum, mauris nisl facilisis odio, - at elementum nisi mauris nec tortor. Nullam commodo pellentesque - ante ac porttitor. Lorem ipsum dolor sit amet, consectetur - adipiscing elit. Sed feugiat neque magna. Suspendisse potenti. Duis - dictum ac elit at elementum. In sit amet hendrerit lacus. -

    -
    -
    -

    Second container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. - Nulla ullamcorper odio id risus pretium, sed ornare turpis luctus. - Integer ac semper ex, ac convallis lorem. Orci varius natoque - penatibus et magnis dis parturient montes, nascetur ridiculus mus. - In eleifend lobortis lacus sed efficitur. Ut rhoncus consectetur - accumsan. Nulla massa lorem, maximus eu venenatis in, ultrices sit - amet purus. Duis et rutrum mi, id dictum risus. Fusce suscipit metus - congue, fermentum sem non, molestie sem. Donec tortor quam, - imperdiet vel dui at, finibus vestibulum neque. Nulla facilisi. Sed - fermentum vulputate enim, at aliquam diam tincidunt vitae. Nunc - eleifend volutpat pellentesque. Aliquam consectetur enim ac lectus - dapibus bibendum. Cras eget erat magna. Donec turpis quam, efficitur - in leo in, elementum interdum lorem. Duis tempor tempor nulla, at - faucibus leo commodo vel. Nunc in tincidunt diam, ac ultrices - mauris. Nunc quis vehicula tellus. Mauris vehicula gravida eros, - eget facilisis elit viverra faucibus. Sed viverra rhoncus erat, eget - dignissim orci tincidunt et. Nullam sodales elit eu metus lobortis, - ut posuere lectus convallis. Maecenas id lectus id ligula bibendum - congue ac ut risus. Integer eget neque volutpat, mollis nisi sed, - lobortis mauris. In est metus, rhoncus in tristique nec, blandit sed - mauris. Maecenas non risus mauris. Nunc pretium urna sit amet sapien - venenatis mollis. Sed facilisis libero nisi, fermentum malesuada - magna commodo at. Phasellus sed hendrerit ipsum. Donec ultrices - sodales posuere. Proin a finibus mauris, non ornare arcu. Donec eget - cursus augue. Nulla vel porta massa. Ut hendrerit mauris a suscipit - ullamcorper. Donec sagittis tellus non ultrices feugiat. Mauris - sodales, eros id vulputate porttitor, nunc augue aliquet tortor, - eget mattis turpis mauris nec enim. Proin ac rhoncus lacus. Aliquam - venenatis lorem ac arcu commodo tristique id eget dolor. Nam gravida - nisi vel purus interdum, faucibus suscipit arcu placerat. Phasellus - congue velit et quam elementum, a fermentum velit efficitur. Nunc - lobortis, lacus at faucibus vestibulum, mauris nisl facilisis odio, - at elementum nisi mauris nec tortor. Nullam commodo pellentesque - ante ac porttitor. Lorem ipsum dolor sit amet, consectetur - adipiscing elit. Sed feugiat neque magna. Suspendisse potenti. Duis - dictum ac elit at elementum. In sit amet hendrerit lacus. -

    -
    -
    -

    Third container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. - Nulla ullamcorper odio id risus pretium, sed ornare turpis luctus. - Integer ac semper ex, ac convallis lorem. Orci varius natoque - penatibus et magnis dis parturient montes, nascetur ridiculus mus. - In eleifend lobortis lacus sed efficitur. Ut rhoncus consectetur - accumsan. Nulla massa lorem, maximus eu venenatis in, ultrices sit - amet purus. Duis et rutrum mi, id dictum risus. Fusce suscipit metus - congue, fermentum sem non, molestie sem. Donec tortor quam, - imperdiet vel dui at, finibus vestibulum neque. Nulla facilisi. Sed - fermentum vulputate enim, at aliquam diam tincidunt vitae. Nunc - eleifend volutpat pellentesque. Aliquam consectetur enim ac lectus - dapibus bibendum. Cras eget erat magna. Donec turpis quam, efficitur - in leo in, elementum interdum lorem. Duis tempor tempor nulla, at - faucibus leo commodo vel. Nunc in tincidunt diam, ac ultrices - mauris. Nunc quis vehicula tellus. Mauris vehicula gravida eros, - eget facilisis elit viverra faucibus. Sed viverra rhoncus erat, eget - dignissim orci tincidunt et. Nullam sodales elit eu metus lobortis, - ut posuere lectus convallis. Maecenas id lectus id ligula bibendum - congue ac ut risus. Integer eget neque volutpat, mollis nisi sed, - lobortis mauris. In est metus, rhoncus in tristique nec, blandit sed - mauris. Maecenas non risus mauris. Nunc pretium urna sit amet sapien - venenatis mollis. Sed facilisis libero nisi, fermentum malesuada - magna commodo at. Phasellus sed hendrerit ipsum. Donec ultrices - sodales posuere. Proin a finibus mauris, non ornare arcu. Donec eget - cursus augue. Nulla vel porta massa. Ut hendrerit mauris a suscipit - ullamcorper. Donec sagittis tellus non ultrices feugiat. Mauris - sodales, eros id vulputate porttitor, nunc augue aliquet tortor, - eget mattis turpis mauris nec enim. Proin ac rhoncus lacus. Aliquam - venenatis lorem ac arcu commodo tristique id eget dolor. Nam gravida - nisi vel purus interdum, faucibus suscipit arcu placerat. Phasellus - congue velit et quam elementum, a fermentum velit efficitur. Nunc - lobortis, lacus at faucibus vestibulum, mauris nisl facilisis odio, - at elementum nisi mauris nec tortor. Nullam commodo pellentesque - ante ac porttitor. Lorem ipsum dolor sit amet, consectetur - adipiscing elit. Sed feugiat neque magna. Suspendisse potenti. Duis - dictum ac elit at elementum. In sit amet hendrerit lacus. -

    -
    -
    -

    Fourth container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. - Nulla ullamcorper odio id risus pretium, sed ornare turpis luctus. - Integer ac semper ex, ac convallis lorem. Orci varius natoque - penatibus et magnis dis parturient montes, nascetur ridiculus mus. - In eleifend lobortis lacus sed efficitur. Ut rhoncus consectetur - accumsan. Nulla massa lorem, maximus eu venenatis in, ultrices sit - amet purus. Duis et rutrum mi, id dictum risus. Fusce suscipit metus - congue, fermentum sem non, molestie sem. Donec tortor quam, - imperdiet vel dui at, finibus vestibulum neque. Nulla facilisi. Sed - fermentum vulputate enim, at aliquam diam tincidunt vitae. Nunc - eleifend volutpat pellentesque. Aliquam consectetur enim ac lectus - dapibus bibendum. Cras eget erat magna. Donec turpis quam, efficitur - in leo in, elementum interdum lorem. Duis tempor tempor nulla, at - faucibus leo commodo vel. Nunc in tincidunt diam, ac ultrices - mauris. Nunc quis vehicula tellus. Mauris vehicula gravida eros, - eget facilisis elit viverra faucibus. Sed viverra rhoncus erat, eget - dignissim orci tincidunt et. Nullam sodales elit eu metus lobortis, - ut posuere lectus convallis. Maecenas id lectus id ligula bibendum - congue ac ut risus. Integer eget neque volutpat, mollis nisi sed, - lobortis mauris. In est metus, rhoncus in tristique nec, blandit sed - mauris. Maecenas non risus mauris. Nunc pretium urna sit amet sapien - venenatis mollis. Sed facilisis libero nisi, fermentum malesuada - magna commodo at. Phasellus sed hendrerit ipsum. Donec ultrices - sodales posuere. Proin a finibus mauris, non ornare arcu. Donec eget - cursus augue. Nulla vel porta massa. Ut hendrerit mauris a suscipit - ullamcorper. Donec sagittis tellus non ultrices feugiat. Mauris - sodales, eros id vulputate porttitor, nunc augue aliquet tortor, - eget mattis turpis mauris nec enim. Proin ac rhoncus lacus. Aliquam - venenatis lorem ac arcu commodo tristique id eget dolor. Nam gravida - nisi vel purus interdum, faucibus suscipit arcu placerat. Phasellus - congue velit et quam elementum, a fermentum velit efficitur. Nunc - lobortis, lacus at faucibus vestibulum, mauris nisl facilisis odio, - at elementum nisi mauris nec tortor. Nullam commodo pellentesque - ante ac porttitor. Lorem ipsum dolor sit amet, consectetur - adipiscing elit. Sed feugiat neque magna. Suspendisse potenti. Duis - dictum ac elit at elementum. In sit amet hendrerit lacus. -

    -
    -
    -
    - - - - - - diff --git a/packages/components-core/examples/article.html b/packages/components-core/examples/article.html deleted file mode 100644 index 3eb5ce687c..0000000000 --- a/packages/components-core/examples/article.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - Article component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    - - - - - - diff --git a/packages/components-core/examples/button.html b/packages/components-core/examples/button.html deleted file mode 100644 index 8440a59090..0000000000 --- a/packages/components-core/examples/button.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - Button component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    - - -
    -
    - - - - - - diff --git a/packages/components-core/examples/buttonIconOnly.html b/packages/components-core/examples/buttonIconOnly.html deleted file mode 100644 index 492eb4b773..0000000000 --- a/packages/components-core/examples/buttonIconOnly.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - Button Icon Only component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    -
    -
    - - - - - - diff --git a/packages/components-core/examples/buttonTag.html b/packages/components-core/examples/buttonTag.html deleted file mode 100644 index f9ba34a464..0000000000 --- a/packages/components-core/examples/buttonTag.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - Button Tag component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    - -
    -
    - - - - - - diff --git a/packages/components-core/examples/card.html b/packages/components-core/examples/card.html deleted file mode 100644 index aea026c608..0000000000 --- a/packages/components-core/examples/card.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - Card component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - diff --git a/packages/components-core/examples/hero.html b/packages/components-core/examples/hero.html deleted file mode 100644 index 94c1ab05d2..0000000000 --- a/packages/components-core/examples/hero.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - Hero component - Example page - - - - - - - - - - - - - - - - - - - -
    -
    -
    - Put here your content -
    -
    - - - - - diff --git a/packages/components-core/examples/js/example-helper.js b/packages/components-core/examples/js/example-helper.js deleted file mode 100644 index 5fc89c18e8..0000000000 --- a/packages/components-core/examples/js/example-helper.js +++ /dev/null @@ -1,60 +0,0 @@ -// @ts-check - -const toolBar = ` - - `; - -const style = ` - - `; - -function setActivePageLink() { - const parseLink = url => { - const index = url.lastIndexOf("/"); - const page = url.substr(index + 1); - return page; - }; - - const activePage = parseLink(window.location.pathname); - - const links = document.querySelectorAll("a"); - links.forEach(anchor => { - const linkPage = parseLink(anchor.href); - if (linkPage === activePage) { - anchor.classList.add("active"); - } - }); -} - -document.head.insertAdjacentHTML("beforeend", style); -document.querySelector(".container").insertAdjacentHTML("afterbegin", toolBar); -setActivePageLink(); diff --git a/packages/components-core/examples/pagination.html b/packages/components-core/examples/pagination.html deleted file mode 100644 index e030162bad..0000000000 --- a/packages/components-core/examples/pagination.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - Pagination component - Example page - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - - - - - diff --git a/packages/components-core/examples/testimonial.html b/packages/components-core/examples/testimonial.html deleted file mode 100644 index d12b0ce0c4..0000000000 --- a/packages/components-core/examples/testimonial.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - Testimonial component - Example page - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - - - - - diff --git a/packages/components-core/examples/video.html b/packages/components-core/examples/video.html deleted file mode 100644 index 3a5720958e..0000000000 --- a/packages/components-core/examples/video.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - Hero component - Example page - - - - - - - - - - - - - - - -
    -
    -
    -

    Default Video

    -
    -
    - -
    -

    Default Video with caption

    -
    -
    - -
    -

    Youtube Video

    -
    -
    - -
    -

    Youtube Video with caption

    -
    -
    -
    -
    - - - - - diff --git a/packages/components-core/jest.config.js b/packages/components-core/jest.config.js deleted file mode 100644 index 5abfb408e9..0000000000 --- a/packages/components-core/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -// @ts-check -module.exports = { - verbose: true, - testEnvironment: "jsdom", - setupFilesAfterEnv: ["./setupTests.js"], - transform: { - "^.+\\.(js|jsx)$": "babel-jest", - "^.+\\.css$": "jest-transform-css", - "\\.(jpg|jpeg|png|gif|webp|svg)$": "jest-transform-file", - }, -}; diff --git a/packages/components-core/jsdoc.config.js b/packages/components-core/jsdoc.config.js deleted file mode 100644 index 0aa261f793..0000000000 --- a/packages/components-core/jsdoc.config.js +++ /dev/null @@ -1,16 +0,0 @@ -// @ts-check - -const coreConfig = require("../../jsdoc.config"); - -module.exports = { - ...coreConfig, - source: { - ...coreConfig.source, - include: [ - ...coreConfig.source.include, - "./src/core/types", - "./src/components", - ], - excludePattern: "FeedAnatomy", - }, -}; diff --git a/packages/components-core/package.json b/packages/components-core/package.json deleted file mode 100644 index 4cbc423803..0000000000 --- a/packages/components-core/package.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "name": "@asu/components-core", - "version": "4.3.0", - "main": "./dist/libCore.cjs.js", - "browser": "./dist/libCore.umd.js", - "module": "./dist/libCore.es.js", - "types": "./dist/main.d.ts", - "description": "Core UDS React UI components required by other higher-order React packages", - "author": "Nathan Rollins ", - "homepage": "https://github.com/ASU/asu-unity-stack#readme", - "license": "MIT", - "deprecated": "This package has been moved. Please use @asu/unity-react-core to receive continued support.", - "files": [ - "examples/*", - "dist/*", - "docs/*", - "CHANGELOG.md" - ], - "repository": { - "type": "git", - "url": "https://github.com/ASU/asu-unity-stack.git", - "directory": "packages/components-core" - }, - "publishConfig": { - "access": "public", - "registry": "https://npm.pkg.github.com/@asu" - }, - "bugs": { - "url": "https://github.com/ASU/asu-unity-stack/issues" - }, - "scripts": { - "lint": "eslint --fix 'src/**/*.{js,jsx}' --ignore-path ../../.eslintignore", - "test": "jest --config=./jest.config.js --silent --coverage", - "start:dev": "webpack-dashboard -- webpack serve -c webpack/webpack.dev.js", - "prebuild": "rm -rf ./dist", - "build": "yarn prebuild && webpack -c webpack/webpack.prod.js && yarn postbuild", - "postbuild": "cp ./types/main.d.ts ./dist/main.d.ts", - "build:stats": "webpack -c webpack/webpack.prod.js --profile --json=compilation-stats.json", - "storybook": "storybook dev -p 9100", - "build-storybook": "storybook build -o ../../build/$npm_package_name", - "jsdoc": "jsdoc -c jsdoc.config.js", - "predocs": "mkdir -p ./docs", - "docs": "yarn predocs && jsdoc2md --no-cache -c jsdoc.config.js --files ./src/components > ./docs/README.props.md && yarn postdocs", - "postdocs": "node ../../scripts/process-readme-props.js" - }, - "devDependencies": { - "@asu/unity-bootstrap-theme": "^1.20.2", - "@babel/cli": "^7.19.3", - "@babel/core": "^7.21.3", - "@babel/plugin-transform-runtime": "^7.19.6", - "@babel/preset-env": "^7.20.2", - "@storybook/addon-essentials": "^7.6.14", - "@storybook/addon-interactions": "^7.6.14", - "@storybook/addon-links": "^7.6.14", - "@storybook/react": "^7.6.14", - "@storybook/react-webpack5": "^7.6.14", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/react": "^16.0.0", - "@vitejs/plugin-react": "^4.3.1", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "css-loader": "^5.2.0", - "eslint-plugin-storybook": "^0.6.15", - "file-loader": "^6.2.0", - "glob": "^7.1.6", - "jest": "^26.6.3", - "jest-fetch-mock": "^3.0.3", - "jest-image-snapshot": "^4.4.1", - "jest-transform-css": "^6.0.1", - "jest-transform-file": "^1.1.1", - "jsdom-screenshot": "^4.0.0", - "postcss": "^8.4.19", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router-dom": "^6.2.1", - "react-share": "^4.4.0", - "reactstrap": "^9", - "semantic-release": "^22", - "semantic-release-monorepo": "^8.0.2", - "storybook": "^7.6.14", - "storybook-css-modules-preset": "^1.1.1", - "style-loader": "^3.3.1", - "vite": "^5.3.5", - "webpack-merge": "^5.8.0" - }, - "dependencies": { - "classnames": "^2.2.6", - "date-fns": "^2.19.0", - "phone-fns": "^3.2.3", - "prop-types": "^15.7.2", - "styled-components": "^5.3.0" - }, - "peerDependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router-dom": ">= 5.2.0 < 7", - "react-share": "^4.4.0", - "reactstrap": "^9" - }, - "exports": { - "./*": "./*", - ".": { - "import": "./dist/libCore.es.js", - "require": "./dist/libCore.cjs.js" - } - }, - "volta": { - "extends": "../../package.json" - } -} diff --git a/packages/components-core/setupTests.js b/packages/components-core/setupTests.js deleted file mode 100644 index 205b609d0f..0000000000 --- a/packages/components-core/setupTests.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable jest/no-mocks-import */ -// @ts-check -import { toMatchImageSnapshot } from "jest-image-snapshot"; - -import "@testing-library/jest-dom"; -import "@testing-library/jest-dom/extend-expect"; - -import "./__mocks__/window-mock"; -import "./__mocks__/fetch-mock"; - -expect.extend({ toMatchImageSnapshot }); diff --git a/packages/components-core/src/assets/video/stock-video-person-drawing.mp4 b/packages/components-core/src/assets/video/stock-video-person-drawing.mp4 deleted file mode 100644 index d9f6902e97..0000000000 Binary files a/packages/components-core/src/assets/video/stock-video-person-drawing.mp4 and /dev/null differ diff --git a/packages/components-core/src/components/Accordion/AccordionCard/index.js b/packages/components-core/src/components/Accordion/AccordionCard/index.js deleted file mode 100644 index 362a9ec5e4..0000000000 --- a/packages/components-core/src/components/Accordion/AccordionCard/index.js +++ /dev/null @@ -1,68 +0,0 @@ -// @ts-check -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React from "react"; - -import { sanitizeDangerousMarkup } from "../../../../../../shared"; -import { accordionCardPropTypes } from "../../../core/models/shared-prop-types"; - -/** - * @typedef {import('../../../core/types/shared-types').AccordionCardItemProps} AccordionCardItemProps - */ - -/** - * @param {AccordionCardItemProps} props - * @returns {JSX.Element} - * @ignore - */ -export const AccordionCard = ({ id, item, openCard, onClick }) => ( -
    -
    -

    - -

    -
    - {item.content?.body && ( -
    -
    -
    - )} -
    -); - -AccordionCard.propTypes = { - id: PropTypes.number, - item: accordionCardPropTypes, - openCard: PropTypes.number, - onClick: PropTypes.func, -}; diff --git a/packages/components-core/src/components/Accordion/index.js b/packages/components-core/src/components/Accordion/index.js deleted file mode 100644 index 3522c63759..0000000000 --- a/packages/components-core/src/components/Accordion/index.js +++ /dev/null @@ -1,91 +0,0 @@ -// @ts-nocheck -import PropTypes from "prop-types"; -import React, { useState } from "react"; - -import { trackGAEvent } from "../../../../../shared"; -import { accordionCardPropTypes } from "../../core/models/shared-prop-types"; -import { AccordionCard } from "./AccordionCard"; - -const defaultGAEvent = { - event: "collapse", - name: "onclick", - type: "click", - region: "main content", -}; - -const AVAILABLE_GA_ACTIONS = { - OPEN: "open", - CLOSE: "close", -}; - -/** - * @typedef {import('../../core/types/shared-types').AccordionProps} AccordionProps - */ - -/** - * @param {AccordionProps} props - * @returns {JSX.Element} - */ -const Accordion = ({ cards, openedCard }) => { - const [currentOpenCard, setCurrentOpenCard] = useState(openedCard); - - const trackEvent = (cardTitle, action) => { - trackGAEvent({ - ...defaultGAEvent, - action, - text: cardTitle, - }); - }; - - const toggleCard = (event, card, cardTitle) => { - event.preventDefault(); - - // If there is a difference between the previously opened card and the currently opened card, or if the same card is clicked, the close event is triggered. - if (currentOpenCard === card || currentOpenCard) { - // we get the header of the previous card and send it to the GA event from the cards list - trackEvent( - cards[currentOpenCard - 1].content.header, - AVAILABLE_GA_ACTIONS.CLOSE - ); - } - - if (currentOpenCard !== card) { - setCurrentOpenCard(card); - trackEvent(cardTitle, AVAILABLE_GA_ACTIONS.OPEN); - } else { - setCurrentOpenCard(null); - } - }; - - return ( -
    - {cards?.map( - (card, key) => - card.content.body && - card.content.header && ( - - ) - )} -
    - ); -}; - -Accordion.propTypes = { - /** - * Cards to show in the accordion component - */ - cards: PropTypes.arrayOf(accordionCardPropTypes).isRequired, - /** - * Opened card based on rendered card position - */ - openedCard: PropTypes.number, -}; - -export { Accordion }; diff --git a/packages/components-core/src/components/Accordion/index.stories.js b/packages/components-core/src/components/Accordion/index.stories.js deleted file mode 100644 index 12b9d1439c..0000000000 --- a/packages/components-core/src/components/Accordion/index.stories.js +++ /dev/null @@ -1,103 +0,0 @@ -/* eslint react/jsx-props-no-spreading: "off" */ -import React from "react"; - -import { Accordion } from "."; - -export default { - title: "UDS/Accordion", - component: Accordion, - parameters: { - docs: { - description: { - component: `The Accordion component can be used to generate an accordion of editable content cards. - ## Usage - - Accordion users are responsible to meet all UDS design guidelines with their menu, - including rules on the use of Call-to-Action buttons and tags. - - View component examples and source code below. - - This story includes another components for demostration purposes. - `, - }, - }, - }, -}; - -const Template = args => ; - -export const Default = Template.bind({}); -Default.args = { - cards: [ - { - content: { - header: "Accordion Card 1", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - { - content: { - header: "Accordion Card 2", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - { - content: { - header: "Accordion Card 3, opened card", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - ], - openedCard: 3, -}; -export const ColorCombinations = Template.bind({}); -ColorCombinations.args = { - cards: [ - { - content: { - header: "Accordion Card 1", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - { - color: "maroon", - content: { - header: "Accordion Card 2", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - { - color: "gray", - content: { - header: "Accordion Card 3", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - { - color: "dark", - content: { - header: "Accordion Card 4", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - ], -}; -export const Icon = Template.bind({}); -Icon.args = { - cards: [ - { - content: { - icon: ["fas", "pencil-alt"], - header: "Accordion Card 1", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - { - content: { - icon: ["fas", "newspaper"], - header: "Accordion Card 2", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - ], -}; diff --git a/packages/components-core/src/components/Accordion/index.test.js b/packages/components-core/src/components/Accordion/index.test.js deleted file mode 100644 index 25c5d15bcb..0000000000 --- a/packages/components-core/src/components/Accordion/index.test.js +++ /dev/null @@ -1,55 +0,0 @@ -// @ts-check -import { render, fireEvent, cleanup } from "@testing-library/react"; -import React from "react"; - -import { Accordion } from "./index"; - -const defaultArgs = { - cards: [ - { - content: { - header: "Accordion Card", - body: "

    Quatrenary Headline

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    This is a level five headline. There's a fancy word for that too.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

    ", - }, - }, - ], -}; - -const renderAccordion = args => { - return render(); -}; - -describe("#Accordion", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - component = renderAccordion(defaultArgs); - }); - afterEach(cleanup); - - it("should define the component", () => { - expect(component).toBeDefined(); - }); - - it("should open the card", async () => { - const buttonCard = await component.findByTestId("accordion-opener"); - fireEvent.click(buttonCard); - expect(buttonCard.getAttribute("aria-expanded")).toBe("true"); - }); -}); - -describe("#Accordion opened", () => { - it("should define the component opened", async () => { - const props = { - cards: [...defaultArgs.cards], - openedCard: 1, - }; - - const component = renderAccordion(props); - const componentActioner = await component.findByTestId("accordion-opener"); - - expect(component).toBeDefined(); - expect(componentActioner.getAttribute("aria-expanded")).toBe("true"); - }); -}); diff --git a/packages/components-core/src/components/AnchorMenu/index.js b/packages/components-core/src/components/AnchorMenu/index.js deleted file mode 100644 index 4e537594e2..0000000000 --- a/packages/components-core/src/components/AnchorMenu/index.js +++ /dev/null @@ -1,281 +0,0 @@ -// @ts-check -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React, { useState, useEffect, useRef } from "react"; - -import { - debounce, - queryFirstFocusable, - throttle, - trackGAEvent, - useMediaQuery, -} from "../../../../../shared"; -import { Button } from "../Button"; -import { AnchorMenuWrapper } from "./index.styles"; - -const menuTitle = "On This Page"; - -const defaultMobileGAEvent = { - event: "collapse", - name: "onclick", - type: "click", - text: menuTitle, -}; - -/** - * @typedef { import('../../core/types/shared-types').AnchorMenuProps } AnchorMenuProps - */ - -/** - * @param {AnchorMenuProps} props - * @returns {JSX.Element} - */ -export const AnchorMenu = ({ - items, - firstElementId, - focusFirstFocusableElement = false, -}) => { - const anchorMenuRef = useRef(null); - const isSmallDevice = useMediaQuery("(max-width: 991px)"); - const [state, setState] = useState({ - hasHeader: false, - hasAltMenuSpacing: false, - containerClass: "container-xl", - activeContainer: "", - showMenu: false, - sticky: false, - }); - const headerHeight = isSmallDevice ? 110 : 142; - - const handleWindowScroll = () => { - const newState = {}; - const curPos = window.scrollY; - // Select first next sibling element of the anchor menu - const firstElement = document - .getElementById(firstElementId) - ?.getBoundingClientRect().top; - const anchorMenuHeight = 103; - - // Scroll position - if (firstElement >= 0) { - newState.sticky = false; - newState.activeContainer = ""; - } - if (curPos > anchorMenuRef.current.getBoundingClientRect().top) - newState.sticky = true; - - // Change active containers on scroll - const subsHeight = state.hasHeader - ? headerHeight + anchorMenuHeight - : anchorMenuHeight; - items?.forEach(({ targetIdName }) => { - const container = document.getElementById(targetIdName); - const containerTop = container?.getBoundingClientRect().top - subsHeight; - const containerBottom = - container?.getBoundingClientRect().bottom - subsHeight; - if (containerTop < 0 && containerBottom > 0) { - newState.activeContainer = targetIdName; - } - }); - - setState(prevState => ({ - ...prevState, - ...newState, - })); - }; - - const throttleWindowScroll = () => { - const timeout = 150; - // prevent function from being called excessively - throttle(handleWindowScroll, timeout); - // ensure function executes after scrolling stops - debounce(handleWindowScroll, timeout); - }; - - // Is ASU Header on the document - const isHeader = () => { - const pageHeader = - document.getElementById("asu-header") || - document.getElementById("headerContainer") || - document.getElementById("asuHeader"); - return !!pageHeader; - }; - - // Is element present which requires different spacing for the ASU Header - // Sets prop for styled-component to change anchor menu style - const isAltMenuSpacing = () => { - const degreeDetailPageContainer = document.getElementById( - "degreeDetailPageContainer" - ); - return !!degreeDetailPageContainer; - }; - - // Returns the first container class found from ancestors or default - function getContainerClass(el = null) { - if (el === null) return state.containerClass; - - const result = Object.values(el.classList).filter(c => - [ - "container-sm", - "container-md", - "container", - "container-lg", - "container-xl", - "container-fluid", - ].includes(c) - ); - - if (result.length > 0) return result.join(" "); - - return getContainerClass(el.parentElement); - } - - // get values from outside this component - // set initial state from external values - useEffect(() => { - const firstElement = document.getElementById(firstElementId) || null; - const newState = { - hasHeader: isHeader(), - hasAltMenuSpacing: isAltMenuSpacing(), - containerClass: getContainerClass(firstElement), - }; - setState(prevState => ({ - ...prevState, - ...newState, - })); - }, []); - - useEffect(() => { - window?.addEventListener("scroll", throttleWindowScroll); - return () => window.removeEventListener("scroll", throttleWindowScroll); - }, [state.hasHeader]); - - const handleClickLink = container => { - // Set scroll position considering if ASU Header is setted or not - const curScroll = - window.scrollY - (state.hasHeader ? headerHeight + 100 : 100); - const anchorMenuHeight = isSmallDevice ? 410 : 90; - // Set where to scroll to - let scrollTo = - document.getElementById(container)?.getBoundingClientRect().top + - curScroll; - - if (!anchorMenuRef.current.classList.contains("sticky")) - scrollTo -= anchorMenuHeight; - - if (focusFirstFocusableElement) - queryFirstFocusable(`#${container}`)?.focus(); - - window.scrollTo({ top: scrollTo, behavior: "smooth" }); - }; - - const trackMobileDropdownEvent = () => { - trackGAEvent({ - ...defaultMobileGAEvent, - action: state.showMenu ? "close" : "open", - }); - }; - - const handleMenuVisibility = () => { - setState(prevState => ({ - ...prevState, - showMenu: !prevState.showMenu, - })); - }; - - return ( - items?.length > 0 && ( - -
    - {isSmallDevice ? ( - - ) : ( -

    {menuTitle}:

    - )} - -
    - -
    -
    -
    - ) - ); -}; - -AnchorMenu.propTypes = { - /** - * Anchor menu items - */ - items: PropTypes.arrayOf( - PropTypes.shape({ - text: PropTypes.string.isRequired, - targetIdName: PropTypes.string.isRequired, - icon: PropTypes.arrayOf(PropTypes.string), - }) - ).isRequired, - /** - * First next sibling element of the anchor menu - */ - firstElementId: PropTypes.string.isRequired, - /** - * If true it focus the first focusable element into the section - * If false it focus the next menu item into the nav bar - */ - focusFirstFocusableElement: PropTypes.bool, -}; diff --git a/packages/components-core/src/components/AnchorMenu/index.stories.js b/packages/components-core/src/components/AnchorMenu/index.stories.js deleted file mode 100644 index 60c9642150..0000000000 --- a/packages/components-core/src/components/AnchorMenu/index.stories.js +++ /dev/null @@ -1,258 +0,0 @@ -/* eslint react/jsx-props-no-spreading: "off" */ -import classNames from "classnames"; -import React from "react"; - -import { AnchorMenu } from "."; - -export default { - title: "UDS/AnchorMenu", - component: AnchorMenu, - excludeStories: ["Containers"], - parameters: { - docs: { - description: { - component: `The Anchor Menu component can be used to generate a responsive anchor menu. - ## Usage - - Anchor menu users are responsible to meet all UDS design guidelines with their menu, - including rules on the use of Call-to-Action buttons and tags. - - View component examples and source code below. - - This story includes another components for demostration purposes. - `, - }, - }, - }, -}; - -export const Containers = () => { - return ( - <> -
    -

    First container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. Nulla - ullamcorper odio id risus pretium, sed ornare turpis luctus. Integer - ac semper ex, ac convallis lorem. Orci varius natoque penatibus et - magnis dis parturient montes, nascetur ridiculus mus. In eleifend - lobortis lacus sed efficitur. Ut rhoncus consectetur accumsan. Nulla - massa lorem, maximus eu venenatis in, ultrices sit amet purus. Duis et - rutrum mi, id dictum risus. Fusce suscipit metus congue, fermentum sem - non, molestie sem. Donec tortor quam, imperdiet vel dui at, finibus - vestibulum neque. Nulla facilisi. Sed fermentum vulputate enim, at - aliquam diam tincidunt vitae. Nunc eleifend volutpat pellentesque. - Aliquam consectetur enim ac lectus dapibus bibendum. Cras eget erat - magna. Donec turpis quam, efficitur in leo in, elementum interdum - lorem. Duis tempor tempor nulla, at faucibus leo commodo vel. Nunc in - tincidunt diam, ac ultrices mauris. Nunc quis vehicula tellus. Mauris - vehicula gravida eros, eget facilisis elit viverra faucibus. Sed - viverra rhoncus erat, eget dignissim orci tincidunt et. Nullam sodales - elit eu metus lobortis, ut posuere lectus convallis. Maecenas id - lectus id ligula bibendum congue ac ut risus. Integer eget neque - volutpat, mollis nisi sed, lobortis mauris. In est metus, rhoncus in - tristique nec, blandit sed mauris. Maecenas non risus mauris. Nunc - pretium urna sit amet sapien venenatis mollis. Sed facilisis libero - nisi, fermentum malesuada magna commodo at. Phasellus sed hendrerit - ipsum. Donec ultrices sodales posuere. Proin a finibus mauris, non - ornare arcu. Donec eget cursus augue. Nulla vel porta massa. Ut - hendrerit mauris a suscipit ullamcorper. Donec sagittis tellus non - ultrices feugiat. Mauris sodales, eros id vulputate porttitor, nunc - augue aliquet tortor, eget mattis turpis mauris nec enim. Proin ac - rhoncus lacus. Aliquam venenatis lorem ac arcu commodo tristique id - eget dolor. Nam gravida nisi vel purus interdum, faucibus suscipit - arcu placerat. Phasellus congue velit et quam elementum, a fermentum - velit efficitur. Nunc lobortis, lacus at faucibus vestibulum, mauris - nisl facilisis odio, at elementum nisi mauris nec tortor. Nullam - commodo pellentesque ante ac porttitor. Lorem ipsum dolor sit amet, - consectetur adipiscing elit. Sed feugiat neque magna. Suspendisse - potenti. Duis dictum ac elit at elementum. In sit amet hendrerit - lacus. -

    -
    -
    -

    Second container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. Nulla - ullamcorper odio id risus pretium, sed ornare turpis luctus. Integer - ac semper ex, ac convallis lorem. Orci varius natoque penatibus et - magnis dis parturient montes, nascetur ridiculus mus. In eleifend - lobortis lacus sed efficitur. Ut rhoncus consectetur accumsan. Nulla - massa lorem, maximus eu venenatis in, ultrices sit amet purus. Duis et - rutrum mi, id dictum risus. Fusce suscipit metus congue, fermentum sem - non, molestie sem. Donec tortor quam, imperdiet vel dui at, finibus - vestibulum neque. Nulla facilisi. Sed fermentum vulputate enim, at - aliquam diam tincidunt vitae. Nunc eleifend volutpat pellentesque. - Aliquam consectetur enim ac lectus dapibus bibendum. Cras eget erat - magna. Donec turpis quam, efficitur in leo in, elementum interdum - lorem. Duis tempor tempor nulla, at faucibus leo commodo vel. Nunc in - tincidunt diam, ac ultrices mauris. Nunc quis vehicula tellus. Mauris - vehicula gravida eros, eget facilisis elit viverra faucibus. Sed - viverra rhoncus erat, eget dignissim orci tincidunt et. Nullam sodales - elit eu metus lobortis, ut posuere lectus convallis. Maecenas id - lectus id ligula bibendum congue ac ut risus. Integer eget neque - volutpat, mollis nisi sed, lobortis mauris. In est metus, rhoncus in - tristique nec, blandit sed mauris. Maecenas non risus mauris. Nunc - pretium urna sit amet sapien venenatis mollis. Sed facilisis libero - nisi, fermentum malesuada magna commodo at. Phasellus sed hendrerit - ipsum. Donec ultrices sodales posuere. Proin a finibus mauris, non - ornare arcu. Donec eget cursus augue. Nulla vel porta massa. Ut - hendrerit mauris a suscipit ullamcorper. Donec sagittis tellus non - ultrices feugiat. Mauris sodales, eros id vulputate porttitor, nunc - augue aliquet tortor, eget mattis turpis mauris nec enim. Proin ac - rhoncus lacus. Aliquam venenatis lorem ac arcu commodo tristique id - eget dolor. Nam gravida nisi vel purus interdum, faucibus suscipit - arcu placerat. Phasellus congue velit et quam elementum, a fermentum - velit efficitur. Nunc lobortis, lacus at faucibus vestibulum, mauris - nisl facilisis odio, at elementum nisi mauris nec tortor. Nullam - commodo pellentesque ante ac porttitor. Lorem ipsum dolor sit amet, - consectetur adipiscing elit. Sed feugiat neque magna. Suspendisse - potenti. Duis dictum ac elit at elementum. In sit amet hendrerit - lacus. -

    -
    -
    -

    Third container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. Nulla - ullamcorper odio id risus pretium, sed ornare turpis luctus. Integer - ac semper ex, ac convallis lorem. Orci varius natoque penatibus et - magnis dis parturient montes, nascetur ridiculus mus. In eleifend - lobortis lacus sed efficitur. Ut rhoncus consectetur accumsan. Nulla - massa lorem, maximus eu venenatis in, ultrices sit amet purus. Duis et - rutrum mi, id dictum risus. Fusce suscipit metus congue, fermentum sem - non, molestie sem. Donec tortor quam, imperdiet vel dui at, finibus - vestibulum neque. Nulla facilisi. Sed fermentum vulputate enim, at - aliquam diam tincidunt vitae. Nunc eleifend volutpat pellentesque. - Aliquam consectetur enim ac lectus dapibus bibendum. Cras eget erat - magna. Donec turpis quam, efficitur in leo in, elementum interdum - lorem. Duis tempor tempor nulla, at faucibus leo commodo vel. Nunc in - tincidunt diam, ac ultrices mauris. Nunc quis vehicula tellus. Mauris - vehicula gravida eros, eget facilisis elit viverra faucibus. Sed - viverra rhoncus erat, eget dignissim orci tincidunt et. Nullam sodales - elit eu metus lobortis, ut posuere lectus convallis. Maecenas id - lectus id ligula bibendum congue ac ut risus. Integer eget neque - volutpat, mollis nisi sed, lobortis mauris. In est metus, rhoncus in - tristique nec, blandit sed mauris. Maecenas non risus mauris. Nunc - pretium urna sit amet sapien venenatis mollis. Sed facilisis libero - nisi, fermentum malesuada magna commodo at. Phasellus sed hendrerit - ipsum. Donec ultrices sodales posuere. Proin a finibus mauris, non - ornare arcu. Donec eget cursus augue. Nulla vel porta massa. Ut - hendrerit mauris a suscipit ullamcorper. Donec sagittis tellus non - ultrices feugiat. Mauris sodales, eros id vulputate porttitor, nunc - augue aliquet tortor, eget mattis turpis mauris nec enim. Proin ac - rhoncus lacus. Aliquam venenatis lorem ac arcu commodo tristique id - eget dolor. Nam gravida nisi vel purus interdum, faucibus suscipit - arcu placerat. Phasellus congue velit et quam elementum, a fermentum - velit efficitur. Nunc lobortis, lacus at faucibus vestibulum, mauris - nisl facilisis odio, at elementum nisi mauris nec tortor. Nullam - commodo pellentesque ante ac porttitor. Lorem ipsum dolor sit amet, - consectetur adipiscing elit. Sed feugiat neque magna. Suspendisse - potenti. Duis dictum ac elit at elementum. In sit amet hendrerit - lacus. -

    -
    -
    -

    Fourth container

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus - consectetur lectus nec laoreet elementum. Mauris ut sapien nibh. - Aenean consequat pellentesque rutrum. Pellentesque habitant morbi - tristique senectus et netus et malesuada fames ac turpis egestas. - Aliquam sollicitudin neque vel risus volutpat, nec hendrerit nisl - tempus. Etiam suscipit purus imperdiet, ultrices enim et, maximus - lectus. Aenean hendrerit, nibh eget vehicula sollicitudin, nulla - tellus accumsan justo, quis gravida sapien quam ultricies magna. Nulla - ullamcorper odio id risus pretium, sed ornare turpis luctus. Integer - ac semper ex, ac convallis lorem. Orci varius natoque penatibus et - magnis dis parturient montes, nascetur ridiculus mus. In eleifend - lobortis lacus sed efficitur. Ut rhoncus consectetur accumsan. Nulla - massa lorem, maximus eu venenatis in, ultrices sit amet purus. Duis et - rutrum mi, id dictum risus. Fusce suscipit metus congue, fermentum sem - non, molestie sem. Donec tortor quam, imperdiet vel dui at, finibus - vestibulum neque. Nulla facilisi. Sed fermentum vulputate enim, at - aliquam diam tincidunt vitae. Nunc eleifend volutpat pellentesque. - Aliquam consectetur enim ac lectus dapibus bibendum. Cras eget erat - magna. Donec turpis quam, efficitur in leo in, elementum interdum - lorem. Duis tempor tempor nulla, at faucibus leo commodo vel. Nunc in - tincidunt diam, ac ultrices mauris. Nunc quis vehicula tellus. Mauris - vehicula gravida eros, eget facilisis elit viverra faucibus. Sed - viverra rhoncus erat, eget dignissim orci tincidunt et. Nullam sodales - elit eu metus lobortis, ut posuere lectus convallis. Maecenas id - lectus id ligula bibendum congue ac ut risus. Integer eget neque - volutpat, mollis nisi sed, lobortis mauris. In est metus, rhoncus in - tristique nec, blandit sed mauris. Maecenas non risus mauris. Nunc - pretium urna sit amet sapien venenatis mollis. Sed facilisis libero - nisi, fermentum malesuada magna commodo at. Phasellus sed hendrerit - ipsum. Donec ultrices sodales posuere. Proin a finibus mauris, non - ornare arcu. Donec eget cursus augue. Nulla vel porta massa. Ut - hendrerit mauris a suscipit ullamcorper. Donec sagittis tellus non - ultrices feugiat. Mauris sodales, eros id vulputate porttitor, nunc - augue aliquet tortor, eget mattis turpis mauris nec enim. Proin ac - rhoncus lacus. Aliquam venenatis lorem ac arcu commodo tristique id - eget dolor. Nam gravida nisi vel purus interdum, faucibus suscipit - arcu placerat. Phasellus congue velit et quam elementum, a fermentum - velit efficitur. Nunc lobortis, lacus at faucibus vestibulum, mauris - nisl facilisis odio, at elementum nisi mauris nec tortor. Nullam - commodo pellentesque ante ac porttitor. Lorem ipsum dolor sit amet, - consectetur adipiscing elit. Sed feugiat neque magna. Suspendisse - potenti. Duis dictum ac elit at elementum. In sit amet hendrerit - lacus. -

    -
    - - ); -}; - -const Template = args => ( -
    -
    -
    - {/* Component */} - - {/* Demostration purposes containers */} - -
    -
    -
    -); - -export const Default = Template.bind({}); -Default.args = { - items: [ - { - text: "First container", - targetIdName: "first-container", - icon: ["fas", "link"], - }, - { text: "Second container", targetIdName: "second-container" }, - { text: "Third container", targetIdName: "third-container" }, - { - text: "Fourth container", - targetIdName: "fourth-container", - icon: ["fas", "link"], - }, - ], - firstElementId: "first-container", -}; diff --git a/packages/components-core/src/components/AnchorMenu/index.styles.js b/packages/components-core/src/components/AnchorMenu/index.styles.js deleted file mode 100644 index c46a92ba4d..0000000000 --- a/packages/components-core/src/components/AnchorMenu/index.styles.js +++ /dev/null @@ -1,44 +0,0 @@ -import styled from "styled-components"; - -const AnchorMenuWrapper = styled.div` - &.sticky { - position: fixed; - top: 0; - left: 0; - width: 100%; - &.with-header { - top: ${({ requiresAltMenuSpacing }) => - requiresAltMenuSpacing ? "112px" : "142px"}; - @media (max-width: 992px) { - top: 110px; - } - } - } - .mobile-menu-toggler { - background-color: transparent; - border: none; - cursor: default; - h4 { - align-items: center; - } - h2 { - align-items: center; - } - i { - transition: all 0.3s; - } - } - .show-menu i { - transform: rotate(-180deg); - } - .nav-link { - border: none; - background-color: #ffffff; - i { - width: 2rem !important; - text-align: center !important; - } - } -`; - -export { AnchorMenuWrapper }; diff --git a/packages/components-core/src/components/AnchorMenu/index.test.js b/packages/components-core/src/components/AnchorMenu/index.test.js deleted file mode 100644 index 6c7e6a43d0..0000000000 --- a/packages/components-core/src/components/AnchorMenu/index.test.js +++ /dev/null @@ -1,50 +0,0 @@ -// @ts-check -import { render, fireEvent, cleanup } from "@testing-library/react"; -import React from "react"; - -import { AnchorMenu } from "."; - -import { Containers } from "./index.stories"; - -const defaultArgs = { - items: [ - { text: "First container", targetIdName: "first-container" }, - { text: "Second container", targetIdName: "second-container" }, - { text: "Third container", targetIdName: "third-container" }, - { text: "Fourth container", targetIdName: "fourth-container" }, - ], - firstElementId: "first-container", -}; - -const spyScrollTo = jest.fn(); - -const renderAnchorMenu = props => { - return render( - <> - - - - ); -}; - -describe("#Anchor Menu", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - Object.defineProperty(window, "scrollTo", { value: spyScrollTo }); - component = renderAnchorMenu(defaultArgs); - }); - afterEach(cleanup); - - it("should define the component", () => { - expect(component).toBeDefined(); - }); - - it("should scroll to the section specified", async () => { - const itemExample = "first-container"; - const anchor = await component.findByTestId(`anchor-item-${itemExample}`); - fireEvent.click(anchor); - expect(spyScrollTo).toHaveBeenCalled(); - }); -}); diff --git a/packages/components-core/src/components/Article/index.js b/packages/components-core/src/components/Article/index.js deleted file mode 100644 index fee9b8cfbf..0000000000 --- a/packages/components-core/src/components/Article/index.js +++ /dev/null @@ -1,390 +0,0 @@ -// @ts-check -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React from "react"; -import { - EmailShareButton, - EmailIcon, - FacebookIcon, - FacebookShareButton, - LinkedinIcon, - LinkedinShareButton, - TwitterIcon, - TwitterShareButton, -} from "react-share"; -import { Breadcrumb, BreadcrumbItem } from "reactstrap"; - -import { sanitizeDangerousMarkup } from "../../../../../shared"; -import { Button } from "../Button"; -import { Wrapper, EventInfoWrapper } from "./index.styles"; - -/** - * @typedef {import('../../core/types/article-types').ArticleProps} ArticleProps - */ - -/** - * @param {ArticleProps} props - * @returns {JSX.Element} - */ -export const Article = ({ - type, - articleUrl, - publicationDate, - title, - body, - authorEmail, - authorName, - authorPhone, - authorTitle, - breadcrumbs, - calendarUrl, - eventLocation, - eventTime, - headerImageUrl, - registrationUrl, - zoomUrl, -}) => { - const hClasses = classNames("col", "col-12", { - [`col-lg-8`]: - type === "event" && (registrationUrl || zoomUrl || calendarUrl), - }); - - const primaryButton = () => { - if (registrationUrl) { - return ( -
    -
    - ); - } - return ( -
    -
    - ); - }; - - const AuthorInfo = () => { - return ( -
    -
    -
    {authorName}
    - {authorTitle &&
    {authorTitle}
    } - {authorEmail && ( -
    - - - - {authorEmail} -
    - )} - {authorPhone && ( -
    - - - - {authorPhone} -
    - )} -
    -
    - ); - }; - - const EventInfo = () => { - return ( - -
    -

    For more information contact:

    -
    {authorName}
    -
    {authorTitle}
    - {(authorEmail || authorPhone) && ( -
    - {authorEmail && ( - - )} - {authorPhone && ( - - )} -
    - )} -
    -
    -

    Share this event:

    -
    - - - - {/* @ts-ignore */} - - - - {/* @ts-ignore */} - - - - {/* @ts-ignore */} - - - -
    -
    -
    - ); - }; - - const eventLocations = () => { - return ( -
    -

    - - Location: -

    - {/* eslint-disable-next-line react/no-danger */} -
    - {registrationUrl && zoomUrl && Attend on Zoom} -
    - ); - }; - - const activeBreadcrumb = item => { - if (item.active) { - return ( - - - {item.title} - - - ); - } - - return ( - - - {item.title} - - - ); - }; - - return ( - <> - {headerImageUrl && type !== "event" && ( -
    - )} - - - {breadcrumbs && ( -
    -
    - - {breadcrumbs.map(item => activeBreadcrumb(item))} - -
    -
    - )} - -
    -
    -

    {title}

    -
    - {type === "event" && ( -
    - {(registrationUrl || zoomUrl) && primaryButton()} - {calendarUrl && ( -
    -
    - )} -
    - )} -
    - - {type === "event" ? ( -
    - {eventTime && ( -
    -

    - - Date and time: -

    -
    -
    - )} - {((registrationUrl && zoomUrl) || eventLocation) && - eventLocations()} -
    - ) : ( -
    -
    -
    - - - - {/* @ts-ignore */} - - - - {/* @ts-ignore */} - - - -
    - {publicationDate && ( - {publicationDate} - )} -
    -
    - )} - -
    -
    -
    - - {type === "news" && AuthorInfo()} - {type === "event" && EventInfo()} - - - ); -}; - -Article.propTypes = { - /** - * Type of article - */ - type: PropTypes.oneOf(["event", "news"]), - /** - * This is the relative or absolute url to the full-page article - */ - articleUrl: PropTypes.string.isRequired, - /** - * Date for the article - */ - publicationDate: PropTypes.string.isRequired, - /** - * Title - */ - title: PropTypes.string.isRequired, - /** - * Body content for the article - */ - body: PropTypes.string.isRequired, - /** - * Article author email - */ - authorEmail: PropTypes.string, - /** - * Article author full name - */ - authorName: PropTypes.string.isRequired, - /** - * Article author phone number - */ - authorPhone: PropTypes.string, - /** - * Article author title - */ - authorTitle: PropTypes.string, - /** - * Breadcrumbs array - */ - breadcrumbs: PropTypes.arrayOf( - PropTypes.shape({ - title: PropTypes.string, - url: PropTypes.string, - active: PropTypes.bool, - }) - ), - /** - * URL for an "add to calendar" button - */ - calendarUrl: PropTypes.string, - /** - * Article image - */ - headerImageUrl: PropTypes.string, - /** - * Event location - */ - eventLocation: PropTypes.string, - /** - * Event time - */ - eventTime: PropTypes.string, - /** - * URL for a registation button - */ - registrationUrl: PropTypes.string, - /** - * URL for a Zoom button - */ - zoomUrl: PropTypes.string, -}; - -Article.defaultProps = { - type: "news", - authorEmail: undefined, - authorPhone: undefined, - authorTitle: undefined, - breadcrumbs: undefined, - calendarUrl: undefined, - headerImageUrl: undefined, - eventLocation: undefined, - eventTime: undefined, - registrationUrl: undefined, - zoomUrl: undefined, -}; diff --git a/packages/components-core/src/components/Article/index.stories.js b/packages/components-core/src/components/Article/index.stories.js deleted file mode 100644 index 95d8e80e1b..0000000000 --- a/packages/components-core/src/components/Article/index.stories.js +++ /dev/null @@ -1,129 +0,0 @@ -/* eslint react/jsx-props-no-spreading: "off" */ -import React from "react"; - -import { Article } from "."; - -export default { - title: "UDS/Article", - component: Article, - parameters: { - docs: { - description: { - component: `The Article component can be used to generate UDS-compliant news and event articles. - -## Usage - -By default, the Article component will output the news layout. When "event" is passed to the "type" prop, Article will generate the event layout. - -The following props are only rendered for the event layout: - • calendarUrl - • eventTime - • eventLocation - • registrationUrl - • zoomUrl - -The following prop is only rendered for the news layout: - • headerImageUrl - -View component examples and source code below. - `, - }, - }, - }, - argTypes: { - type: { - type: "inline-radio", - options: ["event", "news"], - }, - }, -}; - -const Template = args =>
    ; - -export const News = Template.bind({}); -News.args = { - type: "news", - articleUrl: "https://example.com", - headerImageUrl: "https://source.unsplash.com/WLUHO9A_xik/1920x512", - title: - "Clarisse Machanguana takes her skill set to the next level at ASU Thunderbird", - publicationDate: "March 18, 2021", - authorName: "Jimena Garrison", - authorTitle: "Media Relations and Strategic Communications", - authorEmail: "jgarris6@asu.edu", - authorPhone: "480-727-4058", - breadcrumbs: [ - { - title: "Home", - url: "/", - }, - { - title: "Second nav item", - url: "/events", - }, - { - title: "Current page", - url: "/events/current-article", - active: true, - }, - ], - body: "

    After 34 years in the game of basketball, Clarisse Machanguana retired. Her eponymous philanthropic foundation remains her only connection to the sport, although the effect of the global game has left imprints in many aspects of her life.

    Playing basketball took her to Portugal and then the U.S., where she attended Old Dominion University in Virginia to study criminal justice. When she realized that sports could be a microcosm of life and values, she decided to create a way to coach sports while teaching life skills in her home country of Mozambique. She started the Clarisse Machanguana Foundation in 2014 with the goal of empowering Mozambican youth through health, education and sports programs.

    Now she’s taking her leadership game to the next level at ASU’s Thunderbird School of Global Management, earning her Master of Global Management degree with a nonprofit management concentration. Machanguana is honing her skills as a global professional in and out of the classroom to propel her foundation even further.

    Here she reflects on the experiences that brought her to Thunderbird and ASU.

    Question: Why basketball?

    Answer: I started at age 6, and because it was popular in my area and I was tall — now 6-feet-5-inches tall, to be exact — people kept telling me I should play. Basketball took me everywhere. I had a scholarship to play in Portugal and the U.S., and later on in Spain, France, Brazil, South Korea and Italy. Basketball became a passport and a school for me, and a source of amazing friendships. I played from age 6 to 40. My foundation now partners with the Department of Education. We collaborate with teachers and teach them to coach life skills and basketball.

    I advocate social causes that are challenging for youth and transform them into opportunities, giving them tools to lift themselves out of the poverty they see. They use the skills like respecting your opponent and perseverance that can be applied in life as well as sports. When you wake up and all you see is poverty, you start to believe that mindset of limitations and scarcity. We give young people something else to believe in, a vision of a better life.

    ", -}; - -export const Event = Template.bind({}); -Event.args = { - type: "event", - articleUrl: "https://example.com", - title: - "ASU Book Group: 'There's No Crying in Newsrooms' by Kristin Grady Gilger and Julia Wallace", - publicationDate: "March 18, 2021", - authorName: "Jackie Young", - authorTitle: "ASU Library", - authorEmail: "jacqueline.young@asu.edu", - authorPhone: "480-727-4058", - breadcrumbs: [ - { - title: "Home", - url: "/", - }, - { - title: "Second nav item", - url: "/events", - }, - { - title: "Current page", - url: "/events/current-article", - active: true, - }, - ], - eventTime: "November 33, 2030,
    12:00 p.m. - 1:30 p.m.", - eventLocation: - "Vault Gallery, Downtown Phoenix campus Library
    411 N Central Ave, Phoenix AZ 85004", - registrationUrl: "https://www.aventri.com/", - zoomUrl: "https://zoom.com", - calendarUrl: "#", - body: "

    After 34 years in the game of basketball, Clarisse Machanguana retired. Her eponymous philanthropic foundation remains her only connection to the sport, although the effect of the global game has left imprints in many aspects of her life.

    Playing basketball took her to Portugal and then the U.S., where she attended Old Dominion University in Virginia to study criminal justice. When she realized that sports could be a microcosm of life and values, she decided to create a way to coach sports while teaching life skills in her home country of Mozambique. She started the Clarisse Machanguana Foundation in 2014 with the goal of empowering Mozambican youth through health, education and sports programs.

    Now she’s taking her leadership game to the next level at ASU’s Thunderbird School of Global Management, earning her Master of Global Management degree with a nonprofit management concentration. Machanguana is honing her skills as a global professional in and out of the classroom to propel her foundation even further.

    Here she reflects on the experiences that brought her to Thunderbird and ASU.

    Question: Why basketball?

    Answer: I started at age 6, and because it was popular in my area and I was tall — now 6-feet-5-inches tall, to be exact — people kept telling me I should play. Basketball took me everywhere. I had a scholarship to play in Portugal and the U.S., and later on in Spain, France, Brazil, South Korea and Italy. Basketball became a passport and a school for me, and a source of amazing friendships. I played from age 6 to 40. My foundation now partners with the Department of Education. We collaborate with teachers and teach them to coach life skills and basketball.

    I advocate social causes that are challenging for youth and transform them into opportunities, giving them tools to lift themselves out of the poverty they see. They use the skills like respecting your opponent and perseverance that can be applied in life as well as sports. When you wake up and all you see is poverty, you start to believe that mindset of limitations and scarcity. We give young people something else to believe in, a vision of a better life.

    ", -}; -Event.parameters = { - docs: { - description: { - story: ` -
    `, - }, - }, -}; diff --git a/packages/components-core/src/components/Article/index.styles.js b/packages/components-core/src/components/Article/index.styles.js deleted file mode 100644 index 40c3547bee..0000000000 --- a/packages/components-core/src/components/Article/index.styles.js +++ /dev/null @@ -1,137 +0,0 @@ -import styled from "styled-components"; - -const Wrapper = styled.div` - &.news-container h2 { - font-size: 48px; - } - - &.event-container h2 { - font-size: 40px; - } - - &.news-container img, - &.event-container img { - width: 100%; - max-width: fit-content; - } - - &.wrapper-container h4 { - margin-top: 0; - } - - &.wrapper-container { - max-width: 1300px; - margin: 0 auto; - padding: 0 10%; - } - - &.event-container.wrapper-container { - max-width: 1500px; - } - - &.news-container { - background: #fff; - } - - ol.breadcrumb { - padding: 0; - margin: 0; - } - - ol.breadcrumb > li { - margin-bottom: 0; - } - - .article-social-media { - margin-bottom: 24px; - } - - .react-share__ShareButton { - margin-right: 12px; - } - - i.news-date { - display: inline-block; - margin-bottom: 24px; - } - - .highlight-gold { - box-shadow: -0.15em 0 0 #ffc627, 0.15em 0 0 #ffc627; - background: #ffc627; - } - - .author { - display: inline-block; - margin-top: 30px; - margin-left: -7px; - padding: 0 6px; - font-size: 16px; - font-weight: 600; - line-height: 16px; - } - - .icon-bg { - display: inline-block; - position: relative; - padding: 1px 3.5px; - background-color: maroon; - margin-right: 4px; - border-radius: 4px; - color: white; - font-size: 0.65rem; - } - - .uds-button { - margin-top: 17px; - padding: 0; - } - - .author-title { - max-width: 250px; - margin-top: 10px; - margin-bottom: 11px; - font-size: 16px; - } - - .author-contact { - margin-top: 4px; - font-size: 14px; - font-weight: 500; - } - - ol.breadcrumb a { - text-decoration: none; - } - - i.fa-calendar, - i.fa-map-marker-alt { - font-size: 1.17rem; - margin-right: 8px; - } - - .event-author { - font-size: 16px; - font-weight: 600; - line-height: 16px; - } - - .event-author-title { - margin-top: 4px; - } - - .event-author-info { - margin-top: 24px; - } -`; - -const EventInfoWrapper = styled.div` - border-top: 1px solid black; - h4 { - margin-top: 0; - } - .react-share__ShareButton { - margin-right: 24px; - } -`; - -export { Wrapper, EventInfoWrapper }; diff --git a/packages/components-core/src/components/Article/index.test.js b/packages/components-core/src/components/Article/index.test.js deleted file mode 100644 index 8274de470a..0000000000 --- a/packages/components-core/src/components/Article/index.test.js +++ /dev/null @@ -1,101 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, cleanup } from "@testing-library/react"; -import React from "react"; - -import { Article } from "."; - -const defaultArgs = { - type: "news", - articleUrl: "https://example.com", - headerImageUrl: "https://source.unsplash.com/random/1920x512", - title: - "Clarisse Machanguana takes her skill set to the next level at ASU Thunderbird", - publicationDate: "March 18, 2021", - authorName: "Jimena Garrison", - authorTitle: "Media Relations and Strategic Communications", - authorEmail: "jgarris6@asu.edu", - authorPhone: "480-727-4058", - breadcrumbs: [ - { - title: "Home", - url: "/", - }, - { - title: "Second nav item", - url: "/events", - }, - { - title: "Current page", - url: "/events/current-article", - active: true, - }, - ], - body: "

    After 34 years in the game of basketball, Clarisse Machanguana retired. Her eponymous philanthropic foundation remains her only connection to the sport, although the effect of the global game has left imprints in many aspects of her life.

    Playing basketball took her to Portugal and then the U.S., where she attended Old Dominion University in Virginia to study criminal justice. When she realized that sports could be a microcosm of life and values, she decided to create a way to coach sports while teaching life skills in her home country of Mozambique. She started the Clarisse Machanguana Foundation in 2014 with the goal of empowering Mozambican youth through health, education and sports programs.

    Now she’s taking her leadership game to the next level at ASU’s Thunderbird School of Global Management, earning her Master of Global Management degree with a nonprofit management concentration. Machanguana is honing her skills as a global professional in and out of the classroom to propel her foundation even further.

    Here she reflects on the experiences that brought her to Thunderbird and ASU.

    Question: Why basketball?

    Answer: I started at age 6, and because it was popular in my area and I was tall — now 6-feet-5-inches tall, to be exact — people kept telling me I should play. Basketball took me everywhere. I had a scholarship to play in Portugal and the U.S., and later on in Spain, France, Brazil, South Korea and Italy. Basketball became a passport and a school for me, and a source of amazing friendships. I played from age 6 to 40. My foundation now partners with the Department of Education. We collaborate with teachers and teach them to coach life skills and basketball.

    I advocate social causes that are challenging for youth and transform them into opportunities, giving them tools to lift themselves out of the poverty they see. They use the skills like respecting your opponent and perseverance that can be applied in life as well as sports. When you wake up and all you see is poverty, you start to believe that mindset of limitations and scarcity. We give young people something else to believe in, a vision of a better life.

    ", -}; - -const renderArticle = props => { - return render(
    ); -}; - -describe("#Article", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - component = renderArticle(defaultArgs); - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); - - const sections = [ - [`Breadcrumbs`, `breadcrumbs`], - [`Title`, `title`], - [`Body`, `body`], - ]; - - it.each(sections)("should define %p section", (_, testId) => - expect(component.queryByTestId(testId)).toBeInTheDocument() - ); -}); - -describe("#News Article", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - const props = { ...defaultArgs }; - component = renderArticle(props); - }); - afterEach(cleanup); - - it("should render hero", () => { - expect(component.queryByTestId("uds-hero")).toBeInTheDocument(); - }); - - it('should render "AuthorInfo" section', () => { - expect(component.queryByTestId("author-info")).toBeInTheDocument(); - }); -}); - -describe("#Event Article", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - const props = { ...defaultArgs, type: "event" }; - component = renderArticle(props); - }); - afterEach(cleanup); - - it("should not render hero", () => { - expect(component.queryByTestId("uds-hero")).not.toBeInTheDocument(); - }); - - it('should render "EventInfo" section', () => { - expect(component.queryByTestId("event-info")).toBeInTheDocument(); - }); -}); diff --git a/packages/components-core/src/components/Article/screenshot.png b/packages/components-core/src/components/Article/screenshot.png deleted file mode 100644 index 7aaa296a3a..0000000000 Binary files a/packages/components-core/src/components/Article/screenshot.png and /dev/null differ diff --git a/packages/components-core/src/components/Button/index.js b/packages/components-core/src/components/Button/index.js deleted file mode 100644 index 5a36d9e582..0000000000 --- a/packages/components-core/src/components/Button/index.js +++ /dev/null @@ -1,172 +0,0 @@ -// @ts-check -/* eslint react/jsx-props-no-spreading: "off" */ -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React from "react"; - -import { trackGAEvent } from "../../../../../shared"; - -const gaDefaultObject = { - name: "onclick", - event: "link", - action: "click", - type: "internal link", - region: "main content", -}; - -/** - * @typedef {import('../../core/types/shared-types').ButtonProps} ButtonProps - */ - -/** - * @param {ButtonProps} props - * @returns {JSX.Element} - */ -export const Button = ({ - label, - cardTitle, - ariaLabel, - block, - color, - disabled, - element, - href, - icon, - innerRef, - onClick, - size, - classes, - target, - ...props -}) => { - const btnClasses = classNames("btn", { - [`btn-${color}`]: true, - [`btn-md`]: size === "small", - [`btn-sm`]: size === "xsmall", - [`btn-block`]: block, - [`disabled`]: disabled, - }); - - let Tag = element; - if (href && element === "button") { - Tag = "a"; - } - - const handleClick = text => { - trackGAEvent({ ...gaDefaultObject, text, section: cardTitle }); - onClick?.(); - }; - - return ( - handleClick(label)} - aria-label={ariaLabel} - target={Tag === "a" ? target : null} - > - {icon && } - {label} - - ); -}; - -Button.propTypes = { - /** - * Button label - */ - label: PropTypes.string, - /** - * Card title - */ - cardTitle: PropTypes.string, - /** - ARIA label for accessibility - */ - ariaLabel: PropTypes.string, - /** - Render button as a block-button? - */ - block: PropTypes.bool, - /** - Button background color - */ - color: PropTypes.oneOf(["gold", "maroon", "gray", "dark"]), - /** - Disable the button? - */ - disabled: PropTypes.bool, - - /** - Pass in a Component to override default button element. - For example: react-router Link - */ - element: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.string, - PropTypes.shape({ $$typeof: PropTypes.symbol, render: PropTypes.func }), - PropTypes.arrayOf( - PropTypes.oneOfType([ - PropTypes.func, - PropTypes.string, - PropTypes.shape({ $$typeof: PropTypes.symbol, render: PropTypes.func }), - ]) - ), - ]), - - /** - Link target url; will cause button to be rendered as `` link - */ - href: PropTypes.string, - /** - React Font Awesome icon prefix and name string to be rendered in button label. Ex: ['fab', 'drupal'] - */ - icon: PropTypes.arrayOf(PropTypes.string), - - /** - * ref will only get you a reference to the Button component, use innerRef to - * get a reference to the DOM element (for things like focus management). - */ - innerRef: PropTypes.oneOfType([ - PropTypes.object, - PropTypes.func, - PropTypes.string, - ]), - - /** - Event handler function for `
    -
    - ); -}; - -export const DefaultButton = Template.bind({}); -DefaultButton.args = { - label: "Default Button", - onClick: handleClick, -}; -DefaultButton.parameters = { - docs: { - source: { - code: `const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); -}; - -`, - }, - }, -}; - -export const SmallGoldButton = Template.bind({}); -SmallGoldButton.args = { - color: "gold", - label: "Small Gold Button", - onClick: handleClick, - size: "small", -}; -SmallGoldButton.parameters = { - docs: { - description: { - story: `When the Button component is provided with an onClick handler function, the Button is rendered as a \``, - }, - source: { - code: `const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); -}; - -`, - }, - }, -}; - -export const IconButton = Template.bind({}); -IconButton.args = { - color: "gold", - icon: ["fas", "rocket"], - label: "Icon Button", - onClick: handleClick, -}; -IconButton.parameters = { - docs: { - description: { - story: `To include a Font Awesome icon in the button label, import the desired React icon and pass it to the Button component via the \`icon\` prop.: - - const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); - }; - - `, - }, - source: { - code: ` - const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); - }; - - `, - }, - }, -}; - -export const LinkButton = Template.bind({}); -LinkButton.args = { - color: "maroon", - href: "/#example-link", - label: "Link Button", -}; -LinkButton.parameters = { - docs: { - description: { - story: `To render a button-style link, \`\`-tag, use the \`href\` prop to set the destination url for this link. When a button is primarily intended for page navigation, this is the recommended solution for accessibility. - - `, - }, - source: { - code: ``, - }, - }, -}; - -export const TargetBlankButton = Template.bind({}); -TargetBlankButton.args = { - color: "maroon", - href: "/#example-link", - label: "Link Button", - target: "_blank", -}; - -const ReactRouterTemplate = args => ( - -
    -
    -
    -
    -
    -); - -export const ReactRouterLinkButton = ReactRouterTemplate.bind({}); -ReactRouterLinkButton.args = { - color: "gold", - element: Link, - label: "React Router Link", - to: "/#example-link", -}; -ReactRouterLinkButton.parameters = { - docs: { - description: { - story: `To use the React Router Link in the Button component, pass an instance of \`\` to the \`element\` prop and any additional props required by Link, e.g. the \`to\` prop required for the destination URL. The rendered link will leverage all features of React Router, and be visually styled for UDS. - - import { BrowserRouter as Router, Link } from "react-router-dom"; - - - <...> - - ); -}; - -ButtonIconOnly.propTypes = { - /** - Color the button based on the background color - */ - color: PropTypes.oneOf(["white", "gray", "black"]), - /** - React Font Awesome icon prefix and name string to be rendered in button label. Ex: ['fab', 'drupal'] - */ - icon: PropTypes.arrayOf(PropTypes.string), - /** - * ref will only get you a reference to the Button component, use innerRef to - * get a reference to the DOM element (for things like focus management). - */ - innerRef: PropTypes.oneOfType([ - PropTypes.object, - PropTypes.func, - PropTypes.string, - ]), - /** - Event handler function for ``, - }, - source: { - code: ` - const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); -}; - -`, - }, - }, -}; - -export const NextButton = Template.bind({}); -NextButton.args = { - color: "white", - icon: ["fas", "chevron-right"], - onClick: handleClick, - size: "large", -}; -NextButton.parameters = { - docs: { - description: { - story: ` - const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); - }; - - `, - }, - source: { - code: ` - const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); -}; - -`, - }, - }, -}; - -export const PrevButton = Template.bind({}); -PrevButton.args = { - color: "white", - icon: ["fas", "chevron-left"], - onClick: handleClick, - size: "large", -}; -PrevButton.parameters = { - docs: { - description: { - story: ` - const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); - }; - - `, - }, - source: { - code: ` - const handleClick = e => { - e.preventDefault(); - alert("The button was clicked."); -}; - -`, - }, - }, -}; diff --git a/packages/components-core/src/components/ButtonIconOnly/index.test.js b/packages/components-core/src/components/ButtonIconOnly/index.test.js deleted file mode 100644 index 00ef32d1bf..0000000000 --- a/packages/components-core/src/components/ButtonIconOnly/index.test.js +++ /dev/null @@ -1,40 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, fireEvent, cleanup } from "@testing-library/react"; -import React from "react"; - -import { ButtonIconOnly } from "."; - -const buttonOnClick = jest.fn(); - -const defaultArgs = { - color: "white", - icon: ["fas", "times"], - onClick: buttonOnClick, -}; - -const renderButtonIcon = props => { - return render(); -}; - -describe("#Button Icon", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - /** @type {ChildNode} */ - let element; - - beforeEach(() => { - component = renderButtonIcon(defaultArgs); - element = component.container.firstChild; - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); - - it("should call onClick", () => { - fireEvent.click(element); - expect(buttonOnClick).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/components-core/src/components/ButtonTag/index.js b/packages/components-core/src/components/ButtonTag/index.js deleted file mode 100644 index f90aff731e..0000000000 --- a/packages/components-core/src/components/ButtonTag/index.js +++ /dev/null @@ -1,140 +0,0 @@ -// @ts-check -/* eslint react/jsx-props-no-spreading: "off" */ -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React from "react"; - -import { trackGAEvent } from "../../../../../shared"; - -const gaDefaultObject = { - name: "onclick", - event: "link", - action: "click", - type: "internal link", - region: "main content", -}; - -/** - * @typedef {import('../../core/types/shared-types').TagsProps} ButtonTagProps - */ - -/** - * @param {ButtonTagProps} props - * @returns {JSX.Element} - */ -export const ButtonTag = ({ - label, - cardTitle, - ariaLabel, - color, - disabled, - element, - innerRef, - href, - onClick, - ...props -}) => { - const btnClasses = classNames("btn", `btn-tag`, { - [`btn-tag-alt-white`]: color === "white", - [`btn-tag-alt-gray`]: color === "gray", - [`btn-tag-alt-dark`]: color === "dark", - [`disabled`]: disabled, - }); - - let Tag = element; - if (href && element === "button") { - Tag = "a"; - } - - const handleClick = text => { - trackGAEvent({ ...gaDefaultObject, text, section: cardTitle }); - onClick?.(); - }; - - return ( - // @ts-ignore - handleClick(label)} - aria-label={ariaLabel} - > - {label} - - ); -}; - -ButtonTag.propTypes = { - /** - Button tag label - */ - label: PropTypes.string, - /** - * Card title - */ - cardTitle: PropTypes.string, - /** - ARIA label for accessibility - */ - ariaLabel: PropTypes.string, - /** - Button background color - */ - color: PropTypes.oneOf(["white", "gray", "dark"]), - /** - Disable the button? - */ - disabled: PropTypes.bool, - - /** - Pass in a Component to override default button element. - For example: react-router Link - */ - element: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.string, - PropTypes.shape({ $$typeof: PropTypes.symbol, render: PropTypes.func }), - PropTypes.arrayOf( - PropTypes.oneOfType([ - PropTypes.func, - PropTypes.string, - PropTypes.shape({ $$typeof: PropTypes.symbol, render: PropTypes.func }), - ]) - ), - ]), - - /** - Link target url; will cause button to be rendered as `
    ` link - */ - href: PropTypes.string, - - /** - * ref will only get you a reference to the Button component, use innerRef to - * get a reference to the DOM element (for things like focus management). - */ - innerRef: PropTypes.oneOfType([ - PropTypes.object, - PropTypes.func, - PropTypes.string, - ]), - - /** - Event handler function for `
    - ))} -
    - )} - {linkUrl && linkLabel && ( - - )} - {tags && ( -
    - {tags.map(tag => ( - // @ts-ignore - - ))} -
    - )} - -); - -CardContent.propTypes = { - type: PropTypes.oneOf(["default", "degree", "event", "news", "story"]), - body: PropTypes.string, - eventFormat: PropTypes.oneOf(["stack", "inline"]), - eventLocation: PropTypes.string, - eventTime: PropTypes.string, - title: PropTypes.string.isRequired, - buttons: PropTypes.arrayOf( - PropTypes.shape({ - ariaLabel: PropTypes.string, - color: PropTypes.oneOf(["gold", "maroon", "gray", "dark"]), - icon: PropTypes.arrayOf(PropTypes.string), - href: PropTypes.string, - label: PropTypes.string, - onClick: PropTypes.func, - size: PropTypes.oneOf(["default", "small", "xsmall"]), - target: PropTypes.oneOf(["_blank", "_self", "_top", "_parent"]), - }) - ), - linkLabel: PropTypes.string, - linkUrl: PropTypes.string, - tags: PropTypes.arrayOf( - PropTypes.shape({ - ariaLabel: PropTypes.string, - color: PropTypes.oneOf(["white", "gray", "dark"]), - href: PropTypes.string, - label: PropTypes.string, - onClick: PropTypes.func, - }) - ), - cardLink: PropTypes.string, -}; - -CardContent.defaultProps = { - type: "default", - body: "", - eventFormat: "stack", - eventLocation: "", - eventTime: "", - buttons: undefined, - linkLabel: undefined, - linkUrl: undefined, - tags: undefined, -}; - -const EventInfo = ({ eventFormat, eventTime, eventLocation }) => { - if (eventFormat === "inline") { - return ( -
    - {eventTime && ( -
    -
    - -
    - {/* eslint-disable-next-line react/no-danger */} -
    -
    - )} - {eventLocation && ( -
    -
    - -
    -
    -
    - )} -
    - ); - } - - // else "stacked" - return ( - <> - {eventTime && ( -
    -
    -
    - -
    - {/* eslint-disable-next-line react/no-danger */} -
    -
    -
    - )} - {eventLocation && ( -
    -
    -
    - -
    - -
    - -
    -
    - )} - - ); -}; - -EventInfo.propTypes = { - eventFormat: PropTypes.oneOf(["stack", "inline"]), - eventLocation: PropTypes.string, - eventTime: PropTypes.string, -}; - -EventInfo.defaultProps = { - eventFormat: "stack", - eventLocation: "", - eventTime: "", -}; diff --git a/packages/components-core/src/components/Card/index.stories.js b/packages/components-core/src/components/Card/index.stories.js deleted file mode 100644 index 8db4874ed5..0000000000 --- a/packages/components-core/src/components/Card/index.stories.js +++ /dev/null @@ -1,328 +0,0 @@ -/* eslint react/jsx-props-no-spreading: "off" */ -import classNames from "classnames"; -import React from "react"; - -import { Card } from "."; - -export default { - title: "UDS/Card", - component: Card, - parameters: { - docs: { - description: { - component: `The Card component can be used to generate UDS-compliant default, degree, story, news and event cards. - -## Usage - -Most props are valid options for all card types, except for the event-specific props. -Card users are responsible to meet all UDS design guidelines with their Cards, -including rules on the use of Call-to-Action buttons and tags. - -View component examples and source code below. - `, - }, - }, - }, -}; - -const Template = args => ( -
    -
    -
    - -
    -
    -
    -); - -const HorizontalTemplate = args => ( -
    -
    -
    - -
    -
    -
    -); - -export const Default = Template.bind({}); -Default.args = { - type: "default", - horizontal: false, - image: "https://source.unsplash.com/WLUHO9A_xik/300x200", - imageAltText: "An example image", - title: "Default title", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", - buttons: [ - { - color: "maroon", - size: "default", - label: "CTA button", - }, - { - color: "gold", - size: "small", - label: "Link Button", - href: "/", - target: "_top", - }, - ], - tags: [ - { color: "gray", label: "tag1", href: "/#example-link" }, - { color: "gray", label: "tag2", href: "/#example-link" }, - { color: "gray", label: "tag3", href: "/#example-link" }, - ], - showBorders: true, -}; - -export const DefaultBorderless = Template.bind({}); -DefaultBorderless.args = { - ...Default.args, - title: "Default card - borderless", - showBorders: false, -}; -DefaultBorderless.storyName = "Default (borderless)"; - -export const Icon = Template.bind({}); -Icon.args = { - type: "default", - horizontal: false, - title: "Default title", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", - buttons: [ - { - color: "maroon", - size: "default", - label: "Button text", - }, - ], - icon: ["fas", "newspaper"], -}; - -export const Degree = Template.bind({}); -Degree.args = { - type: "degree", - horizontal: false, - image: "https://source.unsplash.com/WLUHO9A_xik/300x200", - imageAltText: "An example image", - title: "Default title", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", -}; -Degree.parameters = { - docs: { - description: { - story: ` - - `, - }, - }, -}; - -export const Event = Template.bind({}); -Event.args = { - type: "event", - horizontal: false, - image: "https://source.unsplash.com/WLUHO9A_xik/300x200", - imageAltText: "An example image", - title: - "Event title Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore exercitationem ad voluptatem dolore dolores nulla ipsam quo distinctio expedita doloribus nisi similique obcaecati velit illo autem numquam iusto, rem nesciunt repellendus laborum. Rerum quisquam, soluta aspernatur a harum dolor ducimus nulla. Itaque aliquam cum fugiat error esse ipsam rerum consectetur!", - cardLink: "#example-link", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", - eventFormat: "stack", - eventTime: "Wed, November 21st, 2021
    11:30 a.m - 12:30 p.m.", - eventLocation: "Downtown Phoenix campus", - buttons: [ - { - color: "maroon", - size: "default", - label: "CTA button", - }, - ], -}; -Event.parameters = { - docs: { - description: { - story: ` -The event props, \`eventLocation\` and \`eventTime\` are allowed to contain basic HTML formatting, primarily \`
    \` for line breaks. - - - `, - }, - }, -}; - -export const Story = Template.bind({}); -Story.args = { - type: "story", - horizontal: false, - image: "https://source.unsplash.com/WLUHO9A_xik/300x200", - imageAltText: "An example image", - title: "Story title", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", - buttons: [ - { - color: "maroon", - size: "default", - label: "CTA button", - href: "https://google.com", - target: "_blank", - }, - ], - linkLabel: "Default link", - linkUrl: "https://google.com", - tags: [ - { color: "gray", label: "tag1", href: "/#example-link" }, - { color: "gray", label: "tag2", href: "/#example-link" }, - { color: "gray", label: "tag3", href: "/#example-link" }, - ], -}; -Story.parameters = { - docs: { - description: { - story: ` - - `, - }, - }, -}; - -export const HorizontalStoryCard = HorizontalTemplate.bind({}); -HorizontalStoryCard.args = { - type: "story", - horizontal: true, - image: "https://source.unsplash.com/WLUHO9A_xik/300x200", - imageAltText: "An example image", - title: "Horizontal story", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", - tags: [ - { color: "gray", label: "tag1", href: "/#example-link" }, - { color: "gray", label: "tag2", href: "/#example-link" }, - { color: "gray", label: "tag3", href: "/#example-link" }, - ], -}; -HorizontalStoryCard.parameters = { - docs: { - description: { - story: ` - - `, - }, - }, -}; - -export const AsuNewsStory = Template.bind({}); -AsuNewsStory.args = { - type: "story", - horizontal: false, - image: "https://source.unsplash.com/WLUHO9A_xik/300x200", - imageAltText: "An example image", - title: "ASU News Story title", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", - buttons: [ - { - color: "maroon", - size: "default", - label: "Read at ASU News", - }, - ], -}; -AsuNewsStory.parameters = { - docs: { - description: { - story: ` - - `, - }, - }, -}; diff --git a/packages/components-core/src/components/Card/index.styles.js b/packages/components-core/src/components/Card/index.styles.js deleted file mode 100644 index e1abb8ed67..0000000000 --- a/packages/components-core/src/components/Card/index.styles.js +++ /dev/null @@ -1,15 +0,0 @@ -import styled from "styled-components"; - -const CardWrapper = styled.div` - &.cards-components a + &.cards-components a { - margin-left: 5px; - } - .card-button { - column-gap: 1rem; - } - .card-button .btn { - margin: 0; - } -`; - -export { CardWrapper }; diff --git a/packages/components-core/src/components/Card/index.test.js b/packages/components-core/src/components/Card/index.test.js deleted file mode 100644 index 9c7ed1811f..0000000000 --- a/packages/components-core/src/components/Card/index.test.js +++ /dev/null @@ -1,103 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, cleanup } from "@testing-library/react"; -import React from "react"; - -import { Card } from "."; - -const defaultArgs = { - type: "default", - horizontal: false, - clickable: false, - image: "https://picsum.photos/300/200", - imageAltText: "An example image", - title: "Default title", - body: "(Bold!) Body copy goes here. Limit to 5 lines max. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua eiusmod tempo.", - buttons: [ - { - color: "maroon", - size: "default", - label: "CTA button", - }, - { - color: "gold", - size: "small", - label: "Link Button", - href: "/", - target: "_top", - }, - ], - tags: [ - { color: "gray", label: "tag1", href: "/#example-link" }, - { color: "gray", label: "tag2", href: "/#example-link" }, - { color: "gray", label: "tag3", href: "/#example-link" }, - ], - linkUrl: "/#", - linkLabel: "Link", -}; - -const renderCard = props => { - return render(); -}; - -describe("#Card", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - component = renderCard(defaultArgs); - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); - - const sections = [ - [`Image`, `card-image`], - [`Title`, `card-title`], - [`Body`, `card-body`], - [`Link`, `card-link`], - [`Tags`, `card-tags`], - ]; - - test.each(sections)("should define %p element", (_, testId) => - expect(component.queryByTestId(testId)).toBeInTheDocument() - ); -}); - -describe("#Card options", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - afterEach(cleanup); - - describe("#Icon card", () => { - const iconProps = { - ...defaultArgs, - image: "", - icon: ["fas", "newspaper"], - }; - - beforeEach(() => { - component = renderCard(iconProps); - }); - - it("should render icon element", () => { - expect(component.queryByTestId("card-icon")).toBeInTheDocument(); - }); - }); - const types = [ - [`degree`, `card-degree`], - [`event`, `card-event`], - [`story`, `card-story`], - ]; - - test.each(types)("should define %p type of card", (type, className) => { - const customProps = { ...defaultArgs, type }; - component = renderCard(customProps); - expect(component.queryByTestId("card-container")?.className).toContain( - className - ); - }); -}); diff --git a/packages/components-core/src/components/Card/utils/emailAddressParser.js b/packages/components-core/src/components/Card/utils/emailAddressParser.js deleted file mode 100644 index 6f202d351b..0000000000 --- a/packages/components-core/src/components/Card/utils/emailAddressParser.js +++ /dev/null @@ -1,16 +0,0 @@ -// this is a little workaround to be able to use email addresses instead of regular links - -const validEmail = email => { - // regexp for valid email addresses - const emailRegexp = /^[A-Z0-9._+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i; - return emailRegexp.test(email); -}; - -export const emailAddressParser = url => { - if (!url.startsWith("https://") && !url.startsWith("http://")) { - if (validEmail(url)) { - return `mailto:${url}`; - } - } - return url; -}; diff --git a/packages/components-core/src/components/FeedAnatomy/FeedBody.js b/packages/components-core/src/components/FeedAnatomy/FeedBody.js deleted file mode 100644 index 68d6e16c8c..0000000000 --- a/packages/components-core/src/components/FeedAnatomy/FeedBody.js +++ /dev/null @@ -1,23 +0,0 @@ -// @ts-check -import PropTypes from "prop-types"; -import React from "react"; - -/** - * - * @param {{ - * children: JSX.Element - * }} props - * @returns {JSX.Element} - * @ignore - */ -const FeedBody = ({ children }) => ( -
    -
    {children}
    -
    -); - -FeedBody.propTypes = { - children: PropTypes.element, -}; - -export { FeedBody }; diff --git a/packages/components-core/src/components/FeedAnatomy/FeedContainerContext.js b/packages/components-core/src/components/FeedAnatomy/FeedContainerContext.js deleted file mode 100644 index 17122a4b58..0000000000 --- a/packages/components-core/src/components/FeedAnatomy/FeedContainerContext.js +++ /dev/null @@ -1,93 +0,0 @@ -/* eslint-disable react/prop-types */ -// @ts-check -import PropTypes from "prop-types"; -import React, { useState, useEffect, createContext } from "react"; -import styled from "styled-components"; - -import { useFetch } from "../../../../../shared"; -import { Loader } from "../Loader"; - -const Container = styled.section``; - -const FeedContext = createContext({ feeds: [] }); - -/** - * This component is the HOC(high order component) used on component-events and component-news packages - * @param {{ - * renderHeader: JSX.Element - * renderBody: JSX.Element - * dataSource: { - * url: string - * filters: string - * } - * maxItems?: number - * dataTransformer?: (data: object) => object - * dataFilter?: (data: object, filters: string) => object - * defaultProps: import("../../core/types/feed-types").FeedType - * noFeedText: string - * }} props - * @returns {JSX.Element} - * @ignore - */ -const FeedContainerProvider = ({ - defaultProps, - dataSource: pDataSource, - noFeedText, - renderHeader, - renderBody, - dataTransformer = item => item, - dataFilter = item => item, - maxItems, -}) => { - const [{ data: rawData, loading, error }, doFetching] = useFetch(); // Call the fetching hook - const [feeds, setFeeds] = useState([]); - - const dataSource = { ...defaultProps.dataSource, ...pDataSource }; - - useEffect(() => { - doFetching(dataSource?.url); - }, [dataSource?.url]); - - useEffect(() => { - // Work all the data and set the filterd and mapped feeds - const transformedData = rawData?.nodes.map(dataTransformer); - const filteredData = transformedData?.filter(item => - dataFilter(item, pDataSource?.filters) - ); - setFeeds(maxItems ? filteredData?.slice(0, maxItems) : filteredData); - }, [rawData]); - - return ( - // Init the context to be used on its childrens - - - {renderHeader} - {error ? ( - Error, try again! - ) : ( - <> - {loading && !feeds?.length && ( -
    - -
    - )} - {feeds?.length - ? renderBody - : !loading &&

    {noFeedText}

    } - - )} -
    -
    - ); -}; - -FeedContainerProvider.propTypes = { - renderHeader: PropTypes.element, - renderBody: PropTypes.element, - maxItems: PropTypes.number, - dataTransformer: PropTypes.func, - dataFilter: PropTypes.func, - noFeedText: PropTypes.string, -}; - -export { FeedContainerProvider, FeedContext }; diff --git a/packages/components-core/src/components/FeedAnatomy/FeedHeader.js b/packages/components-core/src/components/FeedAnatomy/FeedHeader.js deleted file mode 100644 index 5b05d08d92..0000000000 --- a/packages/components-core/src/components/FeedAnatomy/FeedHeader.js +++ /dev/null @@ -1,75 +0,0 @@ -// @ts-check -import React from "react"; -import styled from "styled-components"; - -import { trackGAEvent } from "../../../../../shared"; -import { - feedComponentShape, - feedCtaButtonShape, - feedHeaderShape, -} from "./feed-prop-types"; - -const ButtonColumn = styled.div` - @media screen and (min-width: 768px) { - & { - display: inline-flex; - justify-content: flex-end; - } - } -`; - -/** - * - * @param {{ - * defaultProps: import("../../core/types/feed-types").FeedType - * header: import("../../core/types/feed-types").FeedHeader - * ctaButton: import("../../core/types/feed-types").FeedCtaButton - * }} props - * @returns {JSX.Element} - */ -const FeedHeader = ({ - defaultProps, - header: pHeeder, - ctaButton: pCtaButton, -}) => { - const header = { ...defaultProps.header, ...pHeeder }; - const ctaButton = { ...defaultProps.ctaButton, ...pCtaButton }; - - return ( - - ); -}; - -FeedHeader.propTypes = { - defaultProps: feedComponentShape, - header: feedHeaderShape, - ctaButton: feedCtaButtonShape, -}; - -export { FeedHeader }; diff --git a/packages/components-core/src/components/FeedAnatomy/feed-prop-types.js b/packages/components-core/src/components/FeedAnatomy/feed-prop-types.js deleted file mode 100644 index 6ca5532234..0000000000 --- a/packages/components-core/src/components/FeedAnatomy/feed-prop-types.js +++ /dev/null @@ -1,38 +0,0 @@ -// @ts-check -import PropTypes from "prop-types"; - -const feedHeaderShape = PropTypes.shape({ - color: PropTypes.oneOf(["white", "dark"]), - text: PropTypes.string, -}); - -const feedCtaButtonShape = PropTypes.shape({ - color: PropTypes.oneOf(["gold", "maroon", "gray", "dark"]), - text: PropTypes.string, -}); - -const feedCardButtonShape = PropTypes.shape({ - color: PropTypes.oneOf(["gold", "maroon", "gray", "dark"]), - text: PropTypes.string, - size: PropTypes.string, -}); - -const feedDataSourceShape = PropTypes.shape({ - url: PropTypes.string, - filters: PropTypes.string, -}); - -const feedComponentShape = PropTypes.shape({ - header: feedHeaderShape, - ctaButton: feedCtaButtonShape, - dataSource: feedDataSourceShape, - maxItems: PropTypes.number, -}); - -export { - feedComponentShape, - feedHeaderShape, - feedCtaButtonShape, - feedDataSourceShape, - feedCardButtonShape, -}; diff --git a/packages/components-core/src/components/FeedAnatomy/index.js b/packages/components-core/src/components/FeedAnatomy/index.js deleted file mode 100644 index a2c1beb5c8..0000000000 --- a/packages/components-core/src/components/FeedAnatomy/index.js +++ /dev/null @@ -1,6 +0,0 @@ -// @ts-check - -export * from "./FeedContainerContext"; -export * from "./feed-prop-types"; -export * from "./FeedHeader"; -export * from "./FeedBody"; diff --git a/packages/components-core/src/components/FeedAnatomy/index.test.js b/packages/components-core/src/components/FeedAnatomy/index.test.js deleted file mode 100644 index a18bdce8ff..0000000000 --- a/packages/components-core/src/components/FeedAnatomy/index.test.js +++ /dev/null @@ -1,74 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, cleanup, act } from "@testing-library/react"; -import React from "react"; - -import { FeedContainerProvider, FeedHeader, FeedBody } from "."; - -/** @type {import("../../core/types/feed-types").FeedType} */ -const defaultProps = { - header: { - color: "dark", - text: "Knowledge and enterprise news", - }, - ctaButton: { - color: "gold", - text: "More stories and videos", - url: "https://news.asu.edu", - }, - dataSource: { - url: "https://asunow.asu.edu/feeds-json", - }, -}; -const dataTransformerMock = jest.fn(item => item); -const dataFilterMock = jest.fn(() => true); -const renderHeaderMock = ( - -); -const renderBodyMock = ( - - <> - -); - -const defaulArgs = { - defaultProps, - dataSource: {}, - renderHeader: renderHeaderMock, - renderBody: renderBodyMock, - dataTransformer: dataTransformerMock, - dataFilter: dataFilterMock, -}; - -describe("#FeedContainerContext", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - const renderFeedContainer = async props => { - await act(async () => { - component = await render(); - }); - }; - - beforeEach(async () => { - await renderFeedContainer(defaulArgs); - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); - - const sections = [ - ["Header", "feed-header"], - ["Body", "feed-body"], - ]; - - test.each(sections)("should define %p section", (_, testId) => - expect(component.queryByTestId(testId)).toBeInTheDocument() - ); -}); diff --git a/packages/components-core/src/components/Hero/index.js b/packages/components-core/src/components/Hero/index.js deleted file mode 100644 index 885e73d9a7..0000000000 --- a/packages/components-core/src/components/Hero/index.js +++ /dev/null @@ -1,160 +0,0 @@ -// @ts-check -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React from "react"; - -import { - contentPropType, - imagePropType, -} from "../../core/models/shared-prop-types"; -import { HeroImage } from "./index.styles"; - -/** - * @typedef {import('../../core/types/hero-types').HeroProps} HeroProps - */ - -/** - * @param {HeroProps} props - * @returns {JSX.Element} - * @ignore - */ -function storyHeroHtmlTemplate({ image, title, contents }) { - // eslint-disable-next-line no-console - console.log({ image, title, contents }); - // TODO: to be implemented - return
    TODO: to be implemented
    ; -} - -/** - * @param {HeroProps} props - * @returns {JSX.Element} - * @ignore - */ -function headingHeroHtmlTemplate({ - image, - subTitle, - title, - contents, - contentsColor, -}) { - const imageSize = { - small: "uds-hero-sm", - medium: "uds-hero-md", - large: "uds-hero-lg", - undefined: "", - }; - - const highlightColor = { - gold: "highlight-gold", - black: "highlight-black", - white: "highlight-white", - none: "text-white", - undefined: "", - }; - - const textColor = { - black: "text-dark", - white: "text-white", - undefined: "", - }; - - let titleElement = <>; - - if (title) { - titleElement = ( -

    - - {title.text} - -

    - ); - } - - if (title && subTitle) { - titleElement = ( -
    -

    - - {subTitle.text} - -

    - {titleElement} -
    - ); - } - - return ( -
    -
    - - {titleElement} - {contents && ( -
    - {contents.map((content, index) => ( - // eslint-disable-next-line react/no-array-index-key -

    {content.text}

    - ))} -
    - )} -
    - ); -} - -/** - * - * @param {HeroProps} props - * @returns {JSX.Element} - */ -const Hero = props => { - const type = props.type || "heading-hero"; - - const templateTypes = { - "heading-hero": () => headingHeroHtmlTemplate(props), - "story-hero": () => storyHeroHtmlTemplate(props), - "undefined": () => { - // eslint-disable-next-line no-console - console.error( - `the type '${type}' is not supported by the 'Hero' component.` - ); - return null; - }, - }; - - return templateTypes[type](); -}; - -Hero.propTypes = { - type: PropTypes.oneOf(["heading-hero", "story-hero"]), - image: imagePropType, - title: contentPropType, - subTitle: contentPropType, - contents: PropTypes.arrayOf(contentPropType), - contentsColor: PropTypes.string, -}; - -export { Hero }; diff --git a/packages/components-core/src/components/Hero/index.stories.js b/packages/components-core/src/components/Hero/index.stories.js deleted file mode 100644 index fffbe6022e..0000000000 --- a/packages/components-core/src/components/Hero/index.stories.js +++ /dev/null @@ -1,164 +0,0 @@ -/* eslint-disable react/prop-types */ -// @ts-check -import React from "react"; - -import { Hero } from "."; - -import { imageName } from "../../../../../shared/assets"; - -/** - * @typedef {import('.').HeroProps} HeroProps - */ - -export default { - component: Hero, - title: "UDS/Heroes", - parameters: { - docs: { - description: { - component: " ", - }, - }, - }, - argTypes: { - title: { - control: { - type: "object", - }, - description: "Options for highlightColor are gold, white, black, or none", - }, - }, -}; - -/** - * @param {HeroProps} props - * @returns {JSX.Element} - */ -const Template = props => ; - -/** - * @type {{ args: HeroProps, parameters: object }} - */ -export const HeroSmall = Template.bind({}); - -HeroSmall.args = { - image: { - url: imageName.hero01, - altText: "Hero image", - size: "small", - }, - subTitle: { - text: "Sub title", - highlightColor: "black", - }, - title: { - text: "Heading with a long title 1", - highlightColor: "none", - }, -}; -HeroSmall.parameters = { - docs: { - description: { - story: " ", - }, - }, -}; - -/** - * @type {{ args: HeroProps, parameters: object }} - */ -export const HeroLongTitle = Template.bind({}); - -HeroLongTitle.args = { - image: { - url: imageName.hero01, - altText: "Hero image", - size: "small", - }, - title: { - text: "Heading with a long title 2", - highlightColor: "gold", - maxWidth: "100%", - }, - subTitle: { - text: "Sub title", - highlightColor: "black", - }, -}; -HeroLongTitle.parameters = { - docs: { - description: { - story: " ", - }, - }, -}; - -/** - * @type {{ args: HeroProps, parameters: object }} - */ -export const HeroMedium = Template.bind({}); - -HeroMedium.args = { - image: { - url: imageName.hero01, - altText: "Hero image", - size: "medium", - }, - title: { - text: "Heading 1", - highlightColor: "black", - }, - subTitle: { - text: "Sub title", - highlightColor: "gold", - }, - contentsColor: "white", - contents: [ - { - text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.`, - }, - ], -}; -HeroMedium.parameters = { - docs: { - description: { - story: " ", - }, - }, -}; - -/** - * @type {{ args: HeroProps, parameters: object }} - */ -export const HeroLarge = Template.bind({}); - -HeroLarge.args = { - image: { - url: imageName.hero01, - altText: "Hero image", - size: "large", - }, - title: { - text: "Heading 1", - color: "white", - }, - subTitle: { - text: "Sub title", - highlightColor: "black", - }, - contentsColor: "white", - contents: [ - { - text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.`, - }, - ], -}; -HeroLarge.parameters = { - docs: { - description: { - story: " ", - }, - }, -}; diff --git a/packages/components-core/src/components/Hero/index.styles.js b/packages/components-core/src/components/Hero/index.styles.js deleted file mode 100644 index ae5d8f432e..0000000000 --- a/packages/components-core/src/components/Hero/index.styles.js +++ /dev/null @@ -1,7 +0,0 @@ -import styled from "styled-components"; - -const HeroImage = styled.img` - width: 100%; -`; - -export { HeroImage }; diff --git a/packages/components-core/src/components/Hero/index.test.js b/packages/components-core/src/components/Hero/index.test.js deleted file mode 100644 index ad2a27611b..0000000000 --- a/packages/components-core/src/components/Hero/index.test.js +++ /dev/null @@ -1,75 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, cleanup } from "@testing-library/react"; -import React from "react"; - -import { Hero } from "."; - -const defaultArgs = { - image: { - url: "https://source.unsplash.com/random/800x400?a=1", - altText: "Hero image", - size: "large", - }, - title: { text: "Heading 1", color: "white" }, - subTitle: { text: "Subtitle 1", color: "white" }, - contentsColor: "white", - contents: [ - { - text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.`, - }, - ], -}; - -const renderHero = props => { - return render(); -}; - -describe("#Hero", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - component = renderHero(defaultArgs); - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); - - const sections = [ - [`Image`, `hero-image`], - [`Title`, `hero-title`], - [`Subtitle`, `hero-subtitle`], - [`Content`, `hero-content`], - ]; - - test.each(sections)("should define %p section", (_, testId) => - expect(component.queryByTestId(testId)).toBeInTheDocument() - ); -}); - -describe("#Hero without content and subtitle", () => { - it("should render the title only", () => { - const props = { - image: { - url: "https://source.unsplash.com/random/800x400?a=1", - altText: "Hero image", - size: "small", - }, - title: { - text: "Heading with a long title 1", - highlightColor: "gold", - }, - }; - - const component = renderHero(props); - - expect(component.queryByTestId("hero-image")).toBeInTheDocument(); - expect(component.queryByTestId("hero-title")).toBeInTheDocument(); - expect(component.queryByTestId("hero-subtitle")).not.toBeInTheDocument(); - expect(component.queryByTestId("hero-content")).not.toBeInTheDocument(); - }); -}); diff --git a/packages/components-core/src/components/Image/index.js b/packages/components-core/src/components/Image/index.js deleted file mode 100644 index 2a5ef73272..0000000000 --- a/packages/components-core/src/components/Image/index.js +++ /dev/null @@ -1,130 +0,0 @@ -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React from "react"; - -// eslint-disable-next-line import/no-cycle -import { spreadClasses } from "../../../../../shared"; -import { sanitizeDangerousMarkup } from "../../../../../shared/utils"; - -/** - * @typedef {import('../../core/types/image-types').ImageComponentProps} ImageComponentProps - */ - -/** - * @param {ImageComponentProps} props - * @returns {JSX.Element} - */ - -export const Image = ({ - src, - alt, - cssClasses, - loading = "lazy", - decoding = "async", - dataTestId, - fetchPriority = "auto", - width, - height, - cardLink, - title, - caption, - captionTitle, - border, - dropShadow, -}) => { - const imageProps = { - src, - alt, - loading, - decoding, - fetchpriority: fetchPriority, // React attribute bug workaround - ...(cssClasses?.length > 0 && { className: spreadClasses(cssClasses) }), - ...(dataTestId && { "data-testid": dataTestId }), - ...(width && { width }), - ...(height && { height }), - }; - - const borderAndDropShadowClasses = classNames("uds-img", { - "borderless": !border, - "uds-img-drop-shadow": dropShadow, - }); - - const renderImage = classes => { - const combinedClasses = classes - ? `${imageProps.className} ${classes}` - : imageProps.className; - return cardLink ? ( - - {/* eslint-disable-next-line jsx-a11y/alt-text, react/jsx-props-no-spreading */} - - {title} - - ) : ( - // eslint-disable-next-line jsx-a11y/alt-text, react/jsx-props-no-spreading - - ); - }; - - const renderFigure = () => ( -
    -
    - {renderImage()} - {caption && ( -
    - {captionTitle &&

    {captionTitle}

    } - -
    - )} -
    -
    - ); - - return ( - <>{caption ? renderFigure() : renderImage(borderAndDropShadowClasses)} - ); -}; - -Image.propTypes = { - /** - * Image source (We keep the same name as in the whole project) - */ - src: PropTypes.string.isRequired, - /** - * Image alt text - */ - alt: PropTypes.string.isRequired, - /** - * Array classes for the image - */ - cssClasses: PropTypes.arrayOf(PropTypes.string), - /** - * Image loading mode - */ - loading: PropTypes.oneOf(["lazy", "eager"]), - /** - * Image decoding mode - */ - decoding: PropTypes.oneOf(["sync", "async", "auto"]), - /** - * Image fetch priority mode - */ - fetchPriority: PropTypes.oneOf(["auto", "high", "low"]), - /** - * Width of the image - */ - width: PropTypes.string, - /** - * Height of the image - */ - height: PropTypes.string, - dataTestId: PropTypes.string, - cardLink: PropTypes.string, - title: PropTypes.string, - caption: PropTypes.string, - captionTitle: PropTypes.string, - border: PropTypes.bool, - dropShadow: PropTypes.bool, -}; diff --git a/packages/components-core/src/components/Image/index.stories.js b/packages/components-core/src/components/Image/index.stories.js deleted file mode 100644 index fed3c58af3..0000000000 --- a/packages/components-core/src/components/Image/index.stories.js +++ /dev/null @@ -1,76 +0,0 @@ -// @ts-check -import React from "react"; - -import { Image } from "."; - -export default { - title: "UDS/Image", - component: Image, - parameters: { - docs: { - description: { - component: - "The image component is basically a wrapper for the `` HTML element. It accepts some of the properties available for the native element, but only those that are necessary to function optimally. These properties are documented in the PropTypes object and you can see the specifications there. It is important to note that this component was specifically created for use within the UDS library and its React components, and should not be used directly outside of that context.", - }, - }, - }, -}; - -const Template = args => ; - -export const imageWithNoCaption = Template.bind({}); -imageWithNoCaption.args = { - src: "https://source.unsplash.com/WLUHO9A_xik/800x600", - alt: "Placeholder image", - border: true, -}; - -export const imageWithNoCaptionBorderless = Template.bind({}); -imageWithNoCaptionBorderless.args = { - src: "https://source.unsplash.com/WLUHO9A_xik/800x600", - alt: "Placeholder image", - border: false, -}; - -export const imageWithCaption = Template.bind({}); -imageWithCaption.args = { - src: "https://source.unsplash.com/WLUHO9A_xik/800x600", - alt: "Placeholder image", - caption: "This is a caption.", - captionTitle: "Caption title", - border: true, -}; - -export const imageWithCaptionAndDropshadow = Template.bind({}); -imageWithCaptionAndDropshadow.args = { - src: "https://source.unsplash.com/WLUHO9A_xik/800x600", - alt: "Placeholder image", - caption: "This is a caption.", - captionTitle: "Caption title", - dropShadow: true, - border: true, -}; - -const GridTemplate = args => { - return ( -
    - {[...Array(20)].map((_, idx) => { - // eslint-disable-next-line react/no-array-index-key - return ; - })} -
    - ); -}; - -export const GridImages = GridTemplate.bind({}); -GridImages.args = { - alt: "Placeholder image", - width: "100%", - src: "https://source.unsplash.com/WLUHO9A_xik/800x600", -}; diff --git a/packages/components-core/src/components/Image/index.test.js b/packages/components-core/src/components/Image/index.test.js deleted file mode 100644 index 70ce421566..0000000000 --- a/packages/components-core/src/components/Image/index.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import { render, cleanup } from "@testing-library/react"; -import React from "react"; - -import { Image } from "."; - -const renderImage = props => { - return render(); -}; - -describe("#Image", () => { - let component; - - beforeEach(() => { - component = renderImage({ - src: "https://source.unsplash.com/WLUHO9A_xik/800x600", - alt: "Placeholder image", - }); - }); - - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); -}); diff --git a/packages/components-core/src/components/Loader/index.js b/packages/components-core/src/components/Loader/index.js deleted file mode 100644 index d53570798e..0000000000 --- a/packages/components-core/src/components/Loader/index.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; - -import { LoaderWrapper } from "./index.styles"; - -const Loader = () => ( - -
    -
    -
    -
    - -); - -export { Loader }; diff --git a/packages/components-core/src/components/Loader/index.styles.js b/packages/components-core/src/components/Loader/index.styles.js deleted file mode 100644 index 392059d591..0000000000 --- a/packages/components-core/src/components/Loader/index.styles.js +++ /dev/null @@ -1,39 +0,0 @@ -import styled from "styled-components"; - -const LoaderWrapper = styled.div` - display: inline-block; - position: relative; - width: 80px; - height: 80px; - div { - box-sizing: border-box; - display: block; - position: absolute; - width: 30px; - height: 30px; - margin: 3px; - border: 3px solid #1b1b1b; - border-radius: 50%; - animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; - border-color: #1b1b1b transparent transparent transparent; - } - div:nth-child(1) { - animation-delay: -0.45s; - } - div:nth-child(2) { - animation-delay: -0.3s; - } - div:nth-child(3) { - animation-delay: -0.15s; - } - @keyframes lds-ring { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } - } -`; - -export { LoaderWrapper }; diff --git a/packages/components-core/src/components/Pagination/PageItem/index.js b/packages/components-core/src/components/Pagination/PageItem/index.js deleted file mode 100644 index 6857858f01..0000000000 --- a/packages/components-core/src/components/Pagination/PageItem/index.js +++ /dev/null @@ -1,79 +0,0 @@ -// @ts-check -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React from "react"; - -/** - * @typedef {import('../../../core/types/shared-types').PageItemProps} PageItemProps - */ - -/** - * Generic page item component to render each pagination item - * @param {PageItemProps} props - * @ignore - */ -export const PageItem = ({ - dataId, - isClickeable, - disabled, - pageLinkIcon, - selectedPage, - onClick, - ellipses, - ariaLabel, - children, - ariaDisabled, -}) => { - return ( -
  • - {isClickeable ? ( - - ) : ( - - {children} - - )} -
  • - ); -}; - -PageItem.propTypes = { - isClickeable: PropTypes.bool, - disabled: PropTypes.bool, - pageLinkIcon: PropTypes.bool, - selectedPage: PropTypes.bool, - dataId: PropTypes.string, - onClick: PropTypes.func, - children: PropTypes.node, - ellipses: PropTypes.bool, - ariaLabel: PropTypes.string, - ariaDisabled: PropTypes.bool, -}; - -PageItem.defaultProps = { - isClickeable: false, - disabled: false, - pageLinkIcon: false, - selectedPage: false, - onClick: () => {}, -}; diff --git a/packages/components-core/src/components/Pagination/index.js b/packages/components-core/src/components/Pagination/index.js deleted file mode 100644 index b92301568a..0000000000 --- a/packages/components-core/src/components/Pagination/index.js +++ /dev/null @@ -1,186 +0,0 @@ -// @ts-check -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React, { useState, useEffect } from "react"; - -import { createRange, iff, trackGAEvent } from "../../../../../shared"; -import { PageItem } from "./PageItem"; - -const defaultGAEvent = { - event: "select", - action: "click", - name: "onclick", - type: "pagination", - region: "main content", -}; - -/** - * @typedef {import('../../core/types/shared-types').PaginationProps} PaginationProps - */ - -/** - * @param {PaginationProps} props - * @returns {JSX.Element} - */ -export const Pagination = ({ - type, - background, - currentPage, - totalPages, - onChange, -}) => { - const [selectedPage, setSelectedPage] = useState(null); - - useEffect(() => { - setSelectedPage(currentPage); - }, [currentPage]); - - const trackEvent = page => { - trackGAEvent({ ...defaultGAEvent, text: `page ${page}` }); - }; - - const handleChangePage = (e, page) => { - const actions = { - first: 1, - prev: selectedPage === 1 ? 1 : selectedPage - 1, - next: selectedPage === totalPages ? totalPages : selectedPage + 1, - last: totalPages, - }; - const action = actions[page] ?? page; - setSelectedPage(action); - trackEvent(action); - onChange?.(e, action); - }; - - const renderPages = () => { - // Set the ranges to be shown in the pagination - const lowerRangeLimit = iff( - selectedPage === totalPages - 1, - 2, - selectedPage === totalPages ? 3 : 1 - ); - const upperRangeLimit = iff( - selectedPage === 1, - 3, - selectedPage === 2 ? 2 : 1 - ); - const lowerRange = createRange( - selectedPage - lowerRangeLimit, - selectedPage, - totalPages - ); - const upperRange = createRange( - selectedPage, - selectedPage + 1 + upperRangeLimit, - totalPages - ); - const renderedPages = [...lowerRange, ...upperRange]; - - return ( - <> - {renderedPages[0] !== 1 && ( - handleChangePage(e, "first")} - > - 1 - - )} - {renderedPages[0] > 2 && ...} - {renderedPages.map(page => ( - handleChangePage(e, page)} - > - {page} - - ))} - {renderedPages[renderedPages.length - 1] < totalPages - 1 && ( - ... - )} - {renderedPages[renderedPages.length - 1] !== totalPages && ( - handleChangePage(e, "last")} - > - {totalPages} - - )} - - ); - }; - - return ( - - ); -}; - -Pagination.propTypes = { - /** - * Type of pagination - */ - type: PropTypes.oneOf(["default", "bordered"]).isRequired, - /** - * Background of pagination - */ - background: PropTypes.oneOf(["white", "gray1", "gray2", "gray7"]).isRequired, - /** - * Current page - */ - currentPage: PropTypes.number, - /** - * Total number of pages - */ - totalPages: PropTypes.number, - /** - * Callback fired when the page is changed. - */ - onChange: PropTypes.func.isRequired, -}; - -Pagination.defaultProps = { - currentPage: 1, - totalPages: 10, -}; diff --git a/packages/components-core/src/components/Pagination/index.stories.js b/packages/components-core/src/components/Pagination/index.stories.js deleted file mode 100644 index 9f328ff818..0000000000 --- a/packages/components-core/src/components/Pagination/index.stories.js +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint react/jsx-props-no-spreading: "off" */ -import classNames from "classnames"; -import React from "react"; - -import { Pagination } from "."; - -export default { - title: "UDS/Pagination", - component: Pagination, - parameters: { - docs: { - description: { - component: `The Pagintaion component can be used to generate pagination. - ## Usage - - All props are valid options for all pagination types. - Pagination users are responsible to meet all UDS design guidelines with their Pagination, - including rules on the use of Call-to-Action buttons and tags. - - View component examples and source code below. - `, - }, - }, - }, -}; - -const Template = args => ( -
    -
    -
    - -
    -
    -
    -); - -export const Default = Template.bind({}); -Default.args = { - type: "default", - background: "white", - totalPages: 4, -}; - -export const Bordered = Template.bind({}); -Bordered.args = { - type: "bordered", - background: "white", - totalPages: 45, - showFirstButton: true, - showLastButton: true, -}; - -export const Gray1Background = Template.bind({}); -Gray1Background.args = { type: "default", background: "gray1", currentPage: 7 }; - -export const Gray2Background = Template.bind({}); -Gray2Background.args = { - type: "default", - background: "gray2", - currentPage: 5, - totalNumbers: 5, -}; - -export const Gray7Background = Template.bind({}); -Gray7Background.args = { type: "default", background: "gray7" }; diff --git a/packages/components-core/src/components/Pagination/index.test.js b/packages/components-core/src/components/Pagination/index.test.js deleted file mode 100644 index fc2b8dacc2..0000000000 --- a/packages/components-core/src/components/Pagination/index.test.js +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, cleanup, fireEvent } from "@testing-library/react"; -import React from "react"; - -import { Pagination } from "."; - -const onChangeSpy = jest.fn(); -const totalPages = 4; - -const defaultArgs = { - type: "default", - background: "white", - totalPages, - onChange: onChangeSpy, -}; - -const renderPagination = props => { - return render(); -}; - -describe("#Pagination", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - component = renderPagination(defaultArgs); - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); - - it("should call 'onChange' method on pagination item change", () => { - const clickableElement = component.queryAllByTestId("page-link")[0]; - fireEvent.click(clickableElement); - expect(onChangeSpy).toHaveBeenCalled(); - }); - - it("should 'active' selected item", () => { - const clickableElement = component.queryAllByTestId("page-link")[0]; - fireEvent.click(clickableElement); - setTimeout(() => - expect(clickableElement.parentElement.className).toContain("active") - ); - }); - - it("should disable prev button at the beginning of the pagination elements list", () => { - const element = component.queryAllByTestId("page-link")[0].parentElement; - expect(element.className).toContain("disabled"); - }); - - it("should disable next button at the end of the pagination elements list", () => { - const clickableElement = - component.queryAllByTestId("page-link")[totalPages]; - const nextButton = component.queryByText("Next"); - fireEvent.click(clickableElement); - setTimeout(() => - expect(nextButton.parentElement.className).toContain("disabled") - ); - }); -}); - -describe("#Pagination variations", () => { - it("should set active currentPage", () => { - const currentPage = 3; - const props = { - ...defaultArgs, - currentPage, - }; - - const component = renderPagination(props); - const paginationItem = - component.queryAllByTestId("page-link")[currentPage].parentElement; - - expect(paginationItem.className).toContain("active"); - }); -}); diff --git a/packages/components-core/src/components/RankingCard/index.js b/packages/components-core/src/components/RankingCard/index.js deleted file mode 100644 index a133e00053..0000000000 --- a/packages/components-core/src/components/RankingCard/index.js +++ /dev/null @@ -1,217 +0,0 @@ -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React, { useState } from "react"; - -import { trackGAEvent, sanitizeDangerousMarkup } from "../../../../../shared"; -// eslint-disable-next-line import/no-cycle -import { Image } from "../Image"; - -const gaDefaultObject = { - name: "onclick", - event: "link", - action: "click", - type: "internal link", - region: "main content", -}; - -const AVAILABLE_GA_ACTIONS = { - OPEN: "open", - CLOSE: "close", -}; - -const AVAILABLE_SIZES = { - LARGE: "large", - SMALL: "small", -}; - -const isSmallSize = size => size === AVAILABLE_SIZES.SMALL; - -const ImageWrapper = ({ size, image, imageAlt }) => { - return isSmallSize(size) ? ( -
    - {imageAlt} -
    - ) : ( - {imageAlt} - ); -}; - -ImageWrapper.propTypes = { - size: PropTypes.oneOf(["small", "large"]), - image: PropTypes.string.isRequired, - imageAlt: PropTypes.string.isRequired, -}; - -const CitationWrapper = ({ heading, citation }) => { - return ( -
    -

    {heading}

    -

    — {citation}

    -
    - ); -}; - -CitationWrapper.propTypes = { - heading: PropTypes.string.isRequired, - citation: PropTypes.string.isRequired, -}; - -const InfoLayerWrapper = ({ imageSize, body, heading, readMoreLink }) => { - const [open, setOpen] = useState(false); - // TODO: Switch to useId when we upgrade to React 18 - const uniqueId = `info-layer-${Math.floor(Math.random() * 100000)}`; - - const handleButtonClick = event => { - if (event.type === "click" || event.key === "Enter" || event.key === " ") { - setOpen(!open); - trackGAEvent({ - ...gaDefaultObject, - text: "Expand ranking", - action: open ? AVAILABLE_GA_ACTIONS.OPEN : AVAILABLE_GA_ACTIONS.CLOSE, - section: heading, - }); - } - }; - - return ( -
    -
    -
    - {isSmallSize(imageSize) && ( - // eslint-disable-next-line react/no-danger -

    - )} - {!isSmallSize(imageSize) && ( - <> - - - )} - {isSmallSize(imageSize) && ( - <> - - - )} -

    - {!isSmallSize(imageSize) && ( - // eslint-disable-next-line react/no-danger -

    - )} - {readMoreLink && ( - { - trackGAEvent({ - ...gaDefaultObject, - section: heading, - text: "read more", - }); - }} - > - Read more {heading} - - )} -

    -
    - ); -}; - -InfoLayerWrapper.propTypes = { - imageSize: PropTypes.oneOf(["small", "large"]), - body: PropTypes.string.isRequired, - heading: PropTypes.string.isRequired, - readMoreLink: PropTypes.string, -}; - -export const RankingCard = ({ - imageSize = "large", - image, - imageAlt, - heading, - body, - readMoreLink = "", - citation, -}) => { - return ( -
    - - - {isSmallSize(imageSize) && ( - - )} - - -
    - ); -}; - -RankingCard.propTypes = { - /** - * Size of ranking card - */ - imageSize: PropTypes.oneOf(["small", "large"]).isRequired, - /** - * Ranking card image - */ - image: PropTypes.string.isRequired, - /** - * Card header image alt text - */ - imageAlt: PropTypes.string.isRequired, - /** - * Ranking card heading - */ - heading: PropTypes.string.isRequired, - /** - * Ranking card body content - */ - body: PropTypes.string.isRequired, - /** - * Link for read more - */ - readMoreLink: PropTypes.string, - /** - * Ranking card citation content (Required for small size only) - */ - citation: PropTypes.string, -}; diff --git a/packages/components-core/src/components/RankingCard/index.stories.js b/packages/components-core/src/components/RankingCard/index.stories.js deleted file mode 100644 index a67a096a23..0000000000 --- a/packages/components-core/src/components/RankingCard/index.stories.js +++ /dev/null @@ -1,43 +0,0 @@ -import classNames from "classnames"; -import React from "react"; - -import { RankingCard } from "."; - -import { imageAny } from "../../../../../shared/assets"; - -export default { - title: "UDS/Ranking Card", - component: RankingCard, -}; - -const Template = args => ( -
    -
    -
    - -
    -
    -
    -); - -export const Large = Template.bind({}); -Large.args = { - imageSize: "large", - image: imageAny(), - imageAlt: "Image alt text", - heading: "Ranking title goes here, under the photo", - body: "ASU has topped U.S. News & World Report’s “Most Innovative Schools list since the inception of the category in 2016. ASU again placed ahead of Stanford and MIT on the list, based on a survey of peers. College presidents, provosts and admissions deans around the country nominated up to 10 colleges or universities that are making the most innovative improvements.", - readMoreLink: "https://www.asu.edu/", -}; - -export const Small = Template.bind({}); -Small.args = { - imageSize: "small", - image: imageAny(), - imageAlt: "Image alt text", - heading: "Ranking title goes here, under the photo", - body: "ASU has topped U.S. News & World Report’s “Most Innovative Schools list since the inception of the category in 2016. ASU again placed ahead of Stanford and MIT on the list, based on a survey of peers. College presidents, provosts and admissions deans around the country nominated up to 10 colleges or universities that are making the most innovative improvements.", - readMoreLink: "https://www.asu.edu/", - citation: - "Citation of the ranking should go under the headline, regular body style text", -}; diff --git a/packages/components-core/src/components/RankingCard/index.test.js b/packages/components-core/src/components/RankingCard/index.test.js deleted file mode 100644 index ee41a2ebc8..0000000000 --- a/packages/components-core/src/components/RankingCard/index.test.js +++ /dev/null @@ -1,70 +0,0 @@ -import { render, fireEvent, cleanup } from "@testing-library/react"; -import React from "react"; - -import { RankingCard } from "./index"; - -const rankingCardLarge = { - imageSize: "large", - image: "https://source.unsplash.com/WLUHO9A_xik/600x400", - imageAlt: "Image alt text", - heading: "Ranking title goes here, under the photo", - body: "ASU has topped U.S. News & World Report’s “Most Innovative Schools list since the inception of the category in 2016. ASU again placed ahead of Stanford and MIT on the list, based on a survey of peers. College presidents, provosts and admissions deans around the country nominated up to 10 colleges or universities that are making the most innovative improvements.", - readMoreLink: "https://www.asu.edu/", -}; - -const rankingCardSmall = { - imageSize: "small", - image: "https://source.unsplash.com/WLUHO9A_xik/600x400", - imageAlt: "Image alt text", - heading: "Ranking title goes here, under the photo", - body: "ASU has topped U.S. News & World Report’s “Most Innovative Schools list since the inception of the category in 2016. ASU again placed ahead of Stanford and MIT on the list, based on a survey of peers. College presidents, provosts and admissions deans around the country nominated up to 10 colleges or universities that are making the most innovative improvements.", - readMoreLink: "https://www.asu.edu/", - citation: - "Citation of the ranking should go under the headline, regular body style text", -}; - -const renderRankingCard = args => { - return render(); -}; - -describe("RankingCard large layout", () => { - let component; - - beforeEach(() => { - component = renderRankingCard(rankingCardLarge); - }); - - afterEach(cleanup); - - it("should define the component", () => { - expect(component).toBeDefined(); - }); - - it("should expand the description box", () => { - const buttonChevron = component.getByRole("button"); - const infoLayer = component.getByTestId("info-layer"); - fireEvent.click(buttonChevron); - expect(infoLayer).toHaveClass("active"); - }); -}); - -describe("RankingCard small layout", () => { - let component; - - beforeEach(() => { - component = renderRankingCard(rankingCardSmall); - }); - - afterEach(cleanup); - - it("should define the component", () => { - expect(component).toBeDefined(); - }); - - it("should expand the description box", () => { - const buttonChevron = component.getByRole("button"); - const infoLayer = component.getByTestId("info-layer"); - fireEvent.click(buttonChevron); - expect(infoLayer).toHaveClass("active"); - }); -}); diff --git a/packages/components-core/src/components/TabbedPanels/components/NavControls.js b/packages/components-core/src/components/TabbedPanels/components/NavControls.js deleted file mode 100644 index e007d4da6e..0000000000 --- a/packages/components-core/src/components/TabbedPanels/components/NavControls.js +++ /dev/null @@ -1,42 +0,0 @@ -import PropTypes from "prop-types"; -import React from "react"; - -import { NavControlButtons } from "./NavControls.styles"; - -const NavControls = ({ hidePrev, hideNext, clickPrev, clickNext }) => { - return ( - - {!hidePrev && ( - - )} - {!hideNext && ( - - )} - - ); -}; - -NavControls.propTypes = { - hidePrev: PropTypes.bool, - hideNext: PropTypes.bool, - clickPrev: PropTypes.func.isRequired, - clickNext: PropTypes.func.isRequired, -}; - -export { NavControls }; diff --git a/packages/components-core/src/components/TabbedPanels/components/NavControls.styles.js b/packages/components-core/src/components/TabbedPanels/components/NavControls.styles.js deleted file mode 100644 index afc74c5491..0000000000 --- a/packages/components-core/src/components/TabbedPanels/components/NavControls.styles.js +++ /dev/null @@ -1,12 +0,0 @@ -// @ts-check -import styled from "styled-components"; - -const NavControlButtons = styled.div` - button { - padding: 16px 0; - border: none; - outline: none; - } -`; - -export { NavControlButtons }; diff --git a/packages/components-core/src/components/TabbedPanels/components/TabHeader.js b/packages/components-core/src/components/TabbedPanels/components/TabHeader.js deleted file mode 100644 index 987466a4ff..0000000000 --- a/packages/components-core/src/components/TabbedPanels/components/TabHeader.js +++ /dev/null @@ -1,77 +0,0 @@ -import PropTypes from "prop-types"; -import React, { forwardRef, useRef, useImperativeHandle } from "react"; - -const TabHeader = forwardRef(function TabHeader(props, ref) { - const { - id, - selected, - title, - selectTab, - leftKeyPressed, - rightKeyPressed, - icon, - } = props; - - const inputRef = useRef(null); - - useImperativeHandle( - ref, - () => { - return { - focus() { - inputRef.current.focus(); - }, - scrollIntoView() { - const middle = - inputRef.current?.offsetWidth / 2 + inputRef.current.offsetLeft; - const targetMiddle = - inputRef.current?.offsetParent?.scrollLeft + - inputRef.current?.offsetParent?.offsetWidth / 2; - - inputRef.current?.offsetParent?.scrollBy({ - left: middle - targetMiddle, - }); - }, - }; - }, - [] - ); - - const func = e => { - if (e.keyCode === 37) { - e.preventDefault(); - leftKeyPressed(); - } else if (e.keyCode === 39) { - e.preventDefault(); - rightKeyPressed(); - } - }; - return ( - selectTab(e, id, title)} - onKeyDown={func} - tabIndex={selected ? "" : "-1"} - > - {title} {icon && } - - ); -}); - -TabHeader.propTypes = { - id: PropTypes.string.isRequired, - selected: PropTypes.bool.isRequired, - title: PropTypes.string.isRequired, - selectTab: PropTypes.func.isRequired, - leftKeyPressed: PropTypes.func.isRequired, - rightKeyPressed: PropTypes.func.isRequired, - icon: PropTypes.arrayOf(PropTypes.string), -}; - -export { TabHeader }; diff --git a/packages/components-core/src/components/TabbedPanels/components/index.js b/packages/components-core/src/components/TabbedPanels/components/index.js deleted file mode 100644 index 6769bd7e72..0000000000 --- a/packages/components-core/src/components/TabbedPanels/components/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { NavControls } from "./NavControls"; -export { TabHeader } from "./TabHeader"; diff --git a/packages/components-core/src/components/TabbedPanels/index.js b/packages/components-core/src/components/TabbedPanels/index.js deleted file mode 100644 index 1152ca245d..0000000000 --- a/packages/components-core/src/components/TabbedPanels/index.js +++ /dev/null @@ -1,229 +0,0 @@ -// @ts-check -import PropTypes from "prop-types"; -import React, { useState, useEffect, useRef, useCallback } from "react"; - -import { trackGAEvent } from "../../../../../shared"; -import { NavControls, TabHeader } from "./components"; - -function useRefs() { - const refs = useRef({}); - - const register = useCallback( - refName => ref => { - refs.current[refName] = ref; - }, - [] - ); - - return [refs, register]; -} - -const Tab = ({ id, bgColor, selected, children }) => - selected && ( -
    - {children} -
    - ); - -Tab.propTypes = { - id: PropTypes.string.isRequired, - bgColor: PropTypes.string, - selected: PropTypes.bool, - children: PropTypes.oneOfType([PropTypes.array, PropTypes.element]), -}; - -const TabbedPanels = ({ - initialTab = "", - children, - bgColor = "", - onTabChange = () => {}, -}) => { - const childrenArray = React.Children.toArray(children); - const isMounted = useRef(false); - const [activeTabID, setActiveTabID] = useState( - initialTab && initialTab !== "null" ? initialTab : childrenArray[0].props.id - ); - const headerTabs = useRef(null); - const [headerTabItems, setHeaderTabItems] = useRefs(); - - const updateActiveTabID = tab => { - onTabChange(tab); - - headerTabItems.current[tab]?.focus(); - setActiveTabID(tab); - }; - - const [scrollLeft, setScrollLeft] = useState(0); - const [scrollableWidth, setScrollableWidth] = useState(); - - useEffect(() => { - const onScroll = () => { - setScrollLeft(headerTabs.current.scrollLeft); - }; - headerTabs.current.addEventListener("scroll", onScroll); - onScroll(); - return () => { - if (headerTabs.current) { - headerTabs.current.removeEventListener("scroll", onScroll); - } - }; - }, [scrollableWidth]); - - useEffect(() => { - const onResize = () => { - setScrollableWidth( - headerTabs.current.scrollWidth - headerTabs.current.offsetWidth - ); - }; - window.addEventListener("resize", onResize); - onResize(); - return () => { - if (headerTabs.current) { - window.removeEventListener("resize", onResize); - } - }; - }, []); - - useEffect(() => { - headerTabItems.current[activeTabID]?.scrollIntoView(); - }, [activeTabID]); - - useEffect(() => { - if ( - isMounted.current && - initialTab && - initialTab !== "null" && - activeTabID !== initialTab - ) { - setActiveTabID(initialTab); - } - }, [initialTab]); - - useEffect(() => { - isMounted.current = true; - }, []); - - const trackArrowsEvent = text => { - trackGAEvent({ - event: "select", - action: "click", - name: "onclick", - type: "carousel", - region: "main content", - text, - }); - }; - - const trackLinkEvent = text => { - trackGAEvent({ - event: "link", - action: "click", - name: "onclick", - type: "internal link", - text, - }); - }; - - const tabs = childrenArray.map(el => { - return React.cloneElement(el, { - bgColor, - selected: activeTabID === el.props.id, - }); - }); - - const slideNav = direction => { - const container = headerTabs.current; - const maxScrollLeft = container.scrollWidth - container.clientWidth; - const currentScrollLeft = container.scrollLeft; - - let newScrollLeft = currentScrollLeft + 200 * direction; - - // Ensure the scroll position stays within bounds - newScrollLeft = Math.max(0, Math.min(maxScrollLeft, newScrollLeft)); - - container.scrollTo({ - left: newScrollLeft, - behavior: "smooth", - }); - }; - - const switchToTab = (e, tabID, title) => { - trackLinkEvent(title); - e.preventDefault(); - updateActiveTabID(tabID); - }; - - const incrementIndex = (up = true) => { - const count = childrenArray.length; - const num = up ? 1 : -1; - const currPos = childrenArray.findIndex(c => c.props.id === activeTabID); - const newTabID = childrenArray[(count + currPos + num) % count].props.id; - updateActiveTabID(newTabID); - }; - - let navClasses = "uds-tabbed-panels"; - if (bgColor === "bg-dark") { - navClasses += " uds-tabbed-panels-dark"; - } - - return ( -
    - - -
    - ); -}; - -TabbedPanels.propTypes = { - initialTab: PropTypes.string, - children: PropTypes.arrayOf(PropTypes.element).isRequired, - bgColor: PropTypes.string, - onTabChange: PropTypes.func, -}; - -export { TabbedPanels, Tab }; diff --git a/packages/components-core/src/components/TabbedPanels/index.stories.js b/packages/components-core/src/components/TabbedPanels/index.stories.js deleted file mode 100644 index 1ffa74b74a..0000000000 --- a/packages/components-core/src/components/TabbedPanels/index.stories.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from "react"; -import { MemoryRouter } from "react-router-dom"; - -import { TabbedPanels, Tab } from "./index"; - -export default { - title: "UDS/TabbedPanels", - component: TabbedPanels, -}; - -const Template = () => { - return ( - -
    -
    - - -
    - 1 - Home long tab - This is an ordinary paragraph that is long - enough to to to wrap to multiple lines so that you can see how - spacing eos et accusam et justo duo dolores et ea rebu. -
    - Stet clita kasd gubergren, no sea takimata sanctus est Lorem. -
    -
    - -
    - 2 - Home long tab - This is an ordinary paragraph that is long - enough to to to wrap to multiple lines so that you can see how - spacing eos et accusam et justo duo dolores et ea rebu. -
    - Stet clita kasd gubergren, no sea takimata sanctus est Lorem. -
    -
    - -
    - 3 - Home long tab - This is an ordinary paragraph that is long - enough to to to wrap to multiple lines so that you can see how - spacing eos et accusam et justo duo dolores et ea rebu. -
    - Stet clita kasd gubergren, no sea takimata sanctus est Lorem. -
    -
    - -
    - 4 - Home long tab - This is an ordinary paragraph that is long - enough to to to wrap to multiple lines so that you can see how - spacing eos et accusam et justo duo dolores et ea rebu. -
    - Stet clita kasd gubergren, no sea takimata sanctus est Lorem. -
    -
    - -
    - 5 - Home long tab - This is an ordinary paragraph that is long - enough to to to wrap to multiple lines so that you can see how - spacing eos et accusam et justo duo dolores et ea rebu. -
    - Stet clita kasd gubergren, no sea takimata sanctus est Lorem. -
    -
    - -
    - 6 - Home long tab - This is an ordinary paragraph that is long - enough to to to wrap to multiple lines so that you can see how - spacing eos et accusam et justo duo dolores et ea rebu. -
    - Stet clita kasd gubergren, no sea takimata sanctus est Lorem. -
    -
    -
    -
    -
    -
    - ); -}; - -export const Default = Template.bind({}); diff --git a/packages/components-core/src/components/TabbedPanels/index.test.js b/packages/components-core/src/components/TabbedPanels/index.test.js deleted file mode 100644 index d10294be34..0000000000 --- a/packages/components-core/src/components/TabbedPanels/index.test.js +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, cleanup } from "@testing-library/react"; -import React from "react"; - -import { TabbedPanels, Tab } from "."; - -const tabbedPanelsDefaultArgs = { - bgColor: "bg-dark", -}; - -const renderTabbedPanels = props => { - return render( - - -
    One
    -
    - -
    Two
    -
    -
    - ); -}; - -describe("#TabbedPanels", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - component = renderTabbedPanels(tabbedPanelsDefaultArgs); - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); -}); diff --git a/packages/components-core/src/components/Testimonial/index.js b/packages/components-core/src/components/Testimonial/index.js deleted file mode 100644 index e8f2dea020..0000000000 --- a/packages/components-core/src/components/Testimonial/index.js +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-disable react/require-default-props */ -// @ts-check -import PropTypes from "prop-types"; -import React from "react"; - -import { spreadClasses } from "../../../../../shared"; -// eslint-disable-next-line import/no-cycle -import { Image } from "../Image"; - -/** - * @typedef {import('../../core/types/testimonial-types').TestimonialProps} TestimonialProps - */ - -/** - * @param {TestimonialProps} props - * @returns {JSX.Element} - */ -const Testimonial = ({ imageSource, imageAltText, quote, itemStyle = {} }) => ( -
    - {imageSource && ( - {imageAltText} - )} - - - - -
    - {quote.title && ( -

    - - {quote.title} - -

    - )} - {quote.content && ( -

    - {quote.content} -

    - )} - {(!!quote.cite?.name || !!quote.cite?.description) && ( -
    - {quote.cite.name} - {quote.cite && ( - {quote.cite.description} - )} -
    - )} -
    -
    -); - -Testimonial.propTypes = { - quote: PropTypes.shape({ - title: PropTypes.string, - content: PropTypes.string, - cite: PropTypes.shape({ - name: PropTypes.string, - description: PropTypes.string, - }), - }).isRequired, - imageSource: PropTypes.string, - imageAltText: PropTypes.string, - itemStyle: PropTypes.shape({ - containerCssClass: PropTypes.arrayOf(PropTypes.string), - titleCssClass: PropTypes.arrayOf(PropTypes.string), - contentCssClass: PropTypes.arrayOf(PropTypes.string), - }), -}; - -export { Testimonial }; diff --git a/packages/components-core/src/components/Testimonial/index.stories.js b/packages/components-core/src/components/Testimonial/index.stories.js deleted file mode 100644 index d2e834a3f7..0000000000 --- a/packages/components-core/src/components/Testimonial/index.stories.js +++ /dev/null @@ -1,141 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import React from "react"; - -import { Testimonial } from "."; - -export default { - title: "UDS/Testimonial", - component: Testimonial, - decorators: [ - (Story, context) => { - // Extract wrapper background color from args, it doesn't need passed down to component - const { wrapperBackgroundColor, ...args } = context.args; - return ( -
    - {Story({ args })} -
    - ); - }, - ], - argTypes: { - wrapperBackgroundColor: { - defaultValue: "", - options: ["", "bg-gray-2", "bg-gray-7"], - control: { - type: "radio", - labels: { - "": "White", - "bg-gray-2": "Gray", - "bg-gray-7": "Black", - }, - }, - }, - }, - parameters: { - docs: { - description: { - component: " ", - }, - }, - }, -}; - -const itemColorCombinations = { - Gold: ["accent-gold"], - Maroon: ["accent-maroon"], - Gold_White_Text: ["text-white", "accent-gold"], - // eslint-disable-next-line quote-props - None: null, -}; - -const itemTitleColorCombinations = { - "Highlight gold": ["highlight-gold"], - "Highlight black": ["highlight-black"], - // eslint-disable-next-line quote-props - "None": null, -}; - -const itemQuoteColorCombinations = { - White: ["text-white"], - Maroon: ["text-maroon"], - None: null, -}; - -// eslint-disable-next-line react/prop-types, react/jsx-props-no-spreading -const Template = args => ; - -export const Default = Template.bind({}); -Default.args = { - quote: { - content: `We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness.`, - cite: { - name: `Thomas Jefferson`, - description: `The Declaration of Independence`, - }, - }, -}; - -export const WithImage = Template.bind({}); -WithImage.args = { - quote: { - content: `I used to wonder about that myself. Thought it was a bunch of mumbo-jumbo. A magical power holding together good and evil, the dark side and the light? Crazy thing is, it’s true. The Force, the Jedi — all of it. It’s all true.`, - cite: { - name: `Han Solo`, - }, - }, - imageSource: "https://source.unsplash.com/WLUHO9A_xik/400x400", - imageAltText: "Pretend this is Han Solo", - itemStyle: { - containerCssClass: itemColorCombinations.Gold, - contentCssClass: itemQuoteColorCombinations.none, - }, -}; - -export const NoImage = Template.bind({}); -NoImage.args = { - quote: { - content: `Computers make excellent and efficient servants, but I have no wish to serve under them.`, - cite: { - name: `Spock`, - description: `First officer, USS Enterprise`, - }, - }, - itemStyle: { - containerCssClass: itemColorCombinations.Gold, - }, -}; - -export const WithCitation = Template.bind({}); -WithCitation.args = { - quote: { - content: `ASU is a comprehensive public research university, measured not by whom we exclude, but rather by whom we include and how they succeed; advancing research and discovery of public value; and assuming fundamental responsibility for the economic, social, cultural and overall health of the communities it serves.`, - cite: { - name: `Michael M. Crow`, - description: `ASU Charter`, - }, - }, - imageSource: "https://source.unsplash.com/WLUHO9A_xik/400x400", - imageAltText: "Pretend this is Michael M. Crow, President of ASU", - itemStyle: { - containerCssClass: itemColorCombinations.Gold, - }, -}; - -export const HightlightGold = Template.bind({}); -HightlightGold.args = { - quote: { - title: "Walt Disney", - content: - "Laughter is timeless, imagination has no age, dreams are forever.", - }, - imageSource: "https://source.unsplash.com/WLUHO9A_xik/300x300", - imageAltText: "Image of Walt DisneyImage of Walt Disney", - itemStyle: { - containerCssClass: itemColorCombinations.Gold, - titleCssClass: itemTitleColorCombinations["Highlight gold"], - }, -}; diff --git a/packages/components-core/src/components/Testimonial/index.test.js b/packages/components-core/src/components/Testimonial/index.test.js deleted file mode 100644 index 36b66ccd58..0000000000 --- a/packages/components-core/src/components/Testimonial/index.test.js +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -// @ts-check -import { render, cleanup } from "@testing-library/react"; -import React from "react"; - -import { Testimonial } from "."; - -const defaultArgs = { - quote: { - title: "Testimonial", - content: `ASU is a comprehensive public research university, measured not by whom we exclude, but rather by whom we include and how they succeed; advancing research and discovery of public value; and assuming fundamental responsibility for the economic, social, cultural and overall health of the communities it serves.`, - cite: { - name: `Michael M. Crow`, - description: `ASU Charter`, - }, - }, - imageSource: "https://placeimg.com/400/400/any", -}; - -const renderTestimonial = props => { - return render(); -}; - -describe("#Testimonial", () => { - /** @type {import("@testing-library/react").RenderResult} */ - let component; - - beforeEach(() => { - component = renderTestimonial(defaultArgs); - }); - afterEach(cleanup); - - it("should define component", () => { - expect(component).toBeDefined(); - }); - - const elements = [ - [`Image`, `testimonial-image`], - [`Title`, `testimonial-title`], - [`Content`, `testimonial-content`], - [`Citation`, `testimonial-citation`], - ]; - - test.each(elements)("should define %p element", (_, testId) => - expect(component.queryByTestId(testId)).toBeInTheDocument() - ); -}); diff --git a/packages/components-core/src/components/Video/index.js b/packages/components-core/src/components/Video/index.js deleted file mode 100644 index 109ec62fc1..0000000000 --- a/packages/components-core/src/components/Video/index.js +++ /dev/null @@ -1,133 +0,0 @@ -// @ts-check -import classNames from "classnames"; -import PropTypes from "prop-types"; -import React, { useRef } from "react"; - -import { trackGAEvent } from "../../../../../shared"; - -const defaultGAEvent = { - name: "onclick", - event: "link", - action: "click", - type: "internal link", - region: "main content", - text: "play button", -}; - -/** - * @typedef {import('../../core/types/video-types').VideoProps} VideoProps - */ - -/** - * @param {VideoProps} props - * @returns {JSX.Element} - * @ignore - */ -const videoTemplate = ({ - url = "", - vttUrl, - caption, - title = "", - className, - controls = true, -}) => { - /** @type {React.MutableRefObject} */ - const videoRef = useRef(null); - - const onVideoClick = () => { - trackGAEvent({ ...defaultGAEvent, section: title }); - }; - - return ( -
    -
    - -
    - {caption && ( -
    -
    {caption}
    -
    - )} -
    - ); -}; - -/** - * @param {VideoProps} props - * @returns {JSX.Element} - * @ignore - */ -const youtubeTemplate = ({ title = "", caption, url = "", className }) => ( -
    -
    -