Skip to content

Commit

Permalink
model to manage field creation (#3100)
Browse files Browse the repository at this point in the history
  • Loading branch information
christianvogt authored Aug 16, 2024
1 parent c94f59d commit cf22edf
Show file tree
Hide file tree
Showing 34 changed files with 727 additions and 537 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('User Management', () => {
userGroupSection.findChipItem('system:authenticated').should('exist');
userGroupSection.clearMultiChipItem();
userGroupSection.findErrorText().should('exist');
userGroupSection.selectMultiGroup('odh-admins', false);
userGroupSection.selectMultiGroup('odh-admins');
userGroupSection.findChipItem(/^odh-admins$/).should('exist');
userGroupSection.findMultiGroupSelectButton().click();
userManagement.findSubmitButton().should('be.enabled');
Expand Down
58 changes: 29 additions & 29 deletions frontend/src/concepts/connectionTypes/fields/BooleanFormField.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import * as React from 'react';
import { Checkbox } from '@patternfly/react-core';
import { BooleanField } from '~/concepts/connectionTypes/types';
import DataFormFieldGroup from '~/concepts/connectionTypes/fields/DataFormFieldGroup';
import { FieldProps } from '~/concepts/connectionTypes/fields/types';

type Props = {
field: BooleanField;
isPreview?: boolean;
value?: boolean;
onChange?: (value: boolean) => void;
const BooleanFormField: React.FC<FieldProps<BooleanField>> = ({
id,
field,
mode,
onChange,
value,
'data-testid': dataTestId,
}) => {
const isPreview = mode === 'preview';
return (
<Checkbox
aria-readonly={isPreview}
id={id}
name={id}
data-testid={dataTestId}
label={field.properties.label}
aria-label={field.properties.label || field.name}
isDisabled={field.properties.defaultReadOnly}
isChecked={
isPreview || field.properties.defaultReadOnly ? !!field.properties.defaultValue : !!value
}
onChange={
isPreview || field.properties.defaultReadOnly || !onChange
? () => undefined
: (_e, v) => onChange(v)
}
/>
);
};

const BooleanFormField: React.FC<Props> = ({ field, isPreview, onChange, value }) => (
<DataFormFieldGroup field={field} isPreview={!!isPreview} renderDefaultValue={false}>
{(id) => (
<Checkbox
aria-readonly={isPreview}
id={id}
name={id}
label={field.properties.label}
aria-label={field.properties.label || field.name}
isDisabled={field.properties.defaultReadOnly}
isChecked={
isPreview || field.properties.defaultReadOnly ? !!field.properties.defaultValue : !!value
}
onChange={
isPreview || field.properties.defaultReadOnly || !onChange
? () => undefined
: (_e, v) => onChange(v)
}
/>
)}
</DataFormFieldGroup>
);

export default BooleanFormField;
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,30 @@ import TextFormField from '~/concepts/connectionTypes/fields/TextFormField';
import ShortTextFormField from '~/concepts/connectionTypes/fields/ShortTextFormField';
import UriFormField from '~/concepts/connectionTypes/fields/UriFormField';
import { ConnectionTypeDataField, ConnectionTypeFieldType } from '~/concepts/connectionTypes/types';

type Props<T extends ConnectionTypeDataField> = {
field: T;
isPreview?: boolean;
onChange?: (field: T, value: unknown) => void;
value?: T['properties']['defaultValue'];
import { FieldProps } from '~/concepts/connectionTypes/fields/types';

const components = {
[ConnectionTypeFieldType.ShortText]: ShortTextFormField,
[ConnectionTypeFieldType.Text]: TextFormField,
[ConnectionTypeFieldType.URI]: UriFormField,
[ConnectionTypeFieldType.Hidden]: HiddenFormField,
[ConnectionTypeFieldType.File]: FileFormField,
[ConnectionTypeFieldType.Boolean]: BooleanFormField,
[ConnectionTypeFieldType.Numeric]: NumericFormField,
[ConnectionTypeFieldType.Dropdown]: DropdownFormField,
};

const ConnectionTypeDataFormField = <T extends ConnectionTypeDataField>({
field,
isPreview,
onChange,
value,
}: Props<T>): React.ReactNode => {
const commonProps = {
isPreview,
onChange: onChange ? (v: unknown) => onChange(field, v) : undefined,
// even though the value is the type of the field default value, typescript cannot determine this here
// or when applied to the element itself within the switch statement
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions,@typescript-eslint/no-explicit-any
value: value as any,
};
switch (field.type) {
case ConnectionTypeFieldType.ShortText:
return <ShortTextFormField {...commonProps} field={field} />;

case ConnectionTypeFieldType.Text:
return <TextFormField {...commonProps} field={field} />;

case ConnectionTypeFieldType.URI:
return <UriFormField {...commonProps} field={field} />;

case ConnectionTypeFieldType.Hidden:
return <HiddenFormField {...commonProps} field={field} />;

case ConnectionTypeFieldType.File:
return <FileFormField {...commonProps} field={field} />;

case ConnectionTypeFieldType.Boolean:
return <BooleanFormField {...commonProps} field={field} />;

case ConnectionTypeFieldType.Numeric:
return <NumericFormField {...commonProps} field={field} />;

case ConnectionTypeFieldType.Dropdown:
return <DropdownFormField {...commonProps} field={field} />;
}
return null;
const ConnectionTypeDataFormField = <T extends ConnectionTypeDataField>(
props: FieldProps<T>,
): React.ReactNode => {
const Component = components[props.field.type];
return (
<Component
// delegate all props to the component
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions,@typescript-eslint/no-explicit-any
{...(props as any)}
/>
);
};

export default ConnectionTypeDataFormField;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FormSection } from '@patternfly/react-core';
import * as React from 'react';
import ConnectionTypeDataFormField from '~/concepts/connectionTypes/fields/ConnectionTypeDataFormField';
import DataFormFieldGroup from '~/concepts/connectionTypes/fields/DataFormFieldGroup';
import SectionFormField from '~/concepts/connectionTypes/fields/SectionFormField';
import {
ConnectionTypeDataField,
Expand Down Expand Up @@ -35,12 +36,16 @@ const ConnectionTypeFormFields: React.FC<Props> = ({ fields, isPreview, onChange

const renderDataFields = (dataFields: ConnectionTypeDataField[]) =>
dataFields.map((field, i) => (
<ConnectionTypeDataFormField
key={i}
field={field}
isPreview={isPreview}
onChange={onChange}
/>
<DataFormFieldGroup key={i} field={field}>
{(id) => (
<ConnectionTypeDataFormField
id={id}
field={field}
mode={isPreview ? 'preview' : 'instance'}
onChange={onChange ? (v) => onChange(field, v) : undefined}
/>
)}
</DataFormFieldGroup>
));

return (
Expand Down
51 changes: 18 additions & 33 deletions frontend/src/concepts/connectionTypes/fields/DataFormFieldGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,26 @@
import * as React from 'react';
import { FormGroup } from '@patternfly/react-core';
import { FormGroup, GenerateId } from '@patternfly/react-core';
import { ConnectionTypeDataField } from '~/concepts/connectionTypes/types';
import FormGroupText from '~/components/FormGroupText';
import UnspecifiedValue from '~/concepts/connectionTypes/fields/UnspecifiedValue';
import { defaultValueToString } from '~/concepts/connectionTypes/utils';

type Props<T extends ConnectionTypeDataField> = {
field: T;
isPreview: boolean;
type Props = {
field: ConnectionTypeDataField;
children: (id: string) => React.ReactNode;
renderDefaultValue?: boolean;
};

const DataFormFieldGroup = <T extends ConnectionTypeDataField>({
field,
isPreview,
children,
renderDefaultValue = true,
}: Props<T>): React.ReactNode => {
const id = `${field.type}-${field.envVar}`;
return (
<FormGroup
label={field.name}
fieldId={id}
data-testid={`field ${field.type} ${field.envVar}`}
// do not mark read only fields as required
isRequired={field.required && !field.properties.defaultReadOnly}
>
{field.properties.defaultReadOnly && renderDefaultValue ? (
<FormGroupText id={id}>
{defaultValueToString(field) ?? (isPreview ? <UnspecifiedValue /> : '-')}
</FormGroupText>
) : (
children(id)
)}
</FormGroup>
);
};
const DataFormFieldGroup: React.FC<Props> = ({ field, children }): React.ReactNode => (
<GenerateId>
{(id) => (
<FormGroup
label={field.name}
fieldId={id}
data-testid={`field ${field.type} ${field.envVar}`}
// do not mark read only fields as required
isRequired={field.required && !field.properties.defaultReadOnly}
>
{children(id)}
</FormGroup>
)}
</GenerateId>
);

export default DataFormFieldGroup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';
import FormGroupText from '~/components/FormGroupText';
import { FieldMode } from '~/concepts/connectionTypes/fields/types';
import UnspecifiedValue from '~/concepts/connectionTypes/fields/UnspecifiedValue';
import { ConnectionTypeDataField } from '~/concepts/connectionTypes/types';
import { defaultValueToString } from '~/concepts/connectionTypes/utils';

type Props = {
id: string;
field: ConnectionTypeDataField;
mode?: FieldMode;
children: React.ReactNode;
};

const DefaultValueTextRenderer: React.FC<Props> = ({ id, field, mode, children }) =>
mode !== 'default' && field.properties.defaultReadOnly ? (
<FormGroupText id={id}>
{defaultValueToString(field) ?? (mode === 'preview' ? <UnspecifiedValue /> : '-')}
</FormGroupText>
) : (
children
);

export default DefaultValueTextRenderer;
Loading

0 comments on commit cf22edf

Please sign in to comment.