Skip to content
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

WD-8528 - feat: Display errors in Actions Panel #1691

Merged
merged 13 commits into from
Feb 2, 2024

Conversation

vladimir-cucu
Copy link
Contributor

@vladimir-cucu vladimir-cucu commented Jan 29, 2024

Done

  • Display inline error when trying to get actions for application.
  • Display error in toast when trying to execute action on units.

QA

  • To test the loading state of the button, you can add the following code at the beginning of executeAction function definition:
const executeActionPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error());
  }, 2000);
});
await executeActionPromise;

Details

@webteam-app
Copy link

Demo starting at https://juju-dashboard-1691.demos.haus

Copy link

codecov bot commented Jan 30, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (aa059d2) 95.04% compared to head (53e1808) 95.07%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1691      +/-   ##
==========================================
+ Coverage   95.04%   95.07%   +0.03%     
==========================================
  Files         182      183       +1     
  Lines        5325     5359      +34     
  Branches     1545     1555      +10     
==========================================
+ Hits         5061     5095      +34     
  Misses        243      243              
  Partials       21       21              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@vladimir-cucu vladimir-cucu marked this pull request as ready for review January 30, 2024 13:22
@@ -211,12 +244,21 @@ export default function ActionsPanel(): JSX.Element {
cancelButtonLabel={Label.CANCEL_BUTTON}
confirmButtonLabel={Label.CONFIRM_BUTTON}
confirmButtonAppearance="positive"
onConfirm={() => {
onConfirm={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
event.nativeEvent.stopImmediatePropagation();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This line of code (and maybe similar lines in other places) might become redundant in the case this gets implemented.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for filing that issue. I think it would be good to leave a comment here about why this stopImmediatePropagation is necessary and probably link to the issue you filed as well.

Copy link
Contributor

@huwshimi huwshimi left a comment

Choose a reason for hiding this comment

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

I've ended up making quite a few small comments so I'll do another review of this once those changes have been made.

@@ -119,9 +145,16 @@ export default function ActionsPanel(): JSX.Element {
setActionData(actions.results[0].actions);
}
setFetchingActionData(false);
setInlineErrors((prevInlineErrors) => [null, prevInlineErrors[1]]);
Copy link
Contributor

Choose a reason for hiding this comment

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

This case (and the other setInlineErrors() calls below) would lead to bugs if the total number of errors in the tuple changed (e.g. if a third error was added it would get lost during this call). Instead we should only update the slot that matches the error we're concerned with.

If you're concerned about mutating state then you could do something like:

errors = [... prevInlineErrors];
errors[0] = null;

@@ -211,12 +244,21 @@ export default function ActionsPanel(): JSX.Element {
cancelButtonLabel={Label.CANCEL_BUTTON}
confirmButtonLabel={Label.CONFIRM_BUTTON}
confirmButtonAppearance="positive"
onConfirm={() => {
onConfirm={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
event.nativeEvent.stopImmediatePropagation();
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for filing that issue. I think it would be good to leave a comment here about why this stopImmediatePropagation is necessary and probably link to the issue you filed as well.

console.error(Label.EXECUTE_ACTION_ERROR, error),
);
handleRemovePanelQueryParams();
executeAction()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should clear the errors here in case the user submits the form again. Also, depending on how long this action takes there could be a pause before the panel closes. You could show a loading state in the button. You'd need a new state (e.g. "const [saving, setSaving] = useState(false);".

Suggested change
executeAction()
setSaving(true);
executeAction()

You'll also need to update the button on line 287 to be <ActionButton and then give it the props disabled={saving} loading={saving}.

Comment on lines 255 to 256
.catch((error) => {
setInlineErrors((prevInlineError) => [
Copy link
Contributor

Choose a reason for hiding this comment

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

If you set the saving state when the action starts then you'll need to update it again here:

Suggested change
.catch((error) => {
setInlineErrors((prevInlineError) => [
.catch((error) => {
setSaving(false);
setInlineErrors((prevInlineError) => [

return;
})
.catch((error) => console.error(Label.GET_ACTIONS_ERROR, error));
.catch((error) => {
setInlineErrors((prevInlineErrors) => [
Copy link
Contributor

Choose a reason for hiding this comment

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

Here also we need to only update a single error slot so we don't accidentally remove errors if the number of errors in the tuple changes.

Comment on lines 257 to 258
prevInlineError[0],
Label.EXECUTE_ACTION_ERROR,
Copy link
Contributor

Choose a reason for hiding this comment

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

Also need to only update a single slot in the tuple.

@@ -392,7 +395,7 @@ export default function ConfigPanel(): JSX.Element {

<div className="config-panel__list">
{formErrors
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should also have .some(... or at the very least check that this isn't an empty array.

Comment on lines 60 to 68
const generateErrors = (errors: string[], scrollArea: HTMLElement | null) => (
<ScrollOnRender scrollArea={scrollArea}>
{errors.map((error) => (
<Notification key={error} severity="negative">
{error}
</Notification>
))}
</ScrollOnRender>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Possibly in a follow-up branch this could become a shared component, something like PanelErrors and include the .some(...) like you do in the other panel, as we're doing the same thing in a number of places.

Comment on lines 302 to 304
{inlineErrors.some((error) => error)
? generateInlineErrors(inlineErrors, scrollArea.current)
: null}
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to be outside of the wrapping <p> as this is currently giving errors about nested <p> tags.

Copy link
Contributor

@huwshimi huwshimi left a comment

Choose a reason for hiding this comment

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

Thanks for all the changes. I've made a couple of small comments but this can land once those have been fixed.

return;
})
.catch((error) => console.error(Label.GET_ACTIONS_ERROR, error));
.catch((error) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this also needs to call setFetchingActionData(false); so that the spinner hides when the error appears.

scrollArea?: HTMLElement | null;
};

const PanelInlineErrors = ({
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, thanks for adding this.


import PanelInlineErrors from "./PanelInlineErrors";

describe("PanelInlineErrors", () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be good to also test that the ScrollOnRender component does not appear if inlineErrors is null, an empty array or only contains falsey items.

Copy link
Contributor

@huwshimi huwshimi left a comment

Choose a reason for hiding this comment

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

Just one tiny comment.

@vladimir-cucu vladimir-cucu merged commit 0559f36 into canonical:main Feb 2, 2024
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants