Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ basic chart package Uncharted and adapts Uncharted charts for display as compone
***Charts Included***:
- The Bar Chart
- The Column Chart
- The Doughnut Chart
- The Donut Chart
- The Line Chart
- The Pie Chart
- The Progress Chart
Expand Down
5 changes: 3 additions & 2 deletions uncharted/lib/uncharted.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ defmodule Uncharted do
@type color_name :: atom()

@typedoc """
a struct representing the dataset that the chart uses. Dfferent charts needs similar but differing information so this
is a union type
a struct representing the dataset that the chart uses. Dfferent charts need similar but differing
information so this is a union type
"""
@type dataset ::
Uncharted.BarChart.Dataset.t()
| Uncharted.ColumnChart.Dataset.t()
| Uncharted.DonutChart.Dataset.t()
| Uncharted.LineChart.Dataset.t()
| Uncharted.PieChart.Dataset.t()
| Uncharted.ProgressChart.Dataset.t()
Expand Down
26 changes: 26 additions & 0 deletions uncharted/lib/uncharted/dataset.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defprotocol Uncharted.Dataset do
@doc """
Takes an `Uncharted.dataset` data structure
and returns a label for the data names. This label
will be used as the first header of the data table
provided for accessibilty.
"""
@fallback_to_any true
@spec data_name_label(Uncharted.dataset()) :: String.t()
def data_name_label(dataset)

@doc """
Takes an `Uncharted.dataset` data structure
and returns a label for the data values. This label
will be used as the second header of the data table
provided for accessibilty.
"""
@fallback_to_any true
@spec data_value_label(Uncharted.dataset()) :: String.t()
def data_value_label(dataset)
end

defimpl Uncharted.Dataset, for: Any do
def data_name_label(_), do: "Data Names"
def data_value_label(_), do: "Data Values"
end
19 changes: 19 additions & 0 deletions uncharted/lib/uncharted/donut_chart/base_chart_impl.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defimpl Uncharted.DonutChart, for: Uncharted.BaseChart do
alias Uncharted.BaseChart
alias Uncharted.DonutChart.DonutSlice

def donut_slices(%BaseChart{dataset: nil}), do: []

def donut_slices(%BaseChart{dataset: %{data: []}}), do: []

def donut_slices(%BaseChart{dataset: %{data: data}}) do
data
|> Enum.map(fn datum ->
%DonutSlice{
label: datum.name,
percentage: hd(datum.values),
fill_color: datum.fill_color
}
end)
end
end
34 changes: 34 additions & 0 deletions uncharted/lib/uncharted/donut_chart/dataset.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule Uncharted.DonutChart.Dataset do
@moduledoc """
Struct representing a dataset for a basic donut chart.
"""
defstruct [
:data,
:data_name_label,
:data_value_label,
:center_value,
:center_value_fill_color,
:label,
:label_fill_color,
:secondary_label
]

@typep color :: atom()
@type t() :: %__MODULE__{
data: list(Uncharted.BaseDatum.t()),
data_name_label: String.t(),
data_value_label: String.t(),
center_value: number(),
center_value_fill_color: color(),
label: String.t() | nil,
label_fill_color: color(),
secondary_label: String.t() | nil
}
end

defimpl Uncharted.Dataset, for: Uncharted.DonutChart.Dataset do
alias Uncharted.DonutChart.Dataset

def data_name_label(%Dataset{data_name_label: data_name_label}), do: data_name_label
def data_value_label(%Dataset{data_value_label: data_value_label}), do: data_value_label
end
3 changes: 3 additions & 0 deletions uncharted/lib/uncharted/donut_chart/donut_chart.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defprotocol Uncharted.DonutChart do
def donut_slices(chart)
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Uncharted.DoughnutChart.DoughnutSlice do
defmodule Uncharted.DonutChart.DonutSlice do
@moduledoc """
A struct representing doughnut chart slice display properties.
A struct representing donut chart slice display properties.
"""

defstruct [:label, :percentage, :fill_color]
Expand Down
19 changes: 0 additions & 19 deletions uncharted/lib/uncharted/doughnut_chart/base_chart_impl.ex

This file was deleted.

23 changes: 0 additions & 23 deletions uncharted/lib/uncharted/doughnut_chart/dataset.ex

This file was deleted.

3 changes: 0 additions & 3 deletions uncharted/lib/uncharted/doughnut_chart/doughnut_chart.ex

This file was deleted.

4 changes: 2 additions & 2 deletions uncharted/lib/uncharted/progress_chart/dataset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule Uncharted.ProgressChart.Dataset do
:percentage_fill_color,
:label_fill_color,
progress_shape: :round,
doughnut_width: 5
donut_width: 5
]

@typep color :: atom()
Expand All @@ -27,6 +27,6 @@ defmodule Uncharted.ProgressChart.Dataset do
percentage_fill_color: color(),
label_fill_color: color(),
progress_shape: :round | :butt | :square,
doughnut_width: number()
donut_width: number()
}
end
21 changes: 10 additions & 11 deletions uncharted/test/uncharted/doughnut_chart_test.exs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule Uncharted.DoughnutChartTest do
defmodule Uncharted.DonutChartTest do
alias Uncharted.{
BaseChart,
BaseDatum,
DoughnutChart
DonutChart
}

use ExUnit.Case
Expand All @@ -14,30 +14,29 @@ defmodule Uncharted.DoughnutChartTest do
%BaseDatum{name: "Slice Four", values: [7.0]},
%BaseDatum{name: "Slice Five", values: [22.0]}
]
@dataset %DoughnutChart.Dataset{data: @data}
@dataset %DonutChart.Dataset{data: @data}
@chart %BaseChart{title: "title", dataset: @dataset}

describe "doughnut_slices/1" do
describe "donut_slices/1" do
test "returns the number of slices that make up the dataset" do
assert length(DoughnutChart.doughnut_slices(@chart)) == length(@data)
assert length(DonutChart.donut_slices(@chart)) == length(@data)
end

test "returns doughnut slice labels" do
doughnut_slices = Enum.map(DoughnutChart.doughnut_slices(@chart), & &1.label)
test "returns donut slice labels" do
donut_slices = Enum.map(DonutChart.donut_slices(@chart), & &1.label)
labels = Enum.map(@data, & &1.name)

assert doughnut_slices
assert donut_slices
|> Enum.zip(labels)
|> Enum.all?(fn {actual, expected} -> actual == expected end)
end

test "returns correct percentages for slices" do
doughnut_slice_percentages =
Enum.map(DoughnutChart.doughnut_slices(@chart), & &1.percentage)
donut_slice_percentages = Enum.map(DonutChart.donut_slices(@chart), & &1.percentage)

expected_percentages = [20.0, 24.0, 27.0, 7.0, 22.0]

assert doughnut_slice_percentages == expected_percentages
assert donut_slice_percentages == expected_percentages
end
end
end
16 changes: 9 additions & 7 deletions uncharted_phoenix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
A simple ***Elixir*** charting library that generates easy to customize charts for ***Phoenix*** and ***LiveView***.

## Features
- Easily generate pie charts, doughnut charts, column charts, bar charts, progress counters, and line charts
- Easily generate pie charts, donut charts, column charts, bar charts, progress counters, and line charts
- Generates responsive and accessible SVGs as LiveView components
- Provides advanced styling like gradients and rounded corners
- Smooth animations for updating live data to the UI
Expand Down Expand Up @@ -220,7 +220,7 @@ defp progress_chart(from: %BaseChart{} = chart) do
percentage_text_fill_color: :blue_gradient,
percentage_fill_color: :rose_gradient,
label_fill_color: :rose_gradient,
doughnut_width: 5,
donut_width: 5,
progress_shape: :round
}
}
Expand Down Expand Up @@ -278,12 +278,12 @@ scatter_plot = %BaseChart{
}
```

### The Doughnut Chart
![Doughnut Chart](assets/images/doughnut-chart.jpg "Doughnut Chart")
### The Donut Chart
![Donut Chart](assets/images/donut-chart.jpg "Donut Chart")

```elixir
doughnut_chart = %BaseChart{
title: "Best kind of doughnut",
donut_chart = %BaseChart{
title: "Best kind of donut",
colors: %{
rose_gradient: %Gradient{
start_color: "#642B73",
Expand All @@ -298,7 +298,7 @@ doughnut_chart = %BaseChart{
stop_color: "#FF1379"
}
},
dataset: %DoughnutChart.Dataset{
dataset: %DonutChart.Dataset{
data: [
%BaseDatum{
name: "Cake",
Expand All @@ -321,6 +321,8 @@ doughnut_chart = %BaseChart{
values: [17.0]
}
],
data_name_label: "Donut Type",
data_value_label: "Percentage",
center_value: 100,
center_value_fill_color: :blue_gradient,
label: "Donuts Tasted",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defimpl Uncharted.Component, for: Uncharted.BaseChart do
alias Uncharted.BaseChart
alias Uncharted.BarChart
alias Uncharted.ColumnChart
alias Uncharted.DoughnutChart
alias Uncharted.DonutChart
alias Uncharted.LineChart
alias Uncharted.PieChart
alias Uncharted.ProgressChart
Expand All @@ -12,7 +12,7 @@ defimpl Uncharted.Component, for: Uncharted.BaseChart do
case dataset do
%BarChart.Dataset{} -> UnchartedPhoenix.LiveBarComponent
%ColumnChart.Dataset{} -> UnchartedPhoenix.LiveColumnComponent
%DoughnutChart.Dataset{} -> UnchartedPhoenix.LiveDoughnutComponent
%DonutChart.Dataset{} -> UnchartedPhoenix.LiveDonutComponent
%LineChart.Dataset{} -> UnchartedPhoenix.LiveLineComponent
%PieChart.Dataset{} -> UnchartedPhoenix.LivePieComponent
%ProgressChart.Dataset{} -> UnchartedPhoenix.LiveProgressComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
<caption><%= Chart.title(@chart) %></caption>
<thead>
<tr>
<th scope="col">[Data Title]</th>
<th scope="col">[Data Title]</th>
<th scope="col"><%= Dataset.data_name_label(@chart.dataset) %></th>
<th scope="col"><%= Dataset.data_value_label(@chart.dataset) %></th>
</tr>
</thead>
<%= for bar <- @bars do %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
<caption><%= Chart.title(@chart) %></caption>
<thead>
<tr>
<th scope="col">[Data Title]</th>
<th scope="col">[Data Title]</th>
<th scope="col"><%= Dataset.data_name_label(@chart.dataset) %></th>
<th scope="col"><%= Dataset.data_value_label(@chart.dataset) %></th>
</tr>
</thead>
<%= for column <- @columns do %>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule UnchartedPhoenix.LiveDoughnutComponent do
defmodule UnchartedPhoenix.LiveDonutComponent do
@moduledoc """
Doughnut Chart Component
Donut Chart Component
"""

use Phoenix.LiveComponent
Expand All @@ -13,14 +13,14 @@ defmodule UnchartedPhoenix.LiveDoughnutComponent do
socket =
socket
|> assign(:chart, assigns.chart)
|> assign(:doughnut_slices, Uncharted.DoughnutChart.doughnut_slices(assigns.chart))
|> assign(:donut_slices, Uncharted.DonutChart.donut_slices(assigns.chart))
|> assign(:always_show_table, assigns.always_show_table)

{:ok, socket}
end

def render(assigns) do
Phoenix.View.render(UnchartedPhoenix.ComponentView, "live_doughnut.html", assigns)
Phoenix.View.render(UnchartedPhoenix.ComponentView, "live_donut.html", assigns)
end

def handle_event("show_table", _, socket) do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<figure data-testid="lc-live-doughnut-component" role="img" aria-label="Doughnut chart visualizing <%= Chart.title(@chart) %>" alt="Doughnut chart visualizing <%= Chart.title(@chart) %>" tabindex="0" phx-target="<%= @myself %>" phx-focus="show_table">
<svg id="<%= svg_id(@chart, "chart") %>" class="doughnut-chart" height="100%" width="100%" viewBox="0 0 20 24" overflow="visible">
<title id="doughnutChartTitle">Doughnut chart visualizing <%= Chart.title(@chart) %></title>
<figure data-testid="lc-live-donut-component" role="img" aria-label="Donut chart visualizing <%= Chart.title(@chart) %>" alt="Donut chart visualizing <%= Chart.title(@chart) %>" tabindex="0" phx-target="<%= @myself %>" phx-focus="show_table">
<svg id="<%= svg_id(@chart, "chart") %>" class="donut-chart" height="100%" width="100%" viewBox="0 0 20 24" overflow="visible">
<title id="donutChartTitle">Donut chart visualizing <%= Chart.title(@chart) %></title>

<svg id="<%= svg_id(@chart, "title") %>" class="chart-data" y="0" x="0" width="100%" height="90%">
<circle class="doughnut-hole" r="8" cx="10" cy="10" fill="transparent" />
<circle class="doughnut-ring" r="8" cx="10" cy="10" fill="transparent" stroke-width="3" />
<%= for %{remaining_percentage: remaining_percentage, fill_color: fill_color} <- svg_doughnut_slices(@doughnut_slices) do %>
<circle class="donut-hole" r="8" cx="10" cy="10" fill="transparent" />
<circle class="donut-ring" r="8" cx="10" cy="10" fill="transparent" stroke-width="3" />
<%= for %{remaining_percentage: remaining_percentage, fill_color: fill_color} <- svg_donut_slices(@donut_slices) do %>
<circle class="data-slice" r="8" cx="10" cy="10" fill="transparent"
style="transition: all 0.5s ease"
stroke="<%= color_to_fill(Chart.colors(@chart), fill_color) %>"
Expand All @@ -30,7 +30,7 @@
</svg>

<svg id="<%= svg_id(@chart, "key") %>" class="chart-key" height="10%" width="100%" y="90%" x="0" aria-hidden="true" overflow="visible">
<%= for %{label: label, fill_color: fill_color, label_width: label_width, label_position: label_position} <- svg_doughnut_slices(@doughnut_slices) do %>
<%= for %{label: label, fill_color: fill_color, label_width: label_width, label_position: label_position} <- svg_donut_slices(@donut_slices) do %>
<svg id="<%= label %>" class="chart-label" width="<%= label_width %>%" height="100%" y="0" x="<%= label_position %>%" overflow="visible">
<rect width="90%" x="5%" height=".5" rx=".3" ry=".3" fill="<%= color_to_fill(Chart.colors(@chart), fill_color) %>" />
<foreignObject x="0" y="25%" width="100%" height="100%" overflow="visible">
Expand All @@ -50,11 +50,11 @@
<caption><%= Chart.title(@chart) %></caption>
<thead>
<tr>
<th scope="col"><%= @chart.title %></th>
<th scope="col">Percentage</th>
<th scope="col"><%= Dataset.data_name_label(@chart.dataset) %></th>
<th scope="col"><%= Dataset.data_value_label(@chart.dataset) %></th>
</tr>
</thead>
<%= for slice <- svg_doughnut_slices(@doughnut_slices) do %>
<%= for slice <- svg_donut_slices(@donut_slices) do %>
<tr>
<th scope="row"><%= slice.label %></th>
<td><%= slice.percentage %></td>
Expand Down
Loading