diff --git a/cypress/e2e/create-plan.cy.ts b/cypress/e2e/create-plan.cy.ts index b7909c84e..fe26fbe50 100644 --- a/cypress/e2e/create-plan.cy.ts +++ b/cypress/e2e/create-plan.cy.ts @@ -19,11 +19,11 @@ describe('Plan creation flow', () => { cy.task('log', 'Filling out plan creation form...'); cy.dataTestId('plan-name-input').type(planName); cy.dataTestId('major-autocomplete').type('Computer'); - cy.getDropdownOptions() - .contains('Computer Science') - .then(($el) => { - cy.wrap($el.get(0).innerText).as('major'); - $el.click(); + cy.contains('Computer Science') + .should('exist') + .click() + .then(() => { + cy.get('[data-testid=major-autocomplete]').invoke('text').as('major'); }); // Create plan without upload transcript @@ -63,11 +63,11 @@ describe('Plan creation flow', () => { cy.task('log', 'Filling out plan creation form...'); cy.dataTestId('plan-name-input').type(planName); cy.dataTestId('major-autocomplete').type('Computer'); - cy.getDropdownOptions() - .contains('Computer Science') - .then(($el) => { - cy.wrap($el.get(0).innerText).as('major'); - $el.click(); + cy.contains('Computer Science') + .should('exist') + .click() + .then(() => { + cy.get('[data-testid=major-autocomplete]').invoke('text').as('major'); }); // Create plan with uploading transcript @@ -111,11 +111,11 @@ describe('Plan creation flow', () => { cy.task('log', 'Filling out plan creation form...'); cy.dataTestId('plan-name-input').type(planName); cy.dataTestId('major-autocomplete').type('Computer'); - cy.getDropdownOptions() - .contains('Computer Science') - .then(($el) => { - cy.wrap($el.get(0).innerText).as('major'); - $el.click(); + cy.contains('Computer Science') + .should('exist') + .click() + .then(() => { + cy.get('[data-testid=major-autocomplete]').invoke('text').as('major'); }); // Create template plan without upload transcript diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 789b11cbf..07466f40f 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -21,9 +21,3 @@ Cypress.Commands.add('resetDbAndLogin', () => { }); }); }); - -// Ultity to select dropdown options from MUI's autocomplete -// Usage: cy.getDropdownOptions().contains("Whatever the option contains").click() -Cypress.Commands.add('getDropdownOptions', () => { - return cy.get('.MuiAutocomplete-listbox [role="option"]'); -}); diff --git a/package-lock.json b/package-lock.json index d83ee4254..78004417d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-loading-skeleton": "^3.3.1", + "react-select": "^5.7.7", "react-toastify": "^9.1.1", "superjson": "^1.11.0", "tss-react": "^3.3.6", @@ -776,7 +777,6 @@ "node_modules/@emotion/babel-plugin": { "version": "11.10.5", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/plugin-syntax-jsx": "^7.17.12", @@ -830,7 +830,6 @@ "node_modules/@emotion/react": { "version": "11.10.5", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.10.5", @@ -912,7 +911,6 @@ "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.0", "license": "MIT", - "peer": true, "peerDependencies": { "react": ">=16.8.0" } @@ -1006,6 +1004,11 @@ "react-dom": ">=16.8.0" } }, + "node_modules/@floating-ui/utils": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz", + "integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==" + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "dev": true, @@ -5285,7 +5288,6 @@ "node_modules/babel-plugin-macros": { "version": "3.1.0", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -6447,7 +6449,6 @@ "node_modules/cosmiconfig": { "version": "7.1.0", "license": "MIT", - "peer": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -8455,8 +8456,7 @@ }, "node_modules/find-root": { "version": "1.1.0", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/find-up": { "version": "5.0.0", @@ -11741,6 +11741,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "dev": true, @@ -13940,6 +13945,43 @@ } } }, + "node_modules/react-select": { + "version": "5.7.7", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.7.tgz", + "integrity": "sha512-HhashZZJDRlfF/AKj0a0Lnfs3sRdw/46VJIRd8IbB9/Ovr74+ZIwkAdSBjSPXsFMG+u72c5xShqwLSKIJllzqw==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-select/node_modules/@floating-ui/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/react-select/node_modules/@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dependencies": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, "node_modules/react-simple-code-editor": { "version": "0.13.1", "dev": true, @@ -14985,7 +15027,6 @@ "node_modules/source-map": { "version": "0.5.7", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index 7f97332de..63f83c8c7 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-loading-skeleton": "^3.3.1", + "react-select": "^5.7.7", "react-toastify": "^9.1.1", "superjson": "^1.11.0", "tss-react": "^3.3.6", diff --git a/src/components/AutoCompleteMajor.tsx b/src/components/AutoCompleteMajor.tsx index cac1016d9..26ecdb936 100644 --- a/src/components/AutoCompleteMajor.tsx +++ b/src/components/AutoCompleteMajor.tsx @@ -1,7 +1,6 @@ -import Autocomplete from '@mui/material/Autocomplete'; -import Popper from '@mui/material/Popper'; -import TextField from '@mui/material/TextField'; -import { FC, useCallback, useRef } from 'react'; +import Select from 'react-select'; +import { FC, useRef } from 'react'; +import useMajors from '@/shared/useMajors'; interface AutoCompleteMajorProps extends React.ComponentPropsWithoutRef<'div'> { onValueChange: (value: string) => void; @@ -12,6 +11,34 @@ interface AutoCompleteMajorProps extends React.ComponentPropsWithoutRef<'div'> { defaultValue?: string; } +const customStyles = { + control: (provided: any) => ({ + ...provided, + border: '1px solid var(--color-neutral-500)', + borderRadius: '0.375rem', + fontSize: '14px', + height: '46px', + // You can add more styles as needed + }), + option: (provided: any) => ({ + ...provided, + color: 'black', + }), + input: (provided: any) => ({ + ...provided, + paddingLeft: '0.5rem', + color: 'black', + }), + placeholder: (provided: any) => { + return { + ...provided, + paddingLeft: '0.5rem', + color: 'rgb(163 163 163)', + fontSize: '14px', + }; + }, +}; + const AutoCompleteMajor: FC> = ({ onValueChange, onInputChange, @@ -21,62 +48,26 @@ const AutoCompleteMajor: FC { + const { majors, err } = useMajors(); const containerRef = useRef(null); - - const CustomPopper = useCallback( - (props) => { - if (!containerRef.current) { - return
; - } - const { width } = containerRef.current.getBoundingClientRect(); - return ( - 0 ? '1px solid #EDEFF7' : 'none', - }} - /> - ); - }, - [containerRef, options.length], - ); - + // react-select requires options to be an object, so we convert it + const convertedOptions: any[] = majors.map((e) => ({ label: e, value: e })); return (
-
- onValueChange(value ?? '')} - onInputChange={(_, query) => { - onInputChange(query); +
+