• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zafle / select_menu / 13996261820

21 Mar 2025 04:22PM UTC coverage: 94.76% (-0.9%) from 95.652%
13996261820

push

github

zafle
fix script bug to run vitest test and coverage (add --config)

117 of 144 branches covered (81.25%)

Branch coverage included in aggregate %.

950 of 982 relevant lines covered (96.74%)

42.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.62
/lib/components/SelectComponent/SelectComponent.jsx
1
import { useRef, useEffect } from 'react'
1✔
2

3
// components
4
import Dropdown from '../Dropdown/Dropdown'
1✔
5
import SelectInput from '../SelectInput/SelectInput'
1✔
6
// contexts
7
import useConfig from '../../context/hook/useConfig'
1✔
8
import useSelect from '../../context/hook/useSelect'
1✔
9
// hook
10
import useOnClickOutside from '../../hooks/useOnClickOutside'
1✔
11
// proptypes and css
12
import PropTypes from 'prop-types'
1✔
13
import styles from './SelectComponent.module.css'
1✔
14

15
/**
16
 * SelectComponent - A custom select dropdown component.
17
 *
18
 * @param {Object} props SelectComponent props
19
 *
20
 * An object containing all custom config options :
21
 * @param {Object} props.props
22
 *
23
 * ID for hidden input in SelectInput that will receive the selected option value
24
 * (must be === htmlFor label attribute) :
25
 * @param {string} props.props.id
26
 *
27
 * Name for hidden input in SelectInput that will receive the selected option value :
28
 * @param {string} props.props.name
29
 *
30
 * Select's label ID - used in .select-input aria-labelledBy :
31
 * @param {string} props.props.labelId
32
 *
33
 * ######### REQUIRED #########
34
 * Options'data array :
35
 * @param {Array<string|Object>} props.props.options
36
 *
37
 * Callback function triggered when an option is selected
38
 * (parameter = option selected value)
39
 * - DEFAULT = null
40
 * @param {Function} props.props.onChangeValue
41
 *
42
 * New in V2
43
 * selectedOption is the state given by user that is changed with onChangeValue
44
 * @param {string} props.props.selectedOption
45
 *
46
 * Defines default selected option :
47
 * - POSSIBLE VALUES = 'text of the default selected option' | 'first'  | undefined
48
 * - DEFAULT = undefined
49
 * @param {string} props.props.defaultSelectedOption
50
 *
51
 * ######### REQUIRED IF OPTIONS WITH VALUES #########
52
 * Name of the property for option's text in the options data's array :
53
 * @param {string} props.props.textField
54
 *
55
 * ######### REQUIRED IF OPTIONS WITH VALUES #########
56
 * Name of the property for option's value in the options data's array :
57
 * @param {string} props.props.valueField
58
 *
59
 * ######### REQUIRED IF OPTIONS WITH OPTGROUPS #########
60
 * Name of the property for optgroup label in the options data's array :
61
 * @param {string} props.props.optGroupLabelField
62
 *
63
 * ######### REQUIRED IF OPTIONS WITH OPTGROUPS #########
64
 * Name of the property for optgroup's options array in the options data's array :
65
 * @param {string} props.props.optGroupOptionsField
66
 *
67
 * SelectComponent max-width (e.g. "300px")
68
 * - DEFAULT = "250px" :
69
 * @param {string} props.props.maxWidth
70
 *
71
 * SelectInput and Dropdown borders
72
 * - POSSIBLE VALUES = 'unset' | e.g. '2px solid blue'
73
 * - DEFAULT = '1px solid #2b2b2b'
74
 * @param {string} props.props.border
75
 *
76
 * SelectInput and Dropdown border-radius
77
 * - POSSIBLE VALUES = 'unset' | e.g. '10px'
78
 * - DEFAULT = '4px'
79
 * @param {string} props.props.borderRadius
80
 *
81
 * SelectComponent margin
82
 * - DEFAULT = '0'
83
 * @param {string} props.props.containerMargin
84
 *
85
 * SelectInput and Dropdown shadow
86
 * - POSSIBLE VALUES = 'unset' | e.g. '4px 4px 10px black'
87
 * - DEFAULT = '4px 4px 10px rgba(0, 0, 0, 0.4)'
88
 * @param {string} props.props.boxShadow
89
 *
90
 * SelectInput and Dropdown shadow if open
91
 * true = box shadow will display only when dropdown is opened
92
 * false = box shadow will always display
93
 * - DEFAULT = false
94
 * @param {boolean} props.props.boxShadowOnOpen
95
 *
96
 * Set color on :focus-visible for focusable elements (SelectInput, clear selection button, options)
97
 * - POSSIBLE VALUES = 'none' = no color | 'default' = default browser color | custom color e.g. 'red'
98
 * - DEFAULT = 'default'
99
 * @param {string} props.props.colorOnFocus
100
 *
101
 * SelectInput height css property
102
 * - DEFAULT = 'unset'
103
 * @param {string} props.props.inputHeight
104
 *
105
 * SelectInput background css property
106
 * - DEFAULT = '#d5d5d5'
107
 * @param {string} props.props.inputBackground
108
 *
109
 * SelectInput color css property
110
 * - DEFAULT = 'inherit'
111
 * @param {string} props.props.inputTextColor
112
 *
113
 * SelectInput vertical padding css property
114
 * - DEFAULT = '8px'
115
 * @param {string} props.props.inputVerticalPadding
116
 *
117
 * SelectInput horizontal padding css property
118
 * - DEFAULT = '10px'
119
 * @param {string} props.props.inputHorizontalPadding
120
 *
121
 * SelectInput font-size css property
122
 * - DEFAULT = '16px'
123
 * @param {string} props.props.inputFontSize
124
 *
125
 * Dropdown background css property
126
 * - DEFAULT = 'white'
127
 * @param {string} props.props.dropdownBackground
128
 *
129
 * Dropdown max-height css property
130
 * - DEFAULT = 'unset'
131
 * @param {string} props.props.dropdownMaxHeight
132
 *
133
 * Dropdown vertical padding css property
134
 * - DEFAULT = '8px'
135
 * @param {string} props.props.dropdownVerticalPadding
136
 *
137
 * Dropdown position
138
 * - POSSIBLE VALUES = 'bottom' | 'top'
139
 * - DEFAULT = 'bottom'
140
 * @param {string} props.props.dropdownPosition
141
 *
142
 * Option color css property
143
 * - DEFAULT = 'inherit'
144
 * @param {string} props.props.optionTextColor
145
 *
146
 * Option background css property on hover
147
 * - DEFAULT = '#484848'
148
 * @param {string} props.props.hoveredOptionBackground
149
 *
150
 * Option color css property on hover
151
 * - DEFAULT = 'white'
152
 * @param {string} props.props.hoveredOptionTextColor
153
 *
154
 * Option vertical padding css property
155
 * - DEFAULT = '4px'
156
 * @param {string} props.props.optionVerticalPadding
157
 *
158
 * Option horizontal padding css property
159
 * - DEFAULT = '14px'
160
 * @param {string} props.props.optionHorizontalPadding
161
 *
162
 * Option font-size css property
163
 * - DEFAULT = '14px'
164
 * @param {string} props.props.optionFontSize
165
 *
166
 * OptGroup label color css property on hover
167
 * - DEFAULT = 'inherit'
168
 * @param {string} props.props.optGroupLabelTextColor
169
 *
170
 * OptGroup font-size css property
171
 * - DEFAULT = '16px'
172
 * @param {string} props.props.optGroupLabelFontSize
173
 *
174
 * OptGroup vertical padding css property
175
 * - DEFAULT = '4px'
176
 * @param {string} props.props.optGroupVerticalPadding
177
 *
178
 * OptGroup horizontal padding css property
179
 * - DEFAULT = '10px'
180
 * @param {string} props.props.optGroupHorizontalPadding
181
 *
182
 * OptGroup margin-top css property
183
 * - DEFAULT = '2px'
184
 * @param {string} props.props.optGroupMarginTop
185
 *
186
 * @returns {React.ReactElement} SelectComponent
187
 */
188
export default function SelectComponent({ props }) {
51✔
189
  // get select and config context
190
  const { isOpen, toggleIsOpen } = useSelect()
51✔
191

192
  const {
51✔
193
    isSet,
51✔
194
    id,
51✔
195
    maxWidth,
51✔
196
    containerMargin,
51✔
197
    colorOnFocus,
51✔
198
    classOnFocus,
51✔
199
    defineConfig,
51✔
200
  } = useConfig()
51✔
201

202
  // set SelectComponent ref
203
  const selectComponentRef = useRef(null)
51✔
204

205
  // set custom config
206
  useEffect(() => {
51✔
207
    if (!isSet) {
32✔
208
      defineConfig({ ...props })
16✔
209
    }
16✔
210
  }, [props, isSet, defineConfig])
51✔
211

212
  // function to close Dropdown
213
  const closeDropdown = () => {
51✔
214
    isOpen && toggleIsOpen()
×
215
  }
×
216

217
  // hook to close dropdown on click outside
218
  useOnClickOutside(selectComponentRef, closeDropdown)
51✔
219

220
  const handleKeyDown = (e) => {
51✔
221
    if (e.key === 'Escape') {
12!
222
      e.preventDefault()
×
223
      closeDropdown()
×
224
    }
×
225
  }
12✔
226

227
  const containerStyle = isSet && {
51✔
228
    maxWidth: maxWidth,
35✔
229
    margin: containerMargin,
35✔
230
    '--outline-focus-visible-color':
35✔
231
      classOnFocus === 'hasCustomFocusVisibleColor' ? colorOnFocus : undefined,
35!
232
  }
35✔
233

234
  if (isSet) {
51✔
235
    return (
35✔
236
      <div
35✔
237
        id={`${id}_select-container`}
35✔
238
        ref={selectComponentRef}
35✔
239
        className={`select-container ${styles.selectContainer}`}
35✔
240
        style={containerStyle}
35✔
241
        onKeyDown={(e) => {
35✔
242
          handleKeyDown(e)
12✔
243
        }}
12✔
244
      >
245
        <SelectInput selectedOption={props.selectedOption} />
35✔
246
        <Dropdown />
35✔
247
      </div>
35✔
248
    )
249
  }
35✔
250
}
51✔
251
SelectComponent.propTypes = {
1✔
252
  // New in V2
253
  // selectedOption is the state given by user that is changed with onChangeValue
254
  selectedOption: PropTypes.string,
1✔
255
  //
256
  props: PropTypes.shape({
1✔
257
    id: PropTypes.string,
1✔
258
    name: PropTypes.string,
1✔
259
    labelId: PropTypes.string,
1✔
260
    options: PropTypes.arrayOf(
1✔
261
      PropTypes.oneOfType([
1✔
262
        // without values
263
        PropTypes.string,
1✔
264
        // with values
265
        PropTypes.shape({
1✔
266
          // textField
267
          [PropTypes.string]: PropTypes.string,
1✔
268
          // valueField
269
          [PropTypes.string]: PropTypes.string,
1✔
270
        }),
1✔
271
        // optgroups without values
272
        PropTypes.shape({
1✔
273
          // labelField
274
          [PropTypes.string]: PropTypes.string,
1✔
275
          // options array
276
          [PropTypes.string]: PropTypes.arrayOf(PropTypes.string),
1✔
277
        }),
1✔
278
        //optgroup with values
279
        PropTypes.shape({
1✔
280
          // labelField
281
          [PropTypes.string]: PropTypes.string,
1✔
282
          // options array
283
          [PropTypes.string]: PropTypes.arrayOf(
1✔
284
            PropTypes.shape({
1✔
285
              // textField
286
              [PropTypes.string]: PropTypes.string,
1✔
287
              // valueField
288
              [PropTypes.string]: PropTypes.string,
1✔
289
            })
1✔
290
          ),
1✔
291
        }),
1✔
292
      ])
1✔
293
    ).isRequired,
1✔
294
    onChangeValue: PropTypes.func,
1✔
295
    // New in V2
296
    // selectedOption is the state given by user that is changed with onChangeValue
297
    selectedOption: PropTypes.string,
1✔
298
    defaultSelectedOption: PropTypes.string,
1✔
299
    textField: PropTypes.string,
1✔
300
    valueField: PropTypes.string,
1✔
301
    optGroupLabelField: PropTypes.string,
1✔
302
    optGroupOptionsField: PropTypes.string,
1✔
303
    maxWidth: PropTypes.string,
1✔
304
    border: PropTypes.string,
1✔
305
    borderRadius: PropTypes.string,
1✔
306
    containerMargin: PropTypes.string,
1✔
307
    boxShadow: PropTypes.string,
1✔
308
    boxShadowOnOpen: PropTypes.bool,
1✔
309
    colorOnFocus: PropTypes.string,
1✔
310
    inputHeight: PropTypes.string,
1✔
311
    inputBackground: PropTypes.string,
1✔
312
    inputTextColor: PropTypes.string,
1✔
313
    inputVerticalPadding: PropTypes.string,
1✔
314
    inputHorizontalPadding: PropTypes.string,
1✔
315
    inputFontSize: PropTypes.string,
1✔
316
    dropdownBackground: PropTypes.string,
1✔
317
    dropdownMaxHeight: PropTypes.string,
1✔
318
    dropdownVerticalPadding: PropTypes.string,
1✔
319
    dropdownPosition: PropTypes.string,
1✔
320
    optionTextColor: PropTypes.string,
1✔
321
    hoveredOptionBackground: PropTypes.string,
1✔
322
    hoveredOptionTextColor: PropTypes.string,
1✔
323
    optionVerticalPadding: PropTypes.string,
1✔
324
    optionHorizontalPadding: PropTypes.string,
1✔
325
    optionFontSize: PropTypes.string,
1✔
326
    optGroupLabelTextColor: PropTypes.string,
1✔
327
    optGroupLabelFontSize: PropTypes.string,
1✔
328
    optGroupVerticalPadding: PropTypes.string,
1✔
329
    optGroupHorizontalPadding: PropTypes.string,
1✔
330
    optGroupMarginTop: PropTypes.string,
1✔
331
  }),
1✔
332
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc