Skip to content

feat: allow adding keyword to data connectors #3719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

lorenzo-cavazzi
Copy link
Member

@lorenzo-cavazzi lorenzo-cavazzi commented Jun 5, 2025

This PR implements the following:

  • New design for keywords, based on a variation of Bootstrap badges.
  • Update the keywords section in the project settings.
  • Add/change keywords when creating/editing data connectors.

Keywords -- project
Screenshot_20250618_115321

Keywords -- project settings
Screenshot_20250618_115428

Keywords -- data connectors edit/creation
Screenshot_20250618_115428

/deploy renku=release-2.2.0

@RenkuBot
Copy link
Contributor

RenkuBot commented Jun 5, 2025

You can access the deployment of this PR at https://renku-ci-ui-3719.dev.renku.ch

@lorenzo-cavazzi lorenzo-cavazzi force-pushed the lorenzo/keywords-data-connectors branch from 90591ca to f7baba3 Compare June 17, 2025 08:24
Copy link
Member

@leafty leafty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partial review, will go look at the keywords input soon

Comment on lines 19 to 21
import cx from "classnames";
import RenkuBadge from "../renkuBadge/RenkuBadge";
import { XCircle } from "react-bootstrap-icons";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: import order (use "organize imports" in vscode)

className?: string;
"data-cy"?: string;
highlighted?: boolean;
removable?: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have this prop? Just keep the removeHandler prop.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case we have places where we switch between view/edit mode. In fact that's not a pattern we use anymore, I'll remove it 👍

"data-cy"?: string;
highlighted?: boolean;
removable?: boolean;
removeHandler?: () => void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I like the way bootstrap has named the event handlers:

Suggested change
removeHandler?: () => void;
remove?: () => void;

This is similar to the toggle function passed for <ModalHeader> and other bootstrap components.

}: KeywordBadgeProps) {
const remove =
removable && removeHandler ? (
<XCircle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"d-flex",
"fw-semibold",
"gap-1",
highlighted ? "bg-success-subtle" : "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
highlighted ? "bg-success-subtle" : "",
highlighted && "bg-success-subtle",

const baseClasses = [
"border",
"badge",
pills ? "rounded-pill" : "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pills ? "rounded-pill" : "",
pills && "rounded-pill",

...colorClasses,
];

const finalClasses = className ? cx(className, baseClasses) : cx(baseClasses);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const finalClasses = className ? cx(className, baseClasses) : cx(baseClasses);
const finalClasses = cx(className, baseClasses);

cx will do this for you.

#{keyword}
</p>
))}
<KeywordContainer className="mt-1">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be rendered without keywords, the mt-1 changes the bottom margin.

Comment on lines 1 to 13
import cx from "classnames";
import { Controller } from "react-hook-form";
import { Button, FormText, Label } from "reactstrap";
import KeywordContainer from "~/components/keywords/KeywordContainer";
import KeywordBadge from "~/components/keywords/KeywordBadge";
import { PlusLg } from "react-bootstrap-icons";
import type {
FieldErrors,
UseFormGetValues,
UseFormSetValue,
Control,
} from "react-hook-form";
import type { ProjectV2MetadataWithKeyword } from "../../settings/projectSettings.types";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import order

Comment on lines 217 to 220
const editedData = {
...data,
};
delete editedData.keyword;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const editedData = {
...data,
};
delete editedData.keyword;
const { keyword, ...editedData } = data;

Copy link
Member

@leafty leafty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review on the new keywords input:


interface ProjectKeywordsFormFieldProps {
control: Control<Required<ProjectV2MetadataWithKeyword>>;
errors: FieldErrors<Required<ProjectV2MetadataWithKeyword>>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to pass errors.

Suggested change
errors: FieldErrors<Required<ProjectV2MetadataWithKeyword>>;

control: Control<Required<ProjectV2MetadataWithKeyword>>;
errors: FieldErrors<Required<ProjectV2MetadataWithKeyword>>;
getValues: UseFormGetValues<Required<ProjectV2MetadataWithKeyword>>;
oldKeywords?: string[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think oldKeywords is not needed, see below.

<Controller
control={control}
name="keyword"
render={({ field }) => (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
render={({ field }) => (
render={({ field, fieldState }) => (

placeholder="Add new keyword"
type="string"
{...field}
className={cx("form-control", errors.keyword && "is-invalid")}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error state is found in fieldState:

Suggested change
className={cx("form-control", errors.keyword && "is-invalid")}
className={cx("form-control", fieldState.error && "is-invalid")}

Also, it doesn't seem that this input is validated, so maybe we can remove error styling here.

Comment on lines 48 to 50
onChange={(e) => {
field.onChange(e);
}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already set correctly from {...field}.

Suggested change
onChange={(e) => {
field.onChange(e);
}}

Comment on lines 70 to 76
const newValue = field.value.trim();
const currentKeywords = getValues("keywords");
if (!currentKeywords.includes(newValue)) {
const newKeywords = [...currentKeywords, newValue];
setValue("keywords", newKeywords);
}
setValue("keyword", "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid repeating this block: you can make a method you can call for both onClick here and onKeyDown above.

<>
{field.value && field.value.length > 0 && (
<KeywordContainer data-cy="project-settings-keywords">
{getValues("keywords")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not continue using field.value here?

Suggested change
{getValues("keywords")
{field.value

<KeywordBadge
data-cy="project-settings-keyword"
key={index}
highlighted={!oldKeywords?.includes(keyword)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be equivalent to use formState (from the render function) here:

Suggested change
highlighted={!oldKeywords?.includes(keyword)}
highlighted={!(formState.defaultValues?.keywords ?? []).includes(keyword)}

key={index}
highlighted={!oldKeywords?.includes(keyword)}
removeHandler={() => {
const newKeywords = getValues("keywords").filter(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const newKeywords = getValues("keywords").filter(
const newKeywords = field.value.filter(

Co-authored-by: Flora Thiebaut <flora.thiebaut@sdsc.ethz.ch>
@lorenzo-cavazzi
Copy link
Member Author

@leafty I addressed all the points, thank you for the review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants