Skip to content
This repository was archived by the owner on Jan 16, 2024. It is now read-only.

Commit 34e6afc

Browse files
author
DROMANE
committed
Moved Header from TableWidget
1 parent 0257bc7 commit 34e6afc

File tree

5 files changed

+269
-83
lines changed

5 files changed

+269
-83
lines changed

src/components/widgets/TableWidget/TableWidget.less

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,3 @@
3333
padding-right: 30px;
3434
}
3535
}
36-
37-
.filtersContainer {
38-
:global {
39-
.ant-select {
40-
padding-top: 10px;
41-
padding-right: 10px;
42-
}
43-
44-
.ant-select-selection {
45-
border: none;
46-
47-
&:focus, &:active{
48-
box-shadow: none;
49-
}
50-
51-
&__rendered {
52-
margin-left: 0;
53-
}
54-
}
55-
}
56-
}

src/components/widgets/TableWidget/TableWidget.tsx

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import React, { FunctionComponent } from 'react'
1+
import React, { FunctionComponent, useCallback, useMemo } from 'react'
22
import { connect } from 'react-redux'
33
import { Dispatch } from 'redux'
44
import { Table } from 'antd'
55
import { ColumnProps, TableProps, TableRowSelection } from 'antd/es/table'
6-
import ActionLink from '../../ui/ActionLink/ActionLink'
76
import { $do } from '../../../actions/actions'
87
import { Store } from '../../../interfaces/store'
98
import { PaginationMode, WidgetListField, WidgetTableMeta } from '../../../interfaces/widget'
@@ -20,11 +19,9 @@ import cn from 'classnames'
2019
import Pagination from '../../ui/Pagination/Pagination'
2120
import HierarchyTable from '../../../components/HierarchyTable/HierarchyTable'
2221
import { BcFilter, FilterGroup } from '../../../interfaces/filters'
23-
import { useTranslation } from 'react-i18next'
2422
import FullHierarchyTable from '../../../components/FullHierarchyTable/FullHierarchyTable'
25-
import { parseFilters } from '../../../utils/filters'
26-
import Select from '../../ui/Select/Select'
2723
import RowOperationsButton from '../../RowOperations/RowOperationsButton'
24+
import Header from './components/Header'
2825
import { useRowMenu } from '../../../hooks/useRowMenu'
2926

3027
type AdditionalAntdTableProps = Partial<Omit<TableProps<DataItem>, 'rowSelection'>>
@@ -44,6 +41,9 @@ export interface TableWidgetOwnProps extends AdditionalAntdTableProps {
4441
export interface TableWidgetProps extends TableWidgetOwnProps {
4542
data: DataItem[]
4643
rowMetaFields: RowMetaField[]
44+
/**
45+
* @deprecated TODO: Remove 2.0 as it is never used
46+
*/
4747
limitBySelf: boolean
4848
/**
4949
* @deprecated TODO: Remove in 2.0 in favor of `widgetName`
@@ -69,7 +69,13 @@ export interface TableWidgetProps extends TableWidgetOwnProps {
6969
* @deprecated TODO: Remove in 2.0.0 as it's moved to RowOperationsMenu
7070
*/
7171
metaInProgress?: boolean
72+
/**
73+
* @deprecated TODO: Remove 2.0 as it is never used
74+
*/
7275
filters: BcFilter[]
76+
/**
77+
* @deprecated TODO: Remove 2.0 as it is never used
78+
*/
7379
filterGroups: FilterGroup[]
7480
/**
7581
* @deprecated TODO: Remove 2.0 as it is never used
@@ -86,8 +92,17 @@ export interface TableWidgetProps extends TableWidgetOwnProps {
8692
*/
8793
onSelectRow?: (bcName: string, cursor: string) => void
8894
onSelectCell: (cursor: string, widgetName: string, fieldKey: string) => void
95+
/**
96+
* @deprecated TODO: Remove 2.0 as it is never used
97+
*/
8998
onRemoveFilters: (bcName: string) => void
99+
/**
100+
* @deprecated TODO: Remove 2.0 as it is never used
101+
*/
90102
onApplyFilter: (bcName: string, filter: BcFilter, widgetName?: string) => void
103+
/**
104+
* @deprecated TODO: Remove 2.0 as it is never used
105+
*/
91106
onForceUpdate: (bcName: string) => void
92107
}
93108

@@ -141,24 +156,23 @@ export const TableWidget: FunctionComponent<TableWidgetProps> = props => {
141156
}
142157
}
143158
const isAllowEdit = (props.allowEdit ?? true) && !props.meta.options?.readOnly
144-
const { t } = useTranslation()
145159

146-
const processCellClick = React.useCallback(
160+
const processCellClick = useCallback(
147161
(recordId: string, fieldKey: string) => {
148162
onSelectCell(recordId, widgetMetaName, fieldKey)
149163
},
150164
[onSelectCell, widgetMetaName]
151165
)
152166

153-
const processedFields: WidgetListField[] = React.useMemo(
167+
const processedFields: WidgetListField[] = useMemo(
154168
() =>
155169
fields.map(item => {
156170
return item.type === FieldType.multivalue ? { ...item, type: FieldType.multivalueHover } : item
157171
}),
158172
[fields]
159173
)
160174

161-
const columns: Array<ColumnProps<DataItem>> = React.useMemo(() => {
175+
const columns: Array<ColumnProps<DataItem>> = useMemo(() => {
162176
return processedFields
163177
.filter(item => item.type !== FieldType.hidden && !item.hidden)
164178
.map(item => {
@@ -217,7 +231,7 @@ export const TableWidget: FunctionComponent<TableWidgetProps> = props => {
217231
selectedCell
218232
])
219233

220-
const resultColumns = React.useMemo(() => {
234+
const resultColumns = useMemo(() => {
221235
const controlColumnsLeft: Array<ColumnProps<DataItem>> = []
222236
const controlColumnsRight: Array<ColumnProps<DataItem>> = []
223237
props.controlColumns?.map(item => {
@@ -226,61 +240,10 @@ export const TableWidget: FunctionComponent<TableWidgetProps> = props => {
226240
return [...controlColumnsLeft, ...columns, ...controlColumnsRight]
227241
}, [columns, props.controlColumns])
228242

229-
const [filterGroupName, setFilterGroupName] = React.useState(null)
230-
const filtersExist = !!props.filters?.length
231-
232-
const handleShowAll = React.useCallback(() => {
233-
onShowAll(widgetBcName, cursor, null, widgetName)
234-
}, [onShowAll, widgetBcName, cursor, widgetName])
235-
236-
const handleRemoveFilters = React.useCallback(() => {
237-
onRemoveFilters(widgetBcName)
238-
onForceUpdate(widgetBcName)
239-
}, [onRemoveFilters, onForceUpdate, widgetBcName])
240-
241-
const handleAddFilters = React.useMemo(() => {
242-
return (value: string) => {
243-
const filterGroup = filterGroups.find(item => item.name === value)
244-
const parsedFilters = parseFilters(filterGroup.filters)
245-
setFilterGroupName(filterGroup.name)
246-
onRemoveFilters(widgetBcName)
247-
parsedFilters.forEach(item => onApplyFilter(widgetBcName, item, widgetName))
248-
onForceUpdate(widgetBcName)
249-
}
250-
}, [filterGroups, widgetBcName, widgetName, setFilterGroupName, onRemoveFilters, onApplyFilter, onForceUpdate])
251-
252-
React.useEffect(() => {
253-
if (!filtersExist) {
254-
setFilterGroupName(null)
255-
}
256-
}, [filtersExist])
257-
258-
const defaultHeader = React.useMemo(() => {
259-
return (
260-
<div className={styles.filtersContainer}>
261-
{!!filterGroups?.length && (
262-
<Select
263-
value={filterGroupName ?? t('Show all').toString()}
264-
onChange={handleAddFilters}
265-
dropdownMatchSelectWidth={false}
266-
>
267-
{filterGroups.map(group => (
268-
<Select.Option key={group.name} value={group.name}>
269-
<span>{group.name}</span>
270-
</Select.Option>
271-
))}
272-
</Select>
273-
)}
274-
{filtersExist && <ActionLink onClick={handleRemoveFilters}> {t('Clear all filters')} </ActionLink>}
275-
{props.limitBySelf && <ActionLink onClick={handleShowAll}> {t('Show all records')} </ActionLink>}
276-
</div>
277-
)
278-
}, [filterGroups, filterGroupName, filtersExist, props.limitBySelf, t, handleAddFilters, handleRemoveFilters, handleShowAll])
279-
280243
const [operationsRef, parentRef, onRow] = useRowMenu()
281244
return (
282245
<div className={styles.tableContainer} ref={parentRef}>
283-
{props.header ?? defaultHeader}
246+
{props.header ?? <Header bcName={widgetBcName} widgetName={widgetName} />}
284247
<Table
285248
className={cn(styles.table, { [styles.tableWithRowMenu]: props.showRowActions })}
286249
columns={resultColumns}
@@ -311,7 +274,13 @@ function mapStateToProps(store: Store, ownProps: TableWidgetOwnProps) {
311274
return {
312275
data: store.data[ownProps.meta.bcName],
313276
rowMetaFields: fields,
277+
/**
278+
* @deprecated
279+
*/
314280
limitBySelf,
281+
/**
282+
* @deprecated
283+
*/
315284
bcName,
316285
/**
317286
* @deprecated
@@ -324,7 +293,13 @@ function mapStateToProps(store: Store, ownProps: TableWidgetOwnProps) {
324293
* @deprecated
325294
*/
326295
pendingDataItem: null as PendingDataItem,
296+
/**
297+
* @deprecated
298+
*/
327299
filters,
300+
/**
301+
* @deprecated
302+
*/
328303
filterGroups: bc?.filterGroups
329304
}
330305
}
@@ -334,19 +309,31 @@ function mapDispatchToProps(dispatch: Dispatch) {
334309
onSelectCell: (cursor: string, widgetName: string, fieldKey: string) => {
335310
dispatch($do.selectTableCellInit({ widgetName, rowId: cursor, fieldKey }))
336311
},
312+
/**
313+
* @deprecated TODO: Remove in 2.0
314+
*/
337315
onShowAll: (bcName: string, cursor: string, route?: Route) => {
338316
dispatch($do.showAllTableRecordsInit({ bcName, cursor }))
339317
},
340318
/**
341319
* @deprecated TODO: Remove in 2.0
342320
*/
343321
onDrillDown: null as (widgetName: string, cursor: string, bcName: string, fieldKey: string) => void,
322+
/**
323+
* @deprecated TODO: Remove in 2.0
324+
*/
344325
onRemoveFilters: (bcName: string) => {
345326
dispatch($do.bcRemoveAllFilters({ bcName }))
346327
},
328+
/**
329+
* @deprecated TODO: Remove in 2.0
330+
*/
347331
onApplyFilter: (bcName: string, filter: BcFilter, widgetName?: string) => {
348332
dispatch($do.bcAddFilter({ bcName, filter, widgetName }))
349333
},
334+
/**
335+
* @deprecated TODO: Remove in 2.0
336+
*/
350337
onForceUpdate: (bcName: string) => {
351338
dispatch($do.bcForceUpdate({ bcName }))
352339
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.filtersContainer {
2+
:global {
3+
.ant-select {
4+
padding-top: 10px;
5+
padding-right: 10px;
6+
}
7+
8+
.ant-select-selection {
9+
border: none;
10+
11+
&:focus, &:active{
12+
box-shadow: none;
13+
}
14+
15+
&__rendered {
16+
margin-left: 0;
17+
}
18+
}
19+
}
20+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import React from 'react'
2+
import { mount, ReactWrapper } from 'enzyme'
3+
import { Store } from 'redux'
4+
import { Store as CoreStore } from '../../../../interfaces/store'
5+
import { mockStore } from '../../../../tests/mockStore'
6+
import * as redux from 'react-redux'
7+
import { Provider } from 'react-redux'
8+
import Header, { HeaderProps } from './Header'
9+
import { FilterType } from '../../../../interfaces/filters'
10+
import { ActionLink } from '../../../index'
11+
import { initLocale } from '../../../../imports/i18n'
12+
import i18n from 'i18next'
13+
import Select from '../../../ui/Select/Select'
14+
15+
describe('TableWidget > Header test', () => {
16+
let store: Store<CoreStore> = null
17+
let wrapper: ReactWrapper = null
18+
19+
const dispatch = jest.fn()
20+
21+
beforeAll(() => {
22+
initLocale('en', null)
23+
store = mockStore()
24+
})
25+
26+
beforeEach(() => {
27+
jest.spyOn(redux, 'useDispatch').mockImplementation(() => {
28+
return dispatch
29+
})
30+
})
31+
32+
afterEach(() => {
33+
dispatch.mockClear()
34+
jest.resetAllMocks()
35+
})
36+
37+
function getMountWrapper() {
38+
const restProps: HeaderProps = {
39+
bcName: 'bcName',
40+
widgetName: 'widgetName'
41+
}
42+
43+
return mount(
44+
<Provider store={store}>
45+
<Header {...restProps} />
46+
</Provider>
47+
)
48+
}
49+
50+
it('should render header', () => {
51+
wrapper = getMountWrapper()
52+
53+
const headerComponent = wrapper.find(Header)
54+
expect(headerComponent).toHaveLength(1)
55+
})
56+
57+
it('header contain clearing button', () => {
58+
store.getState().screen.filters.bcName = [
59+
{
60+
type: 'equalsOneOf' as FilterType,
61+
value: ['Low', 'Middle'],
62+
fieldName: 'importance',
63+
viewName: 'clientlist',
64+
widgetName: 'clientList'
65+
}
66+
]
67+
68+
wrapper = getMountWrapper()
69+
const headerComponent = wrapper.find(Header)
70+
const actionLinkComponent = headerComponent.find(ActionLink)
71+
72+
expect(actionLinkComponent).toHaveLength(1)
73+
expect(actionLinkComponent.text().trim()).toBe(i18n.t('Clear all filters'))
74+
})
75+
76+
it("header don't contain clearing button", () => {
77+
store.getState().screen.filters.bcName = []
78+
79+
wrapper = getMountWrapper()
80+
const headerComponent = wrapper.find(Header)
81+
const actionLinkComponent = headerComponent.find(ActionLink)
82+
83+
expect(actionLinkComponent).toHaveLength(0)
84+
})
85+
86+
it('header contain select for filter groups', () => {
87+
store.getState().screen.bo.bc = {
88+
bcName: {
89+
name: 'bcName',
90+
parentName: null,
91+
url: '',
92+
cursor: null,
93+
filterGroups: [
94+
{
95+
name: 'Example PDQ 1',
96+
filters: 'someField1.contains=123'
97+
},
98+
{
99+
name: 'Example PDQ 2',
100+
filters: 'someField1.contains=321&someField2.equalsOneOf=["Confirmed", "Canceled"]'
101+
}
102+
]
103+
}
104+
}
105+
106+
wrapper = getMountWrapper()
107+
const headerComponent = wrapper.find(Header)
108+
const selectComponent = headerComponent.find(Select)
109+
110+
expect(selectComponent).toHaveLength(1)
111+
})
112+
})

0 commit comments

Comments
 (0)