-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Embeddable Refactor] Create Decoupled Presentation Panel #172017
[Embeddable Refactor] Create Decoupled Presentation Panel #172017
Conversation
…anel' into createPresentationPanel
…anel' into createPresentationPanel
…x Dashboard replace panel
…anel' into createPresentationPanel
…d malformed filters crashing Lens
import { isExplicitInputWithAttributes } from '../embeddable_factory'; | ||
import { EmbeddableInput } from '../i_embeddable'; | ||
|
||
const getLatestAppId = async (): Promise<string | undefined> => { |
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.
Could this be simplified to just return core.application.currentAppId$.pipe(first()).toPromise();
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.
Actually, toPromise is deprecated. instead, you could just use await lastValueFrom(core.application.currentAppId$)
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 that lastValueFrom
will work properly in this case. I think it waits for the observable to complete.
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.
Good call, firstValueFrom
works perfectly here and makes the code much shorter! Updated this in 610daf5
|
||
if (this.parentSubscription) { | ||
this.parentSubscription.unsubscribe(); | ||
} | ||
return; | ||
} | ||
|
||
public async untilInitializationFinished(): Promise<void> { | ||
return new Promise((resolve) => { |
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.
How about just return lastValueFrom (this.initializationFinished)
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 lastValueFrom
will work great here, because I complete
the stream when the initialization is finished. Let me try it 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.
How about using firstValueFrom then?
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 confirm that lastValueFrom
worked great here! Updated this in 610daf5. Thanks for the suggestion!
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 had to revert this change because of a jest test failure. I've opened a followup issue to see what went wrong here.
/** | ||
* The interface that the adapters used to open an inspector have to fullfill. | ||
*/ | ||
export interface Adapters { |
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 type already exists and is declared in src/plugins/inspector/common/adapters/types.ts
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.
Good call, updated that in 610daf5! Best not to duplicate the any
here.
export * from './types'; | ||
export * from '../common/adapters'; |
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.
you will need to export Adapters from common/adapters
ecf34ac
to
0d90056
Compare
0d90056
to
d2eb105
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.
kibana-presentation changes LGTM other than some minor comments. This is an impressive accomplishment and is a huge step forward for the new embeddable system. Thanks for all of the hard work and high quality changes.
code review, tested in chrome
const onChange = jest.fn(); | ||
updateTimeRange(mockTimeRange); | ||
updateTimeRange(undefined); | ||
action.subscribeToCompatibilityChanges(context, onChange); |
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.
Move updates to after subscriptions is created
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.
Done!
it('calls onChange when view mode changes', () => { | ||
const onChange = jest.fn(); | ||
updateViewMode('view'); | ||
action.subscribeToCompatibilityChanges(context, onChange); |
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.
Call updateViewMode after creating subscription
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.
Done!
); | ||
} | ||
|
||
if (loading || !value?.Panel || !value?.unwrappedComponent) |
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.
Should an error state be displayed if loading=false and value.Panel or value.unwrappedComponent do not exist? In this case, the panel will get stuck showing loading screen forever. I think an error would help, especially for developers, who are the ones most likely to trigger this case.
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.
Good call. I've updated the error section above to also trigger when (!loading && (!value?.Panel || !value?.unwrappedComponent))
|
||
const [initialLoadComplete, setInitialLoadComplete] = useState(!dataLoading); | ||
if (dataLoading === false && !initialLoadComplete) { | ||
setInitialLoadComplete(true); |
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.
Should setInitialLoadComplete be inside an effect. State should not be set from rendering.
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 code was just copied over from #171238. Refer to this comment for more info on why it was done this way. I'd be okay with wrapping this in a useEffect
.
💛 Build succeeded, but was flaky
Failed CI Steps
Metrics [docs]Module Count
Public APIs missing comments
Async chunks
Public APIs missing exports
Page load bundle
Unknown metric groupsAPI count
async chunk count
ESLint disabled line counts
References to deprecated APIs
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: |
…2017) Closes elastic#167426 Closes elastic#167890 Contains much of the prep work required to decouple the Embeddables system from Kibana in anticipation of its deprecation and removal. Co-authored-by: Nathan Reese <[email protected]>
…r badges (#186102) Closes #185914 ## Summary The linked regression is a result of #172017, which accidentally removed tooltip support from badges - this PR adds it back so that the controls deprecation badge now shows the tooltip as expected on both hover and focus: <p align="center"> <img src="https://github.com/elastic/kibana/assets/8698078/aca8075b-bce5-4fa6-b4b9-e38e00aefad8" alt="Gif showcasing the deprecation tooltip"/> </p> I also ensured that the tooltip contents are available for screen readers by adding a conditional `aria-label` - if a tooltip is not provided, then the screen reader will read the badge's contents instead. **How to Test** 1. Create a legacy input control embeddable by adding the following to your URL: `/app/visualize#/create?type=input_control_vis` 2. Add that panel to a dashboard 3. You should see the deprecation badge and its corresponding tooltip :+1: ### Checklist - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
Closes #167426
Closes #167890
Summary
Part of #167429
This PR contains much of the prep work required to decouple the Embeddables system from Kibana in anticipation of its deprecation and removal.
To accomplish this, several new systems and concepts have been added to Kibana which will eventually replace portions of the Embeddable framework. An overview of each new concept can be found in this PR description. If you would like more information, the architecture document goes into more detail.
The Presentation Panel
Previously, the generic frame component that linked React components to the UI Actions library was tightly coupled to the Embeddables system and the Embeddables plugin. This PR removes that coupling.
Now, any React component can be linked to the UI Actions library by passing it as a prop to the newly created
PresentationPanel
compoent. This linkage is created via an API object, which is passed from the component via the use imperative handle hook. Methods and state on this API will be used by the Presentation Panel. For instance, if the APIpublishes
a title, the latest value of that title will be displayed in the panel header.Publishing Subjects
This API system works great as-is for completely imperative things like functions,
onEdit
for instance, but for state which can change, we need a standardized system which can publish individual pieces of state in a reactive form. To fulfil this requirement, this PR introduces Publishing subjects.Publishing subjects are essentially read only RXJS behavior subjects. This allows APIs to pass around changeable pieces of state, react to those changes, and get the last published value.
Note
Publishing subjects are meant to be readonly because they are not meant to be a source of truth. Instead, they are meant to forward or publish state from another source of truth, whether it's from a Use State, some other RXJS subject, from Redux, or some other state management system.
A number of convenience functions are included to allow translating state from React components, or state from legacy Embeddables into Publishing Subjects. Additionally, hooks are included to allow components to render the latest state from the publishing subjects.
UI Actions changes
A number of changes have been made to both the UI Actions framework itself, and a number of Actions registered with those frameworks.
Dashboard & Presentation Panel Actions
Because UI Actions can now theoretically be linked to any component via the new Presentation Panel component, actions which were tightly coupled with the old Embeddable system should be refactored to list their actual dependencies declaratively, which effectively decouples them from the embeddable system.
For instance, instead of the
edit panel action
requiring anEmbeddable
instance as its context, it instead requires any API object with anonEdit
method and aviewMode
currently set toedit
. This allows for more clarity in requirements, and allows for more flexibility in what each action actually does.Note
It's important to note that this PR leaves some code in place to ensure interoperability between Actions which are still tightly coupled to Embeddables, and actions which have been updated. This includes:
embeddable
. This should eventually be updated toapi
Subscribe to compatibility changes
Two new optional methods have been added to the UI Action definition:
subscribeToCompatibilityChanges
andcouldBecomeCompatible
. These methods are meant to allow actions to declare exactly which pieces of state from a given API will cause them to become compatible or incompatible.Previously, any state change to any embeddable or its parent would cause the entire tree of Actions to re-calculate their compatibility state. With these changes in place, that code has been removed, and any action which is expected to frequently change its compatibility state in response to some other state can do so via this system.
Architecture / Progress
This PR accomplishes some of the items on our roadmap towards the complicated architecture which will temporarily exist to ease our transition towards the new Embeddables framework. Below, you can see the pieces of this architecture added by this PR, and the pieces which will be added by the followup.
Checklist
Delete any items that are not applicable to this PR.