-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Johanne Lie <joli@patentstyret.no> Co-authored-by: johlie <48760352+johlie@users.noreply.github.com> Co-authored-by: Ole Martin Handeland <git@olemartin.org> closes undefined
- Loading branch information
Showing
33 changed files
with
1,120 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
src/altinn-app-frontend/src/components/base/ListComponent.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import React from 'react'; | ||
|
||
import { getInitialStateMock } from '__mocks__/initialStateMock'; | ||
import { SortDirection } from '@altinn/altinn-design-system'; | ||
import { screen } from '@testing-library/react'; | ||
import { mockComponentProps, renderWithProviders } from 'testUtils'; | ||
import type { PreloadedState } from 'redux'; | ||
|
||
import { ListComponent } from 'src/components/base/ListComponent'; | ||
import type { IListProps } from 'src/components/base/ListComponent'; | ||
import type { IDataListsState } from 'src/shared/resources/dataLists'; | ||
import type { RootState } from 'src/store'; | ||
|
||
const countries = [ | ||
{ Name: 'Norway', Population: 5, HighestMountain: 2469 }, | ||
{ Name: 'Sweden', Population: 10, HighestMountain: 1738 }, | ||
{ Name: 'Denmark', Population: 6, HighestMountain: 170 }, | ||
{ Name: 'Germany', Population: 83, HighestMountain: 2962 }, | ||
{ Name: 'Spain', Population: 47, HighestMountain: 3718 }, | ||
{ Name: 'France', Population: 67, HighestMountain: 4807 }, | ||
]; | ||
|
||
export const testState: IDataListsState = { | ||
dataLists: { | ||
['countries']: { | ||
listItems: countries, | ||
dataListId: 'countries', | ||
loading: true, | ||
sortColumn: 'HighestMountain', | ||
sortDirection: SortDirection.Ascending, | ||
}, | ||
}, | ||
dataListsWithIndexIndicator: [], | ||
error: null, | ||
}; | ||
|
||
const render = (props: Partial<IListProps> = {}, customState: PreloadedState<RootState> = {}) => { | ||
const allProps: IListProps = { | ||
...mockComponentProps, | ||
dataListId: 'countries', | ||
tableHeaders: ['Name', 'Population', 'HighestMountain'], | ||
sortableColumns: ['Population', 'HighestMountain'], | ||
pagination: { alternatives: [2, 5], default: 2 }, | ||
getTextResourceAsString: (value) => value, | ||
...props, | ||
}; | ||
|
||
renderWithProviders(<ListComponent {...allProps} />, { | ||
preloadedState: { | ||
...getInitialStateMock(), | ||
dataListState: { | ||
dataLists: { | ||
[allProps.id]: { listItems: countries, id: 'countries' }, | ||
}, | ||
error: { | ||
name: '', | ||
message: '', | ||
}, | ||
...customState, | ||
}, | ||
}, | ||
}); | ||
}; | ||
|
||
describe('ListComponent', () => { | ||
jest.useFakeTimers(); | ||
|
||
it('should render rows that is sent in but not rows that is not sent in', async () => { | ||
render({}); | ||
expect(screen.getByText('Norway')).toBeInTheDocument(); | ||
expect(screen.getByText('Sweden')).toBeInTheDocument(); | ||
expect(screen.queryByText('Italy')).not.toBeInTheDocument(); | ||
}); | ||
}); |
195 changes: 195 additions & 0 deletions
195
src/altinn-app-frontend/src/components/base/ListComponent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
import React from 'react'; | ||
|
||
import { | ||
Pagination, | ||
RadioButton, | ||
SortDirection, | ||
Table, | ||
TableBody, | ||
TableCell, | ||
TableFooter, | ||
TableHeader, | ||
TableRow, | ||
} from '@altinn/altinn-design-system'; | ||
import type { ChangeProps, RowData, SortProps } from '@altinn/altinn-design-system'; | ||
|
||
import type { PropsFromGenericComponent } from '..'; | ||
|
||
import { useAppDispatch, useAppSelector } from 'src/common/hooks'; | ||
import { useGetDataList } from 'src/components/hooks'; | ||
import { DataListsActions } from 'src/shared/resources/dataLists/dataListsSlice'; | ||
|
||
import { getLanguageFromKey } from 'altinn-shared/utils'; | ||
|
||
export type IListProps = PropsFromGenericComponent<'List'>; | ||
|
||
const defaultDataList: any[] = []; | ||
export interface rowValue { | ||
[key: string]: string; | ||
} | ||
|
||
export const ListComponent = ({ | ||
tableHeaders, | ||
id, | ||
pagination, | ||
formData, | ||
handleDataChange, | ||
getTextResourceAsString, | ||
sortableColumns, | ||
dataModelBindings, | ||
language, | ||
}: IListProps) => { | ||
const dynamicDataList = useGetDataList({ id }); | ||
const calculatedDataList = dynamicDataList || defaultDataList; | ||
const defaultPagination = pagination ? pagination.default : 0; | ||
const rowsPerPage = useAppSelector((state) => state.dataListState.dataLists[id]?.size || defaultPagination); | ||
const currentPage = useAppSelector((state) => state.dataListState.dataLists[id]?.pageNumber || 0); | ||
|
||
const sortColumn = useAppSelector((state) => state.dataListState.dataLists[id]?.sortColumn || null); | ||
const sortDirection = useAppSelector( | ||
(state) => state.dataListState.dataLists[id]?.sortDirection || SortDirection.NotActive, | ||
); | ||
const totalItemsCount = useAppSelector( | ||
(state) => state.dataListState.dataLists[id]?.paginationData?.totaltItemsCount || 0, | ||
); | ||
|
||
const handleChange = ({ selectedValue }: ChangeProps) => { | ||
for (const key in formData) { | ||
handleDataChange(selectedValue[key], { key: key }); | ||
} | ||
}; | ||
|
||
const renderRow = (datalist) => { | ||
const cells: JSX.Element[] = []; | ||
for (const key of Object.keys(datalist)) { | ||
cells.push(<TableCell key={`${key}_${datalist[key]}`}>{datalist[key]}</TableCell>); | ||
} | ||
return cells; | ||
}; | ||
|
||
const renderHeaders = (headers) => { | ||
const cell: JSX.Element[] = []; | ||
for (const header of headers) { | ||
if ((sortableColumns || []).includes(header)) { | ||
cell.push( | ||
<TableCell | ||
onChange={handleSortChange} | ||
sortKey={header} | ||
key={header} | ||
sortDirecton={sortColumn === header ? sortDirection : SortDirection.NotActive} | ||
> | ||
{getTextResourceAsString(header)} | ||
</TableCell>, | ||
); | ||
} else { | ||
cell.push(<TableCell key={header}>{getTextResourceAsString(header)}</TableCell>); | ||
} | ||
} | ||
return cell; | ||
}; | ||
|
||
const dispatch = useAppDispatch(); | ||
|
||
const handleSortChange = ({ sortedColumn, previousSortDirection }: SortProps) => { | ||
dispatch( | ||
DataListsActions.setSort({ | ||
key: id || '', | ||
sortColumn: sortedColumn, | ||
sortDirection: | ||
previousSortDirection === SortDirection.Descending ? SortDirection.Ascending : SortDirection.Descending, | ||
}), | ||
); | ||
}; | ||
|
||
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLSelectElement>) => { | ||
dispatch( | ||
DataListsActions.setPageSize({ | ||
key: id || '', | ||
size: parseInt(event.target.value, 10), | ||
}), | ||
); | ||
}; | ||
|
||
const handleChangeCurrentPage = (newPage: number) => { | ||
dispatch( | ||
DataListsActions.setPageNumber({ | ||
key: id || '', | ||
pageNumber: newPage, | ||
}), | ||
); | ||
}; | ||
const rowAsValue = (datalist) => { | ||
const chosenRowData: rowValue = {}; | ||
for (const key in dataModelBindings) { | ||
chosenRowData[key] = datalist[key]; | ||
} | ||
return chosenRowData; | ||
}; | ||
const rowAsValueString = (datalist) => { | ||
return JSON.stringify(rowAsValue(datalist)); | ||
}; | ||
|
||
const createLabelRadioButton = (datalist) => { | ||
let label = ''; | ||
for (const key in formData) { | ||
label += `${key} ${datalist[key]} `; | ||
} | ||
return label; | ||
}; | ||
|
||
return ( | ||
<Table | ||
selectRows={true} | ||
onChange={handleChange} | ||
selectedValue={formData as RowData} | ||
> | ||
<TableHeader> | ||
<TableRow> | ||
<TableCell radiobutton={true}></TableCell> | ||
{renderHeaders(tableHeaders)} | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{calculatedDataList.map((datalist) => { | ||
return ( | ||
<TableRow | ||
key={JSON.stringify(datalist)} | ||
rowData={rowAsValue(datalist)} | ||
> | ||
<TableCell radiobutton={true}> | ||
<RadioButton | ||
name={datalist} | ||
onChange={() => { | ||
// Intentionally empty to prevent double-selection | ||
}} | ||
value={rowAsValueString(datalist)} | ||
checked={rowAsValueString(datalist) === JSON.stringify(formData) ? true : false} | ||
label={createLabelRadioButton(datalist)} | ||
hideLabel={true} | ||
></RadioButton> | ||
</TableCell> | ||
{renderRow(datalist)} | ||
</TableRow> | ||
); | ||
})} | ||
</TableBody> | ||
{pagination && ( | ||
<TableFooter> | ||
<TableRow> | ||
<TableCell colSpan={tableHeaders && 1 + tableHeaders?.length}> | ||
<Pagination | ||
numberOfRows={totalItemsCount} | ||
rowsPerPageOptions={pagination.alternatives} | ||
rowsPerPage={rowsPerPage} | ||
onRowsPerPageChange={handleChangeRowsPerPage} | ||
currentPage={currentPage} | ||
setCurrentPage={handleChangeCurrentPage} | ||
descriptionTexts={getLanguageFromKey('list_component', language)} | ||
/> | ||
</TableCell> | ||
</TableRow> | ||
</TableFooter> | ||
)} | ||
</Table> | ||
); | ||
}; |
Oops, something went wrong.