@liguelead/design-system 0.0.30 → 0.0.32

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 (46) hide show
  1. package/components/Alert/Alert.stories.tsx +94 -18
  2. package/components/Badge/Badge.stories.tsx +114 -0
  3. package/components/Badge/Badge.styles.ts +36 -0
  4. package/components/Badge/Badge.tsx +23 -0
  5. package/components/Badge/Badge.types.ts +11 -0
  6. package/components/Badge/index.ts +2 -0
  7. package/components/Button/Button.appearance.ts +1 -1
  8. package/components/Button/Button.stories.tsx +99 -18
  9. package/components/Checkbox/Checkbox.stories.tsx +107 -7
  10. package/components/DatePicker/DatePicker.styles.ts +1 -0
  11. package/components/DatePicker/DatePicker.tsx +9 -10
  12. package/components/IconButton/IconButton.sizes.ts +7 -7
  13. package/components/IconButton/IconButton.tsx +0 -1
  14. package/components/InputOpt/InputOpt.stories.tsx +30 -44
  15. package/components/Select/Select.stories.tsx +80 -19
  16. package/components/Select/Select.tsx +7 -9
  17. package/components/Table/Datatable.stories.tsx +186 -0
  18. package/components/Table/Table.stories.tsx +127 -46
  19. package/components/Table/Table.styles.ts +83 -8
  20. package/components/Table/Table.tsx +292 -142
  21. package/components/Table/Table.types.ts +104 -12
  22. package/components/Table/components/ColumnVisibility/ColumnVisibility.style.ts +46 -0
  23. package/components/Table/components/ColumnVisibility/ColumnVisibility.tsx +55 -0
  24. package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.styles.ts +120 -0
  25. package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.tsx +228 -0
  26. package/components/Table/components/DatatableColumnFilterMenu/index.ts +1 -0
  27. package/components/Table/components/DatatableTopBar/DatatableTopBar.styles.ts +25 -0
  28. package/components/Table/components/DatatableTopBar/DatatableTopBar.tsx +89 -0
  29. package/components/Table/components/DatatableTopBar/index.ts +1 -0
  30. package/components/Table/components/SearchInput/SearchInput.tsx +30 -0
  31. package/components/Table/components/TableHeader/TableHeader.tsx +98 -0
  32. package/components/Table/components/TablePagination/TablePagination.tsx +78 -0
  33. package/components/Table/components/index.ts +6 -0
  34. package/components/Table/hooks/useDatatableFilters.ts +88 -0
  35. package/components/Table/stories.fixtures.ts +100 -0
  36. package/components/Table/tanstack-table.d.ts +10 -0
  37. package/components/Table/utils/dateRangeFilterFn.ts +33 -0
  38. package/components/Table/utils/index.ts +2 -1
  39. package/components/Tabs/Tabs.stories.tsx +152 -0
  40. package/components/Tabs/Tabs.styles.ts +12 -0
  41. package/components/Tabs/Tabs.tsx +34 -0
  42. package/components/Tabs/Tabs.types.ts +15 -0
  43. package/components/Tabs/index.ts +2 -0
  44. package/components/TextField/TextField.stories.tsx +135 -12
  45. package/components/index.ts +3 -0
  46. package/package.json +3 -2
@@ -1,53 +1,134 @@
1
- import type {Meta, StoryObj} from '@storybook/react-vite';
2
-
3
- import Table from './Table';
4
-
5
- const meta: Meta<typeof Table> = {
6
- title: 'Data Display/Table',
7
- component: Table,
8
- parameters: {
9
- layout: 'fullscreen',
10
- },
11
- tags: ['autodocs'],
12
- argTypes: {
13
- data: {
14
- control: 'object',
15
- description: 'Array of data objects to be displayed in the table',
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import Table from './Table'
3
+ import { TableQueryState } from './Table.types'
4
+ import { ORDER_DATA, TABLE_COLUMNS } from './stories.fixtures'
5
+
6
+ const meta: Meta = {
7
+ title: 'Data Display/Table',
8
+ component: Table,
9
+ parameters: {
10
+ layout: 'padded',
11
+ docs: {
12
+ description: {
13
+ component: `
14
+ Tabela de dados com ordenação por coluna, busca global, visibilidade de colunas, exportação e paginação.
15
+
16
+ **Props principais:**
17
+ - \`data\` — array de dados
18
+ - \`columns\` — definição de colunas via \`@tanstack/react-table\`
19
+ - \`enableGlobalSearch\` — habilita campo de busca global
20
+ - \`enableColumnVisibility\` — habilita toggle de visibilidade por coluna
21
+ - \`enableClearFilters\` — exibe botão para limpar ordenação e busca
22
+ - \`onExport\` / \`getExportUrl\` — habilita botão de exportação
23
+ - \`mode="server"\` — delega paginação e ordenação ao servidor via \`onQueryChange\`
24
+ `,
25
+ },
26
+ },
16
27
  },
17
- columns: {
18
- control: 'object',
19
- description: 'Column definitions for the table',
28
+ tags: ['autodocs'],
29
+ }
30
+
31
+ export default meta
32
+ type Story = StoryObj
33
+
34
+ const footer = ({ total, filtered }: { total: number; filtered: number }) =>
35
+ `${filtered} de ${total}`
36
+
37
+ export const Default: Story = {
38
+ name: 'Padrão',
39
+ parameters: {
40
+ docs: { description: { story: 'Tabela simples com paginação e ordenação por coluna.' } },
20
41
  },
21
- footerText: {
22
- control: {type: undefined},
23
- description:
24
- 'Function to render custom footer text, receives total and filtered counts',
42
+ render: () => (
43
+ <Table
44
+ data={ORDER_DATA}
45
+ columns={TABLE_COLUMNS}
46
+ pageSize={5}
47
+ footerText={footer}
48
+ />
49
+ ),
50
+ }
51
+
52
+ export const ComBuscaGlobal: Story = {
53
+ name: 'Com busca global',
54
+ parameters: {
55
+ docs: { description: { story: 'Use `enableGlobalSearch` para filtrar todas as colunas de uma vez.' } },
25
56
  },
26
- showPagination: {
27
- control: 'boolean',
28
- description: 'Whether to enable pagination for the table',
57
+ render: () => (
58
+ <Table
59
+ data={ORDER_DATA}
60
+ columns={TABLE_COLUMNS}
61
+ pageSize={5}
62
+ enableGlobalSearch
63
+ searchPlaceholder="Buscar em todos os campos..."
64
+ footerText={footer}
65
+ />
66
+ ),
67
+ }
68
+
69
+ export const ComVisibilidadeColunas: Story = {
70
+ name: 'Com visibilidade de colunas',
71
+ parameters: {
72
+ docs: { description: { story: 'Use `enableColumnVisibility` para permitir mostrar/ocultar colunas.' } },
29
73
  },
30
- pageSize: {
31
- control: 'number',
32
- description: 'Initial number of rows per page when pagination is enabled',
74
+ render: () => (
75
+ <Table
76
+ data={ORDER_DATA}
77
+ columns={TABLE_COLUMNS}
78
+ pageSize={5}
79
+ enableColumnVisibility
80
+ footerText={footer}
81
+ />
82
+ ),
83
+ }
84
+
85
+ export const ComExportacao: Story = {
86
+ name: 'Com exportação',
87
+ parameters: {
88
+ docs: { description: { story: 'Use `onExport` para receber o estado atual e disparar o download.' } },
33
89
  },
34
- },
35
- };
90
+ render: () => (
91
+ <Table
92
+ data={ORDER_DATA}
93
+ columns={TABLE_COLUMNS}
94
+ pageSize={5}
95
+ onExport={(_state: TableQueryState) => alert('Exportação disparada — veja o console.')}
96
+ exportButton={{ label: 'Exportar CSV', variant: 'neutralOutline' }}
97
+ footerText={footer}
98
+ />
99
+ ),
100
+ }
36
101
 
37
- export default meta;
38
- type Story = StoryObj<typeof meta>;
102
+ export const Completa: Story = {
103
+ name: 'Completa',
104
+ parameters: {
105
+ docs: { description: { story: 'Busca global + visibilidade de colunas + limpar filtros + exportação.' } },
106
+ },
107
+ render: () => (
108
+ <Table
109
+ data={ORDER_DATA}
110
+ columns={TABLE_COLUMNS}
111
+ pageSize={5}
112
+ enableGlobalSearch
113
+ enableColumnVisibility
114
+ enableClearFilters
115
+ onExport={(_state: TableQueryState) => console.log('[Export]', _state)}
116
+ exportButton={{ label: 'Exportar', variant: 'neutralOutline' }}
117
+ footerText={footer}
118
+ />
119
+ ),
120
+ }
39
121
 
40
- export const Default: Story = {
41
- args: {
42
- data: [
43
- {name: 'John Doe', email: 'john.doe@example.com'},
44
- {name: 'Jane Smith', email: 'jane.smith@example.com'},
45
- ],
46
- columns: [
47
- {header: 'Name', accessorKey: 'name'},
48
- {header: 'Email', accessorKey: 'email'},
49
- ],
50
- showPagination: true,
51
- pageSize: 10,
52
- },
53
- };
122
+ export const SemDados: Story = {
123
+ name: 'Sem dados',
124
+ parameters: {
125
+ docs: { description: { story: 'Estado vazio exibido quando `data` é um array vazio.' } },
126
+ },
127
+ render: () => (
128
+ <Table
129
+ data={[]}
130
+ columns={TABLE_COLUMNS}
131
+ emptyTitle="Nenhum pedido encontrado."
132
+ />
133
+ ),
134
+ }
@@ -1,9 +1,37 @@
1
1
  import styled from 'styled-components'
2
2
  import { spacing, fontSize, fontWeight } from '@liguelead/foundation'
3
3
  import { parseColor } from '../../utils'
4
+ import Button from '../Button'
4
5
 
5
6
  export const TableWrapper = styled.div`
6
7
  background: ${({ theme }) => parseColor(theme.colors.white)};
8
+ width: 100%;
9
+ `
10
+
11
+ export const TableScrollContainer = styled.div`
12
+ overflow-x: auto;
13
+ width: 100%;
14
+ `
15
+
16
+ export const TableToolbar = styled.div`
17
+ display: flex;
18
+ justify-content: space-between;
19
+ align-items: flex-start;
20
+ gap: ${spacing.spacing16}px;
21
+ margin-bottom: ${spacing.spacing16}px;
22
+ `
23
+
24
+ export const ToolbarLeft = styled.div`
25
+ display: flex;
26
+ gap: ${spacing.spacing8}px;
27
+ align-items: center;
28
+ flex: 1;
29
+ `
30
+
31
+ export const ToolbarRight = styled.div`
32
+ display: flex;
33
+ gap: ${spacing.spacing8}px;
34
+ align-items: center;
7
35
  `
8
36
 
9
37
  export const StyledTable = styled.table`
@@ -13,27 +41,32 @@ export const StyledTable = styled.table`
13
41
  export const TableFooter = styled.div<{ $hasPagination: boolean }>`
14
42
  display: flex;
15
43
  justify-content: center;
16
- flex-direction: ${({ $hasPagination }) => ($hasPagination ? 'column-reverse' : 'row')};
44
+ flex-direction: ${({ $hasPagination }) => ($hasPagination ? 'row-reverse' : 'row')};
17
45
  align-items: center;
18
- gap: ${spacing.spacing8}px;
19
- padding: ${spacing.spacing16}px;
46
+ padding: ${spacing.spacing16}px 0;
20
47
  font-size: ${fontSize.fontSize14}px;
21
- border-top: 1px solid #e5e7eb;
48
+ border-top: 1px solid ${({ theme }) => parseColor(theme.colors.neutral300)};
22
49
  `
23
50
 
24
51
  export const FooterText = styled.div`
25
52
  display: flex;
26
53
  align-items: center;
54
+ color: ${({ theme }) => parseColor(theme.colors.textMedium)};
55
+ font-size: ${fontSize.fontSize14}px;
56
+ font-weight: ${fontWeight.fontWeight400};
27
57
  `
28
58
 
29
59
  export const PaginationContainer = styled.div`
30
60
  display: flex;
31
61
  align-items: center;
62
+ flex: 1;
63
+ justify-content: center;
32
64
  `
33
65
 
34
66
  export const PaginationControls = styled.div`
35
67
  display: flex;
36
68
  gap: ${spacing.spacing4}px;
69
+ align-items: center;
37
70
  `
38
71
 
39
72
  export const PaginationElipsis = styled.span`
@@ -57,15 +90,53 @@ export const Td = styled.td`
57
90
 
58
91
  export const Tr = styled.tr`
59
92
  &:not(:last-child) td {
60
- border-bottom: 1px solid #e5e7eb;
93
+ border-bottom: 1px solid ${({ theme }) => parseColor(theme.colors.neutral300)};
61
94
  }
62
95
  `
63
- export const Th = styled.th`
64
- padding: 12px 16px;
65
- color: ${({ theme }) => parseColor(theme.colors.textMedium)};
96
+
97
+ export const Th = styled.th<{ $sortable?: boolean; $hasActiveSort?: boolean; $filterable?: boolean }>`
98
+ padding: ${spacing.spacing8}px;
99
+ color: ${({ theme, $hasActiveSort }) =>
100
+ $hasActiveSort
101
+ ? parseColor(theme.colors.primary)
102
+ : parseColor(theme.colors.textMedium)};
66
103
  font-size: ${fontSize.fontSize14}px;
67
104
  font-weight: ${fontWeight.fontWeight600};
68
105
  border-bottom: 1px solid ${({ theme }) => parseColor(theme.colors.neutral300)};
106
+ background-color: ${({ theme, $hasActiveSort }) =>
107
+ $hasActiveSort ? parseColor(theme.colors.primaryLighter) : 'transparent'};
108
+ cursor: ${({ $sortable, $filterable }) => ($sortable || $filterable ? 'pointer' : 'default')};
109
+ user-select: none;
110
+ transition: background-color 0.2s ease, color 0.2s ease;
111
+ position: relative;
112
+
113
+ &:focus {
114
+ outline: 2px solid rgba(163, 163, 163, 0.50);
115
+ outline-offset: -2px;
116
+ z-index: 1;
117
+ }
118
+
119
+ &:hover {
120
+ color: ${({ theme }) => parseColor(theme.colors.textMedium)};
121
+ background-color: ${({ theme, $sortable, $filterable, $hasActiveSort }) => {
122
+ if ($hasActiveSort) return parseColor(theme.colors.neutral200);
123
+ if ($sortable || $filterable) return parseColor(theme.colors.neutral100);
124
+ return 'transparent';
125
+ }};
126
+ }
127
+ `
128
+
129
+ export const ThContent = styled.div<{ $filterable?: boolean }>`
130
+ display: flex;
131
+ align-items: center;
132
+ gap: ${spacing.spacing16}px;
133
+ width: 100%;
134
+ `
135
+
136
+ export const SortIconWrapper = styled.span`
137
+ display: inline-flex;
138
+ flex-direction: column;
139
+ line-height: 0;
69
140
  `
70
141
 
71
142
  export const EmptyMessage = styled.td`
@@ -74,3 +145,7 @@ export const EmptyMessage = styled.td`
74
145
  font-size: ${fontSize.fontSize14}px;
75
146
  text-align: center;
76
147
  `
148
+
149
+ export const PaginationButton = styled(Button)<{ $active?: boolean }>`
150
+ color: ${({ theme, $active }) => $active ? parseColor(theme.colors.white) : parseColor(theme.colors.textMedium)};
151
+ `