Skip to content
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

[PLAY-1586] Timeline Sub Sub Components #3801

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
82 changes: 59 additions & 23 deletions playbook/app/pb_kits/playbook/pb_timeline/_item.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from 'react'
import classnames from 'classnames'

import { buildCss, buildHtmlProps } from '../utilities/props'
import { globalProps, GlobalProps } from "../utilities/globalProps";
import { globalProps, GlobalProps } from "../utilities/globalProps"

import DateStacked from '../pb_date_stacked/_date_stacked'
import IconCircle from '../pb_icon_circle/_icon_circle'

import TimelineLabel from './subcomponents/Label'
import TimelineStep from './subcomponents/Step'
import TimelineDetail from './subcomponents/Detail'

type ItemProps = {
className?: string,
children?: React.ReactNode[] | React.ReactNode,
Expand All @@ -17,6 +20,13 @@ type ItemProps = {
lineStyle?: 'solid' | 'dotted',
} & GlobalProps

function isElementOfType<P>(
element: React.ReactNode,
component: React.ComponentType<P>
): element is React.ReactElement<P> {
return React.isValidElement<P>(element) && element.type === component
}

const TimelineItem = ({
className,
children,
Expand All @@ -31,31 +41,57 @@ const TimelineItem = ({

const htmlProps = buildHtmlProps(htmlOptions)

const childrenArray = React.Children.toArray(children)

const labelChild = childrenArray.find(
(child): child is React.ReactElement => isElementOfType(child, TimelineLabel)
)

const stepChild = childrenArray.find(
(child): child is React.ReactElement => isElementOfType(child, TimelineStep)
)

const detailChild = childrenArray.find(
(child): child is React.ReactElement => isElementOfType(child, TimelineDetail)
)

const otherChildren = childrenArray.filter(
(child) =>
!isElementOfType(child, TimelineLabel) &&
!isElementOfType(child, TimelineStep) &&
!isElementOfType(child, TimelineDetail)
)

return (
<div
<div
{...htmlProps}
className={classnames(timelineItemCss, globalProps(props), className)}
>
<div className="pb_timeline_item_left_block">
{date &&
<DateStacked
align="center"
date={date}
size="sm"
/>
}
</div>
<div className="pb_timeline_item_step">
<IconCircle
icon={icon}
size="xs"
variant={iconColor}
/>
<div className="pb_timeline_item_connector" />
</div>
<div className="pb_timeline_item_right_block">
{children}
</div>
{labelChild || (
<div className="pb_timeline_item_left_block">
{date && (
<DateStacked
align="center"
date={date}
size="sm"
/>
)}
</div>
)}
{stepChild || (
<div className="pb_timeline_item_step">
<IconCircle icon={icon}
size="xs"
variant={iconColor}
/>
<div className="pb_timeline_item_connector" />
</div>
)}
{detailChild || (
<div className="pb_timeline_item_right_block">
{ otherChildren }
</div>
)}
</div>
)
}
Expand Down
8 changes: 8 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/_timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../uti
import { GlobalProps, globalProps } from '../utilities/globalProps'

import TimelineItem from './_item'
import {
TimelineStep,
TimelineLabel,
TimelineDetail,
} from './subcomponents'

type TimelineProps = {
aria?: { [key: string]: string },
Expand Down Expand Up @@ -47,5 +52,8 @@ const Timeline = ({
}

Timeline.Item = TimelineItem
Timeline.Step = TimelineStep
Timeline.Label = TimelineLabel
Timeline.Detail = TimelineDetail

export default Timeline
12 changes: 12 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/date_area.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<%= pb_content_tag do %>
<% if object.date.present? %>
<%= pb_rails("date_stacked", props: {
date: object.date,
size: "sm",
align: "center"
}) %>
<% else %>
<%= content.presence %>
<% end %>
<% end %>

13 changes: 13 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/date_area.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Playbook
module PbTimeline
class DateArea < Playbook::KitBase
prop :date

def classname
generate_classname("pb_timeline_item_left_block")
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= pb_content_tag do %>
<%= content.presence %>
<% end %>
11 changes: 11 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/detail_area.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Playbook
module PbTimeline
class DetailArea < Playbook::KitBase
def classname
generate_classname("pb_timeline_item_right_block")
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<%= pb_rails("timeline", props: {orientation: "horizontal", show_date: true}) do %>
<%= pb_rails("timeline/item", props: { line_style: "solid"}) do |item| %>

<% item.date_area do %>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specifying the item.date_area allows us to only update the date area

you don't have to provide the node or detail area if you don't want to.

This is needed because our node area has default functionally and it will use the icon circle kit it you dont't put anything

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After doing more research i found that github's design system uses the same pattern https://primer.style/components/action-menu/rails/alpha#examples

<%= pb_rails("timeline/date_area") do %>
<%= pb_rails("title", props: { text: "Any Kit Here", size: 2 }) %>
<% end %>
<% end %>

<% item.node_area do %>
<%= pb_rails("timeline/node_area", props: { icon: 'check', icon_color: 'teal' }) %>
<% end %>

<% item.detail_area do %>
<%= pb_rails("title_detail", props: {
title: "Jackson Heights",
detail: "37-27 74th Street"
}) %>
<% end %>
<% end %>
<%= pb_rails("timeline/item", props: { line_style: "dotted"}) do |item| %>

<% item.node_area do %>
<%= pb_rails("timeline/node_area") do %>
<%= pb_rails("pill", props: { text: "Any Kit" , variant: "success" }) %>
<% end %>
<% end %>

<% item.detail_area do %>
<%= pb_rails("title_detail", props: {
title: "Greenpoint",
detail: "81 Gate St Brooklyn"
}) %>
<% end %>
<% end %>

<%= pb_rails("timeline/item", props: {icon: "map-marker-alt", icon_color: "purple", date: Date.today+1 }) do |item| %>
<%= pb_rails("title_detail", props: {
title: "Society Hill",
detail: "72 E St Astoria"
}) %>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react'

import Timeline from '../_timeline'
import Title from '../../pb_title/_title'
import Pill from '../../pb_pill/_pill'

import TitleDetail from '../../pb_title_detail/_title_detail'

const TimelineWithChildren = (props) => (
<div>
<Timeline orientation="horizontal"
showDate
{...props}
>
<Timeline.Item lineStyle="solid"
{...props}
>
<Timeline.Label>
<Title size={2}
text='Any Kit Here'
/>
</Timeline.Label>
<Timeline.Step icon="user"
iconColor="royal"
/>
<Timeline.Detail>
<TitleDetail detail="37-27 74th Street"
title="Jackson Heights"
{...props}
/>
</Timeline.Detail>
</Timeline.Item>

<Timeline.Item lineStyle="dotted"
{...props}
>
<Timeline.Step>
<Pill text="Any Kit"
variant="success"
/>
</Timeline.Step>
<Timeline.Detail>
<TitleDetail detail="81 Gate St Brooklyn"
title="Greenpoint"
{...props}
/>
</Timeline.Detail>
</Timeline.Item>

<Timeline.Item lineStyle="solid"
{...props}
>
<Timeline.Label date={new Date(new Date().setDate(new Date().getDate() + 1))} />
<Timeline.Step icon="map-marker-alt"
iconColor="purple"
/>
<Timeline.Detail>
<TitleDetail detail="72 E St Astoria"
title="Society Hill"
{...props}
/>
</Timeline.Detail>
</Timeline.Item>
</Timeline>
</div>
)

export default TimelineWithChildren
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
You can use whatever kit you want for the date area of the timeline item, the node area, and the content area at the bottom or left.

Checkout the code in this example to see the children kits in action.

Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ examples:
- timeline_default: Default
- timeline_vertical: Vertical
- timeline_with_date: With Date
- timeline_with_children: With Children


react:
- timeline_default: Default
- timeline_vertical: Vertical
- timeline_with_date: With Date

- timeline_with_children: With Children
1 change: 1 addition & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/docs/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as TimelineDefault } from './_timeline_default.jsx'
export { default as TimelineVertical } from './_timeline_vertical.jsx'
export { default as TimelineWithDate } from './_timeline_with_date.jsx'
export { default as TimelineWithChildren } from './_timeline_with_children.jsx'
38 changes: 17 additions & 21 deletions playbook/app/pb_kits/playbook/pb_timeline/item.html.erb
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
<%= pb_content_tag do %>
<% if date_area %>
<%= date_area %>
<% else %>
<%= pb_rails("timeline/date_area", props: { date: date }) %>
<% end %>

<div class="pb_timeline_item_left_block">
<% if object.date.present? %>
<%= pb_rails("date_stacked", props: {
date: object.date,
size: "sm",
align: "center"
}) %>
<% end %>
</div>

<div class="pb_timeline_item_step">
<%= pb_rails("icon_circle", props: {
icon: object.icon,
variant: object.icon_color,
size: "xs"
}) %>
<div class="pb_timeline_item_connector"></div>
</div>
<% if node_area %>
<%= node_area %>
<% else %>
<%= pb_rails("timeline/node_area", props: { icon: icon, icon_color: icon_color }) %>
<% end %>

<div class="pb_timeline_item_right_block">
<%= content.presence %>
</div>
<% if detail_area %>
<%= detail_area %>
<% else %>
<%= pb_rails("timeline/detail_area") do %>
<%= content %>
<% end %>
<% end %>
<% end %>
4 changes: 4 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class Item < Playbook::KitBase
values: %w[solid dotted],
default: "solid"

renders_one :date_area
renders_one :node_area
renders_one :detail_area

def classname
generate_classname("pb_timeline_item_kit", line_style)
end
Expand Down
14 changes: 14 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/node_area.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<%= pb_content_tag do %>
<% if object.icon.present? %>
<%= pb_rails("icon_circle", props: {
icon: object.icon,
variant: object.icon_color,
size: "xs"
}) %>
<% else %>
<%= content.presence %>
<% end %>
<div class="pb_timeline_item_connector"></div>
<% end %>


16 changes: 16 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/node_area.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Playbook
module PbTimeline
class NodeArea < Playbook::KitBase
prop :icon, type: Playbook::Props::String
prop :icon_color, type: Playbook::Props::Enum,
values: %w[default royal blue purple teal red yellow green],
default: "default"

def classname
generate_classname("pb_timeline_item_step")
end
end
end
end
Loading
Loading