Skip to content

Use new DateFilterV2 for Advanced Visibility and Archival workflow listing #901

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
"generate:idl": "mkdir -p src/__generated__/idl && npm run generate:idl:proto && npm run generate:idl:proto:types",
"generate:idl:proto": "rm -rf src/__generated__/idl/proto && cp -R node_modules/cadence-idl/proto src/__generated__/idl/proto",
"generate:idl:proto:types": "rm -rf src/__generated__/proto-ts && ./node_modules/.bin/proto-loader-gen-types --includeDirs=src/__generated__/idl/proto/ --enums=String --longs=String --bytes=String --defaults --inputTemplate='%s__Input' --outputTemplate='%s' --oneofs --grpcLib=@grpc/grpc-js --outDir=src/__generated__/proto-ts/ $(npx glob --all --nodir --cwd=src/__generated__/idl/proto **/*.proto) ",
"test": "jest --config jest.config.ts && npm run test:types",
"test": "TZ=UTC jest --config jest.config.ts && npm run test:types",
"test:unit": "npm run test:unit:browser && npm run test:unit:node",
"test:unit:browser": "jest --config jest/browser/jest.config.ts",
"test:unit:node": "jest --config jest/node/jest.config.ts",
"test:unit:browser": "TZ=UTC jest --config jest/browser/jest.config.ts",
"test:unit:node": "TZ=UTC jest --config jest/node/jest.config.ts",
"test:types": "tstyche"
},
"engines": {
Expand Down
265 changes: 265 additions & 0 deletions src/components/date-filter-v2/__tests__/date-filter-v2.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
import React, { useState } from 'react';

import { type StatefulPopoverProps } from 'baseui/popover';
import { type TimePickerProps } from 'baseui/timepicker';

import { render, screen, fireEvent, act, waitFor } from '@/test-utils/rtl';

import dayjs from '@/utils/datetime/dayjs';

import DateFilterV2 from '../date-filter-v2';
import { type DateFilterRange } from '../date-filter-v2.types';

jest.useFakeTimers().setSystemTime(new Date('2023-05-25'));

// Mock StatefulPopover to render content immediately in tests
jest.mock('baseui/popover', () => {
const originalModule = jest.requireActual('baseui/popover');
return {
...originalModule,
StatefulPopover: ({ content, children }: StatefulPopoverProps) => {
const [isShown, setIsShown] = useState(false);

return (
<div onClick={() => setIsShown(true)}>
{children}
{isShown ? (
<div data-testid="popover-content">
{typeof content === 'function' &&
content({ close: () => setIsShown(false) })}
</div>
) : null}
</div>
);
},
};
});

jest.mock('baseui/timepicker', () => ({
TimePicker: jest.fn(
({ value, onChange, disabled }: TimePickerProps) =>
onChange && (
<input
data-testid="time-picker"
value={value?.toTimeString() ?? ''}
onChange={(e) => onChange(new Date(e.target.value))}
disabled={disabled}
/>
)
),
}));

const mockDateOverrides: DateFilterRange = {
start: dayjs('2023-05-23T00:00:00.000Z'),
end: dayjs('2023-05-24T00:00:00.000Z'),
};

describe(DateFilterV2.name, () => {
it('displays the date filter component with placeholder when no dates are provided', () => {
setup({});
expect(screen.getByPlaceholderText('Mock placeholder')).toBeInTheDocument();
});

it('renders without errors when dates are already provided', () => {
setup({
overrides: mockDateOverrides,
});

expect(
screen.getByDisplayValue('23 May, 00:00:00 UTC - 24 May, 00:00:00 UTC')
).toBeInTheDocument();
});

it('opens a popover when clicked', () => {
setup({});
const datePicker = screen.getByPlaceholderText('Mock placeholder');

act(() => {
fireEvent.click(datePicker);
});

// Check for elements in the popover
expect(screen.getByText('Quick Range')).toBeInTheDocument();
expect(screen.getByText('Custom Range')).toBeInTheDocument();
expect(screen.getByText('Last 5 minutes')).toBeInTheDocument();
});

it('selects a relative time range when clicking a quick range button', () => {
const { mockOnChangeDates } = setup({});
const datePicker = screen.getByPlaceholderText('Mock placeholder');

act(() => {
fireEvent.click(datePicker);
});

const lastFiveMinutesButton = screen.getByText('Last 5 minutes');

act(() => {
fireEvent.click(lastFiveMinutesButton);
});

expect(mockOnChangeDates).toHaveBeenCalledWith({
start: 'now-5m',
end: 'now',
});
});

it('allows selecting a custom date range via the calendar', () => {
const { mockOnChangeDates } = setup({});
const datePicker = screen.getByPlaceholderText('Mock placeholder');

act(() => {
fireEvent.click(datePicker);
});

const saveButton = screen.getByText('Save');
const timePickers = screen.getAllByTestId('time-picker');

act(() => {
fireEvent.click(screen.getByLabelText(/May 13th 2023/));
});

expect(saveButton).toBeDisabled();
timePickers.forEach((picker) => {
expect(picker).toBeDisabled();
});

act(() => {
fireEvent.click(screen.getByLabelText(/May 14th 2023/));
});

expect(saveButton).not.toBeDisabled();
timePickers.forEach((picker) => {
expect(picker).not.toBeDisabled();
});

act(() => {
fireEvent.click(saveButton);
});

expect(mockOnChangeDates).toHaveBeenCalledWith({
start: dayjs('2023-05-13'),
end: dayjs('2023-05-14'),
});
});

it('handles single date selection (same start and end date)', () => {
const { mockOnChangeDates } = setup({});
const datePicker = screen.getByPlaceholderText('Mock placeholder');

act(() => {
fireEvent.click(datePicker);
});

act(() => {
fireEvent.click(screen.getByLabelText(/May 13th 2023/));
});

act(() => {
fireEvent.click(screen.getByLabelText(/May 13th 2023/));
});

const saveButton = screen.getByText('Save');
act(() => {
fireEvent.click(saveButton);
});

expect(mockOnChangeDates).toHaveBeenCalledWith({
start: dayjs('2023-05-13'),
end: dayjs('2023-05-13').endOf('day'),
});
});

it('allows time adjustment after date selection', () => {
const { mockOnChangeDates } = setup({});
const datePicker = screen.getByPlaceholderText('Mock placeholder');

act(() => {
fireEvent.click(datePicker);
});

act(() => {
fireEvent.click(screen.getByLabelText(/May 13th 2023/));
});

act(() => {
fireEvent.click(screen.getByLabelText(/May 14th 2023/));
});

const timePickers = screen.getAllByTestId('time-picker');
expect(timePickers).toHaveLength(2);

act(() => {
fireEvent.change(timePickers[0], {
target: { value: '2023-05-13 11:45' },
});
fireEvent.change(timePickers[1], {
target: { value: '2023-05-14 15:45' },
});
});

const saveButton = screen.getByText('Save');
act(() => {
fireEvent.click(saveButton);
});

expect(mockOnChangeDates).toHaveBeenCalledWith(
expect.objectContaining({
start: dayjs('2023-05-13 11:45'),
end: dayjs('2023-05-14 15:45'),
})
);
});

it('displays the correct format when using relative date values', () => {
setup({
overrides: {
start: 'now-1h',
end: 'now',
},
});

expect(screen.getByDisplayValue('Last 1 hour')).toBeInTheDocument();
});

it('closes popover when clicking the close button', () => {
setup({});
const datePicker = screen.getByPlaceholderText('Mock placeholder');

act(() => {
fireEvent.click(datePicker);
});

const quickRangeHeader = screen.getByText('Quick Range');
expect(quickRangeHeader).toBeInTheDocument();

const closeButton = screen.getByTestId('close-button');

act(() => {
fireEvent.click(closeButton);
});

waitFor(() => {
expect(quickRangeHeader).not.toBeInTheDocument();
});
});
});

function setup({ overrides }: { overrides?: Partial<DateFilterRange> }) {
const mockOnChangeDates = jest.fn();

const result = render(
<DateFilterV2
label="Mock label"
placeholder="Mock placeholder"
dates={{
start: undefined,
end: undefined,
...overrides,
}}
onChangeDates={mockOnChangeDates}
/>
);

return { mockOnChangeDates, ...result };
}
12 changes: 12 additions & 0 deletions src/components/date-filter-v2/date-filter-v2.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { type RelativeDurationConfig } from './date-filter-v2.types';

export const DATE_FILTER_RELATIVE_VALUES = {
'now-5m': { label: 'Last 5 minutes', durationSeconds: 5 * 60 },
'now-15m': { label: 'Last 15 minutes', durationSeconds: 15 * 60 },
'now-1h': { label: 'Last 1 hour', durationSeconds: 1 * 60 * 60 },
'now-6h': { label: 'Last 6 hours', durationSeconds: 6 * 60 * 60 },
'now-12h': { label: 'Last 12 hours', durationSeconds: 12 * 60 * 60 },
'now-1d': { label: 'Last 1 day', durationSeconds: 1 * 24 * 60 * 60 },
'now-7d': { label: 'Last 7 days', durationSeconds: 7 * 24 * 60 * 60 },
'now-30d': { label: 'Last 30 days', durationSeconds: 30 * 24 * 60 * 60 },
} as const satisfies Record<`now-${string}`, RelativeDurationConfig>;
Loading
Loading