Skip to content

Commit

Permalink
Timeline component (#89)
Browse files Browse the repository at this point in the history
* feat: started timeline component

* feat: timeline component

* chore: add Timeline story

* chore: change Date to Time

* chore: change detail and add tests

* fix: merge imports

* fix: added icon as a prop

* fix: replace parent with fragment

* chore: added heading level as a prop

* fix: extracted classname prop

* chore: added sr-only to span

* fix: added aria-hidden to Icon
  • Loading branch information
joeychrys authored May 25, 2022
1 parent 39078ae commit 87df117
Show file tree
Hide file tree
Showing 15 changed files with 490 additions and 1 deletion.
20 changes: 20 additions & 0 deletions public/images/timeline-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions public/images/timeline-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
132 changes: 132 additions & 0 deletions src/docs/pages/TimelinePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { FC } from 'react';
import { CodeExample, DemoPage } from './DemoPage';
import { Timeline, Button } from '../../lib';
import { HiArrowNarrowRight, HiCalendar } from 'react-icons/hi';

const TimelinePage: FC = () => {
const examples: CodeExample[] = [
{
title: 'Default timeline',
code: (
<Timeline>
<Timeline.Item>
<Timeline.Point />
<Timeline.Content>
<Timeline.Time>February 2022</Timeline.Time>
<Timeline.Title>Application UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order
E-commerce & Marketing pages.
</Timeline.Body>
<Button color="alternative">
Learn More
<HiArrowNarrowRight className="ml-2 h-3 w-3" />
</Button>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point />
<Timeline.Content>
<Timeline.Time>March 2022</Timeline.Time>
<Timeline.Title>Marketing UI design in Figma</Timeline.Title>
<Timeline.Body>
All of the pages and components are first designed in Figma and we keep a parity between the two
versions even as we update the project.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point />
<Timeline.Content>
<Timeline.Time>April 2022</Timeline.Time>
<Timeline.Title>E-Commerce UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get started with dozens of web components and interactive elements built on top of Tailwind CSS.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
</Timeline>
),
codeClassName: 'dark:!bg-gray-900',
},
{
title: 'Vertical timeline',
code: (
<Timeline>
<Timeline.Item>
<Timeline.Point icon={HiCalendar} />
<Timeline.Content>
<Timeline.Time>February 2022</Timeline.Time>
<Timeline.Title>Application UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order
E-commerce & Marketing pages.
</Timeline.Body>
<Button color="alternative">
Learn More
<HiArrowNarrowRight className="ml-2 h-3 w-3" />
</Button>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point icon={HiCalendar} />
<Timeline.Content>
<Timeline.Time>March 2022</Timeline.Time>
<Timeline.Title>Marketing UI design in Figma</Timeline.Title>
<Timeline.Body>
All of the pages and components are first designed in Figma and we keep a parity between the two
versions even as we update the project.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point icon={HiCalendar} />
<Timeline.Content>
<Timeline.Time>April 2022</Timeline.Time>
<Timeline.Title>E-Commerce UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get started with dozens of web components and interactive elements built on top of Tailwind CSS.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
</Timeline>
),
codeClassName: 'dark:!bg-gray-900',
},
{
title: 'Stepper timeline',
code: (
<Timeline horizontal>
<Timeline.Item>
<Timeline.Point icon={HiCalendar} />
<Timeline.Content>
<Timeline.Title>Flowbite Library v1.0.0</Timeline.Title>
<Timeline.Time>Released on December 2, 2021</Timeline.Time>
<Timeline.Body>Get started with dozens of web components and interactive elements.</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point icon={HiCalendar} />
<Timeline.Content>
<Timeline.Title>Flowbite Library v1.2.0</Timeline.Title>
<Timeline.Time>Released on December 23, 2021</Timeline.Time>
<Timeline.Body>Get started with dozens of web components and interactive elements.</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point icon={HiCalendar} />
<Timeline.Content>
<Timeline.Title>Flowbite Library v1.3.0</Timeline.Title>
<Timeline.Time>Released on January 5, 2022</Timeline.Time>
<Timeline.Body>Get started with dozens of web components and interactive elements.</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
</Timeline>
),
codeClassName: 'dark:!bg-gray-900',
},
];
return <DemoPage examples={examples} />;
};

export default TimelinePage;
13 changes: 13 additions & 0 deletions src/docs/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
HiDeviceTablet,
HiDuplicate,
HiHome,
HiOutlineClock,
HiPencilAlt,
HiStar,
HiTable,
Expand Down Expand Up @@ -48,6 +49,7 @@ import RatingPage from './pages/RatingPage';
import SpinnersPage from './pages/SpinnersPage';
import TablePage from './pages/TablePage';
import ToastPage from './pages/ToastPage';
import TimelinePage from './pages/TimelinePage';
import TooltipsPage from './pages/TooltipsPage';
import SidebarPage from './pages/SidebarPage';
import TabsPage from './pages/TabsPage';
Expand Down Expand Up @@ -317,6 +319,17 @@ export const routes: RouteProps[] = [
images: { light: 'tabs-light.svg', dark: 'tabs-dark.svg' },
},
},
{
title: 'Timeline',
icon: HiOutlineClock,
href: '/timeline',
component: <TimelinePage />,
group: false,
card: {
className: 'w-24',
images: { light: 'timeline-light.svg', dark: 'timeline-dark.svg' },
},
},
{
title: 'Theme',
icon: MdColorLens,
Expand Down
67 changes: 67 additions & 0 deletions src/lib/components/Timeline/Timeline.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { FC } from 'react';
import { cleanup, render } from '@testing-library/react';

import { Timeline, TimelineProps } from '.';

describe('Timeline Component', () => {
afterEach(cleanup);

it('should be able to render a timeline component', () => {
const { getByTestId } = render(<TestComponent />);
expect(getByTestId('timeline-component')).toBeTruthy();
});

it('should be able to render a horizontal timeline component', () => {
const { getAllByTestId } = render(<TestComponent horizontal />);
const timelineItems = getAllByTestId('timeline-item');
const timelineContents = getAllByTestId('timeline-content');
const timelinePoints = getAllByTestId('timeline-point');

expect(timelineItems[0].className).toContain('relative mb-6 sm:mb-0');
expect(timelineContents[0].className).toContain('mt-3 sm:pr-8');
expect(timelinePoints[0].className).toContain('flex items-center');
});
});

const TestComponent: FC<TimelineProps> = (props) => (
<Timeline {...props}>
<Timeline.Item>
<Timeline.Point>
<div className="absolute -left-1.5 mt-1.5 h-3 w-3 rounded-full border border-white bg-gray-200 dark:border-gray-900 dark:bg-gray-700"></div>
</Timeline.Point>
<Timeline.Content>
<Timeline.Time>February 2022</Timeline.Time>
<Timeline.Title>Application UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order
E-commerce & Marketing pages.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point>
<div className="absolute -left-1.5 mt-1.5 h-3 w-3 rounded-full border border-white bg-gray-200 dark:border-gray-900 dark:bg-gray-700"></div>
</Timeline.Point>
<Timeline.Content>
<Timeline.Time>March 2022</Timeline.Time>
<Timeline.Title>Marketing UI design in Figma</Timeline.Title>
<Timeline.Body>
All of the pages and components are first designed in Figma and we keep a parity between the two versions even
as we update the project.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point>
<div className="absolute -left-1.5 mt-1.5 h-3 w-3 rounded-full border border-white bg-gray-200 dark:border-gray-900 dark:bg-gray-700"></div>
</Timeline.Point>
<Timeline.Content>
<Timeline.Time>April 2022</Timeline.Time>
<Timeline.Title>E-Commerce UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get started with dozens of web components and interactive elements built on top of Tailwind CSS.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
</Timeline>
);
50 changes: 50 additions & 0 deletions src/lib/components/Timeline/Timeline.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Meta, Story } from '@storybook/react/types-6-0';
import { Timeline, TimelineProps } from '.';

export default {
title: 'Components/Timeline',
component: Timeline,
} as Meta;

const Template: Story<TimelineProps> = (args) => <Timeline {...args} />;

export const DefaultTimeline = Template.bind({});
DefaultTimeline.storyName = 'Default Timeline';
DefaultTimeline.args = {
children: (
<>
<Timeline.Item>
<Timeline.Point />
<Timeline.Content>
<Timeline.Time>February 2022</Timeline.Time>
<Timeline.Title>Application UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order
E-commerce & Marketing pages.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point />
<Timeline.Content>
<Timeline.Time>March 2022</Timeline.Time>
<Timeline.Title>Marketing UI design in Figma</Timeline.Title>
<Timeline.Body>
All of the pages and components are first designed in Figma and we keep a parity between the two versions
even as we update the project.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point />
<Timeline.Content>
<Timeline.Time>April 2022</Timeline.Time>
<Timeline.Title>E-Commerce UI code in Tailwind CSS</Timeline.Title>
<Timeline.Body>
Get started with dozens of web components and interactive elements built on top of Tailwind CSS.
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
</>
),
};
16 changes: 16 additions & 0 deletions src/lib/components/Timeline/TimelineBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { FC, PropsWithChildren, ComponentProps } from 'react';
import classNames from 'classnames';

export type TimelineBodyProps = PropsWithChildren<
ComponentProps<'p'> & {
className?: string;
}
>;

export const TimelineBody: FC<TimelineBodyProps> = ({ children, className, ...props }) => {
return (
<p className={classNames('mb-4 text-base font-normal text-gray-500 dark:text-gray-400', className)} {...props}>
{children}
</p>
);
};
18 changes: 18 additions & 0 deletions src/lib/components/Timeline/TimelineContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { FC, PropsWithChildren, ComponentProps } from 'react';
import classNames from 'classnames';
import { useTimelineContext } from './TimelineContext';

export type TimelineContentProps = PropsWithChildren<
ComponentProps<'div'> & {
className?: string;
}
>;

export const TimelineContent: FC<TimelineContentProps> = ({ children, className, ...props }) => {
const { horizontal } = useTimelineContext();
return (
<div data-testid="timeline-content" className={classNames({ 'mt-3 sm:pr-8': horizontal }, className)} {...props}>
{children}
</div>
);
};
17 changes: 17 additions & 0 deletions src/lib/components/Timeline/TimelineContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createContext, useContext } from 'react';

export type TimelineContext = {
horizontal?: boolean;
};

export const TimelineContext = createContext<TimelineContext | undefined>(undefined);

export function useTimelineContext(): TimelineContext {
const context = useContext(TimelineContext);

if (!context) {
throw new Error('useTimelineContext should be used within the TimelineContext providor!');
}

return context;
}
Loading

0 comments on commit 87df117

Please sign in to comment.