From d09f748892a9220f20993b928f0480aa0a5fe13c Mon Sep 17 00:00:00 2001 From: Ghazwan Date: Fri, 17 Apr 2026 16:27:28 +0700 Subject: [PATCH 1/7] [BOOKINGSG-9219][GZ] rename style filename, move static fn outside of component --- .../{badge.style.tsx => badge.styles.ts} | 0 src/badge/badge.tsx | 20 +++++++++---------- src/badge/types.ts | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) rename src/badge/{badge.style.tsx => badge.styles.ts} (100%) diff --git a/src/badge/badge.style.tsx b/src/badge/badge.styles.ts similarity index 100% rename from src/badge/badge.style.tsx rename to src/badge/badge.styles.ts diff --git a/src/badge/badge.tsx b/src/badge/badge.tsx index 9867c9a4a..3f3c93dfb 100644 --- a/src/badge/badge.tsx +++ b/src/badge/badge.tsx @@ -1,6 +1,15 @@ -import { BadgeOverlay, BadgeWrapper, StyledBadge } from "./badge.style"; +import { BadgeOverlay, BadgeWrapper, StyledBadge } from "./badge.styles"; import type { BadgeProps, BadgeVariant } from "./types"; +// ============================================================================= +// HELPER FUNCTIONS +// ============================================================================= +function getDisplayCount(count: number) { + if (count <= 999) return count.toString(); + if (count === 1000) return "1K"; + return "1K+"; +} + export const Badge = ({ children, count = 0, @@ -21,15 +30,6 @@ export const Badge = ({ ]; const shouldShowCount = variantsToShowCount.includes(variant); - // ============================================================================= - // HELPER FUNCTIONS - // ============================================================================= - function getDisplayCount(count: number) { - if (count <= 999) return count.toString(); - if (count === 1000) return "1K"; - return "1K+"; - } - // ============================================================================= // RENDER FUNCTIONS // ============================================================================= diff --git a/src/badge/types.ts b/src/badge/types.ts index 5975eb2bf..35278dfd5 100644 --- a/src/badge/types.ts +++ b/src/badge/types.ts @@ -10,8 +10,8 @@ export type BadgeColor = "default" | "important"; export interface BadgeProps extends React.HTMLAttributes { badgeOffset?: [string, string] | undefined; children?: JSX.Element | undefined; - count?: number | undefined; - variant?: BadgeVariant | undefined; color?: BadgeColor | undefined; + count?: number | undefined; "data-testid"?: string | undefined; + variant?: BadgeVariant | undefined; } From 07a7c67d997a214b940bd08cd92fe4ee52361e2f Mon Sep 17 00:00:00 2001 From: Ghazwan Date: Fri, 17 Apr 2026 16:29:12 +0700 Subject: [PATCH 2/7] [BOOKINGSG-9219][GZ] migrate tokens --- src/badge/badge.styles.ts | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/badge/badge.styles.ts b/src/badge/badge.styles.ts index 6f8524e43..db538c670 100644 --- a/src/badge/badge.styles.ts +++ b/src/badge/badge.styles.ts @@ -1,6 +1,6 @@ import styled, { css } from "styled-components"; -import { V3_Border, V3_Colour, V3_Font, V3_Radius } from "../v3_theme"; +import { Border, Colour, Font, Radius } from "../theme"; import type { BadgeProps } from "./types"; // ============================================================================= @@ -48,8 +48,7 @@ export const BadgeWrapper = styled.div` const numberBadgeStyles = css` min-width: 1.25rem; padding: 0.25rem 0.375rem; - font-size: ${V3_Font.Spec["body-size-xs"]}; - font-weight: ${V3_Font.Spec["weight-bold"]}; + ${Font["body-xs-bold"]} line-height: 1; `; @@ -61,11 +60,9 @@ const dotBadgeStyles = css` export const StyledBadge = styled.div` background-color: ${({ $color }) => - $color === "important" - ? V3_Colour["icon-error"] - : V3_Colour["bg-primary"]}; - color: ${V3_Colour["text-inverse"]}; - font-weight: ${V3_Font.Spec["weight-bold"]}; + $color === "important" ? Colour["icon-error"] : Colour["bg-primary"]}; + color: ${Colour["text-inverse"]}; + font-weight: ${Font["body-xs-bold"]}; display: flex; align-items: center; justify-content: center; @@ -76,15 +73,14 @@ export const StyledBadge = styled.div` case "number": return css` ${numberBadgeStyles} - border-radius: ${V3_Radius.full}; + border-radius: ${Radius.full}; `; case "number-with-border": return css` ${numberBadgeStyles} - border-radius: ${V3_Radius.full}; - box-shadow: 0 0 0 ${V3_Border["width-020"]} - ${V3_Colour["bg"]}; + border-radius: ${Radius.full}; + box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; `; case "dot": @@ -95,20 +91,18 @@ export const StyledBadge = styled.div` case "dot-with-border": return css` ${dotBadgeStyles} - box-shadow: 0 0 0 ${V3_Border["width-020"]} ${V3_Colour[ - "bg" - ]}; + box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; `; case "square-number": return css` ${numberBadgeStyles} - border-radius: ${V3_Radius.sm}; + border-radius: ${Radius.sm}; padding: 0.25rem 0.4375rem; ${$color === "default" && css` - background-color: ${V3_Colour["bg-primary-subtler"]}; - color: ${V3_Colour["text-primary"]}; + background-color: ${Colour["bg-primary-subtler"]}; + color: ${Colour["text-primary"]}; `} `; From 9d61fe0d23c35f51152e30ec48922fa0a8b8693d Mon Sep 17 00:00:00 2001 From: Ghazwan Date: Fri, 17 Apr 2026 16:34:01 +0700 Subject: [PATCH 3/7] [BOOKINGSG-9219][GZ] convert interpolation props --- src/badge/badge.styles.ts | 145 +++++++++++++++++--------------------- src/badge/badge.tsx | 54 +++++++++++--- 2 files changed, 106 insertions(+), 93 deletions(-) diff --git a/src/badge/badge.styles.ts b/src/badge/badge.styles.ts index db538c670..06adb6013 100644 --- a/src/badge/badge.styles.ts +++ b/src/badge/badge.styles.ts @@ -1,113 +1,94 @@ -import styled, { css } from "styled-components"; +import styled from "styled-components"; import { Border, Colour, Font, Radius } from "../theme"; -import type { BadgeProps } from "./types"; // ============================================================================= -// STYLE INTERFACE, transient props are denoted with $ -// See more https://styled-components.com/docs/api#transient-props +// STYLE TOKENS // ============================================================================= -interface StyledBadgeProps { - $variant: BadgeProps["variant"]; - $color: BadgeProps["color"]; -} -interface BadgeContainerProps { - $isOverlay?: boolean; -} -interface BadgeWrapperProps extends BadgeContainerProps { - $offset?: [string, string]; -} +export const tokens = { + wrapper: { + offsetX: "--fds-internal-badge-wrapper-offsetX", + offsetY: "--fds-internal-badge-wrapper-offsetY", + }, +}; // ============================================================================= // STYLING // ============================================================================= -export const BadgeOverlay = styled.div` - ${(props) => - props.$isOverlay && - css` - position: relative; - width: fit-content; - height: fit-content; - `} -`; - -export const BadgeWrapper = styled.div` - ${(props) => - props.$isOverlay && - css` - position: absolute; - top: 0; - right: 0; - transform: translate(50%, -25%) - ${props.$offset - ? `translate(${props.$offset[0]},${props.$offset[1]})` - : ""}; - `} -`; - -const numberBadgeStyles = css` +const numberBadgeStyles = ` min-width: 1.25rem; padding: 0.25rem 0.375rem; ${Font["body-xs-bold"]} line-height: 1; `; -const dotBadgeStyles = css` +const dotBadgeStyles = ` border-radius: 50%; width: 0.5rem; height: 0.5rem; `; -export const StyledBadge = styled.div` - background-color: ${({ $color }) => - $color === "important" ? Colour["icon-error"] : Colour["bg-primary"]}; +export const BadgeOverlay = styled.div` + &.badgeOverlayIsOverlay { + position: relative; + width: fit-content; + height: fit-content; + } +`; + +export const BadgeWrapper = styled.div` + &.badgeWrapperIsOverlay { + position: absolute; + top: 0; + right: 0; + transform: translate(50%, -25%) + translate( + var(${tokens.wrapper.offsetX}, 0px), + var(${tokens.wrapper.offsetY}, 0px) + ); + } +`; + +export const StyledBadge = styled.div` + background-color: ${Colour["bg-primary"]}; color: ${Colour["text-inverse"]}; - font-weight: ${Font["body-xs-bold"]}; display: flex; align-items: center; justify-content: center; - width: fit-content; - ${({ $variant, $color }) => { - switch ($variant) { - case "number": - return css` - ${numberBadgeStyles} - border-radius: ${Radius.full}; - `; - case "number-with-border": - return css` - ${numberBadgeStyles} - border-radius: ${Radius.full}; - box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; - `; + &.styledBadgeImportantColor { + background-color: ${Colour["icon-error"]}; + } + + &.styledBadgeNumber { + ${numberBadgeStyles} + border-radius: ${Radius.full}; + } + + &.styledBadgeNumberWithBorder { + ${numberBadgeStyles} + border-radius: ${Radius.full}; + box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; + } - case "dot": - return css` - ${dotBadgeStyles} - `; + &.styledBadgeDot { + ${dotBadgeStyles} + } - case "dot-with-border": - return css` - ${dotBadgeStyles} - box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; - `; + &.styledBadgeDotWithBorder { + ${dotBadgeStyles} + box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; + } - case "square-number": - return css` - ${numberBadgeStyles} - border-radius: ${Radius.sm}; - padding: 0.25rem 0.4375rem; - ${$color === "default" && - css` - background-color: ${Colour["bg-primary-subtler"]}; - color: ${Colour["text-primary"]}; - `} - `; + &.styledBadgeSquareNumber { + ${numberBadgeStyles} + border-radius: ${Radius.sm}; + padding: 0.25rem 0.4375rem; + } - default: - return ""; - } - }}; + &.styledBadgeSquareNumberDefaultColor { + background-color: ${Colour["bg-primary-subtler"]}; + color: ${Colour["text-primary"]}; + } `; diff --git a/src/badge/badge.tsx b/src/badge/badge.tsx index 3f3c93dfb..bb52d272f 100644 --- a/src/badge/badge.tsx +++ b/src/badge/badge.tsx @@ -1,4 +1,8 @@ -import { BadgeOverlay, BadgeWrapper, StyledBadge } from "./badge.styles"; +import clsx from "clsx"; +import { useRef } from "react"; + +import { useApplyStyle } from "../theme"; +import * as styles from "./badge.styles"; import type { BadgeProps, BadgeVariant } from "./types"; // ============================================================================= @@ -17,6 +21,7 @@ export const Badge = ({ color = "default", badgeOffset, "data-testid": testId = "badge", + className, ...otherProps }: BadgeProps) => { // ============================================================================= @@ -30,25 +35,52 @@ export const Badge = ({ ]; const shouldShowCount = variantsToShowCount.includes(variant); + // ============================================================================= + // REFS + // ============================================================================= + const wrapperRef = useRef(null); + + useApplyStyle(wrapperRef, { + [styles.tokens.wrapper.offsetX]: badgeOffset?.[0], + [styles.tokens.wrapper.offsetY]: badgeOffset?.[1], + }); + // ============================================================================= // RENDER FUNCTIONS // ============================================================================= return ( - - + - {shouldShowCount ? displayCount : null} - - + + {children} - + ); }; From 771483b77190b117f1ffeb857a8a848de1310639 Mon Sep 17 00:00:00 2001 From: Ghazwan Date: Fri, 17 Apr 2026 16:40:32 +0700 Subject: [PATCH 4/7] [BOOKINGSG-9219][GZ] convert styled components to linaria css --- src/badge/badge.styles.ts | 90 +++++++++++++++++++-------------------- src/badge/badge.tsx | 32 +++++++------- 2 files changed, 58 insertions(+), 64 deletions(-) diff --git a/src/badge/badge.styles.ts b/src/badge/badge.styles.ts index 06adb6013..b10388f0c 100644 --- a/src/badge/badge.styles.ts +++ b/src/badge/badge.styles.ts @@ -1,4 +1,4 @@ -import styled from "styled-components"; +import { css } from "@linaria/core"; import { Border, Colour, Font, Radius } from "../theme"; @@ -28,67 +28,63 @@ const dotBadgeStyles = ` height: 0.5rem; `; -export const BadgeOverlay = styled.div` - &.badgeOverlayIsOverlay { - position: relative; - width: fit-content; - height: fit-content; - } +export const Overlay = css` + position: relative; + width: fit-content; + height: fit-content; `; -export const BadgeWrapper = styled.div` - &.badgeWrapperIsOverlay { - position: absolute; - top: 0; - right: 0; - transform: translate(50%, -25%) - translate( - var(${tokens.wrapper.offsetX}, 0px), - var(${tokens.wrapper.offsetY}, 0px) - ); - } +export const OverlayWrapper = css` + position: absolute; + top: 0; + right: 0; + transform: translate(50%, -25%) + translate( + var(${tokens.wrapper.offsetX}, 0px), + var(${tokens.wrapper.offsetY}, 0px) + ); `; -export const StyledBadge = styled.div` +export const badge = css` background-color: ${Colour["bg-primary"]}; color: ${Colour["text-inverse"]}; display: flex; align-items: center; justify-content: center; width: fit-content; +`; - &.styledBadgeImportantColor { - background-color: ${Colour["icon-error"]}; - } +export const badgeImportantColor = css` + background-color: ${Colour["icon-error"]}; +`; - &.styledBadgeNumber { - ${numberBadgeStyles} - border-radius: ${Radius.full}; - } +export const badgeNumber = css` + ${numberBadgeStyles} + border-radius: ${Radius.full}; +`; - &.styledBadgeNumberWithBorder { - ${numberBadgeStyles} - border-radius: ${Radius.full}; - box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; - } +export const badgeNumberWithBorder = css` + ${numberBadgeStyles} + border-radius: ${Radius.full}; + box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; +`; - &.styledBadgeDot { - ${dotBadgeStyles} - } +export const badgeDot = css` + ${dotBadgeStyles} +`; - &.styledBadgeDotWithBorder { - ${dotBadgeStyles} - box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; - } +export const badgeDotWithBorder = css` + ${dotBadgeStyles} + box-shadow: 0 0 0 ${Border["width-020"]} ${Colour.bg}; +`; - &.styledBadgeSquareNumber { - ${numberBadgeStyles} - border-radius: ${Radius.sm}; - padding: 0.25rem 0.4375rem; - } +export const badgeSquareNumber = css` + ${numberBadgeStyles} + border-radius: ${Radius.sm}; + padding: 0.25rem 0.4375rem; +`; - &.styledBadgeSquareNumberDefaultColor { - background-color: ${Colour["bg-primary-subtler"]}; - color: ${Colour["text-primary"]}; - } +export const badgeSquareNumberDefaultColor = css` + background-color: ${Colour["bg-primary-subtler"]}; + color: ${Colour["text-primary"]}; `; diff --git a/src/badge/badge.tsx b/src/badge/badge.tsx index bb52d272f..984b3c9aa 100644 --- a/src/badge/badge.tsx +++ b/src/badge/badge.tsx @@ -49,38 +49,36 @@ export const Badge = ({ // RENDER FUNCTIONS // ============================================================================= return ( - - +
- {shouldShowCount ? displayCount : null} - - +
+ {children} -
+ ); }; From 913c283f4a5a1c0a651fffed8caec374b70aea64 Mon Sep 17 00:00:00 2001 From: Ghazwan Date: Fri, 17 Apr 2026 16:46:39 +0700 Subject: [PATCH 5/7] [BOOKINGSG-9219][GZ] add unit test --- tests/badge/badge.spec.tsx | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/badge/badge.spec.tsx diff --git a/tests/badge/badge.spec.tsx b/tests/badge/badge.spec.tsx new file mode 100644 index 000000000..298969c8d --- /dev/null +++ b/tests/badge/badge.spec.tsx @@ -0,0 +1,44 @@ +import { render, screen } from "@testing-library/react"; +import { Badge } from "src/badge"; + +describe("Badge", () => { + describe("count display", () => { + it("should display the count as-is when 999 or below", () => { + render(); + + expect(screen.getByText("999")).toBeInTheDocument(); + }); + + it("should display '1K' when count is exactly 1000", () => { + render(); + + expect(screen.getByText("1K")).toBeInTheDocument(); + }); + + it("should display '1K+' when count exceeds 1000", () => { + render(); + + expect(screen.getByText("1K+")).toBeInTheDocument(); + }); + }); + + describe("variants", () => { + it.each(["number", "number-with-border", "square-number"] as const)( + "should display the count for variant '%s'", + (variant) => { + render(); + + expect(screen.getByText("5")).toBeInTheDocument(); + } + ); + + it.each(["dot", "dot-with-border"] as const)( + "should not display the count for variant '%s'", + (variant) => { + render(); + + expect(screen.queryByText("5")).not.toBeInTheDocument(); + } + ); + }); +}); From f0b398bb2430e29cfd68add068f57699b98037b4 Mon Sep 17 00:00:00 2001 From: Ghazwan Date: Fri, 17 Apr 2026 17:23:16 +0700 Subject: [PATCH 6/7] [BOOKINGSG-9219][GZ] add visual testing --- .../src/app/components/badge/anchored.e2e.tsx | 36 ++++++++ .../src/app/components/badge/badge.module.css | 4 + .../src/app/components/badge/count.e2e.tsx | 15 ++++ .../src/app/components/badge/variants.e2e.tsx | 72 ++++++++++++++++ ...Anchored-positioning-and-offset--mount.png | Bin 0 -> 3509 bytes .../Badge-Count-formatting--mount.png | Bin 0 -> 2726 bytes .../chromium/Badge-Variants--mount.png | Bin 0 -> 5137 bytes .../Badge-Variants-dark-mode--mount.png | Bin 0 -> 5206 bytes e2e/tests/components/badge/badge.e2e.spec.ts | 77 ++++++++++++++++++ 9 files changed, 204 insertions(+) create mode 100644 e2e/nextjs-app/src/app/components/badge/anchored.e2e.tsx create mode 100644 e2e/nextjs-app/src/app/components/badge/badge.module.css create mode 100644 e2e/nextjs-app/src/app/components/badge/count.e2e.tsx create mode 100644 e2e/nextjs-app/src/app/components/badge/variants.e2e.tsx create mode 100644 e2e/tests/components/badge/__screenshots__/chromium/Badge-Anchored-positioning-and-offset--mount.png create mode 100644 e2e/tests/components/badge/__screenshots__/chromium/Badge-Count-formatting--mount.png create mode 100644 e2e/tests/components/badge/__screenshots__/chromium/Badge-Variants--mount.png create mode 100644 e2e/tests/components/badge/__screenshots__/chromium/Badge-Variants-dark-mode--mount.png create mode 100644 e2e/tests/components/badge/badge.e2e.spec.ts diff --git a/e2e/nextjs-app/src/app/components/badge/anchored.e2e.tsx b/e2e/nextjs-app/src/app/components/badge/anchored.e2e.tsx new file mode 100644 index 000000000..320a5bdc5 --- /dev/null +++ b/e2e/nextjs-app/src/app/components/badge/anchored.e2e.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { Button } from "@lifesg/react-design-system"; +import { Avatar } from "@lifesg/react-design-system/avatar"; +import { Badge } from "@lifesg/react-design-system/badge"; + +export default function Story() { + return ( +
+ +
+ ); +} diff --git a/e2e/nextjs-app/src/app/components/badge/badge.module.css b/e2e/nextjs-app/src/app/components/badge/badge.module.css new file mode 100644 index 000000000..fe941b34b --- /dev/null +++ b/e2e/nextjs-app/src/app/components/badge/badge.module.css @@ -0,0 +1,4 @@ +.wrapper { + padding: 1em; + background-color: burlywood; +} diff --git a/e2e/nextjs-app/src/app/components/badge/count.e2e.tsx b/e2e/nextjs-app/src/app/components/badge/count.e2e.tsx new file mode 100644 index 000000000..a8e35d9ea --- /dev/null +++ b/e2e/nextjs-app/src/app/components/badge/count.e2e.tsx @@ -0,0 +1,15 @@ +"use client"; + +import { Badge } from "@lifesg/react-design-system/badge"; + +export default function Story() { + return ( +
+ + + + + +
+ ); +} diff --git a/e2e/nextjs-app/src/app/components/badge/variants.e2e.tsx b/e2e/nextjs-app/src/app/components/badge/variants.e2e.tsx new file mode 100644 index 000000000..0e1c9c54c --- /dev/null +++ b/e2e/nextjs-app/src/app/components/badge/variants.e2e.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { Badge } from "@lifesg/react-design-system/badge"; +import clsx from "clsx"; +import styles from "./badge.module.css"; + +export default function Story() { + return ( +
+
+ + + + + +
+ +
+ + + + + +
+
+ ); +} diff --git a/e2e/tests/components/badge/__screenshots__/chromium/Badge-Anchored-positioning-and-offset--mount.png b/e2e/tests/components/badge/__screenshots__/chromium/Badge-Anchored-positioning-and-offset--mount.png new file mode 100644 index 0000000000000000000000000000000000000000..4db470d960a2ef44b48d7e296dc17f9df2fe7da9 GIT binary patch literal 3509 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 Q=%9w79!$6Niv5Fs0Mm3d3jhEB literal 0 HcmV?d00001 diff --git a/e2e/tests/components/badge/__screenshots__/chromium/Badge-Count-formatting--mount.png b/e2e/tests/components/badge/__screenshots__/chromium/Badge-Count-formatting--mount.png new file mode 100644 index 0000000000000000000000000000000000000000..f34e1b678a86c539502a566f977ba4dd54e214fc GIT binary patch literal 2726 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 WJrO!NZ9W&mM`dGWXIXpEGx0w;?EBRK literal 0 HcmV?d00001 diff --git a/e2e/tests/components/badge/__screenshots__/chromium/Badge-Variants--mount.png b/e2e/tests/components/badge/__screenshots__/chromium/Badge-Variants--mount.png new file mode 100644 index 0000000000000000000000000000000000000000..b3b86d321c979aaf033ee313bf7754a13b386863 GIT binary patch literal 5137 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 literal 0 HcmV?d00001 diff --git a/e2e/tests/components/badge/__screenshots__/chromium/Badge-Variants-dark-mode--mount.png b/e2e/tests/components/badge/__screenshots__/chromium/Badge-Variants-dark-mode--mount.png new file mode 100644 index 0000000000000000000000000000000000000000..b830da9ce29e7afa289215c5179f6ff7ad8b8ff1 GIT binary patch literal 5206 zcmchbXH-+&wuS?!ASj9`A}FX3igXbWkRphv^d=xk0Kw3aA}v5rKtEa#5JIogd+!NF zy7b z0{}{4@AFctJpe z&PH?he6?)N^3PJ8wDN897!*5m++FZt{Js8l?{@Ek+i%=v&>DqISe6PjY^Pn<=&rh9 z_82HOk8y8}VDCg&WjrxjQI%RO)WI*;=r$1dsWckKX2I7?P-*4PAG5Nl+xjjB$MLQ2 zC5wK(Rjl**M@Pu_0n>M>b5U~jViJ6MHJSTXM=|MHdF=?no-dJJihlaexRXCjGsRb)rc38${+0}BQF`vgLf?ar&9qe(l;lo2YlFe6dhU+|4-OS`!Y!JqlSBjS-LAsrww)tXk?jTb#jlW% ziXFZ#prL8Qin?LL+D70sZBM|9{tY`OEbiNN@@~0;oh~)4+Ubzg+jXaT=BUEDIN*s< zeUy5^Qg2T(*QV$c4@-M2n(5R+EJ`(tzcs9+sZB54c5Zf$a)3@v3t6pu8Gdo?5G^x+ zv7?TQ!|9W*+UQCc1=K%UBX7Vj$b5!s(=uoBtw$x$W$9Rx7?{>fh}bSOsG^BBnbEp0 z{BTK5OqP%Z;+<1r>WLwIXojPvxJP~=gdeu&RTrmBHER9=@7Od*}Qh_q-Oa zvAEV|Dbi)gFm>R|>*L(D{Fn6`U~U0{(Q6tYeO$S9CfYolu=UbNzXz;uxdoLFVAFOf z+&S38CLO`(lJs$}p__XN-@Jtk6TMeAEHmufW$$$GUYDYoxs&^0u73-^=e07t?~aPjQ4mf=hV z-m#T7k3XVJizfzTUN((s9$pP^z~Wu-_c<-PpH7+>co}=N&s}D4?+r#&m3M5Xu$Sxs z1z#;M7oL8U)aQ$Aexe^WPIiLvQB?7ijRiO$HBMu>+AjuI1{Qjuai86Dd?#D)b>$KD zq13Vp{ZfkaeNuTd@enI)^d+m1M-6aCzpZ$^_5$f!(7pI;;;B8d zU`GJho9dRvHS9x|hQh3Z`b^Zc{>HK*;2deksamq;$C+ova(p&7gu@GJw?08q#Mt@1 z6!-UO&<+rBC`TGG8-77&yPXAg*Tmz^&hk0t64xVD+OWDkw(+oFcDQYx(+r%?!>bMg zkz|$2-T9>wRhNLSW66KB0+Qq9=XxNEnVDapuni|C7@$^nm14prsFWevQ3lcYOY?D1 z?(wieY|NDecv9V6Fha9y`Gc{ddCZT*+4s}|9H~Y0G#Z){4_Wf<QYdDsPES{>z$N`YrQd=foZ)$-Pez zl&eNdIzn4_meJ7vv!=v(i6k6u@JKBPTY#FzZK1>1 z32j5KSgjJ%zBygUJwMo@2Y6WnFBcIJH}+UjSoNIuxH5zrsPT-*rCXjDLUEFnr=DsP4a{z4a@r?M4@I_gH!*H7Z?44>LM#U@d!y*j5Yxe>!)RkGG2!Vn;rw z8Z$YaIV@SVI;kg(hvQE$xWS7sW85B{bKEn_EuTRpUz2;-u0&?mQXU!W&N0YBrkEm? z_%ftxfe~5{?i_@r<8#;- z_j}Gu)f3K-waxn)W|QK0bG`VGYxH{NpAJYXaL#nC?n}m8*#5v=kGPv*DtwnZqTaGp zQ-ke{?{SAjS99&GQ632LZeU8(?L-~~9__(*nM)FOrxG$+xaq{+GE@Dl-zj8J7`(co zeSof!0H1vTdwCn~%Zjw0Uy5pZO_~%~B%d;U{0oyu6;4{KU5y!f-I|4!hS(!D^C&uUXd-B9pUFyqB+*4|!wSZfc1*{3xv zv+cL-7QHGBUg`~GZ9ph15F(p6szKJMM-h$CH4IO z`h}J!v`y0?^w++|cJr|=Jrn?n3rW9M>hl(Ha@{qVFtq8OgA`|! zqR6*zviy*O0*~T;u%oIxY@U^z%J5u9+WF@ls$B6)5J=BbcEwA#HzV$fJY%NP^=T@+ zJ`!0|Beu5Im6qn5)oq8;e)-ZElp-RH-OYYUSAB6(34M0ZfidX>23McpM!%2p zX}bzyzzQ|Ew!CRO3YXjbZaeGq(lNEin4CSPmoGTyhMa@WAtR>@^MStt7( zX78swk{jw~>+mw~T|d#M|D@Yj2b2Vnqb8d)rWZs4W-J(}dt13h6_maW4eUN`Zr)8G z#KFImSRUZaRBTZ`)u~l`K`E%R`$CE}Y3}l0tEGpERql92Fx};melQ$NeLF7SOIo!D5zoIov#+(oQF5|ET9N0uKat&WKGUD!l^!Woh zCC+2~e?!hI8^ln^Bpq3r@p}!mDMSO=(lr=s!@3MH|0{&fCy9hO@6~sMTW^_fZg-PZ zrI*#u3Gx#a-;9^_#6blOQc0}AZ*)j~4&-HJ&*ERVF|Fv?fYVyOfuxyFCFdlCaf`%)7HaHEyg z#eDbjtQ_`+*2>vG$+?#?74aq+A@j?$~wATL+SI#g$0CLsO%Jy5`^qk#T z#YQt+1F~8|%+{Rd;|^gkZahYX{^CjN`|-2r-Ei!ri<$gxnjJafzB4lsaP!7(#Fcvr zyRgsLPWVl4rCFKJ+ozawFQ>3^6;pjloYa&HPZ3BmE+P6J-B)OQ1hj4UIS*b%swGl6>Ob+4qQY{p) z&be459poN+=eo-!CMGs(dmk>LjdvK9?qdgN(qw)(Wk=5N95OJy_t^JmfFA<)SQq@hk|YY7ax zbBCy-mr25s`7~I)cU)hJyQ73vDrvh^MDksL5crM9vyx3!EuWLrS%g{hN2TE`nXgH+ zZ%fzhH+L#*$ev?UX_^!^Ft=`8tst&XVxmcKAgMh!7;*VZ`XAiE&5lg{Mw#@rf1h|a zA7|jd$om4y&o^zTkB{%->?k5ylT}hF7}u`;3Tf4Y&3;X&Ppk2m?Mp(Ln5=1OA&o1Z znJrq~Uv0l06Auf2NLlbs2)vjcYB3*I%nkGz8mY+0d%*^#-^D{c=368g7|3J?c<}u1 zmd`jv@?NbszPu3A*C?J4UR|A1T^&+%o4r)nc%Mhc)MR)d^5`U+%EzbL2W`Y_W|p^? zXJc!$)y^98;lm{pZ=Uv?eLu`JSlK8-&MA9Ts%;JwI^WKBh8#wf=k$tT9K}0;l8Rtj zz|F}#FLqDzwRG~z!R_$LHrnr|?|efL2-LK+cK-g|_{cWf?QP4$-rg5_5xyPYhSy}d zPfk(voRtuuN8W0)GMoA9xOA|;ULtXQr!$^)7a_|%HI>Z6tAzrF%)hSpvac#d*B6Yv zmv-;|H|%kJoX2sf^v`1+3412)2tMN%4;Dsh(Je*t|K&u?t^FV3Avmp1>=rkaZLH_O z*Sa$;iTfQ1(b3NQMh;zS*KK-ft~hM}^EgU_nLT6sE!o$sDo8wE*a)MLO}EQv=Eaci zzE}Z<)2gVUeequIDSEKQY}HyRsi}iVBOh<<48?&Iq^c@I@!t^#68~q=hWJ_{a~~lD zh+#pGTbOT?GjE!%1!3eT5y-ou%EiIC4IUIo@@a46kFOoRt~##!S5Fy4Q{EmhhH~*k z(%c_IZ02v=e5kGeO3TP8ecm;rwzg2GPdVU5mU3Wk!i^{~p{?JNTc@=n>_F25hlSRB zGbRVxPyg=&&3{Cq)beS}sRutHY1se$ZP$OiP$Q5U2a(v^I+0Gwz&Ua&paR&kDW?J% SHy)9b@?1&%Ns)r-$A18-?Pl%( literal 0 HcmV?d00001 diff --git a/e2e/tests/components/badge/badge.e2e.spec.ts b/e2e/tests/components/badge/badge.e2e.spec.ts new file mode 100644 index 000000000..03737527f --- /dev/null +++ b/e2e/tests/components/badge/badge.e2e.spec.ts @@ -0,0 +1,77 @@ +import { test as base, expect, Locator, Page } from "@playwright/test"; +import { AbstractStoryPage, compareScreenshot } from "../../utils"; + +class StoryPage extends AbstractStoryPage { + protected readonly component = "badge"; + + public readonly locators: { + variantsRowDefault: Locator; + variantsRowImportant: Locator; + badgeCount1000: Locator; + badgeCount2090: Locator; + anchoredOffset: Locator; + anchoredAvatar: Locator; + }; + + constructor(page: Page) { + super(page); + + this.locators = { + variantsRowDefault: page.getByTestId("badge-dot-default"), + variantsRowImportant: page.getByTestId("badge-dot-important"), + badgeCount1000: page.getByTestId("badge-count-1000"), + badgeCount2090: page.getByTestId("badge-count-2090"), + anchoredOffset: page.getByTestId("badge-anchored-offset"), + anchoredAvatar: page.getByTestId("badge-anchored-avatar"), + }; + } +} + +const test = base.extend<{ story: StoryPage }>({ + story: async ({ page }, mountStory) => { + const story = new StoryPage(page); + await mountStory(story); + }, +}); + +test.describe("Badge", () => { + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("variants"); + }); + + test("Variants", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("variants", { mode: "dark" }); + }); + + test("Variants dark mode", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("count"); + }); + + test("Count formatting", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("anchored"); + }); + + test("Anchored positioning and offset", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); +}); From 1ba04be32c8261cdcf6f9132a6ef6b2998ed4b19 Mon Sep 17 00:00:00 2001 From: Ghazwan Date: Fri, 17 Apr 2026 17:28:57 +0700 Subject: [PATCH 7/7] [BOOKINGSG-9219][GZ] minor improvement --- src/badge/badge.styles.ts | 12 ++++-------- src/badge/badge.tsx | 22 ++++++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/badge/badge.styles.ts b/src/badge/badge.styles.ts index b10388f0c..61cad1c77 100644 --- a/src/badge/badge.styles.ts +++ b/src/badge/badge.styles.ts @@ -2,9 +2,6 @@ import { css } from "@linaria/core"; import { Border, Colour, Font, Radius } from "../theme"; -// ============================================================================= -// STYLE TOKENS -// ============================================================================= export const tokens = { wrapper: { offsetX: "--fds-internal-badge-wrapper-offsetX", @@ -12,9 +9,6 @@ export const tokens = { }, }; -// ============================================================================= -// STYLING -// ============================================================================= const numberBadgeStyles = ` min-width: 1.25rem; padding: 0.25rem 0.375rem; @@ -28,13 +22,15 @@ const dotBadgeStyles = ` height: 0.5rem; `; -export const Overlay = css` +export const badgeOverlay = css` position: relative; width: fit-content; height: fit-content; `; -export const OverlayWrapper = css` +export const badgeWrapper = css``; + +export const badgeWrapperIsOverlay = css` position: absolute; top: 0; right: 0; diff --git a/src/badge/badge.tsx b/src/badge/badge.tsx index 984b3c9aa..f71641267 100644 --- a/src/badge/badge.tsx +++ b/src/badge/badge.tsx @@ -5,14 +5,16 @@ import { useApplyStyle } from "../theme"; import * as styles from "./badge.styles"; import type { BadgeProps, BadgeVariant } from "./types"; -// ============================================================================= -// HELPER FUNCTIONS -// ============================================================================= function getDisplayCount(count: number) { if (count <= 999) return count.toString(); if (count === 1000) return "1K"; return "1K+"; } +const variantsToShowCount: Set = new Set([ + "number", + "number-with-border", + "square-number", +]); export const Badge = ({ children, @@ -28,12 +30,7 @@ export const Badge = ({ // CONST // ============================================================================= const displayCount = getDisplayCount(count); - const variantsToShowCount: BadgeVariant[] = [ - "number", - "number-with-border", - "square-number", - ]; - const shouldShowCount = variantsToShowCount.includes(variant); + const shouldShowCount = variantsToShowCount.has(variant); // ============================================================================= // REFS @@ -49,11 +46,12 @@ export const Badge = ({ // RENDER FUNCTIONS // ============================================================================= return ( -
+