Skip to content

Commit

Permalink
Environment select creation (#289)
Browse files Browse the repository at this point in the history
* chore: create text area comp

* chore: create tag and environment select

* chore: add createEnvironmentMenu comp

* fix: keep backdrop but make backdrop transparent so drawer component click away listener will register and close drawer

* chore: add createEnvironmentMenu to cluster creation form.

* fix: not placing modal so high as to not cause issues with other components such as drawer and its children. pass override styles for further customization

* refactor: update ClusterEnvironment type and all dependents

* fix: improper name
  • Loading branch information
D-B-Hawk authored Sep 22, 2023
1 parent 2fe8827 commit b07f581
Show file tree
Hide file tree
Showing 30 changed files with 846 additions and 66 deletions.
1 change: 1 addition & 0 deletions components/clusterDetails/clusterDetails.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const Default: StoryObj<typeof ClusterDetails> = {
gitUser: 'D-B-Hawk',
},
type: mockClusterConfig.type as ClusterType,
environment: undefined,
},
},
};
9 changes: 6 additions & 3 deletions components/clusterTable/clusterTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
ClusterType,
Cluster,
WorkloadCluster,
DraftCluster,
} from '../../types/provision';
import { InstallationType } from '../../types/redux';
import Typography from '../../components/typography';
Expand Down Expand Up @@ -139,7 +140,9 @@ const ClusterRow: FunctionComponent<ClusterRowProps> = ({
</StyledTableCell>
<StyledTableCell selected={rowSelected}>
<StyledCellText variant="body2">
{environment && <Tag text={environment} bgColor="light-blue" />}
{environment && (
<Tag text={environment.environmentName} bgColor={environment.labelColor} />
)}
</StyledCellText>
</StyledTableCell>
<StyledTableCell align="left" selected={rowSelected}>
Expand Down Expand Up @@ -267,7 +270,7 @@ const ClusterTableHead: FunctionComponent<ClusterTableHeadProps> = ({ orderBy, o

interface ClusterTableProps extends ComponentPropsWithoutRef<'div'> {
managementCluster: ManagementCluster;
draftCluster?: WorkloadCluster;
draftCluster?: DraftCluster;
onDeleteCluster: () => void;
onMenuOpenClose: (presentedCluster?: Cluster) => void;
presentedClusterId?: string;
Expand Down Expand Up @@ -296,7 +299,7 @@ export const ClusterTable: FunctionComponent<ClusterTableProps> = ({
const filteredWorkloadClusters = useMemo(() => {
const clustersCopy = [...workloadClusters];
if (draftCluster) {
clustersCopy.push(draftCluster);
clustersCopy.push(draftCluster as WorkloadCluster);
}

return clustersCopy
Expand Down
2 changes: 1 addition & 1 deletion components/controlledFields/TextField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useState } from 'react';
import { Control, Controller, FieldValues, UseControllerProps } from 'react-hook-form';

import TextFieldWithRef from '../textField/index';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { useState } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useForm } from 'react-hook-form';

import { CreateEnvironmentMenu } from '../../createEnvironmentMenu';
import Modal from '../../modal';
import useModal from '../../../hooks/useModal';
import { ClusterEnvironment } from '../../../types/provision';

import ControlledEnvironmentSelect from './index';

const meta: Meta<typeof ControlledEnvironmentSelect> = {
component: ControlledEnvironmentSelect,
};

export default meta;

const previouslyCreatedEnvironment: ClusterEnvironment = {
environmentName: 'development',
description: 'Environment for development',
labelColor: 'dark-sky-blue',
};

const ControlledEnvironmentSelectWithHooks = () => {
const { isOpen, openModal, closeModal } = useModal(false);

const [environments, setEnvironments] = useState<ClusterEnvironment[]>([
previouslyCreatedEnvironment,
]);

const {
control,
setValue,
formState: { errors },
} = useForm<{ environment: ClusterEnvironment }>({
mode: 'onBlur',
});

const handleAddEnvironment = (environment: ClusterEnvironment) => {
setEnvironments((curState) => [...curState, environment]);
setValue('environment', environment);
closeModal();
};

return (
<>
<ControlledEnvironmentSelect
control={control}
rules={{
required: 'environment is required',
}}
name="environment"
label="Environment cluster will host"
required
onErrorText={errors.environment?.message}
options={environments}
onAddNewEnvironment={openModal}
/>
<Modal
padding={0}
isOpen={isOpen}
styleOverrides={{ width: '100%', maxWidth: '630px' }}
onCloseModal={closeModal}
>
<CreateEnvironmentMenu
onSubmit={handleAddEnvironment}
onClose={closeModal}
previouslyCreatedEnvironments={environments}
/>
</Modal>
</>
);
};

export const Default: StoryObj<typeof ControlledEnvironmentSelect> = {
render: () => <ControlledEnvironmentSelectWithHooks />,
};
49 changes: 49 additions & 0 deletions components/controlledFields/environmentSelect/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import { Control, Controller, UseControllerProps, FieldValues } from 'react-hook-form';

import { EnvironmentSelectWithRef } from '../../select/';
import { ClusterEnvironment } from '../../../types/provision';

export interface ControlledEnvironmentSelectProps<T extends FieldValues>
extends UseControllerProps<T> {
label: string;
required?: boolean;
control: Control<T>;
options: ClusterEnvironment[];
onAddNewEnvironment: () => void;
helperText?: string;
onErrorText?: string;
}

function ControlledEnvironmentSelect<T extends FieldValues>({
label,
name,
required,
rules,
options,
onAddNewEnvironment,
...rest
}: ControlledEnvironmentSelectProps<T>) {
return (
<Controller
name={name}
rules={rules}
{...rest}
render={({ field, fieldState: { error } }) => (
<EnvironmentSelectWithRef
{...field}
inputRef={field.ref}
fullWidth
label={label}
error={!!error}
required={required}
options={options}
helperText={error ? rest.onErrorText : rest.helperText}
onAddNewEnvironment={onAddNewEnvironment}
/>
)}
/>
);
}

export default ControlledEnvironmentSelect;
44 changes: 44 additions & 0 deletions components/controlledFields/tagSelect/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { Control, Controller, UseControllerProps, FieldValues } from 'react-hook-form';

import { TagSelectWithRef } from '../../select/';

export interface ControlledTagSelectProps<T extends FieldValues> extends UseControllerProps<T> {
label: string;
required?: boolean;
control: Control<T>;
options: readonly string[];
helperText?: string;
onErrorText?: string;
}

function ControlledTagSelect<T extends FieldValues>({
label,
name,
required,
rules,
options,
...rest
}: ControlledTagSelectProps<T>) {
return (
<Controller
name={name}
rules={rules}
{...rest}
render={({ field, fieldState: { error } }) => (
<TagSelectWithRef
{...field}
inputRef={field.ref}
fullWidth
label={label}
error={!!error}
required={required}
options={options}
helperText={error ? rest.onErrorText : rest.helperText}
/>
)}
/>
);
}

export default ControlledTagSelect;
38 changes: 38 additions & 0 deletions components/controlledFields/tagSelect/tagSelect.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useForm } from 'react-hook-form';

import { TAG_COLOR_OPTIONS, TagColor } from '../../tag';

import ControlledTagSelect from './index';

const meta: Meta<typeof ControlledTagSelect> = {
component: ControlledTagSelect,
};

export default meta;

const ControlledTagSelectWithHooks = () => {
const {
control,
formState: { errors },
} = useForm<{ labelColor: TagColor }>({ defaultValues: { labelColor: 'grey' }, mode: 'onBlur' });

return (
<ControlledTagSelect
control={control}
rules={{
required: 'label color is required',
}}
name="labelColor"
label="Label color"
required
onErrorText={errors.labelColor?.message}
options={TAG_COLOR_OPTIONS}
/>
);
};

export const Default: StoryObj<typeof ControlledTagSelect> = {
render: () => <ControlledTagSelectWithHooks />,
};
39 changes: 39 additions & 0 deletions components/controlledFields/textArea/TextArea.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useForm } from 'react-hook-form';

import ControlledTextArea from './index';

const meta: Meta<typeof ControlledTextArea> = {
component: ControlledTextArea,
};

export default meta;

const ControlledTextAreaWithHooks = () => {
const {
control,
formState: { errors },
} = useForm({ defaultValues: { description: 'test' }, mode: 'onBlur' });

return (
<ControlledTextArea
control={control}
rules={{
required: 'Description is required',
maxLength: {
value: 280,
message: 'Max 280 characters are permitted',
},
}}
label="Description"
name="description"
required
onErrorText={errors.description?.message}
/>
);
};

export const Default: StoryObj<typeof ControlledTextArea> = {
render: () => <ControlledTextAreaWithHooks />,
};
34 changes: 34 additions & 0 deletions components/controlledFields/textArea/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Control, Controller, FieldValues, UseControllerProps } from 'react-hook-form';

import TextAreaWithRef from '../../textArea/';

export interface ControlledTextAreaProps<T extends FieldValues> extends UseControllerProps<T> {
label: string;
required?: boolean;
control: Control<T>;
helperText?: string;
onErrorText?: string;
}

function ControlledTextArea<T extends FieldValues>({
label,
...props
}: ControlledTextAreaProps<T>) {
return (
<Controller
{...props}
render={({ field, fieldState: { error } }) => (
<TextAreaWithRef
{...field}
required={props.required}
label={label}
error={!!error}
helperText={error ? props.onErrorText : props.helperText}
/>
)}
/>
);
}

export default ControlledTextArea;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useForm } from 'react-hook-form';

Expand Down
17 changes: 17 additions & 0 deletions components/createEnvironmentMenu/createEnvironmentMenu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Meta, StoryObj } from '@storybook/react';

import { CreateEnvironmentMenu } from '.';

const meta: Meta<typeof CreateEnvironmentMenu> = {
component: CreateEnvironmentMenu,
};

export default meta;

export const Default: StoryObj<typeof CreateEnvironmentMenu> = {
args: {
previouslyCreatedEnvironments: [
{ environmentName: 'development', labelColor: 'dark-sky-blue' },
],
},
};
Loading

0 comments on commit b07f581

Please sign in to comment.