Skip to content

Commit

Permalink
refactor: unify name form fields for Renku 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
leafty committed Dec 13, 2024
1 parent b76f9c3 commit e8a3ff0
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import PermissionsGuard from "../../../permissionsV2/PermissionsGuard";
import type { Project } from "../../../projectsV2/api/projectV2.api";
import { usePatchProjectsByProjectIdMutation } from "../../../projectsV2/api/projectV2.enhanced-api";
import ProjectDescriptionFormField from "../../../projectsV2/fields/ProjectDescriptionFormField";
import ProjectNameFormField from "../../../projectsV2/fields/ProjectNameFormField";
// import ProjectNameFormField from "../../../projectsV2/fields/ProjectNameFormField";
import ProjectNamespaceFormField from "../../../projectsV2/fields/ProjectNamespaceFormField";
import ProjectVisibilityFormField from "../../../projectsV2/fields/ProjectVisibilityFormField";

Expand All @@ -56,6 +56,7 @@ import useProjectPermissions from "../../utils/useProjectPermissions.hook";

import ProjectPageDelete from "./ProjectDelete";
import ProjectPageSettingsMembers from "./ProjectSettingsMembers";
import NameFormField from "../../../entitiesV2/fields/NameFormField";

function notificationProjectUpdated(
notifications: NotificationsManager,
Expand Down Expand Up @@ -194,7 +195,13 @@ function ProjectSettingsEditForm({ project }: ProjectPageSettingsProps) {
)}

<Form noValidate onSubmit={handleSubmit(onSubmit)}>
<ProjectNameFormField name="name" control={control} errors={errors} />
{/* <ProjectNameFormField name="name" control={control} errors={errors} /> */}
<NameFormField
className="mb-3"
control={control}
entityType="project"
name="name"
/>
<PermissionsGuard
disabled={
<ProjectReadOnlyNamespaceField namespace={project.namespace} />
Expand Down
19 changes: 19 additions & 0 deletions client/src/features/entitiesV2/entities.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*!
* Copyright 2024 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export type RenkuEntityType = "group" | "user" | "project" | "data-connector";
26 changes: 26 additions & 0 deletions client/src/features/entitiesV2/entities.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*!
* Copyright 2024 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type { RenkuEntityType } from "./entities.types";

export function displayEntityType(t: RenkuEntityType): string {
if (t === "data-connector") {
return "data connector";
}
return t;
}
73 changes: 73 additions & 0 deletions client/src/features/entitiesV2/fields/GenericInputText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*!
* Copyright 2024 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import cx from "classnames";
import type { FieldValues } from "react-hook-form";
import { Controller } from "react-hook-form";

import { FormText, Input, Label } from "reactstrap";
import type { GenericFormFieldProps } from "./forms.types";

export default function GenericInputText<T extends FieldValues>({
className,
control,
dataCy,
defaultErrorMessage,
defaultValue,
disabled,
name,
helpText,
inputDataCy,
inputId,
label,
rules,
shouldUnregister, // eslint-disable-line spellcheck/spell-checker
}: GenericFormFieldProps<T>) {
const inputHelpId = `${inputId}-help`;

return (
<div className={className} data-cy={dataCy}>
{label && <Label for={inputId}>{label}</Label>}
<Controller
control={control}
defaultValue={defaultValue}
disabled={disabled}
name={name}
render={({ field: { ref, ...rest }, fieldState: { error } }) => (
<>
<Input
aria-describedby={helpText ? inputHelpId : undefined}
className={cx(error && "is-invalid")}
data-cy={inputDataCy}
id={inputId}
type="text"
innerRef={ref}
{...rest}
/>
<div className="invalid-feedback">
{error?.message ?? defaultErrorMessage}
</div>
</>
)}
rules={rules}
shouldUnregister={shouldUnregister} // eslint-disable-line spellcheck/spell-checker
/>
{helpText && <FormText id={inputHelpId}>{helpText}</FormText>}
</div>
);
}
64 changes: 64 additions & 0 deletions client/src/features/entitiesV2/fields/NameFormField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*!
* Copyright 2024 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { useMemo } from "react";
import type { FieldValues } from "react-hook-form";

import { displayEntityType } from "../entities.utils";
import type { EntityFormFieldProps } from "./forms.types";
import GenericInputText from "./GenericInputText";

export default function NameFormField<T extends FieldValues>({
entityType,
defaultErrorMessage: defaultErrorMessage_,
helpText: helpText_,
inputId: inputId_,
inputIdPrefix,
label: label_,
...props
}: EntityFormFieldProps<T>) {
const inputId = useMemo(
() =>
inputId_ ||
(inputIdPrefix
? `${inputIdPrefix}-${entityType}-${props.name}`
: `$${entityType}-${props.name}`),
[entityType, inputIdPrefix, inputId_, props.name]
);
const defaultErrorMessage = useMemo(
() => defaultErrorMessage_ ?? "Please provide a valid name.",
[defaultErrorMessage_]
);
const helpText = useMemo(
() =>
helpText_ ??
`The name you will use to refer to the ${displayEntityType(entityType)}.`,
[entityType, helpText_]
);
const label = useMemo(() => label_ ?? "Name", [label_]);

return (
<GenericInputText
defaultErrorMessage={defaultErrorMessage}
helpText={helpText}
inputId={inputId}
label={label}
{...props}
/>
);
}
46 changes: 46 additions & 0 deletions client/src/features/entitiesV2/fields/forms.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*!
* Copyright 2024 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ReactNode } from "react";
import type { FieldValues, UseControllerProps } from "react-hook-form";
import { RenkuEntityType } from "../entities.types";

/** Most generic type for a form field in the Renku 2.0 UI. */
export interface GenericFormFieldProps<T extends FieldValues>
extends UseControllerProps<T> {
className?: string;
dataCy?: string;
defaultErrorMessage?: ReactNode;
helpText?: ReactNode;
inputDataCy?: string;
inputId: string;
label?: ReactNode;
}

type GenericFormFieldPropsWithOptionalInputId<T extends FieldValues> = Omit<
GenericFormFieldProps<T>,
"inputId"
> &
Partial<Pick<GenericFormFieldProps<T>, "inputId">>;

/** Type for an entity's form field in the Renku 2.0 UI. */
export interface EntityFormFieldProps<T extends FieldValues>
extends GenericFormFieldPropsWithOptionalInputId<T> {
entityType: RenkuEntityType;
inputIdPrefix?: string;
}
11 changes: 8 additions & 3 deletions client/src/features/groupsV2/settings/GroupSettingsMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ import {
usePatchGroupsByGroupSlugMutation,
} from "../../projectsV2/api/projectV2.enhanced-api";
import DescriptionFormField from "../../projectsV2/fields/DescriptionFormField";
import NameFormField from "../../projectsV2/fields/NameFormField";
// import NameFormField from "../../projectsV2/fields/NameFormField";
import SlugFormField from "../../projectsV2/fields/SlugFormField";
import NameFormField from "../../entitiesV2/fields/NameFormField";

type GroupMetadata = Omit<GroupPatchRequest, "repositories">;

Expand Down Expand Up @@ -166,9 +167,13 @@ export default function GroupMetadataForm({ group }: GroupMetadataFormProps) {
<GroupDeleteConfirmation isOpen={isOpen} group={group} toggle={toggle} />
<Form noValidate onSubmit={handleSubmit(onSubmit)}>
<NameFormField
// control={control}
// entityName="group"
// errors={errors}
// name="name"
className="mb-3"
control={control}
entityName="group"
errors={errors}
entityType="group"
name="name"
/>
<SlugFormField
Expand Down
7 changes: 4 additions & 3 deletions client/src/features/projectsV2/new/GroupNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ import LoginAlert from "../../../components/loginAlert/LoginAlert";
import type { GroupPostRequest } from "../api/namespace.api";
import { usePostGroupsMutation } from "../api/projectV2.enhanced-api";
import DescriptionFormField from "../fields/DescriptionFormField";
import NameFormField from "../fields/NameFormField";
// import NameFormField from "../fields/NameFormField";
import SlugFormField from "../fields/SlugFormField";
import WipBadge from "../shared/WipBadge";
import NameFormField from "../../entitiesV2/fields/NameFormField";

function GroupNewHeader() {
return (
Expand Down Expand Up @@ -128,9 +129,9 @@ function GroupMetadataForm() {
<Form noValidate onSubmit={handleSubmit(onSubmit)}>
<NameFormField
control={control}
entityName="group"
errors={errors}
entityType="group"
name="name"
className="mb-3"
/>
<SlugFormField
control={control}
Expand Down
11 changes: 9 additions & 2 deletions client/src/features/projectsV2/new/ProjectV2NewForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import useAppSelector from "../../../utils/customHooks/useAppSelector.hook";
import { slugFromTitle } from "../../../utils/helpers/HelperFunctions";

import ProjectDescriptionFormField from "../fields/ProjectDescriptionFormField";
import ProjectNameFormField from "../fields/ProjectNameFormField";
// import ProjectNameFormField from "../fields/ProjectNameFormField";
import ProjectNamespaceFormField from "../fields/ProjectNamespaceFormField";
import ProjectRepositoryFormField from "../fields/ProjectRepositoryFormField";
import ProjectSlugFormField from "../fields/ProjectSlugFormField";
Expand All @@ -42,6 +42,7 @@ import {
setMetadata,
} from "./projectV2New.slice";
import { PlusLg } from "react-bootstrap-icons";
import NameFormField from "../../entitiesV2/fields/NameFormField";

interface ProjectV2NewFormProps {
currentStep: NewProjectV2State["currentStep"];
Expand Down Expand Up @@ -136,7 +137,13 @@ function ProjectV2NewMetadataStepForm({ currentStep }: ProjectV2NewFormProps) {
<>
<h4 className={cx("d-none", "d-md-block")}>Describe the project</h4>
<Form noValidate onSubmit={handleSubmit(onSubmit)}>
<ProjectNameFormField control={control} errors={errors} name="name" />
{/* <ProjectNameFormField control={control} errors={errors} name="name" /> */}
<NameFormField
className="mb-3"
control={control}
entityType="project"
name="name"
/>
<ProjectSlugFormField control={control} errors={errors} name="slug" />
<ProjectNamespaceFormField
control={control}
Expand Down

0 comments on commit e8a3ff0

Please sign in to comment.