diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 8100286..d2dc8c7 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -35,7 +35,17 @@ const App = () => {
const [isManageMilestonesOpen, setIsManageMilestonesOpen] = useState(false);
const [isManageLabelsOpen, setIsManageLabelsOpen] = useState(false);
const [isManageSortOpen, setIsManageSortOpen] = useState(false);
- const [sortOrder, setSortOrder] = useState([]);
+
+ // Load sort order from localStorage on mount
+ const [sortOrder, setSortOrder] = useState(() => {
+ try {
+ const saved = localStorage.getItem('issueSortOrder');
+ return saved ? JSON.parse(saved) : [];
+ } catch (error) {
+ console.error('Failed to load sort order from localStorage:', error);
+ return [];
+ }
+ });
useEffect(() => {
fetchProject()
@@ -142,6 +152,15 @@ const App = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ // Save sort order to localStorage whenever it changes
+ useEffect(() => {
+ try {
+ localStorage.setItem('issueSortOrder', JSON.stringify(sortOrder));
+ } catch (error) {
+ console.error('Failed to save sort order to localStorage:', error);
+ }
+ }, [sortOrder]);
+
const loadMilestones = () => {
setLoading(true);
setError(null);
diff --git a/frontend/src/App.test.jsx b/frontend/src/App.test.jsx
index aa02253..3f004b3 100644
--- a/frontend/src/App.test.jsx
+++ b/frontend/src/App.test.jsx
@@ -10,8 +10,29 @@ import assigneesCache from './utils/assigneesCache';
vi.mock('./services/api');
describe('App', () => {
+ // Mock localStorage
+ let store = {};
+ const localStorageMock = {
+ getItem: vi.fn((key) => store[key] || null),
+ setItem: vi.fn((key, value) => {
+ store[key] = value.toString();
+ }),
+ removeItem: vi.fn((key) => {
+ delete store[key];
+ }),
+ clear: vi.fn(() => {
+ store = {};
+ }),
+ };
+
beforeEach(() => {
vi.clearAllMocks();
+ // Clear localStorage mock store
+ store = {};
+ Object.defineProperty(window, 'localStorage', {
+ value: localStorageMock,
+ writable: true,
+ });
clearMilestonesCache();
clearLabelsCache();
// Clear assignees cache
@@ -139,4 +160,59 @@ describe('App', () => {
expect(screen.getByText(/No milestones found/i)).toBeInTheDocument();
});
});
+
+ it('loads sort order from localStorage on mount', async () => {
+ const savedSortOrder = ['label1', 'label2', 'label3'];
+ localStorageMock.setItem('issueSortOrder', JSON.stringify(savedSortOrder));
+ api.fetchMilestones.mockResolvedValue([]);
+
+ await act(async () => {
+ render();
+ });
+
+ await waitFor(() => {
+ // Verify localStorage.getItem was called
+ expect(localStorageMock.getItem).toHaveBeenCalledWith('issueSortOrder');
+ });
+ });
+
+ it('saves sort order to localStorage when changed', async () => {
+ api.fetchMilestones.mockResolvedValue([]);
+ api.fetchLabels.mockResolvedValue([
+ { name: 'label1', color: 'ff0000', description: 'Label 1' },
+ { name: 'label2', color: '00ff00', description: 'Label 2' },
+ ]);
+
+ await act(async () => {
+ render();
+ });
+
+ await waitFor(() => {
+ expect(screen.getByText('Sort')).toBeInTheDocument();
+ });
+
+ // Open the Sort modal
+ const sortButton = screen.getByText('Sort');
+ await act(async () => {
+ sortButton.click();
+ });
+
+ await waitFor(() => {
+ expect(screen.getByText(/Sort Issues by Labels/i)).toBeInTheDocument();
+ });
+
+ // Add a label to the sort order
+ const addLabelButton = screen.getByText('+ label1');
+ await act(async () => {
+ addLabelButton.click();
+ });
+
+ // Verify localStorage.setItem was called with the new sort order
+ await waitFor(() => {
+ expect(localStorageMock.setItem).toHaveBeenCalledWith(
+ 'issueSortOrder',
+ JSON.stringify(['label1'])
+ );
+ });
+ });
});