Skip to content

Commit 896ebc4

Browse files
authored
Merge pull request #1846 from AtCoder-NoviSteps/#1845
🎨 Save active contest type (#1845)
2 parents 2017440 + 4b6b822 commit 896ebc4

File tree

3 files changed

+141
-3
lines changed

3 files changed

+141
-3
lines changed

src/lib/components/TaskTables/TaskTable.svelte

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
import TaskTableBodyCell from '$lib/components/TaskTables/TaskTableBodyCell.svelte';
1818
19+
import { activeContestTypeStore } from '$lib/stores/active_contest_type.svelte';
1920
import {
2021
contestTableProviders,
2122
type ContestTableProviders,
@@ -31,7 +32,7 @@
3132
let { taskResults, isLoggedIn }: Props = $props();
3233
3334
// Prepare contest table provider based on the active contest type.
34-
let activeContestType = $state<ContestTableProviders>('abcLatest20Rounds');
35+
let activeContestType = $derived(activeContestTypeStore.get());
3536
3637
let provider: ContestTableProvider = $derived(
3738
contestTableProviders[activeContestType as ContestTableProviders],
@@ -112,8 +113,10 @@
112113
<ButtonGroup class="m-4 contents-center">
113114
{#each Object.entries(contestTableProviders) as [type, config]}
114115
<Button
115-
onclick={() => (activeContestType = type as ContestTableProviders)}
116-
class={activeContestType === type ? 'active-button-class' : ''}
116+
onclick={() => activeContestTypeStore.set(type as ContestTableProviders)}
117+
class={activeContestTypeStore.isSame(type as ContestTableProviders)
118+
? 'active-button-class'
119+
: ''}
117120
aria-label={config.getMetadata().ariaLabel}
118121
>
119122
{config.getMetadata().buttonLabel}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { type ContestTableProviders } from '$lib/utils/contest_table_provider';
2+
3+
/**
4+
* Store that manages the active contest type selection.
5+
*
6+
* This class uses Svelte's state management to track which contest type
7+
* is currently active button. It provides methods to get, set, and
8+
* compare the active contest type.
9+
*
10+
* The store uses the ContestTableProviders type which represents
11+
* different contest table configurations or data providers,
12+
* with a default value of 'abcLatest20Rounds'.
13+
*/
14+
export class ActiveContestTypeStore {
15+
value = $state<ContestTableProviders>('abcLatest20Rounds');
16+
17+
/**
18+
* Creates an instance with the specified contest type.
19+
*
20+
* @param defaultContestType - The default contest type to initialize.
21+
* Defaults to 'abcLatest20Rounds'.
22+
*/
23+
constructor(defaultContestType: ContestTableProviders = 'abcLatest20Rounds') {
24+
this.value = defaultContestType;
25+
}
26+
27+
/**
28+
* Gets the current contest table providers.
29+
*
30+
* @returns The current value of contest table providers.
31+
*/
32+
get(): ContestTableProviders {
33+
return this.value;
34+
}
35+
36+
/**
37+
* Sets the current contest type to the specified value.
38+
*
39+
* @param newContestType - The contest type to set as the current value
40+
*/
41+
set(newContestType: ContestTableProviders): void {
42+
this.value = newContestType;
43+
}
44+
45+
/**
46+
* Validates if the current contest type matches the provided contest type.
47+
* @param contestType - The contest type to compare against
48+
* @returns `true` if the current contest type matches the provided contest type, `false` otherwise
49+
*/
50+
isSame(contestType: ContestTableProviders): boolean {
51+
return this.value === contestType;
52+
}
53+
54+
/**
55+
* Resets the active contest type to the default value.
56+
* Sets the internal value to 'abcLatest20Rounds'.
57+
*/
58+
reset(): void {
59+
this.value = 'abcLatest20Rounds';
60+
}
61+
}
62+
63+
export const activeContestTypeStore = new ActiveContestTypeStore();
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, test, expect, beforeEach } from 'vitest';
2+
3+
import type { ContestTableProviders } from '$lib/utils/contest_table_provider';
4+
import { ActiveContestTypeStore } from '$lib/stores/active_contest_type.svelte';
5+
6+
describe('ActiveContestTypeStore', () => {
7+
let store: ActiveContestTypeStore;
8+
9+
beforeEach(() => {
10+
store = new ActiveContestTypeStore();
11+
});
12+
13+
test('expects to initialize with default value', () => {
14+
expect(store.get()).toBe('abcLatest20Rounds');
15+
});
16+
17+
test('expects to initialize with provided value', () => {
18+
const customStore = new ActiveContestTypeStore('abc319Onwards' as ContestTableProviders);
19+
expect(customStore.get()).toBe('abc319Onwards');
20+
});
21+
22+
test('expects to return the current value when calling get()', () => {
23+
expect(store.get()).toBe('abcLatest20Rounds');
24+
25+
// Change the value and verify get() returns the new value
26+
store.set('abc319Onwards' as ContestTableProviders);
27+
expect(store.get()).toBe('abc319Onwards');
28+
});
29+
30+
test('expects to update the value when calling set()', () => {
31+
store.set('fromAbc212ToAbc318' as ContestTableProviders);
32+
expect(store.value).toBe('fromAbc212ToAbc318');
33+
expect(store.get()).toBe('fromAbc212ToAbc318');
34+
35+
store.set('abc319Onwards' as ContestTableProviders);
36+
expect(store.value).toBe('abc319Onwards');
37+
expect(store.get()).toBe('abc319Onwards');
38+
});
39+
40+
test('expects to correctly determine if contest type is the same with isSame()', () => {
41+
expect(store.isSame('abcLatest20Rounds' as ContestTableProviders)).toBe(true);
42+
expect(store.isSame('abc319Onwards' as ContestTableProviders)).toBe(false);
43+
expect(store.isSame('fromAbc212ToAbc318' as ContestTableProviders)).toBe(false);
44+
45+
store.set('abc319Onwards' as ContestTableProviders);
46+
expect(store.isSame('abc319Onwards' as ContestTableProviders)).toBe(true);
47+
expect(store.isSame('abcLatest20Rounds' as ContestTableProviders)).toBe(false);
48+
expect(store.isSame('fromAbc212ToAbc318' as ContestTableProviders)).toBe(false);
49+
50+
store.set('fromAbc212ToAbc318' as ContestTableProviders);
51+
expect(store.isSame('fromAbc212ToAbc318' as ContestTableProviders)).toBe(true);
52+
expect(store.isSame('abcLatest20Rounds' as ContestTableProviders)).toBe(false);
53+
expect(store.isSame('abc319Onwards' as ContestTableProviders)).toBe(false);
54+
});
55+
56+
test('expects to reset the value to default when calling reset()', () => {
57+
// First change the value to something else
58+
store.set('abc319Onwards' as ContestTableProviders);
59+
expect(store.get()).toBe('abc319Onwards');
60+
61+
// Call reset and verify it goes back to default
62+
store.reset();
63+
expect(store.get()).toBe('abcLatest20Rounds');
64+
65+
// Change to a different value and reset again to verify consistency
66+
store.set('fromAbc212ToAbc318' as ContestTableProviders);
67+
expect(store.get()).toBe('fromAbc212ToAbc318');
68+
69+
store.reset();
70+
expect(store.get()).toBe('abcLatest20Rounds');
71+
});
72+
});

0 commit comments

Comments
 (0)