diff --git a/e2e/Sandbox/MainWindow.xaml.cs b/e2e/Sandbox/MainWindow.xaml.cs index c027865..22381b2 100644 --- a/e2e/Sandbox/MainWindow.xaml.cs +++ b/e2e/Sandbox/MainWindow.xaml.cs @@ -46,8 +46,8 @@ public partial class MainWindow : Window { static readonly string _dashboardFilePath = Path.Combine(Environment.CurrentDirectory, "Dashboards"); - readonly string _readFilePath = Path.Combine(_dashboardFilePath, DashboardFileNames.Sales); - //readonly string _readFilePath = Path.Combine(_dashboardFilePath, "My Dashboard.rdash"); + //readonly string _readFilePath = Path.Combine(_dashboardFilePath, DashboardFileNames.Sales); + readonly string _readFilePath = Path.Combine(_dashboardFilePath, "New Dashboard.rdash"); readonly string _saveJsonToPath = Path.Combine(_dashboardFilePath, "MyDashboard.json"); readonly string _saveRdashToPath = Path.Combine(_dashboardFilePath, DashboardFileNames.MyDashboard); diff --git a/src/Reveal.Sdk.Dom.Tests/Core/VisualizationCollectionFixture.cs b/src/Reveal.Sdk.Dom.Tests/Core/VisualizationCollectionFixture.cs index 624d4ea..cfa5bf8 100644 --- a/src/Reveal.Sdk.Dom.Tests/Core/VisualizationCollectionFixture.cs +++ b/src/Reveal.Sdk.Dom.Tests/Core/VisualizationCollectionFixture.cs @@ -1,4 +1,6 @@ using Reveal.Sdk.Dom.Core; +using Reveal.Sdk.Dom.Data; +using Reveal.Sdk.Dom.Filters; using Reveal.Sdk.Dom.Visualizations; using System.Collections.Generic; using Xunit; @@ -108,6 +110,12 @@ private class MockVisualization : IVisualization, IParentDocument public string Description { get; set; } public IDataDefinition DataDefinition { get; } RdashDocument IParentDocument.Document { get; set; } + public List FilterBindings { get; } + + public DataSourceItem GetDataSourceItem() + { + throw new System.NotImplementedException(); + } } } } diff --git a/src/Reveal.Sdk.Dom/RdashDocument.cs b/src/Reveal.Sdk.Dom/RdashDocument.cs index 472e921..7e74a76 100644 --- a/src/Reveal.Sdk.Dom/RdashDocument.cs +++ b/src/Reveal.Sdk.Dom/RdashDocument.cs @@ -1,197 +1,195 @@ -using Reveal.Sdk.Dom.Data; -using Reveal.Sdk.Dom.Core.Serialization; -using Reveal.Sdk.Dom.Filters; -using Reveal.Sdk.Dom.Visualizations; -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Reveal.Sdk.Dom.Core.Constants; -using Reveal.Sdk.Dom.Variables; -using Newtonsoft.Json.Converters; -using System; -using Reveal.Sdk.Dom.Core; -using System.IO; -using Reveal.Sdk.Dom.Core.Utilities; - -namespace Reveal.Sdk.Dom -{ - public sealed class RdashDocument - { - private VisualizationCollection _visualizations; - - /// - /// Creates a new instance of an . - /// - public RdashDocument() : this("New Dashboard") { } - - /// - /// Creates a new instance of an . - /// - /// The title of the dashboard. - public RdashDocument(string title) - { - Title = title; - _visualizations = new VisualizationCollection(this); - } - - /// - /// Gets or sets the title of the dashboard. - /// - public string Title { get; set; } - - /// - /// Gets or sets the description of the dashboard. - /// - public string Description { get; set; } - - /// - /// Gets or sets the name of the theme the dashboard will apply to all visualizations. - /// - [JsonProperty("ThemeName")] - [JsonConverter(typeof(StringEnumConverter))] - public Theme Theme { get; set; } = Theme.Mountain; - - /// - /// Gets the name of the API that created the .rdash file. - /// - [JsonProperty] - public string CreatedWith { get; private set; } = GlobalConstants.RdashDocument.CreatedWith; - - /// - /// Gets the name of the API that last saved the .rdash file. - /// - [JsonProperty] - public string SavedWith { get; internal set; } = string.Empty; - - [JsonProperty] - internal int FormatVersion { get; set; } = 6; - - /// - /// Gets or sets whether the viewer displaying the dashboard will automatically layout visualizations, or use an absolute layout controlled by each visualization's ColumnSpan and RowSpan properties. True by default. - /// - public bool UseAutoLayout { get; set; } = true; - - [JsonProperty] - internal string Tags { get; set; } - - /// - /// Gets the collection of data sources available for creating visualizations. - /// - [JsonProperty] - public List DataSources { get; internal set; } = new List(); - - /// - /// Gets the collection of dashboard filters that can bound to any visualization using the visualization's FilterBindings property. - /// - [JsonProperty("GlobalFilters")] - public List Filters { get; internal set; } = new List(); - - /// - /// TODO: implement - /// - [JsonProperty] - internal List GlobalVariables { get; set; } = new List(); - - /// - /// Gets the collection of visualizations that are displayed in the dashboard. - /// - [JsonProperty("Widgets")] - public VisualizationCollection Visualizations - { - get => _visualizations; - internal set - { - _visualizations = value; - _visualizations.Parent = this; - } - } - - /// - /// Import all visualizations from another document. - /// - /// The containing the visualizations to import. - /// Thrown if the document is null - public void Import(RdashDocument document) - { - if (document == null) - throw new ArgumentNullException(); - - //todo: think about dashboard filters. should we bring them over? maybe some import options to control it? - //let's wait for feedback from customers - //for now let's just clear any visualization filters that may be binding to a dashboard filter - foreach (var viz in document.Visualizations) - { - if (viz is IFilterBindings bindings) - bindings.FilterBindings.Clear(); - } - - DataSources.AddRange(document.DataSources); //add all data sources, unused data sources will be removed later during serialization - - Visualizations.AddRange(document.Visualizations); - } - - /// - /// Import a visualization from another document. - /// - /// The containing the visualization to import. - /// The ID of the visualization to import. - /// Thrown if the document is null - /// Thrown if the visualizationId is null or empty. - public void Import(RdashDocument document, string visualizationId) - { - if (document == null) - throw new ArgumentNullException(); - - if (string.IsNullOrEmpty(visualizationId)) - throw new ArgumentException("Value cannot be null or empty.", nameof(visualizationId)); - - var visualization = document.Visualizations.Find(v => v.Id == visualizationId); - Import(document, visualization); - } - - /// - /// Import a visualization from another document. - /// - /// The containing the visualization to import. - /// The visualization. - /// Thrown if the document or visualization is null. - public void Import(RdashDocument document, IVisualization visualization) - { - if (document == null) - throw new ArgumentNullException(nameof(document)); - - if (visualization == null) - throw new ArgumentNullException(nameof(visualization)); - - //todo: think about dashboard filters. should we bring them over? maybe some import options to control it? - //let's wait for feedback from customers - //for now let's just clear any visualization filters that may be binding to a dashboard filter - if (visualization is IFilterBindings bindings) - bindings.FilterBindings.Clear(); - - DataSources.AddRange(document.DataSources); //add all data sources, unused data sources will be removed later during serialization - - Visualizations.Add(visualization); - } - - /// - /// Creates an from a .rdash file. - /// - /// The file path to the dashboard file (.rdash). - /// The representing the contents of the loaded .rdash file. - public static RdashDocument Load(string filePath) - { - return RdashSerializer.Load(filePath); - } - - /// - /// Creates an from a .rdash file stream. - /// - /// The dashboard file stream - /// - public static RdashDocument Load(Stream stream) - { - return RdashSerializer.Load(stream); +using Reveal.Sdk.Dom.Data; +using Reveal.Sdk.Dom.Core.Serialization; +using Reveal.Sdk.Dom.Filters; +using Reveal.Sdk.Dom.Visualizations; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Reveal.Sdk.Dom.Core.Constants; +using Reveal.Sdk.Dom.Variables; +using Newtonsoft.Json.Converters; +using System; +using Reveal.Sdk.Dom.Core; +using System.IO; +using Reveal.Sdk.Dom.Core.Utilities; + +namespace Reveal.Sdk.Dom +{ + public sealed class RdashDocument + { + private VisualizationCollection _visualizations; + + /// + /// Creates a new instance of an . + /// + public RdashDocument() : this("New Dashboard") { } + + /// + /// Creates a new instance of an . + /// + /// The title of the dashboard. + public RdashDocument(string title) + { + Title = title; + _visualizations = new VisualizationCollection(this); + } + + /// + /// Gets or sets the title of the dashboard. + /// + public string Title { get; set; } + + /// + /// Gets or sets the description of the dashboard. + /// + public string Description { get; set; } + + /// + /// Gets or sets the name of the theme the dashboard will apply to all visualizations. + /// + [JsonProperty("ThemeName")] + [JsonConverter(typeof(StringEnumConverter))] + public Theme Theme { get; set; } = Theme.Mountain; + + /// + /// Gets the name of the API that created the .rdash file. + /// + [JsonProperty] + public string CreatedWith { get; private set; } = GlobalConstants.RdashDocument.CreatedWith; + + /// + /// Gets the name of the API that last saved the .rdash file. + /// + [JsonProperty] + public string SavedWith { get; internal set; } = string.Empty; + + [JsonProperty] + internal int FormatVersion { get; set; } = 6; + + /// + /// Gets or sets whether the viewer displaying the dashboard will automatically layout visualizations, or use an absolute layout controlled by each visualization's ColumnSpan and RowSpan properties. True by default. + /// + public bool UseAutoLayout { get; set; } = true; + + [JsonProperty] + internal string Tags { get; set; } + + /// + /// Gets the collection of data sources available for creating visualizations. + /// + [JsonProperty] + public List DataSources { get; internal set; } = new List(); + + /// + /// Gets the collection of dashboard filters that can bound to any visualization using the visualization's FilterBindings property. + /// + [JsonProperty("GlobalFilters")] + public List Filters { get; internal set; } = new List(); + + /// + /// TODO: implement + /// + [JsonProperty] + internal List GlobalVariables { get; set; } = new List(); + + /// + /// Gets the collection of visualizations that are displayed in the dashboard. + /// + [JsonProperty("Widgets")] + public VisualizationCollection Visualizations + { + get => _visualizations; + internal set + { + _visualizations = value; + _visualizations.Parent = this; + } + } + + /// + /// Import all visualizations from another document. + /// + /// The containing the visualizations to import. + /// Thrown if the document is null + public void Import(RdashDocument document) + { + if (document == null) + throw new ArgumentNullException(); + + //todo: think about dashboard filters. should we bring them over? maybe some import options to control it? + //let's wait for feedback from customers + //for now let's just clear any visualization filters that may be binding to a dashboard filter + foreach (var viz in document.Visualizations) + { + viz.FilterBindings.Clear(); + } + + DataSources.AddRange(document.DataSources); //add all data sources, unused data sources will be removed later during serialization + + Visualizations.AddRange(document.Visualizations); + } + + /// + /// Import a visualization from another document. + /// + /// The containing the visualization to import. + /// The ID of the visualization to import. + /// Thrown if the document is null + /// Thrown if the visualizationId is null or empty. + public void Import(RdashDocument document, string visualizationId) + { + if (document == null) + throw new ArgumentNullException(); + + if (string.IsNullOrEmpty(visualizationId)) + throw new ArgumentException("Value cannot be null or empty.", nameof(visualizationId)); + + var visualization = document.Visualizations.Find(v => v.Id == visualizationId); + Import(document, visualization); + } + + /// + /// Import a visualization from another document. + /// + /// The containing the visualization to import. + /// The visualization. + /// Thrown if the document or visualization is null. + public void Import(RdashDocument document, IVisualization visualization) + { + if (document == null) + throw new ArgumentNullException(nameof(document)); + + if (visualization == null) + throw new ArgumentNullException(nameof(visualization)); + + //todo: think about dashboard filters. should we bring them over? maybe some import options to control it? + //let's wait for feedback from customers + //for now let's just clear any visualization filters that may be binding to a dashboard filter + visualization.FilterBindings.Clear(); + + DataSources.AddRange(document.DataSources); //add all data sources, unused data sources will be removed later during serialization + + Visualizations.Add(visualization); + } + + /// + /// Creates an from a .rdash file. + /// + /// The file path to the dashboard file (.rdash). + /// The representing the contents of the loaded .rdash file. + public static RdashDocument Load(string filePath) + { + return RdashSerializer.Load(filePath); + } + + /// + /// Creates an from a .rdash file stream. + /// + /// The dashboard file stream + /// + public static RdashDocument Load(Stream stream) + { + return RdashSerializer.Load(stream); } /// @@ -199,35 +197,35 @@ public static RdashDocument Load(Stream stream) /// /// A JSON string representing the .rdash file. /// - public static RdashDocument LoadFromJson(string json) - { - return RdashSerializer.Deserialize(json); - } - - /// - /// Saves the as a .rdash file. - /// - /// The file path to save the (must include the .rdash extensions). - public void Save(string filePath) - { - RdashSerializer.Save(this, filePath); - } - - /// - /// Converts the to a JSON string. - /// - /// A JSON string representing the - public string ToJsonString() - { - return RdashSerializer.SerializeDocument(this); - } - + public static RdashDocument LoadFromJson(string json) + { + return RdashSerializer.Deserialize(json); + } + + /// + /// Saves the as a .rdash file. + /// + /// The file path to save the (must include the .rdash extensions). + public void Save(string filePath) + { + RdashSerializer.Save(this, filePath); + } + + /// + /// Converts the to a JSON string. + /// + /// A JSON string representing the + public string ToJsonString() + { + return RdashSerializer.SerializeDocument(this); + } + /// /// Validates the to ensure it's in a valid state for serialization. - /// - public void Validate() + /// + public void Validate() { RdashDocumentValidator.Validate(this); - } - } -} + } + } +} diff --git a/src/Reveal.Sdk.Dom/Visualizations/Extensions/IFilterBindingsExtensions.cs b/src/Reveal.Sdk.Dom/Visualizations/Extensions/IFilterBindingsExtensions.cs deleted file mode 100644 index d28432b..0000000 --- a/src/Reveal.Sdk.Dom/Visualizations/Extensions/IFilterBindingsExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Reveal.Sdk.Dom.Filters; - -namespace Reveal.Sdk.Dom.Visualizations -{ - public static class IFilterBindingsExtensions - { - public static T ConnectDashboardFilter(this T visualization, DashboardFilter dashboardFilter) - where T : IFilterBindings - { - return visualization.ConnectDashboardFilter(dashboardFilter, null); - } - - public static T ConnectDashboardFilter(this T visualization, DashboardFilter dashboardFilter, string fieldName) - where T : IFilterBindings - { - if (dashboardFilter is DashboardDateFilter) - { - visualization.FilterBindings.Add(new DashboardDateFilterBinding(fieldName ?? "Date")); - } - else if (dashboardFilter is DashboardDataFilter dataFilter) - { - var binding = fieldName == null ? new DashboardDataFilterBinding(dataFilter) : new DashboardDataFilterBinding(dataFilter, fieldName); - visualization.FilterBindings.Add(binding); - } - return visualization; - } - - public static T ConnectDashboardFilters(this T visualization, params DashboardFilter[] dashboardFilter) - where T : IFilterBindings - { - foreach (var filter in dashboardFilter) - { - visualization.ConnectDashboardFilter(filter); - } - return visualization; - } - } -} diff --git a/src/Reveal.Sdk.Dom/Visualizations/Extensions/IVisualizationExtensions.cs b/src/Reveal.Sdk.Dom/Visualizations/Extensions/IVisualizationExtensions.cs index ab766ec..6dd1f06 100644 --- a/src/Reveal.Sdk.Dom/Visualizations/Extensions/IVisualizationExtensions.cs +++ b/src/Reveal.Sdk.Dom/Visualizations/Extensions/IVisualizationExtensions.cs @@ -46,7 +46,7 @@ public static T AddFilter(this T visualization, string field) { tdd.QuickFilters.Add(new VisualizationFilter(field)); //visualization.Filters.Add(new VisualizationFilter(field)); //todo: can we expose a Filters property on IVisualization? Can the filters property apply to both Tabular and Xmla? - } + } return visualization; } @@ -59,5 +59,36 @@ public static T AddFilters(this T visualization, params string[] fields) } return visualization; } + + public static T ConnectDashboardFilter(this T visualization, DashboardFilter dashboardFilter) + where T : IVisualization + { + return visualization.ConnectDashboardFilter(dashboardFilter, null); + } + + public static T ConnectDashboardFilter(this T visualization, DashboardFilter dashboardFilter, string fieldName) + where T : IVisualization + { + if (dashboardFilter is DashboardDateFilter) + { + visualization.FilterBindings.Add(new DashboardDateFilterBinding(fieldName ?? "Date")); + } + else if (dashboardFilter is DashboardDataFilter dataFilter) + { + var binding = fieldName == null ? new DashboardDataFilterBinding(dataFilter) : new DashboardDataFilterBinding(dataFilter, fieldName); + visualization.FilterBindings.Add(binding); + } + return visualization; + } + + public static T ConnectDashboardFilters(this T visualization, params DashboardFilter[] dashboardFilter) + where T : IVisualization + { + foreach (var filter in dashboardFilter) + { + visualization.ConnectDashboardFilter(filter); + } + return visualization; + } } } diff --git a/src/Reveal.Sdk.Dom/Visualizations/Interfaces/IFilterBindings.cs b/src/Reveal.Sdk.Dom/Visualizations/Interfaces/IFilterBindings.cs deleted file mode 100644 index 5e585f2..0000000 --- a/src/Reveal.Sdk.Dom/Visualizations/Interfaces/IFilterBindings.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Reveal.Sdk.Dom.Filters; -using System.Collections.Generic; - -namespace Reveal.Sdk.Dom.Visualizations -{ - public interface IFilterBindings - { - List FilterBindings { get; } - } -} \ No newline at end of file diff --git a/src/Reveal.Sdk.Dom/Visualizations/Interfaces/IVisualization.cs b/src/Reveal.Sdk.Dom/Visualizations/Interfaces/IVisualization.cs index 1f1522e..e943369 100644 --- a/src/Reveal.Sdk.Dom/Visualizations/Interfaces/IVisualization.cs +++ b/src/Reveal.Sdk.Dom/Visualizations/Interfaces/IVisualization.cs @@ -1,5 +1,8 @@ using Newtonsoft.Json; using Reveal.Sdk.Dom.Core.Serialization.Converters; +using Reveal.Sdk.Dom.Data; +using Reveal.Sdk.Dom.Filters; +using System.Collections.Generic; namespace Reveal.Sdk.Dom.Visualizations { @@ -14,5 +17,7 @@ public interface IVisualization int RowSpan { get; set; } string Description { get; set; } IDataDefinition DataDefinition { get; } + List FilterBindings { get; } + DataSourceItem GetDataSourceItem(); } } \ No newline at end of file diff --git a/src/Reveal.Sdk.Dom/Visualizations/Visualization.cs b/src/Reveal.Sdk.Dom/Visualizations/Visualization.cs index 004d64d..8c4241b 100644 --- a/src/Reveal.Sdk.Dom/Visualizations/Visualization.cs +++ b/src/Reveal.Sdk.Dom/Visualizations/Visualization.cs @@ -10,30 +10,17 @@ namespace Reveal.Sdk.Dom.Visualizations { - public abstract class Visualization : Visualization, ISettingsProvider, IFilterBindings + public abstract class Visualization : Visualization, ISettingsProvider where TSettings : VisualizationSettings, new() { protected Visualization(string title, DataSourceItem dataSourceItem) : base(title, dataSourceItem) { } - [JsonIgnore] - public List FilterBindings - { - get { return ((DataDefinitionBase)DataDefinition).Bindings.Bindings; } - } - [JsonProperty("ActionsModel", Order = 10)] public VisualizationLinker Linker { get; set; } [JsonProperty("VisualizationSettings", Order = 5)] public TSettings Settings { get; internal set; } = new TSettings(); - - //todo: is it possible to create a Filters property that can properly handle both Tabular and Xmla data specs? - //[JsonIgnore] - //public List Filters - //{ - // get { return DataDefinition.QuickFilters; } //this works for tabluar - //} } public abstract class Visualization : IVisualization, IParentDocument @@ -73,6 +60,19 @@ protected Visualization(string title, DataSourceItem dataSourceItem) /// public string Description { get; set; } + [JsonIgnore] + public List FilterBindings + { + get { return ((DataDefinitionBase)DataDefinition).Bindings.Bindings; } + } + + //todo: is it possible to create a Filters property that can properly handle both Tabular and Xmla data specs? + //[JsonIgnore] + //public List Filters + //{ + // get { return DataDefinition.QuickFilters; } //this works for tabluar + //} + /// /// Gets the data source item for the visualization. /// @@ -96,7 +96,7 @@ public DataSourceItem GetDataSourceItem() /// /// Updates the data source item and available fields for the visualization. /// - /// The created with a data source builder. + /// The . public void UpdateDataSourceItem(DataSourceItem dataSourceItem) { if (DataDefinition == null || dataSourceItem == null)