@takaro/lib-components 0.0.13 → 0.0.15

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 (39) hide show
  1. package/Dockerfile.dev +1 -1
  2. package/package.json +21 -20
  3. package/src/components/actions/ToggleButton/ToggleButton.stories.tsx +1 -0
  4. package/src/components/actions/ToggleButton/ToggleButton.tsx +3 -1
  5. package/src/components/actions/ToggleButton/ToggleButtonGroup.tsx +16 -2
  6. package/src/components/charts/AreaChart/AreaChart.stories.tsx +15 -20
  7. package/src/components/charts/GeoMercator/GeoMercator.stories.tsx +24 -18
  8. package/src/components/charts/GeoMercator/index.tsx +160 -58
  9. package/src/components/charts/GeoMercator/iso3166-alpha2-to-alpha3.ts +250 -0
  10. package/src/components/charts/GeoMercator/world.json +333 -0
  11. package/src/components/charts/ZoomControls.tsx +47 -0
  12. package/src/components/charts/index.tsx +3 -0
  13. package/src/components/data/Drawer/Drawer.stories.tsx +19 -10
  14. package/src/components/data/Drawer/DrawerContent.tsx +56 -1
  15. package/src/components/data/Drawer/useDrawer.tsx +16 -2
  16. package/src/components/data/Table/index.tsx +94 -33
  17. package/src/components/data/Table/style.ts +21 -0
  18. package/src/components/data/Table/subcomponents/Pagination/PageSizeSelect.tsx +1 -0
  19. package/src/components/dialogs/Dialog/DialogContent.tsx +1 -5
  20. package/src/components/feedback/Alert/Alert.stories.tsx +1 -10
  21. package/src/components/feedback/Alert/index.tsx +11 -15
  22. package/src/components/feedback/Alert/style.ts +9 -11
  23. package/src/components/feedback/ErrorPage/index.tsx +2 -2
  24. package/src/components/feedback/snacks/Drawer/Drawer.stories.tsx +1 -1
  25. package/src/components/feedback/snacks/Drawer/index.tsx +4 -0
  26. package/src/components/inputs/Date/DatePicker/Controlled.tsx +2 -0
  27. package/src/components/inputs/Date/DatePicker/DatePicker.stories.tsx +1 -1
  28. package/src/components/inputs/Date/DatePicker/Generic.tsx +20 -4
  29. package/src/components/inputs/Date/DatePicker/style.ts +3 -4
  30. package/src/components/inputs/index.ts +7 -0
  31. package/src/components/inputs/selects/SelectQueryField/Controlled.tsx +10 -0
  32. package/src/components/inputs/selects/SelectQueryField/Generic/index.tsx +39 -16
  33. package/src/components/inputs/selects/SelectQueryField/SelectQueryField.stories.tsx +20 -0
  34. package/src/components/inputs/selects/SubComponents/OptionGroup.tsx +4 -2
  35. package/src/components/other/CollapseList/index.tsx +26 -31
  36. package/src/components/visual/Card/Card.stories.tsx +4 -1
  37. package/src/components/visual/Card/CardBody.tsx +11 -0
  38. package/src/components/visual/Card/CardTitle.tsx +23 -0
  39. package/src/components/visual/Card/index.tsx +21 -14
@@ -18,10 +18,9 @@ export const ResultContainer = styled.div<{ readOnly: boolean; hasError: boolean
18
18
  z-index: ${({ theme }) => theme.zIndex.dropdown};
19
19
  cursor: ${({ readOnly }) => (readOnly ? 'not-allowed' : 'pointer')};
20
20
  user-select: none;
21
-
22
- span {
23
- color: ${({ theme }) => theme.colors.primary};
24
- }
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: space-between;
25
24
  `;
26
25
 
27
26
  export const ContentContainer = styled.div`
@@ -1,3 +1,10 @@
1
+ export interface PaginationProps {
2
+ isFetching: boolean;
3
+ hasNextPage: boolean;
4
+ isFetchingNextPage: boolean;
5
+ fetchNextPage: () => void;
6
+ }
7
+
1
8
  export { ControlledCheckBox as CheckBox } from './CheckBox';
2
9
  export type { ControlledCheckBoxProps as CheckBoxProps } from './CheckBox';
3
10
  export { GenericCheckBox as UnControlledCheckBox } from './CheckBox/Generic';
@@ -29,6 +29,11 @@ export const ControlledSelectQueryField: FC<ControlledSelectQueryFieldProps> & S
29
29
  inPortal,
30
30
  debounce,
31
31
  isLoadingData,
32
+ hasNextPage,
33
+ fetchNextPage,
34
+ isFetching,
35
+ isFetchingNextPage,
36
+ optionCount,
32
37
  handleInputValueChange,
33
38
  } = defaultsApplier(props);
34
39
 
@@ -85,7 +90,12 @@ export const ControlledSelectQueryField: FC<ControlledSelectQueryFieldProps> & S
85
90
  onFocus={handleOnFocus}
86
91
  value={field.value}
87
92
  debounce={debounce}
93
+ optionCount={optionCount}
88
94
  handleInputValueChange={handleInputValueChange}
95
+ isFetching={isFetching}
96
+ isFetchingNextPage={isFetchingNextPage}
97
+ hasNextPage={hasNextPage}
98
+ fetchNextPage={fetchNextPage}
89
99
  >
90
100
  {children}
91
101
  </GenericSelectQueryField>
@@ -28,14 +28,15 @@ import { useDebounce } from '../../../../../hooks';
28
28
  import { setAriaDescribedBy } from '../../../layout';
29
29
  import { FeedBackContainer } from '../style';
30
30
  import { SelectItem, SelectContext, getLabelFromChildren } from '../../';
31
+ import { PaginationProps } from '../../../';
31
32
 
32
33
  /* The SearchField depends on a few things of <Select/> */
33
34
  import { GroupLabel } from '../../SelectField/style';
34
35
  import { SelectContainer, SelectButton, StyledArrowIcon, StyledFloatingOverlay } from '../../sharedStyle';
35
- import { IconButton, Spinner } from '../../../../../components';
36
+ import { IconButton, InfiniteScroll, Spinner } from '../../../../../components';
36
37
  import { GenericTextField } from '../../../TextField/Generic';
37
38
 
38
- interface SharedSelectQueryFieldProps {
39
+ interface SharedSelectQueryFieldProps extends PaginationProps {
39
40
  // Enables loading data feedback for user
40
41
  isLoadingData?: boolean;
41
42
  /// The placeholder text to show when the input is empty
@@ -44,7 +45,7 @@ interface SharedSelectQueryFieldProps {
44
45
  debounce?: number;
45
46
  /// Triggered whenever the input value changes.
46
47
  /// This is used to trigger the API call to get the new options
47
- handleInputValueChange: (value: string) => void;
48
+ handleInputValueChange?: (value: string) => void;
48
49
  /// render inPortal
49
50
  inPortal?: boolean;
50
51
 
@@ -53,6 +54,9 @@ interface SharedSelectQueryFieldProps {
53
54
 
54
55
  /// The selected items shown in the select field
55
56
  render?: (selectedItems: SelectItem[]) => React.ReactNode;
57
+
58
+ /// The total options that will be visible when fully loaded
59
+ optionCount?: number;
56
60
  }
57
61
 
58
62
  interface SingleSelectQueryFieldProps extends SharedSelectQueryFieldProps {
@@ -100,12 +104,17 @@ export const GenericSelectQueryField = forwardRef<HTMLInputElement, GenericSelec
100
104
  hasError,
101
105
  children,
102
106
  readOnly,
107
+ isFetchingNextPage,
108
+ isFetching,
109
+ fetchNextPage,
110
+ hasNextPage,
103
111
  render,
104
112
  multiple = false,
105
113
  canClear = false,
106
114
  debounce = 250,
107
115
  isLoadingData: isLoading = false,
108
116
  handleInputValueChange,
117
+ optionCount,
109
118
  } = defaultsApplier(props);
110
119
 
111
120
  const [open, setOpen] = useState<boolean>(false);
@@ -117,8 +126,10 @@ export const GenericSelectQueryField = forwardRef<HTMLInputElement, GenericSelec
117
126
  const listItemsRef = useRef<Array<HTMLLIElement | null>>([]);
118
127
 
119
128
  useEffect(() => {
120
- if ((inputValue.shouldUpdate && debouncedValue) || (inputValue && debouncedValue === ''))
121
- handleInputValueChange(debouncedValue);
129
+ if (handleInputValueChange) {
130
+ if ((inputValue.shouldUpdate && debouncedValue) || (inputValue && debouncedValue === ''))
131
+ handleInputValueChange(debouncedValue);
132
+ }
122
133
  }, [debouncedValue]);
123
134
 
124
135
  const { refs, strategy, x, y, context } = useFloating<HTMLInputElement>({
@@ -196,17 +207,21 @@ export const GenericSelectQueryField = forwardRef<HTMLInputElement, GenericSelec
196
207
  },
197
208
  })}
198
209
  >
199
- <GenericTextField
200
- id={`${name}-input`}
201
- name={`${name}-input`}
202
- hasDescription={false}
203
- icon={<SearchIcon />}
204
- hasError={hasError}
205
- value={inputValue.value}
206
- onChange={onInputChange}
207
- placeholder={placeholder}
208
- ref={ref}
209
- />
210
+ {' '}
211
+ {handleInputValueChange && (
212
+ <GenericTextField
213
+ id={`${name}-input`}
214
+ name={`${name}-input`}
215
+ hasDescription={false}
216
+ icon={<SearchIcon />}
217
+ suffix={isLoading ? 'Loading' : optionCount !== undefined ? `Result: ${optionCount}` : undefined}
218
+ hasError={hasError}
219
+ value={inputValue.value}
220
+ onChange={onInputChange}
221
+ placeholder={placeholder}
222
+ ref={ref}
223
+ />
224
+ )}
210
225
  {/* it will always contain 1 because of the group label */}
211
226
  {isLoading && (
212
227
  <FeedBackContainer>
@@ -215,6 +230,14 @@ export const GenericSelectQueryField = forwardRef<HTMLInputElement, GenericSelec
215
230
  </FeedBackContainer>
216
231
  )}
217
232
  {hasOptions && options}
233
+ {hasOptions && !isLoading && (
234
+ <InfiniteScroll
235
+ isFetching={isFetching}
236
+ hasNextPage={hasNextPage}
237
+ fetchNextPage={fetchNextPage}
238
+ isFetchingNextPage={isFetchingNextPage}
239
+ />
240
+ )}
218
241
  {/* Basically first interaction */}
219
242
  {!hasOptions && inputValue.value === '' && <FeedBackContainer>Start typing to search</FeedBackContainer>}
220
243
  {/* When there is no result */}
@@ -69,6 +69,11 @@ export const ServerSideSubmit: StoryFn<SelectQueryFieldProps> = (args) => {
69
69
  handleInputValueChange={mockAPICall}
70
70
  isLoadingData={loading}
71
71
  required={false}
72
+ hasNextPage={false}
73
+ optionCount={10}
74
+ isFetching={false}
75
+ isFetchingNextPage={false}
76
+ fetchNextPage={() => {}}
72
77
  name="film"
73
78
  >
74
79
  {/* In this case the label is the same as the value but ofcourse that can differ*/}
@@ -124,6 +129,11 @@ export const ClientSideSubmit: StoryFn<SelectQueryFieldProps> = (args) => {
124
129
  handleInputValueChange={handleInputChange}
125
130
  required={false}
126
131
  debounce={0}
132
+ hasNextPage={false}
133
+ optionCount={10}
134
+ isFetching={false}
135
+ isFetchingNextPage={false}
136
+ fetchNextPage={() => {}}
127
137
  name="film"
128
138
  >
129
139
  {/* In this case the label is the same as the value but ofcourse that can differ*/}
@@ -186,6 +196,11 @@ export const ClientSideMultiSelectSubmit: StoryFn<SelectQueryFieldProps> = (args
186
196
  required={false}
187
197
  canClear={args.canClear}
188
198
  debounce={0}
199
+ hasNextPage={false}
200
+ optionCount={10}
201
+ isFetching={false}
202
+ isFetchingNextPage={false}
203
+ fetchNextPage={() => {}}
189
204
  multiple
190
205
  name="films"
191
206
  >
@@ -240,6 +255,11 @@ export const Generic: StoryFn<SelectQueryFieldProps> = () => {
240
255
  hasDescription={false}
241
256
  value={result}
242
257
  name="film"
258
+ hasNextPage={false}
259
+ optionCount={10}
260
+ isFetching={false}
261
+ isFetchingNextPage={false}
262
+ fetchNextPage={() => {}}
243
263
  >
244
264
  {/* In this case the label is the same as the value but ofcourse that can differ*/}
245
265
  <SelectQueryField.OptionGroup>
@@ -6,10 +6,12 @@ export interface OptionGroupProps extends PropsWithChildren {
6
6
  }
7
7
 
8
8
  export const OptionGroup: FC<OptionGroupProps> = ({ children, label }) => {
9
- /* Currently this is actually never rendered. in Select.index.tsx the OptionGroup is built based on the props*/
9
+ {
10
+ /* This is actually never rendered, the optiongroup is build in the select fields themself*/
11
+ }
10
12
  return (
11
13
  <li>
12
- {label} && <div>{label}</div>
14
+ {label && <div>{label}</div>}
13
15
  <ul>{children}</ul>
14
16
  </li>
15
17
  );
@@ -1,7 +1,7 @@
1
1
  import { FC, PropsWithChildren, ReactElement, useState } from 'react';
2
2
  import { styled } from '../../../styled';
3
3
  import { IoMdArrowDropup as ArrowUp } from 'react-icons/io';
4
- import { AnimatePresence, motion } from 'framer-motion';
4
+ import { motion } from 'framer-motion';
5
5
  import { useTheme } from '../../../hooks';
6
6
 
7
7
  const StyledList = styled.div`
@@ -78,36 +78,31 @@ const Item: FC<PropsWithChildren<ItemProps>> = ({ collapsed = false, title, chil
78
78
  <ArrowUp size={18} />
79
79
  </Header>
80
80
  {description && <p>{description}</p>}
81
- <AnimatePresence>
82
- {!isCollapsed && (
83
- <motion.div
84
- style={{ maxHeight: '100%', overflowY: 'hidden' }}
85
- key={`collapse-item-${title}`}
86
- variants={{
87
- open: { opacity: 1, height: 'auto', flexGrow: 1, minHeight: 0, overflowY: 'auto' },
88
- collapsed: { opacity: 0, height: 0 },
89
- }}
90
- initial="collapsed"
91
- animate="open"
92
- exit="collapsed"
93
- transition={{ duration: 0.125, ease: 'linear' }}
94
- >
95
- <motion.div
96
- variants={{
97
- open: { y: 0 },
98
- collapsed: { y: -6 },
99
- }}
100
- transition={{ duration: 0.125, ease: 'linear' }}
101
- style={{
102
- transformOrigin: 'top center',
103
- padding: `${theme.spacing['0_75']} ${theme.spacing['0_5']} ${theme.spacing['1_5']} ${theme.spacing['1']}`,
104
- }}
105
- >
106
- {children}
107
- </motion.div>
108
- </motion.div>
109
- )}
110
- </AnimatePresence>
81
+ <motion.div
82
+ style={{ maxHeight: '100%', overflowY: 'hidden' }}
83
+ key={`collapse-item-${title}`}
84
+ variants={{
85
+ open: { opacity: 1, height: 'auto', flexGrow: 1, minHeight: 0, overflowY: 'auto', visibility: 'visible' },
86
+ collapsed: { opacity: 0, height: 0, visibility: 'hidden' },
87
+ }}
88
+ initial="collapsed"
89
+ animate={isCollapsed ? 'collapsed' : 'open'}
90
+ transition={{ duration: 0.125, ease: 'linear' }}
91
+ >
92
+ <motion.div
93
+ variants={{
94
+ open: { y: 0 },
95
+ collapsed: { y: -6 },
96
+ }}
97
+ transition={{ duration: 0.125, ease: 'linear' }}
98
+ style={{
99
+ transformOrigin: 'top center',
100
+ padding: `${theme.spacing['0_75']} ${theme.spacing['0_5']} ${theme.spacing['1_5']} ${theme.spacing['1']}`,
101
+ }}
102
+ >
103
+ {children}
104
+ </motion.div>
105
+ </motion.div>
111
106
  </div>
112
107
  );
113
108
  };
@@ -13,7 +13,10 @@ export default {
13
13
  export const Default: StoryFn<CardProps> = (args) => {
14
14
  return (
15
15
  <Card variant={args.variant} onClick={() => {}}>
16
- this is the content
16
+ <Card.Title label="This is the title">
17
+ <span>Here we can put logic that goes on the right side</span>
18
+ </Card.Title>
19
+ <Card.Body>this is the body</Card.Body>
17
20
  </Card>
18
21
  );
19
22
  };
@@ -0,0 +1,11 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { styled } from '../../../styled';
3
+
4
+ const Container = styled.div`
5
+ padding: ${({ theme }) => theme.spacing[2]};
6
+ min-width: 20rem;
7
+ `;
8
+
9
+ export const CardBody: FC<PropsWithChildren> = (props) => {
10
+ return <Container>{props.children}</Container>;
11
+ };
@@ -0,0 +1,23 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { styled } from '../../../styled';
3
+
4
+ const Container = styled.div`
5
+ display: flex;
6
+ justify-content: space-between;
7
+ align-items: center;
8
+ border-bottom: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
9
+ padding: ${({ theme }) => theme.spacing[1]};
10
+ `;
11
+
12
+ interface CardTitleProps {
13
+ label: string;
14
+ }
15
+
16
+ export const CardTitle: FC<PropsWithChildren<CardTitleProps>> = (props) => {
17
+ return (
18
+ <Container>
19
+ <h2>{props.label}</h2>
20
+ <div>{props.children}</div>
21
+ </Container>
22
+ );
23
+ };
@@ -1,5 +1,7 @@
1
- import { forwardRef, HTMLProps } from 'react';
1
+ import { FC, HTMLProps, PropsWithChildren } from 'react';
2
2
  import { styled } from '../../../styled';
3
+ import { CardTitle } from './CardTitle';
4
+ import { CardBody } from './CardBody';
3
5
 
4
6
  type Variant = 'default' | 'outline';
5
7
 
@@ -8,8 +10,8 @@ const Container = styled.div<{ canClick: boolean; variant: Variant }>`
8
10
  background-color: ${({ theme, variant }) =>
9
11
  variant === 'outline' ? theme.colors.background : theme.colors.backgroundAlt};
10
12
  border: 0.1rem solid ${({ theme }) => theme.colors.backgroundAccent};
11
- padding: ${({ theme }) => theme.spacing[2]};
12
13
  cursor: ${({ canClick }) => (canClick ? 'pointer' : 'default')};
14
+ height: fit-content;
13
15
 
14
16
  &:focus-within {
15
17
  border-color: none;
@@ -31,22 +33,27 @@ export interface CardProps extends HTMLProps<HTMLDivElement> {
31
33
  variant?: Variant;
32
34
  }
33
35
 
34
- // Forward ref and spread all props to the Container
35
- export const Card = forwardRef<HTMLDivElement, CardProps>(function Card(
36
- { children, variant = 'default', ...props },
37
- ref,
38
- ) {
39
- const canClick = 'onClick' in props;
36
+ interface SubComponentTypes {
37
+ Title: typeof CardTitle;
38
+ Body: typeof CardBody;
39
+ }
40
40
 
41
- // Extract the className prop, if present
41
+ export const Card: FC<PropsWithChildren<CardProps>> & SubComponentTypes = function Card({
42
+ children,
43
+ variant = 'default',
44
+ ...props
45
+ }) {
46
+ const canClick = 'onClick' in props;
42
47
  const { className, ...restProps } = props;
43
48
 
44
49
  return (
45
- // TODO: type this properly
46
- //eslint-disable-next-line
47
- //@ts-ignore
48
- <Container ref={ref} canClick={canClick} variant={variant} className={className} {...restProps}>
50
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
51
+ // @ts-ignore
52
+ <Container canClick={canClick} variant={variant} className={className} {...restProps}>
49
53
  {children}
50
54
  </Container>
51
55
  );
52
- });
56
+ };
57
+
58
+ Card.Title = CardTitle;
59
+ Card.Body = CardBody;