[Security Solution] Fix few styling issues (#87045)

This commit is contained in:
Patryk Kopyciński 2021-01-04 13:53:23 +01:00 committed by GitHub
parent cd06251fc4
commit f654e06f76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 142 deletions

View file

@ -51,6 +51,14 @@ const HoverActionsContainer = styled(EuiPanel)`
HoverActionsContainer.displayName = 'HoverActionsContainer';
const FullWidthFlexGroup = styled(EuiFlexGroup)`
width: 100%;
`;
const FullWidthFlexItem = styled(EuiFlexItem)`
width: 100%;
`;
export const getColumns = ({
browserFields,
columnHeaders,
@ -159,10 +167,15 @@ export const getColumns = ({
sortable: true,
truncateText: false,
render: (values: ToStringArray | null | undefined, data: EventFieldsData) => (
<EuiFlexGroup direction="column" alignItems="flexStart" component="span" gutterSize="none">
<FullWidthFlexGroup
direction="column"
alignItems="flexStart"
component="span"
gutterSize="none"
>
{values != null &&
values.map((value, i) => (
<EuiFlexItem
<FullWidthFlexItem
grow={false}
component="span"
key={`event-details-value-flex-item-${contextId}-${eventId}-${data.field}-${i}-${value}`}
@ -182,9 +195,9 @@ export const getColumns = ({
/>
)}
</div>
</EuiFlexItem>
</FullWidthFlexItem>
))}
</EuiFlexGroup>
</FullWidthFlexGroup>
),
},
{

View file

@ -8,6 +8,7 @@ import React, { useCallback, memo } from 'react';
import {
EuiSelectableOption,
EuiModalBody,
EuiModalHeader,
EuiMarkdownEditorUiPlugin,
EuiCodeBlock,
} from '@elastic/eui';
@ -47,24 +48,32 @@ const TimelineEditorComponent: React.FC<TimelineEditorProps> = ({ onClosePopover
[]
);
const handleTimelineChange = useCallback(
(timelineTitle, timelineId, graphEventId) => {
const url = formatUrl(getTimelineUrl(timelineId ?? '', graphEventId), {
absolute: true,
skipSearch: true,
});
onInsert(`[${timelineTitle}](${url})`, {
block: false,
});
},
[formatUrl, onInsert]
);
return (
<EuiModalBody>
<SelectableTimeline
hideUntitled={true}
getSelectableOptions={handleGetSelectableOptions}
onTimelineChange={(timelineTitle, timelineId, graphEventId) => {
const url = formatUrl(getTimelineUrl(timelineId ?? '', graphEventId), {
absolute: true,
skipSearch: true,
});
onInsert(`[${timelineTitle}](${url})`, {
block: false,
});
}}
onClosePopover={onClosePopover}
timelineType={TimelineType.default}
/>
</EuiModalBody>
<>
<EuiModalHeader />
<EuiModalBody>
<SelectableTimeline
hideUntitled={true}
getSelectableOptions={handleGetSelectableOptions}
onTimelineChange={handleTimelineChange}
onClosePopover={onClosePopover}
timelineType={TimelineType.default}
/>
</EuiModalBody>
</>
);
};

View file

@ -58,7 +58,7 @@ const HeaderChildrenFlexItem = styled(EuiFlexItem)`
const HistogramPanel = styled(Panel)<{ height?: number }>`
display: flex;
flex-direction: column;
${({ height }) => (height != null ? `height: ${height}px;` : '')}
${({ height }) => (height != null ? `min-height: ${height}px;` : '')}
`;
export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> = ({

View file

@ -156,6 +156,7 @@ export const Header = React.memo<Props>(
onChange={onSearchInputChange}
placeholder={i18n.FILTER_PLACEHOLDER}
value={searchInput}
fullWidth
/>
</SearchContainer>
<CountRow filteredBrowserFields={filteredBrowserFields} />

View file

@ -3,11 +3,14 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiSelectableProps } from '@elastic/eui';
import React from 'react';
import { shallow, ShallowWrapper, mount } from 'enzyme';
import { TimelineType } from '../../../../../common/types/timeline';
import { SortFieldTimeline, Direction } from '../../../../graphql/types';
import { SelectableTimeline, ORIGINAL_PAGE_SIZE, SearchProps } from './';
import { SelectableTimeline, ORIGINAL_PAGE_SIZE } from './';
const mockFetchAllTimeline = jest.fn();
jest.mock('../../../containers/all', () => {
@ -40,10 +43,10 @@ describe('SelectableTimeline', () => {
});
test('render placeholder', () => {
const searchProps: SearchProps = wrapper
const searchProps: EuiSelectableProps['searchProps'] = wrapper
.find('[data-test-subj="selectable-input"]')
.prop('searchProps');
expect(searchProps.placeholder).toEqual('e.g. Timeline name or description');
expect(searchProps!.placeholder).toEqual('e.g. Timeline name or description');
});
});
@ -58,10 +61,10 @@ describe('SelectableTimeline', () => {
});
test('render placeholder', () => {
const searchProps: SearchProps = wrapper
const searchProps: EuiSelectableProps['searchProps'] = wrapper
.find('[data-test-subj="selectable-input"]')
.prop('searchProps');
expect(searchProps.placeholder).toEqual('e.g. Timeline template name or description');
expect(searchProps!.placeholder).toEqual('e.g. Timeline template name or description');
});
});
});
@ -89,7 +92,7 @@ describe('SelectableTimeline', () => {
jest.clearAllMocks();
});
test('shoule be called with correct args', () => {
test('should be called with correct args', () => {
expect(mockFetchAllTimeline).toBeCalledWith(args);
});
});

View file

@ -12,8 +12,7 @@ import {
EuiIcon,
EuiTextColor,
EuiSelectableOption,
EuiPortal,
EuiFilterGroup,
EuiSelectableProps,
EuiFilterButton,
} from '@elastic/eui';
import { isEmpty, debounce } from 'lodash/fp';
@ -41,30 +40,9 @@ const MyEuiFlexItem = styled(EuiFlexItem)`
white-space: nowrap;
`;
const MyEuiFlexGroup = styled(EuiFlexGroup)`
padding 0px 4px;
`;
const EuiSelectableContainer = styled.div<{ isLoading: boolean }>`
.euiSelectable {
.euiFormControlLayout__childrenWrapper {
display: flex;
}
${({ isLoading }) => `${
isLoading
? `
.euiFormControlLayoutIcons {
display: none;
}
.euiFormControlLayoutIcons.euiFormControlLayoutIcons--right {
display: block;
left: 12px;
top: 12px;
}`
: ''
}
`}
}
const StyledEuiFilterButton = styled(EuiFilterButton)`
border-top: 0;
border-bottom: 0;
`;
export const ORIGINAL_PAGE_SIZE = 50;
@ -95,15 +73,6 @@ export interface SelectableTimelineProps {
timelineType: TimelineTypeLiteral;
}
export interface SearchProps {
'data-test-subj'?: string;
isLoading: boolean;
placeholder: string;
onSearch: (arg: string) => void;
incremental: boolean;
inputRef: (arg: HTMLInputElement | null) => void;
}
const SelectableTimelineComponent: React.FC<SelectableTimelineProps> = ({
hideUntitled = false,
getSelectableOptions,
@ -115,7 +84,6 @@ const SelectableTimelineComponent: React.FC<SelectableTimelineProps> = ({
const [heightTrigger, setHeightTrigger] = useState(0);
const [searchTimelineValue, setSearchTimelineValue] = useState<string>('');
const [onlyFavorites, setOnlyFavorites] = useState(false);
const [searchRef, setSearchRef] = useState<HTMLInputElement | null>(null);
const { fetchAllTimeline, timelines, loading, totalCount: timelineCount } = useGetAllTimeline();
const selectableListOuterRef = useRef<HTMLDivElement | null>(null);
const selectableListInnerRef = useRef<HTMLDivElement | null>(null);
@ -156,8 +124,8 @@ const SelectableTimelineComponent: React.FC<SelectableTimelineProps> = ({
[heightTrigger, pageSize]
);
const renderTimelineOption = useCallback((option, searchValue) => {
return (
const renderTimelineOption = useCallback(
(option, searchValue) => (
<EuiFlexGroup
gutterSize="s"
justifyContent="spaceBetween"
@ -193,8 +161,9 @@ const SelectableTimelineComponent: React.FC<SelectableTimelineProps> = ({
/>
</EuiFlexItem>
</EuiFlexGroup>
);
}, []);
),
[]
);
const handleTimelineChange = useCallback(
(options) => {
@ -215,39 +184,53 @@ const SelectableTimelineComponent: React.FC<SelectableTimelineProps> = ({
[onClosePopover, onTimelineChange]
);
const favoritePortal = useMemo(
() =>
searchRef != null ? (
<EuiPortal insert={{ sibling: searchRef, position: 'after' }}>
<MyEuiFlexGroup gutterSize="xs" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiFilterGroup>
<EuiFilterButton
size="l"
data-test-subj="only-favorites-toggle"
hasActiveFilters={onlyFavorites}
onClick={handleOnToggleOnlyFavorites}
>
{i18nTimeline.ONLY_FAVORITES}
</EuiFilterButton>
</EuiFilterGroup>
</EuiFlexItem>
</MyEuiFlexGroup>
</EuiPortal>
) : null,
[searchRef, onlyFavorites, handleOnToggleOnlyFavorites]
const EuiSelectableContent = useCallback(
(list, search) => (
<>
{search}
{list}
</>
),
[]
);
const searchProps: SearchProps = {
'data-test-subj': 'timeline-super-select-search-box',
isLoading: loading,
placeholder: useMemo(() => i18n.SEARCH_BOX_TIMELINE_PLACEHOLDER(timelineType), [timelineType]),
onSearch: onSearchTimeline,
incremental: true,
inputRef: (node: HTMLInputElement | null) => {
setSearchRef(node);
},
};
const searchProps: EuiSelectableProps['searchProps'] = useMemo(
() => ({
'data-test-subj': 'timeline-super-select-search-box',
placeholder: i18n.SEARCH_BOX_TIMELINE_PLACEHOLDER(timelineType),
onSearch: onSearchTimeline,
incremental: true,
append: (
<StyledEuiFilterButton
size="l"
data-test-subj="only-favorites-toggle"
hasActiveFilters={onlyFavorites}
onClick={handleOnToggleOnlyFavorites}
>
{i18nTimeline.ONLY_FAVORITES}
</StyledEuiFilterButton>
),
}),
[handleOnToggleOnlyFavorites, onSearchTimeline, onlyFavorites, timelineType]
);
const listProps: EuiSelectableProps['listProps'] = useMemo(
() => ({
rowHeight: TIMELINE_ITEM_HEIGHT,
showIcons: false,
windowProps: {
onScroll: ({ scrollOffset }) =>
handleOnScroll(
timelines.filter((t) => !hideUntitled || t.title !== '').length,
timelineCount,
scrollOffset
),
outerRef: selectableListOuterRef,
innerRef: selectableListInnerRef,
},
}),
[handleOnScroll, hideUntitled, timelineCount, timelines]
);
useEffect(() => {
fetchAllTimeline({
@ -267,46 +250,25 @@ const SelectableTimelineComponent: React.FC<SelectableTimelineProps> = ({
}, [fetchAllTimeline, onlyFavorites, pageSize, searchTimelineValue, timelineType]);
return (
<EuiSelectableContainer isLoading={loading}>
<EuiSelectable
data-test-subj="selectable-input"
height={POPOVER_HEIGHT}
isLoading={loading && timelines.length === 0}
listProps={{
rowHeight: TIMELINE_ITEM_HEIGHT,
showIcons: false,
windowProps: {
onScroll: ({ scrollOffset }) =>
handleOnScroll(
timelines.filter((t) => !hideUntitled || t.title !== '').length,
timelineCount,
scrollOffset
),
outerRef: selectableListOuterRef,
innerRef: selectableListInnerRef,
},
}}
renderOption={renderTimelineOption}
onChange={handleTimelineChange}
searchable
searchProps={searchProps}
singleSelection={true}
options={getSelectableOptions({
timelines,
onlyFavorites,
searchTimelineValue,
timelineType,
})}
>
{(list, search) => (
<>
{search}
{favoritePortal}
{list}
</>
)}
</EuiSelectable>
</EuiSelectableContainer>
<EuiSelectable
data-test-subj="selectable-input"
height={POPOVER_HEIGHT}
isLoading={loading && timelines.length === 0}
listProps={listProps}
renderOption={renderTimelineOption}
onChange={handleTimelineChange}
searchable
searchProps={searchProps}
singleSelection={true}
options={getSelectableOptions({
timelines,
onlyFavorites,
searchTimelineValue,
timelineType,
})}
>
{EuiSelectableContent}
</EuiSelectable>
);
};

View file

@ -322,7 +322,7 @@ export const formattedSearchStrategyResponse = {
endgameSecurity: 0,
filebeatSystemModule: 1793,
winlogbeatSecurity: 42,
winlogbeatMWSysmonOperational: null,
winlogbeatMWSysmonOperational: 1781,
},
};

View file

@ -58,7 +58,7 @@ export const hostOverview: SecuritySolutionFactory<HostsQueries.overview> = {
winlogbeatMWSysmonOperational: getOr(
null,
'winlog_module.mwsysmon_operational_event_count.doc_count',
response
aggregations
),
},
};

View file

@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) {
endgameSecurity: 4,
filebeatSystemModule: 0,
winlogbeatSecurity: 0,
winlogbeatMWSysmonOperational: null,
winlogbeatMWSysmonOperational: 0,
};
it('Make sure that we get OverviewHost data', async () => {