From 65c6763ffe45ff5d96f518f6d10ae1e607f3b9ef Mon Sep 17 00:00:00 2001
From: Marco Vidal Garcia <marco.vidal@kiwi.com>
Date: Thu, 9 Nov 2023 17:30:22 +0100
Subject: [PATCH] feat(ChoiceGroup): migrate to Tailwind

Closes FEPLT-1707
---
 docs/src/data/tailwind-migration-status.yaml  |  2 +-
 .../src/ChoiceGroup/README.md                 |  2 +-
 .../ChoiceGroup/components/Feedback.js.flow   |  3 -
 .../src/ChoiceGroup/components/Feedback.tsx   | 60 ++++++--------
 .../ChoiceGroup/components/FilterWrapper.tsx  | 80 +++++--------------
 .../src/ChoiceGroup/index.tsx                 | 29 ++-----
 6 files changed, 52 insertions(+), 124 deletions(-)

diff --git a/docs/src/data/tailwind-migration-status.yaml b/docs/src/data/tailwind-migration-status.yaml
index c90697149d..648039fefc 100644
--- a/docs/src/data/tailwind-migration-status.yaml
+++ b/docs/src/data/tailwind-migration-status.yaml
@@ -20,7 +20,7 @@ Card: true
 CardSection: true
 CarrierLogo: true
 Checkbox: true
-ChoiceGroup: false
+ChoiceGroup: true
 Collapse: true
 Coupon: false
 CountryFlag: true
diff --git a/packages/orbit-components/src/ChoiceGroup/README.md b/packages/orbit-components/src/ChoiceGroup/README.md
index 3f91f3521d..0aa83abd17 100644
--- a/packages/orbit-components/src/ChoiceGroup/README.md
+++ b/packages/orbit-components/src/ChoiceGroup/README.md
@@ -77,7 +77,7 @@ For more realistic usage you can check out the "Render prop" example in Storyboo
 
 ## Functional specs
 
-- onChange props in `<Radio />` or `<Checkbox />` will be overrode by internal onChange function
+- onChange props in `<Radio />` or `<Checkbox />` will be overridden by internal onChange function
 - If you want to handle selecting field, pass `onChange` to `<ChoiceGroup />` and it will be always triggered when `<Radio />` or `<Checkbox />` should change
 - `onChange` will return `SyntheticEvent` of field that should change
 
diff --git a/packages/orbit-components/src/ChoiceGroup/components/Feedback.js.flow b/packages/orbit-components/src/ChoiceGroup/components/Feedback.js.flow
index 9fa354d47c..b6e2fb53fb 100644
--- a/packages/orbit-components/src/ChoiceGroup/components/Feedback.js.flow
+++ b/packages/orbit-components/src/ChoiceGroup/components/Feedback.js.flow
@@ -1,5 +1,4 @@
 // @flow
-import type { StyledComponent } from "styled-components";
 import * as React from "react";
 
 import type { Globals } from "../../common/common.js.flow";
@@ -10,5 +9,3 @@ export type Props = {|
 |};
 
 declare export default React.ComponentType<Props>;
-
-declare export var StyledFormFeedback: StyledComponent<any, any, HTMLElement>;
diff --git a/packages/orbit-components/src/ChoiceGroup/components/Feedback.tsx b/packages/orbit-components/src/ChoiceGroup/components/Feedback.tsx
index d05f66e1fc..dc4dbb6e8f 100644
--- a/packages/orbit-components/src/ChoiceGroup/components/Feedback.tsx
+++ b/packages/orbit-components/src/ChoiceGroup/components/Feedback.tsx
@@ -1,5 +1,5 @@
 import * as React from "react";
-import styled, { css } from "styled-components";
+import cx from "clsx";
 
 import defaultTheme from "../../defaultTheme";
 
@@ -8,44 +8,30 @@ interface Props {
   children: React.ReactNode;
 }
 
-export const StyledFormFeedback = styled(props => <div {...props} />)`
-  ${({ theme }) => css`
-    color: ${theme.orbit.colorTextError};
-    font-family: ${theme.orbit.fontFamily};
-    font-size: ${theme.orbit.fontSizeFormFeedback};
-    font-weight: ${theme.orbit.fontWeightMedium};
-    line-height: ${theme.orbit.lineHeightTextSmall};
-    width: 100%;
-    margin-top: 2px;
-    position: absolute;
-    top: 100%;
-    max-height: ${Math.floor(
-      theme.orbit.lineHeightText * parseInt(theme.orbit.fontSizeFormFeedback, 10),
-    )}px;
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-    & a {
-      color: ${theme.orbit.colorTextError};
-      font-weight: ${theme.orbit.fontWeightMedium};
-      text-decoration: underline;
-      cursor: pointer;
-    }
-    strong,
-    b {
-      font-weight: ${theme.fontWeightMedium};
-      color: ${theme.paletteInkDark};
-    }
-  `}
-`;
-
-StyledFormFeedback.defaultProps = {
-  theme: defaultTheme,
-};
-
 const FormFeedback = (props: Props) => {
   const { children, dataTest } = props;
-  return <StyledFormFeedback data-test={dataTest}>{children}</StyledFormFeedback>;
+  return (
+    <div
+      className={cx(
+        "orbit-choice-group-feedback",
+        "text-critical-foreground font-base leading-small text-small font-medium",
+        "mt-xxxs relative top-full max-h-[--max-height] w-full overflow-hidden overflow-ellipsis whitespace-nowrap",
+        "[&_a]:cursor-pointer [&_a]:underline",
+        "[&_strong]:text-ink-dark [&_b]:text-ink-dark [&_b]:font-medium [&_strong]:font-medium",
+      )}
+      style={
+        {
+          "--max-height": Math.floor(
+            parseFloat(defaultTheme.orbit.lineHeightText) *
+              parseInt(defaultTheme.orbit.fontSizeFormFeedback, 10),
+          ),
+        } as React.CSSProperties
+      }
+      data-test={dataTest}
+    >
+      {children}
+    </div>
+  );
 };
 
 export default FormFeedback;
diff --git a/packages/orbit-components/src/ChoiceGroup/components/FilterWrapper.tsx b/packages/orbit-components/src/ChoiceGroup/components/FilterWrapper.tsx
index 82774d0826..7252f31c05 100644
--- a/packages/orbit-components/src/ChoiceGroup/components/FilterWrapper.tsx
+++ b/packages/orbit-components/src/ChoiceGroup/components/FilterWrapper.tsx
@@ -1,11 +1,8 @@
 import * as React from "react";
-import styled, { css } from "styled-components";
+import cx from "clsx";
 
-import defaultTheme from "../../defaultTheme";
 import ButtonLink from "../../ButtonLink";
 
-const StyledOnlyButton = styled.div``;
-
 interface Props {
   readonly child: React.ReactElement<any, string | React.JSXElementConstructor<any>>;
   readonly children: React.ReactElement<any, string | React.JSXElementConstructor<any>>;
@@ -16,67 +13,28 @@ interface Props {
   ) => void | Promise<void>;
 }
 
-const hoverAndFocus = () => css`
-  background-color: ${({ theme }) => theme.orbit.paletteBlueLight};
-
-  ${StyledOnlyButton} {
-    visibility: visible;
-    opacity: 1;
-  }
-`;
-
-const StyledContentWrapper = styled.div<{ disabled: boolean }>`
-  ${({ disabled }) => css`
-    box-sizing: border-box;
-    width: 100%;
-    padding-left: 4px;
-    border-radius: 4px;
-    display: flex;
-    align-items: center;
-    height: ${({ theme }) => theme.orbit.heightButtonSmall};
-
-    ${StyledOnlyButton} {
-      visibility: hidden;
-      opacity: 0;
-    }
-
-    ${!disabled &&
-    css`
-      @media (hover: none) {
-        ${StyledOnlyButton} {
-          visibility: visible;
-          opacity: 0.3;
-          &:hover {
-            opacity: 1;
-          }
-        }
-      }
-
-      @media (hover) and (pointer: fine) {
-        &:hover {
-          ${hoverAndFocus};
-        }
-
-        &:focus-within {
-          ${hoverAndFocus}
-        }
-      }
-    `};
-  `}
-`;
-
-StyledContentWrapper.defaultProps = {
-  theme: defaultTheme,
-};
-
 const FilterWrapper = ({ child, children, onOnlySelection, onlySelectionText }: Props) => {
   const { value, label, disabled } = child.props;
 
   return (
-    <StyledContentWrapper disabled={disabled}>
+    <div
+      className={cx(
+        "h-form-box-small pl-xxs box-border flex w-full items-center rounded-[4px]",
+        !disabled &&
+          "hover:[@media(hover)_and_(pointer:fine)]:bg-blue-light focus-within:[@media(hover)_and_(pointer:fine)]:bg-blue-light group",
+      )}
+    >
       {children}
       {onOnlySelection && !disabled && (
-        <StyledOnlyButton>
+        <div
+          className={cx(
+            "orbit-choice-group-filter-wrapper",
+            "[@media(hover)_and_(pointer:fine)]:invisible [@media(hover)_and_(pointer:fine)]:opacity-0",
+            "[@media(hover:none)]:visible [@media(hover:none)]:opacity-30 hover:[@media(hover:none)]:opacity-100",
+            "group-hover:[@media(hover)_and_(pointer:fine)]:visible group-hover:[@media(hover)_and_(pointer:fine)]:opacity-100",
+            "group-focus-within:[@media(hover)_and_(pointer:fine)]:visible group-focus-within:[@media(hover)_and_(pointer:fine)]:opacity-100",
+          )}
+        >
           <ButtonLink
             type="secondary"
             size="small"
@@ -86,9 +44,9 @@ const FilterWrapper = ({ child, children, onOnlySelection, onlySelectionText }:
           >
             {onlySelectionText}
           </ButtonLink>
-        </StyledOnlyButton>
+        </div>
       )}
-    </StyledContentWrapper>
+    </div>
   );
 };
 
diff --git a/packages/orbit-components/src/ChoiceGroup/index.tsx b/packages/orbit-components/src/ChoiceGroup/index.tsx
index fe8b924046..d17f0218dc 100644
--- a/packages/orbit-components/src/ChoiceGroup/index.tsx
+++ b/packages/orbit-components/src/ChoiceGroup/index.tsx
@@ -1,14 +1,13 @@
 "use client";
 
 import * as React from "react";
-import styled from "styled-components";
+import cx from "clsx";
 
 import Heading from "../Heading";
 import type { Type } from "../Heading/types";
 import Stack from "../Stack";
 import { LABEL_SIZES, LABEL_ELEMENTS } from "./consts";
-import Feedback, { StyledFormFeedback } from "./components/Feedback";
-import defaultTheme from "../defaultTheme";
+import Feedback from "./components/Feedback";
 import FilterWrapper from "./components/FilterWrapper";
 import useRandomId from "../hooks/useRandomId";
 import useTheme from "../hooks/useTheme";
@@ -23,22 +22,6 @@ const getHeadingSize = (size: Size): Type => {
   return SIZES[size];
 };
 
-const StyledChoiceGroup = styled.div`
-  width: 100%;
-  display: flex;
-  flex-direction: column;
-
-  ${StyledFormFeedback} {
-    position: relative;
-    margin-top: ${({ theme }) => theme.orbit.spaceXSmall};
-    top: initial;
-  }
-`;
-
-StyledChoiceGroup.defaultProps = {
-  theme: defaultTheme,
-};
-
 const ItemContainer =
   ({ filter, itemProps, onOnlySelection, onlySelectionText }) =>
   ({ children }) => {
@@ -87,12 +70,16 @@ const ChoiceGroup = React.forwardRef<HTMLDivElement, Props>(
     };
 
     return (
-      <StyledChoiceGroup
+      <div
         ref={ref}
         data-test={dataTest}
         role="group"
         aria-labelledby={groupID}
         id={id}
+        className={cx(
+          "flex w-full flex-col",
+          "[&_.orbit-choice-group-feedback]:mt-xs [&_.orbit-choice-group-feedback]:relative [&_.orbit-choice-group-feedback]:top-[initial]",
+        )}
       >
         {label && (
           <Heading id={groupID} type={getHeadingSize(labelSize)} as={labelAs} spaceAfter="medium">
@@ -128,7 +115,7 @@ const ChoiceGroup = React.forwardRef<HTMLDivElement, Props>(
           </Stack>
         )}
         {error && <Feedback>{error}</Feedback>}
-      </StyledChoiceGroup>
+      </div>
     );
   },
 );