diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/OffsetSelect.stories.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/OffsetSelect.stories.tsx deleted file mode 100644 index 8c8b7e34e..000000000 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/OffsetSelect.stories.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Meta, StoryObj } from "@storybook/react"; -import { OffsetSelect } from "./OffsetSelect"; - -export default { - component: OffsetSelect, - args: {}, -} as Meta; - -type Story = StoryObj; - -export const Default: Story = { - args: {}, -}; diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/OffsetSelect.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/OffsetSelect.tsx deleted file mode 100644 index 1f2342bc0..000000000 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/OffsetSelect.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { - MenuToggle, - MenuToggleElement, - Select, - SelectList, - SelectOption, - SelectProps, -} from "@/libs/patternfly/react-core"; -import { useTranslations } from "next-intl"; -import { useState } from "react"; -import { OffsetValue } from "../types"; - -export function OffsetSelect({ - value, - onChange, -}: { - value: OffsetValue; - onChange: (value: OffsetValue) => void; -}) { - const t = useTranslations("ConsumerGroupsTable"); - - const [isOpen, setIsOpen] = useState(false); - - const onToggle = () => { - setIsOpen(!isOpen); - }; - - const offsetValueOption: { [key in OffsetValue]: string } = { - custom: t("offset.custom"), - latest: t("offset.latest"), - earliest: t("offset.earliest"), - specificDateTime: t("offset.specific_date_time"), - }; - - const onSelect: SelectProps["onSelect"] = (_, selection) => { - onChange(selection as OffsetValue); - setIsOpen(false); - }; - - const toggle = (toggleRef: React.Ref) => ( - - {offsetValueOption[value]} - - ); - const makeOptions = () => { - return Object.entries(offsetValueOption).map(([value, label]) => ( - - {label} - - )); - }; - - return ( - - ); -} diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/ResetOffset.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/ResetOffset.tsx index 8b2f60260..e1f2ee0fc 100644 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/ResetOffset.tsx +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/consumer-groups/[groupId]/reset-offset/ResetOffset.tsx @@ -24,8 +24,8 @@ import { partitionSelection, } from "../types"; import { TypeaheadSelect } from "./TypeaheadSelect"; -import { OffsetSelect } from "./OffsetSelect"; import { DryrunSelect } from "./DryrunSelect"; +import { SelectComponent } from "./SelectComponent"; export type Offset = { topicId: string; @@ -73,7 +73,7 @@ export function ResetOffset({ isLoading: boolean; selectDateTimeFormat: DateTimeFormatSelection; handleTopichange: (topicName: string | number) => void; - handlePartitionChange: (partition: number | string) => void; + handlePartitionChange: (partition: number) => void; handleOffsetChange: (value: string) => void; closeResetOffset: () => void; openDryrun: () => void; @@ -163,17 +163,35 @@ export function ResetOffset({ )} {selectTopic === "selectedTopic" && selectPartition === "selectedPartition" && ( - + options={Array.from(new Set(partitions)) + .sort((a, b) => a - b) + .map((partition) => ({ + value: partition, + label: `${partition}`, + }))} value={offset.partition} - selectItems={partitions} - onChange={handlePartitionChange} placeholder={"Select partition"} + onChange={handlePartitionChange} /> )} - + + options={[ + { value: "custom", label: t("offset.custom") }, + { value: "latest", label: t("offset.latest") }, + { value: "earliest", label: t("offset.earliest") }, + { + value: "specificDateTime", + label: t("offset.specific_date_time"), + }, + ]} + value={selectOffset} + onChange={onOffsetSelect} + placeholder="Select an offset" + /> {selectOffset === "custom" && ( void; - placeholder: string; +export type SelectComponentProps = { + options: { value: T; label: string }[]; + value: T; + onChange: (value: T) => void; + placeholder?: string; }; -export function SelectComponent({ +export function SelectComponent({ + options, value, - selectItems, onChange, - placeholder, -}: TypeaheadSelectProps) { - const [isOpen, setIsOpen] = React.useState(false); - const [selected, setSelected] = React.useState(""); - const [inputValue, setInputValue] = React.useState(""); - const [filterValue, setFilterValue] = React.useState(""); - const [selectOptions, setSelectOptions] = React.useState( - [], - ); - const [focusedItemIndex, setFocusedItemIndex] = React.useState( - null, - ); - const [activeItemId, setActiveItemId] = React.useState(null); - const textInputRef = React.useRef(null); - - const NO_RESULTS = "no results"; - - // Update select options based on filter value - React.useEffect(() => { - const initialSelectOptions = selectItems.map((item) => ({ - value: item, - children: String(item), - })); - - let newSelectOptions: SelectOptionProps[] = initialSelectOptions; - - if (filterValue) { - newSelectOptions = initialSelectOptions.filter((option) => - String(option.children) - .toLowerCase() - .includes(filterValue.toLowerCase()), - ); - - if (!newSelectOptions.length) { - newSelectOptions = [ - { - isAriaDisabled: true, - children: `No results found for "${filterValue}"`, - value: NO_RESULTS, - }, - ]; - } - - if (!isOpen) { - setIsOpen(true); - } - } - - setSelectOptions(newSelectOptions); - }, [filterValue, isOpen, selectItems]); - - const createItemId = (value: any) => - `select-typeahead-${value.toString().replace(" ", "-")}`; - - const setActiveAndFocusedItem = (itemIndex: number) => { - setFocusedItemIndex(itemIndex); - const focusedItem = selectOptions[itemIndex]; - setActiveItemId(createItemId(focusedItem.value)); - }; + placeholder = "Select a value", +}: SelectComponentProps) { + const [isOpen, setIsOpen] = useState(false); - const resetActiveAndFocusedItem = () => { - setFocusedItemIndex(null); - setActiveItemId(null); - }; + const onToggle = () => setIsOpen((prev) => !prev); - const closeMenu = () => { + const onSelect: SelectProps["onSelect"] = (_, selection) => { + onChange(selection as T); setIsOpen(false); - resetActiveAndFocusedItem(); - }; - - const onInputClick = () => { - if (!isOpen) { - setIsOpen(true); - } else if (!inputValue) { - closeMenu(); - } - }; - - const selectOption = (value: string | number, content: string) => { - setInputValue(content); - setFilterValue(""); - setSelected(value.toString()); - onChange(value); - closeMenu(); - }; - - const onSelect = ( - _event: React.MouseEvent | undefined, - value: string | number | undefined, - ) => { - if (value && value !== NO_RESULTS) { - const optionText = selectOptions.find( - (option) => option.value === value, - )?.children; - selectOption(value, optionText as string); - } - }; - - const onTextInputChange = ( - _event: React.FormEvent, - value: string, - ) => { - setInputValue(value); - setFilterValue(value); - - resetActiveAndFocusedItem(); - - if (value !== selected) { - setSelected(""); - } - }; - - const handleMenuArrowKeys = (key: string) => { - let indexToFocus = 0; - - if (!isOpen) { - setIsOpen(true); - } - - if (selectOptions.every((option) => option.isDisabled)) { - return; - } - - if (key === "ArrowUp") { - if (focusedItemIndex === null || focusedItemIndex === 0) { - indexToFocus = selectOptions.length - 1; - } else { - indexToFocus = focusedItemIndex - 1; - } - - while (selectOptions[indexToFocus].isDisabled) { - indexToFocus--; - if (indexToFocus === -1) { - indexToFocus = selectOptions.length - 1; - } - } - } - - if (key === "ArrowDown") { - if ( - focusedItemIndex === null || - focusedItemIndex === selectOptions.length - 1 - ) { - indexToFocus = 0; - } else { - indexToFocus = focusedItemIndex + 1; - } - - while (selectOptions[indexToFocus].isDisabled) { - indexToFocus++; - if (indexToFocus === selectOptions.length) { - indexToFocus = 0; - } - } - } - - setActiveAndFocusedItem(indexToFocus); - }; - - const onInputKeyDown = (event: React.KeyboardEvent) => { - const focusedItem = - focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null; - - switch (event.key) { - case "Enter": - if ( - isOpen && - focusedItem && - focusedItem.value !== NO_RESULTS && - !focusedItem.isAriaDisabled - ) { - selectOption(focusedItem.value, focusedItem.children as string); - } - - if (!isOpen) { - setIsOpen(true); - } - - break; - case "ArrowUp": - case "ArrowDown": - event.preventDefault(); - handleMenuArrowKeys(event.key); - break; - } - }; - - const onToggleClick = () => { - setIsOpen(!isOpen); - textInputRef.current?.focus(); - }; - - const onClearButtonClick = () => { - setSelected(""); - setInputValue(""); - setFilterValue(""); - resetActiveAndFocusedItem(); - textInputRef.current?.focus(); }; const toggle = (toggleRef: React.Ref) => ( - - - - - - - - + + {options.find((option) => option.value === value)?.label || placeholder} ); return (