diff --git a/.circleci/config.yml b/.circleci/config.yml index c594d0ab1f..35c520f695 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ orbs: references: default_env: &default_env docker: - - image: cimg/node:16.17.0 + - image: cimg/node:18.18.2 working_directory: ~/repo environment: IGNORE_COMMIT_MESSAGE: 'chore(release): publish' @@ -169,7 +169,7 @@ jobs: - *build_all_packages - run: name: build storybook - command: yarn build-storybook + command: yarn nx run styleguide:build-storybook - run: name: deploy command: yarn deploy @@ -190,14 +190,14 @@ jobs: - *build_all_packages - run: name: build storybook - command: yarn build-storybook + command: yarn nx run styleguide:build-storybook - *save_nx_cache - *save_webpack_cache - run: name: deploy command: | DEPLOY_MESSAGE="User: ${CIRCLE_USERNAME} Project: ${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} Pull Request: ${CIRCLE_PULL_REQUEST}" - npx netlify-cli@2.37.0 deploy --message="${DEPLOY_MESSAGE}" --dir=dist/docs --json > .deploy-output + npx netlify-cli@17.37.1 deploy --message "${DEPLOY_MESSAGE}" --dir dist/storybook/styleguide --json --filter @codecademy/styleguide > .deploy-output - setup_remote_docker - run: docker pull cloudposse/github-commenter:0.5.0-58 - run: diff --git a/.eslintrc.js b/.eslintrc.js index 50b5fa7d66..3e040dfc71 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -46,6 +46,11 @@ module.exports = { '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/restrict-plus-operands': 'off', '@typescript-eslint/restrict-template-expressions': 'off', + 'import/no-cycle': 'off', + 'react/no-unknown-property': [ + 'error', + { ignore: ['mask-type', 'xmlns-x', 'xmlns-i', 'xmlns-graph'] }, + ], }, }, { diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 85adf1e2f0..b9e63e85c8 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -6,7 +6,7 @@ jobs: steps: - uses: Codecademy/run-on-yarn@v3.0.0 with: - node-version: 16 + node-version: 20 command: format:verify lint: @@ -14,6 +14,15 @@ jobs: fail-fast: false matrix: command: ['lint', 'verify'] + runs-on: ubuntu-latest + steps: + - uses: Codecademy/run-on-yarn@v3.0.0 + with: + node-version: 20 + command: build + - run: yarn ${{ matrix.command }} + + runs-on: ubuntu-latest steps: - uses: Codecademy/run-on-yarn@v3.0.0 diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs index 90380937d3..1cdc904996 100644 --- a/.lintstagedrc.mjs +++ b/.lintstagedrc.mjs @@ -9,9 +9,9 @@ export default { commands.push(`yarn syncpack format`); } - // if (micromatch.some(allChanges, 'yarn.lock')) { - // commands.push(`npx yarn-deduplicate`); - // } + if (micromatch.some(allChanges, 'yarn.lock')) { + commands.push(`npx yarn-deduplicate`); + } const eslintExtensions = `{mdx,ts,tsx,js,jsx,json}`; const eslintFiles = micromatch( diff --git a/.nvmrc b/.nvmrc index 37e391febb..bb52a169c1 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.17.0 +v18.18.2 diff --git a/.prettierignore b/.prettierignore index c8e12df14a..291938f37e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,4 +5,6 @@ node_modules packages/gamut-icons/src/icons packages/gamut-styles/**/*.d.ts packages/styleguide/stories/Core/Atoms/Markdown/*.md -.nx + +/.nx/cache +/.nx/workspace-data diff --git a/.vscode/stories.code-snippets b/.vscode/stories.code-snippets index be682ee3d1..da25ae07cd 100644 --- a/.vscode/stories.code-snippets +++ b/.vscode/stories.code-snippets @@ -1,86 +1,137 @@ { - "Component Story": { - "prefix": "component-story", + "Component Doc": { + "prefix": "component-doc", "body": [ - "import { $1 } from '@codecademy/$2';", - "import title from '@codecademy/macros/lib/title.macro';", - "import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks';", - "import { PropsTable } from '@codecademy/storybook-addon-variance';", + "import { Canvas, Controls, Meta } from '@storybook/blocks';", + "", + "import { ComponentHeader } from '~styleguide/blocks';", + "", + "import * as $1Stories from './$1.stories';", + "", + "export const parameters = {", + "subtitle: `Template component`,", + "design: {", + "type: 'figma',", + "url: 'https: //www.figma.com/file/XXX',", + " },", + "status: 'current',", + "source: {", + "repo: '$2',", + "// this is easy to find by right clicking on the file in VSCode and clicking 'Copy Remote File Url From...' and the selecting 'main' or amending the url path below like so: https://github.com/Codecademy/gamut/blob/main/packages/${2}/src/file/location", + "githubLink:", + "'https: //github.com/Codecademy/gamut/blob/main/packages/gamut/src/Logo',", + " },", + "};", + "", + "", + "", + "", + "## Usage", + "", + "Use $1 to [what it should be used for]", + "", + "### Best practices:", + "", + "- [recommendation / best practice for implementation]", + "- [recommendation / best practice for implementation]", + "", + "When NOT to use", + "", + "- [use case]- for [describe the use case], use the [similar component] component.", + "- [use case]- for [describe the use case], use the [similar component] component", + "", + "## Anatomy", + "", + "[Insert image exported from Figma]", + "", + "1. [Element name]", + "- [description including available options and ux writing if relevant]", "", - "", + "## Variants", "", - "`$1` component summary goes here", + "### [Variant 1 name]", "", - "## Design Principles", + "Use the [variant 1 name] to [what it should be used for]", "", - "Design Principles summary", + "", "", - "## Usage Guidelines", + "## Playground", "", - "Usage summary", + "If you are using a story named 'Default', you can forgo the `of` prop.", "", - "## Code Playground", + "", "", - "", - " ", - " {(args) => <$1 {...args} />}", - " ", - "", + "", "", - "" + "## Accessibility considerations", + "", + "- [Accessibility guidance]", + "", + "## UX writing", + "", + "- [content]", + " - [guidance]", + " - [guidance]" ], - "description": "Default Component Story Structure." + "description": "Default Component Doc Structure." }, "Table of Contents Story": { "prefix": "toc-story", "body": [ - "import title from '@codecademy/macros/lib/title.macro';", - "import { Meta } from '@storybook/addon-docs/blocks';", + "import { Meta } from '@storybook/blocks';", + "import { AboutHeader, TableOfContents } from '~styleguide/blocks';", + "", + "export const parameters = {", + " id: '$1',", + " title: '$1/About',", + " subtitle: '$2',", + " }", "", - "import { TableOfContents } from '~styleguide/blocks';", + "", "", - "", + "", "", - "" + "Foundations make up the smallest scale design values that comprise a design system. Sometimes referred to elsewhere as "tokens", they are the abstract units that comprise and stitch together our atoms, molecules, and organisms.", + "", + "" ], "description": "TOC Story Structure." }, - "Canvas Block": { - "prefix": "canvas-block", - "body": [ - "", - " ", - " {(args) => <$2 {...args} />}", - " ", - "" - ], - "description": "A single story block wrapped in a canvas" - }, - "Story Block": { - "prefix": "story-block", + "Component Story": { + "prefix": "component-story", "body": [ - " ", - " {(args) => <$2 {...args} />}", - " " + "import { $1 } from '@codecademy/gamut';", + "import type { Meta, StoryObj } from '@storybook/react';", + "", + "const meta: Meta = {", + " component: $1,", + " args: {},", + "};", + "", + "export default meta;", + "type Story = StoryObj;", + "", + "export const Default: Story = {", + " args: {", + " children: 'Test'", + " },", + "};", + "", + "export const Secondary: Story = {", + " args: {", + " children: 'Test again',", + " variant: 'secondary'", + " }", + "};" ], - "description": "A single story block without a canvas" + "description": "Default TSX story structure." } } diff --git a/.yarnclean b/.yarnclean index 632afb12c5..d62c8fee69 100644 --- a/.yarnclean +++ b/.yarnclean @@ -5,7 +5,6 @@ tests powered-test # asset directories -docs website images diff --git a/README.md b/README.md index 49333cfdcb..d77d082801 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Gamut +// Storybook 8 branch + _The component library & design system for Codecademy._ ✨ --- diff --git a/dist/static/CNAME b/dist/static/CNAME deleted file mode 100644 index e9c9997a55..0000000000 --- a/dist/static/CNAME +++ /dev/null @@ -1 +0,0 @@ -gamut.codecademy.com diff --git a/dist/static/robots.txt b/dist/static/robots.txt deleted file mode 100644 index eb0536286f..0000000000 --- a/dist/static/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: diff --git a/dist/static/storybook/index.html b/dist/static/storybook/index.html deleted file mode 100644 index a8130a85ae..0000000000 --- a/dist/static/storybook/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - Styleguide | Codecademy - - - - - - - - - - diff --git a/nx.json b/nx.json index bf3d0091c5..d597dd20d1 100644 --- a/nx.json +++ b/nx.json @@ -16,9 +16,6 @@ } }, "extends": "nx/presets/npm.json", - "affected": { - "defaultBase": "main" - }, "workspaceLayout": { "appsDir": "packages", "libsDir": "packages" @@ -34,7 +31,10 @@ "default", "!{projectRoot}/**/*.(test|spec).ts{,.snap}", "!{projectRoot}/**/__tests__/**/*", - "!{projectRoot}/**/__fixtures__/**/*" + "!{projectRoot}/**/__fixtures__/**/*", + "!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)", + "!{projectRoot}/.storybook/**/*", + "!{projectRoot}/tsconfig.storybook.json" ] }, "targetDefaults": { @@ -42,7 +42,10 @@ "dependsOn": ["^build"], "inputs": ["production", "^production", "babelConfig", "ci"] }, - "test": { + "verify": { + "cache": true + }, + "@nx/jest:jest": { "dependsOn": ["^build"], "inputs": [ "default", @@ -64,5 +67,10 @@ "linter": "eslint" } }, - "$schema": "./node_modules/nx/schemas/nx-schema.json" + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "useDaemonProcess": false, + "nxCloudAccessToken": "ZmVjZWYxNzctYTdmZC00ODYzLTg4MzEtZTE1YTVmZDdkZmY1fHJlYWQ=", + "parallel": 3, + "useInferencePlugins": false, + "defaultBase": "main" } diff --git a/package.json b/package.json index 2505ac8ffd..67727eb169 100644 --- a/package.json +++ b/package.json @@ -4,45 +4,71 @@ "version": "1.2.0", "author": "Codecademy Engineers ", "dependencies": { - "@babel/cli": "7.20.7", - "@babel/core": "7.20.12", - "@babel/preset-typescript": "^7.18.6", + "@emotion/react": "11.11.1", + "@emotion/styled": "11.11.0", + "core-js": "3.7.0", + "lodash": "^4.17.5", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-helmet-async": "^2.0.5" + }, + "devDependencies": { + "@babel/cli": "7.24.7", + "@babel/core": "7.24.7", + "@babel/preset-env": "^7.24.7", + "@babel/preset-react": "^7.24.7", + "@babel/preset-typescript": "^7.24.7", "@codecademy/eslint-config": "8.0.0", "@codecademy/prettier-config": "^0.2.0", "@codecademy/tsconfig": "^0.3.0", "@emotion/babel-plugin": "11.11.0", "@emotion/jest": "^11.11.0", - "@emotion/react": "11.11.1", - "@emotion/styled": "11.11.0", - "@nx/jest": "16.8.1", - "@nx/react": "16.8.1", + "@mdx-js/react": "^3.1.0", + "@nx/jest": "19.3.2", + "@nx/js": "19.3.2", + "@nx/react": "19.3.2", + "@nx/storybook": "19.3.2", + "@nx/web": "19.3.2", + "@nx/webpack": "19.3.2", + "@storybook/addon-controls": "8.3.6", + "@storybook/addon-designs": "^8.0.3", + "@storybook/addon-docs": "^8.3.6", + "@storybook/addon-essentials": "^8.3.6", + "@storybook/addon-links": "^8.3.6", + "@storybook/addon-webpack5-compiler-babel": "^3.0.3", + "@storybook/blocks": "^8.3.6", + "@storybook/core-server": "8.3.6", + "@storybook/docs-tools": "^8.3.6", + "@storybook/icons": "1.2.12", + "@storybook/manager-api": "^8.3.6", + "@storybook/react-webpack5": "^8.3.6", + "@storybook/theming": "^8.3.6", "@svgr/cli": "5.5.0", "@testing-library/dom": "^8.11.1", "@testing-library/jest-dom": "^5.16.1", - "@testing-library/react": "^12.1.2", + "@testing-library/react": "15.0.6", "@testing-library/react-hooks": "^7.0.2", - "@testing-library/user-event": "^13.5.0", + "@testing-library/user-event": "^14.5.2", "@types/classnames": "2.2.10", "@types/invariant": "2.2.29", "@types/konami-code-js": "^0.8.0", "@types/lodash": "4.17.0", - "@types/react": "^17", - "@types/react-dom": "^17", + "@types/react": "18.3.1", + "@types/react-dom": "18.3.0", "@types/react-test-renderer": "^17.0.1", "@types/stylis": "^4.2.0", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", "babel-jest": "29.6.4", "babel-plugin-macros": "3.0.1", - "babel-preset-codecademy": "7.0.0", "component-test-setup": "^0.3.1", "conventional-changelog-cli": "^2.0.34", "conventional-changelog-conventionalcommits": "^4.3.0", - "core-js": "3.7.0", "cpy-cli": "^4.1.0", "eslint": "^8.11.0", "eslint-plugin-gamut": "^2.0.0", "eslint-plugin-local-rules": "^1.1.0", + "eslint-plugin-lodash": "^7.4.0", "gh-pages": "^2.2.0", "husky": "8.0.3", "identity-obj-proxy": "3.0.0", @@ -52,16 +78,13 @@ "jest-junit": "^16.0.0", "lerna": "7.2.0", "lint-staged": "14.0.1", - "lodash": "^4.17.5", "micromatch": "^4.0.5", "mutationobserver-shim": "^0.3.3", - "nx": "16.8.1", - "nx-cloud": "16.4.0", - "prettier": "2.2.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-helmet": "6.1.0", - "react-test-renderer": "^17.0.2", + "nx": "19.3.2", + "onchange": "^7.0.2", + "prettier": "^2.6.2", + "react-test-renderer": "18.3.1", + "storybook": "^8.3.6", "svgo": "^1.3.2", "syncpack": "^10.9.3", "ts-jest": "29.1.1", @@ -69,13 +92,8 @@ "tslib": "2.4.0", "typescript": "5.1.3" }, - "devDependencies": { - "eslint-plugin-lodash": "^7.4.0", - "onchange": "^7.0.2", - "webpack": "4.46.0" - }, "engines": { - "node": "^16.17", + "node": "^18.18.2", "yarn": "^1.21.1" }, "license": "MIT", @@ -116,12 +134,6 @@ "workspaces": { "packages": [ "packages/*" - ], - "nohoist": [ - "**/@storybook", - "**/@storybook/**", - "**/storybook-*", - "**/storybook-*/**" ] } } diff --git a/packages/eslint-plugin-gamut/project.json b/packages/eslint-plugin-gamut/project.json index 412201d58b..168fc2ace2 100644 --- a/packages/eslint-plugin-gamut/project.json +++ b/packages/eslint-plugin-gamut/project.json @@ -3,6 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/eslint-plugin-gamut/src", "projectType": "library", + "tags": [], "targets": { "build": { "executor": "nx:run-commands", @@ -16,10 +17,8 @@ "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/packages/eslint-plugin-gamut"], "options": { - "jestConfig": "packages/eslint-plugin-gamut/jest.config.ts", - "passWithNoTests": true + "jestConfig": "packages/eslint-plugin-gamut/jest.config.ts" } } - }, - "tags": [] + } } diff --git a/packages/gamut-icons/babel.config.js b/packages/gamut-icons/babel.config.js index ffd4ed9873..e325473dfd 100644 --- a/packages/gamut-icons/babel.config.js +++ b/packages/gamut-icons/babel.config.js @@ -1,4 +1,19 @@ module.exports = { extends: '../../babel.defaults.js', - presets: ['codecademy', '@babel/preset-typescript'], + presets: [ + [ + '@babel/env', + { + modules: process.env.NODE_ENV === 'test' ? 'commonjs' : false, + targets: 'defaults', + }, + ], + [ + '@babel/react', + { + runtime: 'automatic', + }, + ], + '@babel/preset-typescript', + ], }; diff --git a/packages/gamut-icons/project.json b/packages/gamut-icons/project.json index 1fe2a72193..9712ead4a1 100644 --- a/packages/gamut-icons/project.json +++ b/packages/gamut-icons/project.json @@ -3,6 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/gamut-icons/src", "projectType": "library", + "tags": [], "targets": { "build": { "outputs": [ @@ -31,8 +32,7 @@ "dependsOn": ["build"], "outputs": ["{workspaceRoot}/coverage/packages/gamut-icons"], "options": { - "jestConfig": "packages/gamut-icons/jest.config.ts", - "passWithNoTests": true + "jestConfig": "packages/gamut-icons/jest.config.ts" } }, "clean": { @@ -42,6 +42,5 @@ "command": "rm -rf ./dist" } } - }, - "tags": [] + } } diff --git a/packages/gamut-illustrations/babel.config.js b/packages/gamut-illustrations/babel.config.js index b1bcb85b9c..04bc362829 100644 --- a/packages/gamut-illustrations/babel.config.js +++ b/packages/gamut-illustrations/babel.config.js @@ -1,6 +1,21 @@ module.exports = { extends: '../../babel.defaults.js', - presets: ['codecademy', '@babel/preset-typescript'], + presets: [ + [ + '@babel/env', + { + modules: process.env.NODE_ENV === 'test' ? 'commonjs' : false, + targets: 'defaults', + }, + ], + [ + '@babel/react', + { + runtime: 'automatic', + }, + ], + '@babel/preset-typescript', + ], plugins: [ [ '@emotion/babel-plugin', diff --git a/packages/gamut-illustrations/project.json b/packages/gamut-illustrations/project.json index 428715436d..de30ecec23 100644 --- a/packages/gamut-illustrations/project.json +++ b/packages/gamut-illustrations/project.json @@ -3,6 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/gamut-illustrations/src", "projectType": "library", + "tags": [], "targets": { "build": { "outputs": ["{projectRoot}/dist"], @@ -21,8 +22,7 @@ "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/packages/gamut-illustrations"], "options": { - "jestConfig": "packages/gamut-illustrations/jest.config.ts", - "passWithNoTests": true + "jestConfig": "packages/gamut-illustrations/jest.config.ts" } }, "clean": { @@ -32,6 +32,5 @@ "command": "rm -rf ./dist" } } - }, - "tags": [] + } } diff --git a/packages/gamut-illustrations/src/MobilePhone.tsx b/packages/gamut-illustrations/src/MobilePhone.tsx index c10050089a..fabd13f0fa 100644 --- a/packages/gamut-illustrations/src/MobilePhone.tsx +++ b/packages/gamut-illustrations/src/MobilePhone.tsx @@ -15,7 +15,7 @@ export const MobilePhone: React.FC = ({ viewBox="0 0 108 203" xmlns="http://www.w3.org/2000/svg" aria-hidden={ariaHidden} - class-name={className} + className={className} > { }); it('renders global styles', () => { renderView(); - const allStyles = Array.from( - document.querySelectorAll('style') - ).filter((el) => Boolean(el.getAttribute('data-emotion'))); + const allStyles = Array.from(document.querySelectorAll('style')).filter( + (el) => Boolean(el.getAttribute('data-emotion')) + ); expect(allStyles.length).toBeGreaterThan(0); }); it('does not render global styles when configured', () => { renderView({ useGlobals: false }); - const allStyles = Array.from( - document.querySelectorAll('style') - ).filter((el) => Boolean(el.getAttribute('data-emotion'))); + const allStyles = Array.from(document.querySelectorAll('style')).filter( + (el) => Boolean(el.getAttribute('data-emotion')) + ); expect(allStyles.length).toBe(0); }); @@ -64,9 +64,9 @@ describe(GamutProvider, () => { children: , }); - const allStyles = Array.from( - document.querySelectorAll('style') - ).filter((el) => Boolean(el.getAttribute('data-emotion'))); + const allStyles = Array.from(document.querySelectorAll('style')).filter( + (el) => Boolean(el.getAttribute('data-emotion')) + ); expect(createEmotionCache).toHaveBeenCalledTimes(1); expect(allStyles.length).toBeGreaterThan(0); @@ -83,10 +83,10 @@ describe(GamutProvider, () => { variables: { cool: { '--cool': 'blue' } }, }); - const allStyles = Array.from( - document.querySelectorAll('style') - ).filter((el) => el.innerHTML.includes('--cool')); + const rootElement = document.documentElement; + const rootStyles = getComputedStyle(rootElement); + const blue = rootStyles.getPropertyValue('--cool'); - expect(allStyles.length).toBe(1); + expect(blue).toBe('blue'); }); }); diff --git a/packages/gamut-tests/babel.config.js b/packages/gamut-tests/babel.config.js index 427504c86e..66e5a32ab7 100644 --- a/packages/gamut-tests/babel.config.js +++ b/packages/gamut-tests/babel.config.js @@ -1,6 +1,21 @@ module.exports = { extends: '../../babel.defaults.js', - presets: ['codecademy', '@babel/preset-typescript'], + presets: [ + [ + '@babel/env', + { + modules: process.env.NODE_ENV === 'test' ? 'commonjs' : false, + targets: 'defaults', + }, + ], + [ + '@babel/react', + { + runtime: 'automatic', + }, + ], + '@babel/preset-typescript', + ], plugins: [ [ '@emotion/babel-plugin', diff --git a/packages/gamut-tests/project.json b/packages/gamut-tests/project.json index 2b66f526d2..9fb5a513b2 100644 --- a/packages/gamut-tests/project.json +++ b/packages/gamut-tests/project.json @@ -3,6 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/gamut-tests/src", "projectType": "library", + "tags": [], "targets": { "build": { "outputs": ["{projectRoot}/dist"], @@ -32,6 +33,5 @@ "command": "rm -rf ./dist" } } - }, - "tags": [] + } } diff --git a/packages/gamut-tests/src/index.tsx b/packages/gamut-tests/src/index.tsx index 75d3261f99..3310a74337 100644 --- a/packages/gamut-tests/src/index.tsx +++ b/packages/gamut-tests/src/index.tsx @@ -18,7 +18,7 @@ export const MockGamutProvider: React.FC<{ children?: React.ReactNode }> = ({ ); }; -function withMockGamutProvider( +function withMockGamutProvider( WrappedComponent: React.ComponentType ) { const WithBoundaryComponent: React.FC = (props) => ( diff --git a/packages/gamut/babel.config.js b/packages/gamut/babel.config.js index b1bcb85b9c..04bc362829 100644 --- a/packages/gamut/babel.config.js +++ b/packages/gamut/babel.config.js @@ -1,6 +1,21 @@ module.exports = { extends: '../../babel.defaults.js', - presets: ['codecademy', '@babel/preset-typescript'], + presets: [ + [ + '@babel/env', + { + modules: process.env.NODE_ENV === 'test' ? 'commonjs' : false, + targets: 'defaults', + }, + ], + [ + '@babel/react', + { + runtime: 'automatic', + }, + ], + '@babel/preset-typescript', + ], plugins: [ [ '@emotion/babel-plugin', diff --git a/packages/gamut/package.json b/packages/gamut/package.json index 7816c2651e..bc7b144569 100644 --- a/packages/gamut/package.json +++ b/packages/gamut/package.json @@ -21,7 +21,7 @@ "polished": "^4.1.2", "react-aria-tabpanel": "^4.4.0", "react-focus-on": "^3.5.1", - "react-hook-form": "^7.21.2", + "react-hook-form": "^7.53.1", "react-player": "^2.16.0", "react-select": "^5.2.2", "react-truncate-markup": "^5.1.2", diff --git a/packages/gamut/project.json b/packages/gamut/project.json index b3ba248349..9efa3641a9 100644 --- a/packages/gamut/project.json +++ b/packages/gamut/project.json @@ -3,6 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/gamut/src", "projectType": "library", + "tags": [], "targets": { "build": { "outputs": ["{projectRoot}/dist"], @@ -23,8 +24,7 @@ "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/packages/gamut"], "options": { - "jestConfig": "packages/gamut/jest.config.ts", - "passWithNoTests": true + "jestConfig": "packages/gamut/jest.config.ts" } }, "clean": { @@ -34,6 +34,5 @@ "command": "rm -rf ./dist" } } - }, - "tags": [] + } } diff --git a/packages/gamut/src/AccordionAreaDeprecated/__tests__/AccordionAreaDeprecated.test.tsx b/packages/gamut/src/AccordionAreaDeprecated/__tests__/AccordionAreaDeprecated.test.tsx index 50eff5de23..8dd0f30332 100644 --- a/packages/gamut/src/AccordionAreaDeprecated/__tests__/AccordionAreaDeprecated.test.tsx +++ b/packages/gamut/src/AccordionAreaDeprecated/__tests__/AccordionAreaDeprecated.test.tsx @@ -1,5 +1,5 @@ import { setupRtl } from '@codecademy/gamut-tests'; -import { act } from 'react-dom/test-utils'; +import { act } from 'react'; import { AccordionAreaDeprecated } from '..'; diff --git a/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecated/index.tsx b/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecated/index.tsx index 0a229af65b..ae65e0f0dd 100644 --- a/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecated/index.tsx +++ b/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecated/index.tsx @@ -4,7 +4,7 @@ import hasIn from 'lodash/hasIn'; import { ReactNode } from 'react'; import * as React from 'react'; -import { omitProps } from '../../utils/omitProps'; +import { omitProps } from '../../utils'; import { ButtonDeprecatedBase, ButtonDeprecatedBaseProps, @@ -39,7 +39,7 @@ const themes = [ export type ButtonDeprecatedThemes = | keyof typeof buttonPresetThemes - | typeof themes[number]; + | (typeof themes)[number]; const propKeys = [ 'theme', diff --git a/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecatedBase/index.tsx b/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecatedBase/index.tsx index 2a2a9014d3..fc96ae090a 100644 --- a/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecatedBase/index.tsx +++ b/packages/gamut/src/AccordionButtonDeprecated/ButtonDeprecatedBase/index.tsx @@ -3,7 +3,7 @@ import { HTMLProps, ReactNode } from 'react'; import * as React from 'react'; import { ChildComponentDescriptor } from '../../typings/react'; -import { omitProps } from '../../utils/omitProps'; +import { omitProps } from '../../utils'; // eslint-disable-next-line gamut/no-css-standalone import styles from './styles.module.scss'; diff --git a/packages/gamut/src/Alert/Alert.tsx b/packages/gamut/src/Alert/Alert.tsx index d91b434d34..e6993b6eba 100644 --- a/packages/gamut/src/Alert/Alert.tsx +++ b/packages/gamut/src/Alert/Alert.tsx @@ -6,9 +6,11 @@ import * as React from 'react'; import TruncateMarkup from 'react-truncate-markup'; import { useMeasure } from 'react-use'; -import { Rotation, ToolTip, WithChildrenProp } from '..'; +import { Rotation } from '../Animation'; import { Box } from '../Box'; import { FillButton, IconButton, TextButton } from '../Button'; +import { ToolTip } from '../Tip/ToolTip'; +import { WithChildrenProp } from '../utils'; import { AlertBanner, AlertBox, diff --git a/packages/gamut/src/BodyPortal/index.tsx b/packages/gamut/src/BodyPortal/index.tsx index 0f5a7a8190..6b8f00d528 100644 --- a/packages/gamut/src/BodyPortal/index.tsx +++ b/packages/gamut/src/BodyPortal/index.tsx @@ -5,8 +5,6 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { useIsomorphicLayoutEffect } from 'react-use'; -import { WithChildrenProp } from '..'; - const PortalWrapper = styled .div( system.css({ @@ -20,7 +18,7 @@ const PortalWrapper = styled ) .withComponent(ColorMode); -export const BodyPortal: React.FC = ({ children }) => { +export const BodyPortal: React.FC = ({ children }) => { const [ready, setReady] = useState(false); const mode = useCurrentMode(); diff --git a/packages/gamut/src/Box/props.ts b/packages/gamut/src/Box/props.ts index 99e254c394..536ff57539 100644 --- a/packages/gamut/src/Box/props.ts +++ b/packages/gamut/src/Box/props.ts @@ -1,7 +1,7 @@ import { system } from '@codecademy/gamut-styles'; import { StyleProps, variance } from '@codecademy/variance'; -import { WithChildrenProp } from '..'; +import { WithChildrenProp } from '../utils'; export const boxProps = variance.compose( system.space, diff --git a/packages/gamut/src/Breadcrumbs/__tests__/Breadcrumbs.test.tsx b/packages/gamut/src/Breadcrumbs/__tests__/Breadcrumbs.test.tsx index 451d102649..73ae0d31a4 100644 --- a/packages/gamut/src/Breadcrumbs/__tests__/Breadcrumbs.test.tsx +++ b/packages/gamut/src/Breadcrumbs/__tests__/Breadcrumbs.test.tsx @@ -19,9 +19,9 @@ describe('Breadcrumbs', () => { it('renders crumbs with a path as links', () => { const { view } = renderView({ crumbs: [ - { title: 'one', href: 'one/path' }, - { title: 'two', href: 'two/path' }, - { title: 'three', href: 'three/path' }, + { title: 'one', href: 'www.one.com/path' }, + { title: 'two', href: 'www.two.com/path' }, + { title: 'three', href: 'www.three.com/path' }, ], }); view.getByText('one'); @@ -30,15 +30,16 @@ describe('Breadcrumbs', () => { expect(view.getAllByRole('link')).toHaveLength(3); }); - it('passes payload to onClick if both are provided', () => { - const payload = 'owo'; + it('passes payload to onClick if both are provided', async () => { + const payload = { owo: 'hey' }; const onClick = jest.fn(); const { view } = renderView({ - crumbs: [{ title: 'one', href: 'one/path', payload }], + crumbs: [{ title: 'one', href: '#one/path', payload }], onClick, }); - userEvent.click(view.getByText('one')); + + await userEvent.click(view.getByText('one')); expect(onClick).toHaveBeenCalledWith( expect.anything(), diff --git a/packages/gamut/src/Button/__tests__/IconButton.test.tsx b/packages/gamut/src/Button/__tests__/IconButton.test.tsx index b6e85d27e9..de4caf1dcb 100644 --- a/packages/gamut/src/Button/__tests__/IconButton.test.tsx +++ b/packages/gamut/src/Button/__tests__/IconButton.test.tsx @@ -1,4 +1,5 @@ import { StarIcon } from '@codecademy/gamut-icons'; +import { waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { setupRtl } from 'component-test-setup'; @@ -23,12 +24,12 @@ const renderView = setupRtl(IconButton, buttonProps); const renderFloatingView = setupRtl(IconButtonFloatingMock, buttonProps); describe('IconButton', () => { - it('renders a clickable button', () => { + it('renders a clickable button', async () => { const { view } = renderView(); const cta = view.getByRole('button', { name: label }); - userEvent.click(cta); + await userEvent.click(cta); expect(onClick).toHaveBeenCalled(); }); @@ -83,7 +84,10 @@ describe('IconButton', () => { expect(view.queryByText('tooltip')).toBeNull(); - userEvent.hover(cta); - await view.findByText(tip); + await userEvent.hover(cta); + + await waitFor(() => { + view.getByText(tip); + }); }); }); diff --git a/packages/gamut/src/Button/__tests__/TextButton.test.tsx b/packages/gamut/src/Button/__tests__/TextButton.test.tsx index da2dd685c9..0ca31c95bd 100644 --- a/packages/gamut/src/Button/__tests__/TextButton.test.tsx +++ b/packages/gamut/src/Button/__tests__/TextButton.test.tsx @@ -12,12 +12,12 @@ const renderView = setupRtl(TextButton, { }); describe('TextButton', () => { - it('renders a clickable button', () => { + it('renders a clickable button', async () => { const { view } = renderView(); const cta = view.getByRole('button', { name: 'Click me!' }); - userEvent.click(cta); + await userEvent.click(cta); expect(onClick).toHaveBeenCalled(); }); diff --git a/packages/gamut/src/ButtonBase/__tests__/ButtonBase.test.tsx b/packages/gamut/src/ButtonBase/__tests__/ButtonBase.test.tsx index 726634988d..62eeff1b44 100644 --- a/packages/gamut/src/ButtonBase/__tests__/ButtonBase.test.tsx +++ b/packages/gamut/src/ButtonBase/__tests__/ButtonBase.test.tsx @@ -27,7 +27,7 @@ describe('ButtonBase', () => { const { view } = renderView({ href: '' }); view.getByText(buttonText); - view.getByRole('link'); + expect(view.container.querySelector('a')?.getAttribute('href')).toBe(''); }); it('renders a button with the correct role', () => { diff --git a/packages/gamut/src/Coachmark/index.tsx b/packages/gamut/src/Coachmark/index.tsx index dfbceb6b2f..4411151249 100644 --- a/packages/gamut/src/Coachmark/index.tsx +++ b/packages/gamut/src/Coachmark/index.tsx @@ -1,8 +1,8 @@ import { useEffect, useRef, useState } from 'react'; import * as React from 'react'; -import { WithChildrenProp } from '..'; import { Popover, PopoverProps } from '../Popover'; +import { WithChildrenProp } from '../utils'; export interface CoachmarkProps extends WithChildrenProp { /** diff --git a/packages/gamut/src/ConnectedForm/ConnectedFormGroup.tsx b/packages/gamut/src/ConnectedForm/ConnectedFormGroup.tsx index 29a3ace294..5bb53fa8fa 100644 --- a/packages/gamut/src/ConnectedForm/ConnectedFormGroup.tsx +++ b/packages/gamut/src/ConnectedForm/ConnectedFormGroup.tsx @@ -14,7 +14,7 @@ import { Anchor } from '../Anchor'; import { HiddenText } from '../HiddenText'; import { Markdown } from '../Markdown'; import { ConnectedField, FieldProps, SubmitContextProps } from './types'; -import { useField } from './utils'; +import { getErrorMessage, useField } from './utils'; const ErrorAnchor = styled(Anchor)( css({ @@ -88,6 +88,8 @@ export function ConnectedFormGroup({ ); + const textError = customError || getErrorMessage(error); + return ( {hideLabel ? {renderedLabel} : renderedLabel} @@ -112,7 +114,7 @@ export function ConnectedFormGroup({ }} skipDefaultOverrides={{ a: true }} inline - text={error || customError} + text={textError} spacing="none" /> diff --git a/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedInput.tsx b/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedInput.tsx index 6be1764aba..333e1a48bb 100644 --- a/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedInput.tsx +++ b/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedInput.tsx @@ -17,7 +17,7 @@ export const ConnectedInput: React.FC = ({ return ( = ({ disabled, }); - return ; + return ( + + ); }; diff --git a/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedSelect.tsx b/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedSelect.tsx index 73cabeac74..8aa9078c43 100644 --- a/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedSelect.tsx +++ b/packages/gamut/src/ConnectedForm/ConnectedInputs/ConnectedSelect.tsx @@ -17,7 +17,7 @@ export const ConnectedSelect: React.FC = ({ return (