mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* Adding legend controls * Adding url support and finishing feature * Moving config to right side; setting min/max to data boundries when deactivating auto * Removing legend from max and min labels * removing uneseccary unshift(0) * Change autobounds behavior
This commit is contained in:
parent
96197f7a87
commit
db5e282d59
11 changed files with 298 additions and 23 deletions
|
@ -34,6 +34,8 @@ interface Props {
|
|||
onViewChange: (view: string) => void;
|
||||
view: string;
|
||||
intl: InjectedIntl;
|
||||
boundsOverride: InfraWaffleMapBounds;
|
||||
autoBounds: boolean;
|
||||
}
|
||||
|
||||
interface MetricFormatter {
|
||||
|
@ -51,12 +53,10 @@ const METRIC_FORMATTERS: MetricFormatters = {
|
|||
[InfraMetricType.cpu]: {
|
||||
formatter: InfraFormatterType.percent,
|
||||
template: '{{value}}',
|
||||
bounds: { min: 0, max: 1 },
|
||||
},
|
||||
[InfraMetricType.memory]: {
|
||||
formatter: InfraFormatterType.percent,
|
||||
template: '{{value}}',
|
||||
bounds: { min: 0, max: 1 },
|
||||
},
|
||||
[InfraMetricType.rx]: { formatter: InfraFormatterType.bits, template: '{{value}}/s' },
|
||||
[InfraMetricType.tx]: { formatter: InfraFormatterType.bits, template: '{{value}}/s' },
|
||||
|
@ -67,19 +67,33 @@ const METRIC_FORMATTERS: MetricFormatters = {
|
|||
};
|
||||
|
||||
const calculateBoundsFromNodes = (nodes: InfraNode[]): InfraWaffleMapBounds => {
|
||||
const values = nodes.map(node => node.metric.value);
|
||||
// if there is only one value then we need to set the bottom range to zero
|
||||
if (values.length === 1) {
|
||||
values.unshift(0);
|
||||
const maxValues = nodes.map(node => node.metric.max);
|
||||
const minValues = nodes.map(node => node.metric.value);
|
||||
// if there is only one value then we need to set the bottom range to zero for min
|
||||
// otherwise the legend will look silly since both values are the same for top and
|
||||
// bottom.
|
||||
if (minValues.length === 1) {
|
||||
minValues.unshift(0);
|
||||
}
|
||||
return { min: min(values) || 0, max: max(values) || 0 };
|
||||
return { min: min(minValues) || 0, max: max(maxValues) || 0 };
|
||||
};
|
||||
|
||||
export const NodesOverview = injectI18n(
|
||||
class extends React.Component<Props, {}> {
|
||||
public static displayName = 'Waffle';
|
||||
public render() {
|
||||
const { loading, nodes, nodeType, reload, intl, view, options, timeRange } = this.props;
|
||||
const {
|
||||
autoBounds,
|
||||
boundsOverride,
|
||||
loading,
|
||||
nodes,
|
||||
nodeType,
|
||||
reload,
|
||||
intl,
|
||||
view,
|
||||
options,
|
||||
timeRange,
|
||||
} = this.props;
|
||||
if (loading) {
|
||||
return (
|
||||
<InfraLoadingPanel
|
||||
|
@ -113,13 +127,8 @@ export const NodesOverview = injectI18n(
|
|||
/>
|
||||
);
|
||||
}
|
||||
const { metric } = this.props.options;
|
||||
const metricFormatter = get(
|
||||
METRIC_FORMATTERS,
|
||||
metric.type,
|
||||
METRIC_FORMATTERS[InfraMetricType.count]
|
||||
);
|
||||
const bounds = (metricFormatter && metricFormatter.bounds) || calculateBoundsFromNodes(nodes);
|
||||
const dataBounds = calculateBoundsFromNodes(nodes);
|
||||
const bounds = autoBounds ? dataBounds : boundsOverride;
|
||||
return (
|
||||
<MainContainer>
|
||||
<ViewSwitcherContainer>
|
||||
|
@ -146,6 +155,7 @@ export const NodesOverview = injectI18n(
|
|||
timeRange={timeRange}
|
||||
onFilter={this.handleDrilldown}
|
||||
bounds={bounds}
|
||||
dataBounds={dataBounds}
|
||||
/>
|
||||
</MapContainer>
|
||||
)}
|
||||
|
|
|
@ -57,7 +57,7 @@ const GradientLegendContainer = styled.div`
|
|||
height: 10px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
right: 40px;
|
||||
`;
|
||||
|
||||
const GradientLegendTick = styled.div`
|
||||
|
|
|
@ -5,19 +5,41 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { WithWaffleOptions } from '../../containers/waffle/with_waffle_options';
|
||||
import { InfraFormatter, InfraWaffleMapBounds, InfraWaffleMapLegend } from '../../lib/lib';
|
||||
import { GradientLegend } from './gradient_legend';
|
||||
import { LegendControls } from './legend_controls';
|
||||
import { isInfraWaffleMapGradientLegend, isInfraWaffleMapStepLegend } from './lib/type_guards';
|
||||
import { StepLegend } from './steps_legend';
|
||||
interface Props {
|
||||
legend: InfraWaffleMapLegend;
|
||||
bounds: InfraWaffleMapBounds;
|
||||
dataBounds: InfraWaffleMapBounds;
|
||||
formatter: InfraFormatter;
|
||||
}
|
||||
|
||||
export const Legend: React.SFC<Props> = ({ legend, bounds, formatter }) => {
|
||||
interface LegendControlOptions {
|
||||
auto: boolean;
|
||||
bounds: InfraWaffleMapBounds;
|
||||
}
|
||||
|
||||
export const Legend: React.SFC<Props> = ({ dataBounds, legend, bounds, formatter }) => {
|
||||
return (
|
||||
<LegendContainer>
|
||||
<WithWaffleOptions>
|
||||
{({ changeBoundsOverride, changeAutoBounds, autoBounds, boundsOverride }) => (
|
||||
<LegendControls
|
||||
dataBounds={dataBounds}
|
||||
bounds={bounds}
|
||||
autoBounds={autoBounds}
|
||||
boundsOverride={boundsOverride}
|
||||
onChange={(options: LegendControlOptions) => {
|
||||
changeBoundsOverride(options.bounds);
|
||||
changeAutoBounds(options.auto);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</WithWaffleOptions>
|
||||
{isInfraWaffleMapGradientLegend(legend) && (
|
||||
<GradientLegend formatter={formatter} legend={legend} bounds={bounds} />
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonIcon,
|
||||
EuiFieldNumber,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiPopover,
|
||||
EuiPopoverTitle,
|
||||
EuiSwitch,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React, { SyntheticEvent, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { InfraWaffleMapBounds } from '../../lib/lib';
|
||||
|
||||
interface Props {
|
||||
onChange: (options: { auto: boolean; bounds: InfraWaffleMapBounds }) => void;
|
||||
bounds: InfraWaffleMapBounds;
|
||||
dataBounds: InfraWaffleMapBounds;
|
||||
autoBounds: boolean;
|
||||
boundsOverride: InfraWaffleMapBounds;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
export const LegendControls = injectI18n(
|
||||
({ intl, autoBounds, boundsOverride, onChange, dataBounds }: Props) => {
|
||||
const [isPopoverOpen, setPopoverState] = useState(false);
|
||||
const [draftAuto, setDraftAuto] = useState(autoBounds);
|
||||
const [draftBounds, setDraftBounds] = useState(autoBounds ? dataBounds : boundsOverride); // should come from bounds prop
|
||||
const buttonComponent = (
|
||||
<EuiButtonIcon
|
||||
iconType="gear"
|
||||
color="text"
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'xpack.infra.legendControls.buttonLabel',
|
||||
defaultMessage: 'configure legend',
|
||||
})}
|
||||
onClick={() => setPopoverState(true)}
|
||||
/>
|
||||
);
|
||||
|
||||
const handleAutoChange = (e: SyntheticEvent<HTMLInputElement>) => {
|
||||
setDraftAuto(e.currentTarget.checked);
|
||||
};
|
||||
|
||||
const createBoundsHandler = (name: string) => (e: SyntheticEvent<HTMLInputElement>) => {
|
||||
const value = parseFloat(e.currentTarget.value);
|
||||
setDraftBounds({ ...draftBounds, [name]: value });
|
||||
};
|
||||
|
||||
const handlePopoverClose = () => {
|
||||
setPopoverState(false);
|
||||
};
|
||||
|
||||
const handleApplyClick = () => {
|
||||
onChange({ auto: draftAuto, bounds: draftBounds });
|
||||
};
|
||||
|
||||
const commited =
|
||||
draftAuto === autoBounds &&
|
||||
boundsOverride.min === draftBounds.min &&
|
||||
boundsOverride.max === draftBounds.max;
|
||||
|
||||
const boundsValidRange = draftBounds.min < draftBounds.max;
|
||||
|
||||
return (
|
||||
<ControlContainer>
|
||||
<EuiPopover
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={handlePopoverClose}
|
||||
id="legendControls"
|
||||
button={buttonComponent}
|
||||
withTitle
|
||||
>
|
||||
<EuiPopoverTitle>Legend Options</EuiPopoverTitle>
|
||||
<EuiForm>
|
||||
<EuiFormRow>
|
||||
<EuiSwitch
|
||||
name="bounds"
|
||||
label={intl.formatMessage({
|
||||
id: 'xpack.infra.legendControls.switchLabel',
|
||||
defaultMessage: 'Auto calculate range',
|
||||
})}
|
||||
checked={draftAuto}
|
||||
onChange={handleAutoChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{(!boundsValidRange && (
|
||||
<EuiText color="danger" grow={false} size="s">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.legendControls.errorMessage"
|
||||
defaultMessage="Min should be less than max"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
)) ||
|
||||
null}
|
||||
<EuiFlexGroup style={{ marginTop: 0 }}>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
label={intl.formatMessage({
|
||||
id: 'xpack.infra.legendControls.minLabel',
|
||||
defaultMessage: 'Min',
|
||||
})}
|
||||
isInvalid={!boundsValidRange}
|
||||
>
|
||||
<EuiFieldNumber
|
||||
disabled={draftAuto}
|
||||
step={0.1}
|
||||
value={isNaN(draftBounds.min) ? '' : draftBounds.min}
|
||||
isInvalid={!boundsValidRange}
|
||||
name="legendMin"
|
||||
onChange={createBoundsHandler('min')}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
label={intl.formatMessage({
|
||||
id: 'xpack.infra.legendControls.maxLabel',
|
||||
defaultMessage: 'Max',
|
||||
})}
|
||||
isInvalid={!boundsValidRange}
|
||||
>
|
||||
<EuiFieldNumber
|
||||
disabled={draftAuto}
|
||||
step={0.1}
|
||||
isInvalid={!boundsValidRange}
|
||||
value={isNaN(draftBounds.max) ? '' : draftBounds.max}
|
||||
name="legendMax"
|
||||
onChange={createBoundsHandler('max')}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiButton
|
||||
type="submit"
|
||||
size="s"
|
||||
fill
|
||||
disabled={commited || !boundsValidRange}
|
||||
onClick={handleApplyClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.legendControls.applyButton"
|
||||
defaultMessage="Apply"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiForm>
|
||||
</EuiPopover>
|
||||
</ControlContainer>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const ControlContainer = styled.div`
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
right: 6px;
|
||||
bottom: 0;
|
||||
`;
|
|
@ -26,6 +26,7 @@ interface Props {
|
|||
timeRange: InfraTimerangeInput;
|
||||
onFilter: (filter: string) => void;
|
||||
bounds: InfraWaffleMapBounds;
|
||||
dataBounds: InfraWaffleMapBounds;
|
||||
}
|
||||
|
||||
export const Map: React.SFC<Props> = ({
|
||||
|
@ -36,6 +37,7 @@ export const Map: React.SFC<Props> = ({
|
|||
formatter,
|
||||
bounds,
|
||||
nodeType,
|
||||
dataBounds,
|
||||
}) => {
|
||||
const map = nodesToWaffleMap(nodes);
|
||||
return (
|
||||
|
@ -80,7 +82,12 @@ export const Map: React.SFC<Props> = ({
|
|||
}
|
||||
})}
|
||||
</WaffleMapInnerContainer>
|
||||
<Legend formatter={formatter} bounds={bounds} legend={options.legend} />
|
||||
<Legend
|
||||
formatter={formatter}
|
||||
bounds={bounds}
|
||||
dataBounds={dataBounds}
|
||||
legend={options.legend}
|
||||
/>
|
||||
</WaffleMapOuterContainer>
|
||||
);
|
||||
}}
|
||||
|
|
|
@ -48,7 +48,7 @@ export const StepLegend: React.SFC<Props> = ({ legend, formatter }) => {
|
|||
|
||||
const StepLegendContainer = styled.div`
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
padding: 10px 40px 10px 10px;
|
||||
`;
|
||||
|
||||
const StepContainer = styled.div`
|
||||
|
|
|
@ -8,6 +8,7 @@ import React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { isBoolean, isNumber } from 'lodash';
|
||||
import {
|
||||
InfraMetricInput,
|
||||
InfraMetricType,
|
||||
|
@ -26,12 +27,16 @@ const selectOptionsUrlState = createSelector(
|
|||
waffleOptionsSelectors.selectGroupBy,
|
||||
waffleOptionsSelectors.selectNodeType,
|
||||
waffleOptionsSelectors.selectCustomOptions,
|
||||
(metric, view, groupBy, nodeType, customOptions) => ({
|
||||
waffleOptionsSelectors.selectBoundsOverride,
|
||||
waffleOptionsSelectors.selectAutoBounds,
|
||||
(metric, view, groupBy, nodeType, customOptions, boundsOverride, autoBounds) => ({
|
||||
metric,
|
||||
groupBy,
|
||||
nodeType,
|
||||
view,
|
||||
customOptions,
|
||||
boundsOverride,
|
||||
autoBounds,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -42,6 +47,8 @@ export const withWaffleOptions = connect(
|
|||
nodeType: waffleOptionsSelectors.selectNodeType(state),
|
||||
view: waffleOptionsSelectors.selectView(state),
|
||||
customOptions: waffleOptionsSelectors.selectCustomOptions(state),
|
||||
boundsOverride: waffleOptionsSelectors.selectBoundsOverride(state),
|
||||
autoBounds: waffleOptionsSelectors.selectAutoBounds(state),
|
||||
urlState: selectOptionsUrlState(state),
|
||||
}),
|
||||
bindPlainActionCreators({
|
||||
|
@ -50,6 +57,8 @@ export const withWaffleOptions = connect(
|
|||
changeNodeType: waffleOptionsActions.changeNodeType,
|
||||
changeView: waffleOptionsActions.changeView,
|
||||
changeCustomOptions: waffleOptionsActions.changeCustomOptions,
|
||||
changeBoundsOverride: waffleOptionsActions.changeBoundsOverride,
|
||||
changeAutoBounds: waffleOptionsActions.changeAutoBounds,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -65,6 +74,8 @@ interface WaffleOptionsUrlState {
|
|||
nodeType?: ReturnType<typeof waffleOptionsSelectors.selectNodeType>;
|
||||
view?: ReturnType<typeof waffleOptionsSelectors.selectView>;
|
||||
customOptions?: ReturnType<typeof waffleOptionsSelectors.selectCustomOptions>;
|
||||
bounds?: ReturnType<typeof waffleOptionsSelectors.selectBoundsOverride>;
|
||||
auto?: ReturnType<typeof waffleOptionsSelectors.selectAutoBounds>;
|
||||
}
|
||||
|
||||
export const WithWaffleOptionsUrlState = () => (
|
||||
|
@ -76,6 +87,8 @@ export const WithWaffleOptionsUrlState = () => (
|
|||
changeNodeType,
|
||||
changeView,
|
||||
changeCustomOptions,
|
||||
changeAutoBounds,
|
||||
changeBoundsOverride,
|
||||
}) => (
|
||||
<UrlStateContainer
|
||||
urlState={urlState}
|
||||
|
@ -97,6 +110,12 @@ export const WithWaffleOptionsUrlState = () => (
|
|||
if (newUrlState && newUrlState.customOptions) {
|
||||
changeCustomOptions(newUrlState.customOptions);
|
||||
}
|
||||
if (newUrlState && newUrlState.bounds) {
|
||||
changeBoundsOverride(newUrlState.bounds);
|
||||
}
|
||||
if (newUrlState && newUrlState.auto) {
|
||||
changeAutoBounds(newUrlState.auto);
|
||||
}
|
||||
}}
|
||||
onInitialize={initialUrlState => {
|
||||
if (initialUrlState && initialUrlState.metric) {
|
||||
|
@ -114,6 +133,12 @@ export const WithWaffleOptionsUrlState = () => (
|
|||
if (initialUrlState && initialUrlState.customOptions) {
|
||||
changeCustomOptions(initialUrlState.customOptions);
|
||||
}
|
||||
if (initialUrlState && initialUrlState.bounds) {
|
||||
changeBoundsOverride(initialUrlState.bounds);
|
||||
}
|
||||
if (initialUrlState && initialUrlState.auto) {
|
||||
changeAutoBounds(initialUrlState.auto);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -128,6 +153,8 @@ const mapToUrlState = (value: any): WaffleOptionsUrlState | undefined =>
|
|||
nodeType: mapToNodeTypeUrlState(value.nodeType),
|
||||
view: mapToViewUrlState(value.view),
|
||||
customOptions: mapToCustomOptionsUrlState(value.customOptions),
|
||||
bounds: mapToBoundsOverideUrlState(value.boundsOverride),
|
||||
auto: mapToAutoBoundsUrlState(value.autoBounds),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
|
@ -169,3 +196,11 @@ const mapToCustomOptionsUrlState = (subject: any) => {
|
|||
? subject
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const mapToBoundsOverideUrlState = (subject: any) => {
|
||||
return subject != null && isNumber(subject.max) && isNumber(subject.min) ? subject : undefined;
|
||||
};
|
||||
|
||||
const mapToAutoBoundsUrlState = (subject: any) => {
|
||||
return subject != null && isBoolean(subject) ? subject : undefined;
|
||||
};
|
||||
|
|
|
@ -27,7 +27,15 @@ export const HomePageContent: React.SFC = () => (
|
|||
<WithWaffleTime>
|
||||
{({ currentTimeRange, isAutoReloading }) => (
|
||||
<WithWaffleOptions>
|
||||
{({ metric, groupBy, nodeType, view, changeView }) => (
|
||||
{({
|
||||
metric,
|
||||
groupBy,
|
||||
nodeType,
|
||||
view,
|
||||
changeView,
|
||||
autoBounds,
|
||||
boundsOverride,
|
||||
}) => (
|
||||
<WithWaffleNodes
|
||||
filterQuery={filterQueryAsJson}
|
||||
metric={metric}
|
||||
|
@ -52,6 +60,8 @@ export const HomePageContent: React.SFC = () => (
|
|||
timeRange={currentTimeRange}
|
||||
view={view}
|
||||
onViewChange={changeView}
|
||||
autoBounds={autoBounds}
|
||||
boundsOverride={boundsOverride}
|
||||
/>
|
||||
)}
|
||||
</WithWaffleNodes>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import actionCreatorFactory from 'typescript-fsa';
|
||||
import { InfraMetricInput, InfraNodeType, InfraPathInput } from '../../../graphql/types';
|
||||
import { InfraGroupByOptions } from '../../../lib/lib';
|
||||
import { InfraGroupByOptions, InfraWaffleMapBounds } from '../../../lib/lib';
|
||||
|
||||
const actionCreator = actionCreatorFactory('x-pack/infra/local/waffle_options');
|
||||
|
||||
|
@ -15,3 +15,5 @@ export const changeGroupBy = actionCreator<InfraPathInput[]>('CHANGE_GROUP_BY');
|
|||
export const changeCustomOptions = actionCreator<InfraGroupByOptions[]>('CHANGE_CUSTOM_OPTIONS');
|
||||
export const changeNodeType = actionCreator<InfraNodeType>('CHANGE_NODE_TYPE');
|
||||
export const changeView = actionCreator<string>('CHANGE_VIEW');
|
||||
export const changeBoundsOverride = actionCreator<InfraWaffleMapBounds>('CHANGE_BOUNDS_OVERRIDE');
|
||||
export const changeAutoBounds = actionCreator<boolean>('CHANGE_AUTO_BOUNDS');
|
||||
|
|
|
@ -13,8 +13,10 @@ import {
|
|||
InfraNodeType,
|
||||
InfraPathInput,
|
||||
} from '../../../graphql/types';
|
||||
import { InfraGroupByOptions } from '../../../lib/lib';
|
||||
import { InfraGroupByOptions, InfraWaffleMapBounds } from '../../../lib/lib';
|
||||
import {
|
||||
changeAutoBounds,
|
||||
changeBoundsOverride,
|
||||
changeCustomOptions,
|
||||
changeGroupBy,
|
||||
changeMetric,
|
||||
|
@ -28,6 +30,8 @@ export interface WaffleOptionsState {
|
|||
nodeType: InfraNodeType;
|
||||
view: string;
|
||||
customOptions: InfraGroupByOptions[];
|
||||
boundsOverride: InfraWaffleMapBounds;
|
||||
autoBounds: boolean;
|
||||
}
|
||||
|
||||
export const initialWaffleOptionsState: WaffleOptionsState = {
|
||||
|
@ -36,6 +40,8 @@ export const initialWaffleOptionsState: WaffleOptionsState = {
|
|||
nodeType: InfraNodeType.host,
|
||||
view: 'map',
|
||||
customOptions: [],
|
||||
boundsOverride: { max: 1, min: 0 },
|
||||
autoBounds: true,
|
||||
};
|
||||
|
||||
const currentMetricReducer = reducerWithInitialState(initialWaffleOptionsState.metric).case(
|
||||
|
@ -62,10 +68,21 @@ const currentViewReducer = reducerWithInitialState(initialWaffleOptionsState.vie
|
|||
(current, target) => target
|
||||
);
|
||||
|
||||
const currentBoundsOverrideReducer = reducerWithInitialState(
|
||||
initialWaffleOptionsState.boundsOverride
|
||||
).case(changeBoundsOverride, (current, target) => target);
|
||||
|
||||
const currentAutoBoundsReducer = reducerWithInitialState(initialWaffleOptionsState.autoBounds).case(
|
||||
changeAutoBounds,
|
||||
(current, target) => target
|
||||
);
|
||||
|
||||
export const waffleOptionsReducer = combineReducers<WaffleOptionsState>({
|
||||
metric: currentMetricReducer,
|
||||
groupBy: currentGroupByReducer,
|
||||
nodeType: currentNodeTypeReducer,
|
||||
view: currentViewReducer,
|
||||
customOptions: currentCustomOptionsReducer,
|
||||
boundsOverride: currentBoundsOverrideReducer,
|
||||
autoBounds: currentAutoBoundsReducer,
|
||||
});
|
||||
|
|
|
@ -11,3 +11,5 @@ export const selectGroupBy = (state: WaffleOptionsState) => state.groupBy;
|
|||
export const selectCustomOptions = (state: WaffleOptionsState) => state.customOptions;
|
||||
export const selectNodeType = (state: WaffleOptionsState) => state.nodeType;
|
||||
export const selectView = (state: WaffleOptionsState) => state.view;
|
||||
export const selectBoundsOverride = (state: WaffleOptionsState) => state.boundsOverride;
|
||||
export const selectAutoBounds = (state: WaffleOptionsState) => state.autoBounds;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue