@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
@@ -1,5 +1,6 @@
1
1
  import { forwardRef, HTMLProps, useState } from 'react';
2
2
  import { styled } from '../../../styled';
3
+ import { Button } from '../../../components';
3
4
  import { AnimatePresence, motion, PanInfo } from 'framer-motion';
4
5
  import SimpleBar from 'simplebar-react';
5
6
 
@@ -15,6 +16,39 @@ const Container = styled(motion.div)`
15
16
  border-left: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
16
17
  `;
17
18
 
19
+ const ButtonContainer = styled.div`
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: flex-start;
23
+ gap: ${({ theme }) => theme.spacing[1]};
24
+ `;
25
+
26
+ const CloseConfirmationWrapper = styled.div`
27
+ position: absolute;
28
+ top: 0;
29
+ left: 0;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ width: 100%;
34
+ height: 100%;
35
+ background: rgba(0, 0, 0, 0.8);
36
+ `;
37
+
38
+ const CloseConfirmationContainer = styled.div`
39
+ height: 150px;
40
+ padding: ${({ theme }) => theme.spacing[2]};
41
+ width: calc(100% - 200px);
42
+ background-color: ${({ theme }) => theme.colors.background};
43
+ border: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
44
+ border-radius: ${({ theme }) => theme.borderRadius.medium};
45
+
46
+ h2,
47
+ p {
48
+ margin-bottom: ${({ theme }) => theme.spacing[1]};
49
+ }
50
+ `;
51
+
18
52
  export const HandleContainer = styled.div`
19
53
  height: 80vh;
20
54
  top: 10vh;
@@ -35,7 +69,16 @@ export const HandleContainer = styled.div`
35
69
  `;
36
70
 
37
71
  export const DrawerContent = forwardRef<HTMLElement, HTMLProps<HTMLDivElement>>(function DrawerContent(props, propRef) {
38
- const { context, labelId, descriptionId, getFloatingProps, setOpen, canDrag } = useDrawerContext();
72
+ const {
73
+ context,
74
+ labelId,
75
+ descriptionId,
76
+ getFloatingProps,
77
+ setOpen,
78
+ canDrag,
79
+ showConfirmDialog,
80
+ setShowConfirmDialog,
81
+ } = useDrawerContext();
39
82
 
40
83
  const ref = useMergeRefs([context.refs.setFloating, propRef]);
41
84
  const [dragPosition, setDragPosition] = useState<number>(0);
@@ -100,6 +143,18 @@ export const DrawerContent = forwardRef<HTMLElement, HTMLProps<HTMLDivElement>>(
100
143
  </HandleContainer>
101
144
  )}
102
145
  <SimpleBar style={{ maxHeight: '92vh' }}>{props.children}</SimpleBar>
146
+ {showConfirmDialog && (
147
+ <CloseConfirmationWrapper>
148
+ <CloseConfirmationContainer>
149
+ <h2>Your progress will be lost</h2>
150
+ <p>Are you sure you want to exit? Your progress will not be saved.</p>
151
+ <ButtonContainer>
152
+ <Button text="Cancel" color="secondary" onClick={() => setShowConfirmDialog(false)} />
153
+ <Button text="Discard changes" onClick={() => setOpen(false)} color="error" />
154
+ </ButtonContainer>
155
+ </CloseConfirmationContainer>
156
+ </CloseConfirmationWrapper>
157
+ )}
103
158
  </Container>
104
159
  </FloatingFocusManager>
105
160
  </FloatingOverlay>
@@ -6,17 +6,20 @@ export interface DrawerOptions {
6
6
  initialOpen?: boolean;
7
7
  onOpenChange?: (open: boolean) => void;
8
8
  canDrag?: boolean;
9
+ promptCloseConfirmation?: boolean;
9
10
  }
10
11
 
11
12
  export function useDrawer({
12
13
  initialOpen = false,
13
14
  canDrag = false,
14
15
  open: controlledOpen,
16
+ promptCloseConfirmation = false,
15
17
  onOpenChange: setControlledOpen,
16
18
  }: DrawerOptions) {
17
19
  const [uncontrolledOpen, setUncontrolledOpen] = useState(initialOpen);
18
20
  const [labelId, setLabelId] = useState<string | undefined>();
19
21
  const [descriptionId, setDescriptionId] = useState<string | undefined>();
22
+ const [showConfirmDialog, setShowConfirmDialog] = useState<boolean>(false);
20
23
 
21
24
  const open = controlledOpen ?? uncontrolledOpen;
22
25
  const setOpen = setControlledOpen ?? setUncontrolledOpen;
@@ -32,13 +35,24 @@ export function useDrawer({
32
35
  useClick(context, {
33
36
  enabled: controlledOpen == null,
34
37
  }),
35
- useDismiss(context, { outsidePressEvent: 'mousedown' }),
38
+ useDismiss(context, {
39
+ outsidePressEvent: 'mousedown',
40
+ outsidePress: (_mouseEvent) => {
41
+ if (promptCloseConfirmation === true) {
42
+ setShowConfirmDialog(true);
43
+ return false;
44
+ }
45
+ return true;
46
+ },
47
+ }),
36
48
  ]);
37
49
 
38
50
  return useMemo(
39
51
  () => ({
40
52
  open,
41
53
  setOpen,
54
+ showConfirmDialog,
55
+ setShowConfirmDialog,
42
56
  ...interactions,
43
57
  ...data,
44
58
  labelId,
@@ -47,6 +61,6 @@ export function useDrawer({
47
61
  setDescriptionId,
48
62
  canDrag,
49
63
  }),
50
- [open, setOpen, interactions, data, labelId, descriptionId],
64
+ [open, setOpen, interactions, data, labelId, descriptionId, showConfirmDialog],
51
65
  );
52
66
  }
@@ -17,11 +17,18 @@ import {
17
17
  VisibilityState,
18
18
  ColumnPinningState,
19
19
  RowSelectionState,
20
+ ExpandedState,
21
+ getExpandedRowModel,
22
+ Row,
20
23
  } from '@tanstack/react-table';
21
24
  import { Wrapper, StyledTable, Toolbar, Flex, TableWrapper } from './style';
22
- import { Button, Empty, Spinner, ToggleButtonGroup } from '../../../components';
23
- import { AiOutlinePicCenter as RelaxedDensityIcon, AiOutlinePicRight as TightDensityIcon } from 'react-icons/ai';
24
-
25
+ import { Button, Empty, IconButton, Spinner, ToggleButtonGroup } from '../../../components';
26
+ import {
27
+ AiOutlinePicCenter as RelaxedDensityIcon,
28
+ AiOutlinePicRight as TightDensityIcon,
29
+ AiOutlineRight as ExpandIcon,
30
+ AiOutlineUp as CollapseIcon,
31
+ } from 'react-icons/ai';
25
32
  import { ColumnHeader } from './subcomponents/ColumnHeader';
26
33
  import { ColumnVisibility } from './subcomponents/ColumnVisibility';
27
34
  import { Filter } from './subcomponents/Filter';
@@ -37,8 +44,12 @@ export interface TableProps<DataType extends object> {
37
44
  data: DataType[];
38
45
  isLoading?: boolean;
39
46
 
40
- // currently not possible to type this properly: https://github.com/TanStack/table/issues/4241
47
+ /// Condition for row to be expandable
48
+ canExpand?: (row: Row<DataType>) => boolean;
49
+ /// What to render when row can be expanded
50
+ renderDetailPanel?: (row: Row<DataType>) => JSX.Element;
41
51
 
52
+ /// currently not possible to type this properly: https://github.com/TanStack/table/issues/4241
42
53
  columns: ColumnDef<DataType, any>[];
43
54
 
44
55
  /// Renders actions that are always visible
@@ -83,7 +94,9 @@ export function Table<DataType extends object>({
83
94
  title,
84
95
  rowSelection,
85
96
  columnSearch,
97
+ renderDetailPanel,
86
98
  renderToolbar,
99
+ canExpand = () => false,
87
100
  renderRowSelectionActions,
88
101
  isLoading = false,
89
102
  }: TableProps<DataType>) {
@@ -111,9 +124,13 @@ export function Table<DataType extends object>({
111
124
  {} as Record<string, boolean>,
112
125
  );
113
126
  });
127
+
114
128
  const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({});
115
129
  const { storedValue: density, setValue: setDensity } = useLocalStorage<Density>(`table-density-${id}`, 'tight');
116
130
 
131
+ // Might because potentially none fullfil the canExpand condtion.
132
+ const rowsMightExpand = renderDetailPanel ? true : false;
133
+ const [expanded, setExpanded] = useState<ExpandedState>({});
117
134
  const [openColumnVisibilityTooltip, setOpenColumnVisibilityTooltip] = useState<boolean>(false);
118
135
  const [hasShownColumnVisibilityTooltip, setHasShownColumnVisibilityTooltip] = useState<boolean>(false);
119
136
  const [columnOrder, setColumnOrder] = useState<ColumnOrderState>(
@@ -126,6 +143,7 @@ export function Table<DataType extends object>({
126
143
  );
127
144
 
128
145
  const ROW_SELECTION_COL_SPAN = rowSelection ? 1 : 0;
146
+ const EXPAND_ROW_COL_SPAN = rowsMightExpand ? 1 : 0;
129
147
  const MINIMUM_ROW_COUNT_FOR_PAGINATION = 5;
130
148
 
131
149
  // handles the column visibility tooltip (shows tooltip when the first column is hidden)
@@ -154,6 +172,7 @@ export function Table<DataType extends object>({
154
172
  data,
155
173
  columns,
156
174
  getCoreRowModel: getCoreRowModel(),
175
+ getExpandedRowModel: getExpandedRowModel(),
157
176
  pageCount: pagination?.pageOptions.pageCount ?? -1,
158
177
  manualPagination: true,
159
178
  paginateExpandedRows: true, // Expanded rows will be paginated this means that rows that take up more space will be shown on next page.
@@ -169,6 +188,7 @@ export function Table<DataType extends object>({
169
188
  enablePinning: true,
170
189
  enableHiding: !!columnVisibility,
171
190
  enableRowSelection: !!rowSelection,
191
+ getRowCanExpand: canExpand,
172
192
  autoResetPageIndex: false,
173
193
 
174
194
  columnResizeMode: 'onChange',
@@ -180,6 +200,7 @@ export function Table<DataType extends object>({
180
200
  onColumnOrderChange: setColumnOrder,
181
201
  onColumnPinningChange: setColumnPinning,
182
202
  onRowSelectionChange: rowSelection ? rowSelection?.setRowSelectionState : undefined,
203
+ onExpandedChange: setExpanded,
183
204
 
184
205
  initialState: {
185
206
  columnVisibility,
@@ -194,6 +215,7 @@ export function Table<DataType extends object>({
194
215
  state: {
195
216
  columnVisibility,
196
217
  columnOrder,
218
+ expanded,
197
219
  sorting: sorting.sortingState,
198
220
  columnFilters: columnFiltering.columnFiltersState,
199
221
  globalFilter: columnSearch.columnSearchState,
@@ -204,6 +226,7 @@ export function Table<DataType extends object>({
204
226
  });
205
227
 
206
228
  const tableHasNoData = isLoading === false && table.getRowModel().rows.length === 0;
229
+ const tableHasData = isLoading === false && table.getRowModel().rows.length !== 0;
207
230
 
208
231
  // rowSelection.rowSelectionState has the following shape: { [rowId: string]: boolean }
209
232
  const hasRowSelection = useMemo(() => {
@@ -254,7 +277,7 @@ export function Table<DataType extends object>({
254
277
  <thead>
255
278
  {table.getHeaderGroups().map((headerGroup) => (
256
279
  <tr key={headerGroup.id}>
257
- {rowSelection && table.getRowModel().rows.length !== 0 && !isLoading && (
280
+ {rowSelection && tableHasData && (
258
281
  <Th
259
282
  isActive={false}
260
283
  isRight={false}
@@ -276,6 +299,17 @@ export function Table<DataType extends object>({
276
299
  </div>
277
300
  </Th>
278
301
  )}
302
+ {rowsMightExpand && tableHasData && (
303
+ <Th
304
+ isActive={false}
305
+ isRight={false}
306
+ isDragging={false}
307
+ canDrag={false}
308
+ isRowSelection={true}
309
+ width={15}
310
+ />
311
+ )}
312
+
279
313
  {headerGroup.headers.map((header) => (
280
314
  <ColumnHeader
281
315
  header={header}
@@ -291,7 +325,7 @@ export function Table<DataType extends object>({
291
325
  {/* loading state */}
292
326
  {isLoading && (
293
327
  <tr>
294
- <td colSpan={table.getAllColumns().length + ROW_SELECTION_COL_SPAN}>
328
+ <td colSpan={table.getAllColumns().length + ROW_SELECTION_COL_SPAN + EXPAND_ROW_COL_SPAN}>
295
329
  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '15px' }}>
296
330
  <Spinner size="small" />
297
331
  </div>
@@ -302,11 +336,11 @@ export function Table<DataType extends object>({
302
336
  {/* empty state */}
303
337
  {tableHasNoData && (
304
338
  <tr>
305
- <td colSpan={table.getAllColumns().length + ROW_SELECTION_COL_SPAN}>
339
+ <td colSpan={table.getAllColumns().length + ROW_SELECTION_COL_SPAN + EXPAND_ROW_COL_SPAN}>
306
340
  <div style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
307
341
  <Empty
308
342
  header=""
309
- description="Items will appear here. Add your first item to begin!"
343
+ description="Data will appear here."
310
344
  actions={[
311
345
  <Button
312
346
  key={id + '-learn-more-button'}
@@ -323,30 +357,51 @@ export function Table<DataType extends object>({
323
357
 
324
358
  {!isLoading &&
325
359
  table.getRowModel().rows.map((row) => (
326
- <tr key={row.id}>
327
- {row.getCanSelect() && (
328
- <td style={{ paddingRight: '10px', width: '15px' }}>
329
- <CheckBox
330
- value={row.getIsSelected()}
331
- id={row.id}
332
- name={row.id}
333
- hasError={false}
334
- disabled={!row.getCanSelect()}
335
- onChange={() => row.toggleSelected()}
336
- hasDescription={false}
337
- size="small"
338
- />
339
- </td>
340
- )}
341
- {row.getVisibleCells().map(({ column, id, getContext }) => (
342
- <td key={id}>{flexRender(column.columnDef.cell, getContext())}</td>
343
- ))}
344
- {row.getIsExpanded() && (
345
- <tr>
346
- <td colSpan={table.getVisibleLeafColumns().length} />
347
- </tr>
348
- )}
349
- </tr>
360
+ <>
361
+ <tr key={row.id}>
362
+ {row.getCanExpand() ? (
363
+ <td style={{ paddingRight: '10px', width: '15px' }}>
364
+ {row.getIsExpanded() ? (
365
+ <IconButton
366
+ size="tiny"
367
+ icon={<CollapseIcon />}
368
+ ariaLabel="Collapse expanded row"
369
+ onClick={() => row.toggleExpanded(false)}
370
+ />
371
+ ) : (
372
+ <IconButton
373
+ size="tiny"
374
+ icon={<ExpandIcon />}
375
+ ariaLabel="expand row"
376
+ onClick={() => row.toggleExpanded(true)}
377
+ />
378
+ )}
379
+ </td>
380
+ ) : rowsMightExpand ? (
381
+ <td />
382
+ ) : (
383
+ <></>
384
+ )}
385
+ {row.getCanSelect() && (
386
+ <td style={{ paddingRight: '10px', width: '15px' }}>
387
+ <CheckBox
388
+ value={row.getIsSelected()}
389
+ id={row.id}
390
+ name={row.id}
391
+ hasError={false}
392
+ disabled={!row.getCanSelect()}
393
+ onChange={() => row.toggleSelected()}
394
+ hasDescription={false}
395
+ size="small"
396
+ />
397
+ </td>
398
+ )}
399
+ {row.getVisibleCells().map(({ column, id, getContext }) => (
400
+ <td key={id}>{flexRender(column.columnDef.cell, getContext())}</td>
401
+ ))}
402
+ </tr>
403
+ {row.getIsExpanded() && renderDetailPanel!(row)}
404
+ </>
350
405
  ))}
351
406
  </tbody>
352
407
 
@@ -355,9 +410,15 @@ export function Table<DataType extends object>({
355
410
  <tr>
356
411
  {/* This is the row selection */}
357
412
  {ROW_SELECTION_COL_SPAN ? <td colSpan={1} /> : null}
413
+ {/* This is for the row expansion icon */}
414
+ {EXPAND_ROW_COL_SPAN ? <td colSpan={1} /> : null}
358
415
  {pagination && (
359
416
  <>
360
- <td colSpan={table.getVisibleLeafColumns().length - 3 - ROW_SELECTION_COL_SPAN} />
417
+ <td
418
+ colSpan={
419
+ table.getVisibleLeafColumns().length - 3 - ROW_SELECTION_COL_SPAN - EXPAND_ROW_COL_SPAN
420
+ }
421
+ />
361
422
  <td colSpan={1}>
362
423
  <span>
363
424
  showing {table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1}-
@@ -46,6 +46,27 @@ export const StyledTable = styled.table<{ density: Density }>`
46
46
  border-bottom: none;
47
47
  }
48
48
 
49
+ tr.subrow {
50
+ border-bottom: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
51
+
52
+ &:nth-child(even) {
53
+ background-color: ${({ theme }) => theme.colors.background};
54
+ }
55
+
56
+ &:nth-child(odd) {
57
+ background-color: ${({ theme }) => theme.colors.backgroundAlt};
58
+ }
59
+
60
+ td {
61
+ &:first-child {
62
+ padding-left: 0;
63
+ padding-bottom: 0;
64
+ }
65
+ padding: ${({ theme, density }) =>
66
+ density === 'tight' ? `${theme.spacing['0_5']} 0` : `${theme.spacing['0_5']} 0`};
67
+ }
68
+ }
69
+
49
70
  td {
50
71
  border-right: none;
51
72
  border-bottom: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
@@ -14,6 +14,7 @@ export const PageSizeSelect: FC<PageSizeSelectProps> = ({ onPageSizeChange, page
14
14
  id="page-size"
15
15
  multiple={false}
16
16
  name="pageSize"
17
+ inPortal={true}
17
18
  value={pageSize.toString() || '10'}
18
19
  onChange={onPageSizeChange}
19
20
  render={(selectedItems) => {
@@ -10,10 +10,6 @@ const StyledFloatingOverlay = styled(FloatingOverlay)`
10
10
  place-items: center;
11
11
  `;
12
12
 
13
- const CardBody = styled.div`
14
- min-width: 300px;
15
- `;
16
-
17
13
  export const DialogContent = forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(
18
14
  function DialogContent(props, propRef) {
19
15
  const { context: floatingContext, ...context } = useDialogContext();
@@ -37,7 +33,7 @@ export const DialogContent = forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivE
37
33
  ...props,
38
34
  })}
39
35
  >
40
- <CardBody>{props.children}</CardBody>
36
+ <Card.Body>{props.children}</Card.Body>
41
37
  </Card>
42
38
  </FloatingFocusManager>
43
39
  </StyledFloatingOverlay>
@@ -13,16 +13,7 @@ export default {
13
13
 
14
14
  export const Default: StoryFn<AlertProps> = (args) => <Alert {...args} />;
15
15
 
16
- export const Success: StoryObj<AlertProps> = {
17
- args: {
18
- title: 'Order complete',
19
- text: 'Your order has been sucessfully processed. We are completing the report.',
20
- variant: 'success',
21
- dismiss: true,
22
- },
23
- };
24
-
25
- export const Error: StoryObj<AlertProps> = {
16
+ export const WithList: StoryObj<AlertProps> = {
26
17
  args: {
27
18
  title: 'There were errors with your submission',
28
19
  text: [
@@ -24,11 +24,10 @@ export interface AlertProps {
24
24
  elevation?: Elevation;
25
25
  dismiss?: boolean;
26
26
  action?: Action;
27
- showIcon?: boolean;
28
27
  }
29
28
 
30
29
  export const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
31
- { variant, title, text, dismiss = false, elevation = 4, action, showIcon = true },
30
+ { variant, title, text, dismiss = false, elevation = 4, action },
32
31
  ref,
33
32
  ) {
34
33
  const [visible, setVisible] = useState(true);
@@ -85,19 +84,16 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
85
84
  ref={ref}
86
85
  role="status"
87
86
  >
88
- <Grid hasTitle={hasTitle} showIcon={showIcon}>
89
- {showIcon && <IconContainer variant={variant}>{getIcon()}</IconContainer>}
90
-
91
- {/* If title is declared set title, otherwise put everything on single line */}
92
- {title && (
93
- <>
94
- <h2>{title}</h2>
95
- <div />
96
- </>
97
- )}
98
- {renderText()}
99
- {hasTitle ? <div /> : null}
100
- <ButtonContainer hasTitle={hasTitle} show={dismiss || action ? true : false} variant={variant}>
87
+ <Grid>
88
+ <IconContainer variant={variant}>{getIcon()}</IconContainer>
89
+ {title && <h2>{title}</h2>}
90
+ <p>{renderText()}</p>
91
+ <ButtonContainer
92
+ hasTitle={hasTitle}
93
+ show={dismiss || action ? true : false}
94
+ variant={variant}
95
+ className="action"
96
+ >
101
97
  {action && (
102
98
  <Button size="tiny" variant="outline" onClick={handleExecute} text={action.text} color={variant} />
103
99
  )}
@@ -12,7 +12,8 @@ export const Container = styled(motion.div)<{
12
12
  $elevation: Elevation;
13
13
  }>`
14
14
  width: 100%;
15
- padding: ${({ theme }) => `${theme.spacing['0_75']} ${theme.spacing[1]}`};
15
+ padding: ${({ theme }) =>
16
+ `${theme.spacing['0_75']} ${theme.spacing['0_75']} ${theme.spacing['0_75']} ${theme.spacing['0']}`};
16
17
  border-radius: ${({ theme }) => theme.borderRadius.large};
17
18
  box-shadow: ${({ theme, $elevation }) => theme.elevation[$elevation]};
18
19
  margin: ${({ theme }) => `${theme.spacing['1_5']} auto`};
@@ -58,24 +59,21 @@ export const Container = styled(motion.div)<{
58
59
  }}
59
60
  `;
60
61
 
61
- export const Grid = styled.div<{ hasTitle: boolean; showIcon: boolean }>`
62
+ export const Grid = styled.div`
62
63
  display: grid;
63
- grid-template-columns: ${({ theme, hasTitle, showIcon }) => {
64
- const result = '';
65
- if (showIcon) result.concat(`${theme.spacing[5]}`);
66
- result.concat(' 1fr');
67
- if (hasTitle) result.concat(' fit-content(100px)');
68
- return result;
69
- }}
64
+ grid-template-columns: 40px 1fr;
70
65
  align-items: center;
71
- gap: ${({ theme, hasTitle }) => (hasTitle ? 0 : theme.spacing['0_5'])};
66
+
67
+ p,
68
+ .action {
69
+ grid-column: 2;
70
+ }
72
71
  `;
73
72
 
74
73
  export const IconContainer = styled.div<{ variant: AlertVariants }>`
75
74
  display: flex;
76
75
  align-items: center;
77
76
  justify-content: center;
78
- margin-right: ${({ theme }) => theme.spacing['1']};
79
77
  svg {
80
78
  fill: ${({ variant, theme }): string => theme.colors[variant]};
81
79
  }
@@ -122,13 +122,13 @@ const defaultListItems = [
122
122
  icon: <AiOutlineBook />,
123
123
  title: 'Documentation',
124
124
  description: 'Learn how to integrate our tools with your app',
125
- to: 'docs.takaro.io',
125
+ to: 'https://docs.takaro.io',
126
126
  },
127
127
  {
128
128
  icon: <AiOutlineMenu />,
129
129
  title: 'Api reference',
130
130
  description: 'A complete API reference for our libraries',
131
- to: 'https://api.stg.takaro.dev/api.html',
131
+ to: 'https://api.takaro.io/api.html',
132
132
  },
133
133
  /*
134
134
  {
@@ -34,7 +34,7 @@ export const Downloads: StoryFn = () => {
34
34
  children: (
35
35
  <CustomContent>
36
36
  <h4>Pdf Ready</h4>
37
- <p>
37
+ <p style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
38
38
  <CheckMarkIcon /> Download now
39
39
  </p>
40
40
  </CustomContent>
@@ -43,6 +43,10 @@ const Content = styled.div<{ expanded: boolean }>`
43
43
  border-radius: ${({ theme }) => `0 0 ${theme.borderRadius.large} ${theme.borderRadius.large}`};
44
44
  border-radius-top-left: 0;
45
45
  border-radius-top-right: 0;
46
+
47
+ h4 {
48
+ margin-bottom: ${({ theme }) => theme.spacing['0_5']};
49
+ }
46
50
  `;
47
51
 
48
52
  export const DrawerSnack = forwardRef<HTMLDivElement, PropsWithChildren<CustomContentProps>>(function DrawerSnack(
@@ -29,6 +29,7 @@ export const ControlledDatePicker: FC<ControlledDatePickerProps> = (props) => {
29
29
  description,
30
30
  allowFutureDates = true,
31
31
  allowPastDates = true,
32
+ canClear,
32
33
  customDateFilter,
33
34
  } = defaultsApplier(props);
34
35
 
@@ -91,6 +92,7 @@ export const ControlledDatePicker: FC<ControlledDatePickerProps> = (props) => {
91
92
  format={format}
92
93
  placeholder={placeholder}
93
94
  mode={mode}
95
+ canClear={canClear}
94
96
  />
95
97
  )}
96
98
  {showError && error?.message && <ErrorMessage message={error.message} />}
@@ -253,7 +253,7 @@ export const AbsoluteSubmit = () => {
253
253
  name="date"
254
254
  required={false}
255
255
  loading={false}
256
- description={'The role will be automatically removed after this date'}
256
+ description={'The role will be automatically removed after this date.'}
257
257
  popOverPlacement={'bottom'}
258
258
  timePickerOptions={{ interval: 30 }}
259
259
  allowPastDates={false}
@@ -1,5 +1,5 @@
1
- import { FC, useLayoutEffect, useMemo, useState } from 'react';
2
- import { Button, Popover } from '../../../../components';
1
+ import { FC, MouseEvent, useLayoutEffect, useMemo, useState } from 'react';
2
+ import { Button, IconButton, Popover } from '../../../../components';
3
3
  import { DateTime, DateTimeFormatOptions, Settings } from 'luxon';
4
4
  import { dateFormats, timeFormats } from './formats';
5
5
  import { GenericInputProps } from '../../InputProps';
@@ -8,6 +8,7 @@ import { TimePicker } from '../subcomponents/TimePicker';
8
8
  import { Calendar } from '../subcomponents/Calendar';
9
9
  import { RelativePicker, timeDirection } from '../subcomponents/RelativePicker';
10
10
  import { Placement } from '@floating-ui/react';
11
+ import { AiOutlineClose as ClearIcon } from 'react-icons/ai';
11
12
 
12
13
  interface TimePickerOptions {
13
14
  /// Determines the interval between time options
@@ -51,6 +52,9 @@ export interface DatePickerProps {
51
52
 
52
53
  /// Placeholder text for the input
53
54
  placeholder?: string;
55
+
56
+ /// Can set field back to undefined
57
+ canClear?: boolean;
54
58
  }
55
59
 
56
60
  export type GenericDatePickerProps = GenericInputProps<string, HTMLInputElement> & DatePickerProps;
@@ -71,6 +75,7 @@ export const GenericDatePicker: FC<GenericDatePickerProps> = ({
71
75
  format = DateTime.DATE_SHORT,
72
76
  allowPastDates = true,
73
77
  allowFutureDates = true,
78
+ canClear = false,
74
79
  customDateFilter,
75
80
  mode,
76
81
  }) => {
@@ -172,11 +177,22 @@ export const GenericDatePicker: FC<GenericDatePickerProps> = ({
172
177
  return renderPlaceholder();
173
178
  };
174
179
 
180
+ const handleClear = (e: MouseEvent) => {
181
+ e.preventDefault();
182
+ e.stopPropagation();
183
+ if (onChange) {
184
+ onChange(null as any);
185
+ }
186
+ };
187
+
175
188
  return (
176
189
  <Popover placement={popOverPlacement} open={open} onOpenChange={setOpen}>
177
- <Popover.Trigger asChild>
190
+ <Popover.Trigger asChild readOnly={readOnly}>
178
191
  <ResultContainer readOnly={readOnly} hasError={hasError} onClick={() => setOpen(!open)}>
179
- {renderResult()}
192
+ <span>{renderResult()}</span>
193
+ {!readOnly && canClear && value && !open && (
194
+ <IconButton size="tiny" icon={<ClearIcon />} ariaLabel="clear" onClick={handleClear} />
195
+ )}
180
196
  </ResultContainer>
181
197
  </Popover.Trigger>
182
198
  <Popover.Content>