Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,6 @@ export const ClusterDetailsFormFields: React.FC<ClusterDetailsFormFieldsProps> =
nameInputRef.current?.focus();
}, []);

const selectOptions = React.useMemo(
() =>
versions.map((version) => ({
label: version.label,
value: version.value,
})),
[versions],
);

const additionalSelectOption = React.useMemo(() => {
if (
values.customOpenshiftSelect &&
Expand Down Expand Up @@ -151,16 +142,15 @@ export const ClusterDetailsFormFields: React.FC<ClusterDetailsFormFieldsProps> =
<>
<OpenShiftVersionDropdown
name="openshiftVersion"
items={selectOptions}
versions={versions}
showReleasesLink={false}
showOpenshiftVersionModal={() => setOpenshiftVersionModalOpen(true)}
customItem={additionalSelectOption}
customVersion={additionalSelectOption}
/>
{openshiftVersionModalOpen && (
<OpenShiftVersionModal
allVersions={allVersions}
setOpenshiftVersionModalOpen={setOpenshiftVersionModalOpen}
onClose={() => setOpenshiftVersionModalOpen(false)}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,15 @@ const DetailsForm: React.FC<DetailsFormProps> = ({
nameInputRef.current?.focus();
}, []);

const selectOptions = React.useMemo(
() =>
ocpVersions.map((version) => ({
label: version.label,
value: version.value,
})),
[ocpVersions],
);

const additionalSelectOption = React.useMemo(() => {
if (
values.customOpenshiftSelect &&
!selectOptions.some((option) => option.value === values.customOpenshiftSelect)
!ocpVersions.some((option) => option.value === values.customOpenshiftSelect)
) {
return allVersions.find((version) => version.value === values.customOpenshiftSelect);
}
return undefined;
}, [allVersions, selectOptions, values.customOpenshiftSelect]);
}, [allVersions, ocpVersions, values.customOpenshiftSelect]);

return (
<Form>
Expand All @@ -66,16 +57,15 @@ const DetailsForm: React.FC<DetailsFormProps> = ({
/>
<OpenShiftVersionDropdown
name="openshiftVersion"
items={selectOptions}
versions={ocpVersions}
showReleasesLink={false}
showOpenshiftVersionModal={() => setOpenshiftVersionModalOpen(true)}
customItem={additionalSelectOption}
customVersion={additionalSelectOption}
/>
{openshiftVersionModalOpen && (
<OpenShiftVersionModal
allVersions={allVersions}
setOpenshiftVersionModalOpen={setOpenshiftVersionModalOpen}
onClose={() => setOpenshiftVersionModalOpen(false)}
/>
)}

Expand Down
65 changes: 22 additions & 43 deletions libs/ui-lib/lib/common/components/ui/OpenShiftSelectWithSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import { SelectOptionProps } from '@patternfly/react-core';
import { useField } from 'formik';
import { OpenshiftVersionOptionType } from '../../types';
import { HelperTextType } from './OpenShiftVersionDropdown';
import { useTranslation } from '../../hooks';
import { SelectFieldWithSearch } from './formik';
import { ClusterDetailsValues } from '../clusterWizard';
import { getVersionLabel } from './utils';

type OpenshiftSelectWithSearchProps = {
versions: OpenshiftVersionOptionType[];
Expand All @@ -19,62 +19,41 @@ export const OpenShiftSelectWithSearch: React.FunctionComponent<OpenshiftSelectW
const { t } = useTranslation();
const [{ value }] =
useField<ClusterDetailsValues['customOpenshiftSelect']>('customOpenshiftSelect');
const initialSelectOptions = React.useMemo(
() =>
versions.map((version) => ({
children:
version.supportLevel === 'beta'
? version.label + ' - ' + 'Developer preview release'
: version.label,
value: version.value,
})),
[versions],
);

const [filterValue, setFilterValue] = React.useState<string>('');
const [selectOptions, setSelectOptions] =
React.useState<SelectOptionProps[]>(initialSelectOptions);

const helperText = getHelperText && getHelperText(value, true);

React.useEffect(() => {
let newSelectOptions: SelectOptionProps[] = initialSelectOptions;

// Filter menu items based on the text input value when one exists
if (filterValue) {
newSelectOptions = initialSelectOptions.filter((menuItem) =>
String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase()),
const selectOptions = React.useMemo(() => {
const options = versions
.map((version) => ({
children: getVersionLabel(version, t),
value: version.value,
}))
.filter((v) =>
filterValue ? String(v.children).toLowerCase().includes(filterValue.toLowerCase()) : true,
);

// When no options are found after filtering, display 'No results found'
if (!newSelectOptions.length) {
newSelectOptions = [
{
isDisabled: true,
children: t('ai:No results found for {{filter}}', { filter: filterValue }),
value: 'no_results',
},
];
}
if (!options.length) {
return [
{
isDisabled: true,
children: t('ai:No results found for {{filter}}', { filter: filterValue }),
value: 'no_results',
},
];
}

const selectOptionsWithDividers = newSelectOptions.map((option, index) => {
const match = (option.value as string).match(/\d+\.(\d+)\.\d+/);
return options.map((option, index) => {
const match = option.value.match(/\d+\.(\d+)\.\d+/);
const y = match ? match[1] : null;

const previousY =
index > 0
? ((newSelectOptions[index - 1].value as string).match(/\d+\.(\d+)\.\d+/) || [])[1]
: null;
index > 0 ? (options[index - 1].value.match(/\d+\.(\d+)\.\d+/) || [])[1] : null;

return {
...option,
showDivider: previousY !== null && y !== previousY,
};
});

setSelectOptions(selectOptionsWithDividers);
}, [filterValue, initialSelectOptions, t]);
}, [filterValue, versions, t]);

return (
<SelectFieldWithSearch
Expand All @@ -83,7 +62,7 @@ export const OpenShiftSelectWithSearch: React.FunctionComponent<OpenshiftSelectW
setFilterValue={setFilterValue}
name={'customOpenshiftSelect'}
helperText={
helperText ??
getHelperText?.(value, true) ??
t(
'ai:Select an OpenShift version from the list or use the type ahead to narrow down the list.',
)
Expand Down
98 changes: 41 additions & 57 deletions libs/ui-lib/lib/common/components/ui/OpenShiftVersionDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,81 +4,75 @@ import {
FormGroup,
FormHelperText,
HelperTextItem,
Button,
DropdownItem,
MenuToggle,
MenuToggleElement,
Dropdown,
DropdownGroup,
Divider,
DropdownProps,
} from '@patternfly/react-core';

import { OpenshiftVersionOptionType } from '../../types';
import { useTranslation } from '../../hooks/use-translation-wrapper';
import { useField, useFormikContext } from 'formik';
import { useField } from 'formik';
import { getFieldId } from './formik';
import ExternalLink from './ExternalLink';
import { OCP_RELEASES_PAGE } from '../../config';
import { ClusterDetailsValues, ItemDropdown } from '../clusterWizard';
import { getVersionLabel } from './utils';

export type HelperTextType = (value: string | null, inModal?: boolean) => JSX.Element | null;

type OpenShiftVersionDropdownProps = {
name: string;
items: ItemDropdown;
versions: OpenshiftVersionOptionType[];
getHelperText?: HelperTextType;
showReleasesLink: boolean;
showOpenshiftVersionModal: () => void;
customItem?: OpenshiftVersionOptionType;
customVersion?: OpenshiftVersionOptionType;
};

const getParsedVersions = (items: ItemDropdown) => {
const getParsedVersions = (items: OpenshiftVersionOptionType[]) => {
const versionsY = Array.from(new Set(items.map((val) => val.value.match(/^\d+\.(\d+)/)?.[1])));

const parsedVersions = versionsY.map((y) => ({
y: y,
versions: items.filter((val) => val.value.match(/^\d+\.(\d+)/)?.[1] === y),
}));
return { parsedVersions: parsedVersions.reverse() };
return parsedVersions.reverse();
};
export const OpenShiftVersionDropdown = ({
name,
items,
versions,
getHelperText,
showReleasesLink,
showOpenshiftVersionModal,
customItem,
customVersion,
}: OpenShiftVersionDropdownProps) => {
const [field, , { setValue }] = useField<string>(name);
const [isOpen, setOpen] = React.useState(false);
const { t } = useTranslation();
const fieldId = getFieldId(name, 'input');
const {
values: { customOpenshiftSelect },
} = useFormikContext<ClusterDetailsValues>();
const [current, setCurrent] = React.useState<string>();

const current = (customVersion ? [...versions, customVersion] : versions).find(
(item) => item.value === field.value,
);

React.useEffect(() => {
let defaultVersion = versions.find((item) => item.default);
if (customOpenshiftSelect && customItem) {
defaultVersion = customItem;
} else if (customOpenshiftSelect) {
defaultVersion = versions.find((item) => item.value === customOpenshiftSelect);
if (!field.value) {
const defaultVersion = versions.find((item) => item.default) || versions[0];
if (defaultVersion) {
setValue(defaultVersion.value);
}
}
}, [field.value, versions, setValue]);

setCurrent(defaultVersion?.label || '');
setValue(defaultVersion?.value || '');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [customOpenshiftSelect]);

const parsedVersionsForItems = getParsedVersions(items);
const parsedVersions = getParsedVersions(versions);
let lastY: string | undefined = '';
const dropdownItems = parsedVersionsForItems.parsedVersions.map(({ y, versions }) => {
const items = versions.map(({ value, label }) => (
<DropdownItem key={value} id={value} value={value}>
{label}
const dropdownItems = parsedVersions.map(({ y, versions }) => {
const items = versions.map((version) => (
<DropdownItem key={version.value} id={version.value} value={version.value}>
{getVersionLabel(version, t)}
</DropdownItem>
));

Expand All @@ -95,43 +89,24 @@ export const OpenShiftVersionDropdown = ({
{dropdownItems}
</DropdownGroup>
),
customItem && (
customVersion && (
<DropdownGroup label="Custom releases" key="custom-releases">
<DropdownItem key={customItem.value} id={customItem.value} value={customItem.value}>
{customItem.label}
<DropdownItem
key={customVersion.value}
id={customVersion.value}
value={customVersion.value}
>
{getVersionLabel(customVersion, t)}
</DropdownItem>
</DropdownGroup>
),
<DropdownGroup key="all-available-versions">
<DropdownItem key="all-versions" id="all-versions" onSelect={(e) => e.preventDefault()}>
<Button
variant="link"
isInline
onClick={() => {
setOpen(false);
showOpenshiftVersionModal();
}}
id="show-all-versions"
>
{t('ai:Show all available versions')}
</Button>
<DropdownItem key="all-versions" id="all-versions" value="all-versions">
<div className="pf-v6-u-text-color-link">{t('ai:Show all available versions')}</div>
</DropdownItem>
</DropdownGroup>,
].filter(Boolean);

const onSelect = React.useCallback(
(event?: React.MouseEvent<Element, MouseEvent>, val?: string | number) => {
const newLabel = event?.currentTarget.textContent;
const newValue = (val as string) || '';
if (newLabel && event.currentTarget.id !== 'all-versions') {
setCurrent(newLabel);
setValue(newValue);
setOpen(false);
}
},
[setValue],
);

const dropdownToggle = (toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
id={fieldId}
Expand All @@ -143,12 +118,21 @@ export const OpenShiftVersionDropdown = ({
isExpanded={isOpen}
data-testid="openshift-version-dropdown-toggle"
>
{current || t('ai:OpenShift version')}
{current ? getVersionLabel(current, t) : t('ai:OpenShift version')}
</MenuToggle>
);

const helperText = getHelperText && getHelperText(field.value);

const onSelect: DropdownProps['onSelect'] = (_, val) => {
if (val === 'all-versions') {
showOpenshiftVersionModal();
} else if (val) {
setValue(val as string);
}
setOpen(false);
};

return (
<FormGroup
id={`form-control__${fieldId}`}
Expand Down
Loading
Loading