diff --git a/docs/api/dashboard-api.asciidoc b/docs/api/dashboard-api.asciidoc index e6f54dd9156ec..94511c3154fe0 100644 --- a/docs/api/dashboard-api.asciidoc +++ b/docs/api/dashboard-api.asciidoc @@ -1,7 +1,7 @@ [[dashboard-api]] == Import and export dashboard APIs -deprecated::[7.15.0,Both of these APIs have been deprecated in favor of <> and <>.] +deprecated::[7.15.0,These experimental APIs have been deprecated in favor of <> and <>.] Import and export dashboards with the corresponding saved objects, such as visualizations, saved searches, and index patterns. diff --git a/docs/api/dashboard/export-dashboard.asciidoc b/docs/api/dashboard/export-dashboard.asciidoc index 3a20eff0a54d2..098ec976569bd 100644 --- a/docs/api/dashboard/export-dashboard.asciidoc +++ b/docs/api/dashboard/export-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -Export dashboards and corresponding saved objects. +experimental[] Export dashboards and corresponding saved objects. [[dashboard-api-export-request]] ==== Request diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index e4817d6cb7ee9..41eb47500c8d7 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -Import dashboards and corresponding saved objects. +experimental[] Import dashboards and corresponding saved objects. [[dashboard-api-import-request]] ==== Request diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 6bac5e7940dbb..f2da704760f3b 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -516,6 +516,9 @@ Enables the legacy charts library for timelion charts in Visualize. **This setting is deprecated and will not be supported as of 8.0.** Maps values to specific colors in charts using the *Compatibility* palette. +[[visualization-uselegacytimeaxis]]`visualization:useLegacyTimeAxis`:: +Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB + [[visualization-heatmap-maxbuckets]]`visualization:heatmap:maxBuckets`:: The maximum number of buckets a datasource can return. High numbers can have a negative impact on your browser rendering performance. diff --git a/package.json b/package.json index b2065ddc1d608..3c42c077ae5b0 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace", "@elastic/apm-rum": "^5.9.1", "@elastic/apm-rum-react": "^1.3.1", - "@elastic/charts": "38.0.1", + "@elastic/charts": "38.1.0", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.35", "@elastic/ems-client": "8.0.0", diff --git a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts index 3ca3a8505338b..8cf49440f76b8 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts @@ -55,8 +55,7 @@ const { startES } = kbnTestServer.createTestServers({ }); let esServer: kbnTestServer.TestElasticsearchUtils; -// Failing: See https://github.com/elastic/kibana/issues/113697 -describe.skip('migration actions', () => { +describe('migration actions', () => { let client: ElasticsearchClient; beforeAll(async () => { @@ -1158,7 +1157,7 @@ describe.skip('migration actions', () => { it('resolves left wait_for_task_completion_timeout when the task does not complete within the timeout', async () => { const res = (await pickupUpdatedMappings( client, - 'existing_index_with_docs' + '.kibana_1' )()) as Either.Right; const task = waitForPickupUpdatedMappingsTask({ @@ -1539,7 +1538,8 @@ describe.skip('migration actions', () => { } `); }); - it('resolves left request_entity_too_large_exception when the payload is too large', async () => { + // TODO: unskip after https://github.com/elastic/kibana/issues/116111 + it.skip('resolves left request_entity_too_large_exception when the payload is too large', async () => { const newDocs = new Array(10000).fill({ _source: { title: diff --git a/src/plugins/charts/common/index.ts b/src/plugins/charts/common/index.ts index 618466212f5bb..d06dbc73e29b1 100644 --- a/src/plugins/charts/common/index.ts +++ b/src/plugins/charts/common/index.ts @@ -7,6 +7,7 @@ */ export const COLOR_MAPPING_SETTING = 'visualization:colorMapping'; +export const LEGACY_TIME_AXIS = 'visualization:useLegacyTimeAxis'; export { CustomPaletteArguments, diff --git a/src/plugins/charts/server/plugin.ts b/src/plugins/charts/server/plugin.ts index 63b703e6b7538..c7559b525cd22 100644 --- a/src/plugins/charts/server/plugin.ts +++ b/src/plugins/charts/server/plugin.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { CoreSetup, Plugin } from 'kibana/server'; -import { COLOR_MAPPING_SETTING, palette, systemPalette } from '../common'; +import { COLOR_MAPPING_SETTING, LEGACY_TIME_AXIS, palette, systemPalette } from '../common'; import { ExpressionsServerSetup } from '../../expressions/server'; interface SetupDependencies { @@ -45,6 +45,21 @@ export class ChartsServerPlugin implements Plugin { category: ['visualization'], schema: schema.string(), }, + [LEGACY_TIME_AXIS]: { + name: i18n.translate('charts.advancedSettings.visualization.useLegacyTimeAxis.name', { + defaultMessage: 'Legacy chart time axis', + }), + value: false, + description: i18n.translate( + 'charts.advancedSettings.visualization.useLegacyTimeAxis.description', + { + defaultMessage: + 'Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB', + } + ), + category: ['visualization'], + schema: schema.boolean(), + }, }); return {}; diff --git a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx index 166c2272a00f4..1fe149f3eb17d 100644 --- a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx +++ b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx @@ -115,7 +115,6 @@ export function DiscoverChart({ onResetQuery={resetSavedSearch} /> - {showViewModeToggle && ( )} - {timefield && ( = useLegacyTimeAxis + ? {} + : { strokeWidth: 0.1, stroke: isDarkMode ? 'white' : 'black' }; + const verticalAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + visible: false, + }, + tickLabel: { + fontSize: 11, + }, + }; + const xAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + stroke: isDarkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: isDarkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + tickLabel: { + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + }; + return (
@@ -195,15 +241,19 @@ export function DiscoverHistogram({ xAxisFormatter.convert(value)} + gridLine={gridLineStyle} + style={verticalAxisStyle} /> diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 007d3a99cb1dd..001efe0433dc8 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -64,6 +64,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'text', _meta: { description: 'Non-default value of setting.' }, }, + 'visualization:useLegacyTimeAxis': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'visualization:regionmap:showWarnings': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index d35a05fe04780..53d651b022529 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -40,6 +40,7 @@ export interface UsageStats { 'visualize:enableLabs': boolean; 'visualization:heatmap:maxBuckets': number; 'visualization:colorMapping': string; + 'visualization:useLegacyTimeAxis': boolean; 'visualization:regionmap:showWarnings': boolean; 'visualization:tileMap:maxPrecision': number; 'csv:separator': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 138ce3f097ce9..e7c18966ce0c0 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7110,6 +7110,12 @@ "description": "Non-default value of setting." } }, + "visualization:useLegacyTimeAxis": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "visualization:regionmap:showWarnings": { "type": "boolean", "_meta": { diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js index b4fe39c522de7..b177ef632e210 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js @@ -19,6 +19,7 @@ import { createFieldFormatter } from '../../lib/create_field_formatter'; import { checkIfSeriesHaveSameFormatters } from '../../lib/check_if_series_have_same_formatters'; import { TimeSeries } from '../../../visualizations/views/timeseries'; import { MarkdownSimple } from '../../../../../../../../plugins/kibana_react/public'; +import { LEGACY_TIME_AXIS } from '../../../../../../../../plugins/charts/common'; import { replaceVars } from '../../lib/replace_vars'; import { getInterval } from '../../lib/get_interval'; import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter'; @@ -272,6 +273,7 @@ class TimeseriesVisualization extends Component { syncColors={syncColors} palettesService={palettesService} interval={interval} + useLegacyTimeAxis={getConfig(LEGACY_TIME_AXIS, false)} isLastBucketDropped={Boolean( model.drop_last_bucket || model.series.some((series) => series.series_drop_last_bucket) diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 2158283bb80d5..5e98b74c0caa5 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -72,6 +72,7 @@ export const TimeSeries = ({ palettesService, interval, isLastBucketDropped, + useLegacyTimeAxis, }) => { // If the color isn't configured by the user, use the color mapping service // to assign a color from the Kibana palette. Colors will be shared across the @@ -138,6 +139,51 @@ export const TimeSeries = ({ }, [palettesService, series, syncColors] ); + + const darkMode = uiSettings.get('theme:darkMode'); + const gridLineStyle = !useLegacyTimeAxis + ? { + visible: showGrid, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + ...GRID_LINE_CONFIG, + visible: showGrid, + }; + const xAxisStyle = !useLegacyTimeAxis + ? { + tickLabel: { + visible: true, + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + axisTitle: { + visible: true, + padding: 0, + }, + } + : {}; + return ( ); @@ -357,4 +402,5 @@ TimeSeries.propTypes = { annotations: PropTypes.array, interval: PropTypes.number, isLastBucketDropped: PropTypes.bool, + useLegacyTimeAxis: PropTypes.bool, }; diff --git a/src/plugins/vis_types/xy/public/components/xy_axis.tsx b/src/plugins/vis_types/xy/public/components/xy_axis.tsx index 30e1dbbff673e..b224639bdbff3 100644 --- a/src/plugins/vis_types/xy/public/components/xy_axis.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_axis.tsx @@ -25,6 +25,7 @@ export const XYAxis: FC = ({ domain, style, integersOnly, + timeAxisLayerCount, }) => ( = ({ labelFormat={ticks?.labelFormatter} showOverlappingLabels={ticks?.showOverlappingLabels} showDuplicatedTicks={ticks?.showDuplicates} + timeAxisLayerCount={timeAxisLayerCount} /> ); diff --git a/src/plugins/vis_types/xy/public/config/get_axis.ts b/src/plugins/vis_types/xy/public/config/get_axis.ts index 0d6c67d064cf8..796636ef2cb61 100644 --- a/src/plugins/vis_types/xy/public/config/get_axis.ts +++ b/src/plugins/vis_types/xy/public/config/get_axis.ts @@ -8,7 +8,13 @@ import { identity } from 'lodash'; -import { AxisSpec, TickFormatter, YDomainRange, ScaleType as ECScaleType } from '@elastic/charts'; +import { + AxisSpec, + TickFormatter, + YDomainRange, + ScaleType as ECScaleType, + Position, +} from '@elastic/charts'; import { LabelRotation } from '../../../../charts/public'; import { BUCKET_TYPES } from '../../../../data/public'; @@ -33,7 +39,9 @@ export function getAxis( { categoryLines, valueAxis }: Grid, { params, format, formatter, title: fallbackTitle = '', aggType }: Aspect, seriesParams: SeriesParam[], - isDateHistogram = false + isDateHistogram = false, + useMultiLayerTimeAxis = false, + darkMode = false ): AxisConfig { const isCategoryAxis = type === AxisType.Category; // Hide unassigned axis, not supported in elastic charts @@ -74,9 +82,10 @@ export function getAxis( ticks, grid, scale, - style: getAxisStyle(ticks, title, fallbackRotation), + style: getAxisStyle(useMultiLayerTimeAxis, darkMode, ticks, title, fallbackRotation), domain: getAxisDomain(scale, isCategoryAxis), integersOnly: aggType === 'count', + timeAxisLayerCount: useMultiLayerTimeAxis ? 3 : 0, }; } @@ -147,19 +156,52 @@ export function getScale( } function getAxisStyle( + isMultiLayerTimeAxis: boolean, + darkMode: boolean, ticks?: TickOptions, title?: string, rotationFallback: LabelRotation = LabelRotation.Vertical ): AxisSpec['style'] { - return { - axisTitle: { - visible: (title ?? '').trim().length > 0, - }, - tickLabel: { - visible: ticks?.show, - rotation: -(ticks?.rotation ?? rotationFallback), - }, - }; + return isMultiLayerTimeAxis + ? { + tickLabel: { + visible: Boolean(ticks?.show), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(ticks?.show), + }, + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + } + : { + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + tickLabel: { + visible: ticks?.show, + rotation: -(ticks?.rotation ?? rotationFallback), + }, + }; } function getAxisDomain( diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index d2a3b9ad2a103..bd79b915be917 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -29,7 +29,12 @@ import { getAxis } from './get_axis'; import { getAspects } from './get_aspects'; import { ChartType } from '../index'; -export function getConfig(table: Datatable, params: VisParams): VisConfig { +export function getConfig( + table: Datatable, + params: VisParams, + useLegacyTimeAxis = false, + darkMode = false +): VisConfig { const { thresholdLine, orderBucketsBySum, @@ -42,13 +47,6 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { fillOpacity, } = params; const aspects = getAspects(table.columns, params.dimensions); - const xAxis = getAxis( - params.categoryAxes[0], - params.grid, - aspects.x, - params.seriesParams, - params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM - ); const tooltip = getTooltip(aspects, params); const yAxes = params.valueAxes.map((a) => { // find the correct aspect for each value axis @@ -60,10 +58,28 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { params.seriesParams ); }); + + const rotation = getRotation(params.categoryAxes[0]); + + const isDateHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM; + const isHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM; const enableHistogramMode = - (params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM || - params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM) && + (isDateHistogram || isHistogram) && shouldEnableHistogramMode(params.seriesParams, aspects.y, yAxes); + + const useMultiLayerTimeAxis = + enableHistogramMode && isDateHistogram && !useLegacyTimeAxis && rotation === 0; + + const xAxis = getAxis( + params.categoryAxes[0], + params.grid, + aspects.x, + params.seriesParams, + isDateHistogram, + useMultiLayerTimeAxis, + darkMode + ); + const isTimeChart = (aspects.x.params as DateHistogramParams).date ?? false; return { @@ -83,7 +99,7 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { xAxis, yAxes, legend: getLegend(params), - rotation: getRotation(params.categoryAxes[0]), + rotation, thresholdLine: getThresholdLine(thresholdLine, yAxes, params.seriesParams), }; } diff --git a/src/plugins/vis_types/xy/public/plugin.ts b/src/plugins/vis_types/xy/public/plugin.ts index c79ead242e35b..0f1de387161e3 100644 --- a/src/plugins/vis_types/xy/public/plugin.ts +++ b/src/plugins/vis_types/xy/public/plugin.ts @@ -24,7 +24,7 @@ import { } from './services'; import { visTypesDefinitions } from './vis_types'; -import { xyVisRenderer } from './vis_renderer'; +import { getXYVisRenderer } from './vis_renderer'; import * as expressionFunctions from './expression_functions'; @@ -69,7 +69,11 @@ export class VisTypeXyPlugin setThemeService(charts.theme); setPalettesService(charts.palettes); - expressions.registerRenderer(xyVisRenderer); + expressions.registerRenderer( + getXYVisRenderer({ + uiSettings: core.uiSettings, + }) + ); expressions.registerFunction(expressionFunctions.visTypeXyVisFn); expressions.registerFunction(expressionFunctions.categoryAxis); expressions.registerFunction(expressionFunctions.timeMarker); diff --git a/src/plugins/vis_types/xy/public/types/config.ts b/src/plugins/vis_types/xy/public/types/config.ts index e52b47366bc85..287787193bd20 100644 --- a/src/plugins/vis_types/xy/public/types/config.ts +++ b/src/plugins/vis_types/xy/public/types/config.ts @@ -85,6 +85,7 @@ export interface AxisConfig { title?: string; grid?: AxisGrid; integersOnly: boolean; + timeAxisLayerCount?: number; } export interface LegendOptions { diff --git a/src/plugins/vis_types/xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx index 515ad3e7eaf6f..8574e86a23096 100644 --- a/src/plugins/vis_types/xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -66,6 +66,7 @@ export interface VisComponentProps { fireEvent: IInterpreterRenderHandlers['event']; renderComplete: IInterpreterRenderHandlers['done']; syncColors: boolean; + useLegacyTimeAxis: boolean; } export type VisComponentType = typeof VisComponent; @@ -211,8 +212,9 @@ const VisComponent = (props: VisComponentProps) => { ); const { visData, visParams, syncColors } = props; + const isDarkMode = getThemeService().useDarkMode(); - const config = getConfig(visData, visParams); + const config = getConfig(visData, visParams, props.useLegacyTimeAxis, isDarkMode); const timeZone = getTimeZone(); const xDomain = config.xAxis.scale.type === ScaleType.Ordinal ? undefined : getXDomain(config.aspects.x.params); @@ -229,7 +231,7 @@ const VisComponent = (props: VisComponentProps) => { () => config.legend.position ?? Position.Right, [config.legend.position] ); - const isDarkMode = getThemeService().useDarkMode(); + const getSeriesName = getSeriesNameFn(config.aspects, config.aspects.y.length > 1); const splitAccessors = config.aspects.series?.map(({ accessor, formatter }) => { diff --git a/src/plugins/vis_types/xy/public/vis_renderer.tsx b/src/plugins/vis_types/xy/public/vis_renderer.tsx index 093671307d538..77727761015a7 100644 --- a/src/plugins/vis_types/xy/public/vis_renderer.tsx +++ b/src/plugins/vis_types/xy/public/vis_renderer.tsx @@ -9,6 +9,7 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; +import { IUiSettingsClient } from 'kibana/public'; import { VisualizationContainer } from '../../../visualizations/public'; import type { PersistedState } from '../../../visualizations/public'; @@ -17,6 +18,7 @@ import type { ExpressionRenderDefinition } from '../../../expressions/public'; import type { XyVisType } from '../common'; import type { VisComponentType } from './vis_component'; import { RenderValue, visName } from './expression_functions/xy_vis_fn'; +import { LEGACY_TIME_AXIS } from '../../../charts/common'; // @ts-ignore const VisComponent = lazy(() => import('./vis_component')); @@ -28,7 +30,9 @@ function shouldShowNoResultsMessage(visData: any, visType: XyVisType): boolean { return Boolean(isZeroHits); } -export const xyVisRenderer: ExpressionRenderDefinition = { +export const getXYVisRenderer: (deps: { + uiSettings: IUiSettingsClient; +}) => ExpressionRenderDefinition = ({ uiSettings }) => ({ name: visName, displayName: 'XY visualization', reuseDomNode: true, @@ -46,10 +50,11 @@ export const xyVisRenderer: ExpressionRenderDefinition = { fireEvent={handlers.event} uiState={handlers.uiState as PersistedState} syncColors={syncColors} + useLegacyTimeAxis={uiSettings.get(LEGACY_TIME_AXIS, false)} /> , domNode ); }, -}; +}); diff --git a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts index 5744c8e64f5c1..fa4308ae72883 100644 --- a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts +++ b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts @@ -12,7 +12,8 @@ import { PluginFunctionalProviderContext } from 'test/plugin_functional/services export default function ({ getService }: PluginFunctionalProviderContext) { const testSubjects = getService('testSubjects'); - describe('', () => { + // FAILING: https://github.com/elastic/kibana/issues/116463 + describe.skip('', () => { it('finds an index pattern', async () => { await testSubjects.existOrFail('indexPatternTitle'); }); diff --git a/test/functional/apps/dashboard/dashboard_snapshots.ts b/test/functional/apps/dashboard/dashboard_snapshots.ts index 3aba671c0a4b2..9279bbd5806e7 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.ts +++ b/test/functional/apps/dashboard/dashboard_snapshots.ts @@ -59,7 +59,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); it('compare area chart snapshot', async () => { @@ -81,7 +81,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); }); } diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index c531ada8a2573..712edb440311f 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -167,7 +167,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); expect(secondAreaChartData).to.eql(secondAreaExpectedChartData); expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData); - expect(firstAxesLabels).to.eql(['12.19GB', '12.2GB', '12.21GB']); + expect(firstAxesLabels).to.eql(['12.2GB', '12.21GB']); expect(secondAxesLabels).to.eql(['5.59KB', '5.6KB']); expect(thirdAxesLabels.toString()).to.be( 'BYTES_5721,BYTES_5722,BYTES_5723,BYTES_5724,BYTES_5725,BYTES_5726,BYTES_5727,BYTES_5728,BYTES_5729,BYTES_5730,BYTES_5731,BYTES_5732,BYTES_5733' diff --git a/test/functional/config.js b/test/functional/config.js index 5b0b79e84e8df..09eccc863a0e5 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -54,6 +54,7 @@ export default async function ({ readConfigFile }) { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', 'visualization:visualize:legacyPieChartsLibrary': true, + 'visualization:useLegacyTimeAxis': true, }, }, diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap index b058c42d8b4d1..6601167e1f83a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap @@ -67,6 +67,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` } } tickFormat={[Function]} + timeAxisLayerCount={0} title="c" /> { onClickValue, onSelectRange, syncColors: false, + useLegacyTimeAxis: false, }; }); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index a3022e830c861..80e4a8d4dc8f7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -31,6 +31,8 @@ import { LabelOverflowConstraint, DisplayValueStyle, RecursivePartial, + AxisStyle, + GridLineStyle, ScaleType, } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; @@ -90,6 +92,7 @@ export type XYChartRenderProps = XYChartProps & { paletteService: PaletteRegistry; formatFactory: FormatFactory; timeZone: string; + useLegacyTimeAxis: boolean; minInterval: number | undefined; interactive?: boolean; onClickValue: (data: LensFilterEvent['data']) => void; @@ -130,6 +133,7 @@ export const getXyChartRenderer = (dependencies: { chartsActiveCursorService: ChartsPluginStart['activeCursor']; paletteService: PaletteRegistry; timeZone: string; + useLegacyTimeAxis: boolean; }): ExpressionRenderDefinition => ({ name: 'lens_xy_chart_renderer', displayName: 'XY chart', @@ -160,6 +164,7 @@ export const getXyChartRenderer = (dependencies: { chartsThemeService={dependencies.chartsThemeService} paletteService={dependencies.paletteService} timeZone={dependencies.timeZone} + useLegacyTimeAxis={dependencies.useLegacyTimeAxis} minInterval={calculateMinInterval(config)} interactive={handlers.isInteractive()} onClickValue={onClickValue} @@ -235,6 +240,7 @@ export function XYChart({ onSelectRange, interactive = true, syncColors, + useLegacyTimeAxis, }: XYChartRenderProps) { const { legend, @@ -320,15 +326,15 @@ export function XYChart({ filteredBarLayers.some((layer) => layer.accessors.length > 1) || filteredBarLayers.some((layer) => layer.splitAccessor); - const isTimeViz = data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time'); + const isTimeViz = Boolean(data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time')); const isHistogramViz = filteredLayers.every((l) => l.isHistogram); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( filteredLayers, data, minInterval, - Boolean(isTimeViz), - Boolean(isHistogramViz) + isTimeViz, + isHistogramViz ); const yAxesMap = { @@ -555,6 +561,72 @@ export function XYChart({ floatingColumns: legend?.floatingColumns ?? 1, } as LegendPositionConfig; + // todo be moved in the chart plugin + const shouldUseNewTimeAxis = isTimeViz && !useLegacyTimeAxis && !shouldRotate; + + const gridLineStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 2, + }; + const xAxisStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + tickLabel: { + visible: Boolean(tickLabelsVisibilitySettings?.x), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: + referenceLinePaddings.bottom != null ? { inner: referenceLinePaddings.bottom } : 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(tickLabelsVisibilitySettings?.x), + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + } + : { + tickLabel: { + visible: tickLabelsVisibilitySettings?.x, + rotation: labelsOrientation?.x, + padding: + referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + }; return ( safeXAccessorLabelRenderer(d.value), }} - allowBrushingLastHistogramBin={Boolean(isTimeViz)} + allowBrushingLastHistogramBin={isTimeViz} rotation={shouldRotate ? 90 : 0} xDomain={xDomain} onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} @@ -618,29 +690,11 @@ export function XYChart({ id="x" position={shouldRotate ? Position.Left : Position.Bottom} title={xTitle} - gridLine={{ - visible: gridlinesVisibilitySettings?.x, - strokeWidth: 2, - }} + gridLine={gridLineStyle} hide={filteredLayers[0].hide || !filteredLayers[0].xAccessor} tickFormat={(d) => safeXAccessorLabelRenderer(d)} - style={{ - tickLabel: { - visible: tickLabelsVisibilitySettings?.x, - rotation: labelsOrientation?.x, - padding: - referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - axisTitle: { - visible: axisTitlesVisibilitySettings.x, - padding: - !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - }} + style={xAxisStyle} + timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} /> {yAxesConfiguration.map((axis) => { diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index f9d48ffaaae37..d3d461e8f8741 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -12,6 +12,7 @@ import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public import type { LensPluginStartDependencies } from '../plugin'; import { getTimeZone } from '../utils'; import type { FormatFactory } from '../../common'; +import { LEGACY_TIME_AXIS } from '../../../../../src/plugins/charts/common'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; @@ -37,6 +38,7 @@ export class XyVisualization { chartsActiveCursorService: charts.activeCursor, paletteService: palettes, timeZone: getTimeZone(core.uiSettings), + useLegacyTimeAxis: core.uiSettings.get(LEGACY_TIME_AXIS), }) ); return getXyVisualization({ paletteService: palettes, fieldFormats }); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts index 159115e2054e1..f6654ff5a6b1d 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts @@ -30,6 +30,5 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./spaces')); loadTestFile(require.resolve('./usage')); loadTestFile(require.resolve('./ilm_migration_apis')); - loadTestFile(require.resolve('./search_frozen_indices')); }); } diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts b/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts deleted file mode 100644 index daa749649e250..0000000000000 --- a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function ({ getService }: FtrProviderContext) { - const kibanaServer = getService('kibanaServer'); - const supertestSvc = getService('supertest'); - const esSupertest = getService('esSupertest'); - const indexPatternId = 'cool-test-index-pattern'; - - async function callExportAPI() { - const job = { - browserTimezone: 'UTC', - columns: ['@timestamp', 'ip', 'utilization'], - searchSource: { - fields: [{ field: '*', include_unmapped: 'true' }], - filter: [ - { - meta: { field: '@timestamp', index: indexPatternId, params: {} }, - range: { - '@timestamp': { - format: 'strict_date_optional_time', - gte: '2020-08-24T00:00:00.000Z', - lte: '2022-08-24T21:40:48.346Z', - }, - }, - }, - ], - index: indexPatternId, - parent: { filter: [], index: indexPatternId, query: { language: 'kuery', query: '' } }, - sort: [{ '@timestamp': 'desc' }], - trackTotalHits: true, - }, - title: 'Test search', - }; - - return await supertestSvc - .post(`/api/reporting/v1/generate/immediate/csv_searchsource`) - .set('kbn-xsrf', 'xxx') - .send(job); - } - - describe('Frozen indices search', () => { - const reset = async () => { - await kibanaServer.uiSettings.replace({ 'search:includeFrozen': false }); - try { - await esSupertest.delete('/test1,test2,test3'); - await kibanaServer.savedObjects.delete({ type: 'index-pattern', id: indexPatternId }); - } catch (err) { - // ignore 404 error - } - }; - - before(reset); - after(reset); - - it('Search includes frozen indices based on Advanced Setting', async () => { - await kibanaServer.uiSettings.update({ 'csv:quoteValues': true }); - - // setup: add multiple indices of test data - await Promise.all([ - esSupertest - .post('/test1/_doc') - .send({ '@timestamp': '2021-08-24T21:36:40Z', ip: '43.98.8.183', utilization: 18725 }), - esSupertest - .post('/test2/_doc') - .send({ '@timestamp': '2021-08-21T09:36:40Z', ip: '63.91.103.79', utilization: 8480 }), - esSupertest - .post('/test3/_doc') - .send({ '@timestamp': '2021-08-17T21:36:40Z', ip: '139.108.162.171', utilization: 3078 }), - ]); - await esSupertest.post('/test*/_refresh'); - - // setup: create index pattern - const indexPatternCreateResponse = await kibanaServer.savedObjects.create({ - type: 'index-pattern', - id: indexPatternId, - overwrite: true, - attributes: { title: 'test*', timeFieldName: '@timestamp' }, - }); - expect(indexPatternCreateResponse.id).to.be(indexPatternId); - - // 1. check the initial data with a CSV export - const initialSearch = await callExportAPI(); - expectSnapshot(initialSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - - // 2. freeze an index in the pattern - await esSupertest.post('/test3/_freeze').expect(200); - await esSupertest.post('/test*/_refresh').expect(200); - - // 3. recheck the search results - const afterFreezeSearch = await callExportAPI(); - expectSnapshot(afterFreezeSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - " - `); - - // 4. update setting to allow searching frozen data - await kibanaServer.uiSettings.update({ 'search:includeFrozen': true }); - - // 5. recheck the search results - const afterAllowSearch = await callExportAPI(); - expectSnapshot(afterAllowSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - }); - }); -} diff --git a/yarn.lock b/yarn.lock index 3e4e3ad4ed224..56d5cf17791ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2337,10 +2337,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@38.0.1": - version "38.0.1" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.0.1.tgz#9c1db7e0f1de869e0b2b505e192bbb9d62d60dc8" - integrity sha512-i9mIA3Ji9jSjuFDtuh9gV1xpCl3sbBEDgJiOgLVt04pr/qZH2W+tr3AV5yHvjsR7Te0Pmh/Cm5wLBvFKaI1nIA== +"@elastic/charts@38.1.0": + version "38.1.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.1.0.tgz#52146564c0e399da2267c10ec4536c33e50969e4" + integrity sha512-u2hQ8+daCvqapKQiFqN8QHWTz3OXby5Xf/ta1Dv59KTDTFIinCZD/M8PyD3MapbKx4b67hL7UxbErqr4rZ8jeA== dependencies: "@popperjs/core" "^2.4.0" chroma-js "^2.1.0" @@ -2351,6 +2351,7 @@ d3-interpolate "^1.4.0" d3-scale "^1.0.7" d3-shape "^1.3.4" + luxon "^1.25.0" prop-types "^15.7.2" re-reselect "^3.4.0" react-redux "^7.1.0" @@ -19720,6 +19721,11 @@ lru-queue@0.1: dependencies: es5-ext "~0.10.2" +luxon@^1.25.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"