From 8a72d3a50d09a4d9cb8b84f65325975cc80071a4 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Fri, 11 Apr 2025 08:40:14 +0200 Subject: [PATCH 01/10] export typeahead utils --- src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.ts b/src/index.ts index 1aed903..434f057 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,5 +8,9 @@ export * from "./lib/types/LabelValueOption"; export * from "./lib/DatePickerInput"; export * from "./lib/ColorPickerInput"; export * from "./lib/helpers/dateUtils"; +export * from "./lib/helpers/mui"; +export * from "./lib/helpers/typeahead"; +export * from "./lib/hooks/useDebounceHook"; +export * from "./lib/hooks/useSafeNameId"; export { useFormContext } from "./lib/context/FormContext"; From f8dc2b0a903de315cb0b7d6e9032d320b15674a3 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Wed, 16 Apr 2025 12:38:57 +0200 Subject: [PATCH 02/10] integrate placeholder fix and changelog --- CHANGELOG.md | 8 ++++++++ .../component/Typeahead/AsyncTypeaheadInput.cy.tsx | 6 +++--- .../component/Typeahead/StaticTypeaheadInput.cy.tsx | 9 +++------ src/index.ts | 1 - src/lib/AsyncTypeaheadInput.tsx | 2 +- src/lib/StaticTypeaheadInput.tsx | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4503f74..c71d00b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Export typeahead helpers and `useDebounceHook` hook. + +### Fixed + +- Hide placeholder on multiple `AsyncTypeAheadInput` and `StaticTypeAheadInput` when at least one option is selected. + ## [3.1.1] - 2025-04-10 ### Fixed diff --git a/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx b/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx index 99dbe00..05f8998 100644 --- a/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx +++ b/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx @@ -641,9 +641,6 @@ it("placeholder", () => { onSubmit={() => { // Nothing to do }} - defaultValues={{ - [name]: simpleOptions, - }} > { , ); + cy.get(`#${name}`).should("have.attr", "placeholder", placeholder); + simpleOptions.slice(0, 2).forEach((option) => selectOption(name, option)); + cy.get(`#${name}`).should("not.have.attr", "placeholder"); }); it("test on input change", () => { diff --git a/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx b/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx index 8ec1066..ad64378 100644 --- a/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx +++ b/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx @@ -398,12 +398,7 @@ it("placeholder", () => { cy.mount(
-
+ @@ -411,6 +406,8 @@ it("placeholder", () => { ); cy.get(`#${name}`).should("have.attr", "placeholder", placeholder); + simpleOptions.slice(0, 2).forEach((option) => selectOption(name, option)); + cy.get(`#${name}`).should("not.have.attr", "placeholder"); }); it("test on input change", () => { diff --git a/src/index.ts b/src/index.ts index 434f057..8831d0a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,5 @@ export * from "./lib/helpers/dateUtils"; export * from "./lib/helpers/mui"; export * from "./lib/helpers/typeahead"; export * from "./lib/hooks/useDebounceHook"; -export * from "./lib/hooks/useSafeNameId"; export { useFormContext } from "./lib/context/FormContext"; diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index 42f8662..c2892a2 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -206,7 +206,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr hideValidationMessage={hideValidationMessage} useBootstrapStyle={useBootstrapStyle} helpText={helpText} - placeholder={placeholder} + placeholder={multiple && value.length > 0 ? undefined : placeholder} paginationIcon={paginationIcon} paginationText={paginationText} variant={variant} diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index b72e47c..ad59824 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -169,7 +169,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput hideValidationMessage={hideValidationMessage} useBootstrapStyle={useBootstrapStyle} helpText={helpText} - placeholder={placeholder} + placeholder={multiple && value.length > 0 ? undefined : placeholder} paginationIcon={paginationIcon} paginationText={paginationText} variant={variant} From a8573843aaf2a7d2b9f1de219c05ce930b328464 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Wed, 16 Apr 2025 15:27:23 +0200 Subject: [PATCH 03/10] adjust typescript --- src/lib/AsyncTypeaheadInput.tsx | 10 ++++++---- src/lib/StaticTypeaheadInput.tsx | 4 +++- src/lib/helpers/typeahead.tsx | 20 ++++++++++---------- src/lib/hooks/useDebounceHook.ts | 4 ++-- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index c2892a2..cab92da 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -69,8 +69,8 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr autocompleteProps, } = props; - const [options, setOptions] = useState(defaultOptions); - const [value, setValue] = useState(defaultSelected); + const [options, setOptions] = useState(defaultOptions); + const [value, setValue] = useState(defaultSelected); const [page, setPage] = useState(1); const [loadMoreOptions, setLoadMoreOptions] = useState(limitResults !== undefined && limitResults < defaultOptions.length); const { name, id } = useSafeNameId(props.name ?? "", props.id); @@ -107,7 +107,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr defaultSelected.map((x) => (typeof x === "string" ? x : x.value)), ); }, - updateValues: (options: TypeaheadOption[]) => { + updateValues: (options: TypeaheadOptions) => { const values = convertAutoCompleteOptionsToStringArray(options); const finalValue = multiple ? values : values[0]; setValue(options); @@ -171,7 +171,9 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr field.onBlur(); }} onChange={(_e, value) => { - const optionsArray = value ? (Array.isArray(value) ? value : [value]) : undefined; + // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) + // however, the component is not intended to be used with mixed types + const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; setValue(optionsArray ?? []); const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index ad59824..715281a 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -140,7 +140,9 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput field.onBlur(); }} onChange={(_, value) => { - const optionsArray = value ? (Array.isArray(value) ? value : [value]) : undefined; + // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) + // however, the component is not intended to be used with mixed types + const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; clearErrors(field.name); diff --git a/src/lib/helpers/typeahead.tsx b/src/lib/helpers/typeahead.tsx index 7aef6be..de85e7a 100644 --- a/src/lib/helpers/typeahead.tsx +++ b/src/lib/helpers/typeahead.tsx @@ -1,12 +1,12 @@ import { AutocompleteRenderOptionState } from "@mui/material/Autocomplete"; import { LabelValueOption } from "../types/LabelValueOption"; -import { TypeaheadOption } from "../types/Typeahead"; +import { TypeaheadOption, TypeaheadOptions } from "../types/Typeahead"; import AutosuggestHighlightMatch from "autosuggest-highlight/match"; import AutosuggestHighlightParse from "autosuggest-highlight/parse"; -const isStringArray = (options: TypeaheadOption[]): boolean => options.length > 0 && options.every((value) => typeof value === "string"); +const isStringArray = (options: TypeaheadOptions): boolean => options.length > 0 && (options as TypeaheadOption[]).every((value) => typeof value === "string"); -const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOption[] | undefined): string[] => { +const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOptions | undefined): string[] => { if (!options) { return []; } @@ -18,29 +18,29 @@ const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOption[] | un return (options as LabelValueOption[]).map((option) => option.value) as string[]; }; -const getSingleAutoCompleteValue = (options: TypeaheadOption[], fieldValue: string | number | undefined): TypeaheadOption[] => { +const getSingleAutoCompleteValue = (options: TypeaheadOptions, fieldValue: string | number | undefined): TypeaheadOptions => { if (fieldValue == undefined) { return []; } - return options.filter((x) => + return (options as TypeaheadOption[]).filter((x) => // loose equality check to handle different types between form value and option value typeof x === "string" ? x == fieldValue : x.value == fieldValue, - ); + ) as TypeaheadOptions; }; -const getMultipleAutoCompleteValue = (options: TypeaheadOption[], fieldValue: (string | number)[] | undefined): TypeaheadOption[] => { +const getMultipleAutoCompleteValue = (options: TypeaheadOptions, fieldValue: (string | number)[] | undefined): TypeaheadOptions => { if (fieldValue == undefined) { return []; } - return options.filter((x) => + return (options as TypeaheadOption[]).filter((x: TypeaheadOption) => typeof x === "string" ? fieldValue.includes(x) : // ensure that form values matches options values even if they are of different types fieldValue.map(String).includes(String(x.value as string | number)), - ); + ) as TypeaheadOptions; }; -const sortOptionsByGroup = (options: TypeaheadOption[]): TypeaheadOption[] => +const sortOptionsByGroup = (options: TypeaheadOptions): TypeaheadOptions => options.sort((x, y) => (typeof x === "string" ? x : x.group?.name ?? "").localeCompare(typeof y === "string" ? y : y.group?.name ?? "")); const groupOptions = (option: TypeaheadOption): string => (typeof option === "string" ? option : option.group?.name ?? ""); diff --git a/src/lib/hooks/useDebounceHook.ts b/src/lib/hooks/useDebounceHook.ts index c97f72c..31d4595 100644 --- a/src/lib/hooks/useDebounceHook.ts +++ b/src/lib/hooks/useDebounceHook.ts @@ -1,12 +1,12 @@ import { useEffect, useRef, useState } from "react"; -import { TypeaheadOption, TypeaheadOptions } from "../types/Typeahead"; +import { TypeaheadOptions } from "../types/Typeahead"; interface DebounceSearch { query: string; delay: number; } -const useDebounceHook = (queryFn: (query: string) => Promise, setOptions: (results: TypeaheadOption[]) => void) => { +const useDebounceHook = (queryFn: (query: string) => Promise, setOptions: (results: TypeaheadOptions) => void) => { const queryRef = useRef(""); const [isLoading, setIsLoading] = useState(false); const [debounceSearch, setDebounceSearch] = useState(undefined); From e1267ef282dd50afce477920d143f4a4ee555a8c Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Mon, 28 Apr 2025 09:34:08 +0200 Subject: [PATCH 04/10] prepare-pr --- CHANGELOG.md | 1 + src/lib/AsyncTypeaheadInput.tsx | 2 +- src/lib/StaticTypeaheadInput.tsx | 2 +- src/lib/helpers/typeahead.tsx | 5 +++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c71d00b..dc11842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Export typeahead helpers and `useDebounceHook` hook. +- Typeahead helpers return type from `TypeaheadOption[]` to `TypeaheadOptions`, since typeaheads are not intended to be used with mixed `string` and `LabelValueOption` options. ### Fixed diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index cab92da..c2850f3 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -173,7 +173,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr onChange={(_e, value) => { // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) // however, the component is not intended to be used with mixed types - const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; + const optionsArray = value ? ((Array.isArray(value) ? value : [value]) as TypeaheadOptions) : undefined; setValue(optionsArray ?? []); const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index 715281a..9e29134 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -142,7 +142,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput onChange={(_, value) => { // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) // however, the component is not intended to be used with mixed types - const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; + const optionsArray = value ? ((Array.isArray(value) ? value : [value]) as TypeaheadOptions) : undefined; const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; clearErrors(field.name); diff --git a/src/lib/helpers/typeahead.tsx b/src/lib/helpers/typeahead.tsx index de85e7a..d552b32 100644 --- a/src/lib/helpers/typeahead.tsx +++ b/src/lib/helpers/typeahead.tsx @@ -4,7 +4,8 @@ import { TypeaheadOption, TypeaheadOptions } from "../types/Typeahead"; import AutosuggestHighlightMatch from "autosuggest-highlight/match"; import AutosuggestHighlightParse from "autosuggest-highlight/parse"; -const isStringArray = (options: TypeaheadOptions): boolean => options.length > 0 && (options as TypeaheadOption[]).every((value) => typeof value === "string"); +const isStringArray = (options: TypeaheadOptions): boolean => + options.length > 0 && (options as TypeaheadOption[]).every((value) => typeof value === "string"); const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOptions | undefined): string[] => { if (!options) { @@ -32,7 +33,7 @@ const getMultipleAutoCompleteValue = (options: TypeaheadOptions, fieldValue: (st if (fieldValue == undefined) { return []; } - return (options as TypeaheadOption[]).filter((x: TypeaheadOption) => + return (options as TypeaheadOption[]).filter((x) => typeof x === "string" ? fieldValue.includes(x) : // ensure that form values matches options values even if they are of different types From 12211f1448a0b73a6d7b8e9795a8e7633706d4ae Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Wed, 30 Apr 2025 11:41:20 +0200 Subject: [PATCH 05/10] fixed --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc11842..99f5cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Export typeahead helpers and `useDebounceHook` hook. -- Typeahead helpers return type from `TypeaheadOption[]` to `TypeaheadOptions`, since typeaheads are not intended to be used with mixed `string` and `LabelValueOption` options. ### Fixed - Hide placeholder on multiple `AsyncTypeAheadInput` and `StaticTypeAheadInput` when at least one option is selected. +- Typeahead helpers return type from `TypeaheadOption[]` to `TypeaheadOptions`, since typeaheads are not intended to be used with mixed `string` and `LabelValueOption` options. ## [3.1.1] - 2025-04-10 From 2cd543a8245fe1ff8740dae4a03295d761485df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Masciav=C3=A8?= Date: Fri, 4 Sep 2026 09:27:01 +0200 Subject: [PATCH 06/10] solution --- CHANGELOG.md | 4 ++ src/lib/FormGroupLayout.tsx | 2 +- src/lib/FormGroupLayoutLabel.tsx | 84 ++++++++++++++++++------------- src/lib/types/CommonInputProps.ts | 2 +- 4 files changed, 55 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c5198..7e75657 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 react node for `labelToolTip` + ## [4.3.0] - 2026-05-20 ### Added diff --git a/src/lib/FormGroupLayout.tsx b/src/lib/FormGroupLayout.tsx index 2a166ff..e986821 100644 --- a/src/lib/FormGroupLayout.tsx +++ b/src/lib/FormGroupLayout.tsx @@ -82,7 +82,7 @@ const FormGroupLayout = (props: F labelStyle={labelStyle} fieldName={props.name} fieldId={id} - tooltip={labelToolTip} + labelToolTip={labelToolTip} layout={layout} /> {switchLayout || checkboxLayout || muiInputLayout ? ( diff --git a/src/lib/FormGroupLayoutLabel.tsx b/src/lib/FormGroupLayoutLabel.tsx index 6f6f5a2..9f3ac98 100644 --- a/src/lib/FormGroupLayoutLabel.tsx +++ b/src/lib/FormGroupLayoutLabel.tsx @@ -1,23 +1,60 @@ -import { CSSProperties, ReactNode } from "react"; import { useFormContext } from "./context/FormContext"; import { FieldPath, FieldValues } from "react-hook-form"; import { Label, UncontrolledTooltip } from "@neolution-ch/reactstrap"; import { getRequiredLabel } from "./helpers/form"; +import { FormGroupLayoutProps } from "./FormGroupLayout"; -interface FormGroupLayoutLabelProps { - label: ReactNode; - tooltip?: ReactNode; +type TooltipProps = Pick, "labelToolTip" | "fieldId">; + +const Tooltip = (props: TooltipProps) => { + const { fieldId, labelToolTip } = props; + const isTextTooltip = typeof labelToolTip === "string"; + + if (!labelToolTip) { + return null; + } + + if (!isTextTooltip) { + return labelToolTip; + } + + return ( + <> + + + + + {labelToolTip} + + + ); +}; + +interface FormGroupLayoutLabelProps extends Pick< + FormGroupLayoutProps, + "labelToolTip" | "layout" | "labelStyle" | "label" +> { fieldName: FieldPath; fieldId: string; - layout?: "checkbox" | "switch" | "muiInput"; - labelStyle?: CSSProperties; } const FormGroupLayoutLabel = (props: FormGroupLayoutLabelProps) => { - const { label, tooltip, fieldName, layout, fieldId, labelStyle } = props; + const { label, labelToolTip, fieldName, layout, fieldId, labelStyle } = props; const { requiredFields } = useFormContext(); - if (!label && !!tooltip) { + if (!label && !!labelToolTip) { throw new Error("You can't have a tooltip without a label"); } @@ -30,33 +67,10 @@ const FormGroupLayoutLabel = (props: FormGroupLayoutLabel const checkboxLayout = layout === "checkbox"; return ( - <> - - {tooltip && ( - - {tooltip} - - )} - + ); }; diff --git a/src/lib/types/CommonInputProps.ts b/src/lib/types/CommonInputProps.ts index 6e9a807..234f54f 100644 --- a/src/lib/types/CommonInputProps.ts +++ b/src/lib/types/CommonInputProps.ts @@ -15,7 +15,7 @@ interface CommonInputProps { id?: string; helpText?: ReactNode; disabled?: boolean; - labelToolTip?: string; + labelToolTip?: ReactNode; markAllOnFocus?: boolean; inputOnly?: boolean; From de64f49912da86acff44218cdaa547f1d7291ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Masciav=C3=A8?= <61880943+Michele-Masciave@users.noreply.github.com> Date: Fri, 4 Sep 2026 09:36:22 +0200 Subject: [PATCH 07/10] Change import to use type for FormGroupLayoutProps Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/lib/FormGroupLayoutLabel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/FormGroupLayoutLabel.tsx b/src/lib/FormGroupLayoutLabel.tsx index 9f3ac98..1274335 100644 --- a/src/lib/FormGroupLayoutLabel.tsx +++ b/src/lib/FormGroupLayoutLabel.tsx @@ -2,7 +2,7 @@ import { useFormContext } from "./context/FormContext"; import { FieldPath, FieldValues } from "react-hook-form"; import { Label, UncontrolledTooltip } from "@neolution-ch/reactstrap"; import { getRequiredLabel } from "./helpers/form"; -import { FormGroupLayoutProps } from "./FormGroupLayout"; +import type { FormGroupLayoutProps } from "./FormGroupLayout"; type TooltipProps = Pick, "labelToolTip" | "fieldId">; From df280b47255cfe5c910b23425700ca61a1d54e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Masciav=C3=A8?= Date: Fri, 4 Sep 2026 09:40:47 +0200 Subject: [PATCH 08/10] x --- src/lib/FormGroupLayoutLabel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/FormGroupLayoutLabel.tsx b/src/lib/FormGroupLayoutLabel.tsx index 1274335..7468f74 100644 --- a/src/lib/FormGroupLayoutLabel.tsx +++ b/src/lib/FormGroupLayoutLabel.tsx @@ -67,7 +67,7 @@ const FormGroupLayoutLabel = (props: FormGroupLayoutLabel const checkboxLayout = layout === "checkbox"; return ( -