Repository: Evertras/bubble-table
Pull request #67 (2022-04-30 01:51:43)

Add some basic queries for sorting and filtering
Helps #60 

Adds some non-mutable queries for current state of sorting and filtering.


Patch:
diff --git a/table/model.go b/table/model.go
index 12f5dd8..6cfdd2c 100644
--- a/table/model.go
+++ b/table/model.go
@@ -42,8 +42,9 @@ type Model struct {
 	pageSize    int
 	currentPage int
 
-	// Sorting, first sortColum will be last sorted
-	sortOrder []sortColumn
+	// Sorting, where a stable sort is applied from first element to last so
+	// that elements are grouped by the later elements.
+	sortOrder []SortColumn
 
 	// Filter
 	filtered        bool
diff --git a/table/query.go b/table/query.go
new file mode 100644
index 0000000..06df583
--- /dev/null
+++ b/table/query.go
@@ -0,0 +1,32 @@
+package table
+
+// GetColumnSorting returns the current sorting rules for the table as a list of
+// SortColumns, which are applied from first to last.  This means that data will
+// be grouped by the later elements in the list.  The returned list is a copy
+// and modifications will have no effect.
+func (m *Model) GetColumnSorting() []SortColumn {
+	c := make([]SortColumn, len(m.sortOrder))
+
+	copy(c, m.sortOrder)
+
+	return c
+}
+
+// GetCanFilter returns true if the table enables filtering at all.  This does
+// not say whether a filter is currently active, only that the feature is enabled.
+func (m *Model) GetCanFilter() bool {
+	return m.filtered
+}
+
+// GetIsFilterActive returns true if the table is currently being filtered.  This
+// does not say whether the table CAN be filtered, only whether or not a filter
+// is actually currently being applied.
+func (m *Model) GetIsFilterActive() bool {
+	return m.filterTextInput.Value() != ""
+}
+
+// GetCurrentFilter returns the current filter text being applied, or an empty
+// string if none is applied.
+func (m *Model) GetCurrentFilter() string {
+	return m.filterTextInput.Value()
+}
diff --git a/table/sort.go b/table/sort.go
index f10855d..494e34d 100644
--- a/table/sort.go
+++ b/table/sort.go
@@ -5,16 +5,21 @@ import (
 	"sort"
 )
 
-type sortDirection int
+// SortDirection indicates whether a column should sort by ascending or descending.
+type SortDirection int
 
 const (
-	sortDirectionAsc sortDirection = iota
-	sortDirectionDesc
+	// SortDirectionAsc indicates the column should be in ascending order.
+	SortDirectionAsc SortDirection = iota
+
+	// SortDirectionDesc indicates the column should be in descending order.
+	SortDirectionDesc
 )
 
-type sortColumn struct {
-	columnKey string
-	direction sortDirection
+// SortColumn describes which column should be sorted and how.
+type SortColumn struct {
+	ColumnKey string
+	Direction SortDirection
 }
 
 // SortByAsc sets the main sorting column to the given key, in ascending order.
@@ -22,10 +27,10 @@ type sortColumn struct {
 // this function is called.  Values are sorted as numbers if possible, or just
 // as simple string comparisons if not numbers.
 func (m Model) SortByAsc(columnKey string) Model {
-	m.sortOrder = []sortColumn{
+	m.sortOrder = []SortColumn{
 		{
-			columnKey: columnKey,
-			direction: sortDirectionAsc,
+			ColumnKey: columnKey,
+			Direction: SortDirectionAsc,
 		},
 	}
 
@@ -37,10 +42,10 @@ func (m Model) SortByAsc(columnKey string) Model {
 // this function is called.  Values are sorted as numbers if possible, or just
 // as simple string comparisons if not numbers.
 func (m Model) SortByDesc(columnKey string) Model {
-	m.sortOrder = []sortColumn{
+	m.sortOrder = []SortColumn{
 		{
-			columnKey: columnKey,
-			direction: sortDirectionDesc,
+			ColumnKey: columnKey,
+			Direction: SortDirectionDesc,
 		},
 	}
 
@@ -50,10 +55,10 @@ func (m Model) SortByDesc(columnKey string) Model {
 // ThenSortByAsc provides a secondary sort after the first, in ascending order.
 // Can be chained multiple times, applying to smaller subgroups each time.
 func (m Model) ThenSortByAsc(columnKey string) Model {
-	m.sortOrder = append([]sortColumn{
+	m.sortOrder = append([]SortColumn{
 		{
-			columnKey: columnKey,
-			direction: sortDirectionAsc,
+			ColumnKey: columnKey,
+			Direction: SortDirectionAsc,
 		},
 	}, m.sortOrder...)
 
@@ -63,10 +68,10 @@ func (m Model) ThenSortByAsc(columnKey string) Model {
 // ThenSortByDesc provides a secondary sort after the first, in descending order.
 // Can be chained multiple times, applying to smaller subgroups each time.
 func (m Model) ThenSortByDesc(columnKey string) Model {
-	m.sortOrder = append([]sortColumn{
+	m.sortOrder = append([]SortColumn{
 		{
-			columnKey: columnKey,
-			direction: sortDirectionDesc,
+			ColumnKey: columnKey,
+			Direction: SortDirectionDesc,
 		},
 	}, m.sortOrder...)
 
@@ -75,7 +80,7 @@ func (m Model) ThenSortByDesc(columnKey string) Model {
 
 type sortableTable struct {
 	rows     []Row
-	byColumn sortColumn
+	byColumn SortColumn
 }
 
 func (s *sortableTable) Len() int {
@@ -118,28 +123,28 @@ func (s *sortableTable) extractNumber(i int, column string) (float64, bool) {
 }
 
 func (s *sortableTable) Less(first, second int) bool {
-	firstNum, firstNumIsValid := s.extractNumber(first, s.byColumn.columnKey)
-	secondNum, secondNumIsValid := s.extractNumber(second, s.byColumn.columnKey)
+	firstNum, firstNumIsValid := s.extractNumber(first, s.byColumn.ColumnKey)
+	secondNum, secondNumIsValid := s.extractNumber(second, s.byColumn.ColumnKey)
 
 	if firstNumIsValid && secondNumIsValid {
-		if s.byColumn.direction == sortDirectionAsc {
+		if s.byColumn.Direction == SortDirectionAsc {
 			return firstNum < secondNum
 		}
 
 		return firstNum > secondNum
 	}
 
-	firstVal := s.extractString(first, s.byColumn.columnKey)
-	secondVal := s.extractString(second, s.byColumn.columnKey)
+	firstVal := s.extractString(first, s.byColumn.ColumnKey)
+	secondVal := s.extractString(second, s.byColumn.ColumnKey)
 
-	if s.byColumn.direction == sortDirectionAsc {
+	if s.byColumn.Direction == SortDirectionAsc {
 		return firstVal < secondVal
 	}
 
 	return firstVal > secondVal
 }
 
-func getSortedRows(sortOrder []sortColumn, rows []Row) []Row {
+func getSortedRows(sortOrder []SortColumn, rows []Row) []Row {
 	var sortedRows []Row
 	if len(sortOrder) == 0 {
 		sortedRows = rows


Test patch:
diff --git a/table/query_test.go b/table/query_test.go
new file mode 100644
index 0000000..30f2cac
--- /dev/null
+++ b/table/query_test.go
@@ -0,0 +1,52 @@
+package table
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetColumnSorting(t *testing.T) {
+	cols := []Column{
+		NewColumn("a", "a", 3),
+		NewColumn("b", "b", 3),
+		NewColumn("c", "c", 3),
+	}
+
+	model := New(cols).SortByAsc("b")
+
+	sorted := model.GetColumnSorting()
+
+	assert.Len(t, sorted, 1, "Should only have one column")
+	assert.Equal(t, sorted[0].ColumnKey, "b", "Should sort column b")
+	assert.Equal(t, sorted[0].Direction, SortDirectionAsc, "Should be ascending")
+
+	sorted[0].Direction = SortDirectionDesc
+
+	assert.NotEqual(
+		t,
+		model.sortOrder[0].Direction,
+		sorted[0].Direction,
+		"Should not have been able to modify actual values",
+	)
+}
+
+func TestGetFilterData(t *testing.T) {
+	model := New([]Column{})
+
+	assert.False(t, model.GetIsFilterActive(), "Should not start with filter active")
+	assert.False(t, model.GetCanFilter(), "Should not start with filter ability")
+	assert.Equal(t, model.GetCurrentFilter(), "", "Filter string should be empty")
+
+	model = model.Filtered(true)
+
+	assert.False(t, model.GetIsFilterActive(), "Should not be filtered just because the ability was activated")
+	assert.True(t, model.GetCanFilter(), "Filter feature should be enabled")
+	assert.Equal(t, model.GetCurrentFilter(), "", "Filter string should be empty")
+
+	model.filterTextInput.SetValue("a")
+
+	assert.True(t, model.GetIsFilterActive(), "Typing anything into box should mark as filtered")
+	assert.True(t, model.GetCanFilter(), "Filter feature should be enabled")
+	assert.Equal(t, model.GetCurrentFilter(), "a", "Filter string should be what was typed")
+}
diff --git a/table/sort_test.go b/table/sort_test.go
index 27d65a9..257a099 100644
--- a/table/sort_test.go
+++ b/table/sort_test.go
@@ -141,14 +141,14 @@ func TestSortTwoColumnsAscDescMix(t *testing.T) {
 }
 
 func TestGetSortedRows(t *testing.T) {
-	sortColumns := []sortColumn{
+	sortColumns := []SortColumn{
 		{
-			columnKey: "cb",
-			direction: 1,
+			ColumnKey: "cb",
+			Direction: SortDirectionDesc,
 		},
 		{
-			columnKey: "ca",
-			direction: 0,
+			ColumnKey: "ca",
+			Direction: SortDirectionAsc,
 		},
 	}
 	rows := getSortedRows(sortColumns, []Row{