11import {
2- DataSource ,
2+ DataSourceConfig ,
33 isViewportMenusAction ,
44 isVisualLinksAction ,
55 MenuActionConfig ,
66 MenuRpcResponse ,
7+ RemoteDataSource ,
78 SuggestionFetcher ,
89 useTypeaheadSuggestions ,
910 useVuuMenuActions ,
@@ -12,8 +13,8 @@ import {
1213 VuuUIMessageInRPCEditReject ,
1314 VuuUIMessageInRPCEditSuccess ,
1415} from "@finos/vuu-data" ;
15- import { VuuMenu , VuuTable } from "@finos/vuu-protocol-types" ;
16- import { useCallback , useMemo , useRef } from "react" ;
16+ import { VuuGroupBy , VuuMenu , VuuTable } from "@finos/vuu-protocol-types" ;
17+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
1718import { bySortIndex , isSortedColumn , toSortDef } from "./AgGridDataUtils" ;
1819
1920import {
@@ -23,9 +24,11 @@ import {
2324import { FilterDataProvider } from "./FilterDataProvider" ;
2425import { GroupCellRenderer } from "./GroupCellRenderer" ;
2526import { ViewportRowModelDataSource } from "./ViewportRowModelDataSource" ;
26- import { buildColumnMap } from "@finos/vuu-utils" ;
27+ import { buildColumnMap , itemsOrOrderChanged } from "@finos/vuu-utils" ;
2728import { vuuMenuToAgGridMenu } from "./agGridMenuUtils" ;
2829import { AgData } from "./AgDataWindow" ;
30+ import { ColumnDescriptor } from "@finos/vuu-datagrid-types" ;
31+ import { createColumnDefs } from "./AgGridColumnUtils" ;
2932
3033type Column = {
3134 getId : ( ) => string ;
@@ -38,11 +41,23 @@ interface FilterChangedEvent {
3841 getFilterModel : ( ) => AgGridFilter ;
3942 } ;
4043}
44+
45+ export interface AgGridDataRow {
46+ [ key : string ] : unknown ;
47+ expanded ?: boolean ;
48+ groupKey : string ;
49+ groupKeys ?: string ;
50+ groupRow : boolean ;
51+ }
52+
53+ const isAgGridGroupDataRow = ( data : unknown ) : data is AgGridDataRow =>
54+ typeof data === "object" &&
55+ data !== null &&
56+ typeof ( data as AgGridDataRow ) [ "groupKey" ] === "string" ;
57+
4158type RowGroupOpenedEvent = {
42- data : {
43- expanded : boolean ;
44- groupKey : string ;
45- } ;
59+ data ?: AgGridDataRow ;
60+ expanded : boolean ;
4661 node : { expanded ?: boolean } ;
4762} ;
4863type ColumnState = { colId : string ; sortIndex : number } [ ] ;
@@ -55,7 +70,8 @@ type SortChangedEvent = {
5570const NullSuggestionFetcher : SuggestionFetcher = async ( ) => [ ] ;
5671
5772export interface ViewportRowModelHookProps {
58- dataSource : DataSource ;
73+ columns ?: ColumnDescriptor [ ] ;
74+ dataSource : RemoteDataSource ;
5975 onFeatureEnabled ?: ( message : VuuFeatureMessage ) => void ;
6076 onRpcResponse ?: (
6177 response :
@@ -65,7 +81,16 @@ export interface ViewportRowModelHookProps {
6581 ) => void ;
6682}
6783
84+ type GroupByConfigChange = {
85+ groupBy : VuuGroupBy ;
86+ } ;
87+
88+ const hasGroupByChange = (
89+ message ?: Partial < DataSourceConfig >
90+ ) : message is GroupByConfigChange => Array . isArray ( message ?. groupBy ) ;
91+
6892export const useViewportRowModel = ( {
93+ columns,
6994 dataSource,
7095 onRpcResponse,
7196 onFeatureEnabled,
@@ -74,8 +99,12 @@ export const useViewportRowModel = ({
7499 const getTypeaheadSuggestionsRef = useRef < SuggestionFetcher > (
75100 NullSuggestionFetcher
76101 ) ;
102+ const groupByRef = useRef < VuuGroupBy > ( [ ] ) ;
103+ const [ groupBy , setGroupBy ] = useState < VuuGroupBy > ( groupByRef . current ) ;
77104 getTypeaheadSuggestionsRef . current = useTypeaheadSuggestions ( ) ;
78105
106+ const { table } = dataSource ;
107+
79108 // It is important that these values are not assigned in advance. They
80109 // are accessed at the point of construction of ContextMenu
81110 const menuActionConfig : MenuActionConfig = useMemo (
@@ -140,23 +169,57 @@ export const useViewportRowModel = ({
140169 return new FilterDataProvider ( table , getTypeaheadSuggestionsRef ) ;
141170 } , [ ] ) ;
142171
172+ const columnDefs = useMemo ( ( ) => {
173+ return Array . isArray ( columns )
174+ ? createColumnDefs ( createFilterDataProvider ( table ) , columns , groupBy )
175+ : undefined ;
176+ } , [ columns , createFilterDataProvider , groupBy , table ] ) ;
177+
178+ useEffect ( ( ) => {
179+ // We listen to dsataSource config changes to detect changes applied
180+ // directly to the dataSource, i.e. not applied via AgGrid and detected via
181+ // AgGrid event callbacks. For the former, AgGrid has no knowledge
182+ // that a change has occurred, so will not render the subsequent row refresh
183+ // correctly. In the case of GroupBy, for example. we need to recompute the
184+ // column defs to apply grouping. This will cause AgGrid to re-render columns
185+ // taking grouping into account and will enable subsequent refresh of grouped
186+ // data to be handled correctly.
187+ // Where a config change DOES originate from AgGrid (e.g user has applied
188+ // grouping from the Ag Grid context menu), we store latest value in a ref,
189+ // so that we can ignore the config change event(s) that will be fired by
190+ // the dataSource for this config change.
191+ dataSource . on ( "config" , ( config ) => {
192+ if (
193+ hasGroupByChange ( config ) &&
194+ itemsOrOrderChanged ( groupByRef . current , config . groupBy )
195+ ) {
196+ setGroupBy ( config . groupBy ) ;
197+ }
198+ } ) ;
199+ } , [ dataSource ] ) ;
200+
201+ // Fired when user has applied grouping from AG Grid, either via the
202+ // column menu or by dragging a column onto the group bar.
143203 const handleColumnRowGroupChanged = useCallback (
144204 ( evt : unknown ) => {
145205 const columnRowGroupChangedEvent = evt as ColumnRowGroupChangedEvent ;
146206 const { columns } = columnRowGroupChangedEvent ;
147207 if ( columns !== null ) {
148- const colIds = columns . map ( ( c ) => c . getId ( ) ) ;
149- viewportDatasource . setRowGroups ( colIds ) ;
208+ const vuuGroupBy : VuuGroupBy = columns . map ( ( c ) => c . getId ( ) ) ;
209+ groupByRef . current = vuuGroupBy ;
210+ viewportDatasource . setRowGroups ( vuuGroupBy ) ;
150211 }
151212 } ,
152213 [ viewportDatasource ]
153214 ) ;
154215
155216 const handleRowGroupOpened = useCallback (
156217 ( evt : RowGroupOpenedEvent ) => {
157- const { groupKey } = evt . data ;
158- const { expanded = false } = evt . node ;
159- viewportDatasource . setExpanded ( groupKey , ! expanded ) ;
218+ if ( isAgGridGroupDataRow ( evt . data ) ) {
219+ const { groupKey } = evt . data ;
220+ const { expanded = false } = evt . node ;
221+ viewportDatasource . setExpanded ( groupKey , ! expanded ) ;
222+ }
160223 } ,
161224 [ viewportDatasource ]
162225 ) ;
@@ -238,6 +301,7 @@ export const useViewportRowModel = ({
238301
239302 return {
240303 autoGroupColumnDef,
304+ columnDefs,
241305 createFilterDataProvider,
242306 viewportDatasource,
243307 defaultColDef : {
0 commit comments