-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from vaskoz/infinitescroll
Infinitescroll
- Loading branch information
Showing
3 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Infinite Scroll List | ||
|
||
The problem is to design an infinite scroll list like you'd see in a | ||
webpage. | ||
|
||
The implementation should know how many total sites are possible for a | ||
list. Then *AddSites* is called to add more to your current list. Your | ||
list should be able to answer questions about sites not yet loaded too. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package infinitescroll | ||
|
||
// Checkbox represents the state of the select all checkbox | ||
type Checkbox int | ||
|
||
const ( | ||
// Checked is when the select all box is selected. | ||
Checked Checkbox = iota | ||
// Unchecked is when the select all box is deselected. | ||
Unchecked | ||
// Indeterminate is when the select all box is greyed out. | ||
Indeterminate | ||
) | ||
|
||
type table struct { | ||
currentState Checkbox | ||
sites map[string]bool | ||
totalCount, clickCount uint64 | ||
} | ||
|
||
// Table represents an infinite scroll list in a webpage. | ||
type Table interface { | ||
AddSites(sites []string) | ||
ToggleSelectAll() | ||
CurrentState() Checkbox | ||
IsClicked(site string) bool | ||
Click(site string) | ||
} | ||
|
||
// NewTable creates a new instance of an Table | ||
func NewTable(totalCount uint64) Table { | ||
return &table{sites: make(map[string]bool), | ||
currentState: Unchecked, totalCount: totalCount} | ||
} | ||
|
||
func (ist *table) AddSites(sites []string) { | ||
click := ist.CurrentState() == Checked | ||
for _, site := range sites { | ||
ist.sites[site] = click | ||
} | ||
if click { | ||
ist.clickCount += uint64(len(sites)) | ||
} | ||
} | ||
|
||
func (ist *table) ToggleSelectAll() { | ||
if ist.currentState == Checked { | ||
ist.currentState = Unchecked | ||
toggleAll(ist.sites, false) | ||
ist.clickCount = 0 | ||
} else { | ||
ist.currentState = Checked | ||
toggleAll(ist.sites, true) | ||
ist.clickCount = uint64(len(ist.sites)) | ||
} | ||
} | ||
|
||
func toggleAll(sites map[string]bool, click bool) { | ||
for site := range sites { | ||
sites[site] = click | ||
} | ||
} | ||
|
||
func (ist *table) CurrentState() Checkbox { return ist.currentState } | ||
|
||
func (ist *table) IsClicked(site string) bool { | ||
if ist.currentState == Checked { | ||
return true | ||
} else if ist.currentState == Unchecked { | ||
return false | ||
} | ||
return ist.sites[site] | ||
} | ||
|
||
func (ist *table) Click(site string) { | ||
if state := ist.CurrentState(); state == Unchecked || state == Checked { | ||
ist.currentState = Indeterminate | ||
} | ||
clicked := ist.sites[site] | ||
ist.sites[site] = !clicked | ||
if clicked { | ||
ist.clickCount-- | ||
} else { | ||
ist.clickCount++ | ||
} | ||
if ist.clickCount == 0 { | ||
ist.currentState = Unchecked | ||
} | ||
if ist.clickCount == ist.totalCount { | ||
ist.currentState = Checked | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package infinitescroll | ||
|
||
import "testing" | ||
|
||
func TestClickThenUnclick(t *testing.T) { | ||
t.Parallel() | ||
table := NewTable(3000) | ||
table.AddSites([]string{"google", "yahoo", "microsoft", "amazon"}) | ||
table.Click("amazon") | ||
if result := table.CurrentState(); result != Indeterminate { | ||
t.Errorf("Expected Indeterminate but got %v", result) | ||
} | ||
table.Click("amazon") | ||
if result := table.CurrentState(); result != Unchecked { | ||
t.Errorf("Expected Unchecked but got %v", result) | ||
} | ||
} | ||
|
||
func TestIsClickForSeenAndUnseen(t *testing.T) { | ||
t.Parallel() | ||
table := NewTable(5) | ||
table.AddSites([]string{"google", "yahoo", "microsoft", "amazon"}) | ||
if clicked := table.IsClicked("unseen"); clicked { | ||
t.Errorf("Should be unclicked") | ||
} | ||
if selectAll := table.CurrentState(); selectAll != Unchecked { | ||
t.Errorf("Table should be unchecked for all here") | ||
} | ||
table.Click("amazon") | ||
if selectAll := table.CurrentState(); selectAll != Indeterminate { | ||
t.Errorf("Table should be indeterminate because 1 site is clicked") | ||
} | ||
if clicked := table.IsClicked("unseen"); clicked { | ||
t.Errorf("Should be unclicked") | ||
} | ||
} | ||
|
||
func TestToggleAll(t *testing.T) { | ||
t.Parallel() | ||
table := NewTable(3) | ||
table.AddSites([]string{"google", "yahoo"}) | ||
table.ToggleSelectAll() | ||
if clicked := table.IsClicked("google"); !clicked { | ||
t.Errorf("Google should be clicked because of the toggle select all") | ||
} | ||
if clicked := table.IsClicked("yahoo"); !clicked { | ||
t.Errorf("Yahoo should be clicked because of the toggle select all") | ||
} | ||
if clicked := table.IsClicked("amazon"); !clicked { | ||
t.Errorf("Unseen Amazon should be clicked because of the toggle select all") | ||
} | ||
table.AddSites([]string{"amazon"}) | ||
table.ToggleSelectAll() | ||
if clicked := table.IsClicked("google"); clicked { | ||
t.Errorf("Google should be unclicked because of the toggle select all") | ||
} | ||
if clicked := table.IsClicked("yahoo"); clicked { | ||
t.Errorf("Yahoo should be unclicked because of the toggle select all") | ||
} | ||
if clicked := table.IsClicked("amazon"); clicked { | ||
t.Errorf("Amazon should be unclicked because of the toggle select all") | ||
} | ||
} | ||
|
||
func TestToggleAllWithClicks(t *testing.T) { | ||
t.Parallel() | ||
table := NewTable(3) | ||
table.ToggleSelectAll() | ||
table.AddSites([]string{"google", "yahoo"}) | ||
if state := table.CurrentState(); state != Checked { | ||
t.Errorf("Table should be checked for select all") | ||
} | ||
table.Click("google") | ||
if state := table.CurrentState(); state != Indeterminate { | ||
t.Errorf("Table should be indeterminate after a deselect click") | ||
} | ||
table.ToggleSelectAll() | ||
if state := table.CurrentState(); state != Checked { | ||
t.Errorf("Table should be select all") | ||
} | ||
} | ||
|
||
func TestManuallySelectTotalSites(t *testing.T) { | ||
t.Parallel() | ||
table := NewTable(3) | ||
table.AddSites([]string{"google", "yahoo", "amazon"}) | ||
if state := table.CurrentState(); state != Unchecked { | ||
t.Errorf("Table should be unchecked") | ||
} | ||
table.Click("google") | ||
table.Click("yahoo") | ||
if state := table.CurrentState(); state != Indeterminate { | ||
t.Errorf("Table should be indeterminate after two clicks") | ||
} | ||
table.Click("amazon") | ||
if state := table.CurrentState(); state != Checked { | ||
t.Errorf("Everything manually checked up to the total size") | ||
} | ||
} |