From a44115e7202fecb9d4c2aed5a0c88790361716cf Mon Sep 17 00:00:00 2001 From: Marie Lucca <40550942+francinelucca@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:09:01 -0500 Subject: [PATCH] Revert "chore(Select): Remove CSS modules feature flag from Select (#5464)" This reverts commit d43f27f7f7b63ec6dfe8b6efe62c3d9fd6e7386b. --- .changeset/violet-tables-eat.md | 5 -- packages/react/src/Select/Select.tsx | 125 ++++++++++++++++++++++++--- 2 files changed, 113 insertions(+), 17 deletions(-) delete mode 100644 .changeset/violet-tables-eat.md diff --git a/.changeset/violet-tables-eat.md b/.changeset/violet-tables-eat.md deleted file mode 100644 index ac644ed6b67..00000000000 --- a/.changeset/violet-tables-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@primer/react": minor ---- - -Remove CSS modules feature flag from Select diff --git a/packages/react/src/Select/Select.tsx b/packages/react/src/Select/Select.tsx index 75d49c79be2..98faa357844 100644 --- a/packages/react/src/Select/Select.tsx +++ b/packages/react/src/Select/Select.tsx @@ -1,18 +1,69 @@ import React from 'react' +import styled from 'styled-components' import {clsx} from 'clsx' import type {StyledWrapperProps} from '../internal/components/TextInputWrapper' import TextInputWrapper from '../internal/components/TextInputWrapper' +import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' +import {useFeatureFlag} from '../FeatureFlags' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import classes from './Select.module.css' export type SelectProps = Omit< - Omit<React.ComponentProps<'select'>, 'size'> & Omit<StyledWrapperProps, 'variant'>, + Omit<React.ComponentPropsWithoutRef<'select'>, 'size'> & Omit<StyledWrapperProps, 'variant'>, 'multiple' | 'hasLeadingVisual' | 'hasTrailingVisual' | 'as' > & { placeholder?: string } +const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_ga' + +const arrowRightOffset = '4px' + +const StyledSelect = toggleStyledComponent( + CSS_MODULES_FEATURE_FLAG, + 'select', + styled.select` + appearance: none; + border-radius: inherit; + border: 0; + color: currentColor; + font-size: inherit; + outline: none; + width: 100%; + + /* Firefox hacks: */ + /* 1. Makes Firefox's native dropdown menu's background match the theme. + + background-color should be 'transparent', but Firefox uses the background-color on + <select> to determine the background color used for the dropdown menu. + + 2. Adds 1px margins to the <select> so the background color doesn't hide the focus outline created with an inset box-shadow. + */ + background-color: inherit; + margin-top: 1px; + margin-left: 1px; + margin-bottom: 1px; + + /* 2. Prevents visible overlap of partially transparent background colors. + + 'colors.input.disabledBg' happens to be partially transparent in light mode, so we use a + transparent background-color on a disabled <select>. */ + &:disabled { + background-color: transparent; + } + + /* 3. Maintain dark bg color in Firefox on Windows high-contrast mode + + Firefox makes the <select>'s background color white when setting 'background-color: transparent;' */ + @media screen and (forced-colors: active) { + &:disabled { + background-color: -moz-combobox; + } + } + `, +) + const ArrowIndicatorSVG: React.FC<React.PropsWithChildren<{className?: string}>> = ({className}) => { return ( <svg @@ -28,33 +79,83 @@ const ArrowIndicatorSVG: React.FC<React.PropsWithChildren<{className?: string}>> ) } +const StyledArrowIndicatorSVG = styled(ArrowIndicatorSVG)` + pointer-events: none; + position: absolute; + right: ${arrowRightOffset}; + top: 50%; + transform: translateY(-50%); +` + const ArrowIndicator: React.FC<{className?: string}> = ({className}) => { - return <ArrowIndicatorSVG className={clsx(classes.ArrowIndicator, className)} /> + const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + if (enabled) { + return <ArrowIndicatorSVG className={clsx(classes.ArrowIndicator, className)} /> + } + + return <StyledArrowIndicatorSVG /> } const Select = React.forwardRef<HTMLSelectElement, SelectProps>( - ( - {block, children, contrast, disabled, placeholder, size, required, validationStatus, sx, ...rest}: SelectProps, - ref, - ) => { + ({block, children, contrast, disabled, placeholder, size, required, validationStatus, ...rest}: SelectProps, ref) => { + const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + if (enabled) { + return ( + <TextInputWrapper + block={block} + contrast={contrast} + disabled={disabled} + size={size} + validationStatus={validationStatus} + className={classes.TextInputWrapper} + sx={rest.sx} + > + <StyledSelect + ref={ref} + required={required} + disabled={disabled} + aria-invalid={validationStatus === 'error' ? 'true' : 'false'} + data-hasplaceholder={Boolean(placeholder)} + defaultValue={placeholder ?? undefined} + className={clsx(classes.Select, disabled && classes.Disabled)} + {...rest} + > + {placeholder && ( + <option value="" disabled={required} hidden={required}> + {placeholder} + </option> + )} + {children} + </StyledSelect> + <ArrowIndicator className={classes.ArrowIndicator} /> + </TextInputWrapper> + ) + } + return ( <TextInputWrapper + sx={{ + overflow: 'hidden', + position: 'relative', + '@media screen and (forced-colors: active)': { + svg: { + fill: disabled ? 'GrayText' : 'FieldText', + }, + }, + }} block={block} contrast={contrast} disabled={disabled} size={size} validationStatus={validationStatus} - className={classes.TextInputWrapper} - sx={sx} > - <select + <StyledSelect ref={ref} required={required} disabled={disabled} aria-invalid={validationStatus === 'error' ? 'true' : 'false'} data-hasplaceholder={Boolean(placeholder)} defaultValue={placeholder ?? undefined} - className={clsx(classes.Select, disabled && classes.Disabled)} {...rest} > {placeholder && ( @@ -63,8 +164,8 @@ const Select = React.forwardRef<HTMLSelectElement, SelectProps>( </option> )} {children} - </select> - <ArrowIndicator className={classes.ArrowIndicator} /> + </StyledSelect> + <ArrowIndicator /> </TextInputWrapper> ) },