mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search/migrate search filter actions (#23133)
* Search: Initial setup * Search: Use icon prop * Search: Add button variants * Search: Enable toggle all * Search: Fix starred filter * Search: update tests * Search: Enable filters * Search: Use emotion styling * Search: Enable dashboard deleting * Search: Enable dashboard moving * Search: Update tests * Search: Add SearchResultsFilter.test.tsx * Search: Tweak types * Search: Remove onReset * Search: Remove redundant fragment * Search: Use HorizontalGroup * Search: Alight top checkbox
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { SearchResultsFilter, Props } from './SearchResultsFilter';
|
||||
|
||||
const noop = jest.fn();
|
||||
|
||||
const findBtnByText = (wrapper: any, text: string) =>
|
||||
wrapper.findWhere((c: any) => c.name() === 'Button' && c.text() === text);
|
||||
|
||||
const setup = (propOverrides?: Partial<Props>, renderMethod = shallow) => {
|
||||
const props: Props = {
|
||||
//@ts-ignore
|
||||
allChecked: false,
|
||||
canDelete: false,
|
||||
canMove: false,
|
||||
deleteItem: noop,
|
||||
moveTo: noop,
|
||||
onSelectAllChanged: noop,
|
||||
onStarredFilterChange: noop,
|
||||
onTagFilterChange: noop,
|
||||
selectedStarredFilter: 'starred',
|
||||
selectedTagFilter: 'tag',
|
||||
tagFilterOptions: [],
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
const wrapper = renderMethod(<SearchResultsFilter {...props} />);
|
||||
const instance = wrapper.instance();
|
||||
|
||||
return {
|
||||
wrapper,
|
||||
instance,
|
||||
};
|
||||
};
|
||||
|
||||
describe('SearchResultsFilter', () => {
|
||||
it('should render "filter by starred" and "filter by tag" filters by default', () => {
|
||||
const { wrapper } = setup();
|
||||
expect(wrapper.find({ placeholder: 'Filter by starred' })).toHaveLength(1);
|
||||
expect(wrapper.find({ placeholder: 'Filter by tag' })).toHaveLength(1);
|
||||
expect(findBtnByText(wrapper, 'Move')).toHaveLength(0);
|
||||
expect(findBtnByText(wrapper, 'Delete')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should render Move and Delete buttons when canDelete is true', () => {
|
||||
const { wrapper } = setup({ canDelete: true });
|
||||
expect(wrapper.find({ placeholder: 'Filter by starred' })).toHaveLength(0);
|
||||
expect(wrapper.find({ placeholder: 'Filter by tag' })).toHaveLength(0);
|
||||
expect(findBtnByText(wrapper, 'Move')).toHaveLength(1);
|
||||
expect(findBtnByText(wrapper, 'Delete')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should render Move and Delete buttons when canMove is true', () => {
|
||||
const { wrapper } = setup({ canMove: true });
|
||||
expect(wrapper.find({ placeholder: 'Filter by starred' })).toHaveLength(0);
|
||||
expect(wrapper.find({ placeholder: 'Filter by tag' })).toHaveLength(0);
|
||||
expect(findBtnByText(wrapper, 'Move')).toHaveLength(1);
|
||||
expect(findBtnByText(wrapper, 'Delete')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should be called with proper filter option when "filter by starred" is changed', () => {
|
||||
const mockFilterStarred = jest.fn();
|
||||
const option = { value: true, label: 'Yes' };
|
||||
//@ts-ignore
|
||||
const { wrapper } = setup({ onStarredFilterChange: mockFilterStarred }, mount);
|
||||
wrapper
|
||||
.find({ placeholder: 'Filter by starred' })
|
||||
.at(0)
|
||||
.prop('onChange')(option);
|
||||
expect(mockFilterStarred).toHaveBeenCalledTimes(1);
|
||||
expect(mockFilterStarred).toHaveBeenCalledWith(option);
|
||||
});
|
||||
|
||||
it('should be called with proper filter option when "filter by tags" is changed', () => {
|
||||
const mockFilterByTags = jest.fn();
|
||||
const tags = [
|
||||
{ value: 'tag1', label: 'Tag 1' },
|
||||
{ value: 'tag2', label: 'Tag 2' },
|
||||
];
|
||||
//@ts-ignore
|
||||
const { wrapper } = setup({ onTagFilterChange: mockFilterByTags, tagFilterOptions: tags }, mount);
|
||||
wrapper
|
||||
.find({ placeholder: 'Filter by tag' })
|
||||
.at(0)
|
||||
.prop('onChange')(tags[0]);
|
||||
expect(mockFilterByTags).toHaveBeenCalledTimes(1);
|
||||
expect(mockFilterByTags).toHaveBeenCalledWith(tags[0]);
|
||||
});
|
||||
|
||||
it('should call "onSelectAllChanged" when checkbox is changed', () => {
|
||||
const mockSelectAll = jest.fn();
|
||||
const { wrapper } = setup({ onSelectAllChanged: mockSelectAll });
|
||||
wrapper.find('Checkbox').simulate('change');
|
||||
expect(mockSelectAll).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import React, { FC } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { Button, Forms, stylesFactory, useTheme, HorizontalGroup } from '@grafana/ui';
|
||||
import { GrafanaTheme, SelectableValue } from '@grafana/data';
|
||||
|
||||
type onSelectChange = (value: SelectableValue) => void;
|
||||
|
||||
export interface Props {
|
||||
allChecked?: boolean;
|
||||
canDelete?: boolean;
|
||||
canMove?: boolean;
|
||||
deleteItem: () => void;
|
||||
moveTo: () => void;
|
||||
onSelectAllChanged: any;
|
||||
onStarredFilterChange: onSelectChange;
|
||||
onTagFilterChange: onSelectChange;
|
||||
selectedStarredFilter: string;
|
||||
selectedTagFilter: string;
|
||||
tagFilterOptions: SelectableValue[];
|
||||
}
|
||||
|
||||
const starredFilterOptions = [
|
||||
{ label: 'Yes', value: true },
|
||||
{ label: 'No', value: false },
|
||||
];
|
||||
|
||||
export const SearchResultsFilter: FC<Props> = ({
|
||||
allChecked,
|
||||
canDelete,
|
||||
canMove,
|
||||
deleteItem,
|
||||
moveTo,
|
||||
onSelectAllChanged,
|
||||
onStarredFilterChange,
|
||||
onTagFilterChange,
|
||||
selectedStarredFilter,
|
||||
selectedTagFilter,
|
||||
tagFilterOptions,
|
||||
}) => {
|
||||
const showActions = canDelete || canMove;
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<Forms.Checkbox value={allChecked} onChange={onSelectAllChanged} />
|
||||
{showActions ? (
|
||||
<HorizontalGroup spacing="md">
|
||||
<Button disabled={!canMove} onClick={moveTo} icon="fa fa-exchange" variant="secondary">
|
||||
Move
|
||||
</Button>
|
||||
<Button disabled={!canDelete} onClick={deleteItem} icon="fa fa-trash" variant="destructive">
|
||||
Delete
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
) : (
|
||||
<HorizontalGroup spacing="md">
|
||||
<Forms.Select
|
||||
size="sm"
|
||||
placeholder="Filter by starred"
|
||||
key={selectedStarredFilter}
|
||||
options={starredFilterOptions}
|
||||
onChange={onStarredFilterChange}
|
||||
/>
|
||||
|
||||
<Forms.Select
|
||||
size="sm"
|
||||
placeholder="Filter by tag"
|
||||
key={selectedTagFilter}
|
||||
options={tagFilterOptions}
|
||||
onChange={onTagFilterChange}
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
wrapper: css`
|
||||
height: 35px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
label {
|
||||
height: 20px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
@@ -2,4 +2,5 @@ export { SearchResults } from './components/SearchResults';
|
||||
export { SearchField } from './components/SearchField';
|
||||
export { SearchItem } from './components/SearchItem';
|
||||
export { SearchCheckbox } from './components/SearchCheckbox';
|
||||
export { SearchResultsFilter } from './components/SearchResultsFilter';
|
||||
export * from './types';
|
||||
|
||||
Reference in New Issue
Block a user