From abb4c90156e585964b8e363780e1ae4a839d90d4 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Tue, 10 Sep 2024 14:22:31 -0400 Subject: [PATCH 01/16] move SearchInput to ol-components --- frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx | 10 ++++++++-- .../src/components/SearchInput}/SearchInput.test.tsx | 3 +-- .../src/components/SearchInput}/SearchInput.tsx | 6 ++++-- frontends/ol-components/src/index.ts | 2 ++ 4 files changed, 15 insertions(+), 6 deletions(-) rename frontends/{mit-learn/src/pages/HomePage => ol-components/src/components/SearchInput}/SearchInput.test.tsx (96%) rename frontends/{mit-learn/src/pages/HomePage => ol-components/src/components/SearchInput}/SearchInput.tsx (94%) diff --git a/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx b/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx index 83befc5eda..c5e79b49a6 100644 --- a/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx @@ -1,8 +1,14 @@ import React, { useState, useCallback } from "react" import { useNavigate } from "react-router" -import { Typography, styled, ChipLink, Link } from "ol-components" +import { + Typography, + styled, + ChipLink, + Link, + SearchInput, + SearchInputProps, +} from "ol-components" import type { ChipLinkProps } from "ol-components" -import { SearchInput, SearchInputProps } from "./SearchInput" import { ABOUT, SEARCH_CERTIFICATE, diff --git a/frontends/mit-learn/src/pages/HomePage/SearchInput.test.tsx b/frontends/ol-components/src/components/SearchInput/SearchInput.test.tsx similarity index 96% rename from frontends/mit-learn/src/pages/HomePage/SearchInput.test.tsx rename to frontends/ol-components/src/components/SearchInput/SearchInput.test.tsx index 7d8b757ab0..bac9a47464 100644 --- a/frontends/mit-learn/src/pages/HomePage/SearchInput.test.tsx +++ b/frontends/ol-components/src/components/SearchInput/SearchInput.test.tsx @@ -1,8 +1,7 @@ import React from "react" import { render, screen } from "@testing-library/react" import userEvent from "@testing-library/user-event" -import { SearchInput } from "./SearchInput" -import type { SearchInputProps } from "./SearchInput" +import { SearchInput, type SearchInputProps } from "./SearchInput" import invariant from "tiny-invariant" import { ThemeProvider } from "ol-components" const getSearchInput = () => { diff --git a/frontends/mit-learn/src/pages/HomePage/SearchInput.tsx b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx similarity index 94% rename from frontends/mit-learn/src/pages/HomePage/SearchInput.tsx rename to frontends/ol-components/src/components/SearchInput/SearchInput.tsx index 07ba11403c..f5cf330689 100644 --- a/frontends/mit-learn/src/pages/HomePage/SearchInput.tsx +++ b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx @@ -1,7 +1,9 @@ import React, { useCallback } from "react" import { RiSearch2Line, RiCloseLine } from "@remixicon/react" -import { Input, AdornmentButton, styled, pxToRem } from "ol-components" -import type { InputProps } from "ol-components" +import { Input, AdornmentButton } from "../Input/Input" +import type { InputProps } from "../Input/Input" +import styled from "@emotion/styled" +import { pxToRem } from "../ThemeProvider/typography" const StyledInput = styled(Input)(({ theme }) => ({ height: "72px", diff --git a/frontends/ol-components/src/index.ts b/frontends/ol-components/src/index.ts index 7c04f30eec..9c988a0768 100644 --- a/frontends/ol-components/src/index.ts +++ b/frontends/ol-components/src/index.ts @@ -199,6 +199,8 @@ export * from "./constants/imgConfigs" export { Input, AdornmentButton } from "./components/Input/Input" export type { InputProps, AdornmentButtonProps } from "./components/Input/Input" +export { SearchInput } from "./components/SearchInput/SearchInput" +export type { SearchInputProps } from "./components/SearchInput/SearchInput" export { TextField } from "./components/TextField/TextField" export { SimpleSelect, From 16e107a9a3c912faa8d71da85764ad548420a18d Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Tue, 10 Sep 2024 15:14:23 -0400 Subject: [PATCH 02/16] full height / uniform width adornment buttons on Input --- .../src/components/Input/Input.stories.tsx | 2 +- .../src/components/Input/Input.tsx | 53 ++++++++----------- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/frontends/ol-components/src/components/Input/Input.stories.tsx b/frontends/ol-components/src/components/Input/Input.stories.tsx index e78e483649..6abc99871f 100644 --- a/frontends/ol-components/src/components/Input/Input.stories.tsx +++ b/frontends/ol-components/src/components/Input/Input.stories.tsx @@ -104,7 +104,7 @@ export const Adornments: Story = { }, ] return ( - + {Object.values(adornments).flatMap((props, i) => SIZES.map((size) => { return ( diff --git a/frontends/ol-components/src/components/Input/Input.tsx b/frontends/ol-components/src/components/Input/Input.tsx index cf03ade4f7..e9dae0243f 100644 --- a/frontends/ol-components/src/components/Input/Input.tsx +++ b/frontends/ol-components/src/components/Input/Input.tsx @@ -10,12 +10,6 @@ const defaultProps = { multiline: false, } -const buttonPadding = { - medium: 4, - hero: 6, - heroMobile: 4, -} - /** * Base styles for Input and Select components. Includes border, color, hover effects. */ @@ -59,6 +53,18 @@ const baseInputStyles = (theme: Theme) => ({ paddingTop: "6px", paddingBottom: "7px", }, + "&.MuiInputBase-adornedStart": { + paddingLeft: "0", + input: { + paddingLeft: "8px", + }, + }, + "&.MuiInputBase-adornedEnd": { + paddingRight: "0", + input: { + paddingRight: "8px", + }, + }, }) /** @@ -79,17 +85,15 @@ const Input = styled(InputBase)(({ paddingLeft: "12px", paddingRight: "12px", borderRadius: "4px", - "&.MuiInputBase-adornedStart": { - paddingLeft: `${12 - buttonPadding.medium}px`, - }, - "&.MuiInputBase-adornedEnd": { - paddingRight: `${12 - buttonPadding.medium}px`, - }, }, size === "medium" && !multiline && { height: "40px", }, + size === "large" && + !multiline && { + height: "48px", + }, size === "hero" && { "& .MuiInputBase-input": { ...theme.typography.body1, @@ -97,22 +101,10 @@ const Input = styled(InputBase)(({ paddingLeft: "16px", paddingRight: "16px", borderRadius: "8px", - "&.MuiInputBase-adornedStart": { - paddingLeft: `${16 - buttonPadding.hero}px`, - }, - "&.MuiInputBase-adornedEnd": { - paddingRight: `${16 - buttonPadding.hero}px`, - }, [theme.breakpoints.down("sm")]: { "& .MuiInputBase-input": { ...theme.typography.body3, }, - "&.MuiInputBase-adornedStart": { - paddingLeft: `${12 - buttonPadding.heroMobile}px`, - }, - "&.MuiInputBase-adornedEnd": { - paddingRight: `${12 - buttonPadding.heroMobile}px`, - }, }, }, size === "hero" && @@ -130,6 +122,7 @@ const AdornmentButtonStyled = styled("button")(({ theme }) => ({ ...theme.typography.button, // display display: "flex", + flexShrink: 0, justifyContent: "center", alignItems: "center", // background and border @@ -144,24 +137,20 @@ const AdornmentButtonStyled = styled("button")(({ theme }) => ({ ":hover": { background: "rgba(0, 0, 0, 0.06)", }, + height: "100%", ".MuiInputBase-root &": { - // Extra padding to make button easier to click - width: pxToRem(20 + 2 * buttonPadding.medium), - height: pxToRem(20 + 2 * buttonPadding.medium), + width: "40px", ".MuiSvgIcon-root": { fontSize: pxToRem(20), }, }, ".MuiInputBase-sizeHero &": { - // Extra padding to make button easier to click - width: pxToRem(24 + 2 * buttonPadding.hero), - height: pxToRem(24 + 2 * buttonPadding.hero), + width: "56px", ".MuiSvgIcon-root": { fontSize: pxToRem(24), }, [theme.breakpoints.down("sm")]: { - width: pxToRem(16 + 2 * buttonPadding.heroMobile), - height: pxToRem(16 + 2 * buttonPadding.heroMobile), + width: "37px", ".MuiSvgIcon-root": { fontSize: pxToRem(16), }, From 23b8e8a7c1033c881a4a118b4ae3a8ad99405deb Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Tue, 10 Sep 2024 16:13:18 -0400 Subject: [PATCH 03/16] use SearchInput component on SearchPage --- .../src/pages/SearchPage/SearchPage.tsx | 59 ++++++++++--------- .../src/components/Input/Input.tsx | 14 ++--- .../components/SearchInput/SearchInput.tsx | 43 ++++---------- 3 files changed, 49 insertions(+), 67 deletions(-) diff --git a/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx b/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx index a3d6d08318..8b563332ca 100644 --- a/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx +++ b/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx @@ -9,11 +9,15 @@ import { getDepartmentName, } from "@mitodl/course-search-utils" import SearchDisplay from "@/page-components/SearchDisplay/SearchDisplay" -import { SearchInput } from "@/page-components/SearchDisplay/SearchInput" -import { GridColumn, GridContainer } from "@/components/GridLayout/GridLayout" +import { + SearchInput, + styled, + Container, + theme, + VisuallyHidden, +} from "ol-components" import type { LearningResourceOfferor } from "api" import { useOfferorsList } from "api/hooks/learningResources" -import { styled, Container, Grid, theme, VisuallyHidden } from "ol-components" import { capitalize } from "ol-utilities" import MetaTags from "@/page-components/MetaTags/MetaTags" @@ -46,12 +50,19 @@ const Header = styled.div` align-items: center; ` -const SearchField = styled(SearchInput)` - ${({ theme }) => theme.breakpoints.up("md")} { - width: 680px; - min-width: 680px; - } -` +const SearchFieldContainer = styled(Container)({ + display: "flex", + justifyContent: "center", +}) + +const SearchField = styled(SearchInput)(({ theme }) => ({ + [theme.breakpoints.down("sm")]: { + width: "100%", + }, + [theme.breakpoints.up("sm")]: { + width: "570px", + }, +})) const LEARNING_MATERIAL = "learning_material" @@ -216,24 +227,18 @@ const SearchPage: React.FC = () => {

Search

- - - - - setCurrentText(e.target.value)} - onSubmit={(e) => { - onSearchTermSubmit(e.target.value) - }} - onClear={() => { - onSearchTermSubmit("") - }} - placeholder="What do you want to learn?" - /> - - - + + setCurrentText(e.target.value)} + onSubmit={(e) => { + onSearchTermSubmit(e.target.value) + }} + onClear={() => { + onSearchTermSubmit("") + }} + /> +
({ }, height: "100%", ".MuiInputBase-root &": { - width: "40px", + width: "48px", ".MuiSvgIcon-root": { fontSize: pxToRem(20), }, }, ".MuiInputBase-sizeHero &": { - width: "56px", + width: "72px", ".MuiSvgIcon-root": { fontSize: pxToRem(24), }, [theme.breakpoints.down("sm")]: { - width: "37px", + width: "56px", ".MuiSvgIcon-root": { fontSize: pxToRem(16), }, diff --git a/frontends/ol-components/src/components/SearchInput/SearchInput.tsx b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx index f5cf330689..5386c03a4f 100644 --- a/frontends/ol-components/src/components/SearchInput/SearchInput.tsx +++ b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx @@ -3,44 +3,18 @@ import { RiSearch2Line, RiCloseLine } from "@remixicon/react" import { Input, AdornmentButton } from "../Input/Input" import type { InputProps } from "../Input/Input" import styled from "@emotion/styled" -import { pxToRem } from "../ThemeProvider/typography" const StyledInput = styled(Input)(({ theme }) => ({ - height: "72px", boxShadow: "0px 8px 20px 0px rgba(120, 147, 172, 0.10)", - "&.MuiInputBase-adornedEnd": { - paddingRight: "0 !important", - }, [theme.breakpoints.down("sm")]: { - height: "56px", gap: "8px", }, })) -const StyledAdornmentButton = styled(AdornmentButton)(({ theme }) => ({ - ".MuiInputBase-sizeHero &": { - width: "72px", - height: "100%", - flexShrink: 0, - ".MuiSvgIcon-root": { - fontSize: pxToRem(24), - }, - [theme.breakpoints.down("sm")]: { - width: "56px", - height: "100%", - ".MuiSvgIcon-root": { - fontSize: pxToRem(16), - }, - }, - }, -})) - -const StyledClearButton = styled(StyledAdornmentButton)({ - ".MuiInputBase-sizeHero &": { - width: "32px", - ["&:hover"]: { - backgroundColor: "transparent", - }, +const StyledClearButton = styled(AdornmentButton)({ + width: "32px !important", + ["&:hover"]: { + backgroundColor: "transparent", }, }) @@ -99,7 +73,10 @@ const SearchInput: React.FC = (props) => { // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus={props.autoFocus} className={props.className} - placeholder={props.placeholder} + placeholder={ + props.placeholder ?? + "Search for courses, programs, and learning materials..." + } value={props.value} onChange={props.onChange} onKeyDown={onInputKeyDown} @@ -114,13 +91,13 @@ const SearchInput: React.FC = (props) => { )} - - + } /> From ea591a801392275b57847d64f7f7073e4c49b182 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Tue, 10 Sep 2024 16:13:57 -0400 Subject: [PATCH 04/16] remove extra placeholder setting --- frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx b/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx index c5e79b49a6..efe922953b 100644 --- a/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx @@ -237,7 +237,6 @@ const HeroSearch: React.FC = () => { Date: Tue, 10 Sep 2024 16:21:35 -0400 Subject: [PATCH 05/16] set ChannelSearch to use the SearchInput component --- .../src/pages/ChannelPage/ChannelSearch.tsx | 44 +++++++------------ 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx b/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx index 30fec19a8c..d6e396b41d 100644 --- a/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx +++ b/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx @@ -14,36 +14,25 @@ import type { } from "@mitodl/course-search-utils" import { useSearchParams } from "@mitodl/course-search-utils/react-router" import SearchDisplay from "@/page-components/SearchDisplay/SearchDisplay" -import { SearchInput } from "@/page-components/SearchDisplay/SearchInput" +import { SearchInput, styled, VisuallyHidden } from "ol-components" import { getFacetManifest } from "@/pages/SearchPage/SearchPage" import _ from "lodash" -import { styled, VisuallyHidden } from "ol-components" - -const SearchInputContainer = styled.div` - padding-bottom: 40px; - - ${({ theme }) => theme.breakpoints.down("md")} { - padding-bottom: 35px; - } -` - -const StyledSearchInput = styled(SearchInput)` - justify-content: center; - ${({ theme }) => theme.breakpoints.up("md")} { - .input-field { - height: 40px; - width: 450px; - } - - .button-field { - height: 40px; - padding: 12px 16px 12px 12px; - width: 20px; - } - } -` + +const SearchInputContainer = styled.div(({ theme }) => ({ + width: "100%", + display: "flex", + justifyContent: "center", + paddingBottom: "40px", + [theme.breakpoints.down("md")]: { + paddingBottom: "35px", + }, +})) + +const StyledSearchInput = styled(SearchInput)({ + width: "624px", +}) const FACETS_BY_CHANNEL_TYPE: Record = { [ChannelTypeEnum.Topic]: [ @@ -192,9 +181,6 @@ const ChannelSearch: React.FC = ({ onClear={() => { setCurrentTextAndQuery("") }} - classNameInput="input-field" - classNameSearch="button-field" - placeholder="Search for courses, programs, and learning materials..." /> From bee34f290a196b6af4d7abd952d0d52a62f0a0a4 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Tue, 10 Sep 2024 16:22:01 -0400 Subject: [PATCH 06/16] remove extraneous SearchInput component from page-components --- .../SearchDisplay/SearchInput.test.tsx | 79 --------- .../SearchDisplay/SearchInput.tsx | 162 ------------------ 2 files changed, 241 deletions(-) delete mode 100644 frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.test.tsx delete mode 100644 frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.tsx diff --git a/frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.test.tsx b/frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.test.tsx deleted file mode 100644 index 19c16f95f5..0000000000 --- a/frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.test.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from "react" -import { render, screen } from "@testing-library/react" -import userEvent from "@testing-library/user-event" -import { SearchInput } from "./SearchInput" -import type { SearchInputProps } from "./SearchInput" -import invariant from "tiny-invariant" -import { ThemeProvider } from "ol-components" - -const getSearchInput = () => { - const element = screen.getByLabelText("Search for") - invariant(element instanceof HTMLInputElement) - return element -} - -const getSearchButton = (): HTMLButtonElement => { - const button = screen.getByLabelText("Search") - invariant(button instanceof HTMLButtonElement) - return button -} - -/** - * This actually returns an icon (inside a button) - */ -const getClearButton = (): HTMLButtonElement => { - const button = screen.getByLabelText("Clear search text") - invariant(button instanceof HTMLButtonElement) - return button -} - -const searchEvent = (value: string) => - expect.objectContaining({ target: { value } }) - -describe("SearchInput", () => { - const renderSearchInput = (props: Partial = {}) => { - const { value = "", ...otherProps } = props - const onSubmit = jest.fn() - const onChange = jest.fn((e) => e.persist()) - const onClear = jest.fn() - render( - , - { wrapper: ThemeProvider }, - ) - const user = userEvent.setup() - const spies = { onClear, onChange, onSubmit } - return { user, spies } - } - - it("Renders the given value in input", () => { - renderSearchInput({ value: "math" }) - expect(getSearchInput().value).toBe("math") - }) - - it("Calls onChange when text is typed", async () => { - const { user, spies } = renderSearchInput({ value: "math" }) - const input = getSearchInput() - await user.type(getSearchInput(), "s") - expect(spies.onChange).toHaveBeenCalledWith( - expect.objectContaining({ target: input }), - ) - }) - - it("Calls onSubmit when search is clicked", async () => { - const { user, spies } = renderSearchInput({ value: "chemistry" }) - await user.click(getSearchButton()) - expect(spies.onSubmit).toHaveBeenCalledWith(searchEvent("chemistry")) - }) - - it("Calls onClear clear is clicked", async () => { - const { user, spies } = renderSearchInput({ value: "biology" }) - await user.click(getClearButton()) - expect(spies.onClear).toHaveBeenCalled() - }) -}) diff --git a/frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.tsx b/frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.tsx deleted file mode 100644 index 27fcdce3a3..0000000000 --- a/frontends/mit-learn/src/page-components/SearchDisplay/SearchInput.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import React, { useCallback } from "react" - -import { - Input, - AdornmentButton, - FormGroup, - Button, - styled, - css, -} from "ol-components" -import type { InputProps } from "ol-components" -import { RiSearch2Line, RiCloseLine } from "@remixicon/react" - -export interface SearchSubmissionEvent { - target: { - value: string - } - /** - * Deprecated. course-search-utils calls unnecessarily. - */ - preventDefault: () => void -} - -const StyledInput = styled(Input)` - border-radius: 0; - border-top-left-radius: 8px; - border-bottom-left-radius: 8px; - width: 556px; - border-right: none; - height: 48px; - - &.Mui-focused { - border-color: ${({ theme }) => theme.custom.colors.darkGray2}; - color: ${({ theme }) => theme.custom.colors.darkGray2}; - } - - ${({ theme }) => theme.breakpoints.down("md")} { - height: 37px; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - width: 80%; - } -` - -const StyledButton = styled(Button)` - border-top-left-radius: 0; - border-bottom-left-radius: 0; - - ${({ theme }) => theme.breakpoints.up("md")} { - ${({ theme }) => css({ ...theme.typography.body2 })}; - min-width: 64px; - height: 48px; - padding: 8px 16px; - border-top-right-radius: 8px; - border-bottom-right-radius: 8px; - - svg { - height: 1.5em; - width: 1.5em; - } - } - - ${({ theme }) => theme.breakpoints.down("md")} { - ${({ theme }) => css({ ...theme.typography.body4 })}; - height: 37px; - width: 40px; - min-width: 40px; - padding: 0; - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - - svg { - height: 1em; - width: 1em; - font-size: 18px; - } - } -` - -const StyledFormGroup = styled(FormGroup)` - width: 100%; -` - -type SearchSubmitHandler = (event: SearchSubmissionEvent) => void - -interface SearchInputProps { - className?: string - classNameInput?: string - - classNameClear?: string - classNameSearch?: string - value: string - placeholder?: string - autoFocus?: boolean - onChange: React.ChangeEventHandler - onClear: React.MouseEventHandler - onSubmit: SearchSubmitHandler - size?: InputProps["size"] - fullWidth?: boolean -} - -const muiInputProps = { "aria-label": "Search for" } - -const SearchInput: React.FC = (props) => { - const { onSubmit, value } = props - const handleSubmit = useCallback(() => { - const event = { - target: { value }, - preventDefault: () => null, - } - onSubmit(event) - }, [onSubmit, value]) - const onInputKeyDown: React.KeyboardEventHandler = - useCallback( - (e) => { - if (e.key !== "Enter") return - handleSubmit() - }, - [handleSubmit], - ) - - return ( - - - - - ) - } - /> - - - - - ) -} - -export { SearchInput } -export type { SearchInputProps } From 1c9fab1783e5d18e1f473e8181ffb11c14efa99f Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Tue, 10 Sep 2024 16:24:25 -0400 Subject: [PATCH 07/16] input text color should always be darkGray2 --- frontends/ol-components/src/components/Input/Input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/ol-components/src/components/Input/Input.tsx b/frontends/ol-components/src/components/Input/Input.tsx index d8c86f3600..2f8c8177c0 100644 --- a/frontends/ol-components/src/components/Input/Input.tsx +++ b/frontends/ol-components/src/components/Input/Input.tsx @@ -15,7 +15,7 @@ const defaultProps = { */ const baseInputStyles = (theme: Theme) => ({ backgroundColor: "white", - color: theme.custom.colors.silverGrayDark, + color: theme.custom.colors.darkGray2, borderColor: theme.custom.colors.silverGrayLight, borderWidth: "1px", borderStyle: "solid", From 9fc8e5381e98ad0897e2a9dfce2901ef6a8e664f Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Wed, 11 Sep 2024 14:32:41 -0400 Subject: [PATCH 08/16] Update sizes based on Figma --- .../src/pages/ChannelPage/ChannelSearch.tsx | 1 + .../src/pages/SearchPage/SearchPage.tsx | 1 + .../src/components/Input/Input.stories.tsx | 15 +++-- .../src/components/Input/Input.tsx | 67 +++++++++++++------ frontends/ol-components/src/types/theme.d.ts | 2 +- 5 files changed, 60 insertions(+), 26 deletions(-) diff --git a/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx b/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx index d6e396b41d..98e25d6754 100644 --- a/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx +++ b/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx @@ -174,6 +174,7 @@ const ChannelSearch: React.FC = ({ setCurrentText(e.target.value)} onSubmit={(e) => { setCurrentTextAndQuery(e.target.value) diff --git a/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx b/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx index 8b563332ca..5531aa0b61 100644 --- a/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx +++ b/frontends/mit-learn/src/pages/SearchPage/SearchPage.tsx @@ -230,6 +230,7 @@ const SearchPage: React.FC = () => { setCurrentText(e.target.value)} onSubmit={(e) => { onSearchTermSubmit(e.target.value) diff --git a/frontends/ol-components/src/components/Input/Input.stories.tsx b/frontends/ol-components/src/components/Input/Input.stories.tsx index 6abc99871f..86f7bd6cf2 100644 --- a/frontends/ol-components/src/components/Input/Input.stories.tsx +++ b/frontends/ol-components/src/components/Input/Input.stories.tsx @@ -7,7 +7,12 @@ import Grid from "@mui/material/Grid" import { RiCalendarLine, RiCloseLine, RiSearchLine } from "@remixicon/react" import { fn } from "@storybook/test" -const SIZES = ["medium", "hero"] satisfies InputProps["size"][] +const SIZES = [ + "hero", + "large", + "medium", + "small", +] satisfies InputProps["size"][] const ADORNMENTS = { None: undefined, SearchIcon: ( @@ -85,7 +90,9 @@ export const Sizes: Story = { render: (args) => { return ( - + + + ) @@ -104,11 +111,11 @@ export const Adornments: Story = { }, ] return ( - + {Object.values(adornments).flatMap((props, i) => SIZES.map((size) => { return ( - + ) diff --git a/frontends/ol-components/src/components/Input/Input.tsx b/frontends/ol-components/src/components/Input/Input.tsx index 2f8c8177c0..00bd34e003 100644 --- a/frontends/ol-components/src/components/Input/Input.tsx +++ b/frontends/ol-components/src/components/Input/Input.tsx @@ -78,42 +78,55 @@ const Input = styled(InputBase)(({ }) => { return [ baseInputStyles(theme), + { + borderRadius: "4px", + }, + (size === "small" || size === "medium") && { + ...theme.typography.body2, + }, + (size === "large" || size === "hero") && { + ...theme.typography.body1, + }, size === "medium" && { - "& .MuiInputBase-input": { - ...theme.typography.body2, - }, paddingLeft: "12px", paddingRight: "12px", - borderRadius: "4px", }, size === "small" && !multiline && { - height: "40px", + height: "32px", }, size === "medium" && + !multiline && { + height: "40px", + }, + size === "large" && !multiline && { height: "48px", }, - size === "hero" && { - "& .MuiInputBase-input": { - ...theme.typography.body1, + size === "hero" && + !multiline && { + height: "72px", + [theme.breakpoints.down("sm")]: { + height: "37px", + }, }, - paddingLeft: "16px", - paddingRight: "16px", - borderRadius: "8px", + size === "small" && { + padding: "0 8px", + }, + size === "medium" && { + padding: "0 12px", + }, + size === "large" && { + padding: "0 16px", + }, + size === "hero" && { + padding: "0 24px", [theme.breakpoints.down("sm")]: { "& .MuiInputBase-input": { ...theme.typography.body3, }, }, }, - size === "hero" && - !multiline && { - height: "72px", - [theme.breakpoints.down("sm")]: { - height: "56px", - }, - }, ] }) @@ -138,19 +151,31 @@ const AdornmentButtonStyled = styled("button")(({ theme }) => ({ background: "rgba(0, 0, 0, 0.06)", }, height: "100%", - ".MuiInputBase-root &": { - width: "48px", + ".MuiInputBase-sizeSmall &": { + width: "32px", + ".MuiSvgIcon-root": { + fontSize: pxToRem(16), + }, + }, + ".MuiInputBase-sizeMedium &": { + width: "40px", ".MuiSvgIcon-root": { fontSize: pxToRem(20), }, }, + ".MuiInputBase-sizeLarge &": { + width: "48px", + ".MuiSvgIcon-root": { + fontSize: pxToRem(24), + }, + }, ".MuiInputBase-sizeHero &": { width: "72px", ".MuiSvgIcon-root": { fontSize: pxToRem(24), }, [theme.breakpoints.down("sm")]: { - width: "56px", + width: "37px", ".MuiSvgIcon-root": { fontSize: pxToRem(16), }, diff --git a/frontends/ol-components/src/types/theme.d.ts b/frontends/ol-components/src/types/theme.d.ts index ed1f53457f..e971ffe9af 100644 --- a/frontends/ol-components/src/types/theme.d.ts +++ b/frontends/ol-components/src/types/theme.d.ts @@ -71,7 +71,7 @@ declare module "@mui/material/Button" { declare module "@mui/material/InputBase" { interface InputBasePropsSizeOverrides { hero: true - small: false + large: true } } From c3a05c47aab1afe1b6282b50789c4fa5040ff586 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Wed, 11 Sep 2024 15:02:35 -0400 Subject: [PATCH 09/16] fix padding --- frontends/ol-components/src/components/Input/Input.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontends/ol-components/src/components/Input/Input.tsx b/frontends/ol-components/src/components/Input/Input.tsx index 00bd34e003..45844c281c 100644 --- a/frontends/ol-components/src/components/Input/Input.tsx +++ b/frontends/ol-components/src/components/Input/Input.tsx @@ -19,6 +19,10 @@ const baseInputStyles = (theme: Theme) => ({ borderColor: theme.custom.colors.silverGrayLight, borderWidth: "1px", borderStyle: "solid", + borderRadius: "4px", + "&.MuiInputBase-input": { + padding: "0", + }, "&.Mui-disabled": { backgroundColor: theme.custom.colors.lightGray1, }, @@ -78,9 +82,6 @@ const Input = styled(InputBase)(({ }) => { return [ baseInputStyles(theme), - { - borderRadius: "4px", - }, (size === "small" || size === "medium") && { ...theme.typography.body2, }, From d9e80d647ef2e032ca961b424a6e168322236a37 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Wed, 11 Sep 2024 15:07:51 -0400 Subject: [PATCH 10/16] fix css selector --- frontends/ol-components/src/components/Input/Input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/ol-components/src/components/Input/Input.tsx b/frontends/ol-components/src/components/Input/Input.tsx index 45844c281c..5e42954b64 100644 --- a/frontends/ol-components/src/components/Input/Input.tsx +++ b/frontends/ol-components/src/components/Input/Input.tsx @@ -20,7 +20,7 @@ const baseInputStyles = (theme: Theme) => ({ borderWidth: "1px", borderStyle: "solid", borderRadius: "4px", - "&.MuiInputBase-input": { + ".MuiInputBase-input": { padding: "0", }, "&.Mui-disabled": { From 914e07ccaf3796f686000eeb01af473a8ae716a3 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Wed, 11 Sep 2024 15:45:58 -0400 Subject: [PATCH 11/16] fix icon sizes and mobile styles --- .../src/components/Input/Input.tsx | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/frontends/ol-components/src/components/Input/Input.tsx b/frontends/ol-components/src/components/Input/Input.tsx index 5e42954b64..e5ace3b5f9 100644 --- a/frontends/ol-components/src/components/Input/Input.tsx +++ b/frontends/ol-components/src/components/Input/Input.tsx @@ -1,6 +1,5 @@ import React from "react" import styled from "@emotion/styled" -import { pxToRem } from "../ThemeProvider/typography" import InputBase from "@mui/material/InputBase" import type { InputBaseProps } from "@mui/material/InputBase" import type { Theme } from "@mui/material/styles" @@ -86,7 +85,20 @@ const Input = styled(InputBase)(({ ...theme.typography.body2, }, (size === "large" || size === "hero") && { + ".remixicon": { + width: "24px", + height: "24px", + }, ...theme.typography.body1, + [theme.breakpoints.down("sm")]: { + ".remixicon": { + width: "20px", + height: "20px", + }, + "& .MuiInputBase-input": { + ...theme.typography.body3, + }, + }, }, size === "medium" && { paddingLeft: "12px", @@ -103,12 +115,15 @@ const Input = styled(InputBase)(({ size === "large" && !multiline && { height: "48px", + [theme.breakpoints.down("sm")]: { + height: "40px", + }, }, size === "hero" && !multiline && { height: "72px", [theme.breakpoints.down("sm")]: { - height: "37px", + height: "48px", }, }, size === "small" && { @@ -122,11 +137,6 @@ const Input = styled(InputBase)(({ }, size === "hero" && { padding: "0 24px", - [theme.breakpoints.down("sm")]: { - "& .MuiInputBase-input": { - ...theme.typography.body3, - }, - }, }, ] }) @@ -154,32 +164,28 @@ const AdornmentButtonStyled = styled("button")(({ theme }) => ({ height: "100%", ".MuiInputBase-sizeSmall &": { width: "32px", - ".MuiSvgIcon-root": { - fontSize: pxToRem(16), + ".remixicon": { + width: "16px", + height: "16px", }, }, ".MuiInputBase-sizeMedium &": { width: "40px", - ".MuiSvgIcon-root": { - fontSize: pxToRem(20), + ".remixicon": { + width: "20px", + height: "20px", }, }, ".MuiInputBase-sizeLarge &": { width: "48px", - ".MuiSvgIcon-root": { - fontSize: pxToRem(24), + [theme.breakpoints.down("sm")]: { + width: "40px", }, }, ".MuiInputBase-sizeHero &": { width: "72px", - ".MuiSvgIcon-root": { - fontSize: pxToRem(24), - }, [theme.breakpoints.down("sm")]: { - width: "37px", - ".MuiSvgIcon-root": { - fontSize: pxToRem(16), - }, + width: "48px", }, }, From 84a71caa33a6906af39bf629a677d9d8bbc45354 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Wed, 11 Sep 2024 15:47:08 -0400 Subject: [PATCH 12/16] wrap channel search in a container --- frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx b/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx index 98e25d6754..3e77db3e06 100644 --- a/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx +++ b/frontends/mit-learn/src/pages/ChannelPage/ChannelSearch.tsx @@ -14,13 +14,13 @@ import type { } from "@mitodl/course-search-utils" import { useSearchParams } from "@mitodl/course-search-utils/react-router" import SearchDisplay from "@/page-components/SearchDisplay/SearchDisplay" -import { SearchInput, styled, VisuallyHidden } from "ol-components" +import { Container, SearchInput, styled, VisuallyHidden } from "ol-components" import { getFacetManifest } from "@/pages/SearchPage/SearchPage" import _ from "lodash" -const SearchInputContainer = styled.div(({ theme }) => ({ +const SearchInputContainer = styled(Container)(({ theme }) => ({ width: "100%", display: "flex", justifyContent: "center", From 1b90c4035f7cfb17ef08f5aecf7614e5af24e953 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Wed, 11 Sep 2024 15:57:21 -0400 Subject: [PATCH 13/16] hero sized search input on desktop gets 8px border radius --- .../ol-components/src/components/SearchInput/SearchInput.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontends/ol-components/src/components/SearchInput/SearchInput.tsx b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx index 5386c03a4f..c413df7155 100644 --- a/frontends/ol-components/src/components/SearchInput/SearchInput.tsx +++ b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx @@ -9,6 +9,11 @@ const StyledInput = styled(Input)(({ theme }) => ({ [theme.breakpoints.down("sm")]: { gap: "8px", }, + [theme.breakpoints.up("sm")]: { + "&.MuiInputBase-sizeHero": { + borderRadius: "8px !important", + }, + }, })) const StyledClearButton = styled(AdornmentButton)({ From a44625914b38e27182a1a06853d0ffdc9f5ec696 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Wed, 11 Sep 2024 16:00:07 -0400 Subject: [PATCH 14/16] remove ts-expect-error --- .../ol-components/src/components/SelectField/SelectField.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontends/ol-components/src/components/SelectField/SelectField.tsx b/frontends/ol-components/src/components/SelectField/SelectField.tsx index 2c74906538..055d9167a7 100644 --- a/frontends/ol-components/src/components/SelectField/SelectField.tsx +++ b/frontends/ol-components/src/components/SelectField/SelectField.tsx @@ -109,9 +109,6 @@ function Select({ size, ...props }: SelectProps) { } From 3b88e91ecd12a2f8707f56c9a3b18a17c54c8642 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Fri, 13 Sep 2024 14:45:15 -0400 Subject: [PATCH 15/16] per feedback, make size based styling functional --- .../src/components/Input/Input.tsx | 197 +++++++++--------- 1 file changed, 99 insertions(+), 98 deletions(-) diff --git a/frontends/ol-components/src/components/Input/Input.tsx b/frontends/ol-components/src/components/Input/Input.tsx index e5ace3b5f9..86d7c2f844 100644 --- a/frontends/ol-components/src/components/Input/Input.tsx +++ b/frontends/ol-components/src/components/Input/Input.tsx @@ -4,11 +4,90 @@ import InputBase from "@mui/material/InputBase" import type { InputBaseProps } from "@mui/material/InputBase" import type { Theme } from "@mui/material/styles" +type Size = NonNullable + const defaultProps = { size: "medium", multiline: false, +} as const + +const responsiveSize: Record = { + small: "small", + medium: "small", + large: "medium", + hero: "large", } +type SizeStyleProps = { + size: Size + theme: Theme + multiline?: boolean +} +const sizeStyles = ({ size, theme, multiline }: SizeStyleProps) => [ + (size === "small" || size === "medium") && { + ...theme.typography.body2, + }, + (size === "large" || size === "hero") && { + ".remixicon": { + width: "24px", + height: "24px", + }, + ...theme.typography.body1, + }, + size === "medium" && { + paddingLeft: "12px", + paddingRight: "12px", + }, + size === "small" && + !multiline && { + height: "32px", + }, + size === "medium" && + !multiline && { + height: "40px", + }, + size === "large" && + !multiline && { + height: "48px", + }, + size === "hero" && + !multiline && { + height: "72px", + }, + size === "small" && { + padding: "0 8px", + ".Mit-AdornmentButton": { + width: "32px", + ".remixicon": { + width: "16px", + height: "16px", + }, + }, + }, + size === "medium" && { + padding: "0 12px", + ".Mit-AdornmentButton": { + width: "40px", + ".remixicon": { + width: "20px", + height: "20px", + }, + }, + }, + size === "large" && { + padding: "0 16px", + ".Mit-AdornmentButton": { + width: "48px", + }, + }, + size === "hero" && { + padding: "0 24px", + ".Mit-AdornmentButton": { + width: "72px", + }, + }, +] + /** * Base styles for Input and Select components. Includes border, color, hover effects. */ @@ -74,69 +153,28 @@ const baseInputStyles = (theme: Theme) => ({ * A styled input that supports start and end adornments. In most cases, the * higher-level TextField component should be used instead of this component. */ -const Input = styled(InputBase)(({ +type CustomInputProps = { responsive?: true } +const noForward = Object.keys({ + responsive: true, +} satisfies { [key in keyof CustomInputProps]: boolean }) + +const Input = styled(InputBase, { + shouldForwardProp: (prop) => !noForward.includes(prop), +})(({ theme, size = defaultProps.size, multiline, + responsive, }) => { return [ baseInputStyles(theme), - (size === "small" || size === "medium") && { - ...theme.typography.body2, - }, - (size === "large" || size === "hero") && { - ".remixicon": { - width: "24px", - height: "24px", - }, - ...theme.typography.body1, - [theme.breakpoints.down("sm")]: { - ".remixicon": { - width: "20px", - height: "20px", - }, - "& .MuiInputBase-input": { - ...theme.typography.body3, - }, - }, - }, - size === "medium" && { - paddingLeft: "12px", - paddingRight: "12px", - }, - size === "small" && - !multiline && { - height: "32px", - }, - size === "medium" && - !multiline && { - height: "40px", - }, - size === "large" && - !multiline && { - height: "48px", - [theme.breakpoints.down("sm")]: { - height: "40px", - }, - }, - size === "hero" && - !multiline && { - height: "72px", - [theme.breakpoints.down("sm")]: { - height: "48px", - }, - }, - size === "small" && { - padding: "0 8px", - }, - size === "medium" && { - padding: "0 12px", - }, - size === "large" && { - padding: "0 16px", - }, - size === "hero" && { - padding: "0 24px", + ...sizeStyles({ size, theme, multiline }), + responsive && { + [theme.breakpoints.down("sm")]: sizeStyles({ + size: responsiveSize[size], + theme, + multiline, + }), }, ] }) @@ -162,43 +200,6 @@ const AdornmentButtonStyled = styled("button")(({ theme }) => ({ background: "rgba(0, 0, 0, 0.06)", }, height: "100%", - ".MuiInputBase-sizeSmall &": { - width: "32px", - ".remixicon": { - width: "16px", - height: "16px", - }, - }, - ".MuiInputBase-sizeMedium &": { - width: "40px", - ".remixicon": { - width: "20px", - height: "20px", - }, - }, - ".MuiInputBase-sizeLarge &": { - width: "48px", - [theme.breakpoints.down("sm")]: { - width: "40px", - }, - }, - ".MuiInputBase-sizeHero &": { - width: "72px", - [theme.breakpoints.down("sm")]: { - width: "48px", - }, - }, - - color: theme.custom.colors.silverGray, - ".MuiInputBase-root:hover &": { - color: "inherit", - }, - ".MuiInputBase-root.Mui-focused &": { - color: "inherit", - }, - ".MuiInputBase-root.Mui-disabled &": { - color: "inherit", - }, })) const noFocus: React.MouseEventHandler = (e) => e.preventDefault() @@ -210,9 +211,9 @@ type AdornmentButtonProps = React.ComponentProps * styling concerns. * * NOTES: - * - It is generally expected that the content of the AdornmentButton is an - * Mui Icon component. https://mui.com/material-ui/material-icons/ - * - By defualt, the AdornmentButton calls `preventDefault` on `mouseDown` + * - It is generally expected that the content of the AdornmentButton is a + * Remix Icon component. https://remixicon.com/ + * - By default, the AdornmentButton calls `preventDefault` on `mouseDown` * events. This prevents the button from stealing focus from the input on * click. The button is still focusable via keyboard events. You can override * this behavior by passing your own `onMouseDown` handler. @@ -230,7 +231,7 @@ const AdornmentButton: React.FC = (props) => { ) } -type InputProps = Omit +type InputProps = Omit & CustomInputProps export { AdornmentButton, Input, baseInputStyles } export type { InputProps, AdornmentButtonProps } From a5982d09c3c2556e0d5a823cfaa0609591b19893 Mon Sep 17 00:00:00 2001 From: Carey Gumaer Date: Fri, 13 Sep 2024 14:47:31 -0400 Subject: [PATCH 16/16] make SearchInput responsive --- .../ol-components/src/components/SearchInput/SearchInput.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontends/ol-components/src/components/SearchInput/SearchInput.tsx b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx index c413df7155..58c6c0935a 100644 --- a/frontends/ol-components/src/components/SearchInput/SearchInput.tsx +++ b/frontends/ol-components/src/components/SearchInput/SearchInput.tsx @@ -105,6 +105,7 @@ const SearchInput: React.FC = (props) => { } + responsive /> ) }