Skip to content

Commit

Permalink
Node resource table and modal
Browse files Browse the repository at this point in the history
  • Loading branch information
dpanshug committed Dec 24, 2024
1 parent 065da14 commit b0426f6
Show file tree
Hide file tree
Showing 11 changed files with 549 additions and 27 deletions.
6 changes: 3 additions & 3 deletions frontend/src/__mocks__/mockHardwareProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export const mockHardwareProfile = ({
{
displayName: 'Memory',
identifier: 'memory',
minCount: 5,
maxCount: 2,
defaultCount: 2,
minCount: '5Gi',
maxCount: '2Gi',
defaultCount: '2Gi',
},
],
description = '',
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/api/k8s/__tests__/hardwareProfiles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ const data: HardwareProfileKind['spec'] = {
{
displayName: 'Memory',
identifier: 'memory',
minCount: 5,
maxCount: 2,
defaultCount: 2,
minCount: '5Gi',
maxCount: '2Gi',
defaultCount: '2Gi',
},
],
description: 'test description',
Expand Down
71 changes: 71 additions & 0 deletions frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import {
FormSection,
Flex,
FlexItem,
Button,
Content,
Stack,
StackItem,
} from '@patternfly/react-core';
import { PlusCircleIcon } from '@patternfly/react-icons';
import { Identifier } from '~/types';
import NodeResourceTable from './nodeResource/NodeResourceTable';
import ManageNodeResourceModal from './nodeResource/ManageNodeResourceModal';

type ManageNodeResourceSectionProps = {
nodeResources: Identifier[];
setNodeResources: (identifiers: Identifier[]) => void;
};

const ManageNodeResourceSection: React.FC<ManageNodeResourceSectionProps> = ({

Check warning on line 21 in frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx#L21

Added line #L21 was not covered by tests
nodeResources,
setNodeResources,
}) => {
const [isNodeResourceModalOpen, setIsNodeResourceModalOpen] = React.useState<boolean>(false);
return (

Check warning on line 26 in frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx#L24-L26

Added lines #L24 - L26 were not covered by tests
<>
<FormSection
title={
<Flex>
<FlexItem>Node resources</FlexItem>
<FlexItem>
<Content component="p" className="odh-form-section__desc">
Every hardware profile must include CPU and memory resources. Additional resources,
such as GPUs, can be added here.
</Content>
</FlexItem>
</Flex>
}
>
<Stack hasGutter>
<StackItem>
<NodeResourceTable
nodeResources={nodeResources}
onUpdate={(newIdentifiers) => setNodeResources(newIdentifiers)}

Check warning on line 45 in frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx#L45

Added line #L45 was not covered by tests
/>
</StackItem>
<StackItem>
<Button
variant="link"
onClick={() => setIsNodeResourceModalOpen(true)}

Check warning on line 51 in frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx#L51

Added line #L51 was not covered by tests
data-testid="add-node-resource-button"
icon={<PlusCircleIcon />}
>
Add resource
</Button>
</StackItem>
</Stack>
</FormSection>
{isNodeResourceModalOpen ? (
<ManageNodeResourceModal
onClose={() => setIsNodeResourceModalOpen(false)}
onSave={(identifier) => setNodeResources([...nodeResources, identifier])}

Check warning on line 63 in frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx#L61-L63

Added lines #L61 - L63 were not covered by tests
nodeResources={nodeResources}
/>
) : null}

Check warning on line 66 in frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/ManageNodeResourceSection.tsx#L66

Added line #L66 was not covered by tests
</>
);
};

export default ManageNodeResourceSection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as React from 'react';
import { FormGroup, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core';
import MemoryField from '~/components/MemoryField';
import CPUField from '~/components/CPUField';
import NumberInputWrapper from '~/components/NumberInputWrapper';

type CountFormFieldProps = {
label: string;
fieldId: string;
size: number | string;
setSize: (value: number | string) => void;
identifier: string;
errorMessage?: string;
isValid?: boolean;
};

const CountFormField: React.FC<CountFormFieldProps> = ({
label,
fieldId,
size,
setSize,
identifier,
errorMessage,
isValid = true,
}) => {
const renderInputField = () => {
switch (identifier) {
case 'cpu':
return <CPUField onChange={(value) => setSize(value)} value={size} />;
case 'memory':
return <MemoryField onChange={(value) => setSize(value)} value={String(size)} />;
default:
return (

Check warning on line 33 in frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx#L18-L33

Added lines #L18 - L33 were not covered by tests
<NumberInputWrapper
min={0}
value={Number(size)}
onChange={(value) => {
if (value) {
setSize(value);

Check warning on line 39 in frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx#L37-L39

Added lines #L37 - L39 were not covered by tests
}
}}
/>
);
}
};

return (

Check warning on line 47 in frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx#L47

Added line #L47 was not covered by tests
<FormGroup label={label} fieldId={fieldId} data-testid={fieldId}>
{renderInputField()}
{!isValid && errorMessage && (
<FormHelperText>

Check warning on line 51 in frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/CountFormField.tsx#L50-L51

Added lines #L50 - L51 were not covered by tests
<HelperText>
<HelperTextItem data-testid={`${fieldId}-error`} variant="error">
{errorMessage}
</HelperTextItem>
</HelperText>
</FormHelperText>
)}
</FormGroup>
);
};

export default CountFormField;
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from 'react';
import { Modal } from '@patternfly/react-core/deprecated';
import DashboardModalFooter from '~/concepts/dashboard/DashboardModalFooter';
import { Identifier } from '~/types';
import useGenericObjectState from '~/utilities/useGenericObjectState';
import { CPU_UNITS, MEMORY_UNITS_FOR_SELECTION, UnitOption } from '~/utilities/valueUnits';
import { EMPTY_IDENTIFIER } from './const';
import NodeResourceForm from './NodeResourceForm';
import { validateDefaultCount, validateMinCount } from './utils';

type ManageNodeResourceModalProps = {
onClose: () => void;
existingIdentifier?: Identifier;
onSave: (identifier: Identifier) => void;
nodeResources: Identifier[];
};

const ManageNodeResourceModal: React.FC<ManageNodeResourceModalProps> = ({
onClose,
existingIdentifier,
onSave,
nodeResources,
}) => {
const [identifier, setIdentifier] = useGenericObjectState<Identifier>(
existingIdentifier || EMPTY_IDENTIFIER,

Check warning on line 25 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L19-L25

Added lines #L19 - L25 were not covered by tests
);

const [unitOptions, setUnitOptions] = React.useState<UnitOption[]>();

Check warning on line 28 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L28

Added line #L28 was not covered by tests

const isUniqueIdentifier =
identifier.identifier === existingIdentifier?.identifier ||
!nodeResources.some((i) => i.identifier === identifier.identifier);

Check warning on line 32 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L31-L32

Added lines #L31 - L32 were not covered by tests

React.useEffect(() => {
switch (identifier.identifier) {
case 'cpu':
setUnitOptions(CPU_UNITS);
break;
case 'memory':
setUnitOptions(MEMORY_UNITS_FOR_SELECTION);
break;
default:
setUnitOptions(undefined);

Check warning on line 43 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L34-L43

Added lines #L34 - L43 were not covered by tests
}
}, [identifier]);

const isValidCounts = unitOptions
? validateDefaultCount(identifier, unitOptions) && validateMinCount(identifier, unitOptions)
: true;

Check warning on line 49 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L47-L49

Added lines #L47 - L49 were not covered by tests

const isButtonDisabled =
!identifier.displayName || !identifier.identifier || !isUniqueIdentifier || !isValidCounts;

Check warning on line 52 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L52

Added line #L52 was not covered by tests

const handleSubmit = () => {
onSave(identifier);
onClose();

Check warning on line 56 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L54-L56

Added lines #L54 - L56 were not covered by tests
};

return (

Check warning on line 59 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L59

Added line #L59 was not covered by tests
<Modal
title={existingIdentifier ? 'Edit node resource' : 'Add node resource'}

Check warning on line 61 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L61

Added line #L61 was not covered by tests
variant="medium"
isOpen
onClose={onClose}
footer={
<DashboardModalFooter
submitLabel={existingIdentifier ? 'Update' : 'Add'}

Check warning on line 67 in frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/ManageNodeResourceModal.tsx#L67

Added line #L67 was not covered by tests
onSubmit={handleSubmit}
onCancel={onClose}
isSubmitDisabled={isButtonDisabled}
/>
}
>
<NodeResourceForm
identifier={identifier}
setIdentifier={setIdentifier}
unitOptions={unitOptions}
isExistingIdentifier={!!existingIdentifier}
isUniqueIdentifier={isUniqueIdentifier}
/>
</Modal>
);
};

export default ManageNodeResourceModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';
import {
TextInput,
FormGroup,
Form,
FormHelperText,
HelperText,
HelperTextItem,
} from '@patternfly/react-core';
import { Identifier } from '~/types';
import { UpdateObjectAtPropAndValue } from '~/pages/projects/types';
import { UnitOption } from '~/utilities/valueUnits';
import { validateDefaultCount, validateMinCount } from './utils';
import CountFormField from './CountFormField';

type NodeResourceFormProps = {
identifier: Identifier;
setIdentifier: UpdateObjectAtPropAndValue<Identifier>;
unitOptions?: UnitOption[];
isExistingIdentifier?: boolean;
isUniqueIdentifier?: boolean;
};

const NodeResourceForm: React.FC<NodeResourceFormProps> = ({
identifier,
setIdentifier,
unitOptions,
isExistingIdentifier,
isUniqueIdentifier,
}) => {
const validated = isUniqueIdentifier ? 'default' : 'error';

Check warning on line 31 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L25-L31

Added lines #L25 - L31 were not covered by tests

return (

Check warning on line 33 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L33

Added line #L33 was not covered by tests
<Form>
<FormGroup isRequired label="Resource label" fieldId="resource-label">
<TextInput
value={identifier.displayName || ''}
onChange={(_, value) => setIdentifier('displayName', value)}

Check warning on line 38 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L37-L38

Added lines #L37 - L38 were not covered by tests
/>
</FormGroup>

<FormGroup isRequired label="Resource identifier" fieldId="resource-identifier">
<TextInput
value={identifier.identifier || ''}
onChange={(_, value) => setIdentifier('identifier', value)}

Check warning on line 45 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L44-L45

Added lines #L44 - L45 were not covered by tests
isDisabled={
isExistingIdentifier &&
(identifier.identifier === 'cpu' || identifier.identifier === 'memory')

Check warning on line 48 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L47-L48

Added lines #L47 - L48 were not covered by tests
}
validated={validated}
/>
{!isUniqueIdentifier && (
<FormHelperText>

Check warning on line 53 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L52-L53

Added lines #L52 - L53 were not covered by tests
<HelperText>
<HelperTextItem data-testid="resource-identifier-error" variant="error">
Another resource with the same identifier already exists. The resource identifier
must be unique.
</HelperTextItem>
</HelperText>
</FormHelperText>
)}
</FormGroup>

<CountFormField
label="Default"
fieldId="default"
identifier={identifier.identifier}
size={identifier.defaultCount}
setSize={(value) => setIdentifier('defaultCount', value)}
isValid={unitOptions ? validateDefaultCount(identifier, unitOptions) : true}

Check warning on line 70 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L69-L70

Added lines #L69 - L70 were not covered by tests
errorMessage="Default must be equal to or between the minimum and maximum allowed limits."
/>

<CountFormField
label="Minimum allowed"
fieldId="minimum-allowed"
identifier={identifier.identifier}
size={identifier.minCount}
setSize={(value) => setIdentifier('minCount', value)}
isValid={unitOptions ? validateMinCount(identifier, unitOptions) : true}

Check warning on line 80 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L79-L80

Added lines #L79 - L80 were not covered by tests
errorMessage="Minimum allowed value cannot exceed the maximum allowed value."
/>

<CountFormField
label="Maximum allowed"
fieldId="maximum-allowed"
identifier={identifier.identifier}
size={identifier.maxCount}
setSize={(value) => setIdentifier('maxCount', value)}

Check warning on line 89 in frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/hardwareProfiles/nodeResource/NodeResourceForm.tsx#L89

Added line #L89 was not covered by tests
/>
</Form>
);
};
export default NodeResourceForm;
Loading

0 comments on commit b0426f6

Please sign in to comment.