-
Notifications
You must be signed in to change notification settings - Fork 168
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
Labels editing and validation #3426
Labels editing and validation #3426
Conversation
@yih-wang Haley please take a look as well, and let me know what you think |
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.
One quick comment from looking at the video - I will give a more thorough review when I get a chance
editableProps={{ | ||
'aria-label': 'Add label', | ||
defaultValue: '', | ||
'data-testid': 'add-label-input', | ||
}} |
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 way I understand the mockup, I don't think we want the "Add label" button to be an editable label itself. It can just be a button like before, but instead of its onClick
opening a modal, it should add a new label called "New label" via setUnsavedLabels()
. Then the user can click that label to edit it.
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.
+1 to @mturley.
@YuliaKrimerman here's a PF demo of how should the Add label
work: https://www.patternfly.org/components/label/#editable-label-group-with-add-button Hope it helps.
Thanks for the updates @YuliaKrimerman! My only comment is when handling duplicate label errors, the newly created label should be marked in red to indicate the error. From the video, it looks like the existing label is flagged instead. |
And one question to the loading time - from the video the newly added label takes a while to load after saving. Would this delay also occur in the production environment, or is it only in development environment? It’s a bit confusing to me before the label is loaded, so if this is expected in production, adding a loading state might help improve clarity. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3426 +/- ##
==========================================
+ Coverage 85.12% 85.40% +0.27%
==========================================
Files 1338 1353 +15
Lines 30127 31129 +1002
Branches 8274 8692 +418
==========================================
+ Hits 25647 26587 +940
- Misses 4480 4542 +62
... and 265 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
Thanks for the updates @YuliaKrimerman! All look good now! One thing that wasn’t part of the original design proposal but stood out to me as potentially confusing from the video - currently, after saving an edit, the label list keeps its original expanded/collapsed state. This means if the list was collapsed before editing, and the user enters the edit mode then saves any changes, the newly added label isn’t visible because the list stays collapsed. To avoid this potential confusion, it might be clearer to have the label list automatically expand after any edit is saved, regardless of its original collapsed/expanded state. Anyway it doesn't belong to the original scope of the issue, and I don't know how much work it needs, so not sure whether we should create a new story to improve this experience separately in a future sprint @mturley |
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.
@YuliaKrimerman can you add testing instructions in the "How has this been tested?" section of PR description?
modelVersionDetails.findAddLabelButton().click(); | ||
|
||
// Check label exists and is visible | ||
cy.findByTestId('editable-label-New Label').should('exist'); |
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.
can findLabel('New label')
be used instead ofeditable-label-New label
? I believe findLabel
was added for this?
cy.findByTestId('editable-label-New Label').click(); | ||
|
||
// Type the new label | ||
cy.get('input[data-testid="edit-label-input-New Label"]').should('exist'); |
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.
same applies here: modelVersionDetails.findLabelInput('New Label')
instead of 'input[data-testid="edit-label-input-New Label"]'
and the following 2-3 lines...
|
||
cy.findByTestId('editable-label-Testing label').click(); | ||
|
||
cy.get('input[data-testid="edit-label-input-Testing label"]') |
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.
same here
it('should handle label validation', () => { | ||
modelVersionDetails.findEditLabelsButton().click(); | ||
|
||
cy.findByTestId('editable-label-Testing label').click(); |
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 findLabelInput('Testing label')
instead
isSavingEdits={isSavingEdits} | ||
contentWhenEditing={ | ||
<DashboardDescriptionListGroup | ||
data-testid="editable-labels-group" |
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.
Is this test-id necessary? I don't see this test-id used anywhere.
<LabelGroup | ||
data-testid="label-group" | ||
data-testid="editable-label-group" |
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 don't see this testid used anywhere.
@@ -62,6 +64,7 @@ const ModelDetailsView: React.FC<ModelDetailsViewProps> = ({ | |||
) | |||
.then(refresh) | |||
} | |||
data-testid="model-labels" |
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 don't see this testid anywhere
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.
Hey @YuliaKrimerman, progress is looking great here but we need a bit of refactoring.
newText: string, | ||
currentLabel?: string, | ||
) => { | ||
const error = validateLabel(newText, currentLabel); |
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.
If I cause an error (e.g. add a duplicate label) and don't fix it, then add another label, the error disappears and submission is allowed even though there is still a problem. This is because we store errors in state and we can only have one at a time.
Errors don't need to be state here, they can be derived from the existing unsavedLabels
state on each render. I think instead of having the labelErrors
come from React.useState
, we can just have a const labelErrors
object that we compute as part of the rendering logic of the component. That way, simply the call to setUnsavedLabels
that puts us in an error state will cause errors to appear. That does mean a bit of refactoring, since you won't be validating a specific label anymore but validating the list of labels. This also means we may have multiple errors to display - if you have two sets of duplicate labels, you'll want to call them both out.
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.
@yih-wang Haley, we would need your input in this case on how to handle double duplicates, should we show them both like this?(Ignore the double red color of the labels- only the latests will be red and not all of them) Should we even allow multiple duplicates at edit time? Let me know what you think : this is how it looks with what Mike suggested ( if we want to take this route please let me know which text to use)
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.
Hi @YuliaKrimerman, I've reviewed this pattern with Anges, who is finalizing the ux validation guidelines. Her suggestion is to allow multiple duplicates at the edit time.
@kaedward, I'd like to hear your input on the validation message here - Currently, we show the error message below when there's only one label duplicated
‘mylabel’ already exists. Use a unique name that does not match any existing label or property key.
But how should the error message look like when there're multiple duplicates? Should it be a general message like
Labels duplicated. Use a unique name that does not match any existing label or property key.
or a specific message which list all the duplicated labels' names like
‘mylabel’ and 'new' already exist. Use a unique name that does not match any existing label or property key.
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.
@yih-wang How do these sound? Do we need to specify that the label name must not match a property?
The label name should be bolded, not in quotation marks.
ONE ERROR:
label-name appears more than once. Ensure that each label is unique.
MORE THAN 1 ERROR:
label-name and label-name2 appear more than once. Ensure that each label is unique.
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.
Do we need to specify that the label name must not match a property?
@kaedward I think it would be helpful to specify that the label shouldn’t match any existing labels or property keys. This would prevent confusion when users don’t see duplicated labels but encounter issues due to a property key duplicating the label.
The pattern you suggested looks good to me. But I'm wondering whether we should say 'label-name already exists' rather than 'label-name appears more than once' because it's already used elsewhere for duplication validation.
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.
ONE ERROR:
label-name already exists. Ensure that each label is unique and does not match any existing property key.
MORE THAN 1 ERROR:
label-name and label-name2 already exist. Ensure that each label is unique and does not match any existing property key.
@yih-wang Here ya go!
if (error) { | ||
setLabelErrors({ [newText]: error }); | ||
setUnsavedLabels((prev) => { | ||
const filtered = prev.filter((label) => label !== currentLabel); | ||
return [...filtered, newText]; | ||
}); | ||
} else if (newText) { | ||
setUnsavedLabels((prev) => { | ||
if (currentLabel) { | ||
return [...prev, newText]; | ||
} | ||
const filtered = prev.filter((label) => label !== currentLabel); | ||
return [...filtered, newText]; | ||
}); | ||
setLabelErrors({}); | ||
} | ||
}; |
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'm not sure why the setUnsavedLabels
behavior is different if there's an error vs if there isn't. This may resolve itself if you move the validation from here to render time, but I'm pretty sure handleEditComplete
should update the unsaved labels state the same way no matter whether there is an error to show. I think this first call in your if (error)
case is correct:
setUnsavedLabels((prev) => {
const filtered = prev.filter((label) => label !== currentLabel);
return [...filtered, newText];
});
Your other case is making it so any time edit focus is lost, if the user didn't enter duplicate label text, the old label is not filtered out on update and we end up with another duplicate:
Screen.Recording.2024-11-08.at.12.56.50.PM.mov
@YuliaKrimerman When I reviewed the validation pattern with Anges, she pointed out another inconsistency with the upcoming validation guidelines - the ✓ should always remain enabled even when there're duplication errors. This provides better accessibility and aligns with the new form validation pattern. Apologies for the adjustment from the original design, I think it's better to address this comment and change the ✓ behavior to be always enabled. To be specific, if user tries to save (clicking ✓) when there're errors, "the focus should shift from the submit button to the contents in the inline alert (plain alert in our case). This enables screen reader users to hear the error message, and then easily navigate to the fields in the form where the errors exist." (quoted from the validation guidelines) |
Hi @yih-wang @kaedward @mturley. Please take a look at the video recording. It has all the requested changes so far : Handling double duplications, proper error display based on amount of duplications and enabling the save button even when error exist. Also added some top padding above the red alert as Mike suggested (see screenshot, not video). Mike if you have a chance you can also do a re-review while I will work on fixing the last test, and brace for any incoming comments if needed. |
@YuliaKrimerman I'll do a full review asap, but just looking at that screenshot it looks like you lost the red color on the errored labels? |
@mturley Haha no, it's just I screenshot with only the original showing, and not the duplicate. In the video the red is there :) |
Ohh, I misread sorry! |
Thanks @YuliaKrimerman, the latest updates look good! The only question I have is do we decide whether or not address my earlier comment with this pr? Just make sure it's not overlooked.
|
@yih-wang Yeah I didn't address it as part of this issue. I think opening a separate story for this might be a good idea. |
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.
@YuliaKrimerman I'm so sorry for the delay, don't kill me, I have comments.....
const duplicateLabels: string[] = []; | ||
duplicatesMap.forEach((count, label) => { | ||
if (count > 1) { | ||
duplicateLabels.push(label); |
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.
@YuliaKrimerman I hate to drag this out a bit longer but can we add a test or two that covers the duplicate label validation to make codecov happier?
} | ||
|
||
labelsList.forEach((label) => { | ||
if (!labelsList.includes(label) && allExistingKeys.includes(label)) { |
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 logic is wrong. !labelsList.includes(label)
will always be false here because we're looping over labelsList, the label will always be in labelsList. So this validation case is never hit, and as a user I am allowed to create a label that already exists as a key in the properties table below, which replaces it and loses that property value.
I think instead, we need to either consider allExistingKeys
when we are setting up the duplicatesMap
or we need to have this logic be somehow if (weDontAlreadyHaveAnErrorForThisLabel && allExistingKeys.includes(label))
. In that latter case, the message could be specific to the fact that the label matches an existing property key, like **${label}** already exists as a property key. Ensure that each label is unique and does not match any existing property key.
setUnsavedLabels((prev) => [...prev, newLabel]); | ||
}; | ||
|
||
const labelErrors = validateLabels(unsavedLabels); |
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.
validateLabels
doesn't really need to take unsavedLabels
as an argument at all, it can just directly use unsavedLabels
in its body instead of its labelsList
.
style={{ | ||
border: '2px solid #d2d2d2', | ||
color: '#0066CC', | ||
backgroundColor: 'transparent', | ||
}} |
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.
These inline styles override the border/color at all times so there is no longer a hover state. We should avoid inline styles where possible.
I don't think we need our own Button here. Can we not use the addLabelControl
prop of LabelGroup like we had before? That automatically adds a button with the correct hover styles while editing like in this PF example. https://www.patternfly.org/components/label#editable-label-group
I locally reverted that part of this change and put back that addLabelControl
but just had it call your new addNewLabel
function, and it appears to work fine.
aria-live="polite" | ||
isPlain | ||
tabIndex={-1} | ||
style={{ marginTop: '16px' }} |
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.
Instead of inline styles here, you can import spacing from '@patternfly/react-styles/css/utilities/Spacing/spacing';
and then use className={spacing.mtMd}
which will set a margin-top to the PF medium spacer, which is 16px.
@mturley Ready for re-review. Added another error type for our edge case |
numLabels={10} | ||
expandedText="Show Less" | ||
collapsedText="Show More" |
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.
Ooooone last thing - we should set numLabels
back to unsavedLabels.length
and remove the expandedText
and collapsedText
props. When editing labels they should always be expanded, the show more/less behavior is only when they are not being edited which is handled by the other LabelGroup rendered in non-edit mode. Otherwise, if they are collapsed and you click "Add label" the new label doesn't appear until you expand.
Signed-off-by: Mike Turley <[email protected]>
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.
One more thing --- just kidding, approved
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: gitdallas, mturley The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
RHOAIENG-6994
RHOAIENG-6995
Description
[
Screen.Recording.2024-11-06.at.1.31.09.PM.mov
](url)
When going to model/version Detail, editing the labels are behaving as inline labels and also now have a validation for a case a new label already exists ( side not the background of the error message is red in paternfly )
How Has This Been Tested?
on the UI
Test Impact
Working on adding test while getting feedback on the implementation
Request review criteria:
Self checklist (all need to be checked):
If you have UI changes:
After the PR is posted & before it merges:
main