Skip to content

Commit

Permalink
add cancel button to discover search bar (elastic#175808)
Browse files Browse the repository at this point in the history
  • Loading branch information
ppisljar authored and fkanout committed Mar 4, 2024
1 parent 57c4399 commit 9ae8454
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export function ReactExpressionRenderer({
dataAttrs,
padding,
renderError,
abortController,
...expressionRendererOptions
}: ReactExpressionRendererProps) {
const nodeRef = useRef<HTMLDivElement>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface ExpressionRendererParams extends IExpressionLoaderParams {
* An observable which can be used to re-run the expression without destroying the component
*/
reload$?: Observable<unknown>;
abortController?: AbortController;
}

interface ExpressionRendererState {
Expand All @@ -54,6 +55,7 @@ export function useExpressionRenderer(
onEvent,
onRender$,
reload$,
abortController,
...loaderParams
}: ExpressionRendererParams
): ExpressionRendererState {
Expand All @@ -77,6 +79,13 @@ export function useExpressionRenderer(
// will call done() in LayoutEffect when done with rendering custom error state
const errorRenderHandlerRef = useRef<IInterpreterRenderHandlers | null>(null);

useEffect(() => {
if (abortController?.signal)
abortController.signal.onabort = () => {
expressionLoaderRef.current?.cancel();
};
}, [abortController]);

/* eslint-disable react-hooks/exhaustive-deps */
// OK to ignore react-hooks/exhaustive-deps because options update is handled by calling .update()
useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/expressions/public/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export interface IExpressionLoaderParams {
hasCompatibleActions?: ExpressionRenderHandlerParams['hasCompatibleActions'];
getCompatibleCellValueActions?: ExpressionRenderHandlerParams['getCompatibleCellValueActions'];
executionContext?: KibanaExecutionContext;

abortController?: AbortController;
/**
* The flag to toggle on emitting partial results.
* By default, the partial results are disabled.
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/unified_histogram/public/chart/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { useRefetch } from './hooks/use_refetch';
import { useEditVisualization } from './hooks/use_edit_visualization';

export interface ChartProps {
abortController?: AbortController;
isChartAvailable: boolean;
hiddenPanel?: boolean;
className?: string;
Expand Down Expand Up @@ -123,6 +124,7 @@ export function Chart({
onFilter,
onBrushEnd,
withDefaultActions,
abortController,
}: ChartProps) {
const [isSaveModalVisible, setIsSaveModalVisible] = useState(false);
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
Expand Down Expand Up @@ -406,6 +408,7 @@ export function Chart({
/>
)}
<HistogramMemoized
abortController={abortController}
services={services}
dataView={dataView}
request={request}
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/unified_histogram/public/chart/histogram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { useLensProps } from './hooks/use_lens_props';
import type { LensAttributesContext } from './utils/get_lens_attributes';

export interface HistogramProps {
abortController?: AbortController;
services: UnifiedHistogramServices;
dataView: DataView;
request?: UnifiedHistogramRequestContext;
Expand Down Expand Up @@ -102,6 +103,7 @@ export function Histogram({
onFilter,
onBrushEnd,
withDefaultActions,
abortController,
}: HistogramProps) {
const [bucketInterval, setBucketInterval] = useState<UnifiedHistogramBucketInterval>();
const [chartSize, setChartSize] = useState('100%');
Expand Down Expand Up @@ -223,6 +225,7 @@ export function Histogram({
>
<lens.EmbeddableComponent
{...lensProps}
abortController={abortController}
disableTriggers={disableTriggers}
disabledActions={disabledActions}
onFilter={onFilter}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type UnifiedHistogramContainerProps = {
| 'onFilter'
| 'withDefaultActions'
| 'disabledActions'
| 'abortController'
>;

/**
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/unified_histogram/public/layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren<unknown>
withDefaultActions?: EmbeddableComponentProps['withDefaultActions'];

table?: Datatable;
abortController?: AbortController;
}

export const UnifiedHistogramLayout = ({
Expand Down Expand Up @@ -223,6 +224,7 @@ export const UnifiedHistogramLayout = ({
onBrushEnd,
children,
withDefaultActions,
abortController,
}: UnifiedHistogramLayoutProps) => {
const {
allSuggestions,
Expand Down Expand Up @@ -308,6 +310,7 @@ export const UnifiedHistogramLayout = ({
<>
<InPortal node={topPanelNode}>
<ChartMemoized
abortController={abortController}
isChartAvailable={isChartAvailable}
className={chartClassName}
services={services}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ describe('QueryBarTopRowTopRow', () => {
const QUERY_INPUT_SELECTOR = 'QueryStringInputUI';
const TIMEPICKER_SELECTOR = 'Memo(EuiSuperDatePicker)';
const REFRESH_BUTTON_SELECTOR = 'EuiSuperUpdateButton';
const CANCEL_BUTTON_SELECTOR = '[data-test-subj="queryCancelButton"]';
const TIMEPICKER_DURATION = '[data-shared-timefilter-duration]';
const TEXT_BASED_EDITOR = '[data-test-subj="unifiedTextLangEditor"]';

Expand Down Expand Up @@ -396,6 +397,42 @@ describe('QueryBarTopRowTopRow', () => {

expect(getByTestId('dataViewPickerOverride')).toBeInTheDocument();
});

it('Should render cancel button when loading', () => {
const component = mount(
wrapQueryBarTopRowInContext({
isLoading: true,
onCancel: () => {},
isDirty: false,
screenTitle: 'Another Screen',
showDatePicker: true,
showSubmitButton: true,
dateRangeFrom: 'now-7d',
dateRangeTo: 'now',
timeHistory: mockTimeHistory,
})
);

expect(component.find(CANCEL_BUTTON_SELECTOR).length).not.toBe(0);
});

it('Should NOT render cancel button when not loading', () => {
const component = mount(
wrapQueryBarTopRowInContext({
isLoading: false,
onCancel: () => {},
isDirty: false,
screenTitle: 'Another Screen',
showDatePicker: true,
showSubmitButton: true,
dateRangeFrom: 'now-7d',
dateRangeTo: 'now',
timeHistory: mockTimeHistory,
})
);

expect(component.find(CANCEL_BUTTON_SELECTOR).length).toBe(0);
});
});

describe('SharingMetaFields', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
useIsWithinBreakpoints,
EuiSuperUpdateButton,
EuiToolTip,
EuiButton,
EuiButtonIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { TimeHistoryContract, getQueryLog } from '@kbn/data-plugin/public';
Expand Down Expand Up @@ -67,6 +69,10 @@ export const strings = {
i18n.translate('unifiedSearch.queryBarTopRow.submitButton.refresh', {
defaultMessage: 'Refresh query',
}),
getCancelQueryLabel: () =>
i18n.translate('unifiedSearch.queryBarTopRow.submitButton.cancel', {
defaultMessage: 'Cancel',
}),
getRunQueryLabel: () =>
i18n.translate('unifiedSearch.queryBarTopRow.submitButton.run', {
defaultMessage: 'Run query',
Expand Down Expand Up @@ -127,6 +133,7 @@ export interface QueryBarTopRowProps<QT extends Query | AggregateQuery = Query>
onRefresh?: (payload: { dateRange: TimeRange }) => void;
onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void;
onSubmit: (payload: { dateRange: TimeRange; query?: Query | QT }) => void;
onCancel?: () => void;
placeholder?: string;
prepend?: React.ComponentProps<typeof EuiFieldText>['prepend'];
query?: Query | QT;
Expand Down Expand Up @@ -282,6 +289,7 @@ export const QueryBarTopRow = React.memo(
dateRangeRef.current = currentDateRange;

const propsOnSubmit = props.onSubmit;
const propsOnCancel = props.onCancel;

const toRecentlyUsedRanges = (ranges: TimeRange[]) =>
ranges.map(({ from, to }: { from: string; to: string }) => {
Expand Down Expand Up @@ -339,6 +347,20 @@ export const QueryBarTopRow = React.memo(
[persistedLog, onSubmit]
);

const onClickCancelButton = useCallback(
(event: React.MouseEvent<HTMLButtonElement>) => {
if (persistedLog && queryRef.current && isOfQueryType(queryRef.current)) {
persistedLog.add(queryRef.current.query);
}
event.preventDefault();

if (propsOnCancel) {
propsOnCancel();
}
},
[persistedLog, propsOnCancel]
);

const propsOnChange = props.onChange;
const onQueryChange = useCallback(
(query: Query) => {
Expand Down Expand Up @@ -486,6 +508,39 @@ export const QueryBarTopRow = React.memo(
return <EuiFlexItem className={wrapperClasses}>{component}</EuiFlexItem>;
}

function renderCancelButton() {
const buttonLabelCancel = strings.getCancelQueryLabel();

if (submitButtonIconOnly) {
return (
<EuiButtonIcon
iconType="cross"
aria-label={buttonLabelCancel}
onClick={onClickCancelButton}
size={shouldShowDatePickerAsBadge() ? 's' : 'm'}
data-test-subj="queryCancelButton"
color="text"
display="base"
>
{buttonLabelCancel}
</EuiButtonIcon>
);
}

return (
<EuiButton
iconType="cross"
aria-label={buttonLabelCancel}
onClick={onClickCancelButton}
size={shouldShowDatePickerAsBadge() ? 's' : 'm'}
data-test-subj="queryCancelButton"
color="text"
>
{buttonLabelCancel}
</EuiButton>
);
}

function renderUpdateButton() {
if (!shouldRenderUpdatebutton() && !shouldRenderDatePicker()) {
return null;
Expand All @@ -501,25 +556,28 @@ export const QueryBarTopRow = React.memo(
React.cloneElement(props.customSubmitButton, { onClick: onClickSubmitButton })
) : (
<EuiFlexItem grow={false}>
<EuiSuperUpdateButton
iconType={props.isDirty ? iconDirty : 'refresh'}
iconOnly={submitButtonIconOnly}
aria-label={props.isLoading ? buttonLabelUpdate : buttonLabelRefresh}
isDisabled={isDateRangeInvalid || props.isDisabled}
isLoading={props.isLoading}
onClick={onClickSubmitButton}
size={shouldShowDatePickerAsBadge() ? 's' : 'm'}
color={props.isDirty ? 'success' : 'primary'}
fill={props.isDirty}
needsUpdate={props.isDirty}
data-test-subj="querySubmitButton"
// @ts-expect-error Need to fix expecting `children` in EUI
toolTipProps={{
content: props.isDirty ? tooltipDirty : buttonLabelRefresh,
delay: 'long',
position: 'bottom',
}}
/>
{props.isLoading && propsOnCancel && renderCancelButton()}
{(!props.isLoading || !propsOnCancel) && (
<EuiSuperUpdateButton
iconType={props.isDirty ? iconDirty : 'refresh'}
iconOnly={submitButtonIconOnly}
aria-label={props.isLoading ? buttonLabelUpdate : buttonLabelRefresh}
isDisabled={isDateRangeInvalid || props.isDisabled}
isLoading={props.isLoading}
onClick={onClickSubmitButton}
size={shouldShowDatePickerAsBadge() ? 's' : 'm'}
color={props.isDirty ? 'success' : 'primary'}
fill={props.isDirty}
needsUpdate={props.isDirty}
data-test-subj="querySubmitButton"
// @ts-expect-error Need to fix expecting `children` in EUI
toolTipProps={{
content: props.isDirty ? tooltipDirty : buttonLabelRefresh,
delay: 'long',
position: 'bottom',
}}
/>
)}
</EuiFlexItem>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ export function createSearchBar({
dateRangeTo={timeRange.to}
refreshInterval={refreshInterval.value}
isRefreshPaused={refreshInterval.pause}
isLoading={props.isLoading}
onCancel={props.onCancel}
filters={filters}
query={query}
onFiltersUpdated={defaultFiltersUpdated(data.query, props.onFiltersUpdated)}
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/unified_search/public/search_bar/search_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface SearchBarOwnProps<QT extends AggregateQuery | Query = Query> {
onClearSavedQuery?: () => void;

onRefresh?: (payload: { dateRange: TimeRange }) => void;
onCancel?: () => void;
// Autorefresh
onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void;
indicateNoData?: boolean;
Expand Down Expand Up @@ -582,6 +583,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
isDisabled={this.props.isDisabled}
onRefresh={this.props.onRefresh}
onRefreshChange={this.props.onRefreshChange}
onCancel={this.props.onCancel}
onChange={this.onQueryBarChange}
isDirty={this.isDirty()}
customSubmitButton={
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/lens/public/embeddable/embeddable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ interface LensBaseEmbeddableInput extends EmbeddableInput {
onTableRowClick?: (
data: Simplify<LensTableRowContextMenuEvent['data'] & PreventableEvent>
) => void;
abortController?: AbortController;
}

export type LensByValueInput = {
Expand Down Expand Up @@ -1146,6 +1147,7 @@ export class Embeddable
className={input.className}
style={input.style}
executionContext={this.getExecutionContext()}
abortController={this.input.abortController}
addUserMessages={(messages) => this.addUserMessages(messages)}
onRuntimeError={(error) => {
this.updateOutput({ error });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export type EmbeddableComponentProps = (TypedLensByValueInput | LensByReferenceI
withDefaultActions?: boolean;
extraActions?: Action[];
showInspector?: boolean;
abortController?: AbortController;
};

export type EmbeddableComponent = React.ComponentType<EmbeddableComponentProps>;
Expand Down Expand Up @@ -142,6 +143,7 @@ interface EmbeddablePanelWrapperProps {
extraActions?: Action[];
showInspector?: boolean;
withDefaultActions?: boolean;
abortController?: AbortController;
}

const EmbeddablePanelWrapper: FC<EmbeddablePanelWrapperProps> = ({
Expand All @@ -152,6 +154,7 @@ const EmbeddablePanelWrapper: FC<EmbeddablePanelWrapperProps> = ({
extraActions,
showInspector = true,
withDefaultActions,
abortController,
}) => {
const [embeddable, loading] = useEmbeddableFactory({ factory, input });
useEffect(() => {
Expand Down
Loading

0 comments on commit 9ae8454

Please sign in to comment.