Skip to content

Commit

Permalink
creates generic ItemSelectorField
Browse files Browse the repository at this point in the history
  • Loading branch information
invincibleJai committed Mar 20, 2020
1 parent 4cb764f commit 57b35e7
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 169 deletions.
Expand Up @@ -12,5 +12,6 @@ export { default as ResourceLimitField } from './ResourceLimitField';
export { default as SwitchField } from './SwitchField';
export { default as TextAreaField } from './TextAreaField';
export { default as YAMLEditorField } from './YAMLEditorField';
export { default as ItemSelectorField } from './item-selector-field/ItemSelectorField';
export * from './field-utils';
export * from './field-types';
@@ -1,4 +1,4 @@
.odc-event-source-selector {
.odc-item-selector-field {
display: flex;
flex-direction: column;
flex-flow: wrap;
Expand Down
@@ -0,0 +1,127 @@
import * as React from 'react';
import * as _ from 'lodash';
import { useField, useFormikContext, FormikValues } from 'formik';
import { LoadingInline } from '@console/internal/components/utils';
import { FormGroup, Alert } from '@patternfly/react-core';
import { StarIcon } from '@patternfly/react-icons';
import { getFieldId } from '@console/shared';
import SelectorCard from './SelectorCard';
import './ItemSelectorField.scss';

interface Item {
name: string;
title: string;
displayName: string;
iconUrl?: string;
[x: string]: any;
}

interface NormalizedItem {
[item: string]: Item;
}

interface ItemSelectorFieldProps {
itemList: NormalizedItem;
name: string;
label?: string;
loadingItems?: boolean;
isRecommending?: boolean;
recommended?: string;
couldNotRecommend?: boolean;
onSelect?: (name: string) => void;
}

const ItemSelectorField: React.FC<ItemSelectorFieldProps> = ({
itemList,
name,
label,
loadingItems,
isRecommending,
recommended,
couldNotRecommend,
onSelect,
}) => {
const [selected, { error: selectedError, touched: selectedTouched }] = useField(name);
const { setFieldValue, setFieldTouched, validateForm } = useFormikContext<FormikValues>();
const itemCount = _.keys(itemList).length;

const handleItemChange = React.useCallback(
(image: string) => {
setFieldValue(name, image);
setFieldTouched(name, true);
validateForm();
},
[name, setFieldValue, setFieldTouched, validateForm],
);

React.useEffect(() => {
if (!selected.value && itemCount === 1) {
const image = _.find(itemList);
handleItemChange(image.name);
}
if (!selected.value && recommended) {
handleItemChange(recommended);
}
}, [itemCount, itemList, handleItemChange, selected.value, recommended]);

if (itemCount === 1) {
return null;
}

const fieldId = getFieldId(name, 'selector');
const isValid = !(selectedTouched && selectedError);
const errorMessage = !isValid ? selectedError : '';

return (
<FormGroup
fieldId={fieldId}
label={label}
helperTextInvalid={errorMessage}
isValid={isValid}
isRequired
>
{isRecommending && (
<>
<LoadingInline /> Detecting recommended {label}...
</>
)}
{recommended && (
<>
<Alert variant="success" title="Builder image(s) detected." isInline>
Recommended {label} are represented by{' '}
<StarIcon style={{ color: 'var(--pf-global--primary-color--100)' }} /> icon.
</Alert>
<br />
</>
)}
{couldNotRecommend && (
<>
<Alert variant="warning" title="Unable to detect the builder image." isInline>
Select the most appropriate one from the list to continue.
</Alert>
<br />
</>
)}
{loadingItems ? (
<LoadingInline />
) : (
<div id="item-selector-field" className="odc-item-selector-field">
{_.values(itemList).map((item) => (
<SelectorCard
key={item.name}
title={item.title}
iconUrl={item.iconUrl}
name={item.name}
displayName={item.displayName}
selected={selected.value === item.name}
recommended={recommended === item.name}
onChange={onSelect || handleItemChange}
/>
))}
</div>
)}
</FormGroup>
);
};

export default ItemSelectorField;
Expand Up @@ -2,7 +2,7 @@
position: relative;
width: 136px;
min-height: 118px;
margin: 4px;
margin: 4px !important;
align-items: center;
border: 0;

Expand Down
Expand Up @@ -2,19 +2,23 @@ import * as React from 'react';
import * as classNames from 'classnames';
import { Card, CardHeader, CardBody } from '@patternfly/react-core';
import { StarIcon } from '@patternfly/react-icons';
import { BuilderImage } from '../../../utils/imagestream-utils';

import './SelectorCard.scss';

export interface SelectorCardProps {
image: BuilderImage;
interface SelectorCardProps {
title: string;
iconUrl: string;
name: string;
displayName: string;
selected: boolean;
recommended?: boolean;
onChange: (name: string) => void;
}

const SelectorCard: React.FC<SelectorCardProps> = ({
image,
title,
iconUrl,
name,
displayName,
selected,
recommended = false,
onChange,
Expand All @@ -24,15 +28,15 @@ const SelectorCard: React.FC<SelectorCardProps> = ({
<Card
component="button"
type="button"
aria-label={image.title}
aria-label={title}
className={classes}
onClick={() => onChange(image.name)}
onClick={() => onChange(name)}
>
<CardHeader>
<img className="odc-selector-card__icon" src={image.iconUrl} alt={image.displayName} />
<img className="odc-selector-card__icon" src={iconUrl} alt={displayName} />
</CardHeader>
<CardBody>
<span className="odc-selector-card__title">{image.title}</span>
<span className="odc-selector-card__title">{title}</span>
</CardBody>
{recommended && (
<span className="odc-selector-card__recommended">
Expand Down

This file was deleted.

@@ -1,13 +1,8 @@
import * as React from 'react';
import * as _ from 'lodash';
import { useField, useFormikContext, FormikValues } from 'formik';
import { LoadingInline } from '@console/internal/components/utils';
import { FormGroup, Alert } from '@patternfly/react-core';
import { StarIcon } from '@patternfly/react-icons';
import { getFieldId } from '@console/shared';
import { useFormikContext, FormikValues } from 'formik';
import { NormalizedBuilderImages } from '../../../utils/imagestream-utils';
import SelectorCard from './SelectorCard';
import './BuilderImageSelector.scss';
import { ItemSelectorField } from '@console/shared';

export interface BuilderImageSelectorProps {
loadingImageStream: boolean;
Expand All @@ -18,85 +13,29 @@ const BuilderImageSelector: React.FC<BuilderImageSelectorProps> = ({
loadingImageStream,
builderImages,
}) => {
const [selected, { error: selectedError, touched: selectedTouched }] = useField('image.selected');
const { values, setFieldValue, setFieldTouched, validateForm } = useFormikContext<FormikValues>();
const builderImageCount = _.keys(builderImages).length;

const handleImageChange = React.useCallback(
(image: string) => {
setFieldValue('image.selected', image);
setFieldValue('image.tag', _.get(builderImages, `${image}.recentTag.name`, ''));
setFieldTouched('image.selected', true);
setFieldTouched('image.tag', true);
validateForm();
},
[setFieldValue, setFieldTouched, validateForm, builderImages],
);
const { values, setFieldValue, setFieldTouched } = useFormikContext<FormikValues>();
const { isRecommending, recommended, couldNotRecommend } = values?.image;

React.useEffect(() => {
if (!selected.value && builderImageCount === 1) {
const image = _.find(builderImages);
handleImageChange(image.name);
}
if (!selected.value && values.image.recommended) {
handleImageChange(values.image.recommended);
if (values.image.selected) {
setFieldValue(
'image.tag',
_.get(builderImages, `${values.image.selected}.recentTag.name`, ''),
);
setFieldTouched('image.tag', true);
}
}, [
builderImageCount,
builderImages,
handleImageChange,
selected.value,
values.image.recommended,
]);

if (builderImageCount === 1) {
return null;
}
}, [values.image.selected, setFieldValue, setFieldTouched, builderImages]);

const fieldId = getFieldId('image.name', 'selector');
const isValid = !(selectedTouched && selectedError);
const errorMessage = !isValid ? selectedError : '';
return (
<FormGroup
fieldId={fieldId}
label="Builder Image"
helperTextInvalid={errorMessage}
isValid={isValid}
isRequired
>
{values.image.isRecommending && (
<>
<LoadingInline /> Detecting recommended builder images...
</>
)}
{values.image.recommended && (
<Alert variant="success" title="Builder image(s) detected." isInline>
Recommended builder images are represented by{' '}
<StarIcon style={{ color: 'var(--pf-global--primary-color--100)' }} /> icon.
</Alert>
)}
{values.image.couldNotRecommend && (
<Alert variant="warning" title="Unable to detect the builder image." isInline>
Select the most appropriate one from the list to continue.
</Alert>
)}
<br />
{loadingImageStream ? (
<LoadingInline />
) : (
<div id="builder-image-selector-field" className="odc-builder-image-selector">
{_.values(builderImages).map((image) => (
<SelectorCard
key={image.name}
image={image}
selected={selected.value === image.name}
recommended={values.image.recommended === image.name}
onChange={handleImageChange}
/>
))}
</div>
)}
</FormGroup>
<ItemSelectorField
itemList={builderImages}
name="image.selected"
label="builder images"
loadingItems={loadingImageStream}
isRecommending={isRecommending}
recommended={recommended}
couldNotRecommend={couldNotRecommend}
/>
);
};

Expand Down
@@ -1,39 +1,16 @@
import * as React from 'react';
import * as _ from 'lodash';
import { useFormikContext, FormikValues } from 'formik';
import SelectorCard from '@console/dev-console/src/components/import/builder/SelectorCard';
import { ItemSelectorField } from '@console/shared';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import './EventSourcesSelector.scss';
import { NormalizedEventSources } from '../import-types';

interface EventSourcesSelectorProps {
eventSourceList: any;
eventSourceList: NormalizedEventSources;
}

const EventSourcesSelector: React.FC<EventSourcesSelectorProps> = ({ eventSourceList }) => {
const { values, setFieldValue, setFieldTouched, validateForm } = useFormikContext<FormikValues>();

const handleTypeChange = React.useCallback(
(value: string) => {
setFieldValue('type', value);
setFieldTouched('type', true);
validateForm();
},
[setFieldValue, setFieldTouched, validateForm],
);
return (
<FormSection title="Type" fullWidth>
<div id="event-source-selector-field" className="odc-event-source-selector">
{_.values(eventSourceList).map((type) => (
<SelectorCard
key={type.name}
image={type}
selected={values.type === type.name}
onChange={handleTypeChange}
/>
))}
</div>
</FormSection>
);
};
const EventSourcesSelector: React.FC<EventSourcesSelectorProps> = ({ eventSourceList }) => (
<FormSection title="Type" fullWidth>
<ItemSelectorField itemList={eventSourceList} name="type" />
</FormSection>
);

export default EventSourcesSelector;
Expand Up @@ -35,3 +35,15 @@ export interface EventSourceFormData {
sink: KnativeServiceName;
data?: EventSourceData;
}

export interface EventSourceList {
title: string;
iconUrl: string;
name: string;
displayName: string;
[x: string]: any;
}

export interface NormalizedEventSources {
[eventSourceName: string]: EventSourceList;
}

0 comments on commit 57b35e7

Please sign in to comment.