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

[Lens] Build endzone markers #97849

Merged
merged 5 commits into from
Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,16 @@ describe('Axes Settings', () => {
false
);
});

it('hides the endzone visibility flag if no setter is passed in', () => {
const component = shallow(<AxisSettingsPopover {...props} />);
expect(component.find('[data-test-subj="lnsshowEndzones"]').length).toBe(0);
});

it('shows the switch if setter is present', () => {
const component = shallow(
<AxisSettingsPopover {...props} endzonesVisible={true} setEndzoneVisibility={() => {}} />
);
expect(component.find('[data-test-subj="lnsshowEndzones"]').prop('checked')).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ export interface AxisSettingsPopoverProps {
* Toggles the axis title visibility
*/
toggleAxisTitleVisibility: (axis: AxesSettingsConfigKeys, checked: boolean) => void;
/**
* Set endzone visibility
*/
setEndzoneVisibility?: (checked: boolean) => void;
/**
* Flag whether endzones are visible
*/
endzonesVisible?: boolean;
}
const popoverConfig = (
axis: AxesSettingsConfigKeys,
Expand Down Expand Up @@ -138,6 +146,8 @@ export const AxisSettingsPopover: React.FunctionComponent<AxisSettingsPopoverPro
areGridlinesVisible,
isAxisTitleVisible,
toggleAxisTitleVisibility,
setEndzoneVisibility,
endzonesVisible,
}) => {
const [title, setTitle] = useState<string | undefined>(axisTitle);

Expand Down Expand Up @@ -212,6 +222,20 @@ export const AxisSettingsPopover: React.FunctionComponent<AxisSettingsPopoverPro
onChange={() => toggleGridlinesVisibility(axis)}
checked={areGridlinesVisible}
/>
{setEndzoneVisibility && (
<>
<EuiSpacer size="m" />
<EuiSwitch
compressed
data-test-subj={`lnsshowEndzones`}
label={i18n.translate('xpack.lens.xyChart.showEnzones', {
defaultMessage: 'Show partial data markers',
})}
onChange={() => setEndzoneVisibility(!Boolean(endzonesVisible))}
checked={Boolean(endzonesVisible)}
/>
</>
)}
</ToolbarPopover>
);
};
130 changes: 130 additions & 0 deletions x-pack/plugins/lens/public/xy_visualization/expression.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { createMockExecutionContext } from '../../../../../src/plugins/expressio
import { mountWithIntl } from '@kbn/test/jest';
import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
import { EmptyPlaceholder } from '../shared_components/empty_placeholder';
import { XyEndzones } from './x_domain';

const onClickValue = jest.fn();
const onSelectRange = jest.fn();
Expand Down Expand Up @@ -549,6 +550,135 @@ describe('xy_expression', () => {
}
`);
});

describe('endzones', () => {
const { args } = sampleArgs();
const data: LensMultiTable = {
type: 'lens_multitable',
tables: {
first: createSampleDatatableWithRows([
{ a: 1, b: 2, c: new Date('2021-04-22').valueOf(), d: 'Foo' },
{ a: 1, b: 2, c: new Date('2021-04-23').valueOf(), d: 'Foo' },
{ a: 1, b: 2, c: new Date('2021-04-24').valueOf(), d: 'Foo' },
]),
},
dateRange: {
// first and last bucket are partial
fromDate: new Date('2021-04-22T12:00:00.000Z'),
toDate: new Date('2021-04-24T12:00:00.000Z'),
},
};
const timeArgs: XYArgs = {
...args,
layers: [
{
...args.layers[0],
seriesType: 'line',
xScaleType: 'time',
isHistogram: true,
splitAccessor: undefined,
},
],
};

test('it extends interval if data is exceeding it', () => {
const component = shallow(
<XYChart
{...defaultProps}
minInterval={24 * 60 * 60 * 1000}
data={data}
args={timeArgs}
/>
);

expect(component.find(Settings).prop('xDomain')).toEqual({
// shortened to 24th midnight (elastic-charts automatically adds one min interval)
max: new Date('2021-04-24').valueOf(),
// extended to 22nd midnight because of first bucket
min: new Date('2021-04-22').valueOf(),
minInterval: 24 * 60 * 60 * 1000,
});
});

test('it renders endzone component bridging gap between domain and extended domain', () => {
const component = shallow(
<XYChart
{...defaultProps}
minInterval={24 * 60 * 60 * 1000}
data={data}
args={timeArgs}
/>
);

expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual(
expect.objectContaining({
domainStart: new Date('2021-04-22T12:00:00.000Z').valueOf(),
domainEnd: new Date('2021-04-24T12:00:00.000Z').valueOf(),
domainMin: new Date('2021-04-22').valueOf(),
domainMax: new Date('2021-04-24').valueOf(),
})
);
});

test('should pass enabled histogram mode and min interval to endzones component', () => {
const component = shallow(
<XYChart
{...defaultProps}
minInterval={24 * 60 * 60 * 1000}
data={data}
args={timeArgs}
/>
);

expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual(
expect.objectContaining({
interval: 24 * 60 * 60 * 1000,
isFullBin: false,
})
);
});

test('should pass disabled histogram mode and min interval to endzones component', () => {
const component = shallow(
<XYChart
{...defaultProps}
minInterval={24 * 60 * 60 * 1000}
data={data}
args={{
...args,
layers: [
{
...args.layers[0],
seriesType: 'bar',
xScaleType: 'time',
isHistogram: true,
},
],
}}
/>
);

expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual(
expect.objectContaining({
interval: 24 * 60 * 60 * 1000,
isFullBin: true,
})
);
});

test('it does not render endzones if disabled via settings', () => {
const component = shallow(
<XYChart
{...defaultProps}
minInterval={24 * 60 * 60 * 1000}
data={data}
args={{ ...timeArgs, hideEndzones: true }}
/>
);

expect(component.find(XyEndzones).length).toEqual(0);
});
});
});

test('it has xDomain undefined if the x is not a time scale or a histogram', () => {
Expand Down
50 changes: 40 additions & 10 deletions x-pack/plugins/lens/public/xy_visualization/expression.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { desanitizeFilterContext } from '../utils';
import { fittingFunctionDefinitions, getFitOptions } from './fitting_functions';
import { getAxesConfiguration } from './axes_configuration';
import { getColorAssignments } from './color_assignment';
import { getXDomain, XyEndzones } from './x_domain';

declare global {
interface Window {
Expand Down Expand Up @@ -183,6 +184,13 @@ export const xyChart: ExpressionFunctionDefinition<
defaultMessage: 'Define how curve type is rendered for a line chart',
}),
},
hideEndzones: {
types: ['boolean'],
default: false,
help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', {
defaultMessage: 'Hide endzone markers for partial data',
}),
},
},
fn(data: LensMultiTable, args: XYArgs) {
return {
Expand Down Expand Up @@ -330,9 +338,17 @@ export function XYChart({
renderMode,
syncColors,
}: XYChartRenderProps) {
const { legend, layers, fittingFunction, gridlinesVisibilitySettings, valueLabels } = args;
const {
legend,
layers,
fittingFunction,
gridlinesVisibilitySettings,
valueLabels,
hideEndzones,
} = args;
const chartTheme = chartsThemeService.useChartsTheme();
const chartBaseTheme = chartsThemeService.useChartsBaseTheme();
const darkMode = chartsThemeService.useDarkMode();
const filteredLayers = getFilteredLayers(layers, data);

if (filteredLayers.length === 0) {
Expand Down Expand Up @@ -387,15 +403,13 @@ export function XYChart({
const isTimeViz = data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time');
const isHistogramViz = filteredLayers.every((l) => l.isHistogram);

const xDomain = isTimeViz
? {
min: data.dateRange?.fromDate.getTime(),
max: data.dateRange?.toDate.getTime(),
minInterval,
}
: isHistogramViz
? { minInterval }
: undefined;
const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain(
layers,
data,
minInterval,
Boolean(isTimeViz),
Boolean(isHistogramViz)
);

const getYAxesTitles = (
axisSeries: Array<{ layer: string; accessor: string }>,
Expand Down Expand Up @@ -602,6 +616,22 @@ export function XYChart({
/>
))}

{!hideEndzones && (
<XyEndzones
baseDomain={rawXDomain}
extendedDomain={xDomain}
darkMode={darkMode}
histogramMode={filteredLayers.every(
(layer) =>
layer.isHistogram &&
(layer.seriesType.includes('stacked') || !layer.splitAccessor) &&
(layer.seriesType.includes('stacked') ||
!layer.seriesType.includes('bar') ||
!chartHasMoreThanOneBarSeries)
)}
/>
)}

{filteredLayers.flatMap((layer, layerIndex) =>
layer.accessors.map((accessor, accessorIndex) => {
const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('#toExpression', () => {
fittingFunction: 'Carry',
tickLabelsVisibilitySettings: { x: false, yLeft: true, yRight: true },
gridlinesVisibilitySettings: { x: false, yLeft: true, yRight: true },
hideEndzones: true,
layers: [
{
layerId: 'first',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export const buildExpression = (
},
],
valueLabels: [state?.valueLabels || 'hide'],
hideEndzones: [state?.hideEndzones || false],
layers: validLayers.map((layer) => {
const columnToLabel = getColumnToLabelMap(layer, datasourceLayers[layer.layerId]);

Expand Down
Loading