-
-
Notifications
You must be signed in to change notification settings - Fork 8
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
CustomComponentLifecycle - Swap/Override components in element-web or matrix-react-sdk using module-api and the CustomComponentLifecycle #36
Conversation
Added new workflow
…rix-react-sdk-module-api into jts/usermenu-lifecycle
This reverts commit 7ea7922.
This reverts commit 7ea7922.
@MidhunSureshR @t3chguy |
@robintown @t3chguy |
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 personally don't have a lot of context for how this project works, and to what extent it already embraces things that feel a little hacky, but that's my impression of this solution.
Am I right that this only allows customization of class components? While many important components in matrix-react-sdk are classes, nowadays we're trying to only write functional components, so I'd much prefer a solution that generalizes to them as well. If it's not possible to customize functional components in a similar manner, then adding this feature could create a counter-productive incentive to rewrite our functional components as classes.
I'll need to defer final judgement to someone like @t3chguy who may have more experience with the module API, but hopefully my thoughts are of some use.
|
||
protected customComponentListener: CustomComponentListener = (customComponentOpts: CustomComponentOpts) => { | ||
customComponentOpts.CustomComponent = ({ children }) => { | ||
let usermenu: any = React.Children.toArray(children)[0]; |
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.
All of this casting and use of any
feels not great. Is there some way we can get more type-safety when customizing like this? (again this goes back to not knowing how much type-safety to expect in this project)
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.
Just to be clear, you are referring to:
super(undefined as any);
andlet usermenu: any = React.Children.toArray(children)[0];
?
In case 1. seems to be the standard of all other lifecycle tests within this project. But I can look into it - should be possible to use the interface for ModuleApi instead.
In case 2. Is just a mock-implementation of what a module might look like. But I'll look into it to see if I can get rid of the explicit any. And see what I can do to provide more type-safety.
Appreciate the feedback @robintown! 👍
In theory it shouldn't matter if it is a class or a functional component. As long as you are able to put the I would greatly appreciate if you could also have a look at the example provided in the PR-description on how it could be implemented in As for some more context on the proposed solution: I did some alternative experimentation on how to achieve the end goal of component override/customisation(Factory implementations etc.).
That being said, I'd be willing to put down the work if there are any suggestions on how to improve it. |
I have zero experience with the module API and very little context aside that so please do not block this one me. |
@robintown - I extended the tests to include usecase of how you can just as easily wrap and swap a functional component with the same method. I also took your comment into account and got rid off the casting in the constructor of module, and I dropped the "any", that was pointed out in one of the tests (Although was solved by casting the result. As If neither of you has experience with the module-api. Maybe I could suggest adding @germain-gg as a code reviewer? The mechanics of how this works, can be confusing. 1. Initialization of CustomComponentOpts:
2. Invocation of ModuleRunner:
This feature could help resolve reported issues, like: |
I marked this as needs product/design as I think we would need their alignment/approval to understand if this is a way in which we want extensibility to work. |
@langleyd Understandable, thank you for getting back to me. Please let me know if I can be of assistance in any way. Our team (Verji) have chosen to move ahead with this feature on our fork, as it provides us with a "cleaner", organised structure of custom code vs upstream, and drastically lessens the burden of updating/maintaining our fork. |
Hi @JohnSimonsen , This is a very generic solution and its somewhat unclear what the implications would be on our apps overall maintainability if we were to take this approach. It’s great that it helps you manage your fork but our product’s module apis(including on other platforms) are still experimental and without a more evolved strategy/guidance we can't accept this at this point in time. |
@langleyd Thanks for the update, a bit disappointed with the decision. It would've been a huge win to get this into the upstream, for us at least. And probably others, as it would allow the community to be able to create detailed customisations on component level, completely without having to fork the project. We'll stay tuned if there are better solutions presented at a later time. In the meantime we'll get some mileage on this solution, and if you're interested - we'll be happy to give you an update later with more mature results. Appreciate you took the time to look into it, @langleyd! 😊 |
CustomComponentLifecycle
This lifecycle borrows a lot from the existing WrapperLifecycle.ts(credit: Mikhail Aheichyk, Nordeck IT + Consulting GmbH.) and can be implemented in the same fashion. However we felt it was needed to create a seperate lifecycle as the intended usecase is different. The idea behind the CustomComponentLifecycle is that it should "swap" an existing standard element/matrix-react-sdk component with a custom component in it's place by writing a module. This deviates from the WrapperLifeCycle's usecase to wrap a component and append custom components as children to the wrapper / sibling of the wrapped component.
Changes
CustomComponentLifecycle.ts
CustomComponentLifecycle.test.tsx
types.ts
- AddedCustomComponentLifecycle
toAnyLifecycle
README.md
- Added description of CustomComponentLifecycleWhy is it needed?
Implementation example
As an experiment / Proof of concept i attempted the following:
Can I swap the UserMenu component in
matrix-react-sdk
seamlessly, with a custom module with custom elements and behaviours/functionality. Still retaining the original state, children and reactivity?The highlighted elements are (customisations) proof of a "modified" UserMenu.
The reactivity from child to expand panel etc, are retained. And since I based my customisation on the existing standard UserMenu all the functionality was transfered (i.e light/dark theme, default menu options etc.)
1. The module custom-usermenu-module
I created a new Module called CustomUserMenuModule with the following dependencies
matrix-react-sdk
andmatrix-js-sdk
allowed me to harness the full type library and stores from those packages. Meaning I could copy the entireUserMenu.ts
file frommatrix-react-sdk
to my modulecustom-usermenu-module
without any errors. I could then make my customisations directly in my module.Implementation of the module:
Note: Notice that the implementation uses the lifecycle proposed in this PR.
2. The Implementation example of the lifecycle in matrix-react-sdk (UserMenu)
📄matrix-react-sdk>src>components>views>SpacePanel.tsx
👇 (Lines with a "👈" are relevant in this implementation)
Note: Notice that the implementation uses the lifecycle proposed in this PR.
Signed-off-by: John Tore Simonsen jts@verji.com