Skip to content

Commit 7c34a1a

Browse files
Merge branch 'main' into uploading-android-map-files
2 parents 04dae25 + 82a1776 commit 7c34a1a

File tree

64 files changed

+3403
-508
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+3403
-508
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
"@dnd-kit/utilities": "^2.0.0",
9595
"@elastic/apm-rum": "^5.12.0",
9696
"@elastic/apm-rum-react": "^1.4.2",
97-
"@elastic/charts": "59.0.0",
97+
"@elastic/charts": "59.1.0",
9898
"@elastic/datemath": "5.0.3",
9999
"@elastic/elasticsearch": "npm:@elastic/[email protected]",
100100
"@elastic/ems-client": "8.4.0",

src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ import { mount, ReactWrapper } from 'enzyme';
1212
import { I18nProvider } from '@kbn/i18n-react';
1313

1414
import { pluginServices } from '../services/plugin_services';
15-
import { DashboardListing, DashboardListingProps } from './dashboard_listing';
15+
import { DashboardListing } from './dashboard_listing';
1616

1717
/**
1818
* Mock Table List view. This dashboard component is a wrapper around the shared UX table List view. We
1919
* need to ensure we're passing down the correct props, but the table list view itself doesn't need to be rendered
2020
* in our tests because it is covered in its package.
2121
*/
2222
import { TableListView } from '@kbn/content-management-table-list-view';
23+
import { DashboardListingProps } from './types';
2324
// import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view';
2425
jest.mock('@kbn/content-management-table-list-view-table', () => {
2526
const originalModule = jest.requireActual('@kbn/content-management-table-list-view-table');

src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx

Lines changed: 19 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,25 @@
77
*/
88

99
import { FormattedRelative, I18nProvider } from '@kbn/i18n-react';
10-
import React, { PropsWithChildren, useCallback, useMemo, useState } from 'react';
10+
import React, { useMemo } from 'react';
1111

1212
import {
1313
type TableListViewKibanaDependencies,
1414
TableListViewKibanaProvider,
15-
type UserContentCommonSchema,
1615
} from '@kbn/content-management-table-list-view-table';
1716
import { TableListView } from '@kbn/content-management-table-list-view';
18-
import { ViewMode } from '@kbn/embeddable-plugin/public';
19-
import { reportPerformanceMetricEvent } from '@kbn/ebt-tools';
20-
import type { SavedObjectsFindOptionsReference } from '@kbn/core/public';
17+
2118
import { toMountPoint, useExecutionContext } from '@kbn/kibana-react-plugin/public';
2219

23-
import {
24-
DASHBOARD_CONTENT_ID,
25-
SAVED_OBJECT_DELETE_TIME,
26-
SAVED_OBJECT_LOADED_TIME,
27-
} from '../dashboard_constants';
28-
import {
29-
dashboardListingTableStrings,
30-
dashboardListingErrorStrings,
31-
} from './_dashboard_listing_strings';
3220
import { pluginServices } from '../services/plugin_services';
33-
import { confirmCreateWithUnsaved } from './confirm_overlays';
34-
import { DashboardItem } from '../../common/content_management';
35-
import { DashboardUnsavedListing } from './dashboard_unsaved_listing';
36-
import { DashboardApplicationService } from '../services/application/types';
37-
import { DashboardListingEmptyPrompt } from './dashboard_listing_empty_prompt';
38-
39-
// because the type of `application.capabilities.advancedSettings` is so generic, the provider
40-
// requiring the `save` key to be part of it is causing type issues - so, creating a custom type
41-
type TableListViewApplicationService = DashboardApplicationService & {
42-
capabilities: { advancedSettings: { save: boolean } };
43-
};
44-
45-
const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit';
46-
const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage';
47-
48-
interface DashboardSavedObjectUserContent extends UserContentCommonSchema {
49-
attributes: {
50-
title: string;
51-
description?: string;
52-
timeRestore: boolean;
53-
};
54-
}
5521

56-
const toTableListViewSavedObject = (hit: DashboardItem): DashboardSavedObjectUserContent => {
57-
const { title, description, timeRestore } = hit.attributes;
58-
return {
59-
type: 'dashboard',
60-
id: hit.id,
61-
updatedAt: hit.updatedAt!,
62-
references: hit.references,
63-
attributes: {
64-
title,
65-
description,
66-
timeRestore,
67-
},
68-
};
69-
};
70-
71-
export type DashboardListingProps = PropsWithChildren<{
72-
initialFilter?: string;
73-
useSessionStorageIntegration?: boolean;
74-
goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void;
75-
getDashboardUrl: (dashboardId: string, usesTimeRestore: boolean) => string;
76-
}>;
22+
import { DashboardUnsavedListing } from './dashboard_unsaved_listing';
23+
import { useDashboardListingTable } from './hooks/use_dashboard_listing_table';
24+
import {
25+
DashboardListingProps,
26+
DashboardSavedObjectUserContent,
27+
TableListViewApplicationService,
28+
} from './types';
7729

7830
export const DashboardListing = ({
7931
children,
@@ -89,123 +41,22 @@ export const DashboardListing = ({
8941
http,
9042
chrome: { theme },
9143
savedObjectsTagging,
92-
dashboardSessionStorage,
93-
settings: { uiSettings },
94-
notifications: { toasts },
44+
9545
coreContext: { executionContext },
96-
dashboardCapabilities: { showWriteControls },
97-
dashboardContentManagement: { findDashboards, deleteDashboards },
9846
} = pluginServices.getServices();
9947

100-
const [unsavedDashboardIds, setUnsavedDashboardIds] = useState<string[]>(
101-
dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()
102-
);
103-
10448
useExecutionContext(executionContext, {
10549
type: 'application',
10650
page: 'list',
10751
});
10852

109-
const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING);
110-
const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING);
111-
112-
const createItem = useCallback(() => {
113-
if (useSessionStorageIntegration && dashboardSessionStorage.dashboardHasUnsavedEdits()) {
114-
confirmCreateWithUnsaved(() => {
115-
dashboardSessionStorage.clearState();
116-
goToDashboard();
117-
}, goToDashboard);
118-
return;
119-
}
120-
goToDashboard();
121-
}, [dashboardSessionStorage, goToDashboard, useSessionStorageIntegration]);
122-
123-
const fetchItems = useCallback(
124-
(
125-
searchTerm: string,
126-
{
127-
references,
128-
referencesToExclude,
129-
}: {
130-
references?: SavedObjectsFindOptionsReference[];
131-
referencesToExclude?: SavedObjectsFindOptionsReference[];
132-
} = {}
133-
) => {
134-
const searchStartTime = window.performance.now();
135-
136-
return findDashboards
137-
.search({
138-
search: searchTerm,
139-
size: listingLimit,
140-
hasReference: references,
141-
hasNoReference: referencesToExclude,
142-
})
143-
.then(({ total, hits }) => {
144-
const searchEndTime = window.performance.now();
145-
const searchDuration = searchEndTime - searchStartTime;
146-
reportPerformanceMetricEvent(pluginServices.getServices().analytics, {
147-
eventName: SAVED_OBJECT_LOADED_TIME,
148-
duration: searchDuration,
149-
meta: {
150-
saved_object_type: DASHBOARD_CONTENT_ID,
151-
},
152-
});
153-
return {
154-
total,
155-
hits: hits.map(toTableListViewSavedObject),
156-
};
157-
});
158-
},
159-
[findDashboards, listingLimit]
160-
);
161-
162-
const deleteItems = useCallback(
163-
async (dashboardsToDelete: Array<{ id: string }>) => {
164-
try {
165-
const deleteStartTime = window.performance.now();
166-
167-
await deleteDashboards(
168-
dashboardsToDelete.map(({ id }) => {
169-
dashboardSessionStorage.clearState(id);
170-
return id;
171-
})
172-
);
173-
174-
const deleteDuration = window.performance.now() - deleteStartTime;
175-
reportPerformanceMetricEvent(pluginServices.getServices().analytics, {
176-
eventName: SAVED_OBJECT_DELETE_TIME,
177-
duration: deleteDuration,
178-
meta: {
179-
saved_object_type: DASHBOARD_CONTENT_ID,
180-
total: dashboardsToDelete.length,
181-
},
182-
});
183-
} catch (error) {
184-
toasts.addError(error, {
185-
title: dashboardListingErrorStrings.getErrorDeletingDashboardToast(),
186-
});
187-
}
188-
189-
setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges());
190-
},
191-
[dashboardSessionStorage, deleteDashboards, toasts]
192-
);
193-
194-
const editItem = useCallback(
195-
({ id }: { id: string | undefined }) => goToDashboard(id, ViewMode.EDIT),
196-
[goToDashboard]
197-
);
198-
const emptyPrompt = (
199-
<DashboardListingEmptyPrompt
200-
createItem={createItem}
201-
goToDashboard={goToDashboard}
202-
unsavedDashboardIds={unsavedDashboardIds}
203-
setUnsavedDashboardIds={setUnsavedDashboardIds}
204-
useSessionStorageIntegration={useSessionStorageIntegration}
205-
/>
206-
);
207-
208-
const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTableStrings;
53+
const { unsavedDashboardIds, refreshUnsavedDashboards, tableListViewTableProps } =
54+
useDashboardListingTable({
55+
goToDashboard,
56+
getDashboardUrl,
57+
useSessionStorageIntegration,
58+
initialFilter,
59+
});
20960

21061
const savedObjectsTaggingFakePlugin = useMemo(() => {
21162
return savedObjectsTagging.hasApi // TODO: clean up this logic once https://github.com/elastic/kibana/issues/140433 is resolved
@@ -231,32 +82,13 @@ export const DashboardListing = ({
23182
FormattedRelative,
23283
}}
23384
>
234-
<TableListView<DashboardSavedObjectUserContent>
235-
getDetailViewLink={({ id, attributes: { timeRestore } }) =>
236-
getDashboardUrl(id, timeRestore)
237-
}
238-
deleteItems={!showWriteControls ? undefined : deleteItems}
239-
createItem={!showWriteControls ? undefined : createItem}
240-
editItem={!showWriteControls ? undefined : editItem}
241-
entityNamePlural={getEntityNamePlural()}
242-
title={getTableListTitle()}
243-
headingId="dashboardListingHeading"
244-
initialPageSize={initialPageSize}
245-
initialFilter={initialFilter}
246-
entityName={getEntityName()}
247-
listingLimit={listingLimit}
248-
emptyPrompt={emptyPrompt}
249-
findItems={fetchItems}
250-
id="dashboard"
251-
>
85+
<TableListView<DashboardSavedObjectUserContent> {...tableListViewTableProps}>
25286
<>
25387
{children}
25488
<DashboardUnsavedListing
25589
goToDashboard={goToDashboard}
25690
unsavedDashboardIds={unsavedDashboardIds}
257-
refreshUnsavedDashboards={() =>
258-
setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges())
259-
}
91+
refreshUnsavedDashboards={refreshUnsavedDashboards}
26092
/>
26193
</>
26294
</TableListView>

src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const makeDefaultProps = (): DashboardListingEmptyPromptProps => ({
3434
goToDashboard: jest.fn(),
3535
setUnsavedDashboardIds: jest.fn(),
3636
useSessionStorageIntegration: true,
37+
disableCreateDashboardButton: false,
3738
});
3839

3940
function mountWith({
@@ -75,6 +76,21 @@ test('renders empty prompt with link when showWriteControls is on', async () =>
7576
expect(component!.find('EuiLink').length).toBe(1);
7677
});
7778

79+
test('renders disabled action button when disableCreateDashboardButton is true', async () => {
80+
pluginServices.getServices().dashboardCapabilities.showWriteControls = true;
81+
82+
let component: ReactWrapper;
83+
await act(async () => {
84+
({ component } = mountWith({ props: { disableCreateDashboardButton: true } }));
85+
});
86+
87+
component!.update();
88+
89+
expect(component!.find(`[data-test-subj="newItemButton"]`).first().prop('disabled')).toEqual(
90+
true
91+
);
92+
});
93+
7894
test('renders continue button when no dashboards exist but one is in progress', async () => {
7995
pluginServices.getServices().dashboardCapabilities.showWriteControls = true;
8096
let component: ReactWrapper;

src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import {
2222
getNewDashboardTitle,
2323
dashboardUnsavedListingStrings,
2424
} from './_dashboard_listing_strings';
25-
import { DashboardListingProps } from './dashboard_listing';
2625
import { pluginServices } from '../services/plugin_services';
2726
import { confirmDiscardUnsavedChanges } from './confirm_overlays';
2827
import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_session_storage/dashboard_session_storage_service';
28+
import { DashboardListingProps } from './types';
2929

3030
export interface DashboardListingEmptyPromptProps {
3131
createItem: () => void;
32+
disableCreateDashboardButton?: boolean;
3233
unsavedDashboardIds: string[];
3334
goToDashboard: DashboardListingProps['goToDashboard'];
3435
setUnsavedDashboardIds: React.Dispatch<React.SetStateAction<string[]>>;
@@ -41,6 +42,7 @@ export const DashboardListingEmptyPrompt = ({
4142
unsavedDashboardIds,
4243
goToDashboard,
4344
createItem,
45+
disableCreateDashboardButton,
4446
}: DashboardListingEmptyPromptProps) => {
4547
const {
4648
application,
@@ -56,7 +58,13 @@ export const DashboardListingEmptyPrompt = ({
5658
const getEmptyAction = useCallback(() => {
5759
if (!isEditingFirstDashboard) {
5860
return (
59-
<EuiButton onClick={createItem} fill iconType="plusInCircle" data-test-subj="newItemButton">
61+
<EuiButton
62+
onClick={createItem}
63+
fill
64+
iconType="plusInCircle"
65+
data-test-subj="newItemButton"
66+
disabled={disableCreateDashboardButton}
67+
>
6068
{noItemsStrings.getCreateNewDashboardText()}
6169
</EuiButton>
6270
);
@@ -94,11 +102,12 @@ export const DashboardListingEmptyPrompt = ({
94102
</EuiFlexGroup>
95103
);
96104
}, [
97-
dashboardSessionStorage,
98105
isEditingFirstDashboard,
106+
createItem,
107+
disableCreateDashboardButton,
108+
dashboardSessionStorage,
99109
setUnsavedDashboardIds,
100110
goToDashboard,
101-
createItem,
102111
]);
103112

104113
if (!showWriteControls) {

0 commit comments

Comments
 (0)