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

feat(ChoiceGroup): migrate to Tailwind #4143

Merged
merged 1 commit into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/data/tailwind-migration-status.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Card: true
CardSection: true
CarrierLogo: true
Checkbox: true
ChoiceGroup: false
ChoiceGroup: true
Collapse: true
Coupon: false
CountryFlag: true
Expand Down
2 changes: 1 addition & 1 deletion packages/orbit-components/src/ChoiceGroup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// @flow
import type { StyledComponent } from "styled-components";
import * as React from "react";

import type { Globals } from "../../common/common.js.flow";
Expand All @@ -10,5 +9,3 @@ export type Props = {|
|};

declare export default React.ComponentType<Props>;

declare export var StyledFormFeedback: StyledComponent<any, any, HTMLElement>;
60 changes: 23 additions & 37 deletions packages/orbit-components/src/ChoiceGroup/components/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react";
import styled, { css } from "styled-components";
import cx from "clsx";

import defaultTheme from "../../defaultTheme";

Expand All @@ -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;
Original file line number Diff line number Diff line change
@@ -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>>;
Expand All @@ -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"
Expand All @@ -86,9 +44,9 @@ const FilterWrapper = ({ child, children, onOnlySelection, onlySelectionText }:
>
{onlySelectionText}
</ButtonLink>
</StyledOnlyButton>
</div>
)}
</StyledContentWrapper>
</div>
);
};

Expand Down
29 changes: 8 additions & 21 deletions packages/orbit-components/src/ChoiceGroup/index.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 }) => {
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -128,7 +115,7 @@ const ChoiceGroup = React.forwardRef<HTMLDivElement, Props>(
</Stack>
)}
{error && <Feedback>{error}</Feedback>}
</StyledChoiceGroup>
</div>
);
},
);
Expand Down
Loading