Skip to content

Commit

Permalink
Timeline load more component (#667)
Browse files Browse the repository at this point in the history
* group history events

* timeline load more

* Initialize Queries UI (#665)

* Init commit

* more commit

* Add more changes

* Add tests

* clean up code

* Init queries UI

* Add unit tests

* Fix unsaved merge conflict

* Fix tests and merge conflicts again

* update snapshot

* Fix snapshot

* Reorder some props in tile

* resolve comments

* Fix tests

* Fix tests and make design responsive

* Remove snapshots

* Update tests for tile input

* fix retry link spacing

* fix lint

---------

Co-authored-by: Adhitya Mamallan <[email protected]>
  • Loading branch information
Assem-Hafez and adhityamamallan authored Sep 20, 2024
1 parent 9e77dc5 commit 02e913f
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
mockIsIntersecting,
intersectionMockInstance,
} from 'react-intersection-observer/test-utils';

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

import { RequestError } from '@/utils/request/request-error';

import WorkflowHistoryTimelineLoadMore from '../workflow-history-timeline-load-more';
import { type Props } from '../workflow-history-timeline-load-more.types';

describe('WorkflowHistoryTimelineLoadMore', () => {
it('renders loading state while fetching next page', () => {
setup({ isFetchingNextPage: true });

expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
});

it('renders error message if there is an error, and allows retrying', () => {
const { mockFetchNextPage } = setup({
error: new RequestError('An error occurred', 500),
});

expect(screen.getByText(/Failed to load more items./)).toBeInTheDocument();

act(() => {
fireEvent.click(screen.getByText(/Retry manually/));
});

expect(mockFetchNextPage).toHaveBeenCalled();
});

it('renders loading state with the infinite scroll ref when more workflows can be loaded', () => {
const { mockFetchNextPage } = setup({
hasNextPage: true,
isFetchingNextPage: false,
});

const spinnerDiv = screen.getByTestId('intersection-observer-container');
const instance = intersectionMockInstance(spinnerDiv);
expect(instance.observe).toHaveBeenCalledWith(spinnerDiv);

act(() => {
mockIsIntersecting(spinnerDiv, 1);
});
waitFor(
() => {
expect(mockFetchNextPage).toHaveBeenCalled();
},
{ timeout: 2000 }
); //TODO @assem.hafez remove the waitFor when removing the setTimeout
});
});

function setup(overrides: Partial<Props>) {
const mockFetchNextPage = jest.fn();
const defaultProps: Props = {
error: null,
fetchNextPage: mockFetchNextPage,
hasNextPage: true,
isFetchingNextPage: false,
};

render(<WorkflowHistoryTimelineLoadMore {...defaultProps} {...overrides} />);

return { mockFetchNextPage };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { styled as createStyled, withStyle } from 'baseui';
import { StyledLink } from 'baseui/link';

export const styled = {
EndMessageContainer: createStyled<'div', { $isError?: boolean }>(
'div',
({ $theme, $isError }) => ({
margin: '0 auto',
padding: $theme.sizing.scale600,
...$theme.typography.LabelSmall,
color: $isError
? $theme.colors.contentNegative
: $theme.colors.contentTertiary,
})
),
SpinnerContainer: createStyled('div', ({ $theme }) => ({
display: 'flex',
justifyContent: 'center',
padding: $theme.sizing.scale600,
})),
RetryLink: withStyle(StyledLink, ({ $theme }) => ({
color: $theme.colors.contentNegative,
cursor: 'pointer',
})),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';

import { Spinner } from 'baseui/spinner';
import { InView } from 'react-intersection-observer';

import { styled } from './workflow-history-timeline-load-more.styles';
import { type Props } from './workflow-history-timeline-load-more.types';

export default function WorkflowHistoryTimelineLoadMore(props: Props) {
if (props.isFetchingNextPage) {
return (
<styled.SpinnerContainer>
<Spinner data-testid="loading-spinner" />
</styled.SpinnerContainer>
);
}

if (props.error) {
return (
<styled.EndMessageContainer $isError={true}>
Failed to load more items.{' '}
<styled.RetryLink
onClick={() => props.fetchNextPage()}
target="_blank"
rel="noreferrer"
>
Retry manually
</styled.RetryLink>
</styled.EndMessageContainer>
);
}

if (props.hasNextPage) {
return (
<InView
as="div"
data-testid="intersection-observer-container"
onChange={(inView) => {
if (inView && !props.isFetchingNextPage) {
// For testing purposes
// TODO @assem.hafez: Remove setimeout on page finalization
setTimeout(() => props.fetchNextPage(), 2000);
}
}}
>
<styled.SpinnerContainer>
<Spinner data-testid="loading-spinner" />
</styled.SpinnerContainer>
</InView>
);
}
return null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { RequestError } from '@/utils/request/request-error';

export type Props = {
error: RequestError | null;
fetchNextPage: () => void;
hasNextPage: boolean;
isFetchingNextPage: boolean;
};

0 comments on commit 02e913f

Please sign in to comment.