-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Embeddable rebuild] Allow Dashboard to provide references (#176455)
Adds the ability for the Dashboard to provide references for its React Embeddable children to inject, and adds the ability for the React Embeddable children to provide extracted references back to the Dashboard.
- Loading branch information
1 parent
06ecb5a
commit 6827db4
Showing
18 changed files
with
443 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
examples/embeddable_examples/public/react_embeddables/field_list/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
export const FIELD_LIST_ID = 'field_list'; | ||
export const ADD_FIELD_LIST_ACTION_ID = 'create_field_list'; | ||
export const FIELD_LIST_DATA_VIEW_REF_NAME = 'field_list_data_view_id'; |
35 changes: 35 additions & 0 deletions
35
...ples/embeddable_examples/public/react_embeddables/field_list/create_field_list_action.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { i18n } from '@kbn/i18n'; | ||
import { apiIsPresentationContainer } from '@kbn/presentation-containers'; | ||
import { EmbeddableApiContext } from '@kbn/presentation-publishing'; | ||
import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; | ||
import { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin'; | ||
import { ADD_FIELD_LIST_ACTION_ID, FIELD_LIST_ID } from './constants'; | ||
|
||
export const registerCreateFieldListAction = (uiActions: UiActionsPublicStart) => { | ||
uiActions.registerAction<EmbeddableApiContext>({ | ||
id: ADD_FIELD_LIST_ACTION_ID, | ||
getIconType: () => 'indexOpen', | ||
isCompatible: async ({ embeddable }) => { | ||
return apiIsPresentationContainer(embeddable); | ||
}, | ||
execute: async ({ embeddable }) => { | ||
if (!apiIsPresentationContainer(embeddable)) throw new IncompatibleActionError(); | ||
embeddable.addNewPanel({ | ||
panelType: FIELD_LIST_ID, | ||
}); | ||
}, | ||
getDisplayName: () => | ||
i18n.translate('embeddableExamples.unifiedFieldList.displayName', { | ||
defaultMessage: 'Field list', | ||
}), | ||
}); | ||
uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_FIELD_LIST_ACTION_ID); | ||
}; |
221 changes: 221 additions & 0 deletions
221
...s/embeddable_examples/public/react_embeddables/field_list/field_list_react_embeddable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; | ||
import { css } from '@emotion/react'; | ||
import { ChartsPluginStart } from '@kbn/charts-plugin/public'; | ||
import { Reference } from '@kbn/content-management-utils'; | ||
import { CoreStart } from '@kbn/core-lifecycle-browser'; | ||
import { DataPublicPluginStart } from '@kbn/data-plugin/public'; | ||
import { | ||
DataViewsPublicPluginStart, | ||
DATA_VIEW_SAVED_OBJECT_TYPE, | ||
type DataView, | ||
} from '@kbn/data-views-plugin/public'; | ||
import { | ||
initializeReactEmbeddableTitles, | ||
initializeReactEmbeddableUuid, | ||
ReactEmbeddableFactory, | ||
RegisterReactEmbeddable, | ||
registerReactEmbeddableFactory, | ||
useReactEmbeddableApiHandle, | ||
useReactEmbeddableUnsavedChanges, | ||
} from '@kbn/embeddable-plugin/public'; | ||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; | ||
import { LazyDataViewPicker, withSuspense } from '@kbn/presentation-util-plugin/public'; | ||
import { euiThemeVars } from '@kbn/ui-theme'; | ||
import { | ||
UnifiedFieldListSidebarContainer, | ||
type UnifiedFieldListSidebarContainerProps, | ||
} from '@kbn/unified-field-list'; | ||
import { cloneDeep } from 'lodash'; | ||
import React, { useEffect, useState } from 'react'; | ||
import { BehaviorSubject } from 'rxjs'; | ||
import { FIELD_LIST_DATA_VIEW_REF_NAME, FIELD_LIST_ID } from './constants'; | ||
import { FieldListApi, FieldListSerializedStateState } from './types'; | ||
|
||
const DataViewPicker = withSuspense(LazyDataViewPicker, null); | ||
|
||
const getCreationOptions: UnifiedFieldListSidebarContainerProps['getCreationOptions'] = () => { | ||
return { | ||
originatingApp: '', | ||
localStorageKeyPrefix: 'examples', | ||
timeRangeUpdatesType: 'timefilter', | ||
compressed: true, | ||
showSidebarToggleButton: false, | ||
disablePopularFields: true, | ||
}; | ||
}; | ||
|
||
export const registerFieldListFactory = ( | ||
core: CoreStart, | ||
{ | ||
dataViews, | ||
data, | ||
charts, | ||
fieldFormats, | ||
}: { | ||
dataViews: DataViewsPublicPluginStart; | ||
data: DataPublicPluginStart; | ||
charts: ChartsPluginStart; | ||
fieldFormats: FieldFormatsStart; | ||
} | ||
) => { | ||
const fieldListEmbeddableFactory: ReactEmbeddableFactory< | ||
FieldListSerializedStateState, | ||
FieldListApi | ||
> = { | ||
deserializeState: (state) => { | ||
const serializedState = cloneDeep(state.rawState) as FieldListSerializedStateState; | ||
// inject the reference | ||
const dataViewIdRef = state.references?.find( | ||
(ref) => ref.name === FIELD_LIST_DATA_VIEW_REF_NAME | ||
); | ||
if (dataViewIdRef && serializedState) { | ||
serializedState.dataViewId = dataViewIdRef?.id; | ||
} | ||
return serializedState; | ||
}, | ||
getComponent: async (initialState, maybeId) => { | ||
const uuid = initializeReactEmbeddableUuid(maybeId); | ||
const { titlesApi, titleComparators, serializeTitles } = | ||
initializeReactEmbeddableTitles(initialState); | ||
|
||
const allDataViews = await dataViews.getIdsWithTitle(); | ||
|
||
const selectedDataViewId$ = new BehaviorSubject<string | undefined>( | ||
initialState.dataViewId ?? (await dataViews.getDefaultDataView())?.id | ||
); | ||
const selectedFieldNames$ = new BehaviorSubject<string[] | undefined>( | ||
initialState.selectedFieldNames | ||
); | ||
|
||
return RegisterReactEmbeddable((apiRef) => { | ||
const { unsavedChanges, resetUnsavedChanges } = useReactEmbeddableUnsavedChanges( | ||
uuid, | ||
fieldListEmbeddableFactory, | ||
{ | ||
dataViewId: [selectedDataViewId$, (value) => selectedDataViewId$.next(value)], | ||
selectedFieldNames: [ | ||
selectedFieldNames$, | ||
(value) => selectedFieldNames$.next(value), | ||
(a, b) => { | ||
return (a?.slice().sort().join(',') ?? '') === (b?.slice().sort().join(',') ?? ''); | ||
}, | ||
], | ||
...titleComparators, | ||
} | ||
); | ||
|
||
useReactEmbeddableApiHandle( | ||
{ | ||
...titlesApi, | ||
unsavedChanges, | ||
resetUnsavedChanges, | ||
serializeState: async () => { | ||
const dataViewId = selectedDataViewId$.getValue(); | ||
const references: Reference[] = dataViewId | ||
? [ | ||
{ | ||
type: DATA_VIEW_SAVED_OBJECT_TYPE, | ||
name: FIELD_LIST_DATA_VIEW_REF_NAME, | ||
id: dataViewId, | ||
}, | ||
] | ||
: []; | ||
return { | ||
rawState: { | ||
...serializeTitles(), | ||
// here we skip serializing the dataViewId, because the reference contains that information. | ||
selectedFieldNames: selectedFieldNames$.getValue(), | ||
}, | ||
references, | ||
}; | ||
}, | ||
}, | ||
apiRef, | ||
uuid | ||
); | ||
|
||
const [selectedDataViewId, selectedFieldNames] = useBatchedPublishingSubjects( | ||
selectedDataViewId$, | ||
selectedFieldNames$ | ||
); | ||
|
||
const [selectedDataView, setSelectedDataView] = useState<DataView | undefined>(undefined); | ||
|
||
useEffect(() => { | ||
if (!selectedDataViewId) return; | ||
let mounted = true; | ||
(async () => { | ||
const dataView = await dataViews.get(selectedDataViewId); | ||
if (!mounted) return; | ||
setSelectedDataView(dataView); | ||
})(); | ||
return () => { | ||
mounted = false; | ||
}; | ||
}, [selectedDataViewId]); | ||
|
||
return ( | ||
<EuiFlexGroup direction="column" gutterSize="none"> | ||
<EuiFlexItem | ||
grow={false} | ||
css={css` | ||
padding: ${euiThemeVars.euiSizeS}; | ||
`} | ||
> | ||
<DataViewPicker | ||
dataViews={allDataViews} | ||
selectedDataViewId={selectedDataViewId} | ||
onChangeDataViewId={(nextSelection) => { | ||
selectedDataViewId$.next(nextSelection); | ||
}} | ||
trigger={{ | ||
label: | ||
selectedDataView?.getName() ?? | ||
i18n.translate('embeddableExamples.unifiedFieldList.selectDataViewMessage', { | ||
defaultMessage: 'Please select a data view', | ||
}), | ||
}} | ||
/> | ||
</EuiFlexItem> | ||
<EuiFlexItem> | ||
{selectedDataView ? ( | ||
<UnifiedFieldListSidebarContainer | ||
fullWidth={true} | ||
variant="list-always" | ||
dataView={selectedDataView} | ||
allFields={selectedDataView.fields} | ||
getCreationOptions={getCreationOptions} | ||
workspaceSelectedFieldNames={selectedFieldNames} | ||
services={{ dataViews, data, fieldFormats, charts, core }} | ||
onAddFieldToWorkspace={(field) => | ||
selectedFieldNames$.next([ | ||
...(selectedFieldNames$.getValue() ?? []), | ||
field.name, | ||
]) | ||
} | ||
onRemoveFieldFromWorkspace={(field) => { | ||
selectedFieldNames$.next( | ||
(selectedFieldNames$.getValue() ?? []).filter((name) => name !== field.name) | ||
); | ||
}} | ||
/> | ||
) : null} | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
); | ||
}); | ||
}, | ||
}; | ||
|
||
registerReactEmbeddableFactory(FIELD_LIST_ID, fieldListEmbeddableFactory); | ||
}; |
19 changes: 19 additions & 0 deletions
19
examples/embeddable_examples/public/react_embeddables/field_list/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { | ||
DefaultEmbeddableApi, | ||
SerializedReactEmbeddableTitles, | ||
} from '@kbn/embeddable-plugin/public'; | ||
|
||
export type FieldListSerializedStateState = SerializedReactEmbeddableTitles & { | ||
dataViewId?: string; | ||
selectedFieldNames?: string[]; | ||
}; | ||
|
||
export type FieldListApi = DefaultEmbeddableApi; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.