@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.
- package/components/Alert/Alert.stories.tsx +94 -18
- package/components/Badge/Badge.stories.tsx +114 -0
- package/components/Badge/Badge.styles.ts +36 -0
- package/components/Badge/Badge.tsx +23 -0
- package/components/Badge/Badge.types.ts +11 -0
- package/components/Badge/index.ts +2 -0
- package/components/Button/Button.appearance.ts +1 -1
- package/components/Button/Button.stories.tsx +99 -18
- package/components/Checkbox/Checkbox.stories.tsx +107 -7
- package/components/DatePicker/DatePicker.styles.ts +1 -0
- package/components/DatePicker/DatePicker.tsx +9 -10
- package/components/IconButton/IconButton.sizes.ts +7 -7
- package/components/IconButton/IconButton.tsx +0 -1
- package/components/InputOpt/InputOpt.stories.tsx +30 -44
- package/components/Select/Select.stories.tsx +80 -19
- package/components/Select/Select.tsx +7 -9
- package/components/Table/Datatable.stories.tsx +186 -0
- package/components/Table/Table.stories.tsx +127 -46
- package/components/Table/Table.styles.ts +83 -8
- package/components/Table/Table.tsx +292 -142
- package/components/Table/Table.types.ts +104 -12
- package/components/Table/components/ColumnVisibility/ColumnVisibility.style.ts +46 -0
- package/components/Table/components/ColumnVisibility/ColumnVisibility.tsx +55 -0
- package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.styles.ts +120 -0
- package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.tsx +228 -0
- package/components/Table/components/DatatableColumnFilterMenu/index.ts +1 -0
- package/components/Table/components/DatatableTopBar/DatatableTopBar.styles.ts +25 -0
- package/components/Table/components/DatatableTopBar/DatatableTopBar.tsx +89 -0
- package/components/Table/components/DatatableTopBar/index.ts +1 -0
- package/components/Table/components/SearchInput/SearchInput.tsx +30 -0
- package/components/Table/components/TableHeader/TableHeader.tsx +98 -0
- package/components/Table/components/TablePagination/TablePagination.tsx +78 -0
- package/components/Table/components/index.ts +6 -0
- package/components/Table/hooks/useDatatableFilters.ts +88 -0
- package/components/Table/stories.fixtures.ts +100 -0
- package/components/Table/tanstack-table.d.ts +10 -0
- package/components/Table/utils/dateRangeFilterFn.ts +33 -0
- package/components/Table/utils/index.ts +2 -1
- package/components/Tabs/Tabs.stories.tsx +152 -0
- package/components/Tabs/Tabs.styles.ts +12 -0
- package/components/Tabs/Tabs.tsx +34 -0
- package/components/Tabs/Tabs.types.ts +15 -0
- package/components/Tabs/index.ts +2 -0
- package/components/TextField/TextField.stories.tsx +135 -12
- package/components/index.ts +3 -0
- package/package.json +3 -2
|
@@ -84,7 +84,7 @@ const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
|
|
|
84
84
|
const baseId = useId()
|
|
85
85
|
const errorId = `${baseId}-error`
|
|
86
86
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
87
|
-
const [inputValue, setInputValue] = useState(() =>
|
|
87
|
+
const [inputValue, setInputValue] = useState(() =>
|
|
88
88
|
!isRange && value ? format(value, 'dd/MM/yyyy') : ''
|
|
89
89
|
)
|
|
90
90
|
|
|
@@ -128,8 +128,8 @@ const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
|
|
|
128
128
|
: typeof error === 'object' &&
|
|
129
129
|
error !== null &&
|
|
130
130
|
'message' in error
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
? String((error as { message?: string }).message ?? '')
|
|
132
|
+
: undefined
|
|
133
133
|
|
|
134
134
|
const handleSingleSelect = (date?: Date) => {
|
|
135
135
|
if (disabled) return
|
|
@@ -226,11 +226,11 @@ const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
|
|
|
226
226
|
onRangeChange?.(nextRange)
|
|
227
227
|
const serialized = nextRange
|
|
228
228
|
? JSON.stringify({
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
from: nextRange.from
|
|
230
|
+
? serializeDate(nextRange.from)
|
|
231
|
+
: null,
|
|
232
|
+
to: nextRange.to ? serializeDate(nextRange.to) : null
|
|
233
|
+
})
|
|
234
234
|
: ''
|
|
235
235
|
|
|
236
236
|
register?.onChange({
|
|
@@ -247,7 +247,6 @@ const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
|
|
|
247
247
|
mode="range"
|
|
248
248
|
selected={rangeValue}
|
|
249
249
|
onSelect={handleRangeSelect}
|
|
250
|
-
|
|
251
250
|
captionLayout={appliedCaptionLayout}
|
|
252
251
|
navLayout="around"
|
|
253
252
|
fromYear={resolvedStartYear}
|
|
@@ -355,7 +354,7 @@ const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
|
|
|
355
354
|
</DatePickerTriggerWrapper>
|
|
356
355
|
|
|
357
356
|
<Popover.Portal>
|
|
358
|
-
<DatePickerContent sideOffset={
|
|
357
|
+
<DatePickerContent sideOffset={2} align="center">
|
|
359
358
|
{picker}
|
|
360
359
|
</DatePickerContent>
|
|
361
360
|
</Popover.Portal>
|
|
@@ -4,11 +4,11 @@ import { ButtonSizeTypes } from '../Button/Button.types'
|
|
|
4
4
|
export const IconButtonSizes = (size: ButtonSizeTypes) => {
|
|
5
5
|
const sizes = {
|
|
6
6
|
sm: `
|
|
7
|
-
padding: ${spacing.
|
|
7
|
+
padding: ${spacing.spacing8}px ${spacing.spacing8}px;
|
|
8
8
|
border-radius: ${spacing.spacing4}px;
|
|
9
9
|
& svg {
|
|
10
|
-
width: ${spacing.
|
|
11
|
-
height: ${spacing.
|
|
10
|
+
width: ${spacing.spacing20}px;
|
|
11
|
+
height: ${spacing.spacing20}px;
|
|
12
12
|
}
|
|
13
13
|
`,
|
|
14
14
|
md: `
|
|
@@ -16,16 +16,16 @@ export const IconButtonSizes = (size: ButtonSizeTypes) => {
|
|
|
16
16
|
border-radius: ${spacing.spacing4}px;
|
|
17
17
|
min-width: ${spacing.spacing36}px;
|
|
18
18
|
& svg {
|
|
19
|
-
width: ${spacing.
|
|
20
|
-
height: ${spacing.
|
|
19
|
+
width: ${spacing.spacing20}px;
|
|
20
|
+
height: ${spacing.spacing20}px;
|
|
21
21
|
}
|
|
22
22
|
`,
|
|
23
23
|
lg: `
|
|
24
24
|
padding: ${spacing.spacing12}px ${spacing.spacing12}px;
|
|
25
25
|
border-radius: ${spacing.spacing4}px;
|
|
26
26
|
& svg {
|
|
27
|
-
width: ${spacing.
|
|
28
|
-
height: ${spacing.
|
|
27
|
+
width: ${spacing.spacing20}px;
|
|
28
|
+
height: ${spacing.spacing20}px;
|
|
29
29
|
}
|
|
30
30
|
`
|
|
31
31
|
}
|
|
@@ -4,48 +4,18 @@ import InputOpt from './InputOpt'
|
|
|
4
4
|
const meta: Meta<typeof InputOpt> = {
|
|
5
5
|
title: 'Form/InputOpt',
|
|
6
6
|
component: InputOpt,
|
|
7
|
-
parameters: {
|
|
8
|
-
layout: 'centered',
|
|
9
|
-
},
|
|
7
|
+
parameters: { layout: 'centered' },
|
|
10
8
|
tags: ['autodocs'],
|
|
11
9
|
argTypes: {
|
|
12
|
-
length: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
control: 'text',
|
|
22
|
-
description: 'Helper text below input'
|
|
23
|
-
},
|
|
24
|
-
placeholderChar: {
|
|
25
|
-
control: 'text',
|
|
26
|
-
description: 'Placeholder character for empty fields'
|
|
27
|
-
},
|
|
28
|
-
inputMode: {
|
|
29
|
-
control: 'select',
|
|
30
|
-
options: ['numeric', 'text'],
|
|
31
|
-
description: 'Input mode for mobile keyboards'
|
|
32
|
-
},
|
|
33
|
-
autoFocus: {
|
|
34
|
-
control: 'boolean',
|
|
35
|
-
description: 'Auto focus first input on mount'
|
|
36
|
-
},
|
|
37
|
-
disabled: {
|
|
38
|
-
control: 'boolean',
|
|
39
|
-
description: 'Whether inputs are disabled'
|
|
40
|
-
},
|
|
41
|
-
requiredSymbol: {
|
|
42
|
-
control: 'boolean',
|
|
43
|
-
description: 'Show required asterisk'
|
|
44
|
-
},
|
|
45
|
-
error: {
|
|
46
|
-
control: 'boolean',
|
|
47
|
-
description: 'Whether inputs have error state'
|
|
48
|
-
}
|
|
10
|
+
length: { control: 'number', description: 'Número de campos' },
|
|
11
|
+
label: { control: 'text', description: 'Label do campo' },
|
|
12
|
+
helperText: { control: 'text', description: 'Texto auxiliar abaixo do campo' },
|
|
13
|
+
placeholderChar: { control: 'text', description: 'Caractere placeholder para campos vazios' },
|
|
14
|
+
inputMode: { control: 'select', options: ['numeric', 'text'], description: 'Modo do teclado mobile' },
|
|
15
|
+
autoFocus: { control: 'boolean', description: 'Foca o primeiro campo automaticamente' },
|
|
16
|
+
disabled: { control: 'boolean', description: 'Desabilita todos os campos' },
|
|
17
|
+
requiredSymbol: { control: 'boolean', description: 'Exibe asterisco de campo obrigatório' },
|
|
18
|
+
error: { control: 'boolean', description: 'Estado de erro' },
|
|
49
19
|
},
|
|
50
20
|
}
|
|
51
21
|
|
|
@@ -53,9 +23,25 @@ export default meta
|
|
|
53
23
|
type Story = StoryObj<typeof meta>
|
|
54
24
|
|
|
55
25
|
export const Default: Story = {
|
|
56
|
-
args: {
|
|
57
|
-
label: 'Verification Code',
|
|
58
|
-
length: 6,
|
|
59
|
-
},
|
|
26
|
+
args: { label: 'Código de verificação', length: 6 },
|
|
60
27
|
}
|
|
61
28
|
|
|
29
|
+
export const Desabilitado: Story = {
|
|
30
|
+
name: 'Estado desabilitado',
|
|
31
|
+
args: { label: 'Código de verificação', length: 6, disabled: true },
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const ComErro: Story = {
|
|
35
|
+
name: 'Estado de erro',
|
|
36
|
+
args: { label: 'Código de verificação', length: 6, error: true, helperText: 'Código inválido. Tente novamente.' },
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const Alfanumerico: Story = {
|
|
40
|
+
name: 'Alfanumérico',
|
|
41
|
+
args: { label: 'Código de convite', length: 8, inputMode: 'text', placeholderChar: '_' },
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const PIN4Digitos: Story = {
|
|
45
|
+
name: 'PIN de 4 dígitos',
|
|
46
|
+
args: { label: 'PIN de acesso', length: 4, inputMode: 'numeric' },
|
|
47
|
+
}
|
|
@@ -1,45 +1,69 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import Select from './Select'
|
|
3
3
|
|
|
4
|
+
const defaultOptions = [
|
|
5
|
+
{ label: 'São Paulo', value: 'sp' },
|
|
6
|
+
{ label: 'Rio de Janeiro', value: 'rj' },
|
|
7
|
+
{ label: 'Minas Gerais', value: 'mg' },
|
|
8
|
+
{ label: 'Bahia', value: 'ba' },
|
|
9
|
+
{ label: 'Paraná', value: 'pr' },
|
|
10
|
+
]
|
|
11
|
+
|
|
4
12
|
const meta: Meta<typeof Select> = {
|
|
5
13
|
title: 'Form/Select',
|
|
6
14
|
component: Select,
|
|
7
15
|
parameters: {
|
|
8
16
|
layout: 'centered',
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component: `
|
|
20
|
+
Campo de seleção para escolha de uma opção em uma lista.
|
|
21
|
+
|
|
22
|
+
**Quando usar:**
|
|
23
|
+
- Listas com mais de 5 opções (abaixo disso, prefira RadioButton)
|
|
24
|
+
- Quando o espaço é limitado
|
|
25
|
+
|
|
26
|
+
**Boas práticas:**
|
|
27
|
+
- Sempre defina um \`placeholder\` descritivo
|
|
28
|
+
- Use \`helperText\` para orientar a escolha
|
|
29
|
+
- Use \`requiredSymbol\` em campos obrigatórios
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
9
33
|
},
|
|
10
34
|
tags: ['autodocs'],
|
|
11
|
-
args: {
|
|
12
|
-
options: [],
|
|
13
|
-
},
|
|
14
35
|
argTypes: {
|
|
15
36
|
label: {
|
|
16
37
|
control: 'text',
|
|
17
|
-
description: '
|
|
38
|
+
description: 'Label do select',
|
|
18
39
|
},
|
|
19
40
|
placeholder: {
|
|
20
41
|
control: 'text',
|
|
21
|
-
description: '
|
|
42
|
+
description: 'Placeholder quando nenhuma opção está selecionada',
|
|
22
43
|
},
|
|
23
44
|
helperText: {
|
|
24
45
|
control: 'text',
|
|
25
|
-
description: '
|
|
46
|
+
description: 'Texto auxiliar abaixo do select',
|
|
26
47
|
},
|
|
27
48
|
size: {
|
|
28
49
|
control: 'select',
|
|
29
50
|
options: ['sm', 'md', 'lg'],
|
|
30
|
-
description: '
|
|
51
|
+
description: 'Tamanho do select',
|
|
52
|
+
table: { defaultValue: { summary: 'md' } },
|
|
31
53
|
},
|
|
32
54
|
disabled: {
|
|
33
55
|
control: 'boolean',
|
|
34
|
-
description: '
|
|
56
|
+
description: 'Desabilita o select',
|
|
57
|
+
table: { defaultValue: { summary: 'false' } },
|
|
35
58
|
},
|
|
36
59
|
requiredSymbol: {
|
|
37
60
|
control: 'boolean',
|
|
38
|
-
description: '
|
|
61
|
+
description: 'Exibe asterisco de campo obrigatório',
|
|
62
|
+
table: { defaultValue: { summary: 'false' } },
|
|
39
63
|
},
|
|
40
64
|
options: {
|
|
41
65
|
control: 'object',
|
|
42
|
-
description: '
|
|
66
|
+
description: 'Array de opções `{ label, value }`',
|
|
43
67
|
},
|
|
44
68
|
},
|
|
45
69
|
}
|
|
@@ -47,17 +71,54 @@ const meta: Meta<typeof Select> = {
|
|
|
47
71
|
export default meta
|
|
48
72
|
type Story = StoryObj<typeof meta>
|
|
49
73
|
|
|
50
|
-
const defaultOptions = [
|
|
51
|
-
{ label: 'Option 1', value: 'option1' },
|
|
52
|
-
{ label: 'Option 2', value: 'option2' },
|
|
53
|
-
{ label: 'Option 3', value: 'option3' },
|
|
54
|
-
{ label: 'Option 4', value: 'option4' },
|
|
55
|
-
]
|
|
56
|
-
|
|
57
74
|
export const Default: Story = {
|
|
58
75
|
args: {
|
|
59
|
-
label: '
|
|
60
|
-
placeholder: '
|
|
76
|
+
label: 'Estado',
|
|
77
|
+
placeholder: 'Selecione um estado...',
|
|
78
|
+
options: defaultOptions,
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const Tamanhos: Story = {
|
|
83
|
+
name: 'Tamanhos',
|
|
84
|
+
parameters: {
|
|
85
|
+
docs: { description: { story: 'Três tamanhos disponíveis para diferentes densidades de layout.' } },
|
|
86
|
+
},
|
|
87
|
+
render: () => (
|
|
88
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, width: 320 }}>
|
|
89
|
+
<Select label="Pequeno" size="sm" placeholder="sm" options={defaultOptions} />
|
|
90
|
+
<Select label="Médio" size="md" placeholder="md" options={defaultOptions} />
|
|
91
|
+
<Select label="Grande" size="lg" placeholder="lg" options={defaultOptions} />
|
|
92
|
+
</div>
|
|
93
|
+
),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const ComHelperText: Story = {
|
|
97
|
+
name: 'Com helper text',
|
|
98
|
+
args: {
|
|
99
|
+
label: 'Categoria',
|
|
100
|
+
placeholder: 'Selecione...',
|
|
101
|
+
helperText: 'Escolha a categoria que melhor descreve o item.',
|
|
102
|
+
options: defaultOptions,
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const Desabilitado: Story = {
|
|
107
|
+
name: 'Estado desabilitado',
|
|
108
|
+
args: {
|
|
109
|
+
label: 'País',
|
|
110
|
+
placeholder: 'Brasil',
|
|
111
|
+
disabled: true,
|
|
112
|
+
options: defaultOptions,
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const Obrigatorio: Story = {
|
|
117
|
+
name: 'Campo obrigatório',
|
|
118
|
+
args: {
|
|
119
|
+
label: 'Estado',
|
|
120
|
+
placeholder: 'Selecione um estado...',
|
|
121
|
+
requiredSymbol: true,
|
|
61
122
|
options: defaultOptions,
|
|
62
123
|
},
|
|
63
124
|
}
|
|
@@ -42,15 +42,13 @@ const Select = forwardRef<HTMLInputElement, SelectProps>(
|
|
|
42
42
|
const [open, setOpen] = useState(false)
|
|
43
43
|
|
|
44
44
|
const handleOnChange = (value: string) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
})
|
|
53
|
-
}
|
|
45
|
+
setSelectValue(value)
|
|
46
|
+
register?.onChange({
|
|
47
|
+
target: {
|
|
48
|
+
name: register.name,
|
|
49
|
+
value
|
|
50
|
+
}
|
|
51
|
+
})
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
const state = getState(disabled, !!error)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import Table from './Table'
|
|
4
|
+
import { DatatableColumnFilters, TableQueryState } from './Table.types'
|
|
5
|
+
import { ORDER_DATA, DATATABLE_COLUMNS } from './stories.fixtures'
|
|
6
|
+
|
|
7
|
+
const meta: Meta = {
|
|
8
|
+
title: 'Data Display/Datatable',
|
|
9
|
+
component: Table,
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'padded',
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: `
|
|
15
|
+
Variante \`datatable\` do componente Table — focada em filtragem por coluna.
|
|
16
|
+
|
|
17
|
+
**Tipos de filtro disponíveis:**
|
|
18
|
+
- \`text\` — campo de texto livre
|
|
19
|
+
- \`select\` — dropdown com opções predefinidas
|
|
20
|
+
- \`dateRange\` — seletor de intervalo de datas
|
|
21
|
+
|
|
22
|
+
**Modos de operação:**
|
|
23
|
+
- \`mode="client"\` (padrão) — filtragem e paginação feitas no browser
|
|
24
|
+
- \`mode="server"\` — todo o estado é emitido via \`onQueryChange\`, o servidor processa
|
|
25
|
+
|
|
26
|
+
**Controle de estado:**
|
|
27
|
+
- Não-controlado: estado interno gerenciado pelo componente
|
|
28
|
+
- Controlado: passe \`datatableColumnFiltersValue\` + \`onDatatableColumnFiltersChange\`
|
|
29
|
+
`,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
tags: ['autodocs'],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default meta
|
|
37
|
+
type Story = StoryObj
|
|
38
|
+
|
|
39
|
+
const footer = ({ total, filtered }: { total: number; filtered: number }) =>
|
|
40
|
+
`${filtered} de ${total}`
|
|
41
|
+
|
|
42
|
+
export const Default: Story = {
|
|
43
|
+
name: 'Padrão',
|
|
44
|
+
parameters: {
|
|
45
|
+
docs: { description: { story: 'Clique no cabeçalho de qualquer coluna filtrável para abrir o menu de filtro.' } },
|
|
46
|
+
},
|
|
47
|
+
render: () => (
|
|
48
|
+
<Table
|
|
49
|
+
variant="datatable"
|
|
50
|
+
data={ORDER_DATA}
|
|
51
|
+
columns={DATATABLE_COLUMNS}
|
|
52
|
+
pageSize={5}
|
|
53
|
+
footerText={footer}
|
|
54
|
+
/>
|
|
55
|
+
),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const ComToolbar: Story = {
|
|
59
|
+
name: 'Com toolbar completa',
|
|
60
|
+
parameters: {
|
|
61
|
+
docs: { description: { story: 'Busca global + visibilidade de colunas + botão de limpar todos os filtros.' } },
|
|
62
|
+
},
|
|
63
|
+
render: () => (
|
|
64
|
+
<Table
|
|
65
|
+
variant="datatable"
|
|
66
|
+
data={ORDER_DATA}
|
|
67
|
+
columns={DATATABLE_COLUMNS}
|
|
68
|
+
pageSize={5}
|
|
69
|
+
datatableEnableGlobalSearch
|
|
70
|
+
datatableEnableColumnVisibility
|
|
71
|
+
datatableEnableClearFilters
|
|
72
|
+
footerText={footer}
|
|
73
|
+
/>
|
|
74
|
+
),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const Controlado: Story = {
|
|
78
|
+
name: 'Controlado',
|
|
79
|
+
parameters: {
|
|
80
|
+
docs: {
|
|
81
|
+
description: {
|
|
82
|
+
story: 'Estado dos filtros gerenciado externamente. O JSON abaixo reflete o estado em tempo real.',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
render: () => {
|
|
87
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
88
|
+
const [filters, setFilters] = useState<DatatableColumnFilters>({
|
|
89
|
+
customer: { type: 'text', value: 'João' },
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
94
|
+
<Table
|
|
95
|
+
variant="datatable"
|
|
96
|
+
data={ORDER_DATA}
|
|
97
|
+
columns={DATATABLE_COLUMNS}
|
|
98
|
+
pageSize={5}
|
|
99
|
+
datatableColumnFiltersValue={filters}
|
|
100
|
+
onDatatableColumnFiltersChange={setFilters}
|
|
101
|
+
footerText={footer}
|
|
102
|
+
/>
|
|
103
|
+
<pre style={{ fontSize: 12, background: '#f5f5f5', padding: 12, borderRadius: 6, margin: 0 }}>
|
|
104
|
+
{JSON.stringify(filters, null, 2)}
|
|
105
|
+
</pre>
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const ServerMode: Story = {
|
|
112
|
+
name: 'Server mode',
|
|
113
|
+
parameters: {
|
|
114
|
+
docs: {
|
|
115
|
+
description: {
|
|
116
|
+
story: 'Com `mode="server"`, qualquer mudança de filtro, paginação ou busca emite o `queryState` completo via `onQueryChange`.',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
render: () => {
|
|
121
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
122
|
+
const [queryState, setQueryState] = useState<TableQueryState | null>(null)
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
126
|
+
<Table
|
|
127
|
+
variant="datatable"
|
|
128
|
+
mode="server"
|
|
129
|
+
data={ORDER_DATA}
|
|
130
|
+
columns={DATATABLE_COLUMNS}
|
|
131
|
+
pageSize={5}
|
|
132
|
+
pageCount={2}
|
|
133
|
+
totalRows={10}
|
|
134
|
+
filteredRows={10}
|
|
135
|
+
onQueryChange={setQueryState}
|
|
136
|
+
footerText={footer}
|
|
137
|
+
/>
|
|
138
|
+
{queryState && (
|
|
139
|
+
<pre style={{ fontSize: 12, background: '#f5f5f5', padding: 12, borderRadius: 6, margin: 0 }}>
|
|
140
|
+
{JSON.stringify(queryState, null, 2)}
|
|
141
|
+
</pre>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
)
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const ComExportacao: Story = {
|
|
149
|
+
name: 'Com exportação',
|
|
150
|
+
parameters: {
|
|
151
|
+
docs: {
|
|
152
|
+
description: {
|
|
153
|
+
story: 'O `queryState` passado para `onExport` inclui os filtros ativos no momento do clique.',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
render: () => (
|
|
158
|
+
<Table
|
|
159
|
+
variant="datatable"
|
|
160
|
+
data={ORDER_DATA}
|
|
161
|
+
columns={DATATABLE_COLUMNS}
|
|
162
|
+
pageSize={5}
|
|
163
|
+
onExport={(_state: TableQueryState) => {
|
|
164
|
+
console.log('[Datatable Export]', _state)
|
|
165
|
+
alert('Exportação disparada — veja o console.')
|
|
166
|
+
}}
|
|
167
|
+
exportButton={{ label: 'Exportar', variant: 'neutralOutline' }}
|
|
168
|
+
footerText={footer}
|
|
169
|
+
/>
|
|
170
|
+
),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const SemDados: Story = {
|
|
174
|
+
name: 'Sem dados',
|
|
175
|
+
parameters: {
|
|
176
|
+
docs: { description: { story: 'Estado vazio do datatable.' } },
|
|
177
|
+
},
|
|
178
|
+
render: () => (
|
|
179
|
+
<Table
|
|
180
|
+
variant="datatable"
|
|
181
|
+
data={[]}
|
|
182
|
+
columns={DATATABLE_COLUMNS}
|
|
183
|
+
emptyTitle="Nenhum registro encontrado."
|
|
184
|
+
/>
|
|
185
|
+
),
|
|
186
|
+
}
|