Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some basic queries for sorting and filtering #67

Merged
merged 3 commits into from
Apr 30, 2022
Merged
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
5 changes: 3 additions & 2 deletions table/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions table/query.go
Original file line number Diff line number Diff line change
@@ -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()
}
52 changes: 52 additions & 0 deletions table/query_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
57 changes: 31 additions & 26 deletions table/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,32 @@ 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.
// If a previous sort was used, it is replaced by the given column each time
// 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,
},
}

Expand All @@ -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,
},
}

Expand All @@ -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...)

Expand All @@ -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...)

Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions table/sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down