@bitrise/bitkit 13.266.0 → 13.267.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit",
3
3
  "description": "Bitrise React component library",
4
- "version": "13.266.0",
4
+ "version": "13.267.1-alpha.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+ssh://git@github.com/bitrise-io/bitkit.git"
@@ -34,14 +34,14 @@
34
34
  "@chakra-ui/theme-tools": "^2.2.8",
35
35
  "@chakra-ui/utils": "^2.2.4",
36
36
  "@emotion/react": "^11.14.0",
37
- "@emotion/styled": "^11.14.0",
37
+ "@emotion/styled": "^11.14.1",
38
38
  "@floating-ui/react-dom-interactions": "^0.8.1",
39
39
  "@fontsource/figtree": "^5.2.8",
40
- "@fontsource/source-code-pro": "^5.2.5",
41
- "framer-motion": "^12.5.0",
40
+ "@fontsource/source-code-pro": "^5.2.6",
41
+ "framer-motion": "^12.20.4",
42
42
  "luxon": "^3.6.1",
43
- "react": "^18.3.1",
44
- "react-dom": "^18.3.1",
43
+ "react": "^19.1.0",
44
+ "react-dom": "^19.1.0",
45
45
  "react-focus-lock": "2.13.6",
46
46
  "react-imask": "^7.6.1",
47
47
  "react-markdown": "^10.1.0"
@@ -51,44 +51,39 @@
51
51
  "react-dom": "^18.2.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@babel/core": "^7.27.1",
54
+ "@babel/core": "^7.27.7",
55
55
  "@babel/preset-env": "^7.27.2",
56
56
  "@babel/preset-react": "^7.27.1",
57
57
  "@babel/preset-typescript": "^7.27.1",
58
58
  "@bitrise/eslint-plugin": "^2.12.0",
59
59
  "@chakra-ui/cli": "^2.5.8",
60
60
  "@google-cloud/storage": "^7.16.0",
61
- "@storybook/addon-actions": "^8.6.14",
62
- "@storybook/addon-essentials": "^8.6.14",
63
- "@storybook/addon-interactions": "^8.6.14",
64
- "@storybook/addon-links": "^8.6.14",
61
+ "@storybook/addon-docs": "^9.0.15",
62
+ "@storybook/addon-links": "^9.0.15",
65
63
  "@storybook/addon-webpack5-compiler-swc": "^3.0.0",
66
- "@storybook/blocks": "^8.6.14",
67
- "@storybook/react": "^8.6.14",
68
- "@storybook/react-webpack5": "^8.6.14",
69
- "@storybook/theming": "^8.6.14",
64
+ "@storybook/react-webpack5": "^9.0.15",
70
65
  "@testing-library/dom": "^10.4.0",
71
66
  "@testing-library/jest-dom": "6.6.3",
72
67
  "@testing-library/react": "16.3.0",
73
68
  "@testing-library/user-event": "^14.6.1",
74
- "@types/jest": "^29.5.14",
69
+ "@types/jest": "^30.0.0",
75
70
  "@types/luxon": "^3.6.2",
76
- "@types/react": "^18.3.18",
77
- "@types/react-dom": "^18.3.5",
71
+ "@types/react": "^19.1.8",
72
+ "@types/react-dom": "^19.1.6",
78
73
  "@typescript-eslint/eslint-plugin": "^7.18.0",
79
74
  "@typescript-eslint/parser": "^7.18.0",
80
- "axios": "^1.9.0",
75
+ "axios": "^1.10.0",
81
76
  "eslint": "^8.57.1",
82
- "glob": "^11.0.2",
83
- "jest": "^29.7.0",
84
- "jest-environment-jsdom": "^29.7.0",
77
+ "glob": "^11.0.3",
78
+ "jest": "^30.0.3",
79
+ "jest-environment-jsdom": "^30.0.2",
85
80
  "jsdom": "26.1.0",
86
81
  "lodash": "^4.17.21",
87
- "prettier": "^3.5.3",
88
- "react-hook-form": "^7.56.4",
89
- "release-it": "^19.0.2",
90
- "storybook": "^8.6.14",
91
- "ts-jest": "^29.3.4",
82
+ "prettier": "^3.6.2",
83
+ "react-hook-form": "^7.59.0",
84
+ "release-it": "^19.0.3",
85
+ "storybook": "^9.0.15",
86
+ "ts-jest": "^29.4.0",
92
87
  "typescript": "^5.8.3"
93
88
  },
94
89
  "files": [
@@ -87,7 +87,7 @@ const DatePickerDayView = ({
87
87
  const [DatePickerDayContext, useDatePickerDayContext] = createContext<Context>();
88
88
  export { DatePickerDayContext };
89
89
 
90
- const DatePickerDay = ({ n }: { n: number }): JSX.Element | null => {
90
+ const DatePickerDay = ({ n }: { n: number }) => {
91
91
  const {
92
92
  onPreview,
93
93
  onSelect,
@@ -57,7 +57,7 @@ const DatePickerHeader = ({
57
57
  onNext: () => void;
58
58
  controls?: 'left' | 'right' | 'both';
59
59
  children?: ReactNode;
60
- }): JSX.Element => {
60
+ }) => {
61
61
  const ctx = useObjectMemo({ controls, onNext, onPrevious });
62
62
  return (
63
63
  <Box
@@ -62,7 +62,7 @@ const DatePickerMonthSelector = ({
62
62
  }: {
63
63
  viewDate: DateTime;
64
64
  onMonthSelected: (month: number, year: number) => void;
65
- }): JSX.Element => {
65
+ }) => {
66
66
  const [selectedYear, setSelectedYear] = useState(viewDate.year);
67
67
  const onPreviousYear = useCallback(() => setSelectedYear((year) => year - 1), []);
68
68
  const onNextYear = useCallback(() => setSelectedYear((year) => year + 1), []);
@@ -1,8 +1,15 @@
1
1
  import React, {
2
+ Children,
2
3
  cloneElement,
3
4
  createContext,
4
5
  forwardRef,
6
+ ForwardRefExoticComponent,
7
+ isValidElement,
8
+ JSX,
5
9
  ReactNode,
10
+ Ref,
11
+ RefAttributes,
12
+ RefObject,
6
13
  useCallback,
7
14
  useContext,
8
15
  useEffect,
@@ -93,23 +100,24 @@ export { DropdownOption, DropdownGroup, DropdownSearch, NoResultsFound, Dropdown
93
100
 
94
101
  function useOptionListWithIndexes({ children }: { children: ReactNode }) {
95
102
  return useMemo(() => {
96
- const childList = React.Children.toArray(children);
103
+ const childList = Children.toArray(children);
97
104
  let idx = 0;
98
105
  const transform = (ch: ReactNode): ReactNode => {
99
- if (React.isValidElement(ch)) {
106
+ if (isValidElement(ch)) {
100
107
  if (ch.type === DropdownOption || ch.type === DropdownDetailedOption) {
101
108
  const index = idx;
102
109
  idx += 1;
103
110
  return cloneElement(ch, {
104
- ...ch.props,
111
+ ...(ch.props || {}),
105
112
  index,
106
- });
113
+ } as any);
107
114
  }
108
- if ('children' in ch.props) {
115
+ if ('children' in (ch.props as any)) {
109
116
  return cloneElement(ch, {
110
- ...ch.props,
111
- children: React.Children.toArray(ch.props.children).map(transform),
112
- });
117
+ ...(ch.props || {}),
118
+ // Cast to any to allow children property
119
+ children: Children.toArray((ch.props as any).children).map(transform),
120
+ } as any);
113
121
  }
114
122
  }
115
123
  return ch;
@@ -119,24 +127,29 @@ function useOptionListWithIndexes({ children }: { children: ReactNode }) {
119
127
  }
120
128
 
121
129
  type UseDropdownProps = {
122
- ref: React.Ref<Element>;
123
- optionsRef: React.RefObject<HTMLDivElement>;
130
+ ref: Ref<Element>;
131
+ optionsRef: RefObject<HTMLDivElement | null>;
124
132
  };
125
133
 
126
134
  function findOption<T>(
127
135
  children: ReactNode,
128
136
  value: T,
129
137
  ): { label: ReactNode; index: number; avatar?: AvatarProps } | null {
130
- const list = React.Children.toArray(children);
138
+ const list = Children.toArray(children);
131
139
  for (let i = 0; i < list.length; i++) {
132
140
  const elem = list[i];
133
- if (React.isValidElement(elem) && !elem.props.isDisabled) {
134
- const optValue = typeof elem.props.value === 'undefined' ? null : elem.props.value;
141
+ if (isValidElement(elem) && !(elem.props as any).isDisabled) {
142
+ const optValue = typeof (elem.props as any).value === 'undefined' ? null : (elem.props as any).value;
135
143
  if (elem.type === DropdownOption && optValue === value) {
136
- return { index: elem.props.index, label: elem.props.children, avatar: elem.props.avatar };
144
+ return {
145
+ index: (elem.props as any).index,
146
+ label: (elem.props as any).children,
147
+ avatar: (elem.props as any).avatar,
148
+ };
137
149
  }
138
150
  const ch =
139
- findOption(elem.props.children, value) || (isSearchable(elem.type) && findOption(elem.type(elem.props), value));
151
+ findOption((elem.props as any).children, value) ||
152
+ (isSearchable(elem.type) && findOption(elem.type(elem.props), value));
140
153
  if (ch) {
141
154
  return ch;
142
155
  }
@@ -194,7 +207,7 @@ function useDropdown<T>({
194
207
  }
195
208
  }, [activeIndex]);
196
209
  const referenceKeyDown = useCallback(
197
- (ev: React.KeyboardEvent) => {
210
+ (ev: React.KeyboardEvent<Element>) => {
198
211
  if (ev.key === 'Enter') {
199
212
  ev.preventDefault();
200
213
  searchOnSubmit();
@@ -208,7 +221,7 @@ function useDropdown<T>({
208
221
  });
209
222
 
210
223
  // this can just be a ref as it will always be updated when formValue is, which will cause a re-render
211
- const labelMapRef = useRef<Map<T, ReactNode>>();
224
+ const labelMapRef = useRef<Map<T, ReactNode>>(new Map());
212
225
  if (!labelMapRef.current) labelMapRef.current = new Map();
213
226
 
214
227
  // clear map when value is changed from the outside
@@ -407,7 +420,7 @@ const Dropdown = forwardRef<Element, DropdownProps<string | null>>(
407
420
  ) => {
408
421
  const dataAttributes = getDataAttributes(props);
409
422
 
410
- const optionsRef = useRef(null);
423
+ const optionsRef = useRef<HTMLDivElement | null>(null);
411
424
  const {
412
425
  avatar,
413
426
  children,
@@ -553,7 +566,7 @@ const Dropdown = forwardRef<Element, DropdownProps<string | null>>(
553
566
 
554
567
  export function typedDropdown<T>() {
555
568
  return {
556
- Dropdown: Dropdown as React.ForwardRefExoticComponent<DropdownProps<T> & React.RefAttributes<Element>>,
569
+ Dropdown: Dropdown as ForwardRefExoticComponent<DropdownProps<T> & RefAttributes<Element>>,
557
570
  DropdownOption: DropdownOption as (p: DropdownOptionProps<T>) => JSX.Element,
558
571
  };
559
572
  }
@@ -7,13 +7,13 @@ const useAutoScroll = ({
7
7
  optionsRef,
8
8
  selectedIndex,
9
9
  }: {
10
- optionsRef: React.RefObject<HTMLDivElement>;
10
+ optionsRef: React.RefObject<HTMLDivElement | null>;
11
11
  listRef: React.MutableRefObject<HTMLElement[]>;
12
12
  activeIndex: number | null;
13
13
  selectedIndex: number | null;
14
14
  keyboardControlled: boolean;
15
15
  }) => {
16
- const prevActiveIndexRef = useRef<number | null>();
16
+ const prevActiveIndexRef = useRef<number | null>(null);
17
17
  useLayoutEffect(() => {
18
18
  prevActiveIndexRef.current = activeIndex;
19
19
  }, [activeIndex]);
@@ -27,7 +27,7 @@ const useFloatingDropdown = ({
27
27
  placement,
28
28
  }: {
29
29
  enabled: boolean;
30
- optionsRef: RefObject<HTMLDivElement>;
30
+ optionsRef: RefObject<HTMLDivElement | null>;
31
31
  dropdownWidth: ChakraProps['width'] | 'match';
32
32
  placement: UseFloatingProps['placement'] | undefined;
33
33
  }) => {
@@ -1,4 +1,4 @@
1
- import { Children, cloneElement, isValidElement, ReactNode, useMemo, useState } from 'react';
1
+ import { Children, cloneElement, isValidElement, ReactElement, ReactNode, useMemo, useState } from 'react';
2
2
  import { chakra } from '@chakra-ui/react';
3
3
  import { useDropdownStyles } from '../Dropdown.context';
4
4
  import isNodeMatch from '../isNodeMatch';
@@ -25,12 +25,13 @@ function useSimpleSearch({ children, onSearch }: { children?: ReactNode; onSearc
25
25
 
26
26
  const transform = (node: ReactNode): ReactNode => {
27
27
  if (isValidElement(node) && node.type === DropdownGroup) {
28
- const groupChildren = Children.toArray(node.props.children).map(transform).filter(Boolean);
28
+ const element = node as ReactElement<{ children?: ReactNode }>;
29
+ const groupChildren = Children.toArray(element.props.children).map(transform).filter(Boolean);
29
30
  if (groupChildren.length === 0) {
30
31
  return null;
31
32
  }
32
- return cloneElement(node, {
33
- ...node.props,
33
+ return cloneElement(element, {
34
+ ...element.props,
34
35
  children: groupChildren,
35
36
  });
36
37
  }
@@ -1,4 +1,4 @@
1
- import React, { JSXElementConstructor, ReactElement, ReactNode } from 'react';
1
+ import { isValidElement, JSXElementConstructor, ReactElement, ReactNode } from 'react';
2
2
 
3
3
  type SearchableElement = JSXElementConstructor<any> & { searchable: true };
4
4
  export function isSearchable(
@@ -17,10 +17,10 @@ function isNodeMatch(node: ReactNode, filter: string): boolean {
17
17
  if (Array.isArray(node)) {
18
18
  return Array.from(node).some((child) => isNodeMatch(child, filter));
19
19
  }
20
- if ('children' in node) {
21
- return isNodeMatch(node.children, filter);
20
+ if (typeof node === 'object' && node !== null && 'children' in node) {
21
+ return isNodeMatch((node as any).children, filter);
22
22
  }
23
- if (React.isValidElement<any>(node)) {
23
+ if (isValidElement<any>(node)) {
24
24
  if (node.type === 'svg') {
25
25
  return false;
26
26
  }
@@ -28,9 +28,18 @@ function isNodeMatch(node: ReactNode, filter: string): boolean {
28
28
  if (isSearchable(ctor)) {
29
29
  return isNodeMatch(ctor(node.props), filter);
30
30
  }
31
- return isNodeMatch(node.props.children, filter);
31
+ if (isValidElement(node)) {
32
+ return isNodeMatch((node as ReactElement<any>).props.children, filter);
33
+ }
34
+ return false;
35
+ }
36
+ if (
37
+ (typeof node === 'object' && node !== null && (Symbol.iterator in node || 'length' in node)) ||
38
+ Array.isArray(node)
39
+ ) {
40
+ return Array.from(node as Iterable<ReactNode> | ArrayLike<ReactNode>).some((child) => isNodeMatch(child, filter));
32
41
  }
33
- return isNodeMatch(Array.from(node), filter);
42
+ return false;
34
43
  }
35
44
 
36
45
  export default isNodeMatch;
@@ -2,6 +2,8 @@ import { DateTime } from 'luxon';
2
2
  import { DateRange } from '../DatePicker/useDateRange';
3
3
  import { FilterContextType, FilterData, FilterOptions, FilterState } from './Filter.types';
4
4
 
5
+ const branches = [...new Array(25000)].map((_, i) => `branch ${i}`);
6
+
5
7
  export const FILTER_STORY_OPTIONS: FilterOptions =
6
8
  'Lorem ipsum dolor sit amet consectetur adipisicing elit Reiciendis reprehenderit laudantium laborum excepturi nam quae quod sunt expedita vel repellat cum cupiditate esse similique est ducimus provident eos numquam voluptas'.split(
7
9
  ' ',
@@ -43,7 +45,7 @@ export const FILTER_STORY_DATA: FilterData = {
43
45
  categoryNamePlural: 'Branches',
44
46
  isMultiple: true,
45
47
  isPatternEnabled: true,
46
- options: ['default 1', 'default 2', 'default 3', 'default 13'],
48
+ options: branches,
47
49
  type: 'tag',
48
50
  },
49
51
  cache_type: {
@@ -23,6 +23,8 @@ import { isEqual, useDebounce } from '../../../utils/utils';
23
23
  import { getOptionLabel } from '../Filter.utils';
24
24
  import { useFilterContext } from '../Filter.context';
25
25
 
26
+ const MAX_ITEMS = 100;
27
+
26
28
  /**
27
29
  * https://gist.github.com/donmccurdy/6d073ce2c6f3951312dfa45da14a420f
28
30
  * RegExp-escapes all characters in the given string.
@@ -242,13 +244,19 @@ const FilterForm = (props: FilterFormProps) => {
242
244
  {!isLoading && isMultiple && (
243
245
  <CheckboxGroup onChange={setSelected} sx={filterStyle.formInputGroup} value={selected}>
244
246
  {items.length
245
- ? items.map((opt) => (
247
+ ? items.slice(0, MAX_ITEMS).map((opt) => (
246
248
  <Checkbox key={opt} value={opt}>
247
249
  {iconsMap && iconsMap[opt] && <Icon name={iconsMap[opt]} />}
248
250
  {getOptionLabel(opt, currentOptionMap)}
249
251
  </Checkbox>
250
252
  ))
251
253
  : getEmptyText()}
254
+ {items.length > MAX_ITEMS && (
255
+ <Text textStyle="body/md/regular">
256
+ We show only {MAX_ITEMS} items, use the search
257
+ <br /> to find more.
258
+ </Text>
259
+ )}
252
260
  </CheckboxGroup>
253
261
  )}
254
262
  {!isLoading && !isMultiple && (
@@ -261,7 +269,7 @@ const FilterForm = (props: FilterFormProps) => {
261
269
  </Radio>
262
270
  )}
263
271
  {items.length
264
- ? items.map((opt) => {
272
+ ? items.slice(0, MAX_ITEMS).map((opt) => {
265
273
  const hasIcon = iconsMap && iconsMap[opt];
266
274
  const label = getOptionLabel(opt, currentOptionMap);
267
275
  return (
@@ -278,6 +286,12 @@ const FilterForm = (props: FilterFormProps) => {
278
286
  );
279
287
  })
280
288
  : getEmptyText()}
289
+ {items.length > MAX_ITEMS && (
290
+ <Text textStyle="body/md/regular">
291
+ We show only {MAX_ITEMS} items, use the search
292
+ <br /> to find more.
293
+ </Text>
294
+ )}
281
295
  </RadioGroup>
282
296
  )}
283
297
  </>
@@ -39,7 +39,7 @@ const LabeledData = ({
39
39
  trendValue,
40
40
  trendColorScheme,
41
41
  ...rest
42
- }: LabeledDataProps): JSX.Element => {
42
+ }: LabeledDataProps) => {
43
43
  const trendIconSize = size === 'sm' ? '16' : '24';
44
44
  const labelColor = size === 'sm' ? 'text/primary' : 'text/secondary';
45
45
  let trendSign;
@@ -166,7 +166,7 @@ const RealStage = ({
166
166
  );
167
167
  };
168
168
 
169
- const ProgressIndicator = ({ activeStageIndex, stages, ...rest }: ProgressIndicatorProps): JSX.Element => {
169
+ const ProgressIndicator = ({ activeStageIndex, stages, ...rest }: ProgressIndicatorProps) => {
170
170
  const style = useMultiStyleConfig('ProgressIndicator', { variant: rest.variant });
171
171
  const extendLines = Boolean(rest.variant === 'horizontal' && rest.extendLines);
172
172
  const activeSegmentIndex = extendLines ? activeStageIndex * 2 : activeStageIndex;
@@ -5,7 +5,7 @@ import Tr from './Tr';
5
5
 
6
6
  export type TablePaginationProps = Omit<PaginationProps, 'variant'> & { colSpan: number };
7
7
 
8
- const TablePagination = ({ colSpan, ...paginationProps }: TablePaginationProps): JSX.Element => {
8
+ const TablePagination = ({ colSpan, ...paginationProps }: TablePaginationProps) => {
9
9
  return (
10
10
  <Tfoot>
11
11
  <Tr>
@@ -17,7 +17,7 @@ const TreeViewGroup = ({ label, role, level, indexPath, titlePath, children }: T
17
17
  ? cloneElement(child, {
18
18
  level,
19
19
  indexPath: [...indexPath, childIndex],
20
- titlePath: [...titlePath, child.props.title],
20
+ titlePath: [...titlePath, (child.props as { title: string }).title],
21
21
  } as { level: number; indexPath: number[]; titlePath: string[] })
22
22
  : child,
23
23
  )}