Skip to content

Commit

Permalink
[Controls] Redux Toolkit and Embeddable Redux Wrapper (#114371)
Browse files Browse the repository at this point in the history
Use new redux wrapper for control group management and upgrade all control group methods to use redux wrapper. Get order of controls from embeddable input, set up preconfigured story.

Co-authored-by: andreadelrio <[email protected]>
  • Loading branch information
ThomThomson and andreadelrio authored Oct 14, 2021
1 parent e5576d6 commit f8cbbbb
Show file tree
Hide file tree
Showing 39 changed files with 1,308 additions and 625 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
"yarn": "^1.21.1"
},
"dependencies": {
"@dnd-kit/core": "^3.1.1",
"@dnd-kit/sortable": "^4.0.0",
"@dnd-kit/utilities": "^2.0.0",
"@babel/runtime": "^7.15.4",
"@dnd-kit/core": "^3.1.1",
"@dnd-kit/sortable": "^4.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* Side Public License, v 1.
*/

import { InputControlFactory } from '../types';
import { ControlsService } from '../controls_service';
import { InputControlFactory } from '../../../services/controls';
import { flightFields, getEuiSelectableOptions } from './flights';
import { OptionsListEmbeddableFactory } from '../control_types/options_list';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,88 @@ import React, { useEffect, useMemo } from 'react';
import uuid from 'uuid';

import { decorators } from './decorators';
import { providers } from '../../../services/storybook';
import { getControlsServiceStub } from './controls_service_stub';
import { ControlGroupContainerFactory } from '../control_group/control_group_container_factory';
import { pluginServices, registry } from '../../../services/storybook';
import { populateStorybookControlFactories } from './storybook_control_factories';
import { ControlGroupContainerFactory } from '../control_group/embeddable/control_group_container_factory';
import { ControlsPanels } from '../control_group/types';
import {
OptionsListEmbeddableInput,
OPTIONS_LIST_CONTROL,
} from '../control_types/options_list/options_list_embeddable';

export default {
title: 'Controls',
description: '',
decorators,
};

const ControlGroupStoryComponent = () => {
const EmptyControlGroupStoryComponent = ({ panels }: { panels?: ControlsPanels }) => {
const embeddableRoot: React.RefObject<HTMLDivElement> = useMemo(() => React.createRef(), []);

providers.overlays.start({});
const overlays = providers.overlays.getService();

const controlsServiceStub = getControlsServiceStub();
pluginServices.setRegistry(registry.start({}));
populateStorybookControlFactories(pluginServices.getServices().controls);

useEffect(() => {
(async () => {
const factory = new ControlGroupContainerFactory(controlsServiceStub, overlays);
const factory = new ControlGroupContainerFactory();
const controlGroupContainerEmbeddable = await factory.create({
inheritParentState: {
useQuery: false,
useFilters: false,
useTimerange: false,
},
controlStyle: 'oneLine',
panels: panels ?? {},
id: uuid.v4(),
panels: {},
});
if (controlGroupContainerEmbeddable && embeddableRoot.current) {
controlGroupContainerEmbeddable.render(embeddableRoot.current);
}
})();
}, [embeddableRoot, controlsServiceStub, overlays]);
}, [embeddableRoot, panels]);

return <div ref={embeddableRoot} />;
};

export const ControlGroupStory = () => <ControlGroupStoryComponent />;
export const EmptyControlGroupStory = () => <EmptyControlGroupStoryComponent />;
export const ConfiguredControlGroupStory = () => (
<EmptyControlGroupStoryComponent
panels={{
optionsList1: {
type: OPTIONS_LIST_CONTROL,
order: 1,
width: 'auto',
explicitInput: {
title: 'Origin City',
id: 'optionsList1',
indexPattern: 'demo data flights',
field: 'OriginCityName',
defaultSelections: ['Toronto'],
} as OptionsListEmbeddableInput,
},
optionsList2: {
type: OPTIONS_LIST_CONTROL,
order: 2,
width: 'auto',
explicitInput: {
title: 'Destination City',
id: 'optionsList2',
indexPattern: 'demo data flights',
field: 'DestCityName',
defaultSelections: ['London'],
} as OptionsListEmbeddableInput,
},
optionsList3: {
type: OPTIONS_LIST_CONTROL,
order: 3,
width: 'auto',
explicitInput: {
title: 'Carrier',
id: 'optionsList3',
indexPattern: 'demo data flights',
field: 'Carrier',
} as OptionsListEmbeddableInput,
},
}}
/>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { flightFields, getEuiSelectableOptions } from './flights';
import { OptionsListEmbeddableFactory } from '../control_types/options_list';
import { InputControlFactory, PresentationControlsService } from '../../../services/controls';

export const populateStorybookControlFactories = (
controlsServiceStub: PresentationControlsService
) => {
const optionsListFactoryStub = new OptionsListEmbeddableFactory(
({ field, search }) =>
new Promise((r) => setTimeout(() => r(getEuiSelectableOptions(field, search)), 500)),
() => Promise.resolve(['demo data flights']),
() => Promise.resolve(flightFields)
);

// cast to unknown because the stub cannot use the embeddable start contract to transform the EmbeddableFactoryDefinition into an EmbeddableFactory
const optionsListControlFactory = optionsListFactoryStub as unknown as InputControlFactory;
optionsListControlFactory.getDefaultInput = () => ({});
controlsServiceStub.registerInputControlType(optionsListControlFactory);
};

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,28 @@ import {
EuiFormRow,
EuiToolTip,
} from '@elastic/eui';
import { ControlGroupContainer } from '../control_group/control_group_container';
import { useChildEmbeddable } from '../hooks/use_child_embeddable';
import { ControlStyle } from '../types';
import { ControlFrameStrings } from './control_frame_strings';

import { ControlGroupInput } from '../types';
import { EditControlButton } from '../editor/edit_control';
import { useChildEmbeddable } from '../../hooks/use_child_embeddable';
import { useReduxContainerContext } from '../../../redux_embeddables/redux_embeddable_context';
import { ControlGroupStrings } from '../control_group_strings';

export interface ControlFrameProps {
container: ControlGroupContainer;
customPrepend?: JSX.Element;
controlStyle: ControlStyle;
enableActions?: boolean;
onRemove?: () => void;
embeddableId: string;
onEdit?: () => void;
}

export const ControlFrame = ({
customPrepend,
enableActions,
embeddableId,
controlStyle,
container,
onRemove,
onEdit,
}: ControlFrameProps) => {
export const ControlFrame = ({ customPrepend, enableActions, embeddableId }: ControlFrameProps) => {
const embeddableRoot: React.RefObject<HTMLDivElement> = useMemo(() => React.createRef(), []);
const embeddable = useChildEmbeddable({ container, embeddableId });
const {
useEmbeddableSelector,
containerActions: { untilEmbeddableLoaded, removeEmbeddable },
} = useReduxContainerContext<ControlGroupInput>();
const { controlStyle } = useEmbeddableSelector((state) => state);

const embeddable = useChildEmbeddable({ untilEmbeddableLoaded, embeddableId });

const [title, setTitle] = useState<string>();

Expand All @@ -61,18 +57,13 @@ export const ControlFrame = ({
'controlFrame--floatingActions-oneLine': !usingTwoLineLayout,
})}
>
<EuiToolTip content={ControlFrameStrings.floatingActions.getEditButtonTitle()}>
<EuiButtonIcon
aria-label={ControlFrameStrings.floatingActions.getEditButtonTitle()}
iconType="pencil"
onClick={onEdit}
color="text"
/>
<EuiToolTip content={ControlGroupStrings.floatingActions.getEditButtonTitle()}>
<EditControlButton embeddableId={embeddableId} />
</EuiToolTip>
<EuiToolTip content={ControlFrameStrings.floatingActions.getRemoveButtonTitle()}>
<EuiToolTip content={ControlGroupStrings.floatingActions.getRemoveButtonTitle()}>
<EuiButtonIcon
aria-label={ControlFrameStrings.floatingActions.getRemoveButtonTitle()}
onClick={onRemove}
aria-label={ControlGroupStrings.floatingActions.getRemoveButtonTitle()}
onClick={() => removeEmbeddable(embeddableId)}
iconType="cross"
color="danger"
/>
Expand Down
Loading

0 comments on commit f8cbbbb

Please sign in to comment.