Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic selection component #2013

Merged
merged 9 commits into from
Nov 21, 2023
24 changes: 12 additions & 12 deletions assets/js/components/ChecksCatalog/ChecksCatalog.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import React, { useEffect, useState } from 'react';
import { groupBy } from 'lodash';

import { providerData } from '@components/ProviderLabel/ProviderLabel';
import { providers } from '@lib/model';
import PageHeader from '@components/PageHeader';
import Accordion from '@components/Accordion';
import Select, { OPTION_ALL } from '@components/Select';
import ProviderLabel from '@components/ProviderLabel';
import CatalogContainer from './CatalogContainer';
import CheckItem from './CheckItem';
import ProviderSelection from './ProviderSelection';

const ALL_FILTER = 'all';
const ALL_FILTER_TEXT = 'All';
const updatedProvider = {
[ALL_FILTER]: { label: ALL_FILTER_TEXT },
...providerData,
};
const providerOptionRenderer = (provider) => (
<ProviderLabel provider={provider} />
);

function ChecksCatalog({ catalogData, catalogError, loading, updateCatalog }) {
const [selectedProvider, setProviderSelected] = useState(ALL_FILTER);
const [selectedProvider, setProviderSelected] = useState(OPTION_ALL);

useEffect(() => {
updateCatalog(selectedProvider);
Expand All @@ -26,10 +24,12 @@ function ChecksCatalog({ catalogData, catalogError, loading, updateCatalog }) {
<>
<div className="flex">
<PageHeader className="font-bold">Checks catalog</PageHeader>
<ProviderSelection
<Select
optionsName="providers"
className="ml-auto"
providers={Object.keys(updatedProvider)}
selected={selectedProvider}
options={[OPTION_ALL, ...providers]}
renderOption={providerOptionRenderer}
value={selectedProvider}
onChange={setProviderSelected}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/ChecksCatalog/ChecksCatalog.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('ChecksCatalog ChecksCatalog component', () => {
/>
);

await user.click(screen.getByText('All'));
await user.click(screen.getByText('All providers'));

const providerFilter = screen.getByText('AWS');

Expand Down
9 changes: 5 additions & 4 deletions assets/js/components/ChecksCatalog/ChecksCatalogPage.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

import { isValidProvider } from '@lib/model';
import { getCatalog } from '@state/selectors/catalog';
import { updateCatalog } from '@state/actions/catalog';
import { checkProviderExists } from '@components/ProviderLabel/ProviderLabel';
import ChecksCatalog from './ChecksCatalog';

const buildUpdateCatalogAction = (provider) => {
const payload = checkProviderExists(provider)
? { provider, target_type: 'cluster' }
: {};
const payload = {
...(isValidProvider(provider) ? { provider, target_type: 'cluster' } : {}),
};

return updateCatalog(payload);
};

Expand Down
37 changes: 0 additions & 37 deletions assets/js/components/ChecksCatalog/ProviderSelection.stories.jsx

This file was deleted.

23 changes: 14 additions & 9 deletions assets/js/components/ProviderLabel/ProviderLabel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,42 @@ import GcpLogo from '@static/gcp-logo.svg';
import NutanixLogo from '@static/nutanix-logo.svg';
import KvmLogo from '@static/suse-kvm-logo.svg';
import VmwareLogo from '@static/vmware-logo.png';
import {
AWS_PROVIDER,
AZURE_PROVIDER,
GCP_PROVIDER,
KVM_PROVIDER,
NUTANIX_PROVIDER,
VMWARE_PROVIDER,
} from '@lib/model';

export const providerData = {
aws: {
[AWS_PROVIDER]: {
logo: AwsLogo,
label: 'AWS',
},
azure: {
[AZURE_PROVIDER]: {
logo: AzureLogo,
label: 'Azure',
},
gcp: {
[GCP_PROVIDER]: {
logo: GcpLogo,
label: 'GCP',
},
nutanix: {
[NUTANIX_PROVIDER]: {
logo: NutanixLogo,
label: 'Nutanix',
},
kvm: {
[KVM_PROVIDER]: {
logo: KvmLogo,
label: 'KVM',
},
vmware: {
[VMWARE_PROVIDER]: {
logo: VmwareLogo,
label: 'VMware',
},
};

export const checkProviderExists = (provider) =>
providerData[provider] ? provider : null;

function ProviderLabel({ provider }) {
return (
<span>
Expand Down
59 changes: 36 additions & 23 deletions assets/js/components/ProviderLabel/ProviderLabel.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,48 @@
import React from 'react';
import ProviderLabel from '.';

export default {
title: 'Components/ProviderLabel',
components: ProviderLabel,
component: ProviderLabel,
};

export function Azure() {
return <ProviderLabel provider="azure" />;
}
export const Azure = {
args: {
provider: 'azure',
},
};

export function AWS() {
return <ProviderLabel provider="aws" />;
}
export const AWS = {
args: {
provider: 'aws',
},
};

export function GCP() {
return <ProviderLabel provider="gcp" />;
}
export const GCP = {
args: {
provider: 'gcp',
},
};

export function Nutanix() {
return <ProviderLabel provider="nutanix" />;
}
export const Nutanix = {
args: {
provider: 'nutanix',
},
};

export function KVM() {
return <ProviderLabel provider="kvm" />;
}
export const KVM = {
args: {
provider: 'kvm',
},
};

export function VMWare() {
return <ProviderLabel provider="vmware" />;
}
export const VMWare = {
args: {
provider: 'vmware',
},
};

export function Unknown() {
return <ProviderLabel provider="unknown" />;
}
export const Unknown = {
args: {
provider: 'unknown',
},
};
10 changes: 1 addition & 9 deletions assets/js/components/ProviderLabel/ProviderLabel.test.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import ProviderLabel, { checkProviderExists } from './ProviderLabel';
import ProviderLabel from './ProviderLabel';

describe('Provider Label', () => {
it('should display an icon and label with AWS as the provider', () => {
Expand Down Expand Up @@ -49,12 +49,4 @@ describe('Provider Label', () => {
'Provider not recognized'
);
});

it('should check if the provider exists', () => {
['azure', 'aws', 'gcp', 'nutanix', 'kvm', 'vmware'].forEach((provider) => {
expect(checkProviderExists(provider)).toBeTruthy();
});

expect(checkProviderExists('other')).not.toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,37 @@ import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid';

import classNames from 'classnames';

import ProviderLabel from '@components/ProviderLabel';
export const OPTION_ALL = 'all';

import { checkProviderExists } from '@components/ProviderLabel/ProviderLabel';
const defaultOnChange = () => {};
const defaultRenderOption = (item) => item;
const doRenderOption = (option, optionsName, renderOption) => {
if (option === OPTION_ALL) {
return `All ${optionsName}`;
}
return renderOption(option);
};

const displayOption = (provider) =>
checkProviderExists(provider) ? (
<ProviderLabel provider={provider} />
) : (
<span>All</span>
);

function ProviderSelection({ className, providers, selected, onChange }) {
function Select({
optionsName,
options,
value,
renderOption = defaultRenderOption,
onChange = defaultOnChange,
className,
}) {
const dropdownSelector = `${optionsName.replace(
/\s+/g,
''
)}-selection-dropdown`;
return (
<div className={classNames('w-64 pb-4', className)}>
<Listbox value={selected} onChange={onChange}>
<Listbox value={value} onChange={onChange}>
<div className="relative mt-1">
<Listbox.Button className="cloud-provider-selection-dropdown relative w-full py-2 pl-3 pr-10 text-left bg-white rounded-lg shadow-md cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-opacity-75 focus-visible:ring-white focus-visible:ring-offset-orange-300 focus-visible:ring-offset-2 focus-visible:border-indigo-500 sm:text-sm">
{displayOption(selected)}
<Listbox.Button
className={`${dropdownSelector} relative w-full py-2 pl-3 pr-10 text-left bg-white rounded-lg cursor-default border border-gray-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-opacity-75 focus-visible:ring-white focus-visible:ring-offset-orange-300 focus-visible:ring-offset-2 focus-visible:border-indigo-500 sm:text-sm`}
>
{doRenderOption(value, optionsName, renderOption)}
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<ChevronUpDownIcon
className="w-5 h-5 text-gray-400"
Expand All @@ -37,15 +50,15 @@ function ProviderSelection({ className, providers, selected, onChange }) {
leaveTo="opacity-0"
>
<Listbox.Options className="absolute w-full py-1 mt-1 overflow-auto text-base bg-white rounded-md shadow-lg max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm z-[1]">
{providers.map((provider, providerIdx) => (
{options.map((option) => (
<Listbox.Option
key={/* eslint-disable */ providerIdx}
key={option}
className={({ active }) =>
`cursor-default select-none relative py-2 pl-10 pr-4 ${
active ? 'text-green-900 bg-green-100' : 'text-gray-900'
}`
}
value={provider}
value={option}
>
{({ selected: isSelected }) => (
<>
Expand All @@ -54,7 +67,7 @@ function ProviderSelection({ className, providers, selected, onChange }) {
isSelected ? 'font-medium' : 'font-normal'
}`}
>
{displayOption(provider)}
{doRenderOption(option, optionsName, renderOption)}
</span>
{isSelected ? (
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-green-600">
Expand All @@ -73,4 +86,4 @@ function ProviderSelection({ className, providers, selected, onChange }) {
);
}

export default ProviderSelection;
export default Select;
Loading