From abaa00caecf328d253a5ec95f5cad6bbc90b7d33 Mon Sep 17 00:00:00 2001 From: Wroud Date: Wed, 17 Mar 2021 13:21:53 +0300 Subject: [PATCH] feat(plugin-data-viewer): CB-751 DataValuePanelService --- .../core-blocks/src/Tabs/TabsState.tsx | 8 ++- .../src/DataPresentationService.ts | 28 ++++++--- .../src/DataViewerTabService.ts | 2 +- .../src/TableViewer/TableGrid.tsx | 4 +- .../TablePresentationBar.tsx | 4 +- .../{ValuePanel.tsx => TableToolsPanel.tsx} | 23 ++----- .../src/TableViewer/TableViewer.tsx | 44 ++++++++----- .../ValuePanel/DataValuePanelBootstrap.ts | 40 ++++++++++++ .../ValuePanel/DataValuePanelService.ts | 45 ++++++++++++++ .../src/TableViewer/ValuePanel/ValuePanel.tsx | 61 +++++++++++++++++++ .../TextValue}/TextValuePresentation.tsx | 16 +++-- .../TextValuePresentationBootstrap.ts | 16 +++-- .../plugin-data-viewer/src/locales/en.ts | 1 + .../plugin-data-viewer/src/locales/ru.ts | 1 + .../plugin-data-viewer/src/manifest.ts | 6 +- 15 files changed, 235 insertions(+), 64 deletions(-) rename webapp/packages/plugin-data-viewer/src/TableViewer/{ValuePanel.tsx => TableToolsPanel.tsx} (69%) create mode 100644 webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelBootstrap.ts create mode 100644 webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelService.ts create mode 100644 webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/ValuePanel.tsx rename webapp/packages/plugin-data-viewer/src/{DataPresentation/TextValuePresentation => ValuePanelPresentation/TextValue}/TextValuePresentation.tsx (77%) rename webapp/packages/plugin-data-viewer/src/{DataPresentation/TextValuePresentation => ValuePanelPresentation/TextValue}/TextValuePresentationBootstrap.ts (53%) diff --git a/webapp/packages/core-blocks/src/Tabs/TabsState.tsx b/webapp/packages/core-blocks/src/Tabs/TabsState.tsx index 58e3aa4a55..f179df85e7 100644 --- a/webapp/packages/core-blocks/src/Tabs/TabsState.tsx +++ b/webapp/packages/core-blocks/src/Tabs/TabsState.tsx @@ -46,9 +46,12 @@ export const TabsState = observer(function TabsState>({ !selectedId && currentTabId === undefined && container - && container.tabInfoList.length > 0 ) { - selectedId = container.tabInfoList[0].key; + const displayed = container.getDisplayed(rest as T); + + if (displayed.length > 0) { + selectedId = displayed[0].key; + } } const [localTabsState] = useState(() => new MetadataMap()); @@ -79,7 +82,6 @@ export const TabsState = observer(function TabsState>({ if (currentTabId !== undefined) { state.selectedId = currentTabId; - state.setSelectedId(currentTabId); dynamic.selectedId = currentTabId; } diff --git a/webapp/packages/plugin-data-viewer/src/DataPresentationService.ts b/webapp/packages/plugin-data-viewer/src/DataPresentationService.ts index c2189cb9a5..a05e63832f 100644 --- a/webapp/packages/plugin-data-viewer/src/DataPresentationService.ts +++ b/webapp/packages/plugin-data-viewer/src/DataPresentationService.ts @@ -17,6 +17,7 @@ export interface IDataPresentationProps< TOptions = any, TResult extends IDatabaseDataResult = IDatabaseDataResult > { + dataFormat: ResultDataFormat; model: IDatabaseDataModel; resultIndex: number; className?: string; @@ -24,7 +25,7 @@ export interface IDataPresentationProps< export enum DataPresentationType { main, - value + toolsPanel } export type DataPresentationComponent< @@ -41,11 +42,15 @@ export type PresentationTabComponent = React.FunctionComponent boolean; + hidden?: ( + dataFormat: ResultDataFormat | null, + model: IDatabaseDataModel, + resultIndex: number + ) => boolean; getPresentationComponent: () => DataPresentationComponent; getTabComponent?: () => PresentationTabComponent; onActivate?: () => void; @@ -69,21 +74,26 @@ export class DataPresentationService { getSupportedList( type: DataPresentationType, - dataFormat: ResultDataFormat | ResultDataFormat[] + dataFormat: ResultDataFormat[], + model: IDatabaseDataModel, + resultIndex: number, ): IDataPresentation[] { return Array.from(this.dataPresentations.values()).filter(presentation => { - if (presentation.type !== type || presentation.hidden?.()) { + if (presentation.type !== type || presentation.hidden?.(null, model, resultIndex)) { return false; } - return presentation.dataFormat === dataFormat || dataFormat.includes(presentation.dataFormat); + return presentation.dataFormat === undefined + || dataFormat.includes(presentation.dataFormat); }); } getSupported( type: DataPresentationType, dataFormat: ResultDataFormat, - presentationId: string | undefined + presentationId: string | undefined, + model: IDatabaseDataModel, + resultIndex: number, ): IDataPresentation | null { if (presentationId) { const presentation = this.dataPresentations.get(presentationId); @@ -95,9 +105,9 @@ export class DataPresentationService { for (const presentation of this.dataPresentations.values()) { if ( - presentation.dataFormat === dataFormat + (presentation.dataFormat === undefined || presentation.dataFormat === dataFormat) && presentation.type === type - && !presentation.hidden?.() + && !presentation.hidden?.(dataFormat, model, resultIndex) ) { return presentation; } diff --git a/webapp/packages/plugin-data-viewer/src/DataViewerTabService.ts b/webapp/packages/plugin-data-viewer/src/DataViewerTabService.ts index 63edbcd538..ad30e92c89 100644 --- a/webapp/packages/plugin-data-viewer/src/DataViewerTabService.ts +++ b/webapp/packages/plugin-data-viewer/src/DataViewerTabService.ts @@ -111,7 +111,7 @@ export class DataViewerTabService { const pageState = this.page.getState(tab); if (pageState) { const presentation = this.dataPresentationService.get(pageState?.presentationId); - if (presentation) { + if (presentation?.dataFormat !== undefined) { model.setDataFormat(presentation.dataFormat); } } diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/TableGrid.tsx b/webapp/packages/plugin-data-viewer/src/TableViewer/TableGrid.tsx index 26e16d5709..955dce47b3 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/TableGrid.tsx +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TableGrid.tsx @@ -40,7 +40,7 @@ export const TableGrid = observer(function TableGrid({ const translate = useTranslate(); const result = model.getResult(resultIndex); - if (dataFormat !== presentation.dataFormat) { + if (presentation.dataFormat !== undefined && dataFormat !== presentation.dataFormat) { if (model.isLoading()) { return null; } @@ -56,6 +56,6 @@ export const TableGrid = observer(function TableGrid({ } return styled(styles)( - + ); }); diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/TablePresentationBar/TablePresentationBar.tsx b/webapp/packages/plugin-data-viewer/src/TableViewer/TablePresentationBar/TablePresentationBar.tsx index 9ac7cb6cde..837b2bd361 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/TablePresentationBar/TablePresentationBar.tsx +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TablePresentationBar/TablePresentationBar.tsx @@ -52,6 +52,7 @@ interface Props { presentationId: string | null | undefined; supportedDataFormat: ResultDataFormat[]; model: IDatabaseDataModel; + resultIndex: number; className?: string; onPresentationChange: (id: string) => void; } @@ -61,11 +62,12 @@ export const TablePresentationBar = observer(function TablePresentationBar({ presentationId, supportedDataFormat, model, + resultIndex, className, onPresentationChange, }: Props) { const dataPresentationService = useService(DataPresentationService); - const presentations = dataPresentationService.getSupportedList(type, supportedDataFormat); + const presentations = dataPresentationService.getSupportedList(type, supportedDataFormat, model, resultIndex); const Tab = PresentationTab; // alias for styles matching const changePresentation = ({ tabId }: ITabData) => onPresentationChange(tabId); diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel.tsx b/webapp/packages/plugin-data-viewer/src/TableViewer/TableToolsPanel.tsx similarity index 69% rename from webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel.tsx rename to webapp/packages/plugin-data-viewer/src/TableViewer/TableToolsPanel.tsx index 97e7a34b56..cfcb31f8c1 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel.tsx +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TableToolsPanel.tsx @@ -11,17 +11,16 @@ import type { PropsWithChildren } from 'react'; import styled, { css } from 'reshadow'; import { TextPlaceholder } from '@cloudbeaver/core-blocks'; -import { useService } from '@cloudbeaver/core-di'; import { useTranslate } from '@cloudbeaver/core-localization'; import type { ResultDataFormat } from '@cloudbeaver/core-sdk'; -import { DataPresentationService, DataPresentationType } from '../DataPresentationService'; +import type { IDataPresentationOptions } from '../DataPresentationService'; import type { DataModelWrapper } from './DataModelWrapper'; type TableGridProps = PropsWithChildren<{ model: DataModelWrapper; // TODO: change to IDatabaseDataModel dataFormat: ResultDataFormat; - presentationId: string | null; + presentation: IDataPresentationOptions | null; resultIndex: number; }>; @@ -32,27 +31,17 @@ const styles = css` } `; -export const ValuePanel = observer(function ValuePanel({ +export const TableToolsPanel = observer(function TableToolsPanel({ model, dataFormat, - presentationId, + presentation, resultIndex, }: TableGridProps) { const translate = useTranslate(); - const dataPresentationService = useService(DataPresentationService); - if (!presentationId) { - return null; - } - - const presentation = dataPresentationService.getSupported( - DataPresentationType.value, - dataFormat, - presentationId - ); const result = model.getResult(resultIndex); - if (!presentation || dataFormat !== presentation.dataFormat) { + if (!presentation || (presentation.dataFormat !== undefined && dataFormat !== presentation.dataFormat)) { if (model.isLoading()) { return null; } @@ -68,6 +57,6 @@ export const ValuePanel = observer(function ValuePanel({ } return styled(styles)( - + ); }); diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewer.tsx b/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewer.tsx index ceab357f31..5ee151493b 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewer.tsx +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewer.tsx @@ -21,8 +21,8 @@ import { TableFooter } from './TableFooter/TableFooter'; import { TableGrid } from './TableGrid'; import { TableHeader } from './TableHeader/TableHeader'; import { TablePresentationBar } from './TablePresentationBar/TablePresentationBar'; +import { TableToolsPanel } from './TableToolsPanel'; import { TableViewerStorageService } from './TableViewerStorageService'; -import { ValuePanel } from './ValuePanel'; const viewerStyles = composes( css` @@ -113,7 +113,10 @@ export const TableViewer = observer(function TableViewer({ const handlePresentationChange = useCallback((id: string) => { const presentation = dataPresentationService.get(id); if (presentation) { - if (presentation.dataFormat !== dataModel?.source.dataFormat) { + if ( + presentation.dataFormat !== undefined + && presentation.dataFormat !== dataModel?.source.dataFormat + ) { dataModel?.setDataFormat(presentation.dataFormat) .reload(); } @@ -141,21 +144,28 @@ export const TableViewer = observer(function TableViewer({ const presentation = dataPresentationService.getSupported( DataPresentationType.main, dataFormat, - presentationId + presentationId, + dataModel, + resultIndex ); - const valuePresentation = valuePresentationId - ? dataPresentationService.getSupported( - DataPresentationType.value, - dataFormat, - valuePresentationId - ) - : null; - if (!presentation) { return There are no available presentation for data format: {dataFormat}; } + const valuePresentation = valuePresentationId + ? dataPresentationService.getSupported( + DataPresentationType.toolsPanel, + dataFormat, + valuePresentationId, + dataModel, + resultIndex + ) + : null; + + const valuePanelDisplayed = valuePresentation + && (valuePresentation.dataFormat === undefined + || valuePresentation?.dataFormat === dataFormat); const overlay = dataModel.source.results.length > 0 && presentation.dataFormat === dataFormat; const loading = dataModel.isLoading(); @@ -168,12 +178,13 @@ export const TableViewer = observer(function TableViewer({ presentationId={presentation.id} supportedDataFormat={dataModel.supportedDataFormats} model={dataModel} + resultIndex={resultIndex} onPresentationChange={handlePresentationChange} /> {(overlay || !loading) && ( - + {dataModel.source.hasResult(resultIndex) && ( - {valuePresentation?.dataFormat === dataFormat && } + {valuePanelDisplayed && } - @@ -216,10 +227,11 @@ export const TableViewer = observer(function TableViewer({ diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelBootstrap.ts b/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelBootstrap.ts new file mode 100644 index 0000000000..0e33f33d9b --- /dev/null +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelBootstrap.ts @@ -0,0 +1,40 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; + +import { DataPresentationService, DataPresentationType } from '../../DataPresentationService'; +import { DataValuePanelService } from './DataValuePanelService'; +import { ValuePanel } from './ValuePanel'; + +@injectable() +export class DataValuePanelBootstrap extends Bootstrap { + constructor( + private readonly dataPresentationService: DataPresentationService, + private readonly dataValuePanelService: DataValuePanelService + ) { + super(); + } + + register(): void | Promise { + this.dataPresentationService.add({ + id: 'value-text-presentation', + type: DataPresentationType.toolsPanel, + title: 'data_viewer_presentation_value_title', + icon: '/icons/text_value_presentation.png', + hidden: ( + dataFormat, + model, + resultIndex + ) => this.dataValuePanelService.getDisplayed({ model, resultIndex, dataFormat }).length === 0, + getPresentationComponent: () => ValuePanel, + }); + } + + load(): void {} +} diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelService.ts b/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelService.ts new file mode 100644 index 0000000000..c64c7e9dca --- /dev/null +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/DataValuePanelService.ts @@ -0,0 +1,45 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { ITabInfo, ITabInfoOptions, TabsContainer } from '@cloudbeaver/core-blocks'; +import { injectable } from '@cloudbeaver/core-di'; +import type { ResultDataFormat } from '@cloudbeaver/core-sdk'; + +import type { IDatabaseDataModel } from '../../DatabaseDataModel/IDatabaseDataModel'; +import type { IDatabaseDataResult } from '../../DatabaseDataModel/IDatabaseDataResult'; + +export interface IDataValuePanelOptions { + dataFormat: ResultDataFormat[]; +} + +export interface IDataValuePanelProps { + dataFormat: ResultDataFormat | null; + model: IDatabaseDataModel; + resultIndex: number; +} + +@injectable() +export class DataValuePanelService { + readonly tabs: TabsContainer, IDataValuePanelOptions>; + + constructor() { + this.tabs = new TabsContainer(); + } + + get(tabId: string): ITabInfo, IDataValuePanelOptions> | undefined { + return this.tabs.getTabInfo(tabId); + } + + getDisplayed(props?: IDataValuePanelProps): Array, IDataValuePanelOptions>> { + return this.tabs.getDisplayed(props); + } + + add(tabInfo: ITabInfoOptions, IDataValuePanelOptions>): void { + this.tabs.add(tabInfo); + } +} diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/ValuePanel.tsx b/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/ValuePanel.tsx new file mode 100644 index 0000000000..e295b68aec --- /dev/null +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/ValuePanel/ValuePanel.tsx @@ -0,0 +1,61 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { observer } from 'mobx-react-lite'; +import styled, { css } from 'reshadow'; + +import { TabList, TabPanelList, TabsState, UNDERLINE_TAB_STYLES } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; +import { composes, useStyles } from '@cloudbeaver/core-theming'; + +import type { IDatabaseResultSet } from '../../DatabaseDataModel/IDatabaseResultSet'; +import type { DataPresentationComponent } from '../../DataPresentationService'; +import { DataValuePanelService } from './DataValuePanelService'; + +const styles = composes( + css` + Tab { + composes: theme-ripple from global; + } + TabList { + composes: theme-background-surface theme-text-on-surface from global; + } + `, + css` + table-left-bar { + display: flex; + } + Tab { + composes: theme-typography--body2 from global; + text-transform: uppercase; + font-weight: normal; + + &:global([aria-selected=true]) { + font-weight: normal !important; + } + } + TabList tab-outer:only-child { + display: none; + } + ` +); + +export const ValuePanel: DataPresentationComponent = observer(function ValuePanel({ + dataFormat, + model, + resultIndex, +}) { + const service = useService(DataValuePanelService); + + return styled(useStyles(styles, UNDERLINE_TAB_STYLES))( + + + + + ); +}); diff --git a/webapp/packages/plugin-data-viewer/src/DataPresentation/TextValuePresentation/TextValuePresentation.tsx b/webapp/packages/plugin-data-viewer/src/ValuePanelPresentation/TextValue/TextValuePresentation.tsx similarity index 77% rename from webapp/packages/plugin-data-viewer/src/DataPresentation/TextValuePresentation/TextValuePresentation.tsx rename to webapp/packages/plugin-data-viewer/src/ValuePanelPresentation/TextValue/TextValuePresentation.tsx index f5cfe457a3..a6982bed65 100644 --- a/webapp/packages/plugin-data-viewer/src/DataPresentation/TextValuePresentation/TextValuePresentation.tsx +++ b/webapp/packages/plugin-data-viewer/src/ValuePanelPresentation/TextValue/TextValuePresentation.tsx @@ -9,19 +9,25 @@ import { observer } from 'mobx-react-lite'; import styled from 'reshadow'; -import { BASE_CONTAINERS_STYLES, TextareaNew } from '@cloudbeaver/core-blocks'; +import { BASE_CONTAINERS_STYLES, TabContainerPanelComponent, TextareaNew } from '@cloudbeaver/core-blocks'; import { useStyles } from '@cloudbeaver/core-theming'; +import { css } from '@reshadow/react'; import { ResultSetFormatAction } from '../../DatabaseDataModel/Actions/ResultSetFormatAction'; import { IResultSetSelectKey, ResultSetSelectAction } from '../../DatabaseDataModel/Actions/ResultSetSelectAction'; import type { IDatabaseResultSet } from '../../DatabaseDataModel/IDatabaseResultSet'; -import type { DataPresentationComponent } from '../../DataPresentationService'; +import type { IDataValuePanelProps } from '../../TableViewer/ValuePanel/DataValuePanelService'; -export const TextValuePresentation: DataPresentationComponent = observer(function TextValuePresentation({ +const styles = css` + TextareaNew { + flex: 1; + } +`; + +export const TextValuePresentation: TabContainerPanelComponent> = observer(function TextValuePresentation({ model, resultIndex, }) { - const styles = useStyles(BASE_CONTAINERS_STYLES); const selection = model.source.getAction(resultIndex, ResultSetSelectAction); const result = model.getResult(resultIndex); @@ -50,7 +56,7 @@ export const TextValuePresentation: DataPresentationComponent { - this.dataPresentationService.add({ - id: 'value-text-presentation', - dataFormat: ResultDataFormat.Resultset, - type: DataPresentationType.value, - title: 'data_viewer_presentation_value_title', - icon: '/icons/text_value_presentation.png', - getPresentationComponent: () => TextValuePresentation, + this.dataValuePanelService.add({ + key: 'text-presentation', + options: { dataFormat: [ResultDataFormat.Resultset] }, + name: 'data_viewer_presentation_value_text_title', + panel: () => TextValuePresentation, }); } diff --git a/webapp/packages/plugin-data-viewer/src/locales/en.ts b/webapp/packages/plugin-data-viewer/src/locales/en.ts index 600f73ef4a..1a5e2cdb8a 100644 --- a/webapp/packages/plugin-data-viewer/src/locales/en.ts +++ b/webapp/packages/plugin-data-viewer/src/locales/en.ts @@ -2,4 +2,5 @@ export default [ ['table_header_sql_expression', 'Enter a SQL expression to filter results'], ['data_viewer_nodata_message', 'No data to show'], ['data_viewer_presentation_value_title', 'Value'], + ['data_viewer_presentation_value_text_title', 'Text'], ]; diff --git a/webapp/packages/plugin-data-viewer/src/locales/ru.ts b/webapp/packages/plugin-data-viewer/src/locales/ru.ts index d89106e015..83217e7934 100644 --- a/webapp/packages/plugin-data-viewer/src/locales/ru.ts +++ b/webapp/packages/plugin-data-viewer/src/locales/ru.ts @@ -2,4 +2,5 @@ export default [ ['table_header_sql_expression', 'Введите SQL выражение чтобы отфильтровать результаты'], ['data_viewer_nodata_message', 'Нет данных для отображения'], ['data_viewer_presentation_value_title', 'Значение'], + ['data_viewer_presentation_value_text_title', 'Текст'], ]; diff --git a/webapp/packages/plugin-data-viewer/src/manifest.ts b/webapp/packages/plugin-data-viewer/src/manifest.ts index f0f90e86e4..59cba0c1df 100644 --- a/webapp/packages/plugin-data-viewer/src/manifest.ts +++ b/webapp/packages/plugin-data-viewer/src/manifest.ts @@ -8,7 +8,6 @@ import type { PluginManifest } from '@cloudbeaver/core-di'; -import { TextValuePresentationBootstrap } from './DataPresentation/TextValuePresentation/TextValuePresentationBootstrap'; import { DataPresentationService } from './DataPresentationService'; import { DataViewerBootstrap } from './DataViewerBootstrap'; import { DataViewerTableService } from './DataViewerTableService'; @@ -17,6 +16,9 @@ import { LocaleService } from './LocaleService'; import { TableFooterMenuService } from './TableViewer/TableFooter/TableFooterMenu/TableFooterMenuService'; import { TableHeaderService } from './TableViewer/TableHeader/TableHeaderService'; import { TableViewerStorageService } from './TableViewer/TableViewerStorageService'; +import { DataValuePanelBootstrap } from './TableViewer/ValuePanel/DataValuePanelBootstrap'; +import { DataValuePanelService } from './TableViewer/ValuePanel/DataValuePanelService'; +import { TextValuePresentationBootstrap } from './ValuePanelPresentation/TextValue/TextValuePresentationBootstrap'; export const manifest: PluginManifest = { info: { @@ -32,6 +34,8 @@ export const manifest: PluginManifest = { TableFooterMenuService, TableHeaderService, LocaleService, + DataValuePanelService, TextValuePresentationBootstrap, + DataValuePanelBootstrap, ], };