Improve live tailing. (#1898)
* disable filter options when live tailing mode is enabled * prevent seek direction change when stop loading is pressed on live mode * disable submit button while tailing * write tests for MultiSelect.styled component to achieve 100% coverage
This commit is contained in:
parent
247fd23bc0
commit
521ba0cb2f
4 changed files with 88 additions and 8 deletions
|
@ -129,6 +129,8 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
: MessageFilterType.STRING_CONTAINS
|
||||
);
|
||||
const [query, setQuery] = React.useState<string>(searchParams.get('q') || '');
|
||||
const [isTailing, setIsTailing] = React.useState<boolean>(isLive);
|
||||
|
||||
const isSeekTypeControlVisible = React.useMemo(
|
||||
() => selectedPartitions.length > 0,
|
||||
[selectedPartitions]
|
||||
|
@ -136,11 +138,13 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
|
||||
const isSubmitDisabled = React.useMemo(() => {
|
||||
if (isSeekTypeControlVisible) {
|
||||
return currentSeekType === SeekType.TIMESTAMP && !timestamp;
|
||||
return (
|
||||
(currentSeekType === SeekType.TIMESTAMP && !timestamp) || isTailing
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}, [isSeekTypeControlVisible, currentSeekType, timestamp]);
|
||||
}, [isSeekTypeControlVisible, currentSeekType, timestamp, isTailing]);
|
||||
|
||||
const partitionMap = React.useMemo(
|
||||
() =>
|
||||
|
@ -345,6 +349,10 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
handleFiltersSubmit(offset);
|
||||
}, [handleFiltersSubmit, seekDirection]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsTailing(isLive);
|
||||
}, [isLive]);
|
||||
|
||||
return (
|
||||
<S.FiltersWrapper>
|
||||
<div>
|
||||
|
@ -352,6 +360,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
<Search
|
||||
placeholder="Search"
|
||||
value={query}
|
||||
disabled={isTailing}
|
||||
handleSearch={(value: string) => setQuery(value)}
|
||||
/>
|
||||
<S.SeekTypeSelectorWrapper>
|
||||
|
@ -362,7 +371,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
selectSize="M"
|
||||
minWidth="100px"
|
||||
options={SeekTypeOptions}
|
||||
disabled={isLive}
|
||||
disabled={isTailing}
|
||||
/>
|
||||
{currentSeekType === SeekType.OFFSET ? (
|
||||
<Input
|
||||
|
@ -373,7 +382,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
className="offset-selector"
|
||||
placeholder="Offset"
|
||||
onChange={({ target: { value } }) => setOffset(value)}
|
||||
disabled={isLive}
|
||||
disabled={isTailing}
|
||||
/>
|
||||
) : (
|
||||
<DatePicker
|
||||
|
@ -384,7 +393,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
dateFormat="MMMM d, yyyy HH:mm"
|
||||
className="date-picker"
|
||||
placeholderText="Select timestamp"
|
||||
disabled={isLive}
|
||||
disabled={isTailing}
|
||||
/>
|
||||
)}
|
||||
</S.SeekTypeSelectorWrapper>
|
||||
|
@ -397,6 +406,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
value={selectedPartitions}
|
||||
onChange={setSelectedPartitions}
|
||||
labelledBy="Select partitions"
|
||||
disabled={isTailing}
|
||||
/>
|
||||
<S.ClearAll onClick={handleClearAllFilters}>Clear all</S.ClearAll>
|
||||
{isFetching ? (
|
||||
|
@ -465,13 +475,13 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
isFetching &&
|
||||
phaseMessage}
|
||||
</p>
|
||||
<S.MessageLoading isLive={isLive}>
|
||||
<S.MessageLoading isLive={isTailing}>
|
||||
<S.MessageLoadingSpinner isFetching={isFetching} />
|
||||
Loading messages.
|
||||
<S.StopLoading
|
||||
onClick={() => {
|
||||
changeSeekDirection(SeekDirection.FORWARD);
|
||||
setIsFetching(false);
|
||||
handleSSECancel();
|
||||
setIsTailing(false);
|
||||
}}
|
||||
>
|
||||
Stop loading
|
||||
|
|
|
@ -9,8 +9,14 @@ const MultiSelect = styled(ReactMultiSelect)<{ minWidth?: string }>`
|
|||
& > .dropdown-container {
|
||||
height: 32px;
|
||||
|
||||
* {
|
||||
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||
}
|
||||
|
||||
& > .dropdown-heading {
|
||||
height: 32px;
|
||||
color: ${({ disabled, theme }) =>
|
||||
disabled ? theme.select.color.disabled : theme.select.color.active};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import React from 'react';
|
||||
import { render } from 'lib/testHelpers';
|
||||
import MultiSelect from 'components/common/MultiSelect/MultiSelect.styled';
|
||||
import { ISelectProps } from 'react-multi-select-component/dist/lib/interfaces';
|
||||
|
||||
const Option1 = { value: 1, label: 'option 1' };
|
||||
const Option2 = { value: 2, label: 'option 2' };
|
||||
|
||||
interface IMultiSelectProps extends ISelectProps {
|
||||
minWidth?: string;
|
||||
}
|
||||
|
||||
const DefaultProps: IMultiSelectProps = {
|
||||
options: [Option1, Option2],
|
||||
labelledBy: 'multi-select',
|
||||
value: [Option1, Option2],
|
||||
};
|
||||
|
||||
describe('MultiSelect.Styled', () => {
|
||||
const setUpComponent = (props: IMultiSelectProps = DefaultProps) => {
|
||||
const { container } = render(<MultiSelect {...props} />);
|
||||
const multiSelect = container.firstChild;
|
||||
const dropdownContainer = multiSelect?.firstChild?.firstChild;
|
||||
|
||||
return { container, multiSelect, dropdownContainer };
|
||||
};
|
||||
|
||||
it('should have 200px minWidth by default', () => {
|
||||
const { container } = setUpComponent();
|
||||
const multiSelect = container.firstChild;
|
||||
|
||||
expect(multiSelect).toHaveStyle('min-width: 200px');
|
||||
});
|
||||
|
||||
it('should have the provided minWidth in styles', () => {
|
||||
const minWidth = '400px';
|
||||
const { container } = setUpComponent({ ...DefaultProps, minWidth });
|
||||
const multiSelect = container.firstChild;
|
||||
|
||||
expect(multiSelect).toHaveStyle(`min-width: ${minWidth}`);
|
||||
});
|
||||
|
||||
describe('when not disabled', () => {
|
||||
it('should have cursor pointer', () => {
|
||||
const { dropdownContainer } = setUpComponent();
|
||||
|
||||
expect(dropdownContainer).toHaveStyle(`cursor: pointer`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when disabled', () => {
|
||||
it('should have cursor not-allowed', () => {
|
||||
const { dropdownContainer } = setUpComponent({
|
||||
...DefaultProps,
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
expect(dropdownContainer).toHaveStyle(`cursor: not-allowed`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,12 +6,14 @@ interface SearchProps {
|
|||
handleSearch: (value: string) => void;
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const Search: React.FC<SearchProps> = ({
|
||||
handleSearch,
|
||||
placeholder = 'Search',
|
||||
value,
|
||||
disabled = false,
|
||||
}) => {
|
||||
const onChange = useDebouncedCallback(
|
||||
(e) => handleSearch(e.target.value),
|
||||
|
@ -26,6 +28,7 @@ const Search: React.FC<SearchProps> = ({
|
|||
defaultValue={value}
|
||||
leftIcon="fas fa-search"
|
||||
inputSize="M"
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue