mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
[Discover] Redesign for the grid, panels and sidebar v1 (#165866)
## Summary
### Part 1
- Resolves https://github.com/elastic/kibana/issues/164287
- Closes https://github.com/elastic/kibana/issues/146339
- Previously separate PR https://github.com/elastic/kibana/pull/164187
Changes:
- ~~swaps checkbox and row selection~~
- removes vertical borders
- adds rows highlight
- increases cell padding
- adds row stripes
- updates header background
- removes grey background from field name and makes it bolder (part of
https://github.com/elastic/kibana/issues/164634)
- updates Surrounding Documents side paddings
### Part 2
- Resolves https://github.com/elastic/kibana/issues/164661
- Previously separate PR https://github.com/elastic/kibana/pull/165687
Changes:
- removes background from panels, tabs and sidebar
- updates "Add a field" button style
- removes shadow from field list items
- makes field search compact
### Part 3
- Resolves https://github.com/elastic/kibana/issues/164662
Changes:
- wraps "Add a field" button in its own container with a top border
- ~~adds a drag handle to sidebar items~~
- ~~adds new Show/Hide buttons to toggle sidebar~~ moves sidebar toggle
button from discover plugin to unified field list
- reduces spaces between sidebar items from 4px to 2px
- reduces padding on Single Document page
- removes border above grid tabs
<img width="600" alt="Screenshot 2023-09-07 at 14 39 48"
src="976db247
-fd70-4c9b-8634-552ece45b522">
Please note that "auto" row height is in a separate PR which is also
ready for review https://github.com/elastic/kibana/pull/164218
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Davis McPhee <davismcphee@hotmail.com>
Co-authored-by: Davis McPhee <davis.mcphee@elastic.co>
This commit is contained in:
parent
6eea595153
commit
6cb937a37a
49 changed files with 807 additions and 280 deletions
|
@ -33,6 +33,8 @@ const getCreationOptions: UnifiedFieldListSidebarContainerProps['getCreationOpti
|
||||||
originatingApp: PLUGIN_ID,
|
originatingApp: PLUGIN_ID,
|
||||||
localStorageKeyPrefix: 'examples',
|
localStorageKeyPrefix: 'examples',
|
||||||
timeRangeUpdatesType: 'timefilter',
|
timeRangeUpdatesType: 'timefilter',
|
||||||
|
compressed: true,
|
||||||
|
showSidebarToggleButton: true,
|
||||||
disablePopularFields: true,
|
disablePopularFields: true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,6 +119,28 @@ describe('DragDrop', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('dragstart sets dragClassName as expected', async () => {
|
||||||
|
const dndDispatch = jest.fn();
|
||||||
|
const component = mount(
|
||||||
|
<ChildDragDropProvider value={[{ ...defaultContextState, dragging: undefined }, dndDispatch]}>
|
||||||
|
<DragDrop value={value} draggable={true} order={[2, 0, 1, 0]} dragClassName="dragTest">
|
||||||
|
<button>Hi!</button>
|
||||||
|
</DragDrop>
|
||||||
|
</ChildDragDropProvider>
|
||||||
|
);
|
||||||
|
const dragDrop = component.find('[data-test-subj="testDragDrop"]').at(0);
|
||||||
|
|
||||||
|
expect(dragDrop.getDOMNode().querySelector('.dragTest')).toBeNull();
|
||||||
|
dragDrop.simulate('dragstart', { dataTransfer });
|
||||||
|
expect(dragDrop.getDOMNode().querySelector('.dragTest')).toBeDefined();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dragDrop.getDOMNode().querySelector('.dragTest')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
test('drop resets all the things', async () => {
|
test('drop resets all the things', async () => {
|
||||||
const preventDefault = jest.fn();
|
const preventDefault = jest.fn();
|
||||||
const stopPropagation = jest.fn();
|
const stopPropagation = jest.fn();
|
||||||
|
|
|
@ -42,6 +42,10 @@ interface BaseProps {
|
||||||
* The CSS class(es) for the root element.
|
* The CSS class(es) for the root element.
|
||||||
*/
|
*/
|
||||||
className?: string;
|
className?: string;
|
||||||
|
/**
|
||||||
|
* CSS class to apply when the item is being dragged
|
||||||
|
*/
|
||||||
|
dragClassName?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event handler that fires when an item
|
* The event handler that fires when an item
|
||||||
|
@ -212,6 +216,7 @@ const removeSelection = () => {
|
||||||
const DragInner = memo(function DragInner({
|
const DragInner = memo(function DragInner({
|
||||||
dataTestSubj,
|
dataTestSubj,
|
||||||
className,
|
className,
|
||||||
|
dragClassName,
|
||||||
value,
|
value,
|
||||||
children,
|
children,
|
||||||
dndDispatch,
|
dndDispatch,
|
||||||
|
@ -305,6 +310,18 @@ const DragInner = memo(function DragInner({
|
||||||
// so we know we have DraggableProps if we reach this code.
|
// so we know we have DraggableProps if we reach this code.
|
||||||
if (e && 'dataTransfer' in e) {
|
if (e && 'dataTransfer' in e) {
|
||||||
e.dataTransfer.setData('text', value.humanData.label);
|
e.dataTransfer.setData('text', value.humanData.label);
|
||||||
|
|
||||||
|
// Apply an optional class to the element being dragged so the ghost
|
||||||
|
// can be styled. We must add it to the actual element for a single
|
||||||
|
// frame before removing it so the ghost picks up the styling.
|
||||||
|
const current = e.currentTarget;
|
||||||
|
|
||||||
|
if (dragClassName && !current.classList.contains(dragClassName)) {
|
||||||
|
current.classList.add(dragClassName);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
current.classList.remove(dragClassName);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chrome causes issues if you try to render from within a
|
// Chrome causes issues if you try to render from within a
|
||||||
|
|
|
@ -3,32 +3,6 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: $euiBorderRadius;
|
|
||||||
|
|
||||||
.euiDataGrid__controls {
|
|
||||||
border: none;
|
|
||||||
border-bottom: $euiBorderThin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.euiDataGridRowCell.euiDataGridRowCell--firstColumn {
|
|
||||||
border-left: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.euiDataGridRowCell.euiDataGridRowCell--lastColumn {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unifiedDataTable__table .euiDataGridRowCell:first-of-type,
|
|
||||||
.unifiedDataTable__table .euiDataGrid--headerShade.euiDataGrid--bordersAll .euiDataGridHeaderCell:first-of-type {
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.euiDataGridRowCell:last-of-type,
|
|
||||||
.euiDataGridHeaderCell:last-of-type {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.unifiedDataTable__cellValue {
|
.unifiedDataTable__cellValue {
|
||||||
|
@ -57,6 +31,29 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
.euiDataGrid__content {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.euiDataGrid__controls {
|
||||||
|
border-top: $euiBorderThin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.euiDataGrid--headerUnderline .euiDataGridHeaderCell {
|
||||||
|
border-bottom: $euiBorderThin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.euiDataGridRowCell.euiDataGridRowCell--controlColumn[data-gridcell-column-id='openDetails'],
|
||||||
|
.euiDataGridRowCell.euiDataGridRowCell--controlColumn[data-gridcell-column-id='select'] {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.euiDataGrid--rowHoverHighlight .euiDataGridRow:hover,
|
||||||
|
.euiDataGrid--rowHoverHighlight .euiDataGridRow:hover .euiDataGridRowCell__contentByHeight + .euiDataGridRowCell__expandActions {
|
||||||
|
background-color: tintOrShade($euiColorLightShade, 50%, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.unifiedDataTable__table {
|
.unifiedDataTable__table {
|
||||||
|
@ -65,14 +62,6 @@
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unifiedDataTable__footer {
|
|
||||||
flex-shrink: 0;
|
|
||||||
background-color: $euiColorLightShade;
|
|
||||||
padding: $euiSize / 2 $euiSize;
|
|
||||||
margin-top: $euiSize / 4;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unifiedDataTable__flyoutHeader {
|
.unifiedDataTable__flyoutHeader {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +107,35 @@
|
||||||
@include euiTextTruncate;
|
@include euiTextTruncate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unifiedDataTable__rowControl {
|
||||||
|
// fine-tuning the vertical alignment with the text for any row height setting
|
||||||
|
margin-top: -3px;
|
||||||
|
.euiDataGridRowCell__truncate & { // "Single line" row height setting
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unifiedDataTable__descriptionList {
|
||||||
|
// force the content truncation when "Single line" row height setting is active
|
||||||
|
.euiDataGridRowCell__truncate & {
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unifiedDataTable__descriptionListTitle {
|
||||||
|
margin-inline: 0 0;
|
||||||
|
padding-inline: 0;
|
||||||
|
background: transparent;
|
||||||
|
font-weight: $euiFontWeightBold;
|
||||||
|
}
|
||||||
|
|
||||||
.unifiedDataTable__descriptionListDescription {
|
.unifiedDataTable__descriptionListDescription {
|
||||||
|
margin-inline: $euiSizeS $euiSizeS;
|
||||||
|
padding-inline: 0;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
|
||||||
|
|
|
@ -218,7 +218,7 @@ describe('Data table columns', function () {
|
||||||
</div>,
|
</div>,
|
||||||
"displayAsText": "timestamp",
|
"displayAsText": "timestamp",
|
||||||
"id": "timestamp",
|
"id": "timestamp",
|
||||||
"initialWidth": 210,
|
"initialWidth": 212,
|
||||||
"isSortable": true,
|
"isSortable": true,
|
||||||
"schema": "datetime",
|
"schema": "datetime",
|
||||||
"visibleCellActions": undefined,
|
"visibleCellActions": undefined,
|
||||||
|
@ -406,7 +406,7 @@ describe('Data table columns', function () {
|
||||||
</div>,
|
</div>,
|
||||||
"displayAsText": "timestamp",
|
"displayAsText": "timestamp",
|
||||||
"id": "timestamp",
|
"id": "timestamp",
|
||||||
"initialWidth": 210,
|
"initialWidth": 212,
|
||||||
"isSortable": true,
|
"isSortable": true,
|
||||||
"schema": "datetime",
|
"schema": "datetime",
|
||||||
"visibleCellActions": undefined,
|
"visibleCellActions": undefined,
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { buildEditFieldButton } from './build_edit_field_button';
|
||||||
|
|
||||||
const openDetails = {
|
const openDetails = {
|
||||||
id: 'openDetails',
|
id: 'openDetails',
|
||||||
width: 24,
|
width: 26,
|
||||||
headerCellRender: () => (
|
headerCellRender: () => (
|
||||||
<EuiScreenReaderOnly>
|
<EuiScreenReaderOnly>
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -15,14 +15,19 @@ import {
|
||||||
EuiCopy,
|
EuiCopy,
|
||||||
EuiDataGridCellValueElementProps,
|
EuiDataGridCellValueElementProps,
|
||||||
EuiPopover,
|
EuiPopover,
|
||||||
|
EuiFlexGroup,
|
||||||
|
EuiFlexItem,
|
||||||
|
useEuiTheme,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { FormattedMessage } from '@kbn/i18n-react';
|
import { FormattedMessage } from '@kbn/i18n-react';
|
||||||
import { euiDarkVars as themeDark, euiLightVars as themeLight } from '@kbn/ui-theme';
|
import { euiDarkVars as themeDark, euiLightVars as themeLight } from '@kbn/ui-theme';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
import type { DataTableRecord } from '@kbn/discover-utils/types';
|
import type { DataTableRecord } from '@kbn/discover-utils/types';
|
||||||
import { UnifiedDataTableContext } from '../table_context';
|
import { UnifiedDataTableContext } from '../table_context';
|
||||||
|
|
||||||
export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueElementProps) => {
|
export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueElementProps) => {
|
||||||
|
const { euiTheme } = useEuiTheme();
|
||||||
const { selectedDocs, expanded, rows, isDarkMode, setSelectedDocs } =
|
const { selectedDocs, expanded, rows, isDarkMode, setSelectedDocs } =
|
||||||
useContext(UnifiedDataTableContext);
|
useContext(UnifiedDataTableContext);
|
||||||
const doc = useMemo(() => rows[rowIndex], [rows, rowIndex]);
|
const doc = useMemo(() => rows[rowIndex], [rows, rowIndex]);
|
||||||
|
@ -46,20 +51,33 @@ export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle
|
||||||
}, [expanded, doc, setCellProps, isDarkMode]);
|
}, [expanded, doc, setCellProps, isDarkMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiCheckbox
|
<EuiFlexGroup
|
||||||
id={doc.id}
|
responsive={false}
|
||||||
aria-label={toggleDocumentSelectionLabel}
|
direction="column"
|
||||||
checked={checked}
|
justifyContent="center"
|
||||||
data-test-subj={`dscGridSelectDoc-${doc.id}`}
|
className="unifiedDataTable__rowControl"
|
||||||
onChange={() => {
|
css={css`
|
||||||
if (checked) {
|
padding-block: ${euiTheme.size.xs}; // to have the same height as "openDetails" control
|
||||||
const newSelection = selectedDocs.filter((docId) => docId !== doc.id);
|
padding-left: ${euiTheme.size.xs}; // space between controls
|
||||||
setSelectedDocs(newSelection);
|
`}
|
||||||
} else {
|
>
|
||||||
setSelectedDocs([...selectedDocs, doc.id]);
|
<EuiFlexItem grow={false}>
|
||||||
}
|
<EuiCheckbox
|
||||||
}}
|
id={doc.id}
|
||||||
/>
|
aria-label={toggleDocumentSelectionLabel}
|
||||||
|
checked={checked}
|
||||||
|
data-test-subj={`dscGridSelectDoc-${doc.id}`}
|
||||||
|
onChange={() => {
|
||||||
|
if (checked) {
|
||||||
|
const newSelection = selectedDocs.filter((docId) => docId !== doc.id);
|
||||||
|
setSelectedDocs(newSelection);
|
||||||
|
} else {
|
||||||
|
setSelectedDocs([...selectedDocs, doc.id]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -62,23 +62,25 @@ export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiToolTip content={buttonLabel} delay="long" ref={toolTipRef}>
|
<div className="unifiedDataTable__rowControl">
|
||||||
<EuiButtonIcon
|
<EuiToolTip content={buttonLabel} delay="long" ref={toolTipRef}>
|
||||||
id={rowIndex === 0 ? tourStep : undefined}
|
<EuiButtonIcon
|
||||||
size="xs"
|
id={rowIndex === 0 ? tourStep : undefined}
|
||||||
iconSize="s"
|
size="xs"
|
||||||
aria-label={buttonLabel}
|
iconSize="s"
|
||||||
data-test-subj={testSubj}
|
aria-label={buttonLabel}
|
||||||
onClick={() => {
|
data-test-subj={testSubj}
|
||||||
const nextHit = isCurrentRowExpanded ? undefined : current;
|
onClick={() => {
|
||||||
toolTipRef.current?.hideToolTip();
|
const nextHit = isCurrentRowExpanded ? undefined : current;
|
||||||
setPressed(Boolean(nextHit));
|
toolTipRef.current?.hideToolTip();
|
||||||
setExpanded?.(nextHit);
|
setPressed(Boolean(nextHit));
|
||||||
}}
|
setExpanded?.(nextHit);
|
||||||
color={isCurrentRowExpanded ? 'primary' : 'text'}
|
}}
|
||||||
iconType={isCurrentRowExpanded ? 'minimize' : 'expand'}
|
color={isCurrentRowExpanded ? 'primary' : 'text'}
|
||||||
isSelected={isCurrentRowExpanded}
|
iconType={isCurrentRowExpanded ? 'minimize' : 'expand'}
|
||||||
/>
|
isSelected={isCurrentRowExpanded}
|
||||||
</EuiToolTip>
|
/>
|
||||||
|
</EuiToolTip>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,15 +13,17 @@ export const MAX_LOADED_GRID_ROWS = 10000;
|
||||||
export const ROWS_PER_PAGE_OPTIONS = [10, 25, 50, DEFAULT_ROWS_PER_PAGE, 250, 500];
|
export const ROWS_PER_PAGE_OPTIONS = [10, 25, 50, DEFAULT_ROWS_PER_PAGE, 250, 500];
|
||||||
|
|
||||||
export const defaultMonacoEditorWidth = 370;
|
export const defaultMonacoEditorWidth = 370;
|
||||||
export const defaultTimeColumnWidth = 210;
|
export const defaultTimeColumnWidth = 212;
|
||||||
export const kibanaJSON = 'kibana-json';
|
export const kibanaJSON = 'kibana-json';
|
||||||
|
|
||||||
export const GRID_STYLE = {
|
export const GRID_STYLE: EuiDataGridStyle = {
|
||||||
border: 'all',
|
border: 'horizontal',
|
||||||
fontSize: 's',
|
fontSize: 's',
|
||||||
cellPadding: 's',
|
cellPadding: 'l',
|
||||||
rowHover: 'none',
|
rowHover: 'highlight',
|
||||||
} as EuiDataGridStyle;
|
header: 'underline',
|
||||||
|
stripes: true,
|
||||||
|
};
|
||||||
|
|
||||||
export const toolbarVisibility = {
|
export const toolbarVisibility = {
|
||||||
showColumnSelector: {
|
showColumnSelector: {
|
||||||
|
|
|
@ -212,7 +212,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
compressed={true}
|
compressed={true}
|
||||||
type="inline"
|
type="inline"
|
||||||
>
|
>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
extension
|
extension
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -223,7 +225,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
bytesDisplayName
|
bytesDisplayName
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -234,7 +238,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
_index
|
_index
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -245,7 +251,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
_score
|
_score
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -363,7 +371,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
compressed={true}
|
compressed={true}
|
||||||
type="inline"
|
type="inline"
|
||||||
>
|
>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
extension
|
extension
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -376,7 +386,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
bytesDisplayName
|
bytesDisplayName
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -389,7 +401,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
_index
|
_index
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -400,7 +414,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
_score
|
_score
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -443,7 +459,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
compressed={true}
|
compressed={true}
|
||||||
type="inline"
|
type="inline"
|
||||||
>
|
>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
extension
|
extension
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -456,7 +474,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
bytesDisplayName
|
bytesDisplayName
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -469,7 +489,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
_index
|
_index
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -480,7 +502,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
_score
|
_score
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -603,7 +627,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
compressed={true}
|
compressed={true}
|
||||||
type="inline"
|
type="inline"
|
||||||
>
|
>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
object.value
|
object.value
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
@ -646,7 +672,9 @@ describe('Unified data table cell rendering', function () {
|
||||||
compressed={true}
|
compressed={true}
|
||||||
type="inline"
|
type="inline"
|
||||||
>
|
>
|
||||||
<EuiDescriptionListTitle>
|
<EuiDescriptionListTitle
|
||||||
|
className="unifiedDataTable__descriptionListTitle"
|
||||||
|
>
|
||||||
object.value
|
object.value
|
||||||
</EuiDescriptionListTitle>
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
|
|
|
@ -137,7 +137,9 @@ export const getRenderCellValueFn =
|
||||||
>
|
>
|
||||||
{pairs.map(([key, value]) => (
|
{pairs.map(([key, value]) => (
|
||||||
<Fragment key={key}>
|
<Fragment key={key}>
|
||||||
<EuiDescriptionListTitle>{key}</EuiDescriptionListTitle>
|
<EuiDescriptionListTitle className="unifiedDataTable__descriptionListTitle">
|
||||||
|
{key}
|
||||||
|
</EuiDescriptionListTitle>
|
||||||
<EuiDescriptionListDescription
|
<EuiDescriptionListDescription
|
||||||
className="unifiedDataTable__descriptionListDescription"
|
className="unifiedDataTable__descriptionListDescription"
|
||||||
dangerouslySetInnerHTML={{ __html: value }}
|
dangerouslySetInnerHTML={{ __html: value }}
|
||||||
|
|
|
@ -191,7 +191,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly with a drag handl
|
||||||
"aria-label": "Preview bytes: number",
|
"aria-label": "Preview bytes: number",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
className="unifiedFieldListItemButton unifiedFieldListItemButton--number unifiedFieldListItemButton--exists custom"
|
className="unifiedFieldListItemButton unifiedFieldListItemButton--number unifiedFieldListItemButton--exists unifiedFieldListItemButton--withDragHandle custom"
|
||||||
dataTestSubj="test-subj"
|
dataTestSubj="test-subj"
|
||||||
dragHandle={
|
dragHandle={
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -105,6 +105,7 @@ export function FieldItemButton<T extends FieldListItem = DataViewField>({
|
||||||
[`unifiedFieldListItemButton--${type}`]: type,
|
[`unifiedFieldListItemButton--${type}`]: type,
|
||||||
[`unifiedFieldListItemButton--exists`]: !isEmpty,
|
[`unifiedFieldListItemButton--exists`]: !isEmpty,
|
||||||
[`unifiedFieldListItemButton--missing`]: isEmpty,
|
[`unifiedFieldListItemButton--missing`]: isEmpty,
|
||||||
|
[`unifiedFieldListItemButton--withDragHandle`]: Boolean(otherProps.dragHandle),
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,6 +23,7 @@ export interface FieldListFiltersProps<T extends FieldListItem> {
|
||||||
getCustomFieldType?: FieldTypeFilterProps<T>['getCustomFieldType'];
|
getCustomFieldType?: FieldTypeFilterProps<T>['getCustomFieldType'];
|
||||||
onSupportedFieldFilter?: FieldTypeFilterProps<T>['onSupportedFieldFilter'];
|
onSupportedFieldFilter?: FieldTypeFilterProps<T>['onSupportedFieldFilter'];
|
||||||
onChangeFieldTypes: FieldTypeFilterProps<T>['onChange'];
|
onChangeFieldTypes: FieldTypeFilterProps<T>['onChange'];
|
||||||
|
compressed?: FieldNameSearchProps['compressed'];
|
||||||
nameFilter: FieldNameSearchProps['nameFilter'];
|
nameFilter: FieldNameSearchProps['nameFilter'];
|
||||||
screenReaderDescriptionId?: FieldNameSearchProps['screenReaderDescriptionId'];
|
screenReaderDescriptionId?: FieldNameSearchProps['screenReaderDescriptionId'];
|
||||||
onChangeNameFilter: FieldNameSearchProps['onChange'];
|
onChangeNameFilter: FieldNameSearchProps['onChange'];
|
||||||
|
@ -38,6 +39,7 @@ export interface FieldListFiltersProps<T extends FieldListItem> {
|
||||||
* @param getCustomFieldType
|
* @param getCustomFieldType
|
||||||
* @param onSupportedFieldFilter
|
* @param onSupportedFieldFilter
|
||||||
* @param onChangeFieldTypes
|
* @param onChangeFieldTypes
|
||||||
|
* @param compressed
|
||||||
* @param nameFilter
|
* @param nameFilter
|
||||||
* @param screenReaderDescriptionId
|
* @param screenReaderDescriptionId
|
||||||
* @param onChangeNameFilter
|
* @param onChangeNameFilter
|
||||||
|
@ -52,6 +54,7 @@ function InnerFieldListFilters<T extends FieldListItem = DataViewField>({
|
||||||
getCustomFieldType,
|
getCustomFieldType,
|
||||||
onSupportedFieldFilter,
|
onSupportedFieldFilter,
|
||||||
onChangeFieldTypes,
|
onChangeFieldTypes,
|
||||||
|
compressed,
|
||||||
nameFilter,
|
nameFilter,
|
||||||
screenReaderDescriptionId,
|
screenReaderDescriptionId,
|
||||||
onChangeNameFilter,
|
onChangeNameFilter,
|
||||||
|
@ -72,6 +75,7 @@ function InnerFieldListFilters<T extends FieldListItem = DataViewField>({
|
||||||
/>
|
/>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
|
compressed={compressed}
|
||||||
nameFilter={nameFilter}
|
nameFilter={nameFilter}
|
||||||
screenReaderDescriptionId={screenReaderDescriptionId}
|
screenReaderDescriptionId={screenReaderDescriptionId}
|
||||||
onChange={onChangeNameFilter}
|
onChange={onChangeNameFilter}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { EuiFieldSearch, type EuiFieldSearchProps } from '@elastic/eui';
|
||||||
export interface FieldNameSearchProps {
|
export interface FieldNameSearchProps {
|
||||||
'data-test-subj': string;
|
'data-test-subj': string;
|
||||||
append?: EuiFieldSearchProps['append'];
|
append?: EuiFieldSearchProps['append'];
|
||||||
|
compressed?: EuiFieldSearchProps['compressed'];
|
||||||
nameFilter: string;
|
nameFilter: string;
|
||||||
screenReaderDescriptionId?: string;
|
screenReaderDescriptionId?: string;
|
||||||
onChange: (nameFilter: string) => unknown;
|
onChange: (nameFilter: string) => unknown;
|
||||||
|
@ -25,6 +26,7 @@ export interface FieldNameSearchProps {
|
||||||
* Search input for fields list
|
* Search input for fields list
|
||||||
* @param dataTestSubject
|
* @param dataTestSubject
|
||||||
* @param append
|
* @param append
|
||||||
|
* @param compressed
|
||||||
* @param nameFilter
|
* @param nameFilter
|
||||||
* @param screenReaderDescriptionId
|
* @param screenReaderDescriptionId
|
||||||
* @param onChange
|
* @param onChange
|
||||||
|
@ -33,6 +35,7 @@ export interface FieldNameSearchProps {
|
||||||
export const FieldNameSearch: React.FC<FieldNameSearchProps> = ({
|
export const FieldNameSearch: React.FC<FieldNameSearchProps> = ({
|
||||||
'data-test-subj': dataTestSubject,
|
'data-test-subj': dataTestSubject,
|
||||||
append,
|
append,
|
||||||
|
compressed,
|
||||||
nameFilter,
|
nameFilter,
|
||||||
screenReaderDescriptionId,
|
screenReaderDescriptionId,
|
||||||
onChange,
|
onChange,
|
||||||
|
@ -52,6 +55,7 @@ export const FieldNameSearch: React.FC<FieldNameSearchProps> = ({
|
||||||
placeholder={searchPlaceholder}
|
placeholder={searchPlaceholder}
|
||||||
value={nameFilter}
|
value={nameFilter}
|
||||||
append={append}
|
append={append}
|
||||||
|
compressed={compressed}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -82,6 +82,7 @@ async function getComponent({
|
||||||
isEmpty: false,
|
isEmpty: false,
|
||||||
groupIndex: 1,
|
groupIndex: 1,
|
||||||
itemIndex: 0,
|
itemIndex: 0,
|
||||||
|
size: 'xs',
|
||||||
workspaceSelectedFieldNames: [],
|
workspaceSelectedFieldNames: [],
|
||||||
};
|
};
|
||||||
const comp = await mountWithIntl(<UnifiedFieldListItem {...props} />);
|
const comp = await mountWithIntl(<UnifiedFieldListItem {...props} />);
|
||||||
|
|
|
@ -33,6 +33,7 @@ import type {
|
||||||
interface GetCommonFieldItemButtonPropsParams {
|
interface GetCommonFieldItemButtonPropsParams {
|
||||||
stateService: UnifiedFieldListSidebarContainerStateService;
|
stateService: UnifiedFieldListSidebarContainerStateService;
|
||||||
field: DataViewField;
|
field: DataViewField;
|
||||||
|
size: FieldItemButtonProps<DataViewField>['size'];
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
toggleDisplay: (field: DataViewField, isSelected?: boolean) => void;
|
toggleDisplay: (field: DataViewField, isSelected?: boolean) => void;
|
||||||
}
|
}
|
||||||
|
@ -40,10 +41,12 @@ interface GetCommonFieldItemButtonPropsParams {
|
||||||
function getCommonFieldItemButtonProps({
|
function getCommonFieldItemButtonProps({
|
||||||
stateService,
|
stateService,
|
||||||
field,
|
field,
|
||||||
|
size,
|
||||||
isSelected,
|
isSelected,
|
||||||
toggleDisplay,
|
toggleDisplay,
|
||||||
}: GetCommonFieldItemButtonPropsParams): {
|
}: GetCommonFieldItemButtonPropsParams): {
|
||||||
field: FieldItemButtonProps<DataViewField>['field'];
|
field: FieldItemButtonProps<DataViewField>['field'];
|
||||||
|
size: FieldItemButtonProps<DataViewField>['size'];
|
||||||
isSelected: FieldItemButtonProps<DataViewField>['isSelected'];
|
isSelected: FieldItemButtonProps<DataViewField>['isSelected'];
|
||||||
buttonAddFieldToWorkspaceProps?: FieldItemButtonProps<DataViewField>['buttonAddFieldToWorkspaceProps'];
|
buttonAddFieldToWorkspaceProps?: FieldItemButtonProps<DataViewField>['buttonAddFieldToWorkspaceProps'];
|
||||||
buttonRemoveFieldFromWorkspaceProps?: FieldItemButtonProps<DataViewField>['buttonRemoveFieldFromWorkspaceProps'];
|
buttonRemoveFieldFromWorkspaceProps?: FieldItemButtonProps<DataViewField>['buttonRemoveFieldFromWorkspaceProps'];
|
||||||
|
@ -54,6 +57,7 @@ function getCommonFieldItemButtonProps({
|
||||||
field.name === '_source' ? undefined : (f: DataViewField) => toggleDisplay(f, isSelected);
|
field.name === '_source' ? undefined : (f: DataViewField) => toggleDisplay(f, isSelected);
|
||||||
return {
|
return {
|
||||||
field,
|
field,
|
||||||
|
size,
|
||||||
isSelected,
|
isSelected,
|
||||||
buttonAddFieldToWorkspaceProps: stateService.creationOptions.buttonAddFieldToWorkspaceProps,
|
buttonAddFieldToWorkspaceProps: stateService.creationOptions.buttonAddFieldToWorkspaceProps,
|
||||||
buttonRemoveFieldFromWorkspaceProps:
|
buttonRemoveFieldFromWorkspaceProps:
|
||||||
|
@ -68,10 +72,11 @@ interface MultiFieldsProps {
|
||||||
multiFields: NonNullable<UnifiedFieldListItemProps['multiFields']>;
|
multiFields: NonNullable<UnifiedFieldListItemProps['multiFields']>;
|
||||||
toggleDisplay: (field: DataViewField) => void;
|
toggleDisplay: (field: DataViewField) => void;
|
||||||
alwaysShowActionButton: boolean;
|
alwaysShowActionButton: boolean;
|
||||||
|
size: FieldItemButtonProps<DataViewField>['size'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const MultiFields: React.FC<MultiFieldsProps> = memo(
|
const MultiFields: React.FC<MultiFieldsProps> = memo(
|
||||||
({ stateService, multiFields, toggleDisplay, alwaysShowActionButton }) => (
|
({ stateService, multiFields, toggleDisplay, alwaysShowActionButton, size }) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<EuiTitle size="xxxs">
|
<EuiTitle size="xxxs">
|
||||||
<h5>
|
<h5>
|
||||||
|
@ -84,7 +89,6 @@ const MultiFields: React.FC<MultiFieldsProps> = memo(
|
||||||
{multiFields.map((entry) => (
|
{multiFields.map((entry) => (
|
||||||
<FieldItemButton
|
<FieldItemButton
|
||||||
key={entry.field.name}
|
key={entry.field.name}
|
||||||
size="xs"
|
|
||||||
flush="both"
|
flush="both"
|
||||||
isEmpty={false}
|
isEmpty={false}
|
||||||
isActive={false}
|
isActive={false}
|
||||||
|
@ -95,6 +99,7 @@ const MultiFields: React.FC<MultiFieldsProps> = memo(
|
||||||
field: entry.field,
|
field: entry.field,
|
||||||
isSelected: entry.isSelected,
|
isSelected: entry.isSelected,
|
||||||
toggleDisplay,
|
toggleDisplay,
|
||||||
|
size,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
@ -187,6 +192,10 @@ export interface UnifiedFieldListItemProps {
|
||||||
* Item index in the field list
|
* Item index in the field list
|
||||||
*/
|
*/
|
||||||
itemIndex: number;
|
itemIndex: number;
|
||||||
|
/**
|
||||||
|
* Item size
|
||||||
|
*/
|
||||||
|
size: FieldItemButtonProps<DataViewField>['size'];
|
||||||
}
|
}
|
||||||
|
|
||||||
function UnifiedFieldListItemComponent({
|
function UnifiedFieldListItemComponent({
|
||||||
|
@ -209,6 +218,7 @@ function UnifiedFieldListItemComponent({
|
||||||
workspaceSelectedFieldNames,
|
workspaceSelectedFieldNames,
|
||||||
groupIndex,
|
groupIndex,
|
||||||
itemIndex,
|
itemIndex,
|
||||||
|
size,
|
||||||
}: UnifiedFieldListItemProps) {
|
}: UnifiedFieldListItemProps) {
|
||||||
const [infoIsOpen, setOpen] = useState(false);
|
const [infoIsOpen, setOpen] = useState(false);
|
||||||
|
|
||||||
|
@ -284,6 +294,7 @@ function UnifiedFieldListItemComponent({
|
||||||
multiFields={multiFields}
|
multiFields={multiFields}
|
||||||
alwaysShowActionButton={alwaysShowActionButton}
|
alwaysShowActionButton={alwaysShowActionButton}
|
||||||
toggleDisplay={toggleDisplay}
|
toggleDisplay={toggleDisplay}
|
||||||
|
size={size}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -315,6 +326,8 @@ function UnifiedFieldListItemComponent({
|
||||||
[field, itemIndex]
|
[field, itemIndex]
|
||||||
);
|
);
|
||||||
const order = useMemo(() => [0, groupIndex, itemIndex], [groupIndex, itemIndex]);
|
const order = useMemo(() => [0, groupIndex, itemIndex], [groupIndex, itemIndex]);
|
||||||
|
const isDragDisabled =
|
||||||
|
alwaysShowActionButton || stateService.creationOptions.disableFieldListItemDragAndDrop;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldPopover
|
<FieldPopover
|
||||||
|
@ -322,26 +335,30 @@ function UnifiedFieldListItemComponent({
|
||||||
button={
|
button={
|
||||||
<DragDrop
|
<DragDrop
|
||||||
draggable
|
draggable
|
||||||
|
dragClassName="unifiedFieldListItemButton__dragging"
|
||||||
order={order}
|
order={order}
|
||||||
value={value}
|
value={value}
|
||||||
onDragStart={closePopover}
|
onDragStart={closePopover}
|
||||||
isDisabled={
|
isDisabled={isDragDisabled}
|
||||||
alwaysShowActionButton || stateService.creationOptions.disableFieldListItemDragAndDrop
|
|
||||||
}
|
|
||||||
dataTestSubj={`${
|
dataTestSubj={`${
|
||||||
stateService.creationOptions.dataTestSubj?.fieldListItemDndDataTestSubjPrefix ??
|
stateService.creationOptions.dataTestSubj?.fieldListItemDndDataTestSubjPrefix ??
|
||||||
'unifiedFieldListItemDnD'
|
'unifiedFieldListItemDnD'
|
||||||
}-${field.name}`}
|
}-${field.name}`}
|
||||||
>
|
>
|
||||||
<FieldItemButton
|
<FieldItemButton
|
||||||
size="xs"
|
|
||||||
fieldSearchHighlight={highlight}
|
fieldSearchHighlight={highlight}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
isActive={infoIsOpen}
|
isActive={infoIsOpen}
|
||||||
flush={alwaysShowActionButton ? 'both' : undefined}
|
flush={alwaysShowActionButton ? 'both' : undefined}
|
||||||
shouldAlwaysShowAction={alwaysShowActionButton}
|
shouldAlwaysShowAction={alwaysShowActionButton}
|
||||||
onClick={field.type !== '_source' ? togglePopover : undefined}
|
onClick={field.type !== '_source' ? togglePopover : undefined}
|
||||||
{...getCommonFieldItemButtonProps({ stateService, field, isSelected, toggleDisplay })}
|
{...getCommonFieldItemButtonProps({
|
||||||
|
stateService,
|
||||||
|
field,
|
||||||
|
isSelected,
|
||||||
|
toggleDisplay,
|
||||||
|
size,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</DragDrop>
|
</DragDrop>
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
width: $euiSize * 19;
|
width: $euiSize * 19;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
&--collapsed {
|
||||||
|
width: auto;
|
||||||
|
padding: $euiSizeS $euiSizeS 0;
|
||||||
|
}
|
||||||
|
|
||||||
@include euiBreakpoint('xs', 's') {
|
@include euiBreakpoint('xs', 's') {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: $euiSize;
|
padding: $euiSize;
|
||||||
|
@ -14,7 +19,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.unifiedFieldListSidebar__list {
|
.unifiedFieldListSidebar__list {
|
||||||
padding: $euiSizeS 0 $euiSizeS $euiSizeS;
|
padding: $euiSizeS $euiSizeS 0;
|
||||||
|
|
||||||
@include euiBreakpoint('xs', 's') {
|
@include euiBreakpoint('xs', 's') {
|
||||||
padding: $euiSizeS 0 0 0;
|
padding: $euiSizeS 0 0 0;
|
||||||
|
@ -38,3 +43,18 @@
|
||||||
.unifiedFieldListSidebar__flyoutHeader {
|
.unifiedFieldListSidebar__flyoutHeader {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unifiedFieldListSidebar .unifiedFieldListItemButton {
|
||||||
|
&.kbnFieldButton {
|
||||||
|
margin-bottom: $euiSizeXS / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.domDragDrop-isDraggable {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.unifiedFieldListItemButton__dragging) {
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,16 +9,29 @@
|
||||||
import './field_list_sidebar.scss';
|
import './field_list_sidebar.scss';
|
||||||
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPageSidebar } from '@elastic/eui';
|
import { css } from '@emotion/react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import {
|
||||||
|
EuiButton,
|
||||||
|
EuiButtonProps,
|
||||||
|
EuiFlexGroup,
|
||||||
|
EuiFlexItem,
|
||||||
|
EuiHideFor,
|
||||||
|
EuiPageSidebar,
|
||||||
|
EuiPageSidebarProps,
|
||||||
|
useEuiTheme,
|
||||||
|
} from '@elastic/eui';
|
||||||
|
import { ToolbarButton } from '@kbn/shared-ux-button-toolbar';
|
||||||
import { type DataViewField } from '@kbn/data-views-plugin/public';
|
import { type DataViewField } from '@kbn/data-views-plugin/public';
|
||||||
import { getDataViewFieldSubtypeMulti } from '@kbn/es-query/src/utils';
|
import { getDataViewFieldSubtypeMulti } from '@kbn/es-query/src/utils';
|
||||||
import { FIELDS_LIMIT_SETTING, SEARCH_FIELDS_FROM_SOURCE } from '@kbn/discover-utils';
|
import { FIELDS_LIMIT_SETTING, SEARCH_FIELDS_FROM_SOURCE } from '@kbn/discover-utils';
|
||||||
import { FieldList } from '../../components/field_list';
|
import { FieldList } from '../../components/field_list';
|
||||||
import { FieldListFilters } from '../../components/field_list_filters';
|
import { FieldListFilters } from '../../components/field_list_filters';
|
||||||
import { FieldListGrouped, type FieldListGroupedProps } from '../../components/field_list_grouped';
|
import { FieldListGrouped, type FieldListGroupedProps } from '../../components/field_list_grouped';
|
||||||
import { FieldsGroupNames } from '../../types';
|
import { FieldsGroupNames, type ButtonAddFieldVariant } from '../../types';
|
||||||
import { GroupedFieldsParams, useGroupedFields } from '../../hooks/use_grouped_fields';
|
import { GroupedFieldsParams, useGroupedFields } from '../../hooks/use_grouped_fields';
|
||||||
import { UnifiedFieldListItem, type UnifiedFieldListItemProps } from '../unified_field_list_item';
|
import { UnifiedFieldListItem, type UnifiedFieldListItemProps } from '../unified_field_list_item';
|
||||||
|
import { SidebarToggleButton, type SidebarToggleButtonProps } from './sidebar_toggle_button';
|
||||||
import {
|
import {
|
||||||
getSelectedFields,
|
getSelectedFields,
|
||||||
shouldShowField,
|
shouldShowField,
|
||||||
|
@ -46,6 +59,11 @@ export type UnifiedFieldListSidebarCustomizableProps = Pick<
|
||||||
*/
|
*/
|
||||||
showFieldList?: boolean;
|
showFieldList?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compressed view
|
||||||
|
*/
|
||||||
|
compressed?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom logic for determining which field is selected
|
* Custom logic for determining which field is selected
|
||||||
*/
|
*/
|
||||||
|
@ -83,6 +101,22 @@ interface UnifiedFieldListSidebarInternalProps {
|
||||||
*/
|
*/
|
||||||
alwaysShowActionButton?: UnifiedFieldListItemProps['alwaysShowActionButton'];
|
alwaysShowActionButton?: UnifiedFieldListItemProps['alwaysShowActionButton'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What button style type to use
|
||||||
|
*/
|
||||||
|
buttonAddFieldVariant: ButtonAddFieldVariant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In case if sidebar is collapsible by default
|
||||||
|
* Pass `undefined` to hide the collapse/expand buttons from the sidebar
|
||||||
|
*/
|
||||||
|
isSidebarCollapsed?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handler to toggle the sidebar
|
||||||
|
*/
|
||||||
|
onToggleSidebar?: SidebarToggleButtonProps['onChange'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger a field editing
|
* Trigger a field editing
|
||||||
*/
|
*/
|
||||||
|
@ -104,10 +138,13 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
|
||||||
workspaceSelectedFieldNames,
|
workspaceSelectedFieldNames,
|
||||||
isProcessing,
|
isProcessing,
|
||||||
alwaysShowActionButton,
|
alwaysShowActionButton,
|
||||||
|
buttonAddFieldVariant,
|
||||||
|
isSidebarCollapsed,
|
||||||
allFields,
|
allFields,
|
||||||
dataView,
|
dataView,
|
||||||
trackUiMetric,
|
trackUiMetric,
|
||||||
showFieldList = true,
|
showFieldList = true,
|
||||||
|
compressed = true,
|
||||||
isAffectedByGlobalFilter,
|
isAffectedByGlobalFilter,
|
||||||
prepend,
|
prepend,
|
||||||
onAddFieldToWorkspace,
|
onAddFieldToWorkspace,
|
||||||
|
@ -116,6 +153,7 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
|
||||||
onSelectedFieldFilter,
|
onSelectedFieldFilter,
|
||||||
onEditField,
|
onEditField,
|
||||||
onDeleteField,
|
onDeleteField,
|
||||||
|
onToggleSidebar,
|
||||||
}) => {
|
}) => {
|
||||||
const { dataViews, core } = services;
|
const { dataViews, core } = services;
|
||||||
const useNewFieldsApi = useMemo(
|
const useNewFieldsApi = useMemo(
|
||||||
|
@ -210,6 +248,7 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
|
||||||
services={services}
|
services={services}
|
||||||
alwaysShowActionButton={alwaysShowActionButton}
|
alwaysShowActionButton={alwaysShowActionButton}
|
||||||
field={field}
|
field={field}
|
||||||
|
size={compressed ? 'xs' : 's'}
|
||||||
highlight={fieldSearchHighlight}
|
highlight={fieldSearchHighlight}
|
||||||
dataView={dataView!}
|
dataView={dataView!}
|
||||||
onAddFieldToWorkspace={onAddFieldToWorkspace}
|
onAddFieldToWorkspace={onAddFieldToWorkspace}
|
||||||
|
@ -235,6 +274,7 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
|
||||||
searchMode,
|
searchMode,
|
||||||
services,
|
services,
|
||||||
alwaysShowActionButton,
|
alwaysShowActionButton,
|
||||||
|
compressed,
|
||||||
dataView,
|
dataView,
|
||||||
onAddFieldToWorkspace,
|
onAddFieldToWorkspace,
|
||||||
onRemoveFieldFromWorkspace,
|
onRemoveFieldFromWorkspace,
|
||||||
|
@ -248,40 +288,94 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { euiTheme } = useEuiTheme();
|
||||||
|
|
||||||
if (!dataView) {
|
if (!dataView) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sidebarToggleButton =
|
||||||
|
typeof isSidebarCollapsed === 'boolean' && onToggleSidebar ? (
|
||||||
|
<SidebarToggleButton
|
||||||
|
buttonSize={compressed ? 's' : 'm'}
|
||||||
|
isSidebarCollapsed={isSidebarCollapsed}
|
||||||
|
onChange={onToggleSidebar}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const pageSidebarProps: Partial<EuiPageSidebarProps> = {
|
||||||
|
className: classnames('unifiedFieldListSidebar', {
|
||||||
|
'unifiedFieldListSidebar--collapsed': isSidebarCollapsed,
|
||||||
|
}),
|
||||||
|
'aria-label': i18n.translate(
|
||||||
|
'unifiedFieldList.fieldListSidebar.indexAndFieldsSectionAriaLabel',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Index and fields',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
id:
|
||||||
|
stateService.creationOptions.dataTestSubj?.fieldListSidebarDataTestSubj ??
|
||||||
|
'unifiedFieldListSidebarId',
|
||||||
|
'data-test-subj':
|
||||||
|
stateService.creationOptions.dataTestSubj?.fieldListSidebarDataTestSubj ??
|
||||||
|
'unifiedFieldListSidebarId',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isSidebarCollapsed && sidebarToggleButton) {
|
||||||
|
return (
|
||||||
|
<EuiHideFor sizes={['xs', 's']}>
|
||||||
|
<div {...pageSidebarProps}>{sidebarToggleButton}</div>
|
||||||
|
</EuiHideFor>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasButtonAddFieldToolbarStyle = buttonAddFieldVariant === 'toolbar';
|
||||||
|
const buttonAddFieldCommonProps: Partial<EuiButtonProps> = {
|
||||||
|
size: 's',
|
||||||
|
iconType: 'indexOpen',
|
||||||
|
'data-test-subj':
|
||||||
|
stateService.creationOptions.dataTestSubj?.fieldListAddFieldButtonTestSubj ??
|
||||||
|
'unifiedFieldListAddField',
|
||||||
|
};
|
||||||
|
const buttonAddFieldLabel = i18n.translate(
|
||||||
|
'unifiedFieldList.fieldListSidebar.addFieldButtonLabel',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Add a field',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiPageSidebar
|
<EuiPageSidebar {...pageSidebarProps}>
|
||||||
className="unifiedFieldListSidebar"
|
|
||||||
aria-label={i18n.translate(
|
|
||||||
'unifiedFieldList.fieldListSidebar.indexAndFieldsSectionAriaLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Index and fields',
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
id={
|
|
||||||
stateService.creationOptions.dataTestSubj?.fieldListSidebarDataTestSubj ??
|
|
||||||
'unifiedFieldListSidebarId'
|
|
||||||
}
|
|
||||||
data-test-subj={
|
|
||||||
stateService.creationOptions.dataTestSubj?.fieldListSidebarDataTestSubj ??
|
|
||||||
'unifiedFieldListSidebarId'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<EuiFlexGroup
|
<EuiFlexGroup
|
||||||
className="unifiedFieldListSidebar__group"
|
className="unifiedFieldListSidebar__group"
|
||||||
direction="column"
|
direction="column"
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
gutterSize="s"
|
gutterSize="none"
|
||||||
responsive={false}
|
responsive={false}
|
||||||
>
|
>
|
||||||
{Boolean(prepend) && <EuiFlexItem grow={false}>{prepend}</EuiFlexItem>}
|
{Boolean(prepend) && (
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
css={css`
|
||||||
|
margin-bottom: ${euiTheme.size.s};
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{prepend}
|
||||||
|
</EuiFlexItem>
|
||||||
|
)}
|
||||||
<EuiFlexItem>
|
<EuiFlexItem>
|
||||||
<FieldList
|
<FieldList
|
||||||
isProcessing={isProcessing}
|
isProcessing={isProcessing}
|
||||||
prepend={<FieldListFilters {...fieldListFiltersProps} />}
|
prepend={
|
||||||
|
<EuiFlexGroup direction="row" gutterSize="s" responsive={false}>
|
||||||
|
{sidebarToggleButton && (
|
||||||
|
<EuiFlexItem grow={false}>{sidebarToggleButton}</EuiFlexItem>
|
||||||
|
)}
|
||||||
|
<EuiFlexItem>
|
||||||
|
<FieldListFilters {...fieldListFiltersProps} compressed={compressed} />
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
}
|
||||||
className="unifiedFieldListSidebar__list"
|
className="unifiedFieldListSidebar__list"
|
||||||
>
|
>
|
||||||
{showFieldList ? (
|
{showFieldList ? (
|
||||||
|
@ -293,25 +387,33 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
|
||||||
) : (
|
) : (
|
||||||
<EuiFlexItem grow />
|
<EuiFlexItem grow />
|
||||||
)}
|
)}
|
||||||
{!!onEditField && (
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiButton
|
|
||||||
iconType="indexOpen"
|
|
||||||
data-test-subj={
|
|
||||||
stateService.creationOptions.dataTestSubj?.fieldListAddFieldButtonTestSubj ??
|
|
||||||
'unifiedFieldListAddField'
|
|
||||||
}
|
|
||||||
onClick={() => onEditField()}
|
|
||||||
size="s"
|
|
||||||
>
|
|
||||||
{i18n.translate('unifiedFieldList.fieldListSidebar.addFieldButtonLabel', {
|
|
||||||
defaultMessage: 'Add a field',
|
|
||||||
})}
|
|
||||||
</EuiButton>
|
|
||||||
</EuiFlexItem>
|
|
||||||
)}
|
|
||||||
</FieldList>
|
</FieldList>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
{!!onEditField && (
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
css={
|
||||||
|
hasButtonAddFieldToolbarStyle
|
||||||
|
? css`
|
||||||
|
padding: ${euiTheme.size.s};
|
||||||
|
border-top: ${euiTheme.border.thin};
|
||||||
|
`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{hasButtonAddFieldToolbarStyle ? (
|
||||||
|
<ToolbarButton
|
||||||
|
{...buttonAddFieldCommonProps}
|
||||||
|
label={buttonAddFieldLabel}
|
||||||
|
onClick={() => onEditField()}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EuiButton {...buttonAddFieldCommonProps} onClick={() => onEditField()}>
|
||||||
|
{buttonAddFieldLabel}
|
||||||
|
</EuiButton>
|
||||||
|
)}
|
||||||
|
</EuiFlexItem>
|
||||||
|
)}
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</EuiPageSidebar>
|
</EuiPageSidebar>
|
||||||
);
|
);
|
||||||
|
|
|
@ -35,6 +35,7 @@ import {
|
||||||
type ExistingFieldsFetcher,
|
type ExistingFieldsFetcher,
|
||||||
} from '../../hooks/use_existing_fields';
|
} from '../../hooks/use_existing_fields';
|
||||||
import { useQuerySubscriber } from '../../hooks/use_query_subscriber';
|
import { useQuerySubscriber } from '../../hooks/use_query_subscriber';
|
||||||
|
import { useSidebarToggle } from '../../hooks/use_sidebar_toggle';
|
||||||
import {
|
import {
|
||||||
UnifiedFieldListSidebar,
|
UnifiedFieldListSidebar,
|
||||||
type UnifiedFieldListSidebarCustomizableProps,
|
type UnifiedFieldListSidebarCustomizableProps,
|
||||||
|
@ -72,11 +73,6 @@ export type UnifiedFieldListSidebarContainerProps = Omit<
|
||||||
*/
|
*/
|
||||||
getCreationOptions: () => UnifiedFieldListSidebarContainerCreationOptions;
|
getCreationOptions: () => UnifiedFieldListSidebarContainerCreationOptions;
|
||||||
|
|
||||||
/**
|
|
||||||
* In case if you have a sidebar toggle button
|
|
||||||
*/
|
|
||||||
isSidebarCollapsed?: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom content to render at the top of field list in the flyout (for example a data view picker)
|
* Custom content to render at the top of field list in the flyout (for example a data view picker)
|
||||||
*/
|
*/
|
||||||
|
@ -115,7 +111,6 @@ const UnifiedFieldListSidebarContainer = forwardRef<
|
||||||
services,
|
services,
|
||||||
dataView,
|
dataView,
|
||||||
workspaceSelectedFieldNames,
|
workspaceSelectedFieldNames,
|
||||||
isSidebarCollapsed, // TODO later: pull the logic of collapsing the sidebar to this component
|
|
||||||
prependInFlyout,
|
prependInFlyout,
|
||||||
variant = 'responsive',
|
variant = 'responsive',
|
||||||
onFieldEdited,
|
onFieldEdited,
|
||||||
|
@ -125,6 +120,7 @@ const UnifiedFieldListSidebarContainer = forwardRef<
|
||||||
);
|
);
|
||||||
const { data, dataViewFieldEditor } = services;
|
const { data, dataViewFieldEditor } = services;
|
||||||
const [isFieldListFlyoutVisible, setIsFieldListFlyoutVisible] = useState<boolean>(false);
|
const [isFieldListFlyoutVisible, setIsFieldListFlyoutVisible] = useState<boolean>(false);
|
||||||
|
const { isSidebarCollapsed, onToggleSidebar } = useSidebarToggle({ stateService });
|
||||||
|
|
||||||
const canEditDataView =
|
const canEditDataView =
|
||||||
Boolean(dataViewFieldEditor?.userPermissions.editIndexPattern()) ||
|
Boolean(dataViewFieldEditor?.userPermissions.editIndexPattern()) ||
|
||||||
|
@ -250,8 +246,15 @@ const UnifiedFieldListSidebarContainer = forwardRef<
|
||||||
isAffectedByGlobalFilter,
|
isAffectedByGlobalFilter,
|
||||||
onEditField: editField,
|
onEditField: editField,
|
||||||
onDeleteField: deleteField,
|
onDeleteField: deleteField,
|
||||||
|
compressed: stateService.creationOptions.compressed ?? false,
|
||||||
|
buttonAddFieldVariant: stateService.creationOptions.buttonAddFieldVariant ?? 'primary',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (stateService.creationOptions.showSidebarToggleButton) {
|
||||||
|
commonSidebarProps.isSidebarCollapsed = isSidebarCollapsed;
|
||||||
|
commonSidebarProps.onToggleSidebar = onToggleSidebar;
|
||||||
|
}
|
||||||
|
|
||||||
const buttonPropsToTriggerFlyout = stateService.creationOptions.buttonPropsToTriggerFlyout;
|
const buttonPropsToTriggerFlyout = stateService.creationOptions.buttonPropsToTriggerFlyout;
|
||||||
|
|
||||||
const renderListVariant = () => {
|
const renderListVariant = () => {
|
||||||
|
@ -319,6 +322,8 @@ const UnifiedFieldListSidebarContainer = forwardRef<
|
||||||
<UnifiedFieldListSidebar
|
<UnifiedFieldListSidebar
|
||||||
{...commonSidebarProps}
|
{...commonSidebarProps}
|
||||||
alwaysShowActionButton={true}
|
alwaysShowActionButton={true}
|
||||||
|
buttonAddFieldVariant="primary" // always for the flyout
|
||||||
|
isSidebarCollapsed={undefined}
|
||||||
prepend={prependInFlyout?.()}
|
prepend={prependInFlyout?.()}
|
||||||
/>
|
/>
|
||||||
</EuiFlyout>
|
</EuiFlyout>
|
||||||
|
@ -333,12 +338,12 @@ const UnifiedFieldListSidebarContainer = forwardRef<
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variant === 'list-always') {
|
if (variant === 'list-always') {
|
||||||
return (!isSidebarCollapsed && renderListVariant()) || null;
|
return renderListVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isSidebarCollapsed && <EuiHideFor sizes={['xs', 's']}>{renderListVariant()}</EuiHideFor>}
|
<EuiHideFor sizes={['xs', 's']}>{renderListVariant()}</EuiHideFor>
|
||||||
<EuiShowFor sizes={['xs', 's']}>{renderButtonVariant()}</EuiShowFor>
|
<EuiShowFor sizes={['xs', 's']}>{renderButtonVariant()}</EuiShowFor>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { SidebarToggleButton, type SidebarToggleButtonProps } from './sidebar_toggle_button';
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { IconButtonGroup, type IconButtonGroupProps } from '@kbn/shared-ux-button-toolbar';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle button props
|
||||||
|
*/
|
||||||
|
export interface SidebarToggleButtonProps {
|
||||||
|
'data-test-subj'?: string;
|
||||||
|
isSidebarCollapsed: boolean;
|
||||||
|
buttonSize: IconButtonGroupProps['buttonSize'];
|
||||||
|
onChange: (isSidebarCollapsed: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A toggle button for the fields sidebar
|
||||||
|
* @param data-test-subj
|
||||||
|
* @param isSidebarCollapsed
|
||||||
|
* @param onChange
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
export const SidebarToggleButton: React.FC<SidebarToggleButtonProps> = ({
|
||||||
|
'data-test-subj': dataTestSubj = 'unifiedFieldListSidebar__toggle',
|
||||||
|
isSidebarCollapsed,
|
||||||
|
buttonSize,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
// TODO: replace with new Eui icons once available
|
||||||
|
return (
|
||||||
|
<div data-test-subj={dataTestSubj}>
|
||||||
|
<IconButtonGroup
|
||||||
|
legend={i18n.translate('unifiedFieldList.fieldListSidebar.toggleSidebarLegend', {
|
||||||
|
defaultMessage: 'Toggle sidebar',
|
||||||
|
})}
|
||||||
|
buttonSize={buttonSize}
|
||||||
|
buttons={[
|
||||||
|
...(isSidebarCollapsed
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
label: i18n.translate('unifiedFieldList.fieldListSidebar.expandSidebarButton', {
|
||||||
|
defaultMessage: 'Show sidebar',
|
||||||
|
}),
|
||||||
|
iconType: 'menuRight',
|
||||||
|
'data-test-subj': `${dataTestSubj}-expand`,
|
||||||
|
onClick: () => onChange(false),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
label: i18n.translate('unifiedFieldList.fieldListSidebar.collapseSidebarButton', {
|
||||||
|
defaultMessage: 'Hide sidebar',
|
||||||
|
}),
|
||||||
|
iconType: 'menuLeft',
|
||||||
|
'data-test-subj': `${dataTestSubj}-collapse`,
|
||||||
|
onClick: () => onChange(true),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { renderHook, act } from '@testing-library/react-hooks';
|
||||||
|
import { useSidebarToggle } from './use_sidebar_toggle';
|
||||||
|
import * as localStorageModule from 'react-use/lib/useLocalStorage';
|
||||||
|
|
||||||
|
jest.spyOn(localStorageModule, 'default');
|
||||||
|
|
||||||
|
describe('UnifiedFieldList useSidebarToggle', () => {
|
||||||
|
const stateService = {
|
||||||
|
creationOptions: {
|
||||||
|
originatingApp: 'test',
|
||||||
|
localStorageKeyPrefix: 'this',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(localStorageModule.default as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle correctly', async () => {
|
||||||
|
const storeMock = jest.fn();
|
||||||
|
(localStorageModule.default as jest.Mock).mockImplementation(() => {
|
||||||
|
return [false, storeMock];
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(useSidebarToggle, {
|
||||||
|
initialProps: {
|
||||||
|
stateService,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isSidebarCollapsed).toBe(false);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onToggleSidebar(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isSidebarCollapsed).toBe(true);
|
||||||
|
expect(storeMock).toHaveBeenCalledWith(true);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onToggleSidebar(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isSidebarCollapsed).toBe(false);
|
||||||
|
expect(storeMock).toHaveBeenLastCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should restore collapsed state and expand from it', async () => {
|
||||||
|
const storeMock = jest.fn();
|
||||||
|
(localStorageModule.default as jest.Mock).mockImplementation(() => {
|
||||||
|
return [true, storeMock];
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(useSidebarToggle, {
|
||||||
|
initialProps: {
|
||||||
|
stateService,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isSidebarCollapsed).toBe(true);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onToggleSidebar(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isSidebarCollapsed).toBe(false);
|
||||||
|
expect(storeMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not persist if local storage key is not defined', async () => {
|
||||||
|
const storeMock = jest.fn();
|
||||||
|
(localStorageModule.default as jest.Mock).mockImplementation(() => {
|
||||||
|
return [false, storeMock];
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(useSidebarToggle, {
|
||||||
|
initialProps: {
|
||||||
|
stateService: {
|
||||||
|
creationOptions: {
|
||||||
|
originatingApp: 'test',
|
||||||
|
localStorageKeyPrefix: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isSidebarCollapsed).toBe(false);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onToggleSidebar(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isSidebarCollapsed).toBe(true);
|
||||||
|
expect(storeMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useCallback, useState, useMemo } from 'react';
|
||||||
|
import useLocalStorage from 'react-use/lib/useLocalStorage';
|
||||||
|
import type { UnifiedFieldListSidebarContainerStateService } from '../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook params
|
||||||
|
*/
|
||||||
|
export interface UseSidebarToggleParams {
|
||||||
|
/**
|
||||||
|
* Service for managing the state
|
||||||
|
*/
|
||||||
|
stateService: UnifiedFieldListSidebarContainerStateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook result type
|
||||||
|
*/
|
||||||
|
export interface UseSidebarToggleResult {
|
||||||
|
isSidebarCollapsed: boolean;
|
||||||
|
onToggleSidebar: (isSidebarCollapsed: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for managing sidebar toggle state
|
||||||
|
* @param stateService
|
||||||
|
*/
|
||||||
|
export const useSidebarToggle = ({
|
||||||
|
stateService,
|
||||||
|
}: UseSidebarToggleParams): UseSidebarToggleResult => {
|
||||||
|
const [initialIsSidebarCollapsed, storeIsSidebarCollapsed] = useLocalStorage<boolean>(
|
||||||
|
`${stateService.creationOptions.localStorageKeyPrefix ?? 'unifiedFieldList'}:sidebarClosed`, // as legacy `discover:sidebarClosed` key
|
||||||
|
false
|
||||||
|
);
|
||||||
|
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState<boolean>(
|
||||||
|
initialIsSidebarCollapsed ?? false
|
||||||
|
);
|
||||||
|
|
||||||
|
const onToggleSidebar = useCallback(
|
||||||
|
(isCollapsed) => {
|
||||||
|
setIsSidebarCollapsed(isCollapsed);
|
||||||
|
if (stateService.creationOptions.localStorageKeyPrefix) {
|
||||||
|
storeIsSidebarCollapsed(isCollapsed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
storeIsSidebarCollapsed,
|
||||||
|
setIsSidebarCollapsed,
|
||||||
|
stateService.creationOptions.localStorageKeyPrefix,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({ isSidebarCollapsed, onToggleSidebar }),
|
||||||
|
[isSidebarCollapsed, onToggleSidebar]
|
||||||
|
);
|
||||||
|
};
|
|
@ -107,6 +107,8 @@ export type OverrideFieldGroupDetails = (
|
||||||
|
|
||||||
export type TimeRangeUpdatesType = 'search-session' | 'timefilter';
|
export type TimeRangeUpdatesType = 'search-session' | 'timefilter';
|
||||||
|
|
||||||
|
export type ButtonAddFieldVariant = 'primary' | 'toolbar';
|
||||||
|
|
||||||
export type SearchMode = 'documents' | 'text-based';
|
export type SearchMode = 'documents' | 'text-based';
|
||||||
|
|
||||||
export interface UnifiedFieldListSidebarContainerCreationOptions {
|
export interface UnifiedFieldListSidebarContainerCreationOptions {
|
||||||
|
@ -116,7 +118,12 @@ export interface UnifiedFieldListSidebarContainerCreationOptions {
|
||||||
originatingApp: string;
|
originatingApp: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Your app name: "discover", "lens", etc. If not provided, sections state would not be persisted.
|
* Pass `true` to enable the compressed view
|
||||||
|
*/
|
||||||
|
compressed?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Your app name: "discover", "lens", etc. If not provided, sections and sidebar toggle states would not be persisted.
|
||||||
*/
|
*/
|
||||||
localStorageKeyPrefix?: string;
|
localStorageKeyPrefix?: string;
|
||||||
|
|
||||||
|
@ -125,6 +132,16 @@ export interface UnifiedFieldListSidebarContainerCreationOptions {
|
||||||
*/
|
*/
|
||||||
timeRangeUpdatesType?: TimeRangeUpdatesType;
|
timeRangeUpdatesType?: TimeRangeUpdatesType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose how the bottom "Add a field" button should look like. Default `primary`.
|
||||||
|
*/
|
||||||
|
buttonAddFieldVariant?: ButtonAddFieldVariant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass `true` to make the sidebar collapsible. Additionally, define `localStorageKeyPrefix` to persist toggle state.
|
||||||
|
*/
|
||||||
|
showSidebarToggleButton?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass `true` to skip auto fetching of fields existence info
|
* Pass `true` to skip auto fetching of fields existence info
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"@kbn/shared-ux-utility",
|
"@kbn/shared-ux-utility",
|
||||||
"@kbn/discover-utils",
|
"@kbn/discover-utils",
|
||||||
"@kbn/ebt-tools",
|
"@kbn/ebt-tools",
|
||||||
|
"@kbn/shared-ux-button-toolbar",
|
||||||
],
|
],
|
||||||
"exclude": ["target/**/*"]
|
"exclude": ["target/**/*"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import {
|
||||||
SEARCH_FIELDS_FROM_SOURCE,
|
SEARCH_FIELDS_FROM_SOURCE,
|
||||||
SHOW_MULTIFIELDS,
|
SHOW_MULTIFIELDS,
|
||||||
} from '@kbn/discover-utils';
|
} from '@kbn/discover-utils';
|
||||||
import { SIDEBAR_CLOSED_KEY } from '../../application/main/components/layout/discover_layout';
|
|
||||||
import { LocalStorageMock } from '../local_storage_mock';
|
import { LocalStorageMock } from '../local_storage_mock';
|
||||||
import { DiscoverServices } from '../../build_services';
|
import { DiscoverServices } from '../../build_services';
|
||||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||||
|
@ -73,9 +72,7 @@ export const services = {
|
||||||
docLinks: { links: { discover: {} } },
|
docLinks: { links: { discover: {} } },
|
||||||
theme,
|
theme,
|
||||||
},
|
},
|
||||||
storage: new LocalStorageMock({
|
storage: new LocalStorageMock({}) as unknown as Storage,
|
||||||
[SIDEBAR_CLOSED_KEY]: false,
|
|
||||||
}) as unknown as Storage,
|
|
||||||
data: {
|
data: {
|
||||||
query: {
|
query: {
|
||||||
timefilter: {
|
timefilter: {
|
||||||
|
|
|
@ -156,7 +156,7 @@ export function ActionBar({
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
{!isSuccessor && showWarning && <EuiSpacer size="s" />}
|
{!isSuccessor && showWarning && <EuiSpacer size="s" />}
|
||||||
{!isSuccessor && showWarning && <ActionBarWarning docCount={docCountAvailable} type={type} />}
|
{!isSuccessor && showWarning && <ActionBarWarning docCount={docCountAvailable} type={type} />}
|
||||||
{!isSuccessor && <EuiSpacer size="s" />}
|
<EuiSpacer size="s" />
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,4 @@
|
||||||
&__cell--highlight {
|
&__cell--highlight {
|
||||||
background-color: tintOrShade($euiColorPrimary, 90%, 70%);
|
background-color: tintOrShade($euiColorPrimary, 90%, 70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.euiDataGridRowCell.euiDataGridRowCell--firstColumn {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import React, { Fragment, memo, useEffect, useRef, useMemo, useCallback } from '
|
||||||
import './context_app.scss';
|
import './context_app.scss';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormattedMessage } from '@kbn/i18n-react';
|
import { FormattedMessage } from '@kbn/i18n-react';
|
||||||
import { EuiText, EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui';
|
import { EuiText, EuiPage, EuiPageBody, EuiSpacer, useEuiPaddingSize } from '@elastic/eui';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
|
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
|
||||||
import { useExecutionContext } from '@kbn/kibana-react-plugin/public';
|
import { useExecutionContext } from '@kbn/kibana-react-plugin/public';
|
||||||
|
@ -215,6 +216,8 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const titlePadding = useEuiPaddingSize('m');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{fetchedState.anchorStatus.value === LoadingStatus.FAILED ? (
|
{fetchedState.anchorStatus.value === LoadingStatus.FAILED ? (
|
||||||
|
@ -235,12 +238,16 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) =>
|
||||||
<EuiPage className={classNames({ dscDocsPage: !isLegacy })}>
|
<EuiPage className={classNames({ dscDocsPage: !isLegacy })}>
|
||||||
<EuiPageBody
|
<EuiPageBody
|
||||||
panelled
|
panelled
|
||||||
paddingSize="s"
|
paddingSize="none"
|
||||||
className="dscDocsContent"
|
className="dscDocsContent"
|
||||||
panelProps={{ role: 'main' }}
|
panelProps={{ role: 'main' }}
|
||||||
>
|
>
|
||||||
<EuiSpacer size="s" />
|
<EuiText
|
||||||
<EuiText data-test-subj="contextDocumentSurroundingHeader">
|
data-test-subj="contextDocumentSurroundingHeader"
|
||||||
|
css={css`
|
||||||
|
padding: ${titlePadding} ${titlePadding} 0;
|
||||||
|
`}
|
||||||
|
>
|
||||||
<strong>
|
<strong>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="discover.context.contextOfTitle"
|
id="discover.context.contextOfTitle"
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
|
|
||||||
import React, { Fragment, useCallback, useMemo, useState } from 'react';
|
import React, { Fragment, useCallback, useMemo, useState } from 'react';
|
||||||
import { FormattedMessage } from '@kbn/i18n-react';
|
import { FormattedMessage } from '@kbn/i18n-react';
|
||||||
import { EuiHorizontalRule, EuiSpacer, EuiText } from '@elastic/eui';
|
import { EuiSpacer, EuiText, useEuiPaddingSize } from '@elastic/eui';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||||
import { SortDirection } from '@kbn/data-plugin/public';
|
import { SortDirection } from '@kbn/data-plugin/public';
|
||||||
import type { SortOrder } from '@kbn/saved-search-plugin/public';
|
import type { SortOrder } from '@kbn/saved-search-plugin/public';
|
||||||
|
@ -149,27 +150,28 @@ export function ContextAppContent({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{!!interceptedWarnings?.length && (
|
<WrapperWithPadding>
|
||||||
<>
|
{!!interceptedWarnings?.length && (
|
||||||
<SearchResponseWarnings
|
<>
|
||||||
variant="callout"
|
<SearchResponseWarnings
|
||||||
interceptedWarnings={interceptedWarnings}
|
variant="callout"
|
||||||
data-test-subj="dscContextInterceptedWarnings"
|
interceptedWarnings={interceptedWarnings}
|
||||||
/>
|
data-test-subj="dscContextInterceptedWarnings"
|
||||||
<EuiSpacer size="s" />
|
/>
|
||||||
</>
|
<EuiSpacer size="s" />
|
||||||
)}
|
</>
|
||||||
<ActionBarMemoized
|
)}
|
||||||
type={SurrDocType.PREDECESSORS}
|
<ActionBarMemoized
|
||||||
defaultStepSize={defaultStepSize}
|
type={SurrDocType.PREDECESSORS}
|
||||||
docCount={predecessorCount}
|
defaultStepSize={defaultStepSize}
|
||||||
docCountAvailable={predecessors.length}
|
docCount={predecessorCount}
|
||||||
onChangeCount={onChangeCount}
|
docCountAvailable={predecessors.length}
|
||||||
isLoading={arePredecessorsLoading}
|
onChangeCount={onChangeCount}
|
||||||
isDisabled={isAnchorLoading}
|
isLoading={arePredecessorsLoading}
|
||||||
/>
|
isDisabled={isAnchorLoading}
|
||||||
{loadingFeedback()}
|
/>
|
||||||
<EuiHorizontalRule margin="xs" />
|
{loadingFeedback()}
|
||||||
|
</WrapperWithPadding>
|
||||||
{isLegacy && rows && rows.length !== 0 && (
|
{isLegacy && rows && rows.length !== 0 && (
|
||||||
<DocTableContextMemoized
|
<DocTableContextMemoized
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -215,16 +217,31 @@ export function ContextAppContent({
|
||||||
</CellActionsProvider>
|
</CellActionsProvider>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<EuiHorizontalRule margin="xs" />
|
<WrapperWithPadding>
|
||||||
<ActionBarMemoized
|
<ActionBarMemoized
|
||||||
type={SurrDocType.SUCCESSORS}
|
type={SurrDocType.SUCCESSORS}
|
||||||
defaultStepSize={defaultStepSize}
|
defaultStepSize={defaultStepSize}
|
||||||
docCount={successorCount}
|
docCount={successorCount}
|
||||||
docCountAvailable={successors.length}
|
docCountAvailable={successors.length}
|
||||||
onChangeCount={onChangeCount}
|
onChangeCount={onChangeCount}
|
||||||
isLoading={areSuccessorsLoading}
|
isLoading={areSuccessorsLoading}
|
||||||
isDisabled={isAnchorLoading}
|
isDisabled={isAnchorLoading}
|
||||||
/>
|
/>
|
||||||
|
</WrapperWithPadding>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WrapperWithPadding: React.FC = ({ children }) => {
|
||||||
|
const padding = useEuiPaddingSize('s');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
css={css`
|
||||||
|
padding: 0 ${padding};
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -50,7 +50,7 @@ export function Doc(props: DocProps) {
|
||||||
values: { id: props.id },
|
values: { id: props.id },
|
||||||
})}
|
})}
|
||||||
</h1>
|
</h1>
|
||||||
<EuiPageBody panelled paddingSize="l" panelProps={{ role: 'main' }}>
|
<EuiPageBody panelled paddingSize="m" panelProps={{ role: 'main' }}>
|
||||||
{reqState === ElasticRequestState.NotFoundDataView && (
|
{reqState === ElasticRequestState.NotFoundDataView && (
|
||||||
<EuiCallOut
|
<EuiCallOut
|
||||||
color="danger"
|
color="danger"
|
||||||
|
|
|
@ -32,18 +32,12 @@ discover-app {
|
||||||
}
|
}
|
||||||
|
|
||||||
.dscPageContent__wrapper {
|
.dscPageContent__wrapper {
|
||||||
padding: $euiSizeS $euiSizeS $euiSizeS 0;
|
|
||||||
overflow: hidden; // Ensures horizontal scroll of table
|
overflow: hidden; // Ensures horizontal scroll of table
|
||||||
|
|
||||||
@include euiBreakpoint('xs', 's') {
|
|
||||||
padding: 0 $euiSizeS $euiSizeS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dscPageContent {
|
.dscPageContent {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: $euiBorderThin;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { EuiPageSidebar } from '@elastic/eui';
|
||||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||||
import type { Query, AggregateQuery } from '@kbn/es-query';
|
import type { Query, AggregateQuery } from '@kbn/es-query';
|
||||||
import { setHeaderActionMenuMounter } from '../../../../kibana_services';
|
import { setHeaderActionMenuMounter } from '../../../../kibana_services';
|
||||||
import { DiscoverLayout, SIDEBAR_CLOSED_KEY } from './discover_layout';
|
import { DiscoverLayout } from './discover_layout';
|
||||||
import { dataViewMock, esHitsMock } from '@kbn/discover-utils/src/__mocks__';
|
import { dataViewMock, esHitsMock } from '@kbn/discover-utils/src/__mocks__';
|
||||||
import { savedSearchMock } from '../../../../__mocks__/saved_search';
|
import { savedSearchMock } from '../../../../__mocks__/saved_search';
|
||||||
import {
|
import {
|
||||||
|
@ -31,9 +31,7 @@ import {
|
||||||
import { createDiscoverServicesMock } from '../../../../__mocks__/services';
|
import { createDiscoverServicesMock } from '../../../../__mocks__/services';
|
||||||
import { FetchStatus } from '../../../types';
|
import { FetchStatus } from '../../../types';
|
||||||
import { RequestAdapter } from '@kbn/inspector-plugin/common';
|
import { RequestAdapter } from '@kbn/inspector-plugin/common';
|
||||||
import { LocalStorageMock } from '../../../../__mocks__/local_storage_mock';
|
|
||||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||||
import { DiscoverServices } from '../../../../build_services';
|
|
||||||
import { buildDataTableRecord } from '@kbn/discover-utils';
|
import { buildDataTableRecord } from '@kbn/discover-utils';
|
||||||
import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock';
|
import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock';
|
||||||
import { createSearchSessionMock } from '../../../../__mocks__/search_session';
|
import { createSearchSessionMock } from '../../../../__mocks__/search_session';
|
||||||
|
@ -41,6 +39,9 @@ import { getSessionServiceMock } from '@kbn/data-plugin/public/search/session/mo
|
||||||
import { DiscoverMainProvider } from '../../services/discover_state_provider';
|
import { DiscoverMainProvider } from '../../services/discover_state_provider';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
import { ErrorCallout } from '../../../../components/common/error_callout';
|
import { ErrorCallout } from '../../../../components/common/error_callout';
|
||||||
|
import * as localStorageModule from 'react-use/lib/useLocalStorage';
|
||||||
|
|
||||||
|
jest.spyOn(localStorageModule, 'default');
|
||||||
|
|
||||||
setHeaderActionMenuMounter(jest.fn());
|
setHeaderActionMenuMounter(jest.fn());
|
||||||
|
|
||||||
|
@ -57,12 +58,7 @@ async function mountComponent(
|
||||||
}) as DataMain$
|
}) as DataMain$
|
||||||
) {
|
) {
|
||||||
const searchSourceMock = createSearchSourceMock({});
|
const searchSourceMock = createSearchSourceMock({});
|
||||||
const services = {
|
const services = createDiscoverServicesMock();
|
||||||
...createDiscoverServicesMock(),
|
|
||||||
storage: new LocalStorageMock({
|
|
||||||
[SIDEBAR_CLOSED_KEY]: prevSidebarClosed,
|
|
||||||
}) as unknown as Storage,
|
|
||||||
} as unknown as DiscoverServices;
|
|
||||||
const time = { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' };
|
const time = { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' };
|
||||||
services.data.query.timefilter.timefilter.getTime = () => time;
|
services.data.query.timefilter.timefilter.getTime = () => time;
|
||||||
(services.data.query.queryString.getDefaultQuery as jest.Mock).mockReturnValue({
|
(services.data.query.queryString.getDefaultQuery as jest.Mock).mockReturnValue({
|
||||||
|
@ -77,6 +73,9 @@ async function mountComponent(
|
||||||
(searchSourceInstanceMock.fetch$ as jest.Mock).mockImplementation(
|
(searchSourceInstanceMock.fetch$ as jest.Mock).mockImplementation(
|
||||||
jest.fn().mockReturnValue(of({ rawResponse: { hits: { total: 2 } } }))
|
jest.fn().mockReturnValue(of({ rawResponse: { hits: { total: 2 } } }))
|
||||||
);
|
);
|
||||||
|
(localStorageModule.default as jest.Mock).mockImplementation(
|
||||||
|
jest.fn(() => [prevSidebarClosed, jest.fn()])
|
||||||
|
);
|
||||||
|
|
||||||
const stateContainer = getDiscoverStateMock({ isTimeBased: true });
|
const stateContainer = getDiscoverStateMock({ isTimeBased: true });
|
||||||
|
|
||||||
|
|
|
@ -6,17 +6,18 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
import './discover_layout.scss';
|
import './discover_layout.scss';
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
EuiButtonIcon,
|
|
||||||
EuiFlexGroup,
|
EuiFlexGroup,
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
EuiHideFor,
|
EuiHideFor,
|
||||||
EuiPage,
|
EuiPage,
|
||||||
EuiPageBody,
|
EuiPageBody,
|
||||||
EuiPanel,
|
EuiPanel,
|
||||||
EuiSpacer,
|
useEuiBackgroundColor,
|
||||||
|
useEuiTheme,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { METRIC_TYPE } from '@kbn/analytics';
|
import { METRIC_TYPE } from '@kbn/analytics';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -52,11 +53,6 @@ import { DiscoverHistogramLayout } from './discover_histogram_layout';
|
||||||
import { ErrorCallout } from '../../../../components/common/error_callout';
|
import { ErrorCallout } from '../../../../components/common/error_callout';
|
||||||
import { addLog } from '../../../../utils/add_log';
|
import { addLog } from '../../../../utils/add_log';
|
||||||
|
|
||||||
/**
|
|
||||||
* Local storage key for sidebar persistence state
|
|
||||||
*/
|
|
||||||
export const SIDEBAR_CLOSED_KEY = 'discover:sidebarClosed';
|
|
||||||
|
|
||||||
const SidebarMemoized = React.memo(DiscoverSidebarResponsive);
|
const SidebarMemoized = React.memo(DiscoverSidebarResponsive);
|
||||||
const TopNavMemoized = React.memo(DiscoverTopNav);
|
const TopNavMemoized = React.memo(DiscoverTopNav);
|
||||||
|
|
||||||
|
@ -72,11 +68,12 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
data,
|
data,
|
||||||
uiSettings,
|
uiSettings,
|
||||||
filterManager,
|
filterManager,
|
||||||
storage,
|
|
||||||
history,
|
history,
|
||||||
spaces,
|
spaces,
|
||||||
inspector,
|
inspector,
|
||||||
} = useDiscoverServices();
|
} = useDiscoverServices();
|
||||||
|
const { euiTheme } = useEuiTheme();
|
||||||
|
const pageBackgroundColor = useEuiBackgroundColor('plain');
|
||||||
const globalQueryState = data.query.getState();
|
const globalQueryState = data.query.getState();
|
||||||
const { main$ } = stateContainer.dataState.data$;
|
const { main$ } = stateContainer.dataState.data$;
|
||||||
const [query, savedQuery, columns, sort] = useAppStateSelector((state) => [
|
const [query, savedQuery, columns, sort] = useAppStateSelector((state) => [
|
||||||
|
@ -109,8 +106,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
return dataView.type !== DataViewType.ROLLUP && dataView.isTimeBased();
|
return dataView.type !== DataViewType.ROLLUP && dataView.isTimeBased();
|
||||||
}, [dataView]);
|
}, [dataView]);
|
||||||
|
|
||||||
const initialSidebarClosed = Boolean(storage.get(SIDEBAR_CLOSED_KEY));
|
|
||||||
const [isSidebarClosed, setIsSidebarClosed] = useState(initialSidebarClosed);
|
|
||||||
const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]);
|
const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]);
|
||||||
|
|
||||||
const isPlainRecord = useMemo(() => getRawRecordType(query) === RecordRawType.PLAIN, [query]);
|
const isPlainRecord = useMemo(() => getRawRecordType(query) === RecordRawType.PLAIN, [query]);
|
||||||
|
@ -172,11 +167,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
filterManager.setFilters(disabledFilters);
|
filterManager.setFilters(disabledFilters);
|
||||||
}, [filterManager]);
|
}, [filterManager]);
|
||||||
|
|
||||||
const toggleSidebarCollapse = useCallback(() => {
|
|
||||||
storage.set(SIDEBAR_CLOSED_KEY, !isSidebarClosed);
|
|
||||||
setIsSidebarClosed(!isSidebarClosed);
|
|
||||||
}, [isSidebarClosed, storage]);
|
|
||||||
|
|
||||||
const contentCentered = resultState === 'uninitialized' || resultState === 'none';
|
const contentCentered = resultState === 'uninitialized' || resultState === 'none';
|
||||||
const documentState = useDataState(stateContainer.dataState.data$.documents$);
|
const documentState = useDataState(stateContainer.dataState.data$.documents$);
|
||||||
|
|
||||||
|
@ -240,7 +230,13 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiPage className="dscPage" data-fetch-counter={fetchCounter.current}>
|
<EuiPage
|
||||||
|
className="dscPage"
|
||||||
|
data-fetch-counter={fetchCounter.current}
|
||||||
|
css={css`
|
||||||
|
background-color: ${pageBackgroundColor};
|
||||||
|
`}
|
||||||
|
>
|
||||||
<h1
|
<h1
|
||||||
id="savedSearchTitle"
|
id="savedSearchTitle"
|
||||||
className="euiScreenReaderOnly"
|
className="euiScreenReaderOnly"
|
||||||
|
@ -274,7 +270,7 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
spaces={spaces}
|
spaces={spaces}
|
||||||
history={history}
|
history={history}
|
||||||
/>
|
/>
|
||||||
<EuiFlexGroup className="dscPageBody__contents" gutterSize="s">
|
<EuiFlexGroup className="dscPageBody__contents" gutterSize="none">
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<SidebarMemoized
|
<SidebarMemoized
|
||||||
documents$={stateContainer.dataState.data$.documents$}
|
documents$={stateContainer.dataState.data$.documents$}
|
||||||
|
@ -284,7 +280,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
onRemoveField={onRemoveColumn}
|
onRemoveField={onRemoveColumn}
|
||||||
onChangeDataView={stateContainer.actions.onChangeDataView}
|
onChangeDataView={stateContainer.actions.onChangeDataView}
|
||||||
selectedDataView={dataView}
|
selectedDataView={dataView}
|
||||||
isClosed={isSidebarClosed}
|
|
||||||
trackUiMetric={trackUiMetric}
|
trackUiMetric={trackUiMetric}
|
||||||
onFieldEdited={onFieldEdited}
|
onFieldEdited={onFieldEdited}
|
||||||
onDataViewCreated={stateContainer.actions.onDataViewCreated}
|
onDataViewCreated={stateContainer.actions.onDataViewCreated}
|
||||||
|
@ -292,23 +287,12 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
/>
|
/>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiHideFor sizes={['xs', 's']}>
|
<EuiHideFor sizes={['xs', 's']}>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem
|
||||||
<div>
|
grow={false}
|
||||||
<EuiSpacer size="s" />
|
css={css`
|
||||||
<EuiButtonIcon
|
border-right: ${euiTheme.border.thin};
|
||||||
iconType={isSidebarClosed ? 'menuRight' : 'menuLeft'}
|
`}
|
||||||
iconSize="m"
|
/>
|
||||||
size="xs"
|
|
||||||
onClick={toggleSidebarCollapse}
|
|
||||||
data-test-subj="collapseSideBarButton"
|
|
||||||
aria-controls="discover-sidebar"
|
|
||||||
aria-expanded={isSidebarClosed ? 'false' : 'true'}
|
|
||||||
aria-label={i18n.translate('discover.toggleSidebarAriaLabel', {
|
|
||||||
defaultMessage: 'Toggle sidebar',
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiHideFor>
|
</EuiHideFor>
|
||||||
<EuiFlexItem className="dscPageContent__wrapper">
|
<EuiFlexItem className="dscPageContent__wrapper">
|
||||||
{resultState === 'none' ? (
|
{resultState === 'none' ? (
|
||||||
|
@ -335,7 +319,10 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
||||||
role="main"
|
role="main"
|
||||||
panelRef={resizeRef}
|
panelRef={resizeRef}
|
||||||
paddingSize="none"
|
paddingSize="none"
|
||||||
|
borderRadius="none"
|
||||||
hasShadow={false}
|
hasShadow={false}
|
||||||
|
hasBorder={false}
|
||||||
|
color="transparent"
|
||||||
className={classNames('dscPageContent', {
|
className={classNames('dscPageContent', {
|
||||||
'dscPageContent--centered': contentCentered,
|
'dscPageContent--centered': contentCentered,
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui';
|
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||||
import { DragDrop, type DropType, DropOverlayWrapper } from '@kbn/dom-drag-drop';
|
import { DragDrop, type DropType, DropOverlayWrapper } from '@kbn/dom-drag-drop';
|
||||||
import useObservable from 'react-use/lib/useObservable';
|
import useObservable from 'react-use/lib/useObservable';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
@ -97,7 +97,6 @@ export const DiscoverMainContent = ({
|
||||||
data-test-subj="dscMainContent"
|
data-test-subj="dscMainContent"
|
||||||
>
|
>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiHorizontalRule margin="none" />
|
|
||||||
{!isPlainRecord && (
|
{!isPlainRecord && (
|
||||||
<DocumentViewModeToggle
|
<DocumentViewModeToggle
|
||||||
viewMode={viewMode}
|
viewMode={viewMode}
|
||||||
|
|
|
@ -119,9 +119,9 @@ export const NoResultsSuggestions: React.FC<NoResultsSuggestionProps> = ({
|
||||||
return (
|
return (
|
||||||
<EuiEmptyPrompt
|
<EuiEmptyPrompt
|
||||||
layout="horizontal"
|
layout="horizontal"
|
||||||
color="plain"
|
color="transparent"
|
||||||
icon={<NoResultsIllustration />}
|
icon={<NoResultsIllustration />}
|
||||||
hasBorder
|
hasBorder={false}
|
||||||
title={
|
title={
|
||||||
<h2 data-test-subj="discoverNoResults">
|
<h2 data-test-subj="discoverNoResults">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|
|
@ -748,5 +748,14 @@ describe('discover responsive sidebar', function () {
|
||||||
|
|
||||||
expect(comp.find('[data-test-subj="custom-data-view-picker"]').exists()).toBe(true);
|
expect(comp.find('[data-test-subj="custom-data-view-picker"]').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow to toggle sidebar', async function () {
|
||||||
|
const comp = await mountComponent(props);
|
||||||
|
expect(findTestSubject(comp, 'fieldList').exists()).toBe(true);
|
||||||
|
findTestSubject(comp, 'unifiedFieldListSidebar__toggle-collapse').simulate('click');
|
||||||
|
expect(findTestSubject(comp, 'fieldList').exists()).toBe(false);
|
||||||
|
findTestSubject(comp, 'unifiedFieldListSidebar__toggle-expand').simulate('click');
|
||||||
|
expect(findTestSubject(comp, 'fieldList').exists()).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -42,7 +42,10 @@ const getCreationOptions: UnifiedFieldListSidebarContainerProps['getCreationOpti
|
||||||
return {
|
return {
|
||||||
originatingApp: PLUGIN_ID,
|
originatingApp: PLUGIN_ID,
|
||||||
localStorageKeyPrefix: 'discover',
|
localStorageKeyPrefix: 'discover',
|
||||||
|
compressed: true,
|
||||||
|
showSidebarToggleButton: true,
|
||||||
disableFieldsExistenceAutoFetching: true,
|
disableFieldsExistenceAutoFetching: true,
|
||||||
|
buttonAddFieldVariant: 'toolbar',
|
||||||
buttonPropsToTriggerFlyout: {
|
buttonPropsToTriggerFlyout: {
|
||||||
contentProps: {
|
contentProps: {
|
||||||
id: DISCOVER_TOUR_STEP_ANCHOR_IDS.addFields,
|
id: DISCOVER_TOUR_STEP_ANCHOR_IDS.addFields,
|
||||||
|
@ -87,10 +90,6 @@ export interface DiscoverSidebarResponsiveProps {
|
||||||
* hits fetched from ES, displayed in the doc table
|
* hits fetched from ES, displayed in the doc table
|
||||||
*/
|
*/
|
||||||
documents$: DataDocuments$;
|
documents$: DataDocuments$;
|
||||||
/**
|
|
||||||
* Has been toggled closed
|
|
||||||
*/
|
|
||||||
isClosed?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* Callback function when selecting a field
|
* Callback function when selecting a field
|
||||||
*/
|
*/
|
||||||
|
@ -380,7 +379,6 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps)
|
||||||
ref={initializeUnifiedFieldListSidebarContainerApi}
|
ref={initializeUnifiedFieldListSidebarContainerApi}
|
||||||
variant={fieldListVariant}
|
variant={fieldListVariant}
|
||||||
getCreationOptions={getCreationOptions}
|
getCreationOptions={getCreationOptions}
|
||||||
isSidebarCollapsed={props.isClosed}
|
|
||||||
services={fieldListSidebarServices}
|
services={fieldListSidebarServices}
|
||||||
dataView={selectedDataView}
|
dataView={selectedDataView}
|
||||||
trackUiMetric={trackUiMetric}
|
trackUiMetric={trackUiMetric}
|
||||||
|
|
|
@ -6,11 +6,10 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EuiTabs, EuiTab, useEuiPaddingSize } from '@elastic/eui';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { EuiTab, EuiTabs, useEuiPaddingSize, useEuiTheme } from '@elastic/eui';
|
||||||
import { FormattedMessage } from '@kbn/i18n-react';
|
import { FormattedMessage } from '@kbn/i18n-react';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import { euiThemeVars } from '@kbn/ui-theme';
|
|
||||||
import { SHOW_FIELD_STATISTICS } from '@kbn/discover-utils';
|
import { SHOW_FIELD_STATISTICS } from '@kbn/discover-utils';
|
||||||
import { VIEW_MODE } from '../../../common/constants';
|
import { VIEW_MODE } from '../../../common/constants';
|
||||||
import { useDiscoverServices } from '../../hooks/use_discover_services';
|
import { useDiscoverServices } from '../../hooks/use_discover_services';
|
||||||
|
@ -22,11 +21,12 @@ export const DocumentViewModeToggle = ({
|
||||||
viewMode: VIEW_MODE;
|
viewMode: VIEW_MODE;
|
||||||
setDiscoverViewMode: (viewMode: VIEW_MODE) => void;
|
setDiscoverViewMode: (viewMode: VIEW_MODE) => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { euiTheme } = useEuiTheme();
|
||||||
const { uiSettings } = useDiscoverServices();
|
const { uiSettings } = useDiscoverServices();
|
||||||
|
|
||||||
const tabsCss = css`
|
const tabsCss = css`
|
||||||
padding: 0 ${useEuiPaddingSize('s')};
|
padding: 0 ${useEuiPaddingSize('s')};
|
||||||
background-color: ${euiThemeVars.euiPageBackgroundColor};
|
border-bottom: ${viewMode === VIEW_MODE.AGGREGATED_LEVEL ? euiTheme.border.thin : 'none'};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const showViewModeToggle = uiSettings.get(SHOW_FIELD_STATISTICS) ?? false;
|
const showViewModeToggle = uiSettings.get(SHOW_FIELD_STATISTICS) ?? false;
|
||||||
|
@ -36,7 +36,7 @@ export const DocumentViewModeToggle = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiTabs size="s" css={tabsCss} data-test-subj="dscViewModeToggle">
|
<EuiTabs size="s" css={tabsCss} data-test-subj="dscViewModeToggle" bottomBorder={false}>
|
||||||
<EuiTab
|
<EuiTab
|
||||||
isSelected={viewMode === VIEW_MODE.DOCUMENT_LEVEL}
|
isSelected={viewMode === VIEW_MODE.DOCUMENT_LEVEL}
|
||||||
onClick={() => setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)}
|
onClick={() => setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)}
|
||||||
|
|
|
@ -275,7 +275,7 @@ export const UnifiedHistogramLayout = ({
|
||||||
chart={chart}
|
chart={chart}
|
||||||
breakdown={breakdown}
|
breakdown={breakdown}
|
||||||
appendHitsCounter={appendHitsCounter}
|
appendHitsCounter={appendHitsCounter}
|
||||||
appendHistogram={showFixedPanels ? <EuiSpacer size="s" /> : <EuiSpacer size="l" />}
|
appendHistogram={<EuiSpacer size="s" />}
|
||||||
disableAutoFetching={disableAutoFetching}
|
disableAutoFetching={disableAutoFetching}
|
||||||
disableTriggers={disableTriggers}
|
disableTriggers={disableTriggers}
|
||||||
disabledActions={disabledActions}
|
disabledActions={disabledActions}
|
||||||
|
|
|
@ -6,12 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { EuiResizableContainer, useGeneratedHtmlId, useResizeObserver } from '@elastic/eui';
|
||||||
EuiResizableContainer,
|
|
||||||
useEuiTheme,
|
|
||||||
useGeneratedHtmlId,
|
|
||||||
useResizeObserver,
|
|
||||||
} from '@elastic/eui';
|
|
||||||
import type { ResizeTrigger } from '@elastic/eui/src/components/resizable_container/types';
|
import type { ResizeTrigger } from '@elastic/eui/src/components/resizable_container/types';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import { isEqual, round } from 'lodash';
|
import { isEqual, round } from 'lodash';
|
||||||
|
@ -162,12 +157,6 @@ export const PanelsResizable = ({
|
||||||
disableResizeWithPortalsHack();
|
disableResizeWithPortalsHack();
|
||||||
}, [disableResizeWithPortalsHack, resizeWithPortalsHackIsResizing]);
|
}, [disableResizeWithPortalsHack, resizeWithPortalsHackIsResizing]);
|
||||||
|
|
||||||
const { euiTheme } = useEuiTheme();
|
|
||||||
const buttonCss = css`
|
|
||||||
margin-top: -${euiTheme.size.base};
|
|
||||||
margin-bottom: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiResizableContainer
|
<EuiResizableContainer
|
||||||
className={className}
|
className={className}
|
||||||
|
@ -189,7 +178,7 @@ export const PanelsResizable = ({
|
||||||
{topPanel}
|
{topPanel}
|
||||||
</EuiResizablePanel>
|
</EuiResizablePanel>
|
||||||
<EuiResizableButton
|
<EuiResizableButton
|
||||||
css={[resizeWithPortalsHackButtonCss, buttonCss]}
|
css={resizeWithPortalsHackButtonCss}
|
||||||
data-test-subj="unifiedHistogramResizableButton"
|
data-test-subj="unifiedHistogramResizableButton"
|
||||||
/>
|
/>
|
||||||
<EuiResizablePanel
|
<EuiResizablePanel
|
||||||
|
|
|
@ -15,6 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
const PageObjects = getPageObjects(['common', 'timePicker', 'discover']);
|
const PageObjects = getPageObjects(['common', 'timePicker', 'discover']);
|
||||||
const kibanaServer = getService('kibanaServer');
|
const kibanaServer = getService('kibanaServer');
|
||||||
const security = getService('security');
|
const security = getService('security');
|
||||||
|
const browser = getService('browser');
|
||||||
const from = 'Jan 1, 2019 @ 00:00:00.000';
|
const from = 'Jan 1, 2019 @ 00:00:00.000';
|
||||||
const to = 'Jan 1, 2019 @ 23:59:59.999';
|
const to = 'Jan 1, 2019 @ 23:59:59.999';
|
||||||
|
|
||||||
|
@ -25,7 +26,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
await kibanaServer.importExport.load(
|
await kibanaServer.importExport.load(
|
||||||
'test/functional/fixtures/kbn_archiver/date_nanos_mixed'
|
'test/functional/fixtures/kbn_archiver/date_nanos_mixed'
|
||||||
);
|
);
|
||||||
await kibanaServer.uiSettings.replace({ defaultIndex: 'timestamp-*' });
|
await kibanaServer.uiSettings.replace({
|
||||||
|
defaultIndex: 'timestamp-*',
|
||||||
|
hideAnnouncements: true, // should be enough vertical space to render rows
|
||||||
|
});
|
||||||
|
await browser.setWindowSize(1200, 900);
|
||||||
await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos_mixed']);
|
await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos_mixed']);
|
||||||
await PageObjects.common.setTime({ from, to });
|
await PageObjects.common.setTime({ from, to });
|
||||||
await PageObjects.common.navigateToApp('discover');
|
await PageObjects.common.navigateToApp('discover');
|
||||||
|
|
|
@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
return actualCount <= expectedCount;
|
return actualCount <= expectedCount;
|
||||||
});
|
});
|
||||||
const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
|
const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
|
||||||
expect(Math.round(newDurationHours)).to.be(26);
|
expect(Math.round(newDurationHours)).to.be(24); // might fail if histogram's width changes
|
||||||
|
|
||||||
await retry.waitFor('doc table containing the documents of the brushed range', async () => {
|
await retry.waitFor('doc table containing the documents of the brushed range', async () => {
|
||||||
const rowData = await PageObjects.discover.getDocTableField(1);
|
const rowData = await PageObjects.discover.getDocTableField(1);
|
||||||
|
|
|
@ -214,16 +214,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
describe('collapse expand', function () {
|
describe('collapse expand', function () {
|
||||||
it('should initially be expanded', async function () {
|
it('should initially be expanded', async function () {
|
||||||
await testSubjects.existOrFail('discover-sidebar');
|
await testSubjects.existOrFail('discover-sidebar');
|
||||||
|
await testSubjects.existOrFail('fieldList');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should collapse when clicked', async function () {
|
it('should collapse when clicked', async function () {
|
||||||
await PageObjects.discover.toggleSidebarCollapse();
|
await PageObjects.discover.toggleSidebarCollapse();
|
||||||
await testSubjects.missingOrFail('discover-sidebar');
|
await testSubjects.existOrFail('discover-sidebar');
|
||||||
|
await testSubjects.missingOrFail('fieldList');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should expand when clicked', async function () {
|
it('should expand when clicked', async function () {
|
||||||
await PageObjects.discover.toggleSidebarCollapse();
|
await PageObjects.discover.toggleSidebarCollapse();
|
||||||
await testSubjects.existOrFail('discover-sidebar');
|
await testSubjects.existOrFail('discover-sidebar');
|
||||||
|
await testSubjects.existOrFail('fieldList');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -370,13 +370,14 @@ export class DiscoverPageObject extends FtrService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async toggleSidebarCollapse() {
|
public async toggleSidebarCollapse() {
|
||||||
return await this.testSubjects.click('collapseSideBarButton');
|
return await this.testSubjects.click('unifiedFieldListSidebar__toggle');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async closeSidebar() {
|
public async closeSidebar() {
|
||||||
await this.retry.tryForTime(2 * 1000, async () => {
|
await this.retry.tryForTime(2 * 1000, async () => {
|
||||||
await this.toggleSidebarCollapse();
|
await this.testSubjects.click('unifiedFieldListSidebar__toggle-collapse');
|
||||||
await this.testSubjects.missingOrFail('discover-sidebar');
|
await this.testSubjects.missingOrFail('unifiedFieldListSidebar__toggle-collapse');
|
||||||
|
await this.testSubjects.missingOrFail('fieldList');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2395,7 +2395,6 @@
|
||||||
"discover.serverLocatorExtension.titleFromLocatorUnknown": "Recherche inconnue",
|
"discover.serverLocatorExtension.titleFromLocatorUnknown": "Recherche inconnue",
|
||||||
"discover.singleDocRoute.errorTitle": "Une erreur s'est produite",
|
"discover.singleDocRoute.errorTitle": "Une erreur s'est produite",
|
||||||
"discover.skipToBottomButtonLabel": "Atteindre la fin du tableau",
|
"discover.skipToBottomButtonLabel": "Atteindre la fin du tableau",
|
||||||
"discover.toggleSidebarAriaLabel": "Activer/Désactiver la barre latérale",
|
|
||||||
"discover.topNav.openSearchPanel.manageSearchesButtonLabel": "Gérer les recherches",
|
"discover.topNav.openSearchPanel.manageSearchesButtonLabel": "Gérer les recherches",
|
||||||
"discover.topNav.openSearchPanel.noSearchesFoundDescription": "Aucune recherche correspondante trouvée.",
|
"discover.topNav.openSearchPanel.noSearchesFoundDescription": "Aucune recherche correspondante trouvée.",
|
||||||
"discover.topNav.openSearchPanel.openSearchTitle": "Ouvrir une recherche",
|
"discover.topNav.openSearchPanel.openSearchTitle": "Ouvrir une recherche",
|
||||||
|
|
|
@ -2410,7 +2410,6 @@
|
||||||
"discover.serverLocatorExtension.titleFromLocatorUnknown": "不明な検索",
|
"discover.serverLocatorExtension.titleFromLocatorUnknown": "不明な検索",
|
||||||
"discover.singleDocRoute.errorTitle": "エラーが発生しました",
|
"discover.singleDocRoute.errorTitle": "エラーが発生しました",
|
||||||
"discover.skipToBottomButtonLabel": "テーブルの最後に移動",
|
"discover.skipToBottomButtonLabel": "テーブルの最後に移動",
|
||||||
"discover.toggleSidebarAriaLabel": "サイドバーを切り替える",
|
|
||||||
"discover.topNav.openSearchPanel.manageSearchesButtonLabel": "検索の管理",
|
"discover.topNav.openSearchPanel.manageSearchesButtonLabel": "検索の管理",
|
||||||
"discover.topNav.openSearchPanel.noSearchesFoundDescription": "一致する検索が見つかりませんでした。",
|
"discover.topNav.openSearchPanel.noSearchesFoundDescription": "一致する検索が見つかりませんでした。",
|
||||||
"discover.topNav.openSearchPanel.openSearchTitle": "検索を開く",
|
"discover.topNav.openSearchPanel.openSearchTitle": "検索を開く",
|
||||||
|
|
|
@ -2410,7 +2410,6 @@
|
||||||
"discover.serverLocatorExtension.titleFromLocatorUnknown": "未知搜索",
|
"discover.serverLocatorExtension.titleFromLocatorUnknown": "未知搜索",
|
||||||
"discover.singleDocRoute.errorTitle": "发生错误",
|
"discover.singleDocRoute.errorTitle": "发生错误",
|
||||||
"discover.skipToBottomButtonLabel": "转到表尾",
|
"discover.skipToBottomButtonLabel": "转到表尾",
|
||||||
"discover.toggleSidebarAriaLabel": "切换侧边栏",
|
|
||||||
"discover.topNav.openSearchPanel.manageSearchesButtonLabel": "管理搜索",
|
"discover.topNav.openSearchPanel.manageSearchesButtonLabel": "管理搜索",
|
||||||
"discover.topNav.openSearchPanel.noSearchesFoundDescription": "未找到匹配的搜索。",
|
"discover.topNav.openSearchPanel.noSearchesFoundDescription": "未找到匹配的搜索。",
|
||||||
"discover.topNav.openSearchPanel.openSearchTitle": "打开搜索",
|
"discover.topNav.openSearchPanel.openSearchTitle": "打开搜索",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue