live message tailing (#1672)
* live tailing * addind test case * fixing useffect array deps * adding test cases for select * adding test cases for filters * deleting unused code * adding test case for filter
This commit is contained in:
parent
e85e1aafa1
commit
39359bb9a9
9 changed files with 227 additions and 1510 deletions
|
@ -1,5 +1,13 @@
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
interface MessageLoadingProps {
|
||||||
|
isLive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageLoadingSpinnerProps {
|
||||||
|
isFetching: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const FiltersWrapper = styled.div`
|
export const FiltersWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -83,3 +91,36 @@ export const MetricsIcon = styled.div`
|
||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const MessageLoading = styled.div<MessageLoadingProps>`
|
||||||
|
color: ${({ theme }) => theme.heading.h3.color};
|
||||||
|
font-size: ${({ theme }) => theme.heading.h3.fontSize};
|
||||||
|
display: ${(props) => (props.isLive ? 'flex' : 'none')};
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 250px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const StopLoading = styled.div`
|
||||||
|
color: ${({ theme }) => theme.pageLoader.borderColor};
|
||||||
|
font-size: ${({ theme }) => theme.heading.h3.fontSize};
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MessageLoadingSpinner = styled.div<MessageLoadingSpinnerProps>`
|
||||||
|
display: ${(props) => (props.isFetching ? 'block' : 'none')};
|
||||||
|
border: 3px solid ${({ theme }) => theme.pageLoader.borderColor};
|
||||||
|
border-bottom: 3px solid ${({ theme }) => theme.pageLoader.borderBottomColor};
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
animation: spin 1.3s linear infinite;
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -53,8 +53,9 @@ const SeekTypeOptions = [
|
||||||
{ value: SeekType.TIMESTAMP, label: 'Timestamp' },
|
{ value: SeekType.TIMESTAMP, label: 'Timestamp' },
|
||||||
];
|
];
|
||||||
const SeekDirectionOptions = [
|
const SeekDirectionOptions = [
|
||||||
{ value: SeekDirection.FORWARD, label: 'Oldest First' },
|
{ value: SeekDirection.FORWARD, label: 'Oldest First', isLive: false },
|
||||||
{ value: SeekDirection.BACKWARD, label: 'Newest First' },
|
{ value: SeekDirection.BACKWARD, label: 'Newest First', isLive: false },
|
||||||
|
{ value: SeekDirection.TAILING, label: 'Live Mode', isLive: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
const Filters: React.FC<FiltersProps> = ({
|
const Filters: React.FC<FiltersProps> = ({
|
||||||
|
@ -100,7 +101,6 @@ const Filters: React.FC<FiltersProps> = ({
|
||||||
(searchParams.get('seekDirection') as SeekDirection) ||
|
(searchParams.get('seekDirection') as SeekDirection) ||
|
||||||
SeekDirection.FORWARD
|
SeekDirection.FORWARD
|
||||||
);
|
);
|
||||||
|
|
||||||
const isSeekTypeControlVisible = React.useMemo(
|
const isSeekTypeControlVisible = React.useMemo(
|
||||||
() => selectedPartitions.length > 0,
|
() => selectedPartitions.length > 0,
|
||||||
[selectedPartitions]
|
[selectedPartitions]
|
||||||
|
@ -167,14 +167,21 @@ const Filters: React.FC<FiltersProps> = ({
|
||||||
search: `?${qs}`,
|
search: `?${qs}`,
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, [seekDirection]);
|
||||||
|
|
||||||
const toggleSeekDirection = (val: string) => {
|
const toggleSeekDirection = (val: string) => {
|
||||||
const nextSeekDirectionValue =
|
switch (val) {
|
||||||
val === SeekDirection.FORWARD
|
case SeekDirection.FORWARD:
|
||||||
? SeekDirection.FORWARD
|
setSeekDirection(SeekDirection.FORWARD);
|
||||||
: SeekDirection.BACKWARD;
|
break;
|
||||||
setSeekDirection(nextSeekDirectionValue);
|
case SeekDirection.BACKWARD:
|
||||||
|
setSeekDirection(SeekDirection.BACKWARD);
|
||||||
|
break;
|
||||||
|
case SeekDirection.TAILING:
|
||||||
|
setSeekDirection(SeekDirection.TAILING);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSSECancel = () => {
|
const handleSSECancel = () => {
|
||||||
|
@ -228,6 +235,7 @@ const Filters: React.FC<FiltersProps> = ({
|
||||||
}, [
|
}, [
|
||||||
clusterName,
|
clusterName,
|
||||||
topicName,
|
topicName,
|
||||||
|
seekDirection,
|
||||||
location,
|
location,
|
||||||
setIsFetching,
|
setIsFetching,
|
||||||
resetMessages,
|
resetMessages,
|
||||||
|
@ -268,6 +276,7 @@ const Filters: React.FC<FiltersProps> = ({
|
||||||
selectSize="M"
|
selectSize="M"
|
||||||
minWidth="100px"
|
minWidth="100px"
|
||||||
options={SeekTypeOptions}
|
options={SeekTypeOptions}
|
||||||
|
disabled={seekDirection === SeekDirection.TAILING}
|
||||||
/>
|
/>
|
||||||
{currentSeekType === SeekType.OFFSET ? (
|
{currentSeekType === SeekType.OFFSET ? (
|
||||||
<Input
|
<Input
|
||||||
|
@ -276,7 +285,9 @@ const Filters: React.FC<FiltersProps> = ({
|
||||||
inputSize="M"
|
inputSize="M"
|
||||||
value={offset}
|
value={offset}
|
||||||
className="offset-selector"
|
className="offset-selector"
|
||||||
|
placeholder="Offset"
|
||||||
onChange={({ target: { value } }) => setOffset(value)}
|
onChange={({ target: { value } }) => setOffset(value)}
|
||||||
|
disabled={seekDirection === SeekDirection.TAILING}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<DatePicker
|
<DatePicker
|
||||||
|
@ -287,6 +298,7 @@ const Filters: React.FC<FiltersProps> = ({
|
||||||
dateFormat="MMMM d, yyyy HH:mm"
|
dateFormat="MMMM d, yyyy HH:mm"
|
||||||
className="date-picker"
|
className="date-picker"
|
||||||
placeholderText="Select timestamp"
|
placeholderText="Select timestamp"
|
||||||
|
disabled={seekDirection === SeekDirection.TAILING}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</S.SeekTypeSelectorWrapper>
|
</S.SeekTypeSelectorWrapper>
|
||||||
|
@ -331,10 +343,27 @@ const Filters: React.FC<FiltersProps> = ({
|
||||||
value={seekDirection}
|
value={seekDirection}
|
||||||
minWidth="120px"
|
minWidth="120px"
|
||||||
options={SeekDirectionOptions}
|
options={SeekDirectionOptions}
|
||||||
|
isLive={seekDirection === SeekDirection.TAILING}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<S.FiltersMetrics>
|
<S.FiltersMetrics>
|
||||||
<p style={{ fontSize: 14 }}>{isFetching && phaseMessage}</p>
|
<p style={{ fontSize: 14 }}>
|
||||||
|
{seekDirection !== SeekDirection.TAILING &&
|
||||||
|
isFetching &&
|
||||||
|
phaseMessage}
|
||||||
|
</p>
|
||||||
|
<S.MessageLoading isLive={seekDirection === SeekDirection.TAILING}>
|
||||||
|
<S.MessageLoadingSpinner isFetching={isFetching} />
|
||||||
|
Loading messages.
|
||||||
|
<S.StopLoading
|
||||||
|
onClick={() => {
|
||||||
|
setSeekDirection(SeekDirection.FORWARD);
|
||||||
|
setIsFetching(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Stop loading
|
||||||
|
</S.StopLoading>
|
||||||
|
</S.MessageLoading>
|
||||||
<S.Metric title="Elapsed Time">
|
<S.Metric title="Elapsed Time">
|
||||||
<S.MetricsIcon>
|
<S.MetricsIcon>
|
||||||
<i className="far fa-clock" />
|
<i className="far fa-clock" />
|
||||||
|
|
|
@ -3,8 +3,11 @@ import Filters, {
|
||||||
FiltersProps,
|
FiltersProps,
|
||||||
} from 'components/Topics/Topic/Details/Messages/Filters/Filters';
|
} from 'components/Topics/Topic/Details/Messages/Filters/Filters';
|
||||||
import { render } from 'lib/testHelpers';
|
import { render } from 'lib/testHelpers';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
const setupWrapper = (props?: Partial<FiltersProps>) => (
|
const setupWrapper = (props?: Partial<FiltersProps>) =>
|
||||||
|
render(
|
||||||
<Filters
|
<Filters
|
||||||
clusterName="test-cluster"
|
clusterName="test-cluster"
|
||||||
topicName="test-topic"
|
topicName="test-topic"
|
||||||
|
@ -20,14 +23,83 @@ const setupWrapper = (props?: Partial<FiltersProps>) => (
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
describe('Filters component', () => {
|
describe('Filters component', () => {
|
||||||
it('matches the snapshot', () => {
|
it('renders component', () => {
|
||||||
const component = render(setupWrapper());
|
setupWrapper();
|
||||||
expect(component.baseElement).toMatchSnapshot();
|
|
||||||
});
|
});
|
||||||
describe('when fetching', () => {
|
describe('when fetching', () => {
|
||||||
it('matches the snapshot', () => {
|
it('shows cancel button while fetching', () => {
|
||||||
const component = render(setupWrapper({ isFetching: true }));
|
setupWrapper({ isFetching: true });
|
||||||
expect(component.baseElement).toMatchSnapshot();
|
expect(screen.getByText('Cancel')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when fetching is over', () => {
|
||||||
|
it('shows submit button while fetching is over', () => {
|
||||||
|
setupWrapper();
|
||||||
|
expect(screen.getByText('Submit')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Input elements', () => {
|
||||||
|
it('search input', () => {
|
||||||
|
setupWrapper();
|
||||||
|
const SearchInput = screen.getByPlaceholderText('Search');
|
||||||
|
expect(SearchInput).toBeInTheDocument();
|
||||||
|
expect(SearchInput).toHaveValue('');
|
||||||
|
userEvent.type(SearchInput, 'Hello World!');
|
||||||
|
expect(SearchInput).toHaveValue('Hello World!');
|
||||||
|
});
|
||||||
|
it('offset input', () => {
|
||||||
|
setupWrapper();
|
||||||
|
const OffsetInput = screen.getByPlaceholderText('Offset');
|
||||||
|
expect(OffsetInput).toBeInTheDocument();
|
||||||
|
expect(OffsetInput).toHaveValue('');
|
||||||
|
userEvent.type(OffsetInput, 'Hello World!');
|
||||||
|
expect(OffsetInput).toHaveValue('Hello World!');
|
||||||
|
});
|
||||||
|
it('timestamp input', () => {
|
||||||
|
setupWrapper();
|
||||||
|
const seekTypeSelect = screen.getAllByRole('listbox');
|
||||||
|
const option = screen.getAllByRole('option');
|
||||||
|
userEvent.click(seekTypeSelect[0]);
|
||||||
|
userEvent.selectOptions(seekTypeSelect[0], ['Timestamp']);
|
||||||
|
expect(option[0]).toHaveTextContent('Timestamp');
|
||||||
|
const TimestampInput = screen.getByPlaceholderText('Select timestamp');
|
||||||
|
expect(TimestampInput).toBeInTheDocument();
|
||||||
|
expect(TimestampInput).toHaveValue('');
|
||||||
|
userEvent.type(TimestampInput, 'Hello World!');
|
||||||
|
expect(TimestampInput).toHaveValue('Hello World!');
|
||||||
|
expect(screen.getByText('Submit')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Select elements', () => {
|
||||||
|
it('seekType select', () => {
|
||||||
|
setupWrapper();
|
||||||
|
const seekTypeSelect = screen.getAllByRole('listbox');
|
||||||
|
const option = screen.getAllByRole('option');
|
||||||
|
expect(option[0]).toHaveTextContent('Offset');
|
||||||
|
userEvent.click(seekTypeSelect[0]);
|
||||||
|
userEvent.selectOptions(seekTypeSelect[0], ['Timestamp']);
|
||||||
|
expect(option[0]).toHaveTextContent('Timestamp');
|
||||||
|
expect(screen.getByText('Submit')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
it('seekDirection select', () => {
|
||||||
|
setupWrapper();
|
||||||
|
const seekDirectionSelect = screen.getAllByRole('listbox');
|
||||||
|
const option = screen.getAllByRole('option');
|
||||||
|
userEvent.click(seekDirectionSelect[1]);
|
||||||
|
userEvent.selectOptions(seekDirectionSelect[1], ['Newest First']);
|
||||||
|
expect(option[1]).toHaveTextContent('Newest First');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when live mode is active', () => {
|
||||||
|
it('stop loading', () => {
|
||||||
|
setupWrapper();
|
||||||
|
const StopLoading = screen.getByText('Stop loading');
|
||||||
|
expect(StopLoading).toBeInTheDocument();
|
||||||
|
userEvent.click(StopLoading);
|
||||||
|
const option = screen.getAllByRole('option');
|
||||||
|
expect(option[1]).toHaveTextContent('Oldest First');
|
||||||
|
expect(screen.getByText('Submit')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,10 +5,14 @@ interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SVGWrapper = styled.i`
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
const LiveIcon: React.FC<Props> = () => {
|
const LiveIcon: React.FC<Props> = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<i>
|
<SVGWrapper data-testid="liveIcon">
|
||||||
<svg
|
<svg
|
||||||
width="16"
|
width="16"
|
||||||
height="16"
|
height="16"
|
||||||
|
@ -19,7 +23,7 @@ const LiveIcon: React.FC<Props> = () => {
|
||||||
<circle cx="8" cy="8" r="7" fill={theme.icons.liveIcon.circleBig} />
|
<circle cx="8" cy="8" r="7" fill={theme.icons.liveIcon.circleBig} />
|
||||||
<circle cx="8" cy="8" r="4" fill={theme.icons.liveIcon.circleSmall} />
|
<circle cx="8" cy="8" r="4" fill={theme.icons.liveIcon.circleSmall} />
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</SVGWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const Select = styled.ul<Props>`
|
||||||
position: relative;
|
position: relative;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: ${(props) => (props.isLive ? '5px' : '0')};
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: ${(props) => (props.selectSize === 'M' ? '32px' : '40px')};
|
height: ${(props) => (props.selectSize === 'M' ? '32px' : '40px')};
|
||||||
border: 1px
|
border: 1px
|
||||||
|
@ -26,7 +27,7 @@ export const Select = styled.ul<Props>`
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
padding-left: ${(props) => (props.isLive ? '36px' : '12px')};
|
padding-left: 16px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
color: ${({ theme, disabled }) =>
|
color: ${({ theme, disabled }) =>
|
||||||
disabled ? theme.select.color.disabled : theme.select.color.normal};
|
disabled ? theme.select.color.disabled : theme.select.color.normal};
|
||||||
|
@ -38,8 +39,8 @@ export const Select = styled.ul<Props>`
|
||||||
background-repeat: no-repeat !important;
|
background-repeat: no-repeat !important;
|
||||||
background-position-x: calc(100% - 8px) !important;
|
background-position-x: calc(100% - 8px) !important;
|
||||||
background-position-y: 55% !important;
|
background-position-y: 55% !important;
|
||||||
|
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||||
&:hover {
|
&:hover:enabled {
|
||||||
color: ${(props) => props.theme.select.color.hover};
|
color: ${(props) => props.theme.select.color.hover};
|
||||||
border-color: ${(props) => props.theme.select.borderColor.hover};
|
border-color: ${(props) => props.theme.select.borderColor.hover};
|
||||||
}
|
}
|
||||||
|
@ -51,7 +52,6 @@ export const Select = styled.ul<Props>`
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: ${(props) => props.theme.select.color.disabled};
|
color: ${(props) => props.theme.select.color.disabled};
|
||||||
border-color: ${(props) => props.theme.select.borderColor.disabled};
|
border-color: ${(props) => props.theme.select.borderColor.disabled};
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -71,7 +71,6 @@ export const OptionList = styled.ul`
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
width: 7px;
|
width: 7px;
|
||||||
|
@ -89,10 +88,12 @@ export const OptionList = styled.ul`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Option = styled.li<OptionProps>`
|
export const Option = styled.li<OptionProps>`
|
||||||
|
display: flex;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${(props) => props.theme.select.backgroundColor.hover};
|
background-color: ${(props) => props.theme.select.backgroundColor.hover};
|
||||||
|
|
|
@ -22,6 +22,7 @@ export interface SelectOption {
|
||||||
label: string | number;
|
label: string | number;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
isLive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Select: React.FC<SelectProps> = ({
|
const Select: React.FC<SelectProps> = ({
|
||||||
|
@ -53,10 +54,12 @@ const Select: React.FC<SelectProps> = ({
|
||||||
if (onChange) onChange(option.value);
|
if (onChange) onChange(option.value);
|
||||||
setShowOptions(false);
|
setShowOptions(false);
|
||||||
};
|
};
|
||||||
|
React.useEffect(() => {
|
||||||
|
setSelectedOption(value);
|
||||||
|
}, [isLive, value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={selectContainerRef}>
|
<div ref={selectContainerRef}>
|
||||||
{isLive && <LiveIcon />}
|
|
||||||
<S.Select
|
<S.Select
|
||||||
role="listbox"
|
role="listbox"
|
||||||
selectSize={selectSize}
|
selectSize={selectSize}
|
||||||
|
@ -66,6 +69,7 @@ const Select: React.FC<SelectProps> = ({
|
||||||
onKeyDown={showOptionsHandler}
|
onKeyDown={showOptionsHandler}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
{isLive && <LiveIcon />}
|
||||||
<S.SelectedOption role="option" tabIndex={0}>
|
<S.SelectedOption role="option" tabIndex={0}>
|
||||||
{options.find(
|
{options.find(
|
||||||
(option) => option.value === (defaultValue || selectedOption)
|
(option) => option.value === (defaultValue || selectedOption)
|
||||||
|
@ -82,6 +86,7 @@ const Select: React.FC<SelectProps> = ({
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="option"
|
role="option"
|
||||||
>
|
>
|
||||||
|
{option.isLive && <LiveIcon />}
|
||||||
{option.label}
|
{option.label}
|
||||||
</S.Option>
|
</S.Option>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import Select, { SelectProps } from 'components/common/Select/Select';
|
import Select, { SelectProps } from 'components/common/Select/Select';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from 'lib/testHelpers';
|
import { render } from 'lib/testHelpers';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
jest.mock('react-hook-form', () => ({
|
jest.mock('react-hook-form', () => ({
|
||||||
useFormContext: () => ({
|
useFormContext: () => ({
|
||||||
|
@ -8,26 +10,47 @@ jest.mock('react-hook-form', () => ({
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const setupWrapper = (props?: Partial<SelectProps>) => (
|
const options = [
|
||||||
<Select name="test" {...props} />
|
{ label: 'test-label1', value: 'test-value1' },
|
||||||
);
|
{ label: 'test-label2', value: 'test-value2' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const renderComponent = (props?: Partial<SelectProps>) =>
|
||||||
|
render(<Select name="test" {...props} />);
|
||||||
|
|
||||||
describe('Custom Select', () => {
|
describe('Custom Select', () => {
|
||||||
|
it('renders component', () => {
|
||||||
|
renderComponent();
|
||||||
|
expect(screen.getByRole('listbox')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
it('show select options when select is being clicked', () => {
|
||||||
|
renderComponent({
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
expect(screen.getByRole('option')).toBeInTheDocument();
|
||||||
|
userEvent.click(screen.getByRole('listbox'));
|
||||||
|
expect(screen.getAllByRole('option')).toHaveLength(3);
|
||||||
|
});
|
||||||
|
it('checking select option change', () => {
|
||||||
|
renderComponent({
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
userEvent.click(screen.getByRole('listbox'));
|
||||||
|
userEvent.selectOptions(screen.getByRole('listbox'), ['test-label1']);
|
||||||
|
expect(screen.getByRole('option')).toHaveTextContent('test-label1');
|
||||||
|
});
|
||||||
|
|
||||||
describe('when non-live', () => {
|
describe('when non-live', () => {
|
||||||
it('matches the snapshot', () => {
|
it('there is not live icon', () => {
|
||||||
const component = render(setupWrapper());
|
renderComponent({ isLive: false });
|
||||||
expect(component.baseElement).toMatchSnapshot();
|
expect(screen.queryByTestId('liveIcon')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when live', () => {
|
describe('when live', () => {
|
||||||
it('matches the snapshot', () => {
|
it('there is live icon', () => {
|
||||||
const component = render(
|
renderComponent({ isLive: true });
|
||||||
setupWrapper({
|
expect(screen.getByTestId('liveIcon')).toBeInTheDocument();
|
||||||
isLive: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(component.baseElement).toMatchSnapshot();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,169 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Custom Select when live matches the snapshot 1`] = `
|
|
||||||
<body>
|
|
||||||
.c0 {
|
|
||||||
position: relative;
|
|
||||||
list-style: none;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-align-items: center;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 40px;
|
|
||||||
border: 1px #ABB5BA solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
width: -webkit-fit-content;
|
|
||||||
width: -moz-fit-content;
|
|
||||||
width: fit-content;
|
|
||||||
padding-left: 36px;
|
|
||||||
padding-right: 16px;
|
|
||||||
color: #171A1C;
|
|
||||||
min-width: auto;
|
|
||||||
background-image: url('data:image/svg+xml,%3Csvg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M1 1L5 5L9 1" stroke="%23454F54"/%3E%3C/svg%3E%0A') !important;
|
|
||||||
background-repeat: no-repeat !important;
|
|
||||||
background-position-x: calc(100% - 8px) !important;
|
|
||||||
background-position-y: 55% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0:hover {
|
|
||||||
color: #171A1C;
|
|
||||||
border-color: #73848C;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0:focus {
|
|
||||||
outline: none;
|
|
||||||
color: #171A1C;
|
|
||||||
border-color: #454F54;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0:disabled {
|
|
||||||
color: #ABB5BA;
|
|
||||||
border-color: #E3E6E8;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c1 {
|
|
||||||
padding-right: 16px;
|
|
||||||
list-style-position: inside;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<i>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
width="16"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<circle
|
|
||||||
cx="8"
|
|
||||||
cy="8"
|
|
||||||
fill="#FAD1D1"
|
|
||||||
r="7"
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
cx="8"
|
|
||||||
cy="8"
|
|
||||||
fill="#E51A1A"
|
|
||||||
r="4"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</i>
|
|
||||||
<ul
|
|
||||||
class="c0"
|
|
||||||
name="test"
|
|
||||||
role="listbox"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
class="c1"
|
|
||||||
role="option"
|
|
||||||
tabindex="0"
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Custom Select when non-live matches the snapshot 1`] = `
|
|
||||||
.c0 {
|
|
||||||
position: relative;
|
|
||||||
list-style: none;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-align-items: center;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 40px;
|
|
||||||
border: 1px #ABB5BA solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
width: -webkit-fit-content;
|
|
||||||
width: -moz-fit-content;
|
|
||||||
width: fit-content;
|
|
||||||
padding-left: 12px;
|
|
||||||
padding-right: 16px;
|
|
||||||
color: #171A1C;
|
|
||||||
min-width: auto;
|
|
||||||
background-image: url('data:image/svg+xml,%3Csvg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M1 1L5 5L9 1" stroke="%23454F54"/%3E%3C/svg%3E%0A') !important;
|
|
||||||
background-repeat: no-repeat !important;
|
|
||||||
background-position-x: calc(100% - 8px) !important;
|
|
||||||
background-position-y: 55% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0:hover {
|
|
||||||
color: #171A1C;
|
|
||||||
border-color: #73848C;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0:focus {
|
|
||||||
outline: none;
|
|
||||||
color: #171A1C;
|
|
||||||
border-color: #454F54;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0:disabled {
|
|
||||||
color: #ABB5BA;
|
|
||||||
border-color: #E3E6E8;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c1 {
|
|
||||||
padding-right: 16px;
|
|
||||||
list-style-position: inside;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<ul
|
|
||||||
class="c0"
|
|
||||||
name="test"
|
|
||||||
role="listbox"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
class="c1"
|
|
||||||
role="option"
|
|
||||||
tabindex="0"
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
`;
|
|
Loading…
Add table
Reference in a new issue