From ef69b885cc6a568e3daca943e22f9f4fa32b3ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cst=C3=BCn=20=C3=96zg=C3=BCr?= Date: Wed, 27 Nov 2024 13:49:45 +0300 Subject: [PATCH] Do not validate options by default in OptionsDropdown Previously Mantine punished the common scenario of duplicate keys too heavily, resulting in component failures in duplicate options data. Now duplicate values are silently ignored as in React. --- .../OptionsDropdown/OptionsDropdown.tsx | 302 ++++++++++-------- 1 file changed, 160 insertions(+), 142 deletions(-) diff --git a/packages/@mantine/core/src/components/Combobox/OptionsDropdown/OptionsDropdown.tsx b/packages/@mantine/core/src/components/Combobox/OptionsDropdown/OptionsDropdown.tsx index 462f3311d3b..ea5df3e247a 100644 --- a/packages/@mantine/core/src/components/Combobox/OptionsDropdown/OptionsDropdown.tsx +++ b/packages/@mantine/core/src/components/Combobox/OptionsDropdown/OptionsDropdown.tsx @@ -1,172 +1,190 @@ -import React from 'react'; -import cx from 'clsx'; -import { CheckIcon } from '../../Checkbox'; -import { ScrollArea, ScrollAreaProps } from '../../ScrollArea/ScrollArea'; -import { Combobox } from '../Combobox'; -import { ComboboxItem, ComboboxLikeRenderOptionInput, ComboboxParsedItem } from '../Combobox.types'; -import { defaultOptionsFilter, FilterOptionsInput } from './default-options-filter'; -import { isEmptyComboboxData } from './is-empty-combobox-data'; -import { isOptionsGroup } from './is-options-group'; -import { validateOptions } from './validate-options'; -import classes from '../Combobox.module.css'; +import React from "react"; +import cx from "clsx"; +import { CheckIcon } from "../../Checkbox"; +import { ScrollArea, ScrollAreaProps } from "../../ScrollArea/ScrollArea"; +import { Combobox } from "../Combobox"; +import { + ComboboxItem, + ComboboxLikeRenderOptionInput, + ComboboxParsedItem, +} from "../Combobox.types"; +import { + defaultOptionsFilter, + FilterOptionsInput, +} from "./default-options-filter"; +import { isEmptyComboboxData } from "./is-empty-combobox-data"; +import { isOptionsGroup } from "./is-options-group"; +import { validateOptions as validateOptionsFn } from "./validate-options"; +import classes from "../Combobox.module.css"; export type OptionsFilter = (input: FilterOptionsInput) => ComboboxParsedItem[]; export interface OptionsGroup { - group: string; - items: ComboboxItem[]; + group: string; + items: ComboboxItem[]; } export type OptionsData = (ComboboxItem | OptionsGroup)[]; interface OptionProps { - data: ComboboxItem | OptionsGroup; - withCheckIcon?: boolean; - value?: string | string[] | null; - checkIconPosition?: 'left' | 'right'; - unstyled: boolean | undefined; - renderOption?: (input: ComboboxLikeRenderOptionInput) => React.ReactNode; + data: ComboboxItem | OptionsGroup; + withCheckIcon?: boolean; + value?: string | string[] | null; + checkIconPosition?: "left" | "right"; + unstyled: boolean | undefined; + renderOption?: (input: ComboboxLikeRenderOptionInput) => React.ReactNode; } -function isValueChecked(value: string | string[] | undefined | null, optionValue: string) { - return Array.isArray(value) ? value.includes(optionValue) : value === optionValue; +function isValueChecked( + value: string | string[] | undefined | null, + optionValue: string, +) { + return Array.isArray(value) + ? value.includes(optionValue) + : value === optionValue; } function Option({ - data, - withCheckIcon, - value, - checkIconPosition, - unstyled, - renderOption, + data, + withCheckIcon, + value, + checkIconPosition, + unstyled, + renderOption, }: OptionProps) { - if (!isOptionsGroup(data)) { - const checked = isValueChecked(value, data.value); - const check = withCheckIcon && checked && ( - - ); + if (!isOptionsGroup(data)) { + const checked = isValueChecked(value, data.value); + const check = withCheckIcon && checked && ( + + ); - const defaultContent = ( - <> - {checkIconPosition === 'left' && check} - {data.label} - {checkIconPosition === 'right' && check} - - ); + const defaultContent = ( + <> + {checkIconPosition === "left" && check} + {data.label} + {checkIconPosition === "right" && check} + + ); - return ( - - {typeof renderOption === 'function' - ? renderOption({ option: data, checked }) - : defaultContent} - - ); - } + return ( + + {typeof renderOption === "function" + ? renderOption({ option: data, checked }) + : defaultContent} + + ); + } - const options = data.items.map((item) => ( -