kibana/x-pack/plugins/timelines/public/components/inspect/index.tsx
Kristof C 7ac6561697
142435 add is one of operator (#144988)
## Summary

This PR adds support for an is one of operator allowing users to filter
multiple values for one field.

[Some investigation
](https://discuss.elastic.co/t/passing-multiple-values-in-kibana-add-filter-is-one-of/232694/2)by
@andrew-goldstein revealed that since the underlying engine uses Lucene,
we can add support for multiple values by using an OR query:

`kibana.alert.workflow_status: ("open" OR "closed" OR "acknowledged")`
is equivalent to
```
"terms": {
      "kibana.alert.workflow_status": [ "open", "closed", "acknowledged"]
    }
```
Where the former is usable in our `DataProviders` used by timeline and
other components that navigate a user to a pre-populated timeline.

As an enhancement to the timeline view, users can also use this `is one
of` operator by interacting with the `Add field` button and selecting
the new operator.

<img width="433" alt="image"
src="https://user-images.githubusercontent.com/28942857/193487154-769005b6-3e5a-40bf-9476-8dd3f3bcb8ee.png">

### Checklist

Delete any items that are not applicable to this PR.

- [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)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [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


## Known issues
This operator does not support timeline templates at this time so usage
there disables the ability for conversion to template field but a better
approach should be implemented to notify users.
https://github.com/elastic/kibana/issues/142437. For now I have added a
template message and prevented users from creating templates with this
operator:

<img width="374" alt="image"
src="https://user-images.githubusercontent.com/28942857/201157676-80017c6c-9f5b-4cd7-ba0b-ee2e43a884cb.png">



## Testing
Create a new timeline or visit an existing one. 
Click 'Add field' button on Timeline in OR query section
add any field ( preferably one that can have many values- consider
`kibana.alerts.workflow_status` but this requires alerts.
Select the `is one of` or `is not one of operator`
Add or remove values in the value section.
Click save.

Co-authored-by: Kristof-Pierre Cummings <kristofpierre.cummings@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
2022-11-16 07:06:20 -07:00

114 lines
2.8 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiButtonIcon } from '@elastic/eui';
import { getOr } from 'lodash/fp';
import React, { useCallback, useState } from 'react';
import styled, { css } from 'styled-components';
import { ModalInspectQuery } from './modal';
import * as i18n from './translations';
import { InspectQuery } from '../../store/t_grid/inputs';
export const BUTTON_CLASS = 'inspectButtonComponent';
export const InspectButtonContainer = styled.div<{ show?: boolean }>`
width: 100%;
display: flex;
flex-grow: 1;
> * {
max-width: 100%;
}
.${BUTTON_CLASS} {
pointer-events: none;
opacity: 0;
transition: opacity ${(props) => getOr(250, 'theme.eui.euiAnimSpeedNormal', props)} ease;
}
${({ show }) =>
show &&
css`
&:hover .${BUTTON_CLASS} {
pointer-events: auto;
opacity: 1;
}
`}
`;
InspectButtonContainer.displayName = 'InspectButtonContainer';
InspectButtonContainer.defaultProps = {
show: true,
};
interface OwnProps {
inspect: InspectQuery | null;
isDisabled?: boolean;
loading: boolean;
onCloseInspect?: () => void;
title: string | React.ReactElement | React.ReactNode;
}
export type InspectButtonProps = OwnProps;
const InspectButtonComponent: React.FC<InspectButtonProps> = ({
inspect,
isDisabled,
loading,
onCloseInspect,
title = '',
}) => {
const [isInspected, setIsInspected] = useState(false);
const isShowingModal = !loading && isInspected;
const handleClick = useCallback(() => {
setIsInspected(true);
}, []);
const handleCloseModal = useCallback(() => {
if (onCloseInspect != null) {
onCloseInspect();
}
setIsInspected(false);
}, [onCloseInspect, setIsInspected]);
let request: string | null = null;
if (inspect != null && inspect.dsl.length > 0) {
request = inspect.dsl[0];
}
let response: string | null = null;
if (inspect != null && inspect.response.length > 0) {
response = inspect.response[0];
}
return (
<>
<EuiButtonIcon
className={BUTTON_CLASS}
aria-label={i18n.INSPECT}
data-test-subj="inspect-icon-button"
iconSize="m"
iconType="inspect"
isDisabled={loading || isDisabled}
title={i18n.INSPECT}
onClick={handleClick}
/>
<ModalInspectQuery
closeModal={handleCloseModal}
isShowing={isShowingModal}
request={request}
response={response}
title={title}
data-test-subj="inspect-modal"
/>
</>
);
};
export const InspectButton = React.memo(InspectButtonComponent);