diff --git a/CHANGELOG.md b/CHANGELOG.md index a3ae8a0..aa674cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- support to allow menu size to fit the longest option into `StaticTypeahead` and `AsyncTypeahead`, via `fitMenuContent`. + ## [3.15.1] - 2026-01-12 ### Fix diff --git a/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx b/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx index 662ce3b..c0f1e7f 100644 --- a/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx +++ b/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx @@ -931,7 +931,6 @@ it("works with fixed options excluded", () => { }); it("innerRef works correctly", () => { - const { simpleOptions } = generateOptions(); const name = faker.random.alpha(10); const options = generateOptions(); @@ -964,3 +963,39 @@ it("innerRef works correctly", () => { cy.get("button[title=focus]").click(); cy.get(`#${name}`).should("be.focused"); }); + +it("works with fitContentMenu", () => { + const name = faker.random.alpha(10); + const specificOptions = [ + { label: "A Very Long Movie Title That Exceeds Normal Lengths", value: "1" }, + { label: "The Lord of the Rings: The Return of the King", value: "2" }, + ]; + + cy.mount( +
+
{ + // Nothing to do + }} + > + await fetchMock(specificOptions, query, true)} + name={name} + label={name} + fitMenuContent + /> + +
, + ); + + cy.get(`#${name}`).click(); + cy.focused().type(specificOptions[0].label); + cy.get(".MuiInputBase-root").should("have.css", "width", "300px"); + cy.get("div[role='presentation']").should(($div) => { + const popperWidth = $div.width() ?? 0; + const paperWidth = $div.find("div.MuiPaper-root").width() ?? 0; + expect(paperWidth).to.be.equal(popperWidth); + expect(popperWidth).to.be.greaterThan(300); + }); +}); diff --git a/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx b/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx index 72ff0cb..b808db5 100644 --- a/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx +++ b/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx @@ -621,3 +621,37 @@ it("innerRef works correctly", () => { cy.get("button[title=focus]").click(); cy.get(`#${name}`).should("be.focused"); }); + +it("works with fitContentMenu", () => { + const { simpleOptions } = generateOptions(); + const name = faker.random.alpha(10); + const specificOptions = ["A Very Long Movie Title That Exceeds Normal Lengths", "The Lord of the Rings: The Return of the King"]; + + cy.mount( +
+
{ + // Nothing to do + }} + > + + +
, + ); + + cy.get(`#${name}`).click(); + cy.get(".MuiInputBase-root").should("have.css", "width", "300px"); + cy.get("div[role='presentation']").should(($div) => { + const popperWidth = $div.width() ?? 0; + const paperWidth = $div.find("div.MuiPaper-root").width() ?? 0; + expect(paperWidth).to.be.equal(popperWidth); + expect(popperWidth).to.be.greaterThan(300); + }); +}); diff --git a/src/index.ts b/src/index.ts index cc0178a..7f5b4fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ export * from "./lib/types/Form"; export * from "./lib/DatePickerInput"; export * from "./lib/ColorPickerInput"; export * from "./lib/RatingInput"; +export * from "./lib/components/Typeahead/TypeaheadFitMenuPopper"; export * from "./lib/helpers/dateUtils"; export * from "./lib/helpers/form"; export * from "./lib/helpers/mui"; diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index 7c007cf..4f855f8 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -19,6 +19,7 @@ import { useFormContext } from "./context/FormContext"; import { TypeaheadTextField } from "./components/Typeahead/TypeaheadTextField"; import { FormGroupLayout } from "./FormGroupLayout"; import { LabelValueOption } from "./types/LabelValueOption"; +import { TypeaheadFitMenuPopper } from "./components/Typeahead/TypeaheadFitMenuPopper"; interface AsyncTypeaheadInputRef { resetValues: () => void; @@ -75,6 +76,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr autocompleteProps, fixedOptions, withFixedOptionsInValue = true, + fitMenuContent, } = props; const [options, setOptions] = useState(defaultOptions); @@ -145,6 +147,10 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr {...autocompleteProps} {...field} + slots={{ + popper: fitMenuContent ? TypeaheadFitMenuPopper : undefined, + ...autocompleteProps?.slots, + }} id={id} multiple={multiple} loading={isLoading} diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index 1e23df8..3c0faa7 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -22,6 +22,7 @@ import { import { TypeaheadTextField } from "./components/Typeahead/TypeaheadTextField"; import { FormGroupLayout } from "./FormGroupLayout"; import { LabelValueOption } from "./types/LabelValueOption"; +import { TypeaheadFitMenuPopper } from "./components/Typeahead/TypeaheadFitMenuPopper"; interface StaticTypeaheadInputProps extends CommonTypeaheadProps { options: TypeaheadOptions; @@ -66,6 +67,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput fixedOptions, withFixedOptionsInValue = true, innerRef, + fitMenuContent, } = props; const [page, setPage] = useState(1); @@ -118,6 +120,10 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput {...autocompleteProps} {...field} + slots={{ + popper: fitMenuContent ? TypeaheadFitMenuPopper : undefined, + ...autocompleteProps?.slots, + }} id={id} multiple={multiple} groupBy={useGroupBy ? groupOptions : undefined} diff --git a/src/lib/components/Typeahead/TypeaheadFitMenuPopper.tsx b/src/lib/components/Typeahead/TypeaheadFitMenuPopper.tsx new file mode 100644 index 0000000..ef272bc --- /dev/null +++ b/src/lib/components/Typeahead/TypeaheadFitMenuPopper.tsx @@ -0,0 +1,20 @@ +import Popper, { PopperProps } from "@mui/material/Popper"; + +const TypeaheadFitMenuPopper = (props: PopperProps) => { + const { anchorEl } = props; + + return ( + + ); +}; + +export { TypeaheadFitMenuPopper }; diff --git a/src/lib/types/Typeahead.ts b/src/lib/types/Typeahead.ts index 9ebb3c0..de2e3e3 100644 --- a/src/lib/types/Typeahead.ts +++ b/src/lib/types/Typeahead.ts @@ -25,6 +25,7 @@ interface CommonTypeaheadProps fixedOptions?: TypeaheadOptions; withFixedOptionsInValue?: boolean; innerRef?: MutableRefObject; + fitMenuContent?: boolean; getOptionDisabled?: (option: TypeaheadOption) => boolean; onChange?: (selected: string | string[]) => void; onInputChange?: (text: string, reason: AutocompleteInputChangeReason) => void;