Skip to content

Commit fcc405f

Browse files
committed
Added support for EnumOrder attribute.
1 parent c620fe3 commit fcc405f

3 files changed

Lines changed: 97 additions & 7 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const TestSet = {
2+
No: 0,
3+
4+
Yes: 1
5+
} as const;
6+
7+
export const TestSetDescription: Record<number, string> = {
8+
0: "No",
9+
10+
1: "Yes"
11+
};
12+
13+
// Add the __order property hidden so it doesn't get enumerated.
14+
Object.defineProperty(TestSetDescription, "__order", {
15+
value: [1, 0],
16+
});
17+
18+
export type TestSet = typeof TestSet[keyof typeof TestSet];

src/SparkDevNetwork.Rock.CodeGenerator.Tests/TypeScriptViewModelGeneratorTests.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,22 @@ public Task GenerateEnumViewModel_UsesFieldDescription()
269269
return Verify( code, Settings );
270270
}
271271

272+
[Fact]
273+
public Task GenerateEnumViewModel_SupportsOrderAttribute()
274+
{
275+
var generator = new TypeScriptViewModelGenerator();
276+
var typeMock = GetEnumMock( "Rock.Enums", "TestSet" );
277+
278+
typeMock.Setup( m => m.GetFields( It.IsAny<BindingFlags>() ) ).Returns( () => [
279+
GetEnumFieldMock( typeMock, "No", 0, order: 1 ).Object,
280+
GetEnumFieldMock( typeMock, "Yes", 1, order: 0 ).Object
281+
] );
282+
283+
var code = generator.GenerateEnumViewModel( typeMock.Object );
284+
285+
return Verify( code, Settings );
286+
}
287+
272288
#endregion
273289

274290
#region GenerateEnumsViewModel
@@ -838,7 +854,7 @@ internal static Mock<Type> GetEnumMock( string @namespace, string typeName )
838854
return typeMock;
839855
}
840856

841-
internal static Mock<FieldInfo> GetEnumFieldMock( Mock<Type> mockType, string fieldName, int fieldValue, bool obsolete = false, string? obsoleteMessage = null, string? description = null )
857+
internal static Mock<FieldInfo> GetEnumFieldMock( Mock<Type> mockType, string fieldName, int fieldValue, bool obsolete = false, string? obsoleteMessage = null, string? description = null, int? order = null )
842858
{
843859
var fieldMock = GetFieldMock( mockType, fieldName, typeof( int ) );
844860

@@ -854,6 +870,11 @@ internal static Mock<FieldInfo> GetEnumFieldMock( Mock<Type> mockType, string fi
854870
attributes.Add( GetDescriptionAttributeData( description ).Object );
855871
}
856872

873+
if ( order != null )
874+
{
875+
attributes.Add( GetOrderAttributeData( order.Value ).Object );
876+
}
877+
857878
fieldMock.Setup( m => m.GetRawConstantValue() ).Returns( fieldValue );
858879
fieldMock.Setup( m => m.GetCustomAttributesData() ).Returns( () => attributes );
859880

@@ -915,6 +936,18 @@ internal static Mock<CustomAttributeData> GetDescriptionAttributeData( string te
915936
return attributeDataMock;
916937
}
917938

939+
internal static Mock<CustomAttributeData> GetOrderAttributeData( int order )
940+
{
941+
var attributeDataMock = new Mock<CustomAttributeData>( MockBehavior.Strict );
942+
var enumOrderTypeMock = XmlDocIdTests.GetTypeMock( "Rock.Enums", "EnumOrderAttribute" );
943+
944+
attributeDataMock.Setup( m => m.AttributeType ).Returns( enumOrderTypeMock.Object );
945+
attributeDataMock.Setup( m => m.ConstructorArguments )
946+
.Returns( [new CustomAttributeTypedArgument( order )] );
947+
948+
return attributeDataMock;
949+
}
950+
918951
internal static Mock<CustomAttributeData> GetEnumDomainAttributeData( string domain )
919952
{
920953
var attributeDataMock = new Mock<CustomAttributeData>( MockBehavior.Strict );

src/SparkDevNetwork.Rock.CodeGenerator/TypeScriptViewModelGenerator.cs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ private string GenerateEnumViewModelContent( Type type )
169169
sb.AppendLine();
170170
sb.Append( GenerateEnumViewModelContent( type, true ) );
171171
sb.AppendLine();
172+
sb.Append( GenerateOrderForEnumIfRequired( type ) );
172173

173174
AppendCommentBlock( sb, typeComment, 0 );
174175

@@ -220,11 +221,8 @@ private string GenerateEnumViewModelContent( Type type, bool isDescription )
220221
sb.AppendLine( $"export const {typeName}Description: Record<number, string> = {{" );
221222
}
222223

223-
//var sortedFields = fields.OrderBy( f => f.GetRawConstantValue() ).ToList();
224-
var sortedFields = fields.ToList();
225-
226224
// Loop through each sorted field and emit the declaration.
227-
for ( int i = 0; i < sortedFields.Count; i++ )
225+
for ( int i = 0; i < fields.Count; i++ )
228226
{
229227
var field = fields[i];
230228
var obsoleteFieldAttribute = field.GetCustomAttributeData( "System.ObsoleteAttribute" );
@@ -234,7 +232,7 @@ private string GenerateEnumViewModelContent( Type type, bool isDescription )
234232
// If this enum value is obsolete and there is another
235233
// enum that is not obsolete with the same integer
236234
// value then skip this one.
237-
var hasOtherField = sortedFields
235+
var hasOtherField = fields
238236
.Any( f => ( int ) f.GetRawConstantValue() == ( int ) field.GetRawConstantValue()
239237
&& f.GetCustomAttributeData( "System.ObsoleteAttribute" ) == null );
240238

@@ -297,7 +295,7 @@ private string GenerateEnumViewModelContent( Type type, bool isDescription )
297295
}
298296
}
299297

300-
if ( i + 1 < sortedFields.Count )
298+
if ( i + 1 < fields.Count )
301299
{
302300
sb.AppendLine( "," );
303301
}
@@ -319,6 +317,47 @@ private string GenerateEnumViewModelContent( Type type, bool isDescription )
319317
return sb.ToString();
320318
}
321319

320+
/// <summary>
321+
/// Generate the additional order field on the enum if any of the enum
322+
/// values have an EnumOrder attribute defined on them.
323+
/// </summary>
324+
/// <param name="type">The type that represents the enumeration.</param>
325+
/// <returns>A string of text to append to the enum file.</returns>
326+
private string GenerateOrderForEnumIfRequired( Type type )
327+
{
328+
var typeName = GetClassNameForType( type );
329+
var fields = type.GetFields( BindingFlags.Static | BindingFlags.Public )
330+
.Where( f => f.GetCustomAttributeData( "System.ObsoleteAttribute" ) == null )
331+
.Select( f => new
332+
{
333+
Order = ( int? ) f.GetCustomAttributeData( "Rock.Enums.EnumOrderAttribute" )
334+
?.ConstructorArguments[0].Value,
335+
Value = f.GetRawConstantValue()
336+
} )
337+
.ToList();
338+
339+
// If no fields have an EnumOrderAttribute, then we don't need to
340+
// generate anything.
341+
if ( !fields.Any( f => f.Order.HasValue ) )
342+
{
343+
return string.Empty;
344+
}
345+
346+
var sb = new StringBuilder();
347+
var orderedValues = fields.OrderBy( f => f.Order ?? 0 )
348+
.ThenBy( f => f.Value )
349+
.Select( f => f.Value )
350+
.Distinct();
351+
352+
sb.AppendLine( "// Add the __order property hidden so it doesn't get enumerated." );
353+
sb.AppendLine( $"Object.defineProperty({typeName}Description, \"__order\", {{" );
354+
sb.AppendLine( $" value: [{string.Join( ", ", orderedValues )}]," );
355+
sb.AppendLine( "});" );
356+
sb.AppendLine();
357+
358+
return sb.ToString();
359+
}
360+
322361
/// <summary>
323362
/// Generates the file for a SystemGuid declaration.
324363
/// </summary>

0 commit comments

Comments
 (0)