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

New NotificationsRoot component #9128

Merged
merged 10 commits into from
Mar 4, 2022
Merged

Conversation

sairina
Copy link
Contributor

@sairina sairina commented Feb 19, 2022

Summary

  • Removes 4 components from CoreBase into new NotificationsRoot component
    • AuthMessage
    • AppError
    • GlobalSnackbar
    • UpdateNotifications
  • Adds tests
  • Registers NotificationsRoot in apiSpec

References

Fixes #9102

Reviewer guidance

Check to see that the component is doing what is described in the issue (#9102) and check that the tests pass with the yarn run test-jest command.

Testing checklist

  • Contributor has fully tested the PR manually
  • Critical and brittle code paths are covered by unit tests

PR process

  • PR has the correct target branch and milestone
  • PR has 'needs review' or 'work-in-progress' label
  • If PR is ready for review, a reviewer has been added. (Don't use 'Assignees')

Reviewer checklist

  • Automated test coverage is satisfactory
  • PR is fully functional
  • PR has been tested for accessibility regressions
  • External dependency files were updated if necessary (yarn and pip)
  • Documentation is updated
  • Contributor is in AUTHORS.md

@sairina sairina marked this pull request as ready for review February 23, 2022 22:54
@sairina sairina linked an issue Feb 23, 2022 that may be closed by this pull request
4 tasks
Copy link
Member

@MisRob MisRob left a comment

Choose a reason for hiding this comment

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

Thank you, @sairina . The overall direction is looking great. I have two high-level suggestions for now that I think would be good to address.

For later reference, the NotificationsRoot is visually responsible for the following four components which I'll call the messaging components

AuthMessage AppError GlobalSnackbar UpdateNotification
AuthMessage AppError GlobalSnackbar UpdateNotification

(1) Decoupling of unnecessary style and layout information (including deletion of related props)

I’d suggest removing all styles that are not directly related to the messaging components. That’s the majority of computed styles (e.g. mainWrapperStyles , contentStyles) as well as styles defined in <style> block. It’d be fine to keep styles that are unnecessary to position these components on a page if there are some. This also applies to related props: fullScreen , hasSidebar ,maxMainWidth , and marginBottom.

Reason

NotificationsRoot is planned to be used in the following way from plugin index pages:

// LearnIndex

<template>
  <NotificationsRoot>
    <router-view />
  </NotificationsRoot>
</template>

One of the points of CoreBase refactor is to allow each page of the plugin that’s rendered in the <router-view /> to have full control over its layout, including margins, padding, etc. to gain more flexibility. That wouldn’t be fully possible if we kept unnecessary layout and style dependencies in the NotificationsRoot.

(2) Decoupling loading state from the default slot

I’d suggest removing v-if="!loading" from the whole wrapper div here and only using it directly on components that need it, that’s AuthMessage and AppError . For example

<KPageContainer v-if="!loading && notAuthorized">
  <AuthMessage
    :authorizedRole="authorizedRole"
    :header="authorizationErrorHeader"
    :details="authorizationErrorDetails"
  />
</KPageContainer>

Reason

Keeping v-if="!loading" on the div that’s also wrapping the default slot has unhappy consequences. Consider the situation demonstrated in the following example when we’d try to set loading state from one of the pages of the Learn plugin:

// LearnIndex

<template>
  <NotificationsRoot>
    <router-view />
  </NotificationsRoot>
</template>
// Library (rendered within the <router-view /> of LearnIndex)

<script>

export default {
  created() {
    state.loading = true
    fetchData().then(() => state.loading = false)
  }
}

</script>

At the moment we set state.loading = true in the created hook, the Library component would get destroyed (therefore state.loading = false would never get called) if we kept v-if="!loading" as is in the NotificationsRoot . This would make all logic around the loading state rigid and difficult to refactor later (and debug - true story :) - for example, we wouldn't be able to use the global loading state at all in a way shown in the example above (and we actually already need to do this in some cases and use hacks to overcome the current limitation).


To sum it up, this means we’d only keep things that are absolutely unnecessary for notifications and messaging components in the NotificationsRoot (and if we find out we need something more from it in the future, we can do that when a need arises while considering architecture and alternative options to solving that particular need).

kolibri/core/assets/src/views/NotificationsRoot.vue Outdated Show resolved Hide resolved
@MisRob
Copy link
Member

MisRob commented Feb 28, 2022

I realize that there might be a need to re-use some of the logic that I’m suggesting for deletion from this particular component so here are some possible directions (posting here since it's closely related to what I'm suggesting, but final decisions and implementation are out of the scope of this PR):

  • If we need to re-use some common layout styles in the future, we can create a default layout component that will encapsulate them and then import this component from each page. Pages will still have freedom to not use a layout and define their own layout (e.g. Library is one example of a page with an atypical layout). We could even have more layout components if we need to re-use different layouts.
  • Regarding the default gray background color of a page (currently defined in mainWrapperStyles), even though I don’t consider it such a problem, it seems to be rather a relic from the old CoreBase that contains much more layout and styles. Keeping it in the NotificationsRoot that’s responsible primarily for messaging would feel a bit confusing in my view, so maybe we could rather apply it to <body> element somewhere from the KolibriApp?

@MisRob
Copy link
Member

MisRob commented Feb 28, 2022

Also, I know it’s quite an abstract task where we’re trying to switch to a different architecture mindset that's quite the opposite of what we’ve relied on so far in the old Corebase while not having places to introduce the new component yet, and therefore it might be tricky to determine what should be preserved in the new one. If that’d be helpful, you may create some temporary test pages where this root component would be used similarly to how we plan to use it from plugin index pages. That would hopefully make development simpler since we could test what we need in the NotificationsRoot for those four messaging components.

Copy link
Member

@nucleogenesis nucleogenesis left a comment

Choose a reason for hiding this comment

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

I agree with @MisRob - I think perhaps that we'll end up seeing how removing the styles plays out in the next sprint as someone takes on #9134 I think that referring to the commit(s) where such styling is removed may serve the person working on that well

@sairina sairina requested review from MisRob and nucleogenesis March 1, 2022 22:23
@sairina
Copy link
Contributor Author

sairina commented Mar 1, 2022

If that’d be helpful, you may create some temporary test pages where this root component would be used similarly to how we plan to use it from plugin index pages.

What I did prior to the PR was remove CoreBase from a place and use NotificationsRoot to see if I could get the error-handling and authorization to work 😄 , so I definitely did a hacky version of that. But, given that there's no styling, I'm wondering if the tests for this component (which were passing and still are all currently passing) are sufficient for this NotificationsRoot?

Copy link
Member

@MisRob MisRob left a comment

Choose a reason for hiding this comment

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

Thank you for the updates and your work, @sairina, the component is looking good to me.

But, given that there's no styling, I'm wondering if the tests for this component (which were passing and still are all currently passing) are sufficient for this NotificationsRoot

I think that these main scenarios are sufficient. In my opinion, it is better to focus on fewer reliable tests that can be easily maintained rather than plenty of tests just to have everything covered.

I left some comments regarding the implementation of some tests that I think would be good to address but don't see that as blocking.

kolibri/core/assets/src/views/NotificationsRoot.vue Outdated Show resolved Hide resolved
kolibri/core/assets/test/views/NotificationsRoot.spec.js Outdated Show resolved Hide resolved
kolibri/core/assets/test/views/NotificationsRoot.spec.js Outdated Show resolved Hide resolved
});

it('if user is not authorized, AuthMessage component should be rendered', async () => {
const { wrapper, store } = makeWrapper({ computed: { notAuthorized: () => true } });
Copy link
Member

Choose a reason for hiding this comment

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

This test, as implemented right now, doesn't really test this scenario. Assume that over time we'd make a mistake in code of the notAuthorized computed property , for example:

notAuthorized() {
  if (
    this.error &&
    this.error.response &&
    this.error.response.status &&
    this.error.response.status == 403
  ) {
    return false; // should be `return true`
  }
  return !this.authorized;
},

Even though AuthMessage wouldn't get displayed after we got 403 response, this test would still pass because notAuthorized is set to true no matter of what due to how the test wrapper is created makeWrapper({ computed: { notAuthorized: () => true } }), giving us the false impression that things work as expected.

I see this pattern in couple of other tests of this suite too. I'd suggest removing anything related to computed from the whole test suite and mocking rather things that are coming from outside of the NotificationsRoot (store state etc.) instead of mocking computed properties, similarly to how you did it on lines 57-58.

I wrote more generally about problems of reaching into internal implementation in tests in #9065 (review) if some more information would help.

@sairina sairina requested a review from MisRob March 3, 2022 01:24
Copy link
Member

@nucleogenesis nucleogenesis left a comment

Choose a reason for hiding this comment

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

Everything looks great to me! If there are any issues they'll come out in the wash as we implement it elsewhere. Great work!

@nucleogenesis
Copy link
Member

@sairina I think it's good to merge this in spite of the DMG build failing

@sairina sairina merged commit 702e0b9 into learningequality:develop Mar 4, 2022
@rtibbles
Copy link
Member

rtibbles commented Mar 4, 2022

Yes, the DMG build failure was definitely not caused by any code in this PR!

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

Successfully merging this pull request may close these issues.

New CoreBase
4 participants