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

LogRock / pebbles / 6235998546

19 Sep 2023 01:15PM UTC coverage: 87.315% (-0.06%) from 87.374%
6235998546

push

github

rodrigosakamoto
feat: update bottom sheet behavior

484 of 644 branches covered (0.0%)

Branch coverage included in aggregate %.

19 of 19 new or added lines in 2 files covered. (100.0%)

5931 of 6703 relevant lines covered (88.48%)

4.0 hits per line

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

89.04
/lib/components/Select2/Select.tsx
1
/* eslint-disable @typescript-eslint/no-explicit-any */
1✔
2
import { BsChevronDown, BsExclamationTriangleFill } from "react-icons/bs";
1✔
3
import uniqueId from "lodash.uniqueid";
1✔
4
import React, { FC, useContext, useMemo } from "react";
1✔
5
import ReactSelect, {
1✔
6
  StylesConfig,
1✔
7
  Theme,
1✔
8
  Props,
1✔
9
  GroupBase,
1✔
10
  components,
1✔
11
  ValueContainerProps,
1✔
12
  SelectComponentsConfig,
1✔
13
} from "react-select";
1✔
14
import styled, { ThemeContext } from "styled-components";
1✔
15
import { HelperDiv, HelperIcon } from "../InputBox/BaseInputBox.styled";
1✔
16
import { CustomSelectProps } from "./Select.types";
1✔
17
import ThemeType from "../../types/theme";
1✔
18
import { DropdownIndicatorWrapper } from "./Select.styled";
1✔
19
import { spacingTokens } from "../../types/tokens";
1✔
20
import { Text } from "../Typography";
1✔
21
import { RequiredAsterisk } from "../InputBox/BaseInputBox";
1✔
22
import SelectBottomSheet from "./SelectBottomSheet";
1✔
23
import { SelectOption } from "./SelectOption";
1✔
24

1✔
25
const { ValueContainer } = components;
1✔
26

1✔
27
export const StyledDiv = styled.div<{ spaceAfter?: spacingTokens }>`
1✔
28
  margin-bottom: ${({ theme, spaceAfter }) =>
1✔
29
    spaceAfter ? theme.spacings?.[spaceAfter as spacingTokens] : 0};
9!
30

1✔
31
  label {
1✔
32
    display: block;
1✔
33
  }
1✔
34

1✔
35
  & ::-webkit-scrollbar {
1✔
36
    width: 12px !important;
1✔
37

1✔
38
    background-color: transparent !important;
1✔
39
  }
1✔
40

1✔
41
  & ::-webkit-scrollbar-track {
1✔
42
    border-radius: 100px !important;
1✔
43

1✔
44
    background-color: ${({ theme }) => theme.colors.neutral[100]} !important;
1✔
45
  }
1✔
46

1✔
47
  & ::-webkit-scrollbar-thumb {
1✔
48
    border: 0px solid transparent !important;
1✔
49
    border-radius: 100px !important;
1✔
50

1✔
51
    background-clip: padding-box !important;
1✔
52
    background-color: ${({ theme }) => theme.colors.neutral[400]} !important;
1✔
53
  }
1✔
54
`;
1✔
55

1✔
56
export const CustomValueContainer: FC<ValueContainerProps> = ({
1✔
57
  children,
135✔
58
  ...props
135✔
59
}) => {
135✔
60
  const { getValue } = props;
135✔
61
  let selectedValues = 0;
135✔
62

135✔
63
  if (Array.isArray(getValue())) {
135✔
64
    selectedValues = getValue().length;
135✔
65
  }
135✔
66

135✔
67
  if (selectedValues > 1) {
135✔
68
    return (
1✔
69
      <ValueContainer {...props}>
1✔
70
        {`${selectedValues} items selected.`}
1✔
71
        {children?.[1 as keyof unknown]}
1✔
72
      </ValueContainer>
1✔
73
    );
1✔
74
  }
1✔
75
  return <ValueContainer {...props}>{children}</ValueContainer>;
135✔
76
};
134✔
77

1✔
78
function CustomChevronIndicator() {
27✔
79
  return (
27✔
80
    <DropdownIndicatorWrapper>
27✔
81
      <BsChevronDown size={16} />
27✔
82
    </DropdownIndicatorWrapper>
27✔
83
  );
27✔
84
}
27✔
85

27✔
86
function Select<
9✔
87
  Option,
9✔
88
  IsMulti extends boolean = false,
9✔
89
  Group extends GroupBase<Option> = GroupBase<Option>
9✔
90
>({
9✔
91
  required,
9✔
92
  helper,
9✔
93
  helperIcon,
9✔
94
  label,
9✔
95
  destructive,
9✔
96
  spaceAfter,
9✔
97
  useBottomSheet,
9✔
98
  bottomSheetFooter,
9✔
99
  bottomSheetHeader,
9✔
100
  bottomSheetPlaceholder,
9✔
101
  ...props
9✔
102
}: Props<Option, IsMulti, Group> & CustomSelectProps) {
9✔
103
  // only used for bottom sheet mode
9✔
104
  const [menuIsOpen, setMenuIsOpen] = React.useState(false);
9✔
105

9✔
106
  const theme = useContext<ThemeType>(ThemeContext);
9✔
107

9✔
108
  const customStyles: StylesConfig<Option, IsMulti, Group> = useMemo(
9✔
109
    () => ({
9✔
110
      control: (provided, state) => {
5✔
111
        return {
27✔
112
          ...provided,
27✔
113
          outline: state.isFocused
27!
114
            ? destructive
×
115
              ? `0px 0px 0px 3px ${theme.colors.destructive[100]}`
×
116
              : `0px 0px 0px 3px ${theme.colors.primary[100]}`
×
117
            : "none",
27✔
118
          boxShadow: state.isFocused
27!
119
            ? destructive
×
120
              ? `0px 0px 0px 3px ${theme.colors.destructive[100]}`
×
121
              : `0px 0px 0px 3px ${theme.colors.primary[100]}`
×
122
            : theme.inputBox[destructive ? "destructive" : "info"].boxShadow,
27!
123
          border: state.isFocused
27!
124
            ? theme.inputBox[destructive ? "destructive" : "info"].focused
×
125
                .border
×
126
            : theme.inputBox[destructive ? "destructive" : "info"].border,
27!
127
          fontFamily: theme.typography.paragraphMedium.fontFamily,
27✔
128
          fontWeight: theme.typography.paragraphMedium.desktop.weights.normal,
27✔
129
          fontSize: theme.typography.paragraphMedium.desktop.fontSize,
27✔
130
        };
27✔
131
      },
27✔
132
      indicatorSeparator: (provided) => ({
5✔
133
        ...provided,
27✔
134
        margin: 0,
27✔
135
        backgroundColor: destructive
27!
136
          ? theme.colors.destructive[400]
×
137
          : theme.colors.neutral[200],
27✔
138
      }),
5✔
139
      option: (provided) => ({
5✔
140
        ...provided,
65✔
141
        fontFamily: theme.typography.paragraphMedium.fontFamily,
65✔
142
        fontWeight: theme.typography.paragraphMedium.desktop.weights.normal,
65✔
143
        fontSize: theme.typography.paragraphMedium.desktop.fontSize,
65✔
144
        padding: theme.inputBox.padding,
65✔
145
      }),
5✔
146
      menu: (provided) => ({
5✔
147
        ...provided,
16✔
148
        borderRadius: 0,
16✔
149
        padding: "12px",
16✔
150
      }),
5✔
151
      placeholder: (provided) => ({
5✔
152
        ...provided,
9✔
153
        color: theme.colors.neutral[400],
9✔
154
      }),
9✔
155
    }),
9✔
156
    []
9✔
157
  );
9✔
158

9✔
159
  const selectTheme: Theme = useMemo(
9✔
160
    () => ({
9✔
161
      borderRadius: theme.inputBox.borderRadius as number,
5✔
162
      colors: {
5✔
163
        danger: theme.colors.destructive[500],
5✔
164
        dangerLight: theme.colors.destructive[300],
5✔
165
        neutral0: "white",
5✔
166
        neutral5: theme.colors.neutral[50],
5✔
167
        neutral10: theme.colors.neutral[100],
5✔
168
        neutral20: theme.colors.neutral[200],
5✔
169
        neutral30: theme.colors.neutral[300],
5✔
170
        neutral40: theme.colors.neutral[400],
5✔
171
        neutral50: theme.colors.neutral[500],
5✔
172
        neutral60: theme.colors.neutral[600],
5✔
173
        neutral70: theme.colors.neutral[700],
5✔
174
        neutral80: theme.colors.neutral[800],
5✔
175
        neutral90: theme.colors.neutral[900],
5✔
176
        primary: theme.colors.primary[500],
5✔
177
        primary25: theme.colors.primary[50],
5✔
178
        primary50: theme.colors.primary[500],
5✔
179
        primary75: theme.colors.primary[700],
5✔
180
      },
5✔
181
      spacing: {
5✔
182
        baseUnit: 4,
9✔
183
        controlHeight: 40,
9✔
184
        menuGutter: helper ? 24 : 3,
9!
185
      },
9✔
186
    }),
9✔
187
    [helper]
9✔
188
  );
9✔
189

9✔
190
  const selectID = uniqueId("pebbles_select");
9✔
191

9✔
192
  const customComponents: SelectComponentsConfig<Option, IsMulti, Group> =
9✔
193
    useMemo(() => {
9✔
194
      const components = { ...props.components } || {};
5✔
195

5✔
196
      components.ValueContainer = CustomValueContainer as any;
5✔
197
      components.DropdownIndicator = CustomChevronIndicator as any;
5✔
198

5✔
199
      if (useBottomSheet) {
5!
200
        components.Menu = SelectBottomSheet as any;
×
201
        components.Option = props.components?.Option || (SelectOption as any);
×
202
      }
×
203

×
204
      return components;
5✔
205
    }, [props.components, useBottomSheet, label]);
9✔
206

9✔
207
  return (
9✔
208
    <StyledDiv spaceAfter={spaceAfter}>
9✔
209
      <Text
9✔
210
        as="label"
9✔
211
        htmlFor={selectID}
9✔
212
        spaceAfter="xsm"
9✔
213
        type="overlineXSmall"
9✔
214
      >
9✔
215
        {label} {required && <RequiredAsterisk />}
9!
216
      </Text>
9✔
217
      <ReactSelect<Option, IsMulti, Group>
9✔
218
        {...props}
9✔
219
        required={required}
9✔
220
        styles={customStyles}
9✔
221
        theme={selectTheme}
9✔
222
        inputId={selectID}
9✔
223
        components={customComponents}
9✔
224
        menuIsOpen={useBottomSheet ? menuIsOpen : props.menuIsOpen}
9!
225
        openMenuOnClick={useBottomSheet ? true : props.openMenuOnClick}
9!
226
        onMenuOpen={() => {
9✔
227
          setMenuIsOpen(true);
7✔
228
          props.onMenuOpen?.();
7✔
229
        }}
7✔
230
        onFocus={(e) => {
9✔
231
          props.onFocus?.(e);
×
232

×
233
          if (useBottomSheet) {
×
234
            e.currentTarget.blur();
×
235
          }
×
236
        }}
×
237
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
9✔
238
        // @ts-ignore
9✔
239
        maxMenuHeight={useBottomSheet ? "100%" : 200}
9!
240
        label={label}
9✔
241
        setMenuIsOpen={setMenuIsOpen}
9✔
242
        bottomSheetHeader={bottomSheetHeader}
9✔
243
        bottomSheetFooter={bottomSheetFooter}
9✔
244
        bottomSheetPlaceholder={bottomSheetPlaceholder}
9✔
245
      />
9✔
246
      {helper && (
9!
247
        <HelperDiv>
9✔
248
          {destructive && !helperIcon && (
9✔
249
            <HelperIcon destructive={destructive}>
9✔
250
              <BsExclamationTriangleFill />
9✔
251
            </HelperIcon>
9✔
252
          )}
9✔
253
          {helperIcon && (
9✔
254
            <HelperIcon destructive={destructive}>{helperIcon}</HelperIcon>
9✔
255
          )}
9✔
256
          <Text
9✔
257
            type="paragraphSmall"
9✔
258
            color={destructive ? "destructive" : "neutral"}
9✔
259
            shade="400"
9✔
260
          >
9✔
261
            {helper}
9✔
262
          </Text>
9✔
263
        </HelperDiv>
9✔
264
      )}
9✔
265
    </StyledDiv>
9✔
266
  );
9✔
267
}
9✔
268

9✔
269
export default Select;
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

© 2025 Coveralls, Inc