@qoretechnologies/reqore 0.38.0 → 0.38.2

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.
Files changed (92) hide show
  1. package/__tests__/dropdown.test.tsx +10 -8
  2. package/__tests__/table.test.tsx +0 -1
  3. package/build-storybook.log +144 -141
  4. package/dist/components/Button/index.d.ts +1 -0
  5. package/dist/components/Button/index.d.ts.map +1 -1
  6. package/dist/components/Button/index.js +4 -4
  7. package/dist/components/Button/index.js.map +1 -1
  8. package/dist/components/Checkbox/index.d.ts.map +1 -1
  9. package/dist/components/Checkbox/index.js +1 -1
  10. package/dist/components/Checkbox/index.js.map +1 -1
  11. package/dist/components/Dropdown/index.d.ts +2 -2
  12. package/dist/components/Dropdown/index.d.ts.map +1 -1
  13. package/dist/components/Dropdown/index.js +5 -5
  14. package/dist/components/Dropdown/index.js.map +1 -1
  15. package/dist/components/Dropdown/item.d.ts +6 -5
  16. package/dist/components/Dropdown/item.d.ts.map +1 -1
  17. package/dist/components/Dropdown/item.js +14 -6
  18. package/dist/components/Dropdown/item.js.map +1 -1
  19. package/dist/components/Dropdown/list.d.ts +4 -2
  20. package/dist/components/Dropdown/list.d.ts.map +1 -1
  21. package/dist/components/Dropdown/list.js +33 -7
  22. package/dist/components/Dropdown/list.js.map +1 -1
  23. package/dist/components/Input/index.d.ts.map +1 -1
  24. package/dist/components/Input/index.js +3 -3
  25. package/dist/components/Input/index.js.map +1 -1
  26. package/dist/components/InputClearButton/index.d.ts.map +1 -1
  27. package/dist/components/InputClearButton/index.js +1 -1
  28. package/dist/components/InputClearButton/index.js.map +1 -1
  29. package/dist/components/Menu/index.js +4 -4
  30. package/dist/components/Menu/index.js.map +1 -1
  31. package/dist/components/Menu/item.d.ts +1 -1
  32. package/dist/components/Menu/item.d.ts.map +1 -1
  33. package/dist/components/Menu/item.js +6 -6
  34. package/dist/components/Menu/item.js.map +1 -1
  35. package/dist/components/Message/index.d.ts.map +1 -1
  36. package/dist/components/Message/index.js +3 -4
  37. package/dist/components/Message/index.js.map +1 -1
  38. package/dist/components/MultiSelect/index.js +2 -2
  39. package/dist/components/MultiSelect/index.js.map +1 -1
  40. package/dist/components/Notifications/notification.d.ts +1 -0
  41. package/dist/components/Notifications/notification.d.ts.map +1 -1
  42. package/dist/components/Notifications/notification.js +10 -21
  43. package/dist/components/Notifications/notification.js.map +1 -1
  44. package/dist/components/Panel/LabelEditor.d.ts.map +1 -1
  45. package/dist/components/Panel/LabelEditor.js +1 -1
  46. package/dist/components/Panel/LabelEditor.js.map +1 -1
  47. package/dist/components/Panel/index.d.ts.map +1 -1
  48. package/dist/components/Panel/index.js +26 -16
  49. package/dist/components/Panel/index.js.map +1 -1
  50. package/dist/components/Tabs/list.d.ts.map +1 -1
  51. package/dist/components/Tabs/list.js +3 -4
  52. package/dist/components/Tabs/list.js.map +1 -1
  53. package/dist/components/Tag/index.js +2 -2
  54. package/dist/components/Tag/index.js.map +1 -1
  55. package/dist/components/Textarea/index.d.ts +4 -0
  56. package/dist/components/Textarea/index.d.ts.map +1 -1
  57. package/dist/components/Textarea/index.js +29 -8
  58. package/dist/components/Textarea/index.js.map +1 -1
  59. package/dist/constants/sizes.d.ts +7 -0
  60. package/dist/constants/sizes.d.ts.map +1 -1
  61. package/dist/constants/sizes.js +8 -1
  62. package/dist/constants/sizes.js.map +1 -1
  63. package/migration-storybook.log +49 -81
  64. package/package.json +15 -15
  65. package/src/components/Button/index.tsx +5 -3
  66. package/src/components/Checkbox/index.tsx +7 -2
  67. package/src/components/Dropdown/index.tsx +16 -14
  68. package/src/components/Dropdown/item.tsx +26 -10
  69. package/src/components/Dropdown/list.tsx +80 -30
  70. package/src/components/Input/index.tsx +9 -3
  71. package/src/components/InputClearButton/index.tsx +1 -0
  72. package/src/components/Menu/index.tsx +2 -2
  73. package/src/components/Menu/item.tsx +93 -85
  74. package/src/components/Message/index.tsx +39 -35
  75. package/src/components/MultiSelect/index.tsx +2 -2
  76. package/src/components/Notifications/notification.tsx +34 -30
  77. package/src/components/Panel/LabelEditor.tsx +1 -0
  78. package/src/components/Panel/index.tsx +28 -17
  79. package/src/components/Tabs/list.tsx +7 -5
  80. package/src/components/Tag/index.tsx +3 -3
  81. package/src/components/Textarea/index.tsx +94 -30
  82. package/src/constants/sizes.ts +8 -0
  83. package/src/stories/Button/Button.stories.tsx +5 -0
  84. package/src/stories/Dropdown/Dropdown.stories.tsx +58 -0
  85. package/src/stories/Dropdown/DropdownTests.stories.tsx +111 -0
  86. package/src/stories/Input/Input.stories.tsx +1 -0
  87. package/src/stories/Message/Message.stories.tsx +13 -5
  88. package/src/stories/MultiSelect/MultiSelect.stories.tsx +2 -2
  89. package/src/stories/Panel/Panel.stories.tsx +96 -0
  90. package/src/stories/TextArea/TextArea.stories.tsx +50 -1
  91. package/src/stories/TextArea/TextAreaTests.stories.tsx +103 -0
  92. package/tests.json +1 -1
@@ -4,13 +4,7 @@ import React, { forwardRef, useEffect, useState } from 'react';
4
4
  import { useMount, useUnmount } from 'react-use';
5
5
  import styled, { css, keyframes } from 'styled-components';
6
6
  import { SPRING_CONFIG } from '../../constants/animations';
7
- import {
8
- ICON_WRAPPER_FROM_HEADER_SIZE,
9
- PADDING_FROM_SIZE,
10
- TABS_SIZE_TO_PX,
11
- TEXT_FROM_SIZE,
12
- TSizes,
13
- } from '../../constants/sizes';
7
+ import { PADDING_FROM_SIZE, TEXT_FROM_SIZE, TSizes } from '../../constants/sizes';
14
8
  import { IReqoreTheme, TReqoreIntent } from '../../constants/theme';
15
9
  import ReqoreThemeProvider from '../../containers/ThemeProvider';
16
10
  import { fadeIn } from '../../helpers/animations';
@@ -82,7 +76,6 @@ export const StyledReqoreNotification = styled(StyledEffect)<IReqoreNotification
82
76
  min-width: ${({ fluid }) => (!fluid ? '30px' : undefined)};
83
77
  max-width: ${({ maxWidth, fluid, fixed }) => maxWidth || (fluid && !fixed ? '100%' : undefined)};
84
78
  border-radius: 5px;
85
- min-height: ${({ size = 'normal' }: IReqoreNotificationStyle) => TABS_SIZE_TO_PX[size]}px;
86
79
  display: flex;
87
80
  flex: ${({ fluid, fixed }) => (fixed ? '0 0 auto' : fluid ? '1 auto' : '0 0 auto')};
88
81
  align-self: ${({ fixed, fluid }) => (fixed ? 'flex-start' : fluid ? 'stretch' : undefined)};
@@ -172,15 +165,12 @@ export const StyledReqoreNotification = styled(StyledEffect)<IReqoreNotification
172
165
  `;
173
166
 
174
167
  export const StyledIconWrapper = styled.div<IReqoreNotificationStyle>`
175
- height: ${({ size = 'normal' }: IReqoreNotificationStyle) =>
176
- ICON_WRAPPER_FROM_HEADER_SIZE[size]}px;
177
168
  flex: 0 1 auto;
178
169
  flex-shrink: 0;
179
170
  display: flex;
180
171
  justify-content: center;
181
172
  align-items: center;
182
173
  transition: all 0.2s ease-out;
183
- margin-top: ${({ size = 'normal' }: IReqoreNotificationStyle) => `${PADDING_FROM_SIZE[size]}px`};
184
174
 
185
175
  ${({ clickable, theme, intent, type }) =>
186
176
  clickable &&
@@ -202,12 +192,21 @@ export const StyledIconWrapper = styled.div<IReqoreNotificationStyle>`
202
192
  `}
203
193
  `;
204
194
 
205
- export const StyledNotificationContentWrapper = styled.div<IReqoreNotificationStyle>`
195
+ export const StyledNotificationInnerContent = styled.div<IReqoreNotificationStyle>`
206
196
  flex: 1;
207
- min-height: ${({ size = 'normal' }: IReqoreNotificationStyle) => TABS_SIZE_TO_PX[size]}px;
208
197
  display: flex;
209
198
  flex-flow: column;
210
199
  justify-content: center;
200
+
201
+ .reqore-heading {
202
+ line-height: 1;
203
+ }
204
+ `;
205
+
206
+ export const StyledNotificationContentWrapper = styled.div<IReqoreNotificationStyle>`
207
+ flex: 1;
208
+ display: flex;
209
+ justify-content: center;
211
210
  padding: ${({ size = 'normal' }: IReqoreNotificationStyle) => `${PADDING_FROM_SIZE[size]}px`};
212
211
  `;
213
212
 
@@ -224,6 +223,9 @@ export const StyledNotificationContent = styled.div`
224
223
  flex: 1;
225
224
  font-size: ${({ size = 'normal' }) => TEXT_FROM_SIZE[size]}px;
226
225
  overflow-wrap: anywhere;
226
+ display: flex;
227
+ flex-flow: column;
228
+ justify-content: center;
227
229
 
228
230
  ${({ hasTitle, size = 'normal' }) =>
229
231
  hasTitle &&
@@ -317,24 +319,26 @@ const ReqoreNotification = forwardRef<HTMLDivElement, IReqoreNotificationProps>(
317
319
  theme={theme}
318
320
  maxWidth='450px'
319
321
  >
320
- {type || intent || icon ? (
321
- <StyledIconWrapper type={type || intent} size={size}>
322
- {intent === 'pending' || type === 'pending' ? (
323
- <ReqoreSpinner size={size} type={5} iconMargin={'both'} />
324
- ) : (
325
- <ReqoreIcon
326
- icon={icon || typeToIcon[type || intent]}
327
- margin={'both'}
328
- size={size}
329
- />
330
- )}
331
- </StyledIconWrapper>
332
- ) : null}
333
322
  <StyledNotificationContentWrapper size={size} theme={theme}>
334
- {title && <ReqoreHeading size={size}>{title}</ReqoreHeading>}
335
- <StyledNotificationContent theme={theme} hasTitle={!!title} size={size}>
336
- {content}
337
- </StyledNotificationContent>
323
+ {type || intent || icon ? (
324
+ <>
325
+ {intent === 'pending' || type === 'pending' ? (
326
+ <ReqoreSpinner size={size} type={5} iconMargin={'right'} />
327
+ ) : (
328
+ <ReqoreIcon
329
+ icon={icon || typeToIcon[type || intent]}
330
+ margin={'right'}
331
+ size={size}
332
+ />
333
+ )}
334
+ </>
335
+ ) : null}
336
+ <StyledNotificationInnerContent>
337
+ {title && <ReqoreHeading size={size}>{title}</ReqoreHeading>}
338
+ <StyledNotificationContent theme={theme} hasTitle={!!title} size={size}>
339
+ {content}
340
+ </StyledNotificationContent>
341
+ </StyledNotificationInnerContent>
338
342
  </StyledNotificationContentWrapper>
339
343
  <StyledIconWrapper
340
344
  type={type || intent}
@@ -33,6 +33,7 @@ export const LabelEditor = memo(
33
33
  onClick={(e) => e.stopPropagation()}
34
34
  value={name}
35
35
  minimal
36
+ fluid
36
37
  onChange={(e: any) => setName(e.target.value)}
37
38
  onKeyUp={(e) => {
38
39
  if (e.key === 'Enter') {
@@ -273,11 +273,9 @@ export const StyledPanelTitle = styled.div<IStyledPanel>`
273
273
  2 +
274
274
  (SIZE_TO_PX[size] + ((transparent && flat) || minimal ? 0 : PADDING_FROM_SIZE[size]) * 2)}px;
275
275
  align-items: center;
276
- padding: ${({ noHorizontalPadding, size, transparent, flat, minimal, intent }: IStyledPanel) =>
276
+ padding: ${({ noHorizontalPadding, size, transparent, flat, intent }: IStyledPanel) =>
277
277
  `${transparent && flat && !intent ? 0 : PADDING_FROM_SIZE[size]}px ${
278
- noHorizontalPadding
279
- ? 0
280
- : `${minimal ? PADDING_FROM_SIZE[size] / 2 : PADDING_FROM_SIZE[size]}px`
278
+ noHorizontalPadding ? 0 : `${PADDING_FROM_SIZE[size]}px`
281
279
  }`};
282
280
  border-bottom: ${({ theme, isCollapsed, flat, opacity = 1 }) =>
283
281
  !isCollapsed && !flat
@@ -308,17 +306,21 @@ export const StyledPanelTitle = styled.div<IStyledPanel>`
308
306
  `;
309
307
 
310
308
  export const StyledPanelTopBar = styled(StyledPanelTitle)`
311
- padding-bottom: ${({ minimal, padded, size }: IStyledPanel) =>
312
- !padded ? `${PADDING_FROM_SIZE[size] / (minimal ? 2 : 1)}px` : minimal ? 0 : undefined};
309
+ padding-bottom: ${({ minimal, padded, size, transparent, isCollapsed }: IStyledPanel) =>
310
+ !padded || isCollapsed
311
+ ? `${PADDING_FROM_SIZE[size]}px`
312
+ : minimal || transparent
313
+ ? 0
314
+ : undefined};
313
315
  padding-top: ${({ minimal, size }: IStyledPanel) =>
314
- minimal ? `${PADDING_FROM_SIZE[size] / 2}px` : undefined};
316
+ minimal ? `${PADDING_FROM_SIZE[size]}px` : undefined};
315
317
  `;
316
318
 
317
319
  export const StyledPanelBottomActions = styled(StyledPanelTitle)`
318
- padding-top: ${({ minimal, padded, size }: IStyledPanel) =>
319
- !padded ? `${PADDING_FROM_SIZE[size] / (minimal ? 2 : 1)}px` : minimal ? 0 : undefined};
320
+ padding-top: ${({ minimal, padded, size, transparent }: IStyledPanel) =>
321
+ !padded ? `${PADDING_FROM_SIZE[size]}px` : minimal || transparent ? 0 : undefined};
320
322
  padding-bottom: ${({ minimal, size }: IStyledPanel) =>
321
- minimal ? `${PADDING_FROM_SIZE[size] / 2}px` : undefined};
323
+ minimal ? `${PADDING_FROM_SIZE[size]}px` : undefined};
322
324
  border-bottom: 0;
323
325
  border-top: ${({ theme, flat, opacity = 1 }) =>
324
326
  !flat
@@ -333,13 +335,21 @@ export const StyledPanelContent = styled.div<IStyledPanel>`
333
335
  ? undefined
334
336
  : noHorizontalPadding
335
337
  ? `${PADDING_FROM_SIZE[size]}px 0`
336
- : `${minimal ? PADDING_FROM_SIZE[size] / 2 : PADDING_FROM_SIZE[size]}px`};
338
+ : `${PADDING_FROM_SIZE[size] / (minimal ? 2 : 1)}px ${PADDING_FROM_SIZE[size]}px`};
337
339
  // The padding is not needed when the panel is minimal and has title, since
338
340
  // the title already has padding and is transparent
339
341
  padding-top: ${({ minimal, hasLabel, padded, size }) =>
340
- minimal && hasLabel && padded ? `${PADDING_FROM_SIZE[size] / 2}px` : undefined};
341
- padding-bottom: ${({ minimal, padded, size }) =>
342
- minimal && padded ? `${PADDING_FROM_SIZE[size] / 2}px` : undefined};
342
+ minimal && hasLabel && padded
343
+ ? `${PADDING_FROM_SIZE[size] / 2}px`
344
+ : padded
345
+ ? `${PADDING_FROM_SIZE[size]}px`
346
+ : undefined};
347
+ padding-bottom: ${({ minimal, padded, size, hasBottomActions }) =>
348
+ minimal && hasBottomActions && padded
349
+ ? `${PADDING_FROM_SIZE[size] / 2}px`
350
+ : padded
351
+ ? `${PADDING_FROM_SIZE[size]}px`
352
+ : undefined};
343
353
  flex: 1;
344
354
  overflow: auto;
345
355
  overflow-wrap: anywhere;
@@ -719,7 +729,7 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
719
729
  isMobile={isMobile || isSmall}
720
730
  ref={measureRef}
721
731
  padded={padded}
722
- transparent={rest.transparent}
732
+ transparent={rest.transparent || opacity === 0}
723
733
  intent={intent}
724
734
  >
725
735
  {hasTitleHeader && (
@@ -830,7 +840,8 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
830
840
  <StyledPanelContent
831
841
  as={'div'}
832
842
  className='reqore-panel-content'
833
- hasLabel={!!label}
843
+ hasLabel={!!hasTitleBar}
844
+ hasBottomActions={hasBottomActions}
834
845
  isCollapsed={_isCollapsed}
835
846
  style={contentStyle}
836
847
  padded={padded}
@@ -850,7 +861,7 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
850
861
  padded={padded}
851
862
  intent={intent}
852
863
  minimal={minimal}
853
- transparent={rest.transparent}
864
+ transparent={rest.transparent || opacity === 0}
854
865
  opacity={opacity ?? (minimal ? 0 : 1)}
855
866
  size={contentSize || panelSize}
856
867
  noHorizontalPadding={noHorizontalPadding}
@@ -5,10 +5,10 @@ import styled, { css } from 'styled-components';
5
5
  import { IReqoreTabsListItem, IReqoreTabsProps } from '.';
6
6
  import { ReqorePopover } from '../..';
7
7
  import {
8
+ CONTROL_TEXT_FROM_SIZE,
8
9
  GAP_FROM_SIZE,
9
10
  ICON_FROM_SIZE,
10
11
  PADDING_FROM_SIZE,
11
- TEXT_FROM_SIZE,
12
12
  TSizes,
13
13
  } from '../../constants/sizes';
14
14
  import { IReqoreBreadcrumbsTheme, IReqoreCustomTheme, IReqoreTheme } from '../../constants/theme';
@@ -100,8 +100,10 @@ const getBadgeLength = (badge: IReqoreButtonProps['badge'], tabsSize: TSizes = '
100
100
  }
101
101
 
102
102
  return (
103
- calculateStringSizeInPixels(badge.toString(), TEXT_FROM_SIZE[getOneLessSize(tabsSize)]) +
104
- PADDING_FROM_SIZE[tabsSize]
103
+ calculateStringSizeInPixels(
104
+ badge.toString(),
105
+ CONTROL_TEXT_FROM_SIZE[getOneLessSize(tabsSize)]
106
+ ) + PADDING_FROM_SIZE[tabsSize]
105
107
  );
106
108
  };
107
109
 
@@ -133,11 +135,11 @@ export const getLabelLength = (
133
135
  ? 0
134
136
  : calculateStringSizeInPixels(
135
137
  (item as IReqoreBreadcrumbItem | IReqoreTabsListItem)?.description?.toString(),
136
- TEXT_FROM_SIZE[getOneLessSize(tabsSize)]
138
+ CONTROL_TEXT_FROM_SIZE[getOneLessSize(tabsSize)]
137
139
  );
138
140
 
139
141
  const topLabelLength =
140
- calculateStringSizeInPixels(label?.toString(), TEXT_FROM_SIZE[tabsSize]) +
142
+ calculateStringSizeInPixels(label?.toString(), CONTROL_TEXT_FROM_SIZE[tabsSize]) +
141
143
  icon +
142
144
  rightIcon +
143
145
  closeIconSize +
@@ -5,10 +5,10 @@ import styled, { css } from 'styled-components';
5
5
  import { ReqorePopover, useReqoreTheme } from '../..';
6
6
  import {
7
7
  BADGE_SIZE_TO_PX,
8
+ CONTROL_TEXT_FROM_SIZE,
8
9
  PADDING_FROM_SIZE,
9
10
  RADIUS_FROM_SIZE,
10
11
  SIZE_TO_PX,
11
- TEXT_FROM_SIZE,
12
12
  TSizes,
13
13
  } from '../../constants/sizes';
14
14
  import { IReqoreTheme, TReqoreIntent } from '../../constants/theme';
@@ -93,7 +93,7 @@ export const StyledTag = styled(StyledEffect)<IReqoreTagStyle>`
93
93
  font-weight: 600;
94
94
  overflow: hidden;
95
95
  vertical-align: middle;
96
- font-size: ${({ size }) => TEXT_FROM_SIZE[size]}px;
96
+ font-size: ${({ size }) => CONTROL_TEXT_FROM_SIZE[size]}px;
97
97
 
98
98
  min-width: ${({ size }) => SIZE_TO_PX[size]}px;
99
99
  max-width: ${({ fixed }) => (fixed !== true ? '100%' : undefined)};
@@ -213,7 +213,7 @@ const StyledTagContentKey = styled(StyledTagContent)`
213
213
 
214
214
  const StyledButtonWrapper = styled.span<IReqoreTagStyle>`
215
215
  flex-shrink: 0;
216
- font-size: ${({ size }) => TEXT_FROM_SIZE[size]}px;
216
+ font-size: ${({ size }) => CONTROL_TEXT_FROM_SIZE[size]}px;
217
217
  width: ${({ size }) => SIZE_TO_PX[size]}px;
218
218
  display: flex;
219
219
  justify-content: center;
@@ -1,7 +1,14 @@
1
1
  import { rgba } from 'polished';
2
- import React, { forwardRef, useEffect, useState } from 'react';
2
+ import React, { forwardRef, useCallback, useEffect, useState } from 'react';
3
3
  import styled, { css } from 'styled-components';
4
- import { RADIUS_FROM_SIZE, SIZE_TO_PX, TEXT_FROM_SIZE, TSizes } from '../../constants/sizes';
4
+ import { ReqoreDropdown } from '../..';
5
+ import {
6
+ CONTROL_TEXT_FROM_SIZE,
7
+ PADDING_FROM_SIZE,
8
+ RADIUS_FROM_SIZE,
9
+ SIZE_TO_PX,
10
+ TSizes,
11
+ } from '../../constants/sizes';
5
12
  import { IReqoreTheme } from '../../constants/theme';
6
13
  import { changeLightness, getReadableColor } from '../../helpers/colors';
7
14
  import { IReqoreAutoFocusRules, useAutoFocus } from '../../hooks/useAutoFocus';
@@ -18,10 +25,13 @@ import {
18
25
  IWithReqoreEffect,
19
26
  IWithReqoreTooltip,
20
27
  } from '../../types/global';
28
+ import { IReqoreDropdownProps } from '../Dropdown';
21
29
  import { StyledEffect } from '../Effect';
22
30
  import { IReqoreInputStyle } from '../Input';
23
31
  import ReqoreInputClearButton from '../InputClearButton';
24
32
 
33
+ export interface IReqoreFormTemplates extends IReqoreDropdownProps {}
34
+
25
35
  export interface IReqoreTextareaProps
26
36
  extends React.HTMLAttributes<HTMLTextAreaElement>,
27
37
  IReqoreReadOnly,
@@ -46,6 +56,7 @@ export interface IReqoreTextareaProps
46
56
  onClearClick?: () => any;
47
57
  wrapperStyle?: React.CSSProperties;
48
58
  focusRules?: IReqoreAutoFocusRules;
59
+ templates?: IReqoreFormTemplates;
49
60
  }
50
61
 
51
62
  export interface IReqoreTextareaStyle extends IReqoreTextareaProps {
@@ -62,16 +73,23 @@ export const StyledTextareaWrapper = styled.div<IReqoreTextareaStyle>`
62
73
  align-self: ${({ fixed, fluid }) => (fixed ? 'flex-start' : fluid ? 'stretch' : undefined)};
63
74
  position: relative;
64
75
  overflow: hidden;
76
+
77
+ &:focus-within {
78
+ .reqore-clear-input-button {
79
+ display: flex;
80
+ }
81
+ }
65
82
  `;
66
83
 
67
84
  export const StyledTextarea = styled(StyledEffect)<IReqoreTextareaStyle>`
68
85
  width: 100%;
69
86
  max-width: 100%;
70
87
  max-height: 100%;
71
- font-size: ${({ _size }) => TEXT_FROM_SIZE[_size]}px;
88
+ font-size: ${({ _size }) => CONTROL_TEXT_FROM_SIZE[_size]}px;
72
89
  margin: 0;
73
- padding: 5px 7px;
90
+ padding: ${({ _size }) => PADDING_FROM_SIZE[_size] / 2}px;
74
91
  min-height: ${({ _size }) => SIZE_TO_PX[_size]}px;
92
+ line-height: ${({ _size }) => SIZE_TO_PX[_size] - CONTROL_TEXT_FROM_SIZE[_size]}px;
75
93
 
76
94
  background-color: ${({ theme, minimal }: IReqoreInputStyle) =>
77
95
  minimal ? 'transparent' : rgba(theme.main, 0.1)};
@@ -141,6 +159,7 @@ const ReqoreInput = forwardRef<HTMLTextAreaElement, IReqoreTextareaProps>(
141
159
  onChange,
142
160
  fixed,
143
161
  focusRules,
162
+ templates,
144
163
  ...rest
145
164
  }: IReqoreTextareaProps,
146
165
  ref
@@ -158,6 +177,76 @@ const ReqoreInput = forwardRef<HTMLTextAreaElement, IReqoreTextareaProps>(
158
177
  useAutosizeTextArea(inputRef, _value, scaleWithContent);
159
178
  useAutoFocus(inputRef, rest.readOnly || rest.disabled ? undefined : focusRules, onChange);
160
179
 
180
+ const handleItemSelect = useCallback(
181
+ (item) => {
182
+ if (!inputRef || !item || !item.value) {
183
+ return;
184
+ }
185
+ // Add the value of the selected item to the current value at the cursor position
186
+ const value = `${_value.slice(0, inputRef.selectionStart)}${item.value}${_value.slice(
187
+ inputRef.selectionEnd
188
+ )}`;
189
+
190
+ setValue(value);
191
+ },
192
+ [inputRef, _value]
193
+ );
194
+
195
+ const renderChildren = () => {
196
+ return (
197
+ <>
198
+ <StyledTextarea
199
+ {...rest}
200
+ effect={{
201
+ interactive: !rest?.disabled && !rest.readOnly,
202
+ ...rest?.effect,
203
+ }}
204
+ onChange={(e) => {
205
+ setValue(e.target.value);
206
+ onChange?.(e);
207
+ }}
208
+ as='textarea'
209
+ className={`${className || ''} reqore-control reqore-textarea`}
210
+ _size={size}
211
+ ref={(ref) => setInputRef(ref)}
212
+ theme={theme}
213
+ rounded={rounded}
214
+ rows={1}
215
+ value={_value}
216
+ readonly={rest?.readOnly}
217
+ tabIndex={rest?.disabled ? -1 : 0}
218
+ />
219
+ <ReqoreInputClearButton
220
+ enabled={!rest?.readOnly && !rest?.disabled && !!(onClearClick && onChange)}
221
+ show={_value && _value !== ''}
222
+ onClick={onClearClick}
223
+ size={size}
224
+ />
225
+ </>
226
+ );
227
+ };
228
+
229
+ if (templates) {
230
+ return (
231
+ <ReqoreDropdown<Omit<IReqoreTextareaStyle & { ref: any }, 'content'>>
232
+ component={StyledTextareaWrapper}
233
+ className={`${className || ''} reqore-control-wrapper`}
234
+ width={width}
235
+ height={height}
236
+ fluid={fluid}
237
+ fixed={fixed}
238
+ _size={size}
239
+ theme={theme}
240
+ style={wrapperStyle}
241
+ ref={targetRef}
242
+ onItemSelect={handleItemSelect}
243
+ {...templates}
244
+ >
245
+ {renderChildren()}
246
+ </ReqoreDropdown>
247
+ );
248
+ }
249
+
161
250
  return (
162
251
  <StyledTextareaWrapper
163
252
  className={`${className || ''} reqore-control-wrapper`}
@@ -170,32 +259,7 @@ const ReqoreInput = forwardRef<HTMLTextAreaElement, IReqoreTextareaProps>(
170
259
  style={wrapperStyle}
171
260
  ref={targetRef}
172
261
  >
173
- <StyledTextarea
174
- {...rest}
175
- effect={{
176
- interactive: !rest?.disabled && !rest.readOnly,
177
- ...rest?.effect,
178
- }}
179
- onChange={(e) => {
180
- setValue(e.target.value);
181
- onChange?.(e);
182
- }}
183
- as='textarea'
184
- className={`${className || ''} reqore-control reqore-textarea`}
185
- _size={size}
186
- ref={(ref) => setInputRef(ref)}
187
- theme={theme}
188
- rounded={rounded}
189
- rows={1}
190
- value={_value}
191
- readonly={rest?.readOnly}
192
- />
193
- <ReqoreInputClearButton
194
- enabled={!rest?.readOnly && !rest?.disabled && !!(onClearClick && onChange)}
195
- show={_value && _value !== ''}
196
- onClick={onClearClick}
197
- size={size}
198
- />
262
+ {renderChildren()}
199
263
  </StyledTextareaWrapper>
200
264
  );
201
265
  }
@@ -109,6 +109,14 @@ export const TEXT_FROM_SIZE = {
109
109
  huge: 21,
110
110
  };
111
111
 
112
+ export const CONTROL_TEXT_FROM_SIZE = {
113
+ tiny: 7,
114
+ small: 10,
115
+ normal: 13,
116
+ big: 16,
117
+ huge: 19,
118
+ };
119
+
112
120
  export const ICON_FROM_SIZE = {
113
121
  tiny: 13,
114
122
  small: 17,
@@ -48,6 +48,11 @@ const Template: StoryFn<typeof ReqoreButton> = (buttonProps) => {
48
48
  <ReqoreButton {...buttonProps} minimal flat>
49
49
  Minimal
50
50
  </ReqoreButton>
51
+ <ReqoreButton {...buttonProps} icon={undefined} rightIcon={undefined} compact>
52
+ Compact
53
+ </ReqoreButton>
54
+ <ReqoreButton {...buttonProps} rightIcon={undefined} compact />
55
+ <ReqoreButton {...buttonProps} compact />
51
56
  <ReqoreButton {...buttonProps} readOnly onClick={alert}>
52
57
  Read only
53
58
  </ReqoreButton>
@@ -1,6 +1,8 @@
1
1
  import { StoryFn, StoryObj } from '@storybook/react';
2
+ import { fireEvent, within } from '@storybook/testing-library';
2
3
  import ReqoreButton, { IReqoreButtonProps } from '../../components/Button';
3
4
  import { IReqoreDropdownProps } from '../../components/Dropdown';
5
+ import { sleep } from '../../helpers/utils';
4
6
  import { ReqoreControlGroup, ReqoreDropdown, ReqoreInput } from '../../index';
5
7
  import { StoryMeta } from '../utils';
6
8
  import { argManager } from '../utils/args';
@@ -315,3 +317,59 @@ export const WithCustomPaging: Story = {
315
317
 
316
318
  play: () => {},
317
319
  };
320
+
321
+ export const WithChildItems: Story = {
322
+ render: (args) => <ReqoreDropdown {...args} />,
323
+ args: {
324
+ label: 'Dropdown with child items',
325
+ items: [
326
+ {
327
+ label: 'Test',
328
+ description: 'I have children',
329
+ rightIcon: 'MenuLine',
330
+ rightIconColor: 'info:lighten:2',
331
+ items: [
332
+ {
333
+ label: 'Test child 1',
334
+ },
335
+ {
336
+ label: 'Test child 2',
337
+ },
338
+ {
339
+ label: 'Test child 3',
340
+ description: 'I have children too',
341
+ items: [
342
+ {
343
+ label: 'Test deep child 1',
344
+ },
345
+ {
346
+ label: 'Test deep child 2',
347
+ },
348
+ {
349
+ label: 'Test deep child 3',
350
+ },
351
+ ],
352
+ },
353
+ {
354
+ label: 'Test child 4',
355
+ },
356
+ ],
357
+ },
358
+ {
359
+ label: 'Normal item',
360
+ description: 'I have no children',
361
+ },
362
+ ],
363
+ },
364
+ play: async ({ canvasElement }) => {
365
+ const canvas = within(canvasElement);
366
+
367
+ await sleep(200);
368
+
369
+ await fireEvent.click(canvas.getAllByText('Dropdown with child items')[0]);
370
+
371
+ await sleep(200);
372
+
373
+ await fireEvent.click(canvas.getAllByText('Test')[0]);
374
+ },
375
+ };
@@ -0,0 +1,111 @@
1
+ import { expect } from '@storybook/jest';
2
+ import { StoryObj } from '@storybook/react';
3
+ import { fireEvent, waitFor, within } from '@storybook/testing-library';
4
+ import ReqoreButton, { IReqoreButtonProps } from '../../components/Button';
5
+ import { IReqoreDropdownProps } from '../../components/Dropdown';
6
+ import { sleep } from '../../helpers/utils';
7
+ import { ReqoreDropdown } from '../../index';
8
+ import { StoryMeta } from '../utils';
9
+ import { argManager } from '../utils/args';
10
+ import { WithChildItems } from './Dropdown.stories';
11
+
12
+ const { createArg, disableArg } = argManager<IReqoreDropdownProps>();
13
+
14
+ const meta = {
15
+ title: 'Form/Dropdown/Tests',
16
+ component: ReqoreDropdown,
17
+ parameters: {
18
+ chromatic: {
19
+ delay: 1500,
20
+ },
21
+ },
22
+ argTypes: {
23
+ ...disableArg('multiSelect'),
24
+ ...createArg('component', {
25
+ defaultValue: ReqoreButton,
26
+ table: {
27
+ disable: true,
28
+ },
29
+ }),
30
+ ...createArg('filterable', {
31
+ defaultValue: true,
32
+ name: 'Filterable',
33
+ type: 'boolean',
34
+ }),
35
+ ...createArg('items', {
36
+ table: {
37
+ disable: true,
38
+ },
39
+ }),
40
+ },
41
+ args: {
42
+ component: ReqoreButton,
43
+ filterable: true,
44
+ },
45
+ } as StoryMeta<typeof ReqoreDropdown<IReqoreButtonProps>>;
46
+
47
+ export default meta;
48
+
49
+ type Story = StoryObj<typeof meta>;
50
+
51
+ export const ListIsClosedWhenItemIsClicked: Story = {
52
+ args: {
53
+ label: 'Default dropdown',
54
+ items: [
55
+ {
56
+ label: 'Test item 1',
57
+ },
58
+ {
59
+ label: 'Test item 2',
60
+ },
61
+ {
62
+ label: 'Test item 3',
63
+ },
64
+ ],
65
+ },
66
+ play: async ({ canvasElement, ...rest }) => {
67
+ const canvas = within(canvasElement);
68
+
69
+ await sleep(200);
70
+
71
+ await fireEvent.click(canvas.getAllByText('Default dropdown')[0]);
72
+
73
+ await sleep(200);
74
+
75
+ await fireEvent.click(canvas.getAllByText('Test item 1')[0]);
76
+
77
+ await sleep(200);
78
+
79
+ await expect(document.querySelector('.reqore-popover-content')).toBeFalsy();
80
+ },
81
+ };
82
+
83
+ export const ItemsCanBeTraversed: Story = {
84
+ ...WithChildItems,
85
+ play: async ({ canvasElement, ...rest }) => {
86
+ const canvas = within(canvasElement);
87
+
88
+ await WithChildItems.play({ canvasElement, ...rest });
89
+
90
+ await waitFor(() => expect(canvas.getAllByText('Test child 3')[0]).toBeTruthy(), {
91
+ timeout: 5000,
92
+ });
93
+
94
+ await fireEvent.click(canvas.getAllByText('Test child 3')[0]);
95
+
96
+ await expect(canvas.getAllByText('Test deep child 3')[0]).toBeTruthy();
97
+ },
98
+ };
99
+
100
+ export const BackButtonsWork: Story = {
101
+ ...ItemsCanBeTraversed,
102
+ play: async ({ canvasElement, ...rest }) => {
103
+ const canvas = within(canvasElement);
104
+
105
+ await ItemsCanBeTraversed.play({ canvasElement, ...rest });
106
+
107
+ await fireEvent.click(document.querySelector('.reqore-dropdown-back-button'));
108
+
109
+ await expect(canvas.getAllByText('Test child 3')[0]).toBeTruthy();
110
+ },
111
+ };
@@ -107,6 +107,7 @@ const Template: StoryFn<typeof ReqoreInput> = (args: IReqoreInputProps) => {
107
107
  placeholder='Clearable Input'
108
108
  onClearClick={handleValueClear}
109
109
  value={value}
110
+ focusRules={{ type: 'auto' }}
110
111
  onChange={handleValueChange}
111
112
  />
112
113
  <ReqoreInput