Skip to content
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

[Table list view] Integrate package into visualizations, maps, graphs #140040

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1c1ba58
Integrate package into visualizations plugin
sebelga Sep 5, 2022
93b7d66
Integrate package into maps plugin
sebelga Sep 5, 2022
06c3b44
Add unique id to tables
sebelga Sep 6, 2022
b4b610d
Fix i18n issue
sebelga Sep 6, 2022
dc56612
Integrate package into graph plugin
sebelga Sep 6, 2022
1fa743e
Fix missing description attribute
sebelga Sep 6, 2022
7296a66
Update findMaps() handler
sebelga Sep 6, 2022
732d577
Fix i18n issue
sebelga Sep 6, 2022
4314565
Add "onClickTitle" prop to navigate to details view
sebelga Sep 6, 2022
cef4d8a
Remove component from kibana-react plugin
sebelga Sep 6, 2022
8287766
Fix visualisation editItem handler
sebelga Sep 7, 2022
3d6db3c
Add unique id to graph table
sebelga Sep 7, 2022
65f97fe
Fix i18n issue
sebelga Sep 7, 2022
7703f5c
Fix link rendering when vis type is unknown
sebelga Sep 7, 2022
70ed7d3
Fix TS issue
sebelga Sep 7, 2022
b3e5737
Remove getApplication from maps kibana services
sebelga Sep 7, 2022
8faa861
Merge branch 'table-list-view/enhance-ux' into table-list-view/integr…
kibanamachine Sep 8, 2022
73b6ba9
Use RedirectAppLinks to prevent full page reload
sebelga Sep 8, 2022
2e54695
Merge branch 'table-list-view/integrate-package-vis-map-graph' of git…
sebelga Sep 8, 2022
9c032e9
Merge branch 'table-list-view/enhance-ux' into table-list-view/integr…
kibanamachine Sep 8, 2022
36365fa
Fix TS issues
sebelga Sep 8, 2022
510aa18
Merge branch 'table-list-view/integrate-package-vis-map-graph' of git…
sebelga Sep 8, 2022
411bb6c
Update to the new EuiPageTemplate component
sebelga Sep 8, 2022
737a4f7
Fix functional tests
sebelga Sep 9, 2022
656eb9c
Merge branch 'table-list-view/enhance-ux' into table-list-view/integr…
kibanamachine Sep 13, 2022
ddfe96e
[CI] Auto-commit changed files from 'node scripts/generate packages_b…
kibanamachine Sep 13, 2022
435c721
Fix functional tests
sebelga Sep 13, 2022
b91722c
Merge branch 'table-list-view/integrate-package-vis-map-graph' of git…
sebelga Sep 13, 2022
636b2d2
Merge branch 'table-list-view/enhance-ux' into table-list-view/integr…
kibanamachine Sep 13, 2022
1195d6c
Revert "[CI] Auto-commit changed files from 'node scripts/generate pa…
sebelga Sep 13, 2022
a2a3e9a
Merge branch 'table-list-view/integrate-package-vis-map-graph' of git…
sebelga Sep 13, 2022
cb1e243
Add kibana.jsonc
sebelga Sep 13, 2022
5697063
[CI] Auto-commit changed files from 'node scripts/generate codeowners'
kibanamachine Sep 13, 2022
42421b3
Merge branch 'table-list-view/enhance-ux' into table-list-view/integr…
kibanamachine Sep 13, 2022
4a030f7
Merge branch 'table-list-view/enhance-ux' into table-list-view/integr…
kibanamachine Sep 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ packages/analytics/shippers/elastic_v3/browser @elastic/kibana-core
packages/analytics/shippers/elastic_v3/common @elastic/kibana-core
packages/analytics/shippers/elastic_v3/server @elastic/kibana-core
packages/analytics/shippers/fullstory @elastic/kibana-core
packages/content-management/table_list @elastic/shared-ux
packages/core/analytics/core-analytics-browser @elastic/kibana-core
packages/core/analytics/core-analytics-browser-internal @elastic/kibana-core
packages/core/analytics/core-analytics-browser-mocks @elastic/kibana-core
Expand Down
7 changes: 7 additions & 0 deletions packages/content-management/table_list/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "shared-common",
"id": "@kbn/content-management-table-list",
"owner": "@elastic/shared-ux",
"runtimeDeps": [],
"typeDeps": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
*/
import React from 'react';
import type { ComponentType } from 'react';
import { from } from 'rxjs';

import { TableListViewProvider, Services } from '../services';

export const getMockServices = (overrides?: Partial<Services>) => {
const services: Services = {
canEditAdvancedSettings: true,
getListingLimitSettingsUrl: () => 'http://elastic.co',
notifyError: () => undefined,
currentAppId$: from('mockedApp'),
navigateToUrl: () => undefined,
...overrides,
};

Expand Down
4 changes: 4 additions & 0 deletions packages/content-management/table_list/src/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { from } from 'rxjs';

import { Services } from './services';

/**
Expand All @@ -23,6 +25,8 @@ export const getStoryServices = (params: Params, action: ActionFn = () => {}) =>
notifyError: (title, text) => {
action('notifyError')({ title, text });
},
currentAppId$: from('mockedApp'),
navigateToUrl: () => undefined,
...params,
};

Expand Down
43 changes: 26 additions & 17 deletions packages/content-management/table_list/src/services.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React, { FC, useContext, useMemo } from 'react';
import type { EuiTableFieldDataColumnType, SearchFilterConfig } from '@elastic/eui';
import type { Observable } from 'rxjs';
import type { FormattedRelative } from '@kbn/i18n-react';
import { RedirectAppLinksKibanaProvider } from '@kbn/shared-ux-link-redirect-app';

import { UserContentCommonSchema } from './table_list_view';

Expand Down Expand Up @@ -37,6 +38,8 @@ export interface Services {
canEditAdvancedSettings: boolean;
getListingLimitSettingsUrl: () => string;
notifyError: NotifyFn;
currentAppId$: Observable<string | undefined>;
navigateToUrl: (url: string) => Promise<void> | void;
searchQueryParser?: (searchQuery: string) => {
searchQuery: string;
references?: SavedObjectsFindOptionsReference[];
Expand Down Expand Up @@ -68,6 +71,8 @@ export interface TableListViewKibanaDependencies {
};
};
getUrlForApp: (app: string, options: { path: string }) => string;
currentAppId$: Observable<string | undefined>;
navigateToUrl: (url: string) => Promise<void> | void;
};
notifications: {
toasts: {
Expand Down Expand Up @@ -146,23 +151,27 @@ export const TableListViewKibanaProvider: FC<TableListViewKibanaDependencies> =
}, [savedObjectsTagging]);

return (
<TableListViewProvider
canEditAdvancedSettings={Boolean(core.application.capabilities.advancedSettings?.save)}
getListingLimitSettingsUrl={() =>
core.application.getUrlForApp('management', {
path: `/kibana/settings?query=savedObjects:listingLimit`,
})
}
notifyError={(title, text) => {
core.notifications.toasts.addDanger({ title: toMountPoint(title), text });
}}
getTagsColumnDefinition={savedObjectsTagging?.ui.getTableColumnDefinition}
getSearchBarFilters={getSearchBarFilters}
searchQueryParser={searchQueryParser}
DateFormatterComp={(props) => <FormattedRelative {...props} />}
>
{children}
</TableListViewProvider>
<RedirectAppLinksKibanaProvider coreStart={core}>
<TableListViewProvider
canEditAdvancedSettings={Boolean(core.application.capabilities.advancedSettings?.save)}
getListingLimitSettingsUrl={() =>
core.application.getUrlForApp('management', {
path: `/kibana/settings?query=savedObjects:listingLimit`,
})
}
notifyError={(title, text) => {
core.notifications.toasts.addDanger({ title: toMountPoint(title), text });
}}
getTagsColumnDefinition={savedObjectsTagging?.ui.getTableColumnDefinition}
getSearchBarFilters={getSearchBarFilters}
searchQueryParser={searchQueryParser}
DateFormatterComp={(props) => <FormattedRelative {...props} />}
currentAppId$={core.application.currentAppId$}
navigateToUrl={core.application.navigateToUrl}
>
{children}
</TableListViewProvider>
</RedirectAppLinksKibanaProvider>
);
};

Expand Down
193 changes: 124 additions & 69 deletions packages/content-management/table_list/src/table_list_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
* Side Public License, v 1.
*/

import React, { useReducer, useCallback, useEffect, useRef, useMemo, ReactNode } from 'react';
import React, {
useReducer,
useCallback,
useEffect,
useRef,
useMemo,
ReactNode,
MouseEvent,
} from 'react';
import useDebounce from 'react-use/lib/useDebounce';
import {
EuiBasicTableColumn,
Expand All @@ -24,6 +32,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';

import { Table, ConfirmDeleteModal, ListingLimitWarning } from './components';
import { useServices } from './services';
Expand Down Expand Up @@ -53,7 +62,10 @@ export interface Props<T extends UserContentCommonSchema = UserContentCommonSche
searchQuery: string,
references?: SavedObjectsFindOptionsReference[]
): Promise<{ total: number; hits: T[] }>;
getDetailViewLink(entity: T): string;
/** Handler to set the item title "href" value. If it returns undefined there won't be a link for this item. */
getDetailViewLink?: (entity: T) => string | undefined;
/** Handler to execute when clicking the item title */
onClickTitle?: (item: T) => void;
createItem?(): void;
deleteItems?(items: T[]): Promise<void>;
editItem?(item: T): void;
Expand Down Expand Up @@ -103,9 +115,22 @@ function TableListViewComp<T extends UserContentCommonSchema>({
editItem,
deleteItems,
getDetailViewLink,
onClickTitle,
id = 'userContent',
children,
}: Props<T>) {
if (!getDetailViewLink && !onClickTitle) {
throw new Error(
`[TableListView] One o["getDetailViewLink" or "onClickTitle"] prop must be provided.`
);
}

if (getDetailViewLink && onClickTitle) {
throw new Error(
`[TableListView] Either "getDetailViewLink" or "onClickTitle" can be provided. Not both.`
);
}

const isMounted = useRef(false);
const fetchIdx = useRef(0);

Expand All @@ -116,12 +141,24 @@ function TableListViewComp<T extends UserContentCommonSchema>({
searchQueryParser,
notifyError,
DateFormatterComp,
navigateToUrl,
currentAppId$,
} = useServices();

const reducer = useMemo(() => {
return getReducer<T>({ DateFormatterComp });
}, [DateFormatterComp]);

const redirectAppLinksCoreStart = useMemo(
() => ({
application: {
navigateToUrl,
currentAppId$,
},
}),
[navigateToUrl, currentAppId$]
);

const [state, dispatch] = useReducer<(state: State<T>, action: Action<T>) => State<T>>(reducer, {
items: [],
totalItems: 0,
Expand All @@ -137,14 +174,37 @@ function TableListViewComp<T extends UserContentCommonSchema>({
defaultMessage: 'Title',
}),
sortable: true,
render: (field: keyof T, record: T) => (
<EuiLink
href={getDetailViewLink(record)}
data-test-subj={`${id}ListingTitleLink-${record.attributes.title.split(' ').join('-')}`}
>
{record.attributes.title}
</EuiLink>
),
render: (field: keyof T, record: T) => {
// The validation is handled at the top of the component
const href = getDetailViewLink ? getDetailViewLink(record) : undefined;

if (!href && !onClickTitle) {
// This item is not clickable
return <span>{record.attributes.title}</span>;
}

return (
<RedirectAppLinks coreStart={redirectAppLinksCoreStart}>
{/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
<EuiLink
href={getDetailViewLink ? getDetailViewLink(record) : undefined}
onClick={
onClickTitle
? (e: MouseEvent) => {
e.preventDefault();
onClickTitle(record);
}
: undefined
}
data-test-subj={`${id}ListingTitleLink-${record.attributes.title
.split(' ')
.join('-')}`}
>
{record.attributes.title}
</EuiLink>
</RedirectAppLinks>
);
},
},
{
field: 'attributes.description',
Expand Down Expand Up @@ -391,74 +451,69 @@ function TableListViewComp<T extends UserContentCommonSchema>({

if (!fetchError && hasNoItems) {
return (
<KibanaPageTemplate
data-test-subj={pageDataTestSubject}
pageBodyProps={{
'aria-labelledby': hasInitialFetchReturned ? headingId : undefined,
}}
isEmptyState={true}
>
{renderNoItemsMessage()}
<KibanaPageTemplate panelled isEmptyState={true} data-test-subj={pageDataTestSubject}>
<KibanaPageTemplate.Section
aria-labelledby={hasInitialFetchReturned ? headingId : undefined}
>
{renderNoItemsMessage()}
</KibanaPageTemplate.Section>
</KibanaPageTemplate>
);
}

return (
<KibanaPageTemplate
data-test-subj={pageDataTestSubject}
pageHeader={{
pageTitle: <span id={headingId}>{tableListTitle}</span>,
rightSideItems: [renderCreateButton() ?? <span />],
'data-test-subj': 'top-nav',
}}
pageBodyProps={{
'aria-labelledby': hasInitialFetchReturned ? headingId : undefined,
}}
>
{/* Any children passed to the component */}
{children}

{/* Too many items error */}
{showLimitError && (
<ListingLimitWarning
canEditAdvancedSettings={canEditAdvancedSettings}
advancedSettingsLink={getListingLimitSettingsUrl()}
entityNamePlural={entityNamePlural}
totalItems={totalItems}
listingLimit={listingLimit}
/>
)}

{/* Error while fetching items */}
{showFetchError && renderFetchError()}

{/* Table of items */}
<Table<T>
dispatch={dispatch}
items={items}
isFetchingItems={isFetchingItems}
searchQuery={searchQuery}
tableColumns={tableColumns}
tableSort={tableSort}
pagination={pagination}
selectedIds={selectedIds}
entityName={entityName}
entityNamePlural={entityNamePlural}
deleteItems={deleteItems}
tableCaption={tableListTitle}
<KibanaPageTemplate panelled data-test-subj={pageDataTestSubject}>
<KibanaPageTemplate.Header
pageTitle={<span id={headingId}>{tableListTitle}</span>}
rightSideItems={[renderCreateButton() ?? <span />]}
data-test-subj="top-nav"
/>

{/* Delete modal */}
{showDeleteModal && (
<ConfirmDeleteModal<T>
isDeletingItems={isDeletingItems}
<KibanaPageTemplate.Section aria-labelledby={hasInitialFetchReturned ? headingId : undefined}>
{/* Any children passed to the component */}
{children}

{/* Too many items error */}
{showLimitError && (
<ListingLimitWarning
canEditAdvancedSettings={canEditAdvancedSettings}
advancedSettingsLink={getListingLimitSettingsUrl()}
entityNamePlural={entityNamePlural}
totalItems={totalItems}
listingLimit={listingLimit}
/>
)}

{/* Error while fetching items */}
{showFetchError && renderFetchError()}

{/* Table of items */}
<Table<T>
dispatch={dispatch}
items={items}
isFetchingItems={isFetchingItems}
searchQuery={searchQuery}
tableColumns={tableColumns}
tableSort={tableSort}
pagination={pagination}
selectedIds={selectedIds}
entityName={entityName}
entityNamePlural={entityNamePlural}
items={selectedItems}
onConfirm={deleteSelectedItems}
onCancel={() => dispatch({ type: 'onCancelDeleteItems' })}
deleteItems={deleteItems}
tableCaption={tableListTitle}
/>
)}

{/* Delete modal */}
{showDeleteModal && (
<ConfirmDeleteModal<T>
isDeletingItems={isDeletingItems}
entityName={entityName}
entityNamePlural={entityNamePlural}
items={selectedItems}
onConfirm={deleteSelectedItems}
onCancel={() => dispatch({ type: 'onCancelDeleteItems' })}
/>
)}
</KibanaPageTemplate.Section>
</KibanaPageTemplate>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage';
interface DashboardSavedObjectUserContent extends UserContentCommonSchema {
attributes: {
title: string;
description?: string;
timeRestore: boolean;
};
}
Expand All @@ -61,6 +62,7 @@ const toTableListViewSavedObject = (
type: 'dashboard',
attributes: {
title: (savedObject.title as string) ?? '',
description: savedObject.description as string,
timeRestore: savedObject.timeRestore as boolean,
},
};
Expand Down
Loading