-
Notifications
You must be signed in to change notification settings - Fork 6
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
base: main
Are you sure you want to change the base?
Conversation
You can access the deployment of this PR at https://renku-ci-ui-3719.dev.renku.ch |
90591ca
to
f7baba3
Compare
There was a problem hiding this 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
import cx from "classnames"; | ||
import RenkuBadge from "../renkuBadge/RenkuBadge"; | ||
import { XCircle } from "react-bootstrap-icons"; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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:
removeHandler?: () => void; | |
remove?: () => void; |
This is similar to the toggle
function passed for <ModalHeader>
and other bootstrap components.
}: KeywordBadgeProps) { | ||
const remove = | ||
removable && removeHandler ? ( | ||
<XCircle |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a real button here, otherwise this action is not accessible.
"d-flex", | ||
"fw-semibold", | ||
"gap-1", | ||
highlighted ? "bg-success-subtle" : "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
highlighted ? "bg-success-subtle" : "", | |
highlighted && "bg-success-subtle", |
const baseClasses = [ | ||
"border", | ||
"badge", | ||
pills ? "rounded-pill" : "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pills ? "rounded-pill" : "", | |
pills && "rounded-pill", |
...colorClasses, | ||
]; | ||
|
||
const finalClasses = className ? cx(className, baseClasses) : cx(baseClasses); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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"> |
There was a problem hiding this comment.
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.
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"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import order
const editedData = { | ||
...data, | ||
}; | ||
delete editedData.keyword; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const editedData = { | |
...data, | |
}; | |
delete editedData.keyword; | |
const { keyword, ...editedData } = data; |
There was a problem hiding this 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>>; |
There was a problem hiding this comment.
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
.
errors: FieldErrors<Required<ProjectV2MetadataWithKeyword>>; |
control: Control<Required<ProjectV2MetadataWithKeyword>>; | ||
errors: FieldErrors<Required<ProjectV2MetadataWithKeyword>>; | ||
getValues: UseFormGetValues<Required<ProjectV2MetadataWithKeyword>>; | ||
oldKeywords?: string[]; |
There was a problem hiding this comment.
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 }) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
render={({ field }) => ( | |
render={({ field, fieldState }) => ( |
placeholder="Add new keyword" | ||
type="string" | ||
{...field} | ||
className={cx("form-control", errors.keyword && "is-invalid")} |
There was a problem hiding this comment.
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
:
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.
onChange={(e) => { | ||
field.onChange(e); | ||
}} |
There was a problem hiding this comment.
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}
.
onChange={(e) => { | |
field.onChange(e); | |
}} |
const newValue = field.value.trim(); | ||
const currentKeywords = getValues("keywords"); | ||
if (!currentKeywords.includes(newValue)) { | ||
const newKeywords = [...currentKeywords, newValue]; | ||
setValue("keywords", newKeywords); | ||
} | ||
setValue("keyword", ""); |
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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?
{getValues("keywords") | |
{field.value |
<KeywordBadge | ||
data-cy="project-settings-keyword" | ||
key={index} | ||
highlighted={!oldKeywords?.includes(keyword)} |
There was a problem hiding this comment.
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:
highlighted={!oldKeywords?.includes(keyword)} | |
highlighted={!(formState.defaultValues?.keywords ?? []).includes(keyword)} |
key={index} | ||
highlighted={!oldKeywords?.includes(keyword)} | ||
removeHandler={() => { | ||
const newKeywords = getValues("keywords").filter( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const newKeywords = getValues("keywords").filter( | |
const newKeywords = field.value.filter( |
Co-authored-by: Flora Thiebaut <flora.thiebaut@sdsc.ethz.ch>
@leafty I addressed all the points, thank you for the review! |
This PR implements the following:
Keywords -- project

Keywords -- project settings

Keywords -- data connectors edit/creation

/deploy renku=release-2.2.0