Skip to content

Commit 50a6aa0

Browse files
authored
feat(staking): add default sorting for browse pools [LW-10104] (#985)
* feat(staking): add default sorting for browse pools * feat(staking): browse pools - add default sorting for hardware wallet legacy
1 parent d444b4e commit 50a6aa0

File tree

12 files changed

+184
-27
lines changed

12 files changed

+184
-27
lines changed

apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
StakePoolsListRowSkeleton,
1010
StakePoolSortOptions,
1111
stakePoolTableConfig,
12-
TranslationsFor
12+
TranslationsFor,
13+
getDefaultSortOrderByField,
14+
DEFAULT_SORT_OPTIONS
1315
} from '@lace/staking';
1416
import { Typography } from 'antd';
1517
import { Search } from '@lace/common';
@@ -31,11 +33,6 @@ type stakePoolsTableProps = {
3133

3234
type LoadMoreDataParam = Parameters<typeof Table.Body>[0]['loadMoreData'];
3335

34-
const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = {
35-
field: 'ticker',
36-
order: 'desc'
37-
};
38-
3936
const searchDebounce = 300;
4037

4138
export const StakePoolsTable = ({ scrollableTargetId }: stakePoolsTableProps): React.ReactElement => {
@@ -109,7 +106,8 @@ export const StakePoolsTable = ({ scrollableTargetId }: stakePoolsTableProps): R
109106
};
110107

111108
const onSortChange = (sortField: SortField) => {
112-
const order = sortField === sort?.field && sort?.order === 'asc' ? 'desc' : 'asc';
109+
const inverseOrder = sort?.order === 'asc' ? 'desc' : 'asc';
110+
const order = sortField !== sort?.field ? getDefaultSortOrderByField(sortField) : inverseOrder;
113111

114112
setSort({ field: sortField, order });
115113
};

packages/staking/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"@storybook/addon-links": "^7.6.7",
8282
"@storybook/blocks": "^7.6.7",
8383
"@storybook/jest": "^0.2.3",
84+
"@storybook/preview-api": "^8.0.4",
8485
"@storybook/react": "^7.6.7",
8586
"@storybook/react-vite": "^7.6.7",
8687
"@storybook/test": "^7.6.7",

packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import { Box, Cell, Flex, Grid, LocalThemeProvider, Section, ThemeColorScheme, Variants } from '@lace/ui';
22
import { action } from '@storybook/addon-actions';
3-
import { StakePoolSortOptions } from 'features/BrowsePools';
3+
import { useArgs } from '@storybook/preview-api';
4+
import { expect, userEvent, waitFor, within } from '@storybook/test';
5+
import { DEFAULT_SORT_OPTIONS, StakePoolSortOptions } from 'features/BrowsePools';
46
import { useCallback, useState } from 'react';
5-
import type { Meta } from '@storybook/react';
7+
import type { Meta, StoryObj } from '@storybook/react';
68

79
import { PoolsFilter, QueryStakePoolsFilters } from '../../store';
810
import { BrowsePoolsPreferencesCard } from './BrowsePoolsPreferencesCard';
911
import { SortAndFilterTab } from './types';
1012

11-
export default {
13+
const meta: Meta<typeof BrowsePoolsPreferencesCard> = {
14+
component: BrowsePoolsPreferencesCard,
1215
title: 'Cards/Stake Pool Sorting & Filter',
13-
} as Meta;
14-
16+
};
17+
export default meta;
1518
const Wrapper = ({ defaultTab }: { defaultTab: SortAndFilterTab }) => {
1619
const [activeTab, setActiveTab] = useState(defaultTab);
17-
const [sort, setSort] = useState<StakePoolSortOptions>({
18-
field: 'saturation',
19-
order: 'asc',
20-
});
20+
const [sort, setSort] = useState<StakePoolSortOptions>(DEFAULT_SORT_OPTIONS);
2121
const [filter, setFilter] = useState<QueryStakePoolsFilters>({
2222
[PoolsFilter.Saturation]: ['', ''],
2323
[PoolsFilter.ProfitMargin]: ['', ''],
@@ -109,3 +109,57 @@ export const Overview = (): JSX.Element => (
109109
</Section>
110110
</>
111111
);
112+
113+
export const Interactions: StoryObj<typeof BrowsePoolsPreferencesCard> = {
114+
args: {
115+
activeTab: SortAndFilterTab.sort,
116+
filter: {
117+
[PoolsFilter.Saturation]: ['', ''],
118+
[PoolsFilter.ProfitMargin]: ['', ''],
119+
[PoolsFilter.Performance]: ['', ''],
120+
[PoolsFilter.Ros]: ['lastepoch'],
121+
},
122+
sort: {
123+
field: 'pledge',
124+
order: 'desc',
125+
},
126+
},
127+
play: async ({ canvasElement, step }) => {
128+
const canvas = within(canvasElement);
129+
await waitFor(() => expect(canvas.getByTestId('radio-btn-test-id-ticker')));
130+
const tickerBtn = canvas.getByTestId('radio-btn-test-id-ticker');
131+
132+
await userEvent.click(tickerBtn);
133+
const saturationBtn = canvas.getByTestId('radio-btn-test-id-saturation');
134+
await step('Change sort field', async () => {
135+
await userEvent.click(saturationBtn);
136+
});
137+
138+
const iconDesc = canvas.getByTestId('sort-desc');
139+
await waitFor(() => expect(saturationBtn).toBeChecked());
140+
await waitFor(() => expect(iconDesc).toBeInTheDocument());
141+
await step('Click on iconDesc', async () => {
142+
await userEvent.click(iconDesc);
143+
});
144+
145+
await waitFor(() => expect(iconDesc).not.toBeInTheDocument());
146+
const iconAsc = canvas.getByTestId('sort-asc');
147+
await waitFor(() => expect(iconAsc).toBeInTheDocument());
148+
},
149+
render: function Render(args) {
150+
const [{ sort }, setArgs] = useArgs();
151+
152+
return (
153+
<BrowsePoolsPreferencesCard
154+
{...args}
155+
sort={sort}
156+
onSortChange={(newSort) => {
157+
setArgs({
158+
...args,
159+
sort: newSort,
160+
});
161+
}}
162+
/>
163+
);
164+
},
165+
};

packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { useCallback, useMemo, useState } from 'react';
2121
import { useTranslation } from 'react-i18next';
2222
import { USE_MULTI_DELEGATION_STAKING_FILTERS, USE_ROS_STAKING_COLUMN } from '../../../featureFlags';
2323
import { PoolsFilter, QueryStakePoolsFilters } from '../../store';
24+
import { getDefaultSortOrderByField } from '../utils';
2425
import * as styles from './BrowsePoolsPreferencesCard.css';
2526
import { BrowsePoolsPreferencesCardLabel } from './BrowsePoolsPreferencesCardLabel';
2627
import { FilterOption, SelectOption, SortAndFilterTab } from './types';
@@ -89,7 +90,7 @@ export const BrowsePoolsPreferencesCard = ({
8990

9091
onSortChange({
9192
field: sortField,
92-
order: 'asc',
93+
order: getDefaultSortOrderByField(sortField),
9394
});
9495
},
9596
[analytics, onSortChange]
@@ -135,8 +136,18 @@ export const BrowsePoolsPreferencesCard = ({
135136
};
136137

137138
const sortingOptions: RadioButtonGroupOption[] = useMemo(() => {
138-
const iconAlphabetical = direction === 'asc' ? SortAlphabeticalAscIcon : SortAlphabeticalDescIcon;
139-
const iconNumerical = direction === 'asc' ? SortNumericalAscIcon : SortNumericalDescIcon;
139+
const iconAlphabetical =
140+
direction === 'asc' ? (
141+
<SortAlphabeticalAscIcon data-testid="sort-asc" />
142+
) : (
143+
<SortAlphabeticalDescIcon data-testid="sort-desc" />
144+
);
145+
const iconNumerical =
146+
direction === 'asc' ? (
147+
<SortNumericalAscIcon data-testid="sort-asc" />
148+
) : (
149+
<SortNumericalDescIcon data-testid="sort-desc" />
150+
);
140151
return [
141152
{
142153
icon: iconAlphabetical,

packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolsListHeader.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useMemo } from 'react';
44
import { useTranslation } from 'react-i18next';
55
import { useOutsideHandles } from '../../outside-handles-provider';
66
import { analyticsActionsMap } from '../analytics';
7+
import { getDefaultSortOrderByField } from '../utils';
78
import { config } from './config';
89

910
export interface TableHeaders {
@@ -66,7 +67,8 @@ export const StakePoolsListHeader = ({ setActiveSort, activeSort }: StakePoolsLi
6667
}));
6768

6869
const onSortChange = (field: SortField) => {
69-
const order = field === activeSort?.field && activeSort?.order === 'asc' ? 'desc' : 'asc';
70+
const inverseOrder = activeSort?.order === 'asc' ? 'desc' : 'asc';
71+
const order = field !== activeSort?.field ? getDefaultSortOrderByField(field) : inverseOrder;
7072

7173
analytics.sendEventToPostHog(analyticsActionsMap[field]);
7274
setActiveSort({ field, order });
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* eslint-disable unicorn/no-useless-undefined */
2+
import { getDefaultSortOrderByField } from '../utils';
3+
4+
describe('getDefaultSortOrderByField', () => {
5+
it('returns asc', () => {
6+
const tickerOrder = getDefaultSortOrderByField('ticker');
7+
const costOrder = getDefaultSortOrderByField('cost');
8+
const marginOrder = getDefaultSortOrderByField('margin');
9+
10+
expect(tickerOrder).toEqual('asc');
11+
expect(costOrder).toEqual('asc');
12+
expect(marginOrder).toEqual('asc');
13+
});
14+
15+
it('returns desc', () => {
16+
const pledgeOrder = getDefaultSortOrderByField('pledge');
17+
expect(pledgeOrder).toEqual('desc');
18+
});
19+
});
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { BrowsePoolsView, StakePoolSortOptions } from './types';
2+
import { getDefaultSortOrderByField } from './utils';
23

34
export const SEARCH_DEBOUNCE_IN_MS = 300;
45

56
export const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = {
67
field: 'ticker',
7-
order: 'asc',
8+
order: getDefaultSortOrderByField('ticker'),
89
};
910

1011
export const DEFAULT_BROWSE_POOLS_VIEW: BrowsePoolsView = BrowsePoolsView.grid;

packages/staking/src/features/BrowsePools/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export { useBrowsePoolsPersistence } from './hooks';
55
export { DEFAULT_SORT_OPTIONS } from './constants';
66

77
// TODO: remove once multi delegation feature is GA'd
8-
export { getSaturationLevel, isOversaturated } from './utils';
8+
export { getSaturationLevel, isOversaturated, getDefaultSortOrderByField } from './utils';
99
export type { StakePoolSortOptions, TranslationsFor, SortField, SortOrder } from './types';
1010
export { BrowsePoolsView } from './types';
1111
export { StakePoolCardProgressBar } from './StakePoolCard';

packages/staking/src/features/BrowsePools/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import inRange from 'lodash/inRange';
2-
import { SaturationLevels } from './types';
2+
import { SaturationLevels, SortField, SortOrder } from './types';
33

44
const mediumUpperBound = 90;
55
const highUpperBound = 95;
@@ -28,3 +28,6 @@ export const getSaturationLevel = (saturation: number): SaturationLevels => {
2828
}
2929
return SaturationLevels.Medium;
3030
};
31+
32+
export const getDefaultSortOrderByField = (field: SortField): SortOrder =>
33+
['ticker', 'cost', 'margin'].includes(field) ? 'asc' : 'desc';

packages/staking/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export {
1616
StakePoolsListRowSkeleton,
1717
getSaturationLevel,
1818
isOversaturated,
19+
getDefaultSortOrderByField,
20+
DEFAULT_SORT_OPTIONS,
1921
} from './features/BrowsePools';
2022
export { mapStakePoolToDisplayData } from './features/store';
2123
/* eslint-enable import/export */

0 commit comments

Comments
 (0)