Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lucky-ladybugs-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"flowbite-react": patch
---

Allow `Timeline.Point` to render components as inner content.
6 changes: 6 additions & 0 deletions apps/web/content/docs/components/timeline.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Use the `horizontal` prop to show the timeline component and the child component

<Example name="timeline.horizontal" />

## Render props

Use the `render` prop to render an Avatar or any other component as TimelinePoint inner content.

<Example name="timeline.render" />

## Theme

To learn more about how to customize the appearance of components, please see the [Theme docs](/docs/customize/theme).
Expand Down
1 change: 1 addition & 0 deletions apps/web/examples/timeline/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { horizontal } from "./timeline.horizontal";
export { root } from "./timeline.root";
export { vertical } from "./timeline.vertical";
export { render } from "./timeline.render";
211 changes: 211 additions & 0 deletions apps/web/examples/timeline/timeline.render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
"use client";

import {
Avatar,
Badge,
Card,
Timeline,
TimelineBody,
TimelineContent,
TimelineItem,
TimelinePoint,
} from "flowbite-react";
import { type CodeData } from "~/components/code-demo";

const code = `
"use client";

import { Avatar, Badge, Timeline } from "flowbite-react";

export function Component() {
<Timeline>
<Timeline.Item>
<Timeline.Point
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-3.jpg" rounded size="xm" />
)}
/>
<Timeline.Content>
<Timeline.Body>
<Card>
<div className="-m-2 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">just now</time>

<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Bonnie moved{" "}
<a href="#" className="font-semibold text-blue-600 hover:underline dark:text-blue-500">
Jese Leos
</a>{" "}
to{" "}
<Badge color="gray" className="inline">
Funny Group
</Badge>
</div>
</div>
</Card>
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" rounded size="xm" />
)}
/>
<Timeline.Content>
<Timeline.Body>
<Card>
<div className="-m-2">
<div className="mb-3 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">2 hours ago</time>
<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Thomas Lean commented on{" "}
<a href="#" className="font-semibold text-gray-900 hover:underline dark:text-white">
Flowbite Pro
</a>
</div>
</div>

<Card className="border-gray-200 bg-gray-50 text-xs font-normal italic text-gray-500 dark:border-gray-500 dark:bg-gray-600 dark:text-gray-300">
<div className="-m-3">
Hi ya'll! I wanted to share a webinar zeroheight is having regarding how to best measure your design
system! This is the second session of our new webinar series on #DesignSystems discussions where
we'll be speaking about Measurement.
</div>
</Card>
</div>
</Card>
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-1.jpg" rounded size="xm" />
)}
/>
<Timeline.Content>
<Timeline.Body>
<Card>
<div className="-m-2 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">1 day ago</time>

<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Jese Leos has changed{" "}
<a href="#" className="font-semibold text-blue-600 hover:underline dark:text-blue-500">
Pricing page
</a>{" "}
task status to <span className="font-semibold text-gray-900 dark:text-white">Finished</span>
</div>
</div>
</Card>
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
</Timeline>
}
`;

export function Component() {
return (
<Timeline>
<TimelineItem>
<TimelinePoint
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-3.jpg" rounded size="xm" />
)}
/>
<TimelineContent>
<TimelineBody>
<Card>
<div className="-m-2 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">just now</time>

<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Bonnie moved{" "}
<a href="#" className="font-semibold text-blue-600 hover:underline dark:text-blue-500">
Jese Leos
</a>{" "}
to{" "}
<Badge color="gray" className="inline">
Funny Group
</Badge>
</div>
</div>
</Card>
</TimelineBody>
</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelinePoint
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" rounded size="xm" />
)}
/>
<TimelineContent>
<TimelineBody>
<Card>
<div className="-m-2">
<div className="mb-3 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">2 hours ago</time>
<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Thomas Lean commented on{" "}
<a href="#" className="font-semibold text-gray-900 hover:underline dark:text-white">
Flowbite Pro
</a>
</div>
</div>

<Card className="border-gray-200 bg-gray-50 text-xs font-normal italic text-gray-500 dark:border-gray-500 dark:bg-gray-600 dark:text-gray-300">
<div className="-m-3">
Hi ya'll! I wanted to share a webinar zeroheight is having regarding how to best measure your design
system! This is the second session of our new webinar series on #DesignSystems discussions where
we'll be speaking about Measurement.
</div>
</Card>
</div>
</Card>
</TimelineBody>
</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelinePoint
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-1.jpg" rounded size="xm" />
)}
/>
<TimelineContent>
<TimelineBody>
<Card>
<div className="-m-2 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">1 day ago</time>

<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Jese Leos has changed{" "}
<a href="#" className="font-semibold text-blue-600 hover:underline dark:text-blue-500">
Pricing page
</a>{" "}
task status to <span className="font-semibold text-gray-900 dark:text-white">Finished</span>
</div>
</div>
</Card>
</TimelineBody>
</TimelineContent>
</TimelineItem>
</Timeline>
);
}

export const render: CodeData = {
type: "single",
code: [
{
fileName: "client",
language: "tsx",
code,
},
],
githubSlug: "timeline/timeline.render.tsx",
component: <Component />,
iframe: 391,
};
32 changes: 32 additions & 0 deletions packages/ui/src/components/Timeline/Timeline.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ describe("Components / Timeline", () => {
expect(timelinePoint().childNodes[0]).toContainHTML("svg");
});

it("should render custom inner content when using render prop", () => {
render(<TestTimelineWithRenderProp horizontal={true} />);

expect(timelinePoint()).toBeInTheDocument();
expect(timelinePoint().childNodes[0]).toContainHTML("img");
});

it("should use `horizontal` classes of content if provided", () => {
render(
<Flowbite theme={{ theme }}>
Expand Down Expand Up @@ -56,6 +63,13 @@ describe("Components / Timeline", () => {
expect(timelinePoint().childNodes[0]).toContainHTML("svg");
});

it("should render custom inner content when using render prop", () => {
render(<TestTimelineWithRenderProp horizontal={false} />);

expect(timelinePoint()).toBeInTheDocument();
expect(timelinePoint().childNodes[0]).toContainHTML("img");
});

it("should use `vertical` classes of content if provided", () => {
render(
<Flowbite theme={{ theme }}>
Expand Down Expand Up @@ -135,6 +149,24 @@ const TestTimelineWithIcon: FC<TimelineProps> = ({ horizontal, className }): JSX
);
};

const TestTimelineWithRenderProp: FC<TimelineProps> = ({ horizontal, className }): JSX.Element => {
return (
<Timeline horizontal={horizontal} className={className}>
<Timeline.Item>
<Timeline.Point render={() => <img src="https://flowbite.com/docs/images/people/profile-picture-5.jpg" />} />
<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>
);
};

const IconSVG = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down
94 changes: 94 additions & 0 deletions packages/ui/src/components/Timeline/Timeline.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type { Meta, StoryFn } from "@storybook/react";
import { Avatar } from "../Avatar";
import { Badge } from "../Badge";
import { Card } from "../Card";
import type { TimelineProps } from "./Timeline";
import { Timeline } from "./Timeline";

Expand Down Expand Up @@ -48,3 +51,94 @@ Default.args = {
</>
),
};

export const ActivityLog = Template.bind({});
ActivityLog.args = {
children: (
<>
<Timeline.Item>
<Timeline.Point
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-3.jpg" rounded size="xm" />
)}
/>
<Timeline.Content>
<Timeline.Body>
<Card>
<div className="-m-2 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">just now</time>

<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Bonnie moved{" "}
<a href="#" className="font-semibold text-blue-600 hover:underline dark:text-blue-500">
Jese Leos
</a>{" "}
to{" "}
<Badge color="gray" className="inline">
Funny Group
</Badge>
</div>
</div>
</Card>
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" rounded size="xm" />
)}
/>
<Timeline.Content>
<Timeline.Body>
<Card>
<div className="-m-2">
<div className="mb-3 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">2 hours ago</time>
<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Thomas Lean commented on{" "}
<a href="#" className="font-semibold text-gray-900 hover:underline dark:text-white">
Flowbite Pro
</a>
</div>
</div>

<Card className="border-gray-200 bg-gray-50 text-xs font-normal italic text-gray-500 dark:border-gray-500 dark:bg-gray-600 dark:text-gray-300">
<div className="-m-3">
Hi ya'll! I wanted to share a webinar zeroheight is having regarding how to best measure your design
system! This is the second session of our new webinar series on #DesignSystems discussions where
we'll be speaking about Measurement.
</div>
</Card>
</div>
</Card>
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
<Timeline.Item>
<Timeline.Point
render={() => (
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-1.jpg" rounded size="xm" />
)}
/>
<Timeline.Content>
<Timeline.Body>
<Card>
<div className="-m-2 sm:flex sm:items-center sm:justify-between">
<time className="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">1 day ago</time>

<div className="text-sm font-normal text-gray-500 dark:text-gray-300">
Jese Leos has changed{" "}
<a href="#" className="font-semibold text-blue-600 hover:underline dark:text-blue-500">
Pricing page
</a>{" "}
task status to <span className="font-semibold text-gray-900 dark:text-white">Finished</span>
</div>
</div>
</Card>
</Timeline.Body>
</Timeline.Content>
</Timeline.Item>
</>
),
};
Loading
Loading