Skip to content

Commit 4310e66

Browse files
authored
feat: replace date-fns formatting with formatDate callback (#101)
* feat: introduce formatDate callback * docs: add date formatting example * chore: remove date-fns dependency * chore: fix typing of format callback * doc: update readme about date formatting
1 parent c79b34a commit 4310e66

File tree

6 files changed

+34
-60
lines changed

6 files changed

+34
-60
lines changed

README.md

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A compact, masonry style alternating timeline react component which is fully cus
88

99
- 🎛️ Customize everything.
1010
- 🎨 Consistent ([BEM](https://getbem.com)) class naming for easy styling with CSS, emotion...
11-
-Date formatting using [date-fns](date-fns.org) standard.
11+
-Custom date formatting.
1212
- ⚖️ Alternating, left or right positioning.
1313
- 🖼️ Render images and custom content.
1414
- 🪄 Built with Typescript.
@@ -49,32 +49,30 @@ const items: TimelineItemsProps = [
4949

5050
The available properties of the `Timeline` component:
5151

52-
| Property | Type | Description | Default |
53-
| :--------------- | :------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | :-------- | ----------------------------------------------------------- | --------------- |
54-
| `items` | [`TimelineItemsProps`](#timelineitemsprops) | Array of timeline items | |
55-
| `positioning?` | `'alternating' | 'left' | 'right'` | How the items should be positioned relative to the timeline | `'alternating'` |
56-
| `minMarkerGap?` | `number` | The minimum gap markers will have between each other | 50 (`px`) |
57-
| `dateLocal?` | `Local` | Date locale | |
58-
| `dateFormat?` | `string` | Specific date format according to date-fns [specification](https://date-fns.org/v2.29.3/docs/format). Ignored when passing a `string` as date | `'P'` |
59-
| `customMarker?` | `ReactElement` | Custom maker element replacing the default | |
60-
| `customPointer?` | `ReactElement` | Custom pointer element replacing the default | |
61-
| `styleConfig?` | [`StyleConfig`](#styleconfig) | Style config object for customizing timeline by setting css custom properties | |
62-
| `className?` | `string` | Additional class name | |
52+
| Property | Type | Description | Default |
53+
| :--------------- | :------------------------------------------ | :---------------------------------------------------------------------------- | :-------------- |
54+
| `items` | [`TimelineItemsProps`](#timelineitemsprops) | Array of timeline items | |
55+
| `positioning?` | `'alternating' \| 'left' \| 'right'` | How the items should be positioned relative to the timeline | `'alternating'` |
56+
| `minMarkerGap?` | `number` | The minimum gap markers will have between each other | 50 (`px`) |
57+
| `formatDate?` | `(date: Date) => string` | Callback to format date | |
58+
| `customMarker?` | `ReactElement` | Custom maker element replacing the default | |
59+
| `customPointer?` | `ReactElement` | Custom pointer element replacing the default | |
60+
| `styleConfig?` | [`StyleConfig`](#styleconfig) | Style config object for customizing timeline by setting css custom properties | |
61+
| `className?` | `string` | Additional class name | |
6362

6463
### TimelineItemsProps
6564

6665
An array of the following properties:
6766

68-
| Property | Type | Description |
69-
| :--------------- | :--------------- | :------------------------------------------------------------------------------- |
70-
| `key` | `Key` | Unique key for each item |
71-
| `title?` | `string` | Optional title paragraph displayed bold |
72-
| `date` | `Date \| string` | Date either being formatted according to provided format or passed as a `string` |
73-
| `children?` | `ReactNode` | Pass custom content as `children` to the component |
74-
| `dateFormat?` | `string` | Overwriting `dateFormat` property of parent `Timeline` |
75-
| `dateLocale?` | `string` | Overwriting `dateLocale` property of parent `Timeline` |
76-
| `customMarker?` | `ReactElement` | Overwriting `customMarker` property of parent `Timeline` |
77-
| `customPointer?` | `ReactElement` | Overwriting `customPointer` property of parent `Timeline` |
67+
| Property | Type | Description |
68+
| :--------------- | :----------------------- | :------------------------------------------------------------------------------- |
69+
| `key` | `Key` | Unique key for each item |
70+
| `title?` | `string` | Optional title paragraph displayed bold |
71+
| `date` | `Date \| string` | Date either being formatted according to provided format or passed as a `string` |
72+
| `children?` | `ReactNode` | Pass custom content as `children` to the component |
73+
| `formatDate?` | `(date: Date) => string` | Callback to format date of specific item |
74+
| `customMarker?` | `ReactElement` | Overwriting `customMarker` property of parent `Timeline` |
75+
| `customPointer?` | `ReactElement` | Overwriting `customPointer` property of parent `Timeline` |
7876

7977
### StyleConfig
8078

package-lock.json

Lines changed: 0 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@
7272
"@vitejs/plugin-react": "^3.0.0",
7373
"@vitest/coverage-istanbul": "^0.32.0",
7474
"babel-loader": "^9.1.0",
75-
"date-fns": "^2.29.3",
7675
"eslint": "^8.34.0",
7776
"eslint-config-airbnb": "19.0.4",
7877
"eslint-config-airbnb-typescript": "^17.0.0",

src/components/Timeline.stories.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,11 @@ export const CustomMarkerAndPointer: StoryObj<typeof Timeline> = {
265265
customPointer: <div className="pointy" />,
266266
},
267267
};
268+
269+
export const DateFormatting: StoryObj<typeof Timeline> = {
270+
args: {
271+
items,
272+
...defaultTimelineConfig,
273+
formatDate: (date) => date.toDateString(),
274+
},
275+
};

src/components/Timeline.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ export type TimelineProps = {
88
items: TimelineItemsProps;
99
positioning?: Positioning;
1010
minMarkerGap?: number;
11-
dateFormat?: string;
12-
dateLocale?: Locale;
11+
formatDate?: (date: Date) => string;
1312
customMarker?: ReactElement;
1413
customPointer?: ReactElement;
1514
styleConfig?: StyleConfig;
@@ -19,11 +18,10 @@ export type TimelineProps = {
1918
export const defaultTimelineConfig: Partial<TimelineProps> = {
2019
positioning: 'alternating',
2120
minMarkerGap: 50,
22-
dateFormat: 'P',
2321
};
2422

2523
export function Timeline(props: TimelineProps) {
26-
const { items, positioning, minMarkerGap, className, dateFormat, dateLocale, customMarker, customPointer, styleConfig } = {
24+
const { items, positioning, minMarkerGap, className, customMarker, customPointer, styleConfig, formatDate } = {
2725
...defaultTimelineConfig,
2826
...props,
2927
};
@@ -107,10 +105,9 @@ export function Timeline(props: TimelineProps) {
107105
{/* First all items are rendered in the left column. They will be assigned to the corresponding column later */}
108106
{items.map((item) => (
109107
<TimelineItem
110-
dateFormat={dateFormat}
111-
dateLocale={dateLocale}
112108
customMarker={customMarker}
113109
customPointer={customPointer}
110+
formatDate={formatDate}
114111
{...item}
115112
ref={(node) => {
116113
const map = getRefMap();

src/components/TimelineItem.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { forwardRef, Key, PropsWithChildren, ReactElement, useRef, useImperativeHandle } from 'react';
2-
import { format } from 'date-fns';
32

43
type PropsWithKey<T> = T & {
54
key: Key;
@@ -13,14 +12,13 @@ export type TimelineItemProps = PropsWithChildren<{
1312
className?: string;
1413
title?: string;
1514
date: Date | string;
16-
dateFormat?: string;
17-
dateLocale?: Locale;
15+
formatDate?: (date: Date) => string;
1816
customMarker?: ReactElement;
1917
customPointer?: ReactElement;
2018
}>;
2119

2220
export const TimelineItem = forwardRef<TimelineItemRefs, TimelineItemProps>(
23-
({ className, title, date, children, dateFormat, dateLocale, customMarker, customPointer }, ref) => {
21+
({ className, title, date, formatDate, children, customMarker, customPointer }, ref) => {
2422
const itemRef = useRef<HTMLDivElement>(null);
2523
const pointerRef = useRef<HTMLDivElement>(null);
2624
const markerRef = useRef<HTMLDivElement>(null);
@@ -40,7 +38,7 @@ export const TimelineItem = forwardRef<TimelineItemRefs, TimelineItemProps>(
4038
<div ref={pointerRef} className={['timeline-card__pointer', customPointer && 'timeline-card__pointer--custom'].join(' ')}>
4139
{customPointer}
4240
</div>
43-
<p className="timeline-card__date">{date instanceof Date ? format(date, dateFormat ?? 'P', { locale: dateLocale }) : date}</p>
41+
<p className="timeline-card__date">{date instanceof Date ? formatDate?.(date) ?? date.toLocaleDateString() : date}</p>
4442
{title && <p className="timeline-card__title">{title}</p>}
4543
{children}
4644
</div>

0 commit comments

Comments
 (0)