feat(front): add search to the language picker (#22095)
## What Adds a search bar to the **Settings → Experience → Language** picker, and makes languages searchable across languages. Each option is matched against: - its displayed label (the name in the current UI language) - its name **in English** — typing `chinese` finds "Chinois — Simplifié" - its **native name** — typing `中文` finds the same option Matching is also accent-insensitive (`francais` finds "Français"). ## How - The shared `Select` already supports search via `withSearchInput` (used by the currency/country pickers) — the picker just opts in. - Cross-language matching uses the platform `Intl.DisplayNames` API to derive each language's English and native names — no hardcoded translation tables, no extra requests. - A generic optional `searchKeywords` field on `SelectOption` lets the `Select` filter match synonyms on top of the label; the filter now runs through the existing `normalizeSearchText`, hence the accent-insensitivity. Behavior is unchanged for every existing `Select` (strict superset for ASCII labels). ## Test - `nx typecheck` / `nx lint` pass for `twenty-front` and `twenty-ui`. - Open the Language dropdown and try `chinese`, `中文`, or `francais`. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22095?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -23,6 +23,7 @@ import { type IconComponent } from 'twenty-ui/icon';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { normalizeSearchText } from '~/utils/normalizeSearchText';
|
||||
|
||||
export type SelectSizeVariant = 'small' | 'default';
|
||||
|
||||
@@ -124,15 +125,20 @@ export const Select = <Value extends SelectValue>({
|
||||
return null;
|
||||
}, [emptyOption, options, pinnedOption, value]);
|
||||
|
||||
const filteredOptions = useMemo(
|
||||
() =>
|
||||
searchInputValue
|
||||
? options.filter(({ label }) =>
|
||||
label.toLowerCase().includes(searchInputValue.toLowerCase()),
|
||||
)
|
||||
: options,
|
||||
[options, searchInputValue],
|
||||
);
|
||||
const filteredOptions = useMemo(() => {
|
||||
if (!isNonEmptyString(searchInputValue)) {
|
||||
return options;
|
||||
}
|
||||
|
||||
const normalizedSearch = normalizeSearchText(searchInputValue);
|
||||
|
||||
return options.filter(
|
||||
({ label, searchKeywords }) =>
|
||||
normalizeSearchText(label).includes(normalizedSearch) ||
|
||||
(isDefined(searchKeywords) &&
|
||||
normalizeSearchText(searchKeywords).includes(normalizedSearch)),
|
||||
);
|
||||
}, [options, searchInputValue]);
|
||||
|
||||
const isDisabled =
|
||||
disabledFromProps ||
|
||||
|
||||
Reference in New Issue
Block a user