Skip to content

Commit

Permalink
[feat][Kibana Presentation] Options List new feature (#149121)
Browse files Browse the repository at this point in the history
## Background
Security solution recently started using controls plugin to provide
users some extra filtering capability with help of options list control.

During this implementation, there was some feedback was given from
design team + we were facing some minor issues because of the caches.
Below section gives the list of changes and the reasoning behind each
change. All of these changes were discussed with @ThomThomson

## Summary

This PR introduces 3 new functionalities for optionsList embeddables.

1. Cache invalidation option when reloading an optionsList
- In security solution we have transactional alerting system, where user
frequently update alerts data
- We need the latest data and 1 minute cache of optionsList was
preventing us from doing so.
- This change adds the capability to clear the cache from an embeddable.
3. option to hideSearch Panel
- As a client of control plugin, we look some control over what panels
are visible and what are not.
5. Option to add custom placeholder for optionsList
- Out product team felt that default placeholder for optionsList `Any`
may be confusing for the user and hence I have added the option for
clients to provide a placeholder.

### Checklist

Delete any items that are not applicable to this PR.

- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
  • Loading branch information
logeekal authored Jan 24, 2023
1 parent d60d537 commit 18aff79
Show file tree
Hide file tree
Showing 8 changed files with 20 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/plugins/controls/common/options_list/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export interface OptionsListEmbeddableInput extends DataControlInput {
hideExclude?: boolean;
hideExists?: boolean;
hideSort?: boolean;
hideActionBar?: boolean;
exclude?: boolean;
placeholder?: string;
}

export type OptionsListField = FieldSpec & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export const OptionsListControl = ({ typeaheadSubject }: { typeaheadSubject: Sub
const exclude = select((state) => state.explicitInput.exclude);
const id = select((state) => state.explicitInput.id);

const placeholder = select((state) => state.explicitInput.placeholder);

const loading = select((state) => state.output.loading);

// debounce loading state so loading doesn't flash when user types
Expand Down Expand Up @@ -128,7 +130,7 @@ export const OptionsListControl = ({ typeaheadSubject }: { typeaheadSubject: Sub
>
{hasSelections || existsSelected
? selectionDisplayNode
: OptionsListStrings.control.getPlaceholder()}
: placeholder ?? OptionsListStrings.control.getPlaceholder()}
</EuiFilterButton>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('Options list popover', () => {
test('available options list width responds to container size', async () => {
let popover = await mountComponent({ popoverProps: { width: 301 } });
let popoverDiv = findTestSubject(popover, 'optionsList-control-popover');
expect(popoverDiv.getDOMNode().getAttribute('style')).toBe('width: 301px;');
expect(popoverDiv.getDOMNode().getAttribute('style')).toBe('width: 301px; min-width: 300px;');

// the div cannot be smaller than 301 pixels wide
popover = await mountComponent({ popoverProps: { width: 300 } });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const OptionsListPopover = ({
const field = select((state) => state.componentState.field);

const hideExclude = select((state) => state.explicitInput.hideExclude);
const hideActionBar = select((state) => state.explicitInput.hideActionBar);
const fieldName = select((state) => state.explicitInput.fieldName);
const title = select((state) => state.explicitInput.title);
const id = select((state) => state.explicitInput.id);
Expand All @@ -52,12 +53,12 @@ export const OptionsListPopover = ({
return (
<div
id={`control-popover-${id}`}
style={{ width: width > 300 ? width : undefined }}
style={{ width, minWidth: 300 }}
data-test-subj={`optionsList-control-popover`}
aria-label={OptionsListStrings.popover.getAriaLabel(fieldName)}
>
<EuiPopoverTitle paddingSize="s">{title}</EuiPopoverTitle>
{field?.type !== 'boolean' && (
{field?.type !== 'boolean' && !hideActionBar && (
<OptionsListPopoverActionBar
showOnlySelected={showOnlySelected}
setShowOnlySelected={setShowOnlySelected}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ export class OptionsListEmbeddable extends Embeddable<OptionsListEmbeddableInput
};

reload = () => {
// clear cache when reload is requested
this.optionsListService.clearOptionsListCache();
this.runOptionsListQuery();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ let optionsListRequestMethod = async (request: OptionsListRequest, abortSignal:
)
);

const clearOptionsListCacheMock = () => {};

export const replaceOptionsListMethod = (
newMethod: (request: OptionsListRequest, abortSignal: AbortSignal) => Promise<OptionsListResponse>
) => (optionsListRequestMethod = newMethod);

export const optionsListServiceFactory: OptionsListServiceFactory = () => {
return {
runOptionsListRequest: optionsListRequestMethod,
clearOptionsListCache: clearOptionsListCacheMock,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ class OptionsListService implements ControlsOptionsListService {
return { rejected: true } as OptionsListResponse;
}
};

public clearOptionsListCache = () => {
this.cachedOptionsListRequest.cache = new memoize.Cache();
};
}

export interface OptionsListServiceRequiredServices {
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/controls/public/services/options_list/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ export interface ControlsOptionsListService {
request: OptionsListRequest,
abortSignal: AbortSignal
) => Promise<OptionsListResponse>;

clearOptionsListCache: () => void;
}

0 comments on commit 18aff79

Please sign in to comment.