From 5f2d0e20f151e572be7ee17923dcf454a928acbe Mon Sep 17 00:00:00 2001 From: Wilker Date: Tue, 12 Sep 2023 12:48:49 +0800 Subject: [PATCH 1/5] [BOOKINGSG-4566][WK] enhance variants --- src/timeline/timeline.style.tsx | 91 ++++++++++++++++++++------- src/timeline/timeline.tsx | 17 +++-- src/timeline/types.ts | 7 +++ stories/timeline/props-table.tsx | 15 +++++ stories/timeline/timeline.stories.mdx | 64 +++++++++++++++++++ 5 files changed, 166 insertions(+), 28 deletions(-) diff --git a/src/timeline/timeline.style.tsx b/src/timeline/timeline.style.tsx index 2c0e7c533..9f544d033 100644 --- a/src/timeline/timeline.style.tsx +++ b/src/timeline/timeline.style.tsx @@ -1,60 +1,99 @@ import styled, { css } from "styled-components"; +import { TickCircleFillIcon } from "@lifesg/react-icons"; import { Color } from "../color"; import { MediaQuery } from "../media"; import { Text } from "../text"; -import { TimelineStatusProps } from "./types"; +import { TimelineStatusProps, Variant } from "./types"; // ============================================================================= // STYLE INTERFACES, transient props are denoted with $ // See more https://styled-components.com/docs/api#transient-props // ============================================================================= -interface CircleIndicatorStyleProps { - $filled?: boolean | undefined; -} - interface TimelineWrapperStyleProps { $startCol?: number | undefined; $colSpan?: number | undefined; } +interface VariantStyleProps { + $variant: Variant; +} + // ============================================================================= // STYLE INTERFACES // ============================================================================= - -export const CircleIndicator = styled.div` - width: 1rem; - height: 1rem; - margin-top: 0.5rem; +export const CircleIndicator = styled.div` + overflow: hidden; + width: 1.5rem; + height: 1.5rem; + margin-top: 4px; border-radius: 50%; + ${(props) => { - if (props.$filled) { - return css` - border: none; - background-color: ${Color.Accent.Light[1]}; - `; + switch (props.$variant) { + case "current": + return css` + background-color: ${Color.Accent.Light[1]}; + `; + case "upcoming-active": + return css` + border: 4px solid ${Color.Accent.Light[1]}; + `; + case "upcoming-inactive": + return css` + border: 4px solid ${Color.Neutral[4]}; + `; + case "completed": + return css` + svg { + display: block; + } + `; } - - return css` - border: 3.2px solid ${Color.Accent.Light[1]}; - background-color: transparent; - `; }} `; -export const LineIndicator = styled.div` - width: 0.25rem; +export const TickCircleIcon = styled(TickCircleFillIcon)` + display: none; + width: 100%; + height: 100%; + transform: scale(1.2); + + path { + fill: ${Color.Validation.Green.Icon}; + } +`; + +export const LineIndicator = styled.div` + width: 4px; flex-grow: 1; margin-top: 0.5rem; border-radius: 2px; - background-color: ${Color.Accent.Light[1]}; + + ${(props) => { + switch (props.$variant) { + case "current": + case "upcoming-active": + return css` + background-color: ${Color.Accent.Light[1]}; + `; + case "upcoming-inactive": + return css` + background-color: ${Color.Neutral[4]}; + `; + case "completed": + return css` + background-color: ${Color.Validation.Green.Icon}; + `; + } + }} `; export const TimelineIndicators = styled.div` display: flex; flex-direction: column; align-items: center; - margin-right: 2rem; + margin-right: 1rem; `; export const TimelineWrapper = styled.div` @@ -81,6 +120,10 @@ export const TimelineTitle = styled(Text.H3)` // default is 2-8-2 on desktop export const TimelineItem = styled.div` display: flex; + + :nth-child(2) ${CircleIndicator} { + margin-top: 0; + } `; export const TimelineItemContent = styled.div` diff --git a/src/timeline/timeline.tsx b/src/timeline/timeline.tsx index 203ab7c5a..963a999cc 100644 --- a/src/timeline/timeline.tsx +++ b/src/timeline/timeline.tsx @@ -2,6 +2,7 @@ import { Text } from "../text"; import { CircleIndicator, LineIndicator, + TickCircleIcon, TimelineIndicators, TimelineItem, TimelineItemContent, @@ -36,6 +37,7 @@ export const Timeline = ({ return <>{content}; }; + const renderTitle = (title: string | JSX.Element): JSX.Element => { if (typeof title === "string") { return ( @@ -50,6 +52,7 @@ export const Timeline = ({ return <>{title}; }; + const renderStatusPills = ( statuses: TimelineStatusProps[] ): JSX.Element[] => { @@ -68,21 +71,27 @@ export const Timeline = ({ ); }); }; + const renderItems = () => items.map((item: TimelineItemProps, index) => { - const { title, content, statuses } = item; + const { title, content, statuses, variant: _variant } = item; const circleIndicatorTestId = baseIndicatorTestId ? `circleindicator${index + 1}_div_${baseIndicatorTestId}` : "circleindicator"; + const variant = + (!_variant && index === 0 ? "current" : _variant) || + "upcoming-active"; return ( - + $variant={variant} + > + + + {renderTitle(title)} diff --git a/src/timeline/types.ts b/src/timeline/types.ts index d98f17c07..acf067b0a 100644 --- a/src/timeline/types.ts +++ b/src/timeline/types.ts @@ -1,3 +1,9 @@ +export type Variant = + | "completed" + | "current" + | "upcoming-active" + | "upcoming-inactive"; + export interface TimelineStatusProps { type: "dark" | "light"; label: string; @@ -7,6 +13,7 @@ export interface TimelineItemProps { title: string | JSX.Element; content: string | JSX.Element; statuses?: TimelineStatusProps[] | undefined; + variant?: Variant | undefined; } export interface TimelineProps { diff --git a/stories/timeline/props-table.tsx b/stories/timeline/props-table.tsx index c8a1b8077..8be32455d 100644 --- a/stories/timeline/props-table.tsx +++ b/stories/timeline/props-table.tsx @@ -79,6 +79,21 @@ const DATA: ApiTableSectionProps[] = [ ), propTypes: ["TimelineStatusProps[]"], }, + { + name: "variant", + description: ( + <> + The variant includes 4 different types, each + representing a unique styling + + ), + propTypes: [ + `"completed"`, + `"current"`, + `"upcoming-active`, + `"upcoming-inactive"`, + ], + }, ], }, { diff --git a/stories/timeline/timeline.stories.mdx b/stories/timeline/timeline.stories.mdx index 254644399..dd285b442 100644 --- a/stories/timeline/timeline.stories.mdx +++ b/stories/timeline/timeline.stories.mdx @@ -108,6 +108,70 @@ There are a variety of contents available to be specified for each `Timeline` it +Variations + +The `Timeline` component comes in 4 variants, a completed, current, upcoming-active and upcoming-inactive. + + + + + + An example with completed variant + + ), + }, + { + title: "Item 2", + variant: "current", + content: ( + + An example with current variant + + ), + }, + { + title: "Item 3", + variant: "upcoming-active", + content: ( + + An example with upcoming-active{" "} + variant + + ), + }, + { + title: "Item 4", + variant: "upcoming-inactive", + content: ( + + An example with upcoming-inactive{" "} + variant + + ), + }, + { + title: "Item 5", + variant: "upcoming-inactive", + content: ( + + An example with upcoming-inactive{" "} + variant + + ), + }, + ]} + /> + + + + Component API From 49616dfe2917a6062d72e7ed8dc69ae70cf069fe Mon Sep 17 00:00:00 2001 From: Wilker Date: Tue, 12 Sep 2023 15:56:04 +0800 Subject: [PATCH 2/5] [BOOKINGSG-4566][WK] style first-of-type to select first item --- src/timeline/timeline.style.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeline/timeline.style.tsx b/src/timeline/timeline.style.tsx index 9f544d033..d89d18348 100644 --- a/src/timeline/timeline.style.tsx +++ b/src/timeline/timeline.style.tsx @@ -121,7 +121,7 @@ export const TimelineTitle = styled(Text.H3)` export const TimelineItem = styled.div` display: flex; - :nth-child(2) ${CircleIndicator} { + :first-of-type ${CircleIndicator} { margin-top: 0; } `; From d955f946a26b9eaab3ca48dfe36dc26b3ec3fd88 Mon Sep 17 00:00:00 2001 From: Wilker Date: Thu, 14 Sep 2023 13:53:12 +0800 Subject: [PATCH 3/5] [BOOKINGSG-4566][WK] pr comments --- src/timeline/timeline.style.tsx | 17 +++++++---------- src/timeline/timeline.tsx | 7 +++---- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/timeline/timeline.style.tsx b/src/timeline/timeline.style.tsx index d89d18348..e48b2e552 100644 --- a/src/timeline/timeline.style.tsx +++ b/src/timeline/timeline.style.tsx @@ -1,5 +1,5 @@ import styled, { css } from "styled-components"; -import { TickCircleFillIcon } from "@lifesg/react-icons"; +import { TickIcon } from "@lifesg/react-icons"; import { Color } from "../color"; import { MediaQuery } from "../media"; import { Text } from "../text"; @@ -23,7 +23,9 @@ interface VariantStyleProps { // STYLE INTERFACES // ============================================================================= export const CircleIndicator = styled.div` - overflow: hidden; + display: flex; + justify-content: center; + align-items: center; width: 1.5rem; height: 1.5rem; margin-top: 4px; @@ -45,6 +47,7 @@ export const CircleIndicator = styled.div` `; case "completed": return css` + background-color: ${Color.Validation.Green.Icon}; svg { display: block; } @@ -53,15 +56,9 @@ export const CircleIndicator = styled.div` }} `; -export const TickCircleIcon = styled(TickCircleFillIcon)` +export const IconTick = styled(TickIcon)` + color: ${Color.Neutral[8]}; display: none; - width: 100%; - height: 100%; - transform: scale(1.2); - - path { - fill: ${Color.Validation.Green.Icon}; - } `; export const LineIndicator = styled.div` diff --git a/src/timeline/timeline.tsx b/src/timeline/timeline.tsx index 963a999cc..da7989c79 100644 --- a/src/timeline/timeline.tsx +++ b/src/timeline/timeline.tsx @@ -1,8 +1,8 @@ import { Text } from "../text"; import { CircleIndicator, + IconTick, LineIndicator, - TickCircleIcon, TimelineIndicators, TimelineItem, TimelineItemContent, @@ -79,8 +79,7 @@ export const Timeline = ({ ? `circleindicator${index + 1}_div_${baseIndicatorTestId}` : "circleindicator"; const variant = - (!_variant && index === 0 ? "current" : _variant) || - "upcoming-active"; + _variant || (index === 0 ? "current" : "upcoming-active"); return ( @@ -89,7 +88,7 @@ export const Timeline = ({ data-testid={circleIndicatorTestId} $variant={variant} > - + From 08fad681c554da6fa7f466012babc941858492d1 Mon Sep 17 00:00:00 2001 From: Wilker Date: Fri, 15 Sep 2023 23:04:03 +0800 Subject: [PATCH 4/5] [BOOKINGSG-4566][WK] update stories and styles --- src/timeline/timeline.style.tsx | 6 +----- src/timeline/timeline.tsx | 18 ++++++++++++++++-- stories/timeline/props-table.tsx | 7 +++++-- stories/timeline/timeline.stories.mdx | 2 +- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/timeline/timeline.style.tsx b/src/timeline/timeline.style.tsx index e48b2e552..108fc1fc0 100644 --- a/src/timeline/timeline.style.tsx +++ b/src/timeline/timeline.style.tsx @@ -28,7 +28,7 @@ export const CircleIndicator = styled.div` align-items: center; width: 1.5rem; height: 1.5rem; - margin-top: 4px; + margin-top: 0.25rem; border-radius: 50%; ${(props) => { @@ -48,9 +48,6 @@ export const CircleIndicator = styled.div` case "completed": return css` background-color: ${Color.Validation.Green.Icon}; - svg { - display: block; - } `; } }} @@ -58,7 +55,6 @@ export const CircleIndicator = styled.div` export const IconTick = styled(TickIcon)` color: ${Color.Neutral[8]}; - display: none; `; export const LineIndicator = styled.div` diff --git a/src/timeline/timeline.tsx b/src/timeline/timeline.tsx index da7989c79..28b3fe4db 100644 --- a/src/timeline/timeline.tsx +++ b/src/timeline/timeline.tsx @@ -12,7 +12,12 @@ import { TimelineTitle, TimelineWrapper, } from "./timeline.style"; -import { TimelineItemProps, TimelineProps, TimelineStatusProps } from "./types"; +import { + TimelineItemProps, + TimelineProps, + TimelineStatusProps, + Variant, +} from "./types"; export const Timeline = ({ items, @@ -72,6 +77,15 @@ export const Timeline = ({ }); }; + const renderIcon = (variant: Variant) => { + switch (variant) { + case "completed": + return ; + default: + return null; + } + }; + const renderItems = () => items.map((item: TimelineItemProps, index) => { const { title, content, statuses, variant: _variant } = item; @@ -88,7 +102,7 @@ export const Timeline = ({ data-testid={circleIndicatorTestId} $variant={variant} > - + {renderIcon(variant)} diff --git a/stories/timeline/props-table.tsx b/stories/timeline/props-table.tsx index 8be32455d..9bdaddfe9 100644 --- a/stories/timeline/props-table.tsx +++ b/stories/timeline/props-table.tsx @@ -83,8 +83,11 @@ const DATA: ApiTableSectionProps[] = [ name: "variant", description: ( <> - The variant includes 4 different types, each - representing a unique styling + The style variant of the item indicator +
+ Note: the first item defaults to{" "} + current, and subsequent items default to{" "} + upcoming-active ), propTypes: [ diff --git a/stories/timeline/timeline.stories.mdx b/stories/timeline/timeline.stories.mdx index dd285b442..6d7f1fd42 100644 --- a/stories/timeline/timeline.stories.mdx +++ b/stories/timeline/timeline.stories.mdx @@ -110,7 +110,7 @@ There are a variety of contents available to be specified for each `Timeline` it Variations -The `Timeline` component comes in 4 variants, a completed, current, upcoming-active and upcoming-inactive. +The `Timeline` component comes in 4 variants and each item can be styled individually. From 67ec8df719e4ab531631257fb77c6dd7d2e25909 Mon Sep 17 00:00:00 2001 From: Wilker Date: Sat, 16 Sep 2023 20:55:59 +0800 Subject: [PATCH 5/5] [BOOKINGSG-4566][WK] styling icon from parent element --- src/timeline/timeline.style.tsx | 8 +++----- src/timeline/timeline.tsx | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/timeline/timeline.style.tsx b/src/timeline/timeline.style.tsx index 108fc1fc0..4fce5524c 100644 --- a/src/timeline/timeline.style.tsx +++ b/src/timeline/timeline.style.tsx @@ -1,5 +1,4 @@ import styled, { css } from "styled-components"; -import { TickIcon } from "@lifesg/react-icons"; import { Color } from "../color"; import { MediaQuery } from "../media"; import { Text } from "../text"; @@ -48,15 +47,14 @@ export const CircleIndicator = styled.div` case "completed": return css` background-color: ${Color.Validation.Green.Icon}; + svg { + color: ${Color.Neutral[8]}; + } `; } }} `; -export const IconTick = styled(TickIcon)` - color: ${Color.Neutral[8]}; -`; - export const LineIndicator = styled.div` width: 4px; flex-grow: 1; diff --git a/src/timeline/timeline.tsx b/src/timeline/timeline.tsx index 28b3fe4db..09533f3ef 100644 --- a/src/timeline/timeline.tsx +++ b/src/timeline/timeline.tsx @@ -1,7 +1,7 @@ +import { TickIcon } from "@lifesg/react-icons"; import { Text } from "../text"; import { CircleIndicator, - IconTick, LineIndicator, TimelineIndicators, TimelineItem, @@ -80,7 +80,7 @@ export const Timeline = ({ const renderIcon = (variant: Variant) => { switch (variant) { case "completed": - return ; + return ; default: return null; }