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

[D&D] Refactor to use AggService and introduce metric visualization #1734

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const createEditorStateReducer = ({
!state.data.aggs!.aggs.find((agg) => agg.schema === schema.name) && schema.defaults
? (schema as any).defaults.slice(0, schema.max)
: { schema: schema.name };

const aggConfig = state.data.aggs!.createAggConfig(defaultConfig, {
addToAggConfigs: false,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function MetricVisOptions({
);

const setColorMode: EuiButtonGroupProps['onChange'] = useCallback(
(id) => setMetricValue('metricColorMode', id as ColorModes),
(id: string) => setMetricValue('metricColorMode', id as ColorModes),
[setMetricValue]
);

Expand Down
3 changes: 3 additions & 0 deletions src/plugins/vis_type_metric/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ import { MetricVisPlugin as Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
return new Plugin(initializerContext);
}

/* Public Types */
export { MetricVisExpressionFunctionDefinition } from './metric_vis_fn';
2 changes: 1 addition & 1 deletion src/plugins/vis_type_metric/public/metric_vis_fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ interface Arguments {
colorRange: Range[];
font: Style;
metric: any[]; // these aren't typed yet
bucket: any; // these aren't typed yet
bucket?: any; // these aren't typed yet
}

export interface MetricVisRenderValue {
Expand Down
12 changes: 7 additions & 5 deletions src/plugins/visualizations/public/legacy/build_pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,8 @@ export const buildVislibDimensions = async (vis: any, params: BuildPipelineParam
splitColumn: schemas.split_column,
};
if (schemas.segment) {
const xAgg = vis.data.aggs.getResponseAggs()[dimensions.x.accessor];
const a = vis.data.aggs.getResponseAggs();
const xAgg = a[dimensions.x.accessor];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ashwin-pc I am curious what is the advantage of writing xAgg in two lines?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh good catch, I missed this during cleanup. I was debugging this function and split it to understand the response object better. Ill recombine it for the follow up PR that cleans up the rest :)

if (xAgg.type.name === 'date_histogram') {
dimensions.x.params.date = true;
const { opensearchUnit, opensearchValue } = xAgg.buckets.getInterval();
Expand Down Expand Up @@ -423,10 +424,10 @@ export const buildPipeline = async (vis: Vis, params: BuildPipelineParams) => {
// request handler
if (vis.type.requestHandler === 'courier') {
pipeline += `opensearchaggs
${prepareString('index', indexPattern!.id)}
metricsAtAllLevels=${vis.isHierarchical()}
partialRows=${vis.params.showPartialRows || false}
${prepareJson('aggConfigs', vis.data.aggs!.aggs)} | `;
${prepareString('index', indexPattern!.id)}
metricsAtAllLevels=${vis.isHierarchical()}
partialRows=${vis.params.showPartialRows || false}
${prepareJson('aggConfigs', vis.data.aggs!.aggs)} | `;
}

const schemas = getSchemas(vis, params);
Expand Down Expand Up @@ -456,5 +457,6 @@ export const buildPipeline = async (vis: Vis, params: BuildPipelineParams) => {
}
}
}

return pipeline;
};
6 changes: 5 additions & 1 deletion src/plugins/wizard/opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
"ui": true,
"requiredPlugins": [
"navigation",
"charts",
"data",
"opensearchDashboardsReact",
"opensearchDashboardsUtils",
"savedObjects",
"embeddable",
"expressions",
"dashboard",
"visualizations",
"opensearchUiShared"
"opensearchUiShared",
"visDefaultEditor"
],
"optionalPlugins": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

.wizSidenav {
@include scrollNavParent(auto 1fr);

grid-area: sideNav;
border-right: $euiBorderThin;
}
Expand All @@ -11,8 +12,13 @@
}

.wizSidenavTabs {
.euiTab__content {
text-transform: capitalize;
}

@include scrollNavParent(min-content 1fr);
&>[role="tabpanel"] {

& > [role="tabpanel"] {
@include scrollNavParent;
}
}
40 changes: 30 additions & 10 deletions src/plugins/wizard/public/application/components/side_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react
import { WizardServices } from '../../types';
import './side_nav.scss';
import { useTypedDispatch, useTypedSelector } from '../utils/state_management';
import { setIndexPattern } from '../utils/state_management/datasource_slice';
import { setIndexPattern } from '../utils/state_management/visualization_slice';
import { useVisualizationType } from '../utils/use';
import { DataTab } from '../contributions';
import { StyleTabConfig } from '../../services/type_service';

export const SideNav = () => {
const {
Expand All @@ -21,17 +23,32 @@ export const SideNav = () => {
},
} = useOpenSearchDashboards<WizardServices>();
const { IndexPatternSelect } = data.ui;
const { indexPattern } = useTypedSelector((state) => state.dataSource);
const { indexPattern: indexPatternId } = useTypedSelector((state) => state.visualization);
const dispatch = useTypedDispatch();
const {
contributions: { containers },
ui: { containerConfig },
} = useVisualizationType();

const tabs: EuiTabbedContentTab[] = containers.sidePanel.map(({ id, name, Component }) => ({
id,
name,
content: Component,
}));
const tabs: EuiTabbedContentTab[] = Object.entries(containerConfig).map(
([containerName, config]) => {
let content = null;
switch (containerName) {
case 'data':
content = <DataTab key="containerName" />;
break;

case 'style':
content = (config as StyleTabConfig).render();
break;
}

return {
id: containerName,
name: containerName,
content,
};
}
);

return (
<section className="wizSidenav">
Expand All @@ -46,10 +63,13 @@ export const SideNav = () => {
placeholder={i18n.translate('wizard.nav.dataSource.selector.placeholder', {
defaultMessage: 'Select index pattern',
})}
indexPatternId={indexPattern?.id || ''}
indexPatternId={indexPatternId || ''}
onChange={async (newIndexPatternId: any) => {
const newIndexPattern = await data.indexPatterns.get(newIndexPatternId);
dispatch(setIndexPattern(newIndexPattern));

if (newIndexPattern) {
dispatch(setIndexPattern(newIndexPatternId));
}
}}
isClearable={false}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/wizard/public/application/components/top_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { getTopNavconfig } from '../utils/get_top_nav_config';
import { WizardServices } from '../../types';

import './top_nav.scss';
import { useTypedSelector } from '../utils/state_management';
import { useIndexPattern } from '../utils/use';

export const TopNav = () => {
const { services } = useOpenSearchDashboards<WizardServices>();
Expand All @@ -22,7 +22,7 @@ export const TopNav = () => {
} = services;

const config = useMemo(() => getTopNavconfig(services), [services]);
const { indexPattern } = useTypedSelector((state) => state.dataSource);
const indexPattern = useIndexPattern();

return (
<div className="wizTopNav">
Expand Down
26 changes: 22 additions & 4 deletions src/plugins/wizard/public/application/components/workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,34 @@ import {
EuiPanel,
EuiPopover,
} from '@elastic/eui';
import React, { FC, useState, useMemo } from 'react';
import React, { FC, useState, useMemo, useEffect } from 'react';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { WizardServices } from '../../types';
import { useTypedDispatch } from '../utils/state_management';
import { useTypedDispatch, useTypedSelector } from '../utils/state_management';
import { setActiveVisualization } from '../utils/state_management/visualization_slice';
import { useVisualizationType } from '../utils/use';

import './workspace.scss';

export const Workspace: FC = ({ children }) => {
const {
services: {
expressions: { ReactExpressionRenderer },
},
} = useOpenSearchDashboards<WizardServices>();
const { toExpression } = useVisualizationType();
const [expression, setExpression] = useState<string>();
const rootState = useTypedSelector((state) => state);

useEffect(() => {
async function loadExpression() {
const exp = await toExpression(rootState);
setExpression(exp);
}

loadExpression();
}, [rootState, toExpression]);

return (
<section className="wizWorkspace">
<EuiFlexGroup className="wizCanvasControls">
Expand All @@ -32,8 +50,8 @@ export const Workspace: FC = ({ children }) => {
</EuiFlexItem>
</EuiFlexGroup>
<EuiPanel className="wizCanvas">
{children ? (
children
{expression ? (
<ReactExpressionRenderer expression={expression} />
) : (
<EuiFlexItem className="wizWorkspace__empty">
<EuiEmptyPrompt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { EuiForm } from '@elastic/eui';
import React, { useMemo } from 'react';
import React, { useMemo, useState } from 'react';
import { useVisualizationType } from '../../../utils/use';
import {
DropboxContribution,
Expand All @@ -20,51 +20,64 @@ import { SelectContribution } from '../common/items';
import { INDEX_FIELD_KEY } from './items/use/use_form_field';
import { DATA_TAB_ID } from '.';
import './config_panel.scss';
import { mapSchemaToAggPanel } from './utils/schema_to_dropbox';
import { useOpenSearchDashboards } from '../../../../../../opensearch_dashboards_react/public';
import { WizardServices } from '../../../../types';
import { SecondaryPanel } from './secondary_panel';

const DEFAULT_ITEMS: MainItemContribution[] = [getTitleContribution()];

export function ConfigPanel() {
const {
contributions: { items },
} = useVisualizationType();
const activeItem = useTypedSelector((state) => state.config.activeItem);
const configItemState = useTypedSelector((state) => state.config.items[activeItem?.id || '']);

const hydratedItems: MainItemContribution[] = [...(items?.[DATA_TAB_ID] ?? []), ...DEFAULT_ITEMS];

const mainPanel = useMemo(() => mapItemToPanelComponents(hydratedItems), [hydratedItems]);
const secondaryPanel = useMemo(() => {
if (!activeItem || !configItemState || typeof configItemState === 'string') return;

// Generate each secondary panel base on active item type
if (activeItem.type === ITEM_TYPES.DROPBOX) {
const activeDropboxContribution = hydratedItems.find(
(item: MainItemContribution) =>
item.type === ITEM_TYPES.DROPBOX && item?.id === activeItem?.id
) as DropboxContribution | undefined;

if (!activeDropboxContribution) return null;

let itemsToRender: SecondaryItemContribution[] = [
getTitleContribution(activeDropboxContribution.label),
getFieldSelectorContribution(),
];

const dropboxFieldInstance = configItemState.instances.find(
({ id }) => id === activeItem.instanceId
);
if (dropboxFieldInstance && dropboxFieldInstance.properties.fieldName) {
itemsToRender = [...itemsToRender, ...activeDropboxContribution.items];
}

return mapItemToPanelComponents(itemsToRender, true);
}
}, [activeItem, configItemState, hydratedItems]);
const vizType = useVisualizationType();
const activeAgg = useTypedSelector((state) => state.visualization.activeVisualization?.activeAgg);
const schemas = vizType.ui.containerConfig.data.schemas;

// TODO: Will cleanup when add and edit field support is re introduced
// const activeItem = useTypedSelector((state) => state.config.activeItem);
// const configItemState = useTypedSelector((state) => state.config.items[activeItem?.id || '']);

// const hydratedItems: MainItemContribution[] = useMemo(
// () => [...(items?.[DATA_TAB_ID] ?? []), ...DEFAULT_ITEMS],
// [items]
// );

// const mainPanel = useMemo(() => mapItemToPanelComponents(hydratedItems), [hydratedItems]);
// const secondaryPanel = useMemo(() => {
// if (!activeItem || !configItemState || typeof configItemState === 'string') return;

// // Generate each secondary panel base on active item type
// if (activeItem.type === ITEM_TYPES.DROPBOX) {
// const activeDropboxContribution = hydratedItems.find(
// (item: MainItemContribution) =>
// item.type === ITEM_TYPES.DROPBOX && item?.id === activeItem?.id
// ) as DropboxContribution | undefined;

// if (!activeDropboxContribution) return null;

// let itemsToRender: SecondaryItemContribution[] = [
// getTitleContribution(activeDropboxContribution.label),
// getFieldSelectorContribution(),
// ];

// const dropboxFieldInstance = configItemState.instances.find(
// ({ id }) => id === activeItem.instanceId
// );
// if (dropboxFieldInstance && dropboxFieldInstance.properties.fieldName) {
// itemsToRender = [...itemsToRender, ...activeDropboxContribution.items];
// }

// return mapItemToPanelComponents(itemsToRender, true);
// }
// }, [activeItem, configItemState, hydratedItems]);

if (!schemas) return null;

const mainPanel = mapSchemaToAggPanel(schemas);

return (
<EuiForm className={`wizConfig ${activeItem ? 'showSecondary' : ''}`}>
<EuiForm className={`wizConfig ${activeAgg ? 'showSecondary' : ''}`}>
<div className="wizConfig__section">{mainPanel}</div>
<div className="wizConfig__section wizConfig--secondary">{secondaryPanel}</div>
<SecondaryPanel />
</EuiForm>
);
}
Expand All @@ -76,16 +89,16 @@ function getTitleContribution(title?: string): TitleItemContribution {
};
}

function getFieldSelectorContribution(): SelectContribution<string> {
return {
type: ItemTypes.SELECT,
id: INDEX_FIELD_KEY,
label: 'Select a Field',
options: (state) => {
return state.dataSource.visualizableFields.map((field) => ({
value: field.name,
inputDisplay: field.displayName,
}));
},
};
}
// function getFieldSelectorContribution(): SelectContribution<string> {
// return {
// type: ItemTypes.SELECT,
// id: INDEX_FIELD_KEY,
// label: 'Select a Field',
// options: (state) => {
// return state.dataSource.visualizableFields.map((field) => ({
// value: field.name,
// inputDisplay: field.displayName,
// }));
// },
// };
// }
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import React from 'react';
import { i18n } from '@osd/i18n';
import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { setSearchField } from '../../../utils/state_management/datasource_slice';
import { setSearchField } from '../../../utils/state_management/visualization_slice';
import { useTypedDispatch } from '../../../utils/state_management';

export interface Props {
Expand Down
Loading