Skip to content

Commit

Permalink
[RHOAIENG-10318] Add connection type field modal
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-phillips-18 committed Aug 13, 2024
1 parent 78d616b commit 07fcc3a
Show file tree
Hide file tree
Showing 6 changed files with 321 additions and 10 deletions.
12 changes: 7 additions & 5 deletions frontend/src/concepts/connectionTypes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ type Field<T extends ConnectionTypeFieldType | string> = {
description?: string;
};

export type ConnectionTypeCommonProperties<V = string> = {
defaultValue?: V;
defaultReadOnly?: boolean;
};

// P default to an empty set of properties
// eslint-disable-next-line @typescript-eslint/ban-types
type DataField<T extends ConnectionTypeFieldType | string, V = string, P = {}> = Field<T> & {
export type DataField<T extends ConnectionTypeFieldType | string, V = string, P = {}> = Field<T> & {
envVar: string;
required?: boolean;
properties: P & {
defaultValue?: V;
defaultReadOnly?: boolean;
};
properties: P & ConnectionTypeCommonProperties<V>;
};

export type SectionField = Field<ConnectionTypeFieldType.Section | 'section'>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import ConnectionTypePreviewDrawer from '~/concepts/connectionTypes/ConnectionTy
import { translateDisplayNameForK8s } from '~/concepts/k8s/utils';
import ApplicationsPage from '~/pages/ApplicationsPage';
import { NameDescType } from '~/pages/projects/types';
import { CreateConnectionTypeFieldsTable } from '~/pages/connectionTypes/connectionTypeFields/CreateConnectionTypeFieldsTable';
import { AddConnectionTypeFieldModal } from '~/pages/connectionTypes/connectionTypeFields/AddConnectionTypeFieldModal';
import { CreateConnectionTypeFooter } from './CreateConnectionTypeFooter';
import { CreateConnectionTypeFieldsTable } from './CreateConnectionTypeFieldsTable';
import { CreateConnectionTypeBreadcrumbs } from './CreateConnectionTypeBreadcrumbs';
import { createConnectionTypeObj } from './CreateConnectionTypeUtils';

Expand All @@ -33,6 +34,7 @@ export const CreateConnectionTypePage: React.FC<CreateConnectionTypePageProps> =
prefillFields,
}) => {
const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false);
const [showAdd, setShowAdd] = React.useState<boolean>(false);

const [connectionNameDesc, setConnectionNameDesc] = React.useState<NameDescType>(
prefillNameDesc || {
Expand All @@ -44,7 +46,12 @@ export const CreateConnectionTypePage: React.FC<CreateConnectionTypePageProps> =
const [connectionEnabled, setConnectionEnabled] = React.useState<boolean>(
prefillEnabled || false,
);
const [connectionFields] = React.useState<ConnectionTypeField[]>(prefillFields || []);
const [connectionFields, setConnectionFields] = React.useState<ConnectionTypeField[]>(
prefillFields || [],
);

const onAddField = (newField: ConnectionTypeField) =>
setConnectionFields((prev) => [...prev, newField]);

return (
<ConnectionTypePreviewDrawer
Expand Down Expand Up @@ -108,9 +115,21 @@ export const CreateConnectionTypePage: React.FC<CreateConnectionTypePageProps> =
values to those fields.
</Text>
<FormGroup>
<CreateConnectionTypeFieldsTable fields={connectionFields} />
<CreateConnectionTypeFieldsTable
fields={connectionFields}
onAddField={() => setShowAdd(true)}
/>
</FormGroup>
</FormSection>
<FormSection className="pf-v5-u-mt-0">
<Button
style={{ width: 'fit-content' }}
variant="secondary"
onClick={() => setShowAdd(true)}
>
Add field
</Button>
</FormSection>
</Form>
</PageSection>
<PageSection stickyOnBreakpoint={{ default: 'bottom' }} variant="light">
Expand All @@ -120,6 +139,15 @@ export const CreateConnectionTypePage: React.FC<CreateConnectionTypePageProps> =
fields={connectionFields}
/>
</PageSection>
{showAdd ? (
<AddConnectionTypeFieldModal
onCancel={() => setShowAdd(false)}
onSubmit={(newField) => {
onAddField(newField);
setShowAdd(false);
}}
/>
) : null}
</ApplicationsPage>
</ConnectionTypePreviewDrawer>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import * as React from 'react';
import {
Checkbox,
Form,
FormGroup,
MenuToggle,
Modal,
Popover,
Select,
SelectList,
SelectOption,
TextArea,
TextInput,
} from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';
import DashboardModalFooter from '~/concepts/dashboard/DashboardModalFooter';
import {
ConnectionTypeCommonProperties,
ConnectionTypeDataField,
ConnectionTypeFieldType,
} from '~/concepts/connectionTypes/types';
import { TextForm } from '~/pages/connectionTypes/connectionTypeFields/TextForm';
import { isEnumMember } from '~/utilities/utils';

const isConnectionTypeFieldType = (
fieldType: string | number | undefined,
): fieldType is ConnectionTypeFieldType =>
isEnumMember(fieldType?.toString(), ConnectionTypeFieldType);

interface AddConnectionTypeFieldModalProps {
onCancel: () => void;
onSubmit: (newField: ConnectionTypeDataField) => void;
// existingFields?: ConnectionTypeField[];
}

const fieldTypeLabels: { [key: string]: string } = {
[ConnectionTypeFieldType.Boolean]: 'Boolean',
[ConnectionTypeFieldType.Dropdown]: 'Short text',
[ConnectionTypeFieldType.File]: 'File',
[ConnectionTypeFieldType.Hidden]: 'Hidden',
[ConnectionTypeFieldType.Numeric]: 'Numeric',
[ConnectionTypeFieldType.ShortText]: 'Short text',
[ConnectionTypeFieldType.Text]: 'Text',
[ConnectionTypeFieldType.URI]: 'URI',
};

export const AddConnectionTypeFieldModal: React.FC<AddConnectionTypeFieldModalProps> = ({
onCancel,
onSubmit,
}) => {
const [name, setName] = React.useState<string>('');
const [description, setDescription] = React.useState<string>();
const [envVar, setEnvVar] = React.useState<string>('');
const [fieldType, setFieldType] = React.useState<ConnectionTypeFieldType>(
ConnectionTypeFieldType.ShortText,
);
const [required, setRequired] = React.useState<boolean>();
const [isOpen, setIsOpen] = React.useState<boolean>(false);
const valid = React.useMemo(() => !!name && !!envVar && !!fieldType, [envVar, fieldType, name]);
const [textProperties, setTextProperties] = React.useState<ConnectionTypeCommonProperties>({});

const onAdd = () => {
switch (fieldType) {
case ConnectionTypeFieldType.Hidden:
case ConnectionTypeFieldType.File:
case ConnectionTypeFieldType.ShortText:
case ConnectionTypeFieldType.Text:
case ConnectionTypeFieldType.URI:
onSubmit({
name,
description,
envVar,
type: fieldType,
properties: {
defaultValue: textProperties.defaultValue,
defaultReadOnly: textProperties.defaultValue
? textProperties.defaultReadOnly
: undefined,
},
required,
});
}
};

const fieldTypeForm = React.useMemo(() => {
switch (fieldType) {
case ConnectionTypeFieldType.Hidden:
case ConnectionTypeFieldType.File:
case ConnectionTypeFieldType.ShortText:
case ConnectionTypeFieldType.Text:
case ConnectionTypeFieldType.URI:
return (
<TextForm
fieldType={fieldType}
properties={textProperties}
onUpdateDefaultValue={(value) =>
setTextProperties((prev) => ({
defaultValue: value,
defaultReadOnly: prev.defaultReadOnly,
}))
}
onUpdateDefaultReadOnly={(value) =>
setTextProperties((prev) => ({
defaultValue: prev.defaultValue,
defaultReadOnly: value,
}))
}
/>
);
}
return null;
}, [fieldType, textProperties]);

return (
<Modal
isOpen
variant="medium"
title="Add field"
onClose={onCancel}
footer={
<DashboardModalFooter
onCancel={onCancel}
onSubmit={onAdd}
submitLabel="Add"
isSubmitDisabled={!valid}
alertTitle="Error"
/>
}
data-testid="archive-model-version-modal"
>
<Form>
<FormGroup fieldId="name" label="Field name" isRequired>
<TextInput id="name" value={name} onChange={(_ev, value) => setName(value)} />
</FormGroup>
<FormGroup
fieldId="description"
label="Field description"
labelIcon={
<Popover
aria-label="field description help"
headerContent="Field description"
bodyContent="Use the field description to provide users in your organization with additional information about a field, or instructions for completing the field. Your input will appear in a popover, like this one."
>
<HelpIcon style={{ color: 'var(--pf-v5-global--icon--Color--light)' }} />
</Popover>
}
>
<TextArea
id="description"
value={description}
onChange={(_ev, value) => setDescription(value)}
/>
</FormGroup>
<FormGroup
fieldId="envVar"
label="Environment variable"
labelIcon={
<Popover
aria-label="environment variable help"
headerContent="Environment variable"
bodyContent="Environment variables grant you access to the value provided when attaching the connection to your workbench."
>
<HelpIcon style={{ color: 'var(--pf-v5-global--icon--Color--light)' }} />
</Popover>
}
isRequired
>
<TextInput id="envVar" value={envVar} onChange={(_ev, value) => setEnvVar(value)} />
</FormGroup>
<FormGroup fieldId="fieldType" label="Field type" isRequired>
<Select
id="fieldType"
isOpen={isOpen}
shouldFocusToggleOnSelect
selected={fieldType}
onSelect={(_e, selection) => {
if (isConnectionTypeFieldType(selection)) {
setFieldType(selection);
setIsOpen(false);
}
}}
onOpenChange={(open) => setIsOpen(open)}
toggle={(toggleRef) => (
<MenuToggle
ref={toggleRef}
id="type-select"
isFullWidth
onClick={() => {
setIsOpen((open) => !open);
}}
isExpanded={isOpen}
>
{fieldTypeLabels[fieldType]}
</MenuToggle>
)}
>
<SelectList>
<SelectOption value={ConnectionTypeFieldType.ShortText}>
{fieldTypeLabels[ConnectionTypeFieldType.ShortText]}
</SelectOption>
<SelectOption value={ConnectionTypeFieldType.Hidden}>
{fieldTypeLabels[ConnectionTypeFieldType.Hidden]}
</SelectOption>
</SelectList>
</Select>
</FormGroup>
{fieldTypeForm}
<FormGroup fieldId="isRequired">
<Checkbox
id="isRequired"
label="Field is required"
isChecked={required || false}
onChange={(_ev, checked) => {
setRequired(checked);
}}
/>
</FormGroup>
</Form>
</Modal>
);
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from 'react';
import {
Bullseye,
Button,
EmptyState,
EmptyStateActions,
EmptyStateBody,
EmptyStateFooter,
EmptyStateHeader,
EmptyStateIcon,
EmptyStateVariant,
Expand All @@ -12,24 +15,34 @@ import { Table, Thead, Tbody, Tr, Th } from '@patternfly/react-table';
import { ConnectionTypeField } from '~/concepts/connectionTypes/types';
import { CreateConnectionTypeFieldsTableRow } from './CreateConnectionTypeFieldsTableRow';

const EmptyFieldsTable: React.FC = () => (
const EmptyFieldsTable: React.FC<{ onAddField: () => void }> = ({ onAddField }) => (
<Bullseye>
<EmptyState variant={EmptyStateVariant.sm}>
<EmptyStateHeader icon={<EmptyStateIcon icon={PlusCircleIcon} />} titleText="No fields" />
<EmptyStateBody>
Add fields to prompt users to input information, and optionally assign default values to
those fields. Connection name and description fields are included by default.
</EmptyStateBody>
<EmptyStateFooter>
<EmptyStateActions>
<Button variant="secondary">Add section heading</Button>
<Button variant="secondary" onClick={onAddField}>
Add field
</Button>
</EmptyStateActions>
</EmptyStateFooter>
</EmptyState>
</Bullseye>
);

type CreateConnectionTypeFieldsTableProps = {
fields: ConnectionTypeField[];
onAddField: () => void;
};

export const CreateConnectionTypeFieldsTable: React.FC<CreateConnectionTypeFieldsTableProps> = ({
fields,
onAddField,
}) => {
const columns = [
'Section heading/field name',
Expand Down Expand Up @@ -57,7 +70,7 @@ export const CreateConnectionTypeFieldsTable: React.FC<CreateConnectionTypeField
</Tbody>
</>
) : (
<EmptyFieldsTable />
<EmptyFieldsTable onAddField={onAddField} />
)}
</Table>
);
Expand Down
Loading

0 comments on commit 07fcc3a

Please sign in to comment.