Skip to content

Commit

Permalink
[Input Controls] Options List Embeddable, Factory & Frame (#106877) (#…
Browse files Browse the repository at this point in the history
…108101)

Co-authored-by: Clint Andrew Hall <[email protected]>
Co-authored-by: andreadelrio <[email protected]>

Co-authored-by: Clint Andrew Hall <[email protected]>
Co-authored-by: andreadelrio <[email protected]>
  • Loading branch information
3 people authored Aug 10, 2021
1 parent 591bd26 commit 8b2ba44
Show file tree
Hide file tree
Showing 16 changed files with 2,696 additions and 0 deletions.
1,953 changes: 1,953 additions & 0 deletions src/plugins/presentation_util/public/components/fixtures/flights.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { Story } from '@storybook/react';

const bar = '#c5ced8';
const panel = '#f7f9fa';
const background = '#e0e6ec';
const minHeight = 60;

const panelStyle = {
height: 165,
width: 400,
background: panel,
};

const kqlBarStyle = { background: bar, padding: 16, minHeight, fontStyle: 'italic' };

const inputBarStyle = { background: '#fff', padding: 4, minHeight };

const layout = (OptionStory: Story) => (
<EuiFlexGroup style={{ background }} direction="column">
<EuiFlexItem style={kqlBarStyle}>KQL Bar</EuiFlexItem>
<EuiFlexItem style={inputBarStyle}>
<OptionStory />
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup>
<EuiFlexItem style={panelStyle} />
<EuiFlexItem style={panelStyle} />
<EuiFlexItem style={panelStyle} />
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup>
<EuiFlexItem style={panelStyle} />
<EuiFlexItem style={panelStyle} />
<EuiFlexItem style={panelStyle} />
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);

export const decorators = [layout];
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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 { map, uniq } from 'lodash';
import { EuiSelectableOption } from '@elastic/eui';

import { flights } from '../../fixtures/flights';

export type Flight = typeof flights[number];
export type FlightField = keyof Flight;

export const getOptions = (field: string) => uniq(map(flights, field)).sort();

export const getEuiSelectableOptions = (field: string, search?: string): EuiSelectableOption[] => {
const options = getOptions(field)
.map((option) => ({
label: option + '',
searchableLabel: option + '',
}))
.filter((option) => !search || option.label.toLowerCase().includes(search.toLowerCase()));
if (options.length > 10) options.length = 10;
return options;
};

export const flightFieldLabels: Record<FlightField, string> = {
AvgTicketPrice: 'Average Ticket Price',
Cancelled: 'Cancelled',
Carrier: 'Carrier',
dayOfWeek: 'Day of Week',
Dest: 'Destination',
DestAirportID: 'Destination Airport ID',
DestCityName: 'Destination City',
DestCountry: 'Destination Country',
DestLocation: 'Destination Location',
DestRegion: 'Destination Region',
DestWeather: 'Destination Weather',
DistanceKilometers: 'Distance (km)',
DistanceMiles: 'Distance (mi)',
FlightDelay: 'Flight Delay',
FlightDelayMin: 'Flight Delay (min)',
FlightDelayType: 'Flight Delay Type',
FlightNum: 'Flight Number',
FlightTimeHour: 'Flight Time (hr)',
FlightTimeMin: 'Flight Time (min)',
Origin: 'Origin',
OriginAirportID: 'Origin Airport ID',
OriginCityName: 'Origin City',
OriginCountry: 'Origin Country',
OriginLocation: 'Origin Location',
OriginRegion: 'Origin Region',
OriginWeather: 'Origin Weather',
timestamp: 'Timestamp',
};

export const flightFields = Object.keys(flightFieldLabels) as FlightField[];
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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 React, { useEffect, useMemo, useState } from 'react';

import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';

import { decorators } from './decorators';
import { getEuiSelectableOptions, flightFields, flightFieldLabels, FlightField } from './flights';
import { OptionsListEmbeddableFactory, OptionsListEmbeddable } from '../control_types/options_list';
import { ControlFrame } from '../control_frame/control_frame';

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

interface OptionsListStorybookArgs {
fields: string[];
twoLine: boolean;
}

const storybookArgs = {
twoLine: false,
fields: ['OriginCityName', 'OriginWeather', 'DestCityName', 'DestWeather'],
};

const storybookArgTypes = {
fields: {
twoLine: {
control: { type: 'bool' },
},
control: {
type: 'check',
options: flightFields,
},
},
};

const OptionsListStoryComponent = ({ fields, twoLine }: OptionsListStorybookArgs) => {
const [embeddables, setEmbeddables] = useState<OptionsListEmbeddable[]>([]);

const optionsListEmbeddableFactory = useMemo(
() =>
new OptionsListEmbeddableFactory(
({ field, search }) =>
new Promise((r) => setTimeout(() => r(getEuiSelectableOptions(field, search)), 500))
),
[]
);

useEffect(() => {
const embeddableCreatePromises = fields.map((field) => {
return optionsListEmbeddableFactory.create({
field,
id: '',
indexPattern: '',
multiSelect: true,
twoLineLayout: twoLine,
title: flightFieldLabels[field as FlightField],
});
});
Promise.all(embeddableCreatePromises).then((newEmbeddables) => setEmbeddables(newEmbeddables));
}, [fields, optionsListEmbeddableFactory, twoLine]);

return (
<EuiFlexGroup alignItems="center" wrap={true} gutterSize={'s'}>
{embeddables.map((embeddable) => (
<EuiFlexItem key={embeddable.getInput().field}>
<ControlFrame twoLine={twoLine} embeddable={embeddable} />
</EuiFlexItem>
))}
</EuiFlexGroup>
);
};

export const OptionsListStory = ({ fields, twoLine }: OptionsListStorybookArgs) => (
<OptionsListStoryComponent fields={fields} twoLine={twoLine} />
);

OptionsListStory.args = storybookArgs;
OptionsListStory.argTypes = storybookArgTypes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.controlFrame--formControlLayout {
width: 100%;
min-width: $euiSize * 12.5;
}

.controlFrame--control {
&.optionsList--filterBtnSingle {
height: 100%;
}
}

.optionsList--filterBtnTwoLine {
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 React, { useMemo } from 'react';
import useMount from 'react-use/lib/useMount';
import classNames from 'classnames';
import { EuiFormControlLayout, EuiFormLabel, EuiFormRow } from '@elastic/eui';

import { InputControlEmbeddable } from '../embeddable/types';

import './control_frame.scss';

interface ControlFrameProps {
embeddable: InputControlEmbeddable;
twoLine?: boolean;
}

export const ControlFrame = ({ twoLine, embeddable }: ControlFrameProps) => {
const embeddableRoot: React.RefObject<HTMLDivElement> = useMemo(() => React.createRef(), []);

useMount(() => {
if (embeddableRoot.current && embeddable) embeddable.render(embeddableRoot.current);
});

const form = (
<EuiFormControlLayout
className="controlFrame--formControlLayout"
fullWidth
prepend={
twoLine ? undefined : (
<EuiFormLabel htmlFor={embeddable.id}>{embeddable.getInput().title}</EuiFormLabel>
)
}
>
<div
className={classNames('controlFrame--control', {
'optionsList--filterBtnTwoLine': twoLine,
'optionsList--filterBtnSingle': !twoLine,
})}
id={embeddable.id}
ref={embeddableRoot}
/>
</EuiFormControlLayout>
);

return twoLine ? (
<EuiFormRow fullWidth label={embeddable.getInput().title}>
{form}
</EuiFormRow>
) : (
form
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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.
*/

export { OptionsListEmbeddableFactory } from './options_list_embeddable_factory';
export { OptionsListEmbeddable } from './options_list_embeddable';
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.optionsList--anchorOverride {
display:block;
}

.optionsList--popoverOverride {
width: 100%;
height: 100%;
}

.optionsList--items {
@include euiScrollBar;

overflow-y: auto;
max-height: $euiSize * 30;
width: $euiSize * 25;
max-width: 100%;
}

.optionsList--filterBtn {
.euiFilterButton__text-hasNotification {
flex-grow: 1;
justify-content: space-between;
width: 0;
}
&.optionsList--filterBtnSingle {
width: 100%;
}
&.optionsList--filterBtnPlaceholder {
.euiFilterButton__textShift {
color: $euiTextSubduedColor;
}
}
}

.optionsList--filterGroupSingle {
box-shadow: none;
height: 100%;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: $euiBorderRadius - 1px;
border-bottom-right-radius: $euiBorderRadius - 1px;
}

.optionsList--filterGroup {
width: 100%;
}
Loading

0 comments on commit 8b2ba44

Please sign in to comment.