Skip to content

Commit 36e6133

Browse files
authored
Merge pull request #976
* feat(32894): add support for (expanded) sub rows * feat(32894): add schema grouping routine * feat(32894): add expander to actions and fix conditions * feat(32894): refactor the schema table to handle expandable rows * fix(32894): fix margin for children rows * fix(32894): fix default * fix(32894): fix translations * fix(32894): fix translations * fix(32894): retrofit the groupBy function * test(32894): add tests * test(32894): fix bug with clock and intercept * test(32894): add tests * test(32894): add tests * chore(32894): linting * refactor(32894): refactor the grouping utility as generic * test(32894): fix tests * refactor(32894): refactor the schema table * refactor(32894): refactor the script table * refactor(32894): move the "expand" CTA to the name column, as a more … * test(32894): update tests * refactor(32894): export the expand version CTA * test(32894): add tests
1 parent a7eb54b commit 36e6133

File tree

12 files changed

+601
-157
lines changed

12 files changed

+601
-157
lines changed

hivemq-edge-frontend/src/components/PaginatedTable/PaginatedTable.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type { CSSProperties } from 'react'
22
import { useState } from 'react'
33
import { useTranslation } from 'react-i18next'
4-
import type { ColumnDef, ColumnFiltersState, Row } from '@tanstack/react-table'
4+
import type { ColumnDef, ColumnFiltersState, ExpandedState, Row } from '@tanstack/react-table'
55
import {
66
flexRender,
77
getCoreRowModel,
8+
getExpandedRowModel,
89
getFacetedMinMaxValues,
910
getFacetedRowModel,
1011
getFacetedUniqueValues,
@@ -49,6 +50,7 @@ interface PaginatedTableProps<T> {
4950
* Define row styles
5051
*/
5152
getRowStyles?: (row: Row<T>) => CSSProperties
53+
getSubRows?: (originalRow: T, index: number) => undefined | T[]
5254
}
5355

5456
const DEFAULT_PAGE_SIZES = [5, 10, 20, 30, 40, 50]
@@ -64,12 +66,14 @@ const PaginatedTable = <T,>({
6466
enablePaginationSizes = true,
6567
enablePaginationGoTo = true,
6668
isError = false,
69+
getSubRows = undefined,
6770
'aria-label': ariaLabel,
6871
}: PaginatedTableProps<T>) => {
6972
const { t } = useTranslation()
7073
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
7174
const [globalFilter, setGlobalFilter] = useState('')
7275
const [rowSelection, setRowSelection] = useState({})
76+
const [expanded, setExpanded] = useState<ExpandedState>({})
7377

7478
const table = useReactTable({
7579
data: data,
@@ -79,12 +83,16 @@ const PaginatedTable = <T,>({
7983
columnFilters,
8084
globalFilter,
8185
rowSelection,
86+
expanded,
8287
},
8388
enableRowSelection: true,
8489
onRowSelectionChange: setRowSelection,
8590
enableColumnFilters: enableColumnFilters,
8691
onColumnFiltersChange: setColumnFilters,
8792
onGlobalFilterChange: setGlobalFilter,
93+
getSubRows,
94+
onExpandedChange: setExpanded,
95+
getExpandedRowModel: getExpandedRowModel(),
8896
getCoreRowModel: getCoreRowModel(),
8997
getFilteredRowModel: getFilteredRowModel(),
9098
getSortedRowModel: getSortedRowModel(),

hivemq-edge-frontend/src/extensions/datahub/components/helpers/DataHubListAction.spec.cy.tsx

Lines changed: 140 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,157 @@
1-
import DataHubListAction from '@datahub/components/helpers/DataHubListAction.tsx'
2-
import type { CombinedPolicy } from '@datahub/types.ts'
3-
import { PolicyType } from '@datahub/types.ts'
41
import { mockDataPolicy } from '@datahub/api/hooks/DataHubDataPoliciesService/__handlers__'
52
import { mockBehaviorPolicy } from '@datahub/api/hooks/DataHubBehaviorPoliciesService/__handlers__'
3+
import DataHubListAction from '@datahub/components/helpers/DataHubListAction.tsx'
4+
import { type CombinedPolicy, PolicyType } from '@datahub/types.ts'
65

76
describe('DataHubListAction', () => {
87
beforeEach(() => {
98
cy.viewport(800, 800)
109
})
1110

12-
it('should render the actions for DRAFT', () => {
13-
const policy: CombinedPolicy = {
14-
type: PolicyType.CREATE_POLICY,
15-
...mockDataPolicy,
16-
}
17-
cy.mountWithProviders(
18-
<DataHubListAction policy={policy} onEdit={cy.stub().as('onEdit')} onDelete={cy.stub().as('onDelete')} />
19-
)
20-
21-
cy.get('button').should('have.length', 2)
22-
cy.getByTestId('list-action-view').should('have.attr', 'aria-label', 'Continue on draft')
23-
cy.getByTestId('list-action-view').should('not.be.disabled')
24-
25-
cy.get('@onDelete').should('not.have.been.called')
26-
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
27-
cy.getByTestId('list-action-delete').click()
28-
cy.get('@onDelete').should('have.been.called')
11+
context('DRAFT', () => {
12+
it('should render the actions for DRAFT', () => {
13+
const policy: CombinedPolicy = {
14+
type: PolicyType.CREATE_POLICY,
15+
...mockDataPolicy,
16+
}
17+
cy.mountWithProviders(
18+
<DataHubListAction policy={policy} onEdit={cy.stub().as('onEdit')} onDelete={cy.stub().as('onDelete')} />
19+
)
20+
21+
cy.get('button').should('have.length', 2)
22+
cy.getByTestId('list-action-view').should('have.attr', 'aria-label', 'Continue on draft')
23+
cy.getByTestId('list-action-view').should('not.be.disabled')
24+
25+
cy.get('@onDelete').should('not.have.been.called')
26+
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
27+
cy.getByTestId('list-action-delete').click()
28+
cy.get('@onDelete').should('have.been.called')
29+
})
2930
})
3031

31-
it('should render the actions for resources', () => {
32-
cy.mountWithProviders(
33-
<DataHubListAction
34-
policy={undefined}
35-
onDownload={cy.stub().as('onDownload')}
36-
onDelete={cy.stub().as('onDelete')}
37-
/>
38-
)
39-
40-
cy.get('button').should('have.length', 2)
41-
cy.getByTestId('list-action-download').should('not.be.disabled')
42-
43-
cy.get('@onDownload').should('not.have.been.called')
44-
cy.getByTestId('list-action-download').should('have.attr', 'aria-label', 'Download')
45-
cy.getByTestId('list-action-download').click()
46-
cy.get('@onDownload').should('have.been.called')
47-
48-
cy.get('@onDelete').should('not.have.been.called')
49-
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
50-
cy.getByTestId('list-action-delete').click()
51-
cy.get('@onDelete').should('have.been.called')
32+
context('SCRIPT and SCHEMA', () => {
33+
it('should render the actions for single version', () => {
34+
cy.mountWithProviders(
35+
<DataHubListAction
36+
policy={undefined}
37+
onDownload={cy.stub().as('onDownload')}
38+
onDelete={cy.stub().as('onDelete')}
39+
onEdit={cy.stub().as('onEdit')}
40+
/>
41+
)
42+
43+
cy.get('button').should('have.length', 2)
44+
45+
cy.get('@onDownload').should('not.have.been.called')
46+
cy.getByTestId('list-action-download').should('not.be.disabled')
47+
cy.getByTestId('list-action-download').should('have.attr', 'aria-label', 'Download')
48+
cy.getByTestId('list-action-download').click()
49+
cy.get('@onDownload').should('have.been.called')
50+
51+
cy.get('@onDelete').should('not.have.been.called')
52+
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
53+
cy.getByTestId('list-action-delete').click()
54+
cy.get('@onDelete').should('have.been.called')
55+
56+
cy.getByTestId('list-action-draft').should('not.exist')
57+
cy.getByTestId('list-action-view-edit').should('not.exist')
58+
})
59+
60+
it('should render the actions for multiple versions', () => {
61+
cy.mountWithProviders(
62+
<DataHubListAction
63+
policy={undefined}
64+
onDownload={cy.stub().as('onDownload')}
65+
onDelete={cy.stub().as('onDelete')}
66+
onEdit={cy.stub().as('onEdit')}
67+
/>
68+
)
69+
70+
cy.get('button').should('have.length', 2)
71+
72+
cy.get('@onDownload').should('not.have.been.called')
73+
cy.getByTestId('list-action-download').should('have.attr', 'aria-label', 'Download')
74+
cy.getByTestId('list-action-download').click()
75+
cy.get('@onDownload').should('have.been.called')
76+
77+
cy.get('@onDelete').should('not.have.been.called')
78+
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
79+
cy.getByTestId('list-action-delete').click()
80+
cy.get('@onDelete').should('have.been.called')
81+
82+
cy.getByTestId('list-action-draft').should('not.exist')
83+
cy.getByTestId('list-action-view-edit').should('not.exist')
84+
})
5285
})
5386

54-
it('should render the actions for DATA_POLICY', () => {
55-
const policy: CombinedPolicy = {
56-
type: PolicyType.DATA_POLICY,
57-
...mockDataPolicy,
58-
}
59-
cy.mountWithProviders(
60-
<DataHubListAction
61-
policy={policy}
62-
onEdit={cy.stub().as('onEdit')}
63-
onDownload={cy.stub().as('onDownload')}
64-
onDelete={cy.stub().as('onDelete')}
65-
/>
66-
)
67-
68-
cy.get('button').should('have.length', 3)
69-
cy.getByTestId('list-action-view').should('not.be.disabled')
70-
71-
cy.get('@onEdit').should('not.have.been.called')
72-
cy.getByTestId('list-action-view').should('have.attr', 'aria-label', 'View / Edit')
73-
cy.getByTestId('list-action-view').click()
74-
cy.get('@onEdit').should('have.been.called')
75-
76-
cy.get('@onDownload').should('not.have.been.called')
77-
cy.getByTestId('list-action-download').should('have.attr', 'aria-label', 'Download')
78-
cy.getByTestId('list-action-download').click()
79-
cy.get('@onDownload').should('have.been.called')
80-
81-
cy.get('@onDelete').should('not.have.been.called')
82-
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
83-
cy.getByTestId('list-action-delete').click()
84-
cy.get('@onDelete').should('have.been.called')
87+
context('DATA_POLICY', () => {
88+
it('should render the actions for DATA_POLICY', () => {
89+
const policy: CombinedPolicy = {
90+
type: PolicyType.DATA_POLICY,
91+
...mockDataPolicy,
92+
}
93+
cy.mountWithProviders(
94+
<DataHubListAction
95+
policy={policy}
96+
onEdit={cy.stub().as('onEdit')}
97+
onDownload={cy.stub().as('onDownload')}
98+
onDelete={cy.stub().as('onDelete')}
99+
/>
100+
)
101+
102+
cy.get('button').should('have.length', 3)
103+
cy.getByTestId('list-action-view').should('not.be.disabled')
104+
105+
cy.get('@onEdit').should('not.have.been.called')
106+
cy.getByTestId('list-action-view').should('have.attr', 'aria-label', 'View / Edit')
107+
cy.getByTestId('list-action-view').click()
108+
cy.get('@onEdit').should('have.been.called')
109+
110+
cy.get('@onDownload').should('not.have.been.called')
111+
cy.getByTestId('list-action-download').should('have.attr', 'aria-label', 'Download')
112+
cy.getByTestId('list-action-download').click()
113+
cy.get('@onDownload').should('have.been.called')
114+
115+
cy.get('@onDelete').should('not.have.been.called')
116+
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
117+
cy.getByTestId('list-action-delete').click()
118+
cy.get('@onDelete').should('have.been.called')
119+
})
85120
})
86121

87-
it('should render the actions for BEHAVIOR_POLICY', () => {
88-
const policy: CombinedPolicy = {
89-
type: PolicyType.BEHAVIOR_POLICY,
90-
...mockBehaviorPolicy,
91-
}
92-
cy.mountWithProviders(
93-
<DataHubListAction
94-
policy={policy}
95-
onEdit={cy.stub().as('onEdit')}
96-
onDownload={cy.stub().as('onDownload')}
97-
onDelete={cy.stub().as('onDelete')}
98-
/>
99-
)
100-
101-
cy.get('button').should('have.length', 3)
102-
cy.getByTestId('list-action-view').should('not.be.disabled')
103-
104-
cy.get('@onEdit').should('not.have.been.called')
105-
cy.getByTestId('list-action-view').should('have.attr', 'aria-label', 'View / Edit')
106-
cy.getByTestId('list-action-view').click()
107-
cy.get('@onEdit').should('have.been.called')
108-
109-
cy.get('@onDownload').should('not.have.been.called')
110-
cy.getByTestId('list-action-download').should('have.attr', 'aria-label', 'Download')
111-
cy.getByTestId('list-action-download').click()
112-
cy.get('@onDownload').should('have.been.called')
113-
114-
cy.get('@onDelete').should('not.have.been.called')
115-
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
116-
cy.getByTestId('list-action-delete').click()
117-
cy.get('@onDelete').should('have.been.called')
122+
context('BEHAVIOR_POLICY', () => {
123+
it('should render the actions for BEHAVIOR_POLICY', () => {
124+
const policy: CombinedPolicy = {
125+
type: PolicyType.BEHAVIOR_POLICY,
126+
...mockBehaviorPolicy,
127+
}
128+
cy.mountWithProviders(
129+
<DataHubListAction
130+
policy={policy}
131+
onEdit={cy.stub().as('onEdit')}
132+
onDownload={cy.stub().as('onDownload')}
133+
onDelete={cy.stub().as('onDelete')}
134+
/>
135+
)
136+
137+
cy.get('button').should('have.length', 3)
138+
cy.getByTestId('list-action-view').should('not.be.disabled')
139+
140+
cy.get('@onEdit').should('not.have.been.called')
141+
cy.getByTestId('list-action-view').should('have.attr', 'aria-label', 'View / Edit')
142+
cy.getByTestId('list-action-view').click()
143+
cy.get('@onEdit').should('have.been.called')
144+
145+
cy.get('@onDownload').should('not.have.been.called')
146+
cy.getByTestId('list-action-download').should('have.attr', 'aria-label', 'Download')
147+
cy.getByTestId('list-action-download').click()
148+
cy.get('@onDownload').should('have.been.called')
149+
150+
cy.get('@onDelete').should('not.have.been.called')
151+
cy.getByTestId('list-action-delete').should('have.attr', 'aria-label', 'Delete')
152+
cy.getByTestId('list-action-delete').click()
153+
cy.get('@onDelete').should('have.been.called')
154+
})
118155
})
119156

120157
it('should be accessible', () => {

hivemq-edge-frontend/src/extensions/datahub/components/helpers/DataHubListAction.tsx

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,26 @@ import { ButtonGroup, HStack } from '@chakra-ui/react'
55
import { LuFileEdit, LuTrash2, LuFileSearch, LuDownload } from 'react-icons/lu'
66

77
import IconButton from '@/components/Chakra/IconButton.tsx'
8-
import type { CombinedPolicy } from '@datahub/types.ts'
9-
import { DesignerStatus, PolicyType } from '@datahub/types.ts'
8+
import { type CombinedPolicy, DesignerStatus, PolicyType } from '@datahub/types.ts'
109
import useDataHubDraftStore from '@datahub/hooks/useDataHubDraftStore.ts'
1110

1211
interface DataHubListActionProps {
1312
policy?: CombinedPolicy
1413
onEdit?: MouseEventHandler<HTMLButtonElement>
1514
onDelete?: MouseEventHandler<HTMLButtonElement>
1615
onDownload?: MouseEventHandler<HTMLButtonElement>
16+
canDelete?: boolean
17+
canDownload?: boolean
1718
}
1819

19-
const DataHubListAction: FC<DataHubListActionProps> = ({ policy, onEdit, onDelete, onDownload }) => {
20+
const DataHubListAction: FC<DataHubListActionProps> = ({
21+
policy,
22+
onEdit,
23+
onDelete,
24+
onDownload,
25+
canDownload = true,
26+
canDelete = true,
27+
}) => {
2028
const { t } = useTranslation('datahub')
2129
const navigate = useNavigate()
2230
const { setStatus } = useDataHubDraftStore()
@@ -25,18 +33,22 @@ const DataHubListAction: FC<DataHubListActionProps> = ({ policy, onEdit, onDelet
2533
// If not policy, it's a resource toolbar
2634
return (
2735
<ButtonGroup size="sm" isAttached>
28-
<IconButton
29-
data-testid="list-action-download"
30-
onClick={onDownload}
31-
aria-label={t('Listings.action.download')}
32-
icon={<LuDownload />}
33-
/>
34-
<IconButton
35-
data-testid="list-action-delete"
36-
onClick={onDelete}
37-
aria-label={t('Listings.action.delete')}
38-
icon={<LuTrash2 />}
39-
/>
36+
{canDownload && (
37+
<IconButton
38+
data-testid="list-action-download"
39+
onClick={onDownload}
40+
aria-label={t('Listings.action.download')}
41+
icon={<LuDownload />}
42+
/>
43+
)}
44+
{canDelete && (
45+
<IconButton
46+
data-testid="list-action-delete"
47+
onClick={onDelete}
48+
aria-label={t('Listings.action.delete')}
49+
icon={<LuTrash2 />}
50+
/>
51+
)}
4052
</ButtonGroup>
4153
)
4254
}

0 commit comments

Comments
 (0)