diff --git a/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_destination_picker.tsx b/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_destination_picker.tsx index 2a7273bac98a6..d88f9af7161db 100644 --- a/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_destination_picker.tsx +++ b/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_destination_picker.tsx @@ -27,14 +27,14 @@ import { DashboardLinkEmbeddableStrings } from './dashboard_link_strings'; export const DashboardLinkDestinationPicker = ({ setDestination, setPlaceholder, - currentDestination, parentDashboard, + initialSelection, ...other }: { setDestination: (destination?: string) => void; setPlaceholder: (placeholder?: string) => void; - currentDestination?: string; parentDashboard?: DashboardContainer; + initialSelection?: string; }) => { const [searchString, setSearchString] = useState(''); const [selectedDashboard, setSelectedDashboard] = useState(); @@ -43,7 +43,11 @@ export const DashboardLinkDestinationPicker = ({ const parentDashboardId = parentDashboard?.select((state) => state.componentState.lastSavedId); const { loading: loadingDashboardList, value: dashboardList } = useAsync(async () => { - return await memoizedFetchDashboards(searchString, undefined, parentDashboardId); + return await memoizedFetchDashboards({ + search: searchString, + currentDashboardId: parentDashboardId, + selectedDashboardId: initialSelection, + }); }, [searchString, parentDashboardId]); useEffect(() => { @@ -52,6 +56,7 @@ export const DashboardLinkDestinationPicker = ({ return { data: dashboard, label: dashboard.attributes.title, + checked: initialSelection && dashboard.id === initialSelection ? 'on' : undefined, ...(dashboard.id === parentDashboardId ? { prepend: ( @@ -63,7 +68,7 @@ export const DashboardLinkDestinationPicker = ({ }) ?? []; setDashboardListOptions(dashboardOptions); - }, [dashboardList, parentDashboardId, searchString]); + }, [dashboardList, parentDashboardId, initialSelection, searchString]); const debouncedSetSearch = useMemo( () => diff --git a/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_tools.tsx b/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_tools.tsx index 3735e5a044ffa..0b0a56443ba5e 100644 --- a/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_tools.tsx +++ b/src/plugins/navigation_embeddable/public/components/dashboard_link/dashboard_link_tools.tsx @@ -6,24 +6,16 @@ * Side Public License, v 1. */ -import { isEmpty, memoize } from 'lodash'; +import { isEmpty, memoize, filter } from 'lodash'; import { DashboardItem } from '../../embeddable/types'; import { dashboardServices } from '../../services/kibana_services'; /** - * Memoized fetch dashboard will only refetch the dashboard information if the given `dashboardId` changed between - * calls; otherwise, it will use the cached dashboard, which may not take into account changes to the dashboard's title - * description, etc. Be mindful when choosing the memoized version. + * ---------------------------------- + * Fetch a single dashboard + * ---------------------------------- */ -export const memoizedFetchDashboard = memoize( - async (dashboardId: string) => { - return await fetchDashboard(dashboardId); - }, - (dashboardId) => { - return dashboardId; - } -); export const fetchDashboard = async (dashboardId: string): Promise => { const findDashboardsService = await dashboardServices.findDashboardsService(); @@ -34,20 +26,39 @@ export const fetchDashboard = async (dashboardId: string): Promise { - return await fetchDashboards(search, size, currentDashboardId); +/** + * Memoized fetch dashboard will only refetch the dashboard information if the given `dashboardId` changed between + * calls; otherwise, it will use the cached dashboard, which may not take into account changes to the dashboard's title + * description, etc. Be mindful when choosing the memoized version. + */ +export const memoizedFetchDashboard = memoize( + async (dashboardId: string) => { + return await fetchDashboard(dashboardId); }, - (search, size, currentDashboardId) => { - return [search, size, currentDashboardId].join('|'); + (dashboardId) => { + return dashboardId; } ); -const fetchDashboards = async ( - search: string = '', - size: number = 10, - currentDashboardId?: string -): Promise => { +/** + * ---------------------------------- + * Fetch lists of dashboards + * ---------------------------------- + */ + +interface FetchDashboardsProps { + size?: number; + search?: string; + currentDashboardId?: string; + selectedDashboardId?: string; +} + +const fetchDashboards = async ({ + search = '', + size = 10, + currentDashboardId, + selectedDashboardId, +}: FetchDashboardsProps): Promise => { const findDashboardsService = await dashboardServices.findDashboardsService(); const responses = await findDashboardsService.search({ search, @@ -55,28 +66,24 @@ const fetchDashboards = async ( options: { onlyTitle: true }, }); - let currentDashboard: DashboardItem | undefined; let dashboardList: DashboardItem[] = responses.hits; - /** When the parent dashboard has been saved (i.e. it has an ID) and there is no search string ... */ - if (currentDashboardId && isEmpty(search)) { - /** ...force the current dashboard (if it is present in the original search results) to the top of the list */ - dashboardList = dashboardList.sort((dashboard) => { - const isCurrentDashboard = dashboard.id === currentDashboardId; - if (isCurrentDashboard) { - currentDashboard = dashboard; - } - return isCurrentDashboard ? -1 : 1; + /** If there is no search string... */ + if (isEmpty(search)) { + /** ... filter out both the current and parent dashboard from the list ... */ + dashboardList = filter(dashboardList, (dash) => { + return dash.id !== currentDashboardId && dash.id !== selectedDashboardId; }); - /** - * If the current dashboard wasn't returned in the original search, perform another search to find it and - * force it to the front of the list - */ - if (!currentDashboard) { - currentDashboard = await fetchDashboard(currentDashboardId); + /** ... so that we can force them to the top of the list as necessary. */ + if (currentDashboardId) { dashboardList.pop(); // the result should still be of `size,` so remove the dashboard at the end of the list - dashboardList.unshift(currentDashboard); // in order to force the current dashboard to the start of the list + dashboardList.unshift(await fetchDashboard(currentDashboardId)); // in order to force the current dashboard to the start of the list + } + + if (selectedDashboardId !== currentDashboardId && selectedDashboardId) { + dashboardList.pop(); + dashboardList.unshift(await fetchDashboard(selectedDashboardId)); } } @@ -87,3 +94,17 @@ const fetchDashboards = async ( return simplifiedDashboardList; }; + +export const memoizedFetchDashboards = memoize( + async ({ search, size, currentDashboardId, selectedDashboardId }: FetchDashboardsProps) => { + return await fetchDashboards({ + search, + size, + currentDashboardId, + selectedDashboardId, + }); + }, + ({ search, size, currentDashboardId, selectedDashboardId }) => { + return [search, size, currentDashboardId, selectedDashboardId].join('|'); + } +); diff --git a/src/plugins/navigation_embeddable/public/components/external_link/external_link_destination_picker.tsx b/src/plugins/navigation_embeddable/public/components/external_link/external_link_destination_picker.tsx index edbc6172bef84..9fb22f9b69368 100644 --- a/src/plugins/navigation_embeddable/public/components/external_link/external_link_destination_picker.tsx +++ b/src/plugins/navigation_embeddable/public/components/external_link/external_link_destination_picker.tsx @@ -19,15 +19,15 @@ const isValidUrl = export const ExternalLinkDestinationPicker = ({ setDestination, setPlaceholder, - currentDestination, + initialSelection, ...other }: { setDestination: (destination?: string) => void; setPlaceholder: (placeholder?: string) => void; - currentDestination?: string; + initialSelection?: string; }) => { const [validUrl, setValidUrl] = useState(true); - const [urlValue, setUrlValue] = useState(currentDestination); + const [urlValue, setUrlValue] = useState(initialSelection); useMount(() => { if (urlValue && urlValue.length > 0) { diff --git a/src/plugins/navigation_embeddable/public/components/navigation_embeddable_link_editor.tsx b/src/plugins/navigation_embeddable/public/components/navigation_embeddable_link_editor.tsx index a8f5dccdc9546..9835f6c11149e 100644 --- a/src/plugins/navigation_embeddable/public/components/navigation_embeddable_link_editor.tsx +++ b/src/plugins/navigation_embeddable/public/components/navigation_embeddable_link_editor.tsx @@ -57,16 +57,10 @@ export const NavigationEmbeddableLinkEditor = ({ const [selectedLinkType, setSelectedLinkType] = useState( linkToEdit.current ? linkToEdit.current.type : DASHBOARD_LINK_TYPE ); - const [linkLabel, setLinkLabel] = useState( - linkToEdit.current ? linkToEdit.current.label || '' : '' - ); - const [linkDestination, setLinkDestination] = useState( - linkToEdit.current?.destination - ); + const [linkLabel, setLinkLabel] = useState(); + const [linkDestination, setLinkDestination] = useState(); const [linkLabelPlaceholder, setLinkLabelPlaceholder] = useState(); - console.log(linkToEdit.current); - const linkTypes: EuiRadioGroupOption[] = useMemo(() => { return ([DASHBOARD_LINK_TYPE, EXTERNAL_LINK_TYPE] as NavigationLinkType[]).map((type) => { return { @@ -114,8 +108,12 @@ export const NavigationEmbeddableLinkEditor = ({ options={linkTypes} idSelected={selectedLinkType} onChange={(id) => { - setLinkDestination(undefined); - setLinkLabelPlaceholder(undefined); + if (linkToEdit.current?.type === id) { + setLinkDestination(linkToEdit.current.destination); + } else { + setLinkDestination(undefined); + setLinkLabelPlaceholder(undefined); + } setSelectedLinkType(id as NavigationLinkType); }} /> @@ -126,13 +124,13 @@ export const NavigationEmbeddableLinkEditor = ({ ) : ( )} @@ -144,7 +142,7 @@ export const NavigationEmbeddableLinkEditor = ({ linkLabelPlaceholder || NavEmbeddableStrings.editor.linkEditor.getLinkTextPlaceholder() } - value={linkLabel} + value={linkLabel || linkToEdit.current?.label} onChange={(e) => { setLinkLabel(e.target.value); }} diff --git a/src/plugins/navigation_embeddable/public/components/navigation_embeddable_panel_editor.tsx b/src/plugins/navigation_embeddable/public/components/navigation_embeddable_panel_editor.tsx index 72da808bddd1a..87a631eec5bc1 100644 --- a/src/plugins/navigation_embeddable/public/components/navigation_embeddable_panel_editor.tsx +++ b/src/plugins/navigation_embeddable/public/components/navigation_embeddable_panel_editor.tsx @@ -23,10 +23,10 @@ import { EuiFlexItem, EuiFlexGroup, EuiFlyoutBody, + EuiButtonIcon, EuiButtonEmpty, EuiFlyoutFooter, EuiFlyoutHeader, - EuiButtonIcon, } from '@elastic/eui'; import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; @@ -63,11 +63,10 @@ export const NavigationEmbeddablePanelEditor = ({ const newLinks = await openLinkEditorFlyout({ links, parentDashboard, - idToEdit: linkToEditId, + idToEdit: linkToEditId, // if this is defined, then we are editing; otherwise, we are adding ref: editLinkFlyoutRef, }); - console.log('new links', newLinks); - setLinks(newLinks); + if (newLinks) setLinks(newLinks); }, [editLinkFlyoutRef, links, parentDashboard] ); diff --git a/src/plugins/navigation_embeddable/public/editor/open_link_editor_flyout.tsx b/src/plugins/navigation_embeddable/public/editor/open_link_editor_flyout.tsx index 292a1b21ecdd4..8b0b4943da4b6 100644 --- a/src/plugins/navigation_embeddable/public/editor/open_link_editor_flyout.tsx +++ b/src/plugins/navigation_embeddable/public/editor/open_link_editor_flyout.tsx @@ -31,10 +31,10 @@ export async function openLinkEditorFlyout({ links, idToEdit, parentDashboard, -}: LinkEditorProps): Promise { - return new Promise((resolve, reject) => { +}: LinkEditorProps): Promise { + return new Promise((resolve, reject) => { const onSave = (newLinks: NavigationEmbeddableLinkList) => { - console.log('on save', newLinks); + // console.log('on save', newLinks); resolve(newLinks); if (ref.current) ReactDOM.unmountComponentAtNode(ref.current); }; @@ -56,5 +56,8 @@ export async function openLinkEditorFlyout({ , ref.current ); + }).catch(() => { + // on reject (i.e. on cancel), just return the original list of links + return undefined; }); }