Skip to content

Commit

Permalink
[Discover] Move total hits counter from histogram to grid area. New c…
Browse files Browse the repository at this point in the history
…ontrols in histogram. (#171638)

- Closes #168825
- Closes #171610
- Closes #167427
- Partially addresses #165192

## Summary

This PR moves the total hits counter closer to the grid, updates
histogram controls and introduces new panel toggle buttons for toggling
fields sidebar and histogram.

<img width="500" alt="Screenshot 2023-12-05 at 15 37 20"
src="https://github.com/elastic/kibana/assets/1415710/5b9bd771-1052-4205-849f-18c21cc299b8">
<img width="500" alt="Screenshot 2023-12-05 at 15 37 29"
src="https://github.com/elastic/kibana/assets/1415710/e5941b27-c497-4d7e-b461-68b66931475a">
<img width="500" alt="Screenshot 2023-12-05 at 15 37 37"
src="https://github.com/elastic/kibana/assets/1415710/97abd32e-9ff2-4d9a-b7e7-b9d6d9cf64db">
<img width="500" alt="Screenshot 2023-12-05 at 15 37 50"
src="https://github.com/elastic/kibana/assets/1415710/10f2b4f4-ec37-41c3-b78b-78c64e14d655">
<img width="400" alt="Screenshot 2023-12-05 at 15 37 59"
src="https://github.com/elastic/kibana/assets/1415710/ef2e28b2-f6ba-4ccb-aea4-3946ba2d5839">
<img width="300" alt="Screenshot 2023-12-05 at 15 38 05"
src="https://github.com/elastic/kibana/assets/1415710/07901ede-0bcb-46a6-a398-4562189fd54f">
<img width="500" alt="Screenshot 2023-12-05 at 15 40 38"
src="https://github.com/elastic/kibana/assets/1415710/17830115-2111-4b8f-ae40-7b5875c06879">
<img width="500" alt="Screenshot 2023-12-05 at 15 40 56"
src="https://github.com/elastic/kibana/assets/1415710/975d475b-280b-495a-b7b7-31c7ade5f21e">
<img width="500" alt="Screenshot 2023-12-05 at 15 43 08"
src="https://github.com/elastic/kibana/assets/1415710/38b6053a-e260-48d8-9591-3f3409df2876">

## Testing

When testing, please check collapsing/expanding the fields sidebar and
histogram. Also for ES|QL mode with suggestions, legacy table, no
results and error prompt, Field Statistics tab, data views without a
time field, light/dark themes.

### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Stratoula Kalafateli <[email protected]>
Co-authored-by: Davis McPhee <[email protected]>
  • Loading branch information
4 people authored Jan 12, 2024
1 parent 6c36503 commit aa33843
Show file tree
Hide file tree
Showing 82 changed files with 2,439 additions and 1,496 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -304,26 +304,14 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
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,
['unifiedFieldListSidebar--fullWidth']: fullWidth,
}),
'aria-label': i18n.translate(
'unifiedFieldList.fieldListSidebar.indexAndFieldsSectionAriaLabel',
{
defaultMessage: 'Index and fields',
}
),
'aria-label': i18n.translate('unifiedFieldList.fieldListSidebar.fieldsSidebarAriaLabel', {
defaultMessage: 'Fields',
}),
id:
stateService.creationOptions.dataTestSubj?.fieldListSidebarDataTestSubj ??
'unifiedFieldListSidebarId',
Expand All @@ -332,6 +320,16 @@ export const UnifiedFieldListSidebarComponent: React.FC<UnifiedFieldListSidebarP
'unifiedFieldListSidebarId',
};

const sidebarToggleButton =
typeof isSidebarCollapsed === 'boolean' && onToggleSidebar ? (
<SidebarToggleButton
buttonSize={compressed ? 's' : 'm'}
isSidebarCollapsed={isSidebarCollapsed}
panelId={pageSidebarProps.id}
onChange={onToggleSidebar}
/>
) : null;

if (isSidebarCollapsed && sidebarToggleButton) {
return (
<EuiHideFor sizes={['xs', 's']}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import React, {
} from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import useObservable from 'react-use/lib/useObservable';
import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
import {
EuiBadge,
Expand All @@ -30,13 +31,12 @@ import {
EuiShowFor,
EuiTitle,
} from '@elastic/eui';
import { BehaviorSubject, Observable } from 'rxjs';
import {
useExistingFieldsFetcher,
type ExistingFieldsFetcher,
} from '../../hooks/use_existing_fields';
import { useQuerySubscriber } from '../../hooks/use_query_subscriber';
import { useSidebarToggle } from '../../hooks/use_sidebar_toggle';
import { getSidebarVisibility, SidebarVisibility } from './get_sidebar_visibility';
import {
UnifiedFieldListSidebar,
type UnifiedFieldListSidebarCustomizableProps,
Expand All @@ -50,7 +50,7 @@ import type {
} from '../../types';

export interface UnifiedFieldListSidebarContainerApi {
isSidebarCollapsed$: Observable<boolean>;
sidebarVisibility: SidebarVisibility;
refetchFieldsExistenceInfo: ExistingFieldsFetcher['refetchFieldsExistenceInfo'];
closeFieldListFlyout: () => void;
// no user permission or missing dataViewFieldEditor service will result in `undefined` API methods
Expand Down Expand Up @@ -122,8 +122,14 @@ const UnifiedFieldListSidebarContainer = forwardRef<
);
const { data, dataViewFieldEditor } = services;
const [isFieldListFlyoutVisible, setIsFieldListFlyoutVisible] = useState<boolean>(false);
const { isSidebarCollapsed, onToggleSidebar } = useSidebarToggle({ stateService });
const [isSidebarCollapsed$] = useState(() => new BehaviorSubject(isSidebarCollapsed));
const [sidebarVisibility] = useState(() =>
getSidebarVisibility({
localStorageKey: stateService.creationOptions.localStorageKeyPrefix
? `${stateService.creationOptions.localStorageKeyPrefix}:sidebarClosed`
: undefined,
})
);
const isSidebarCollapsed = useObservable(sidebarVisibility.isCollapsed$, false);

const canEditDataView =
Boolean(dataViewFieldEditor?.userPermissions.editIndexPattern()) ||
Expand Down Expand Up @@ -225,21 +231,17 @@ const UnifiedFieldListSidebarContainer = forwardRef<
};
}, []);

useEffect(() => {
isSidebarCollapsed$.next(isSidebarCollapsed);
}, [isSidebarCollapsed, isSidebarCollapsed$]);

useImperativeHandle(
componentRef,
() => ({
isSidebarCollapsed$,
sidebarVisibility,
refetchFieldsExistenceInfo,
closeFieldListFlyout,
createField: editField,
editField,
deleteField,
}),
[isSidebarCollapsed$, refetchFieldsExistenceInfo, closeFieldListFlyout, editField, deleteField]
[sidebarVisibility, refetchFieldsExistenceInfo, closeFieldListFlyout, editField, deleteField]
);

if (!dataView) {
Expand All @@ -260,7 +262,7 @@ const UnifiedFieldListSidebarContainer = forwardRef<

if (stateService.creationOptions.showSidebarToggleButton) {
commonSidebarProps.isSidebarCollapsed = isSidebarCollapsed;
commonSidebarProps.onToggleSidebar = onToggleSidebar;
commonSidebarProps.onToggleSidebar = sidebarVisibility.toggle;
}

const buttonPropsToTriggerFlyout = stateService.creationOptions.buttonPropsToTriggerFlyout;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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 { act } from '@testing-library/react-hooks';
import { getSidebarVisibility } from './get_sidebar_visibility';

const localStorageKey = 'test-sidebar-visibility';

describe('UnifiedFieldList getSidebarVisibility', () => {
beforeEach(() => {
localStorage.removeItem(localStorageKey);
});

it('should toggle correctly', async () => {
const state = getSidebarVisibility({ localStorageKey });

expect(state.isCollapsed$.getValue()).toBe(false);

act(() => {
state.toggle(true);
});

expect(state.isCollapsed$.getValue()).toBe(true);
expect(localStorage.getItem(localStorageKey)).toBe('true');

act(() => {
state.toggle(false);
});

expect(state.isCollapsed$.getValue()).toBe(false);
expect(localStorage.getItem(localStorageKey)).toBe('false');
});

it('should restore collapsed state and expand from it', async () => {
localStorage.setItem(localStorageKey, 'true');

const state = getSidebarVisibility({ localStorageKey });

expect(state.isCollapsed$.getValue()).toBe(true);

act(() => {
state.toggle(false);
});

expect(state.isCollapsed$.getValue()).toBe(false);
expect(localStorage.getItem(localStorageKey)).toBe('false');
});

it('should not persist if local storage key is not defined', async () => {
const state = getSidebarVisibility({ localStorageKey: undefined });

expect(state.isCollapsed$.getValue()).toBe(false);

act(() => {
state.toggle(true);
});

expect(state.isCollapsed$.getValue()).toBe(true);
expect(localStorage.getItem(localStorageKey)).toBe(null);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 { BehaviorSubject } from 'rxjs';

export interface SidebarVisibility {
isCollapsed$: BehaviorSubject<boolean>;
toggle: (isCollapsed: boolean) => void;
}

export interface GetSidebarStateParams {
localStorageKey?: string;
}

/**
* For managing sidebar visibility state
* @param localStorageKey
*/
export const getSidebarVisibility = ({
localStorageKey,
}: GetSidebarStateParams): SidebarVisibility => {
const isCollapsed$ = new BehaviorSubject<boolean>(
localStorageKey ? getIsCollapsed(localStorageKey) : false
);

return {
isCollapsed$,
toggle: (isCollapsed) => {
isCollapsed$.next(isCollapsed);
if (localStorageKey) {
setIsCollapsed(localStorageKey, isCollapsed);
}
},
};
};

function getIsCollapsed(localStorageKey: string) {
let isCollapsed = false;
try {
isCollapsed = localStorage?.getItem(localStorageKey) === 'true';
} catch {
// nothing
}

return isCollapsed;
}

function setIsCollapsed(localStorageKey: string, isCollapsed: boolean) {
try {
localStorage?.setItem(localStorageKey, String(isCollapsed));
} catch {
// nothing
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IconButtonGroup, type IconButtonGroupProps } from '@kbn/shared-ux-butto
export interface SidebarToggleButtonProps {
'data-test-subj'?: string;
isSidebarCollapsed: boolean;
panelId?: string;
buttonSize: IconButtonGroupProps['buttonSize'];
onChange: (isSidebarCollapsed: boolean) => void;
}
Expand All @@ -24,12 +25,14 @@ export interface SidebarToggleButtonProps {
* A toggle button for the fields sidebar
* @param data-test-subj
* @param isSidebarCollapsed
* @param panelId
* @param onChange
* @constructor
*/
export const SidebarToggleButton: React.FC<SidebarToggleButtonProps> = ({
'data-test-subj': dataTestSubj = 'unifiedFieldListSidebar__toggle',
isSidebarCollapsed,
panelId,
buttonSize,
onChange,
}) => {
Expand All @@ -49,6 +52,8 @@ export const SidebarToggleButton: React.FC<SidebarToggleButtonProps> = ({
}),
iconType: 'transitionLeftIn',
'data-test-subj': `${dataTestSubj}-expand`,
'aria-expanded': false,
'aria-controls': panelId,
onClick: () => onChange(false),
},
]
Expand All @@ -59,6 +64,8 @@ export const SidebarToggleButton: React.FC<SidebarToggleButtonProps> = ({
}),
iconType: 'transitionLeftOut',
'data-test-subj': `${dataTestSubj}-collapse`,
'aria-expanded': true,
'aria-controls': panelId,
onClick: () => onChange(true),
},
]),
Expand Down
Loading

0 comments on commit aa33843

Please sign in to comment.