interactionStart('drag', e)}
- onMouseUp={(e) => interactionStart('drop', e)}
- >
-
-
- {/* Resize handle */}
- interactionStart('resize', e)}
- onMouseUp={(e) => interactionStart('drop', e)}
- css={css`
- right: 0;
- bottom: 0;
- opacity: 0;
- margin: -2px;
- position: absolute;
- width: ${euiThemeVars.euiSizeL};
- height: ${euiThemeVars.euiSizeL};
- transition: opacity 0.2s, border 0.2s;
- border-radius: 7px 0 7px 0;
- border-bottom: 2px solid ${euiThemeVars.euiColorSuccess};
- border-right: 2px solid ${euiThemeVars.euiColorSuccess};
- :hover {
- background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.05)};
- cursor: se-resize;
+ /** Set initial styles based on state at mount to prevent styles from "blipping" */
+ const initialStyles = useMemo(() => {
+ const initialPanel = gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels[panelId];
+ return css`
+ grid-column-start: ${initialPanel.column + 1};
+ grid-column-end: ${initialPanel.column + 1 + initialPanel.width};
+ grid-row-start: ${initialPanel.row + 1};
+ grid-row-end: ${initialPanel.row + 1 + initialPanel.height};
+ `;
+ }, [gridLayoutStateManager, rowIndex, panelId]);
+
+ useEffect(
+ () => {
+ /** Update the styles of the panel via a subscription to prevent re-renders */
+ const styleSubscription = combineLatest([
+ gridLayoutStateManager.activePanel$,
+ gridLayoutStateManager.gridLayout$,
+ gridLayoutStateManager.runtimeSettings$,
+ ])
+ .pipe(skip(1)) // skip the first emit because the `initialStyles` will take care of it
+ .subscribe(([activePanel, gridLayout, runtimeSettings]) => {
+ const ref = gridLayoutStateManager.panelRefs.current[rowIndex][panelId];
+ const panel = gridLayout[rowIndex].panels[panelId];
+ if (!ref || !panel) return;
+
+ const currentInteractionEvent = gridLayoutStateManager.interactionEvent$.getValue();
+ if (panelId === activePanel?.id) {
+ // if the current panel is active, give it fixed positioning depending on the interaction event
+ const { position: draggingPosition } = activePanel;
+
+ ref.style.zIndex = `${euiThemeVars.euiZModal}`;
+ if (currentInteractionEvent?.type === 'resize') {
+ // if the current panel is being resized, ensure it is not shrunk past the size of a single cell
+ ref.style.width = `${Math.max(
+ draggingPosition.right - draggingPosition.left,
+ runtimeSettings.columnPixelWidth
+ )}px`;
+ ref.style.height = `${Math.max(
+ draggingPosition.bottom - draggingPosition.top,
+ runtimeSettings.rowHeight
+ )}px`;
+
+ // undo any "lock to grid" styles **except** for the top left corner, which stays locked
+ ref.style.gridColumnStart = `${panel.column + 1}`;
+ ref.style.gridRowStart = `${panel.row + 1}`;
+ ref.style.gridColumnEnd = ``;
+ ref.style.gridRowEnd = ``;
+ } else {
+ // if the current panel is being dragged, render it with a fixed position + size
+ ref.style.position = 'fixed';
+ ref.style.left = `${draggingPosition.left}px`;
+ ref.style.top = `${draggingPosition.top}px`;
+ ref.style.width = `${draggingPosition.right - draggingPosition.left}px`;
+ ref.style.height = `${draggingPosition.bottom - draggingPosition.top}px`;
+
+ // undo any "lock to grid" styles
+ ref.style.gridColumnStart = ``;
+ ref.style.gridRowStart = ``;
+ ref.style.gridColumnEnd = ``;
+ ref.style.gridRowEnd = ``;
+ }
+ } else {
+ ref.style.zIndex = '0';
+
+ // if the panel is not being dragged and/or resized, undo any fixed position styles
+ ref.style.position = '';
+ ref.style.left = ``;
+ ref.style.top = ``;
+ ref.style.width = ``;
+ ref.style.height = ``;
+
+ // and render the panel locked to the grid
+ ref.style.gridColumnStart = `${panel.column + 1}`;
+ ref.style.gridColumnEnd = `${panel.column + 1 + panel.width}`;
+ ref.style.gridRowStart = `${panel.row + 1}`;
+ ref.style.gridRowEnd = `${panel.row + 1 + panel.height}`;
}
- `}
- />
-
{
+ styleSubscription.unsubscribe();
+ };
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ );
+
+ return (
+
+
- {renderPanelContents(panelData.id)}
-
-
-
- );
-});
+ {/* drag handle */}
+
interactionStart('drag', e)}
+ onMouseUp={(e) => interactionStart('drop', e)}
+ >
+
+
+ {/* Resize handle */}
+
interactionStart('resize', e)}
+ onMouseUp={(e) => interactionStart('drop', e)}
+ css={css`
+ right: 0;
+ bottom: 0;
+ opacity: 0;
+ margin: -2px;
+ position: absolute;
+ width: ${euiThemeVars.euiSizeL};
+ height: ${euiThemeVars.euiSizeL};
+ transition: opacity 0.2s, border 0.2s;
+ border-radius: 7px 0 7px 0;
+ border-bottom: 2px solid ${euiThemeVars.euiColorSuccess};
+ border-right: 2px solid ${euiThemeVars.euiColorSuccess};
+ :hover {
+ background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.05)};
+ cursor: se-resize;
+ }
+ `}
+ />
+
+ {renderPanelContents(panelId)}
+
+
+
+ );
+ }
+);
diff --git a/packages/kbn-grid-layout/grid/grid_row.tsx b/packages/kbn-grid-layout/grid/grid_row.tsx
index 917f661c91740..e797cd570550a 100644
--- a/packages/kbn-grid-layout/grid/grid_row.tsx
+++ b/packages/kbn-grid-layout/grid/grid_row.tsx
@@ -7,41 +7,23 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import React, { forwardRef, useMemo, useRef } from 'react';
+import React, { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
+import { combineLatest, map, pairwise, skip } from 'rxjs';
import { EuiButtonIcon, EuiFlexGroup, EuiSpacer, EuiTitle, transparentize } from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { euiThemeVars } from '@kbn/ui-theme';
-import { useStateFromPublishingSubject } from '@kbn/presentation-publishing';
+import { DragPreview } from './drag_preview';
import { GridPanel } from './grid_panel';
-import {
- GridLayoutStateManager,
- GridRowData,
- PanelInteractionEvent,
- RuntimeGridSettings,
-} from './types';
-
-const gridColor = transparentize(euiThemeVars.euiColorSuccess, 0.2);
-const getGridBackgroundCSS = (settings: RuntimeGridSettings) => {
- const { gutterSize, columnPixelWidth, rowHeight } = settings;
- return css`
- background-position: top -${gutterSize / 2}px left -${gutterSize / 2}px;
- background-size: ${columnPixelWidth + gutterSize}px ${rowHeight + gutterSize}px;
- background-image: linear-gradient(to right, ${gridColor} 1px, transparent 1px),
- linear-gradient(to bottom, ${gridColor} 1px, transparent 1px);
- `;
-};
+import { GridLayoutStateManager, GridRowData, PanelInteractionEvent } from './types';
export const GridRow = forwardRef<
HTMLDivElement,
{
rowIndex: number;
- rowData: GridRowData;
toggleIsCollapsed: () => void;
- targetRowIndex: number | undefined;
- runtimeSettings: RuntimeGridSettings;
renderPanelContents: (panelId: string) => React.ReactNode;
setInteractionEvent: (interactionData?: PanelInteractionEvent) => void;
gridLayoutStateManager: GridLayoutStateManager;
@@ -49,10 +31,7 @@ export const GridRow = forwardRef<
>(
(
{
- rowData,
rowIndex,
- targetRowIndex,
- runtimeSettings,
toggleIsCollapsed,
renderPanelContents,
setInteractionEvent,
@@ -60,19 +39,122 @@ export const GridRow = forwardRef<
},
gridRef
) => {
- const dragPreviewRef = useRef
(null);
- const activePanel = useStateFromPublishingSubject(gridLayoutStateManager.activePanel$);
+ const currentRow = gridLayoutStateManager.gridLayout$.value[rowIndex];
+ const [panelIds, setPanelIds] = useState(Object.keys(currentRow.panels));
+ const [rowTitle, setRowTitle] = useState(currentRow.title);
+ const [isCollapsed, setIsCollapsed] = useState(currentRow.isCollapsed);
- const { gutterSize, columnCount, rowHeight } = runtimeSettings;
- const isGridTargeted = activePanel?.id && targetRowIndex === rowIndex;
+ const getRowCount = useCallback(
+ (row: GridRowData) => {
+ const maxRow = Object.values(row.panels).reduce((acc, panel) => {
+ return Math.max(acc, panel.row + panel.height);
+ }, 0);
+ return maxRow || 1;
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [rowIndex]
+ );
+
+ /** Set initial styles based on state at mount to prevent styles from "blipping" */
+ const initialStyles = useMemo(() => {
+ const initialRow = gridLayoutStateManager.gridLayout$.getValue()[rowIndex];
+ const runtimeSettings = gridLayoutStateManager.runtimeSettings$.getValue();
+ const { gutterSize, columnCount, rowHeight } = runtimeSettings;
+
+ return css`
+ gap: ${gutterSize}px;
+ grid-template-columns: repeat(
+ ${columnCount},
+ calc((100% - ${gutterSize * (columnCount - 1)}px) / ${columnCount})
+ );
+ grid-template-rows: repeat(${getRowCount(initialRow)}, ${rowHeight}px);
+ `;
+ }, [gridLayoutStateManager, getRowCount, rowIndex]);
+
+ useEffect(
+ () => {
+ /** Update the styles of the grid row via a subscription to prevent re-renders */
+ const styleSubscription = combineLatest([
+ gridLayoutStateManager.interactionEvent$,
+ gridLayoutStateManager.gridLayout$,
+ gridLayoutStateManager.runtimeSettings$,
+ ])
+ .pipe(skip(1)) // skip the first emit because the `initialStyles` will take care of it
+ .subscribe(([interactionEvent, gridLayout, runtimeSettings]) => {
+ const rowRef = gridLayoutStateManager.rowRefs.current[rowIndex];
+ if (!rowRef) return;
+
+ const { gutterSize, rowHeight, columnPixelWidth } = runtimeSettings;
- // calculate row count based on the number of rows needed to fit all panels
- const rowCount = useMemo(() => {
- const maxRow = Object.values(rowData.panels).reduce((acc, panel) => {
- return Math.max(acc, panel.row + panel.height);
- }, 0);
- return maxRow || 1;
- }, [rowData]);
+ rowRef.style.gridTemplateRows = `repeat(${getRowCount(
+ gridLayout[rowIndex]
+ )}, ${rowHeight}px)`;
+
+ const targetRow = interactionEvent?.targetRowIndex;
+ if (rowIndex === targetRow && interactionEvent?.type !== 'drop') {
+ // apply "targetted row" styles
+ const gridColor = transparentize(euiThemeVars.euiColorSuccess, 0.2);
+ rowRef.style.backgroundPosition = `top -${gutterSize / 2}px left -${
+ gutterSize / 2
+ }px`;
+ rowRef.style.backgroundSize = ` ${columnPixelWidth + gutterSize}px ${
+ rowHeight + gutterSize
+ }px`;
+ rowRef.style.backgroundImage = `linear-gradient(to right, ${gridColor} 1px, transparent 1px),
+ linear-gradient(to bottom, ${gridColor} 1px, transparent 1px)`;
+ rowRef.style.backgroundColor = `${transparentize(
+ euiThemeVars.euiColorSuccess,
+ 0.05
+ )}`;
+ } else {
+ // undo any "targetted row" styles
+ rowRef.style.backgroundPosition = ``;
+ rowRef.style.backgroundSize = ``;
+ rowRef.style.backgroundImage = ``;
+ rowRef.style.backgroundColor = `transparent`;
+ }
+ });
+
+ /**
+ * The things that should trigger a re-render are title, collapsed state, and panel ids - panel positions
+ * are being controlled via CSS styles, so they do not need to trigger a re-render. This subscription ensures
+ * that the row will re-render when one of those three things changes.
+ */
+ const rowStateSubscription = gridLayoutStateManager.gridLayout$
+ .pipe(
+ skip(1), // we are initializing all row state with a value, so skip the initial emit
+ map((gridLayout) => {
+ return {
+ title: gridLayout[rowIndex].title,
+ isCollapsed: gridLayout[rowIndex].isCollapsed,
+ panelIds: Object.keys(gridLayout[rowIndex].panels),
+ };
+ }),
+ pairwise()
+ )
+ .subscribe(([oldRowData, newRowData]) => {
+ if (oldRowData.title !== newRowData.title) setRowTitle(newRowData.title);
+ if (oldRowData.isCollapsed !== newRowData.isCollapsed)
+ setIsCollapsed(newRowData.isCollapsed);
+ if (
+ oldRowData.panelIds.length !== newRowData.panelIds.length ||
+ !(
+ oldRowData.panelIds.every((p) => newRowData.panelIds.includes(p)) &&
+ newRowData.panelIds.every((p) => oldRowData.panelIds.includes(p))
+ )
+ ) {
+ setPanelIds(newRowData.panelIds);
+ }
+ });
+
+ return () => {
+ styleSubscription.unsubscribe();
+ rowStateSubscription.unsubscribe();
+ };
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [rowIndex]
+ );
return (
<>
@@ -85,51 +167,43 @@ export const GridRow = forwardRef<
aria-label={i18n.translate('kbnGridLayout.row.toggleCollapse', {
defaultMessage: 'Toggle collapse',
})}
- iconType={rowData.isCollapsed ? 'arrowRight' : 'arrowDown'}
+ iconType={isCollapsed ? 'arrowRight' : 'arrowDown'}
onClick={toggleIsCollapsed}
/>
- {rowData.title}
+ {rowTitle}
>
)}
- {!rowData.isCollapsed && (
+ {!isCollapsed && (
- {Object.values(rowData.panels).map((panelData) => (
+ {panelIds.map((panelId) => (
{
e.preventDefault();
e.stopPropagation();
- const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][panelData.id];
+ const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][panelId];
if (!panelRef) return;
const panelRect = panelRef.getBoundingClientRect();
setInteractionEvent({
type,
- id: panelData.id,
+ id: panelId,
panelDiv: panelRef,
targetRowIndex: rowIndex,
mouseOffsets: {
@@ -144,32 +218,12 @@ export const GridRow = forwardRef<
if (!gridLayoutStateManager.panelRefs.current[rowIndex]) {
gridLayoutStateManager.panelRefs.current[rowIndex] = {};
}
- gridLayoutStateManager.panelRefs.current[rowIndex][panelData.id] = element;
+ gridLayoutStateManager.panelRefs.current[rowIndex][panelId] = element;
}}
/>
))}
- {/* render the drag preview if this row is currently being targetted */}
- {isGridTargeted && (
-
- )}
+
)}
>
diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts
index dfbd013d3642a..bd6343b9e5652 100644
--- a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts
+++ b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts
@@ -8,6 +8,7 @@
*/
import { useEffect, useRef } from 'react';
+import deepEqual from 'fast-deep-equal';
import { resolveGridRow } from './resolve_grid_row';
import { GridLayoutStateManager, GridPanelData } from './types';
@@ -121,6 +122,7 @@ export const useGridLayoutEvents = ({
maxColumn
);
const targetRow = Math.max(Math.round(localYCoordinate / (rowHeight + gutterSize)), 0);
+
const requestedGridData = { ...currentGridData };
if (isResize) {
requestedGridData.width = Math.max(targetColumn - requestedGridData.column, 1);
@@ -154,8 +156,9 @@ export const useGridLayoutEvents = ({
const resolvedOriginGrid = resolveGridRow(originGrid);
nextLayout[lastRowIndex] = resolvedOriginGrid;
}
-
- gridLayout$.next(nextLayout);
+ if (!deepEqual(currentLayout, nextLayout)) {
+ gridLayout$.next(nextLayout);
+ }
}
};
diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts
index cdb99a9ebbfd0..fe657ae253107 100644
--- a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts
+++ b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts
@@ -7,10 +7,11 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import { i18n } from '@kbn/i18n';
import { useEffect, useMemo, useRef } from 'react';
-import { BehaviorSubject, combineLatest, debounceTime, map, retry } from 'rxjs';
+import { BehaviorSubject, debounceTime } from 'rxjs';
+
import useResizeObserver, { type ObservedSize } from 'use-resize-observer/polyfilled';
+
import {
ActivePanel,
GridLayoutData,
@@ -43,10 +44,14 @@ export const useGridLayoutState = ({
...gridSettings,
columnPixelWidth: 0,
});
+ const panelIds$ = new BehaviorSubject(
+ initialLayout.map(({ panels }) => Object.keys(panels))
+ );
return {
rowRefs,
panelRefs,
+ panelIds$,
gridLayout$,
activePanel$,
gridDimensions$,
@@ -67,117 +72,12 @@ export const useGridLayoutState = ({
const columnPixelWidth =
(elementWidth - gridSettings.gutterSize * (gridSettings.columnCount - 1)) /
gridSettings.columnCount;
- gridLayoutStateManager.runtimeSettings$.next({ ...gridSettings, columnPixelWidth });
- });
-
- /**
- * on layout change, update the styles of every panel so that it renders as expected
- */
- const onLayoutChangeSubscription = combineLatest([
- gridLayoutStateManager.gridLayout$,
- gridLayoutStateManager.activePanel$,
- ])
- .pipe(
- map(([gridLayout, activePanel]) => {
- // wait for all panel refs to be ready before continuing
- for (let rowIndex = 0; rowIndex < gridLayout.length; rowIndex++) {
- const currentRow = gridLayout[rowIndex];
- Object.keys(currentRow.panels).forEach((key) => {
- const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][key];
- if (!panelRef && !currentRow.isCollapsed) {
- throw new Error(
- i18n.translate('kbnGridLayout.panelRefNotFoundError', {
- defaultMessage: 'Panel reference does not exist', // the retry will catch this error
- })
- );
- }
- });
- }
- return { gridLayout, activePanel };
- }),
- retry({ delay: 10 }) // retry until panel references all exist
- )
- .subscribe(({ gridLayout, activePanel }) => {
- const runtimeSettings = gridLayoutStateManager.runtimeSettings$.getValue();
- const currentInteractionEvent = gridLayoutStateManager.interactionEvent$.getValue();
- for (let rowIndex = 0; rowIndex < gridLayout.length; rowIndex++) {
- if (activePanel && rowIndex !== currentInteractionEvent?.targetRowIndex) {
- /**
- * If there is an interaction event happening but the current row is not being targetted, it
- * does not need to be re-rendered; so, skip setting the panel styles of this row.
- *
- * If there is **no** interaction event, then this is the initial render so the styles of every
- * panel should be initialized; so, don't skip setting the panel styles.
- */
- continue;
- }
-
- // re-render the targetted row
- const currentRow = gridLayout[rowIndex];
- Object.keys(currentRow.panels).forEach((key) => {
- const panel = currentRow.panels[key];
- const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][key];
- if (!panelRef) {
- return;
- }
-
- const isResize = currentInteractionEvent?.type === 'resize';
- if (panel.id === activePanel?.id) {
- // if the current panel is active, give it fixed positioning depending on the interaction event
- const { position: draggingPosition } = activePanel;
-
- if (isResize) {
- // if the current panel is being resized, ensure it is not shrunk past the size of a single cell
- panelRef.style.width = `${Math.max(
- draggingPosition.right - draggingPosition.left,
- runtimeSettings.columnPixelWidth
- )}px`;
- panelRef.style.height = `${Math.max(
- draggingPosition.bottom - draggingPosition.top,
- runtimeSettings.rowHeight
- )}px`;
-
- // undo any "lock to grid" styles **except** for the top left corner, which stays locked
- panelRef.style.gridColumnStart = `${panel.column + 1}`;
- panelRef.style.gridRowStart = `${panel.row + 1}`;
- panelRef.style.gridColumnEnd = ``;
- panelRef.style.gridRowEnd = ``;
- } else {
- // if the current panel is being dragged, render it with a fixed position + size
- panelRef.style.position = 'fixed';
- panelRef.style.left = `${draggingPosition.left}px`;
- panelRef.style.top = `${draggingPosition.top}px`;
- panelRef.style.width = `${draggingPosition.right - draggingPosition.left}px`;
- panelRef.style.height = `${draggingPosition.bottom - draggingPosition.top}px`;
-
- // undo any "lock to grid" styles
- panelRef.style.gridColumnStart = ``;
- panelRef.style.gridRowStart = ``;
- panelRef.style.gridColumnEnd = ``;
- panelRef.style.gridRowEnd = ``;
- }
- } else {
- // if the panel is not being dragged and/or resized, undo any fixed position styles
- panelRef.style.position = '';
- panelRef.style.left = ``;
- panelRef.style.top = ``;
- panelRef.style.width = ``;
- panelRef.style.height = ``;
-
- // and render the panel locked to the grid
- panelRef.style.gridColumnStart = `${panel.column + 1}`;
- panelRef.style.gridColumnEnd = `${panel.column + 1 + panel.width}`;
- panelRef.style.gridRowStart = `${panel.row + 1}`;
- panelRef.style.gridRowEnd = `${panel.row + 1 + panel.height}`;
- }
- });
- }
+ gridLayoutStateManager.runtimeSettings$.next({ ...gridSettings, columnPixelWidth });
});
return () => {
resizeSubscription.unsubscribe();
- onLayoutChangeSubscription.unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
diff --git a/packages/kbn-grid-layout/tsconfig.json b/packages/kbn-grid-layout/tsconfig.json
index 5a1888db0dc64..f0dd3232a42d5 100644
--- a/packages/kbn-grid-layout/tsconfig.json
+++ b/packages/kbn-grid-layout/tsconfig.json
@@ -17,7 +17,6 @@
"target/**/*"
],
"kbn_references": [
- "@kbn/presentation-publishing",
"@kbn/ui-theme",
"@kbn/i18n",
]
diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts
index e926007f77f25..b146be6f6e252 100644
--- a/packages/kbn-management/settings/setting_ids/index.ts
+++ b/packages/kbn-management/settings/setting_ids/index.ts
@@ -83,7 +83,6 @@ export const DISCOVER_SAMPLE_SIZE_ID = 'discover:sampleSize';
export const DISCOVER_SEARCH_FIELDS_FROM_SOURCE_ID = 'discover:searchFieldsFromSource';
export const DISCOVER_SEARCH_ON_PAGE_LOAD_ID = 'discover:searchOnPageLoad';
export const DISCOVER_SHOW_FIELD_STATISTICS_ID = 'discover:showFieldStatistics';
-export const DISCOVER_SHOW_LEGACY_FIELD_TOP_VALUES_ID = 'discover:showLegacyFieldTopValues';
export const DISCOVER_SHOW_MULTI_FIELDS_ID = 'discover:showMultiFields';
export const DISCOVER_SORT_DEFAULT_ORDER_ID = 'discover:sort:defaultOrder';
export const DOC_TABLE_HIDE_TIME_COLUMNS_ID = 'doc_table:hideTimeColumn';
@@ -122,10 +121,6 @@ export const OBSERVABILITY_APM_TRACE_EXPLORER_TAB_ID = 'observability:apmTraceEx
export const OBSERVABILITY_ENABLE_AWS_LAMBDA_METRICS_ID = 'observability:enableAwsLambdaMetrics';
export const OBSERVABILITY_ENABLE_COMPARISON_BY_DEFAULT_ID =
'observability:enableComparisonByDefault';
-export const OBSERVABILITY_ENABLE_INFRASTRUCTURE_HOSTS_VIEW_ID =
- 'observability:enableInfrastructureHostsView';
-export const OBSERVABILITY_ENABLE_INFRASTRUCTURE_CONTAINER_ASSET_VIEW_ID =
- 'observability:enableInfrastructureContainerAssetView';
export const OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID =
'observability:enableInfrastructureAssetCustomDashboards';
export const OBSERVABILITY_ENABLE_INSPECT_ES_QUERIES_ID = 'observability:enableInspectEsQueries';
diff --git a/packages/kbn-rrule/rrule.ts b/packages/kbn-rrule/rrule.ts
index 9b5eea7d4979f..43e89ee209cb7 100644
--- a/packages/kbn-rrule/rrule.ts
+++ b/packages/kbn-rrule/rrule.ts
@@ -16,6 +16,7 @@ export enum Frequency {
DAILY = 3,
HOURLY = 4,
MINUTELY = 5,
+ SECONDLY = 6,
}
export enum Weekday {
@@ -270,6 +271,13 @@ export const getNextRecurrences = function ({
...opts,
});
}
+ case Frequency.SECONDLY: {
+ const nextRef = moment(refDT).add(interval, 's');
+ return getMinuteOfRecurrences({
+ refDT: nextRef,
+ ...opts,
+ });
+ }
}
};
diff --git a/packages/kbn-search-connectors/components/configuration/connector_configuration_field.tsx b/packages/kbn-search-connectors/components/configuration/connector_configuration_field.tsx
index a43163caa6091..08b91ffc1842c 100644
--- a/packages/kbn-search-connectors/components/configuration/connector_configuration_field.tsx
+++ b/packages/kbn-search-connectors/components/configuration/connector_configuration_field.tsx
@@ -52,7 +52,7 @@ export const ConfigInputField: React.FC = ({
isLoading,
validateAndSetConfigValue,
}) => {
- const { isValid, required, placeholder, value } = configEntry;
+ const { isValid, required, placeholder, value, label } = configEntry;
const [innerValue, setInnerValue] = useState(value);
return (
= ({
validateAndSetConfigValue(event.target.value);
}}
placeholder={placeholder}
+ aria-label={label}
/>
);
};
@@ -74,7 +75,7 @@ export const ConfigInputTextArea: React.FC = ({
configEntry,
validateAndSetConfigValue,
}) => {
- const { isValid, required, placeholder, value } = configEntry;
+ const { isValid, required, placeholder, value, label } = configEntry;
const [innerValue, setInnerValue] = useState(value);
return (
= ({
validateAndSetConfigValue(event.target.value);
}}
placeholder={placeholder}
+ aria-label={label}
/>
);
};
@@ -129,7 +131,7 @@ export const ConfigInputPassword: React.FC = ({
configEntry,
validateAndSetConfigValue,
}) => {
- const { required, value } = configEntry;
+ const { required, value, label } = configEntry;
const [innerValue, setInnerValue] = useState(value);
return (
= ({
setInnerValue(event.target.value);
validateAndSetConfigValue(event.target.value);
}}
+ aria-label={label}
/>
);
};
@@ -170,6 +173,7 @@ export const ConnectorConfigurationField: React.FC {
validateAndSetConfigValue(event.target.value);
}}
+ aria-label={label}
/>
) : (
{
validateAndSetConfigValue(id);
}}
+ aria-label={label}
/>
);
@@ -227,6 +232,7 @@ export const ConnectorConfigurationField: React.FC {
validateAndSetConfigValue(event.target.checked);
}}
+ aria-label={label}
/>
{!hasPlatinumLicense && (
diff --git a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts
index 724cf5bc2b25e..964359cdb7ee5 100644
--- a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts
+++ b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts
@@ -11,6 +11,7 @@ import Url from 'url';
import { resolve } from 'path';
import type { ToolingLog } from '@kbn/tooling-log';
import getPort from 'get-port';
+import getopts from 'getopts';
import { REPO_ROOT } from '@kbn/repo-info';
import type { ArtifactLicense, ServerlessProjectType } from '@kbn/es';
import { isServerlessProjectType, extractAndArchiveLogs } from '@kbn/es/src/utils';
@@ -196,12 +197,8 @@ function getESServerlessOptions(
(config.get('kbnTestServer.serverArgs') as string[])) ||
[];
- const projectType = kbnServerArgs
- .filter((arg) => arg.startsWith('--serverless'))
- .reduce((acc, arg) => {
- const match = arg.match(/--serverless[=\s](\w+)/);
- return acc + (match ? match[1] : '');
- }, '') as ServerlessProjectType;
+ const options = getopts(kbnServerArgs);
+ const projectType = options.serverless as ServerlessProjectType;
if (!isServerlessProjectType(projectType)) {
throw new Error(`Unsupported serverless projectType: ${projectType}`);
diff --git a/packages/kbn-test/src/jest/resolver.js b/packages/kbn-test/src/jest/resolver.js
index 8f985e9463962..aab1b0f597284 100644
--- a/packages/kbn-test/src/jest/resolver.js
+++ b/packages/kbn-test/src/jest/resolver.js
@@ -51,6 +51,13 @@ module.exports = (request, options) => {
});
}
+ if (request === '@launchdarkly/js-sdk-common') {
+ return resolve.sync('@launchdarkly/js-sdk-common/dist/cjs/index.cjs', {
+ basedir: options.basedir,
+ extensions: options.extensions,
+ });
+ }
+
if (request === `elastic-apm-node`) {
return APM_AGENT_MOCK;
}
diff --git a/packages/kbn-test/src/mocha/junit_report_generation.test.js b/packages/kbn-test/src/mocha/junit_report_generation.test.js
index caf023a795154..6dbc8bf6cf1f8 100644
--- a/packages/kbn-test/src/mocha/junit_report_generation.test.js
+++ b/packages/kbn-test/src/mocha/junit_report_generation.test.js
@@ -55,9 +55,12 @@ describe('dev/mocha/junit report generation', () => {
const [testsuite] = report.testsuites.testsuite;
expect(testsuite.$.time).toMatch(DURATION_REGEX);
expect(testsuite.$.timestamp).toMatch(ISO_DATE_SEC_REGEX);
- expect(testsuite.$).toEqual({
- 'command-line':
- 'node scripts/jest --config=packages/kbn-test/jest.config.js --runInBand --coverage=false --passWithNoTests',
+ const expectedCommandLine = process.env.CI
+ ? 'node scripts/jest --config=packages/kbn-test/jest.config.js --runInBand --coverage=false --passWithNoTests'
+ : 'node node_modules/jest-worker/build/workers/processChild.js';
+
+ expect(testsuite.$).toMatchObject({
+ 'command-line': expectedCommandLine,
failures: '2',
name: 'test',
skipped: '1',
diff --git a/packages/serverless/settings/observability_project/index.ts b/packages/serverless/settings/observability_project/index.ts
index 44f30e4320463..d04b0238c5510 100644
--- a/packages/serverless/settings/observability_project/index.ts
+++ b/packages/serverless/settings/observability_project/index.ts
@@ -27,8 +27,6 @@ export const OBSERVABILITY_PROJECT_SETTINGS = [
settings.OBSERVABILITY_APM_TRACE_EXPLORER_TAB_ID,
settings.OBSERVABILITY_ENABLE_AWS_LAMBDA_METRICS_ID,
settings.OBSERVABILITY_APM_ENABLE_CRITICAL_PATH_ID,
- settings.OBSERVABILITY_ENABLE_INFRASTRUCTURE_HOSTS_VIEW_ID,
- settings.OBSERVABILITY_ENABLE_INFRASTRUCTURE_CONTAINER_ASSET_VIEW_ID,
settings.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID,
settings.OBSERVABILITY_LOGS_EXPLORER_ALLOWED_DATA_VIEWS_ID,
settings.OBSERVABILITY_APM_ENABLE_TABLE_SEARCH_BAR,
diff --git a/packages/shared-ux/button/exit_full_screen/src/services.tsx b/packages/shared-ux/button/exit_full_screen/src/services.tsx
index a167b2b116bf0..9497a6ed34468 100644
--- a/packages/shared-ux/button/exit_full_screen/src/services.tsx
+++ b/packages/shared-ux/button/exit_full_screen/src/services.tsx
@@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import React, { FC, useContext, PropsWithChildren } from 'react';
+import React, { FC, useContext, PropsWithChildren, useCallback } from 'react';
import type {
Services,
@@ -37,12 +37,16 @@ export const ExitFullScreenButtonProvider: FC
> = ({ children, ...services }) => {
+ const setIsFullscreen = useCallback(
+ (isFullscreen: boolean) => {
+ services.coreStart.chrome.setIsVisible(!isFullscreen);
+ },
+ [services.coreStart.chrome]
+ );
return (
{
- services.coreStart.chrome.setIsVisible(!isFullscreen);
- },
+ setIsFullscreen,
customBranding$: services.coreStart.customBranding.customBranding$,
}}
>
diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts
index e442a0efeea05..f31ec223e3d9b 100644
--- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts
+++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts
@@ -59,7 +59,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"action": "0e6fc0b74c7312a8c11ff6b14437b93a997358b8",
"action_task_params": "b50cb5c8a493881474918e8d4985e61374ca4c30",
"ad_hoc_run_params": "d4e3c5c794151d0a4f5c71e886b2aa638da73ad2",
- "alert": "05b07040b12ff45ab642f47464e8a6c903cf7b86",
+ "alert": "556a03378f5ee1c31593c3a37c66b54555ee14ff",
"api_key_pending_invalidation": "8f5554d1984854011b8392d9a6f7ef985bcac03c",
"apm-custom-dashboards": "b67128f78160c288bd7efe25b2da6e2afd5e82fc",
"apm-indices": "8a2d68d415a4b542b26b0d292034a28ffac6fed4",
diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts
index d764978c9db92..a3925b3a04f24 100644
--- a/src/dev/build/tasks/os_packages/docker_generator/run.ts
+++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts
@@ -51,7 +51,7 @@ export async function runDockerGenerator(
*/
if (flags.baseImage === 'wolfi')
baseImageName =
- 'docker.elastic.co/wolfi/chainguard-base:latest@sha256:de4d5b06ee2074eb716f29e72b170346fd4715e5f083fc83a378603ce5bd9ced';
+ 'docker.elastic.co/wolfi/chainguard-base:latest@sha256:18153942f0d6e97bc6131cd557c7ed3be6e892846a5df0760896eb8d15b1b236';
let imageFlavor = '';
if (flags.baseImage === 'ubi') imageFlavor += `-ubi`;
diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx
index ae9c5a844dcc3..467e6afa6a897 100644
--- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx
+++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx
@@ -842,7 +842,7 @@ describe('XYChart component', () => {
const lineArea = dataLayers.find(LineSeries).at(0);
const expectedSeriesStyle = expect.objectContaining({
point: expect.objectContaining({
- visible: showPoints ? 'always' : 'never',
+ visible: showPoints ? 'always' : 'auto',
}),
});
expect(lineArea.prop('areaSeriesStyle')).toEqual(expectedSeriesStyle);
diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx
index 473562b63ad5e..942909880f301 100644
--- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx
+++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx
@@ -298,7 +298,7 @@ export const getSeriesName: GetSeriesNameFn = (
const getPointConfig: GetPointConfigFn = ({ markSizeAccessor, showPoints, pointsRadius }) => {
return {
- visible: showPoints || markSizeAccessor ? 'always' : 'never',
+ visible: showPoints || markSizeAccessor ? 'always' : 'auto',
radius: pointsRadius,
fill: markSizeAccessor ? ColorVariant.Series : undefined,
};
diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx
index 034ee2f8e45f4..59b3b3926060a 100644
--- a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx
+++ b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx
@@ -50,11 +50,16 @@ export const DashboardListingPage = ({
}, []);
useEffect(() => {
- coreServices.chrome.setBreadcrumbs([
+ coreServices.chrome.setBreadcrumbs(
+ [
+ {
+ text: getDashboardBreadcrumb(),
+ },
+ ],
{
- text: getDashboardBreadcrumb(),
- },
- ]);
+ project: { value: [] },
+ }
+ );
if (serverlessService) {
// if serverless breadcrumbs available,
diff --git a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx
index 664a3c43a8d9d..027d2aee62b15 100644
--- a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx
+++ b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx
@@ -10,7 +10,7 @@
import { debounce } from 'lodash';
import classNames from 'classnames';
import useResizeObserver from 'use-resize-observer/polyfilled';
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { EuiPortal } from '@elastic/eui';
import { ReactEmbeddableRenderer, ViewMode } from '@kbn/embeddable-plugin/public';
@@ -22,10 +22,7 @@ import {
ControlGroupSerializedState,
} from '@kbn/controls-plugin/public';
import { CONTROL_GROUP_TYPE } from '@kbn/controls-plugin/common';
-import {
- useBatchedPublishingSubjects,
- useStateFromPublishingSubject,
-} from '@kbn/presentation-publishing';
+import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { DashboardGrid } from '../grid';
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen';
@@ -44,7 +41,7 @@ export const useDebouncedWidthObserver = (skipDebounce = false, wait = 100) => {
return { ref, width };
};
-export const DashboardViewportComponent = () => {
+export const DashboardViewport = () => {
const dashboardApi = useDashboardApi();
const [hasControls, setHasControls] = useState(false);
const [
@@ -70,6 +67,9 @@ export const DashboardViewportComponent = () => {
dashboardApi.uuid$,
dashboardApi.fullScreenMode$
);
+ const onExit = useCallback(() => {
+ dashboardApi.setFullScreenMode(false);
+ }, [dashboardApi]);
const panelCount = useMemo(() => {
return Object.keys(panels).length;
@@ -142,6 +142,11 @@ export const DashboardViewportComponent = () => {
/>
) : null}
+ {fullScreenMode && (
+