1
1
import React from 'react'
2
2
import styled from 'styled-components'
3
+ import { clsx } from 'clsx'
3
4
import type { StyledWrapperProps } from '../internal/components/TextInputWrapper'
4
5
import TextInputWrapper from '../internal/components/TextInputWrapper'
6
+ import { toggleStyledComponent } from '../internal/utils/toggleStyledComponent'
7
+ import { useFeatureFlag } from '../FeatureFlags'
8
+ import classes from './Select.module.css'
5
9
6
10
export type SelectProps = Omit <
7
11
Omit < React . ComponentPropsWithoutRef < 'select' > , 'size' > & Omit < StyledWrapperProps , 'variant' > ,
@@ -10,106 +14,159 @@ export type SelectProps = Omit<
10
14
placeholder ?: string
11
15
}
12
16
13
- const arrowRightOffset = '4px '
17
+ const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team '
14
18
15
- const StyledSelect = styled . select `
16
- appearance: none;
17
- border-radius: inherit;
18
- border: 0;
19
- color: currentColor;
20
- font-size: inherit;
21
- outline: none;
22
- width: 100%;
19
+ const arrowRightOffset = '4px'
23
20
24
- /* Firefox hacks: */
25
- /* 1. Makes Firefox's native dropdown menu's background match the theme.
21
+ const StyledSelect = toggleStyledComponent (
22
+ CSS_MODULES_FEATURE_FLAG ,
23
+ 'select' ,
24
+ styled . select `
25
+ appearance: none;
26
+ border-radius: inherit;
27
+ border: 0;
28
+ color: currentColor;
29
+ font-size: inherit;
30
+ outline: none;
31
+ width: 100%;
32
+
33
+ /* Firefox hacks: */
34
+ /* 1. Makes Firefox's native dropdown menu's background match the theme.
26
35
27
36
background-color should be 'transparent', but Firefox uses the background-color on
28
37
<select> to determine the background color used for the dropdown menu.
29
38
30
39
2. Adds 1px margins to the <select> so the background color doesn't hide the focus outline created with an inset box-shadow.
31
40
*/
32
- background-color: inherit;
33
- margin-top: 1px;
34
- margin-left: 1px;
35
- margin-bottom: 1px;
41
+ background-color: inherit;
42
+ margin-top: 1px;
43
+ margin-left: 1px;
44
+ margin-bottom: 1px;
36
45
37
- /* 2. Prevents visible overlap of partially transparent background colors.
46
+ /* 2. Prevents visible overlap of partially transparent background colors.
38
47
39
48
'colors.input.disabledBg' happens to be partially transparent in light mode, so we use a
40
49
transparent background-color on a disabled <select>. */
41
- &:disabled {
42
- background-color: transparent;
43
- }
50
+ &:disabled {
51
+ background-color: transparent;
52
+ }
44
53
45
- /* 3. Maintain dark bg color in Firefox on Windows high-contrast mode
54
+ /* 3. Maintain dark bg color in Firefox on Windows high-contrast mode
46
55
47
56
Firefox makes the <select>'s background color white when setting 'background-color: transparent;' */
48
- @media screen and (forced-colors: active) {
49
- &:disabled {
50
- background-color: -moz-combobox;
57
+ @media screen and (forced-colors: active) {
58
+ &:disabled {
59
+ background-color: -moz-combobox;
60
+ }
51
61
}
52
- }
53
- `
54
-
55
- const ArrowIndicatorSVG : React . FC < React . PropsWithChildren < { className ?: string } > > = ( { className} ) => (
56
- < svg
57
- aria-hidden = "true"
58
- width = "16"
59
- height = "16"
60
- fill = "currentColor"
61
- xmlns = "http://www.w3.org/2000/svg"
62
- className = { className }
63
- >
64
- < path d = "m4.074 9.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.043 9H4.251a.25.25 0 0 0-.177.427ZM4.074 7.47 7.47 4.073a.25.25 0 0 1 .354 0L11.22 7.47a.25.25 0 0 1-.177.426H4.251a.25.25 0 0 1-.177-.426Z" />
65
- </ svg >
62
+ ` ,
66
63
)
67
64
68
- const ArrowIndicator = styled ( ArrowIndicatorSVG ) `
65
+ const ArrowIndicatorSVG : React . FC < React . PropsWithChildren < { className ?: string } > > = ( { className} ) => {
66
+ return (
67
+ < svg
68
+ aria-hidden = "true"
69
+ width = "16"
70
+ height = "16"
71
+ fill = "currentColor"
72
+ xmlns = "http://www.w3.org/2000/svg"
73
+ className = { className }
74
+ >
75
+ < path d = "m4.074 9.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.043 9H4.251a.25.25 0 0 0-.177.427ZM4.074 7.47 7.47 4.073a.25.25 0 0 1 .354 0L11.22 7.47a.25.25 0 0 1-.177.426H4.251a.25.25 0 0 1-.177-.426Z" />
76
+ </ svg >
77
+ )
78
+ }
79
+
80
+ const StyledArrowIndicatorSVG = styled ( ArrowIndicatorSVG ) `
69
81
pointer-events: none;
70
82
position: absolute;
71
83
right: ${ arrowRightOffset } ;
72
84
top: 50%;
73
85
transform: translateY(-50%);
74
86
`
75
87
88
+ const ArrowIndicator : React . FC < { className ?: string } > = ( { className} ) => {
89
+ const enabled = useFeatureFlag ( CSS_MODULES_FEATURE_FLAG )
90
+ if ( enabled ) {
91
+ return < ArrowIndicatorSVG className = { clsx ( classes . ArrowIndicator , className ) } />
92
+ }
93
+
94
+ return < StyledArrowIndicatorSVG />
95
+ }
96
+
76
97
const Select = React . forwardRef < HTMLSelectElement , SelectProps > (
77
- ( { block, children, contrast, disabled, placeholder, size, required, validationStatus, ...rest } : SelectProps , ref ) => (
78
- < TextInputWrapper
79
- sx = { {
80
- overflow : 'hidden' ,
81
- position : 'relative' ,
82
- '@media screen and (forced-colors: active)' : {
83
- svg : {
84
- fill : disabled ? 'GrayText' : 'FieldText' ,
98
+ ( { block, children, contrast, disabled, placeholder, size, required, validationStatus, ...rest } : SelectProps , ref ) => {
99
+ const enabled = useFeatureFlag ( CSS_MODULES_FEATURE_FLAG )
100
+ if ( enabled ) {
101
+ return (
102
+ < TextInputWrapper
103
+ block = { block }
104
+ contrast = { contrast }
105
+ disabled = { disabled }
106
+ size = { size }
107
+ validationStatus = { validationStatus }
108
+ className = { classes . TextInputWrapper }
109
+ sx = { rest . sx }
110
+ >
111
+ < StyledSelect
112
+ ref = { ref }
113
+ required = { required }
114
+ disabled = { disabled }
115
+ aria-invalid = { validationStatus === 'error' ? 'true' : 'false' }
116
+ data-hasplaceholder = { Boolean ( placeholder ) }
117
+ defaultValue = { placeholder ?? undefined }
118
+ className = { clsx ( classes . Select , disabled && classes . Disabled ) }
119
+ { ...rest }
120
+ >
121
+ { placeholder && (
122
+ < option value = "" disabled = { required } hidden = { required } >
123
+ { placeholder }
124
+ </ option >
125
+ ) }
126
+ { children }
127
+ </ StyledSelect >
128
+ < ArrowIndicator className = { classes . ArrowIndicator } />
129
+ </ TextInputWrapper >
130
+ )
131
+ }
132
+
133
+ return (
134
+ < TextInputWrapper
135
+ sx = { {
136
+ overflow : 'hidden' ,
137
+ position : 'relative' ,
138
+ '@media screen and (forced-colors: active)' : {
139
+ svg : {
140
+ fill : disabled ? 'GrayText' : 'FieldText' ,
141
+ } ,
85
142
} ,
86
- } ,
87
- } }
88
- block = { block }
89
- contrast = { contrast }
90
- disabled = { disabled }
91
- size = { size }
92
- validationStatus = { validationStatus }
93
- >
94
- < StyledSelect
95
- ref = { ref }
96
- required = { required }
143
+ } }
144
+ block = { block }
145
+ contrast = { contrast }
97
146
disabled = { disabled }
98
- aria-invalid = { validationStatus === 'error' ? 'true' : 'false' }
99
- data-hasplaceholder = { Boolean ( placeholder ) }
100
- defaultValue = { placeholder ?? undefined }
101
- { ...rest }
147
+ size = { size }
148
+ validationStatus = { validationStatus }
102
149
>
103
- { placeholder && (
104
- < option value = "" disabled = { required } hidden = { required } >
105
- { placeholder }
106
- </ option >
107
- ) }
108
- { children }
109
- </ StyledSelect >
110
- < ArrowIndicator />
111
- </ TextInputWrapper >
112
- ) ,
150
+ < StyledSelect
151
+ ref = { ref }
152
+ required = { required }
153
+ disabled = { disabled }
154
+ aria-invalid = { validationStatus === 'error' ? 'true' : 'false' }
155
+ data-hasplaceholder = { Boolean ( placeholder ) }
156
+ defaultValue = { placeholder ?? undefined }
157
+ { ...rest }
158
+ >
159
+ { placeholder && (
160
+ < option value = "" disabled = { required } hidden = { required } >
161
+ { placeholder }
162
+ </ option >
163
+ ) }
164
+ { children }
165
+ </ StyledSelect >
166
+ < ArrowIndicator />
167
+ </ TextInputWrapper >
168
+ )
169
+ } ,
113
170
)
114
171
115
172
const Option : React . FC < React . PropsWithChildren < React . HTMLProps < HTMLOptionElement > & { value : string } > > = props => (
0 commit comments