Skip to content

Commit

Permalink
[PLAY-1586] Timeline Sub Sub Components (#3801)
Browse files Browse the repository at this point in the history
**What does this PR do?** A clear and concise description with your
runway ticket url.
Runway https://runway.powerhrg.com/backlog_items/PLAY-1586

In this story we let devs add children or "what ever kit they want" to
the date and node area of the timeline kit

The major changes to react is in the
`playbook/app/pb_kits/playbook/pb_timeline/_item.tsx` and in rails i
added a new pattern to be able to conditionally render fragments of
content

I have an alpha here powerhome/nitro-web#43266

**Screenshots:** Screenshots to visualize your addition/change


![screenshot-127_0_0_1_3000-2024_10_17-09_57_21](https://github.com/user-attachments/assets/b07c56ec-9c52-4b1b-8d36-7971e76d3de8)

**How to test?** Steps to confirm the desired behavior:
1. Go to
https://pr3801.playbook.beta.px.powerapp.cloud/kits/timeline/react#with-children
2. Witness greatness 

#### Checklist:
- [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new
kit`, `deprecated`, or `breaking`. See [Changelog &
Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels)
for details.
- [x] **DEPLOY** I have added the `milano` label to show I'm ready for a
review.
- [x] **TESTS** I have added test coverage to my code.

---------

Co-authored-by: Jasper Furniss <[email protected]>
  • Loading branch information
markdoeswork and jasperfurniss authored Nov 5, 2024
1 parent 50fc9f6 commit c5f4170
Show file tree
Hide file tree
Showing 23 changed files with 518 additions and 45 deletions.
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
3 changes: 3 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/detail.html.erb
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.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Playbook
module PbTimeline
class Detail < 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.label do %>
<%= pb_rails("timeline/label") do %>
<%= pb_rails("title", props: { text: "Any Kit Here", size: 2 }) %>
<% end %>
<% end %>

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

<% item.detail 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.step do %>
<%= pb_rails("timeline/step") do %>
<%= pb_rails("pill", props: { text: "Any Kit" , variant: "success" }) %>
<% end %>
<% end %>

<% item.detail 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,2 @@
Any kit can be used inside of our compound components of label, step, or detail. Expand the code snippet below to see how to use these children elements.

3 changes: 2 additions & 1 deletion playbook/app/pb_kits/playbook/pb_timeline/docs/example.yml
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 label %>
<%= label %>
<% else %>
<%= pb_rails("timeline/label", 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 step %>
<%= step %>
<% else %>
<%= pb_rails("timeline/step", props: { icon: icon, icon_color: icon_color }) %>
<% end %>

<div class="pb_timeline_item_right_block">
<%= content.presence %>
</div>
<% if detail%>
<%= detail%>
<% else %>
<%= pb_rails("timeline/detail") 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 :label
renders_one :step
renders_one :detail

def classname
generate_classname("pb_timeline_item_kit", line_style)
end
Expand Down
12 changes: 12 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/label.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/label.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

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

def classname
generate_classname("pb_timeline_item_left_block")
end
end
end
end
14 changes: 14 additions & 0 deletions playbook/app/pb_kits/playbook/pb_timeline/step.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/step.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Playbook
module PbTimeline
class Step < 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

0 comments on commit c5f4170

Please sign in to comment.