Skip to content

Commit 1da77be

Browse files
committed
Added column drag and drop reordering with persistant sizing and ordering
1 parent 1b44640 commit 1da77be

File tree

15 files changed

+984
-168
lines changed

15 files changed

+984
-168
lines changed

src/EventLogExpert.UI.Tests/Store/EventTable/EventTableEffectsTests.cs

Lines changed: 188 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using EventLogExpert.UI.Store.EventTable;
66
using Fluxor;
77
using NSubstitute;
8+
using System.Collections.Immutable;
89

910
namespace EventLogExpert.UI.Tests.Store.EventTable;
1011

@@ -27,8 +28,29 @@ public async Task HandleLoadColumns_ShouldLoadAllColumnsFromPreferences()
2728
await effects.HandleLoadColumns(mockDispatcher);
2829

2930
// Assert
30-
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(a =>
31-
a.LoadedColumns.Count == Enum.GetValues<ColumnName>().Length));
31+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
32+
action.LoadedColumns.Count == Enum.GetValues<ColumnName>().Length));
33+
}
34+
35+
[Fact]
36+
public async Task HandleLoadColumns_ShouldLoadWidthsFromPreferences()
37+
{
38+
// Arrange
39+
var enabledColumns = new List<ColumnName> { ColumnName.Level };
40+
var savedWidths = new Dictionary<ColumnName, int>
41+
{
42+
{ ColumnName.Level, 150 }
43+
};
44+
45+
var (effects, mockDispatcher, mockPreferencesProvider) = CreateEffects(enabledColumns);
46+
mockPreferencesProvider.ColumnWidthsPreference.Returns(savedWidths);
47+
48+
// Act
49+
await effects.HandleLoadColumns(mockDispatcher);
50+
51+
// Assert
52+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
53+
action.ColumnWidths[ColumnName.Level] == 150));
3254
}
3355

3456
[Fact]
@@ -46,9 +68,9 @@ public async Task HandleLoadColumns_ShouldMarkDisabledColumnsAsFalse()
4668
await effects.HandleLoadColumns(mockDispatcher);
4769

4870
// Assert
49-
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(a =>
50-
a.LoadedColumns[ColumnName.Source] == false &&
51-
a.LoadedColumns[ColumnName.EventId] == false));
71+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
72+
action.LoadedColumns[ColumnName.Source] == false &&
73+
action.LoadedColumns[ColumnName.EventId] == false));
5274
}
5375

5476
[Fact]
@@ -67,9 +89,60 @@ public async Task HandleLoadColumns_ShouldMarkEnabledColumnsAsTrue()
6789
await effects.HandleLoadColumns(mockDispatcher);
6890

6991
// Assert
70-
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(a =>
71-
a.LoadedColumns[ColumnName.Level] == true &&
72-
a.LoadedColumns[ColumnName.DateAndTime] == true));
92+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
93+
action.LoadedColumns[ColumnName.Level] == true &&
94+
action.LoadedColumns[ColumnName.DateAndTime] == true));
95+
}
96+
97+
[Fact]
98+
public async Task HandleLoadColumns_ShouldUseDefaultOrderWhenNotSaved()
99+
{
100+
// Arrange
101+
var enabledColumns = new List<ColumnName> { ColumnName.Level };
102+
103+
var (effects, mockDispatcher, _) = CreateEffects(enabledColumns);
104+
105+
// Act
106+
await effects.HandleLoadColumns(mockDispatcher);
107+
108+
// Assert
109+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
110+
action.ColumnOrder.SequenceEqual(ColumnDefaults.Order)));
111+
}
112+
113+
[Fact]
114+
public async Task HandleLoadColumns_ShouldUseDefaultWidthsWhenNotSaved()
115+
{
116+
// Arrange
117+
var enabledColumns = new List<ColumnName> { ColumnName.Level };
118+
119+
var (effects, mockDispatcher, _) = CreateEffects(enabledColumns);
120+
121+
// Act
122+
await effects.HandleLoadColumns(mockDispatcher);
123+
124+
// Assert
125+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
126+
action.ColumnWidths[ColumnName.Level] == ColumnDefaults.GetWidth(ColumnName.Level)));
127+
}
128+
129+
[Fact]
130+
public async Task HandleLoadColumns_ShouldUseSavedOrderWhenPresent()
131+
{
132+
// Arrange
133+
var enabledColumns = new List<ColumnName> { ColumnName.Source, ColumnName.Level };
134+
var savedOrder = new List<ColumnName> { ColumnName.Source, ColumnName.Level };
135+
136+
var (effects, mockDispatcher, mockPreferencesProvider) = CreateEffects(enabledColumns);
137+
mockPreferencesProvider.ColumnOrderPreference.Returns(savedOrder);
138+
139+
// Act
140+
await effects.HandleLoadColumns(mockDispatcher);
141+
142+
// Assert
143+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
144+
action.ColumnOrder[0] == ColumnName.Source &&
145+
action.ColumnOrder[1] == ColumnName.Level));
73146
}
74147

75148
[Fact]
@@ -84,8 +157,82 @@ public async Task HandleLoadColumns_WhenNoColumnsEnabled_ShouldMarkAllAsFalse()
84157
await effects.HandleLoadColumns(mockDispatcher);
85158

86159
// Assert
87-
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(a =>
88-
a.LoadedColumns.All(kvp => kvp.Value == false)));
160+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
161+
action.LoadedColumns.All(kvp => kvp.Value == false)));
162+
}
163+
164+
[Fact]
165+
public async Task HandleReorderColumn_ShouldPersistToPreferences()
166+
{
167+
// Arrange - state reflects post-reducer result (Source moved to index 0)
168+
var postReducerState = new EventTableState
169+
{
170+
ColumnOrder = [ColumnName.Source, ColumnName.Level, ColumnName.DateAndTime]
171+
};
172+
173+
var (effects, mockDispatcher, mockPreferencesProvider) = CreateEffects(state: postReducerState);
174+
175+
var action = new EventTableAction.ReorderColumn(ColumnName.Source, ColumnName.Level, false);
176+
177+
// Act
178+
await effects.HandleReorderColumn(action, mockDispatcher);
179+
180+
// Assert
181+
_ = mockPreferencesProvider.Received(1).ColumnOrderPreference =
182+
Arg.Is<IEnumerable<ColumnName>>(order => order.First() == ColumnName.Source);
183+
}
184+
185+
[Fact]
186+
public async Task HandleResetColumnDefaults_ShouldResetAllColumnSettingsToDefaults()
187+
{
188+
// Arrange
189+
var enabledColumns = new List<ColumnName> { ColumnName.Level, ColumnName.Source };
190+
var (effects, mockDispatcher, mockPreferencesProvider) = CreateEffects(enabledColumns);
191+
192+
// Act
193+
await effects.HandleResetColumnDefaults(mockDispatcher);
194+
195+
// Assert
196+
_ = mockPreferencesProvider.Received(1).EnabledEventTableColumnsPreference =
197+
Arg.Is<IEnumerable<ColumnName>>(c => c.SequenceEqual(ColumnDefaults.EnabledColumns));
198+
_ = mockPreferencesProvider.Received(1).ColumnWidthsPreference =
199+
Arg.Is<IDictionary<ColumnName, int>>(w => w.Count == 0);
200+
_ = mockPreferencesProvider.Received(1).ColumnOrderPreference =
201+
Arg.Is<IEnumerable<ColumnName>>(o => !o.Any());
202+
203+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
204+
action.ColumnWidths.SequenceEqual(ColumnDefaults.Widths) &&
205+
action.ColumnOrder.SequenceEqual(ColumnDefaults.Order) &&
206+
action.LoadedColumns[ColumnName.Level] == true &&
207+
action.LoadedColumns[ColumnName.DateAndTime] == true &&
208+
action.LoadedColumns[ColumnName.Source] == true &&
209+
action.LoadedColumns[ColumnName.EventId] == true &&
210+
action.LoadedColumns[ColumnName.TaskCategory] == true &&
211+
action.LoadedColumns[ColumnName.ActivityId] == false));
212+
}
213+
214+
[Fact]
215+
public async Task HandleSetColumnWidth_ShouldPersistToPreferences()
216+
{
217+
// Arrange
218+
var postReducerState = new EventTableState
219+
{
220+
ColumnWidths = new Dictionary<ColumnName, int>
221+
{
222+
{ ColumnName.Level, 200 }
223+
}.ToImmutableDictionary()
224+
};
225+
226+
var (effects, mockDispatcher, mockPreferencesProvider) = CreateEffects(state: postReducerState);
227+
228+
var action = new EventTableAction.SetColumnWidth(ColumnName.Level, 200);
229+
230+
// Act
231+
await effects.HandleSetColumnWidth(action, mockDispatcher);
232+
233+
// Assert
234+
_ = mockPreferencesProvider.Received(1).ColumnWidthsPreference =
235+
Arg.Is<IDictionary<ColumnName, int>>(width => width[ColumnName.Level] == 200);
89236
}
90237

91238
[Fact]
@@ -107,11 +254,11 @@ public async Task HandleToggleColumn_ShouldOnlyChangeToggledColumn()
107254
await effects.HandleToggleColumn(action, mockDispatcher);
108255

109256
// Assert
110-
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(a =>
111-
a.LoadedColumns[ColumnName.Level] == true &&
112-
a.LoadedColumns[ColumnName.DateAndTime] == false &&
113-
a.LoadedColumns[ColumnName.Source] == true &&
114-
a.LoadedColumns[ColumnName.EventId] == true));
257+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
258+
action.LoadedColumns[ColumnName.Level] == true &&
259+
action.LoadedColumns[ColumnName.DateAndTime] == false &&
260+
action.LoadedColumns[ColumnName.Source] == true &&
261+
action.LoadedColumns[ColumnName.EventId] == true));
115262
}
116263

117264
[Fact]
@@ -132,8 +279,8 @@ public async Task HandleToggleColumn_ShouldUpdatePreferences()
132279

133280
// Assert
134281
_ = mockPreferencesProvider.Received(1).EnabledEventTableColumnsPreference =
135-
Arg.Is<IEnumerable<ColumnName>>(cols =>
136-
cols.Contains(ColumnName.Source));
282+
Arg.Is<IEnumerable<ColumnName>>(columns =>
283+
columns.Contains(ColumnName.Source));
137284
}
138285

139286
[Fact]
@@ -153,10 +300,10 @@ public async Task HandleToggleColumn_WhenColumnDisabled_ShouldEnableIt()
153300
await effects.HandleToggleColumn(action, mockDispatcher);
154301

155302
// Assert
156-
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(a =>
157-
a.LoadedColumns[ColumnName.Level] == true &&
158-
a.LoadedColumns[ColumnName.DateAndTime] == true &&
159-
a.LoadedColumns[ColumnName.Source] == true));
303+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
304+
action.LoadedColumns[ColumnName.Level] == true &&
305+
action.LoadedColumns[ColumnName.DateAndTime] == true &&
306+
action.LoadedColumns[ColumnName.Source] == true));
160307
}
161308

162309
[Fact]
@@ -177,10 +324,10 @@ public async Task HandleToggleColumn_WhenColumnEnabled_ShouldDisableIt()
177324
await effects.HandleToggleColumn(action, mockDispatcher);
178325

179326
// Assert
180-
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(a =>
181-
a.LoadedColumns[ColumnName.Level] == false &&
182-
a.LoadedColumns[ColumnName.DateAndTime] == true &&
183-
a.LoadedColumns[ColumnName.Source] == true));
327+
mockDispatcher.Received(1).Dispatch(Arg.Is<EventTableAction.LoadColumnsCompleted>(action =>
328+
action.LoadedColumns[ColumnName.Level] == false &&
329+
action.LoadedColumns[ColumnName.DateAndTime] == true &&
330+
action.LoadedColumns[ColumnName.Source] == true));
184331
}
185332

186333
[Fact]
@@ -202,11 +349,11 @@ public async Task HandleToggleColumn_WhenDisabling_ShouldRemoveFromPreferences()
202349

203350
// Assert
204351
var _ = mockPreferencesProvider.Received(1).EnabledEventTableColumnsPreference =
205-
Arg.Is<IEnumerable<ColumnName>>(cols =>
206-
cols.Contains(ColumnName.Level) &&
207-
cols.Contains(ColumnName.DateAndTime) &&
208-
!cols.Contains(ColumnName.Source) &&
209-
cols.Count() == 2);
352+
Arg.Is<IEnumerable<ColumnName>>(columns =>
353+
columns.Contains(ColumnName.Level) &&
354+
columns.Contains(ColumnName.DateAndTime) &&
355+
!columns.Contains(ColumnName.Source) &&
356+
columns.Count() == 2);
210357
}
211358

212359
[Fact]
@@ -226,10 +373,10 @@ public async Task HandleToggleColumn_WhenEnabling_ShouldPersistToPreferences()
226373

227374
// Assert
228375
_ = mockPreferencesProvider.Received(1).EnabledEventTableColumnsPreference =
229-
Arg.Is<IEnumerable<ColumnName>>(cols =>
230-
cols.Contains(ColumnName.Level) &&
231-
cols.Contains(ColumnName.Source) &&
232-
cols.Count() == 2);
376+
Arg.Is<IEnumerable<ColumnName>>(columns =>
377+
columns.Contains(ColumnName.Level) &&
378+
columns.Contains(ColumnName.Source) &&
379+
columns.Count() == 2);
233380
}
234381

235382
[Fact]
@@ -259,12 +406,17 @@ public async Task HandleUpdateTable_ShouldDispatchUpdateCombinedEvents()
259406
}
260407

261408
private static (EventTableEffects effects, IDispatcher mockDispatcher, IPreferencesProvider mockPreferencesProvider)
262-
CreateEffects(List<ColumnName>? enabledColumns = null)
409+
CreateEffects(List<ColumnName>? enabledColumns = null, EventTableState? state = null)
263410
{
264411
var mockPreferencesProvider = Substitute.For<IPreferencesProvider>();
265412
mockPreferencesProvider.EnabledEventTableColumnsPreference.Returns(enabledColumns ?? []);
413+
mockPreferencesProvider.ColumnWidthsPreference.Returns(new Dictionary<ColumnName, int>());
414+
mockPreferencesProvider.ColumnOrderPreference.Returns([]);
415+
416+
var mockState = Substitute.For<IState<EventTableState>>();
417+
mockState.Value.Returns(state ?? new EventTableState());
266418

267-
var effects = new EventTableEffects(mockPreferencesProvider);
419+
var effects = new EventTableEffects(mockPreferencesProvider, mockState);
268420
var mockDispatcher = Substitute.For<IDispatcher>();
269421

270422
return (effects, mockDispatcher, mockPreferencesProvider);

0 commit comments

Comments
 (0)