Skip to content

Commit 6e947a8

Browse files
authored
fix: linter rule for hooks (#9660)
1 parent a9490e6 commit 6e947a8

File tree

18 files changed

+100
-123
lines changed

18 files changed

+100
-123
lines changed

biome.json

+15-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"noUnsafeOptionalChaining": "off",
1212
"useExhaustiveDependencies": "off",
1313
"noUnusedImports": "warn",
14-
"useJsxKeyInIterable": "off"
14+
"useJsxKeyInIterable": "off",
15+
"useHookAtTopLevel": "error"
1516
},
1617
"complexity": {
1718
"noBannedTypes": "off",
@@ -100,5 +101,17 @@
100101
"formatter": {
101102
"indentWidth": 2
102103
}
103-
}
104+
},
105+
"overrides": [
106+
{
107+
"include": ["src/**"],
108+
"linter": {
109+
"rules": {
110+
"correctness": {
111+
"useHookAtTopLevel": "off"
112+
}
113+
}
114+
}
115+
}
116+
]
104117
}

frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPAYG.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ export const BillingDetailsPAYG = ({
4545
const billableUsers = Math.max(eligibleUsers.length, minSeats);
4646
const usersCost = seatPrice * billableUsers;
4747

48-
const includedTraffic = BILLING_INCLUDED_REQUESTS;
49-
const overageCost = useOverageCost(includedTraffic);
48+
const overageCost = useOverageCost(BILLING_INCLUDED_REQUESTS);
5049

5150
const totalCost = usersCost + overageCost;
5251

frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPro.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ export const BillingDetailsPro = ({
5252
const freeAssigned = Math.min(eligibleUsers.length, seats);
5353
const paidAssigned = eligibleUsers.length - freeAssigned;
5454
const paidAssignedPrice = seatPrice * paidAssigned;
55-
const includedTraffic = BILLING_INCLUDED_REQUESTS;
56-
const overageCost = useOverageCost(includedTraffic);
55+
const overageCost = useOverageCost(BILLING_INCLUDED_REQUESTS);
5756

5857
const totalCost = planPrice + paidAssignedPrice + overageCost;
5958

frontend/src/component/admin/billing/BillingDashboard/BillingPlan/useOverageCost.ts

-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ import { BILLING_TRAFFIC_PRICE } from './BillingPlan';
99
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
1010

1111
export const useOverageCost = (includedTraffic: number) => {
12-
if (!includedTraffic) {
13-
return 0;
14-
}
15-
1612
const now = new Date();
1713
const formatDate = (date: Date) => format(date, 'yyyy-MM-dd');
1814
const from = formatDate(startOfMonth(now));

frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestTimeline/ChangeRequestTimeline.tsx

+13-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import type {
1212
} from '../../changeRequest.types';
1313
import { HtmlTooltip } from '../../../common/HtmlTooltip/HtmlTooltip';
1414
import ErrorIcon from '@mui/icons-material/Error';
15-
import { useLocationSettings } from 'hooks/useLocationSettings';
15+
import {
16+
type ILocationSettings,
17+
useLocationSettings,
18+
} from 'hooks/useLocationSettings';
1619
import { formatDateYMDHMS } from 'utils/formatDate';
1720

1821
export type ISuggestChangeTimelineProps =
@@ -99,14 +102,18 @@ export const ChangeRequestTimeline: FC<ISuggestChangeTimelineProps> = ({
99102
data = steps;
100103
}
101104
const activeIndex = data.findIndex((item) => item === state);
105+
const { locationSettings } = useLocationSettings();
102106

103107
return (
104108
<StyledPaper elevation={0}>
105109
<StyledBox>
106110
<StyledTimeline>
107111
{data.map((title, index) => {
108112
if (schedule && title === 'Scheduled') {
109-
return createTimelineScheduleItem(schedule);
113+
return createTimelineScheduleItem(
114+
schedule,
115+
locationSettings,
116+
);
110117
}
111118

112119
const color = determineColor(
@@ -195,9 +202,10 @@ export const getScheduleProps = (
195202
}
196203
};
197204

198-
const createTimelineScheduleItem = (schedule: ChangeRequestSchedule) => {
199-
const { locationSettings } = useLocationSettings();
200-
205+
const createTimelineScheduleItem = (
206+
schedule: ChangeRequestSchedule,
207+
locationSettings: ILocationSettings,
208+
) => {
201209
const time = formatDateYMDHMS(
202210
new Date(schedule.scheduledAt),
203211
locationSettings?.locale,

frontend/src/component/commandBar/RecentlyVisited/CommandResultGroup.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,8 @@ export const RecentlyVisitedFeatureButton = ({
169169
featureId: string;
170170
onClick: () => void;
171171
}) => {
172+
const { trackEvent } = usePlausibleTracker();
172173
const onItemClick = () => {
173-
const { trackEvent } = usePlausibleTracker();
174-
175174
trackEvent('command-bar', {
176175
props: {
177176
eventType: `click`,

frontend/src/component/common/Table/SearchHighlightContext/SearchHighlightContext.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ const SearchHighlightContext = createContext('');
55
export const SearchHighlightProvider = SearchHighlightContext.Provider;
66

77
export const useSearchHighlightContext = (): { searchQuery: string } => {
8-
return { searchQuery: useContext(SearchHighlightContext) };
8+
const searchQuery = useContext(SearchHighlightContext);
9+
return { searchQuery };
910
};

frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.test.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { screen } from '@testing-library/react';
22
import { render } from 'utils/testRenderer';
3-
import { FeatureOverviewCell as makeFeatureOverviewCell } from './FeatureOverviewCell';
3+
import { createFeatureOverviewCell } from './FeatureOverviewCell';
44

55
const noOp = () => {};
66

77
test('Display full overview information', () => {
8-
const FeatureOverviewCell = makeFeatureOverviewCell(noOp, noOp);
8+
const FeatureOverviewCell = createFeatureOverviewCell(noOp, noOp);
99

1010
render(
1111
<FeatureOverviewCell
@@ -43,7 +43,7 @@ test('Display full overview information', () => {
4343
});
4444

4545
test('Display minimal overview information', () => {
46-
const FeatureOverviewCell = makeFeatureOverviewCell(noOp, noOp);
46+
const FeatureOverviewCell = createFeatureOverviewCell(noOp, noOp);
4747

4848
render(
4949
<FeatureOverviewCell
@@ -69,7 +69,7 @@ test('Display minimal overview information', () => {
6969
});
7070

7171
test('show archived information', () => {
72-
const FeatureOverviewCell = makeFeatureOverviewCell(noOp, noOp);
72+
const FeatureOverviewCell = createFeatureOverviewCell(noOp, noOp);
7373

7474
render(
7575
<FeatureOverviewCell

frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ const SecondaryFeatureInfo: FC<{
436436
);
437437
};
438438

439-
export const FeatureOverviewCell =
439+
export const createFeatureOverviewCell =
440440
(
441441
onTagClick: (tag: string) => void,
442442
onFlagTypeClick: (type: string) => void,

frontend/src/component/events/EventLog/EventLogFilters.test.tsx

+9-19
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ const allFilterKeys = ['from', 'to', 'createdBy', 'type', 'project', 'feature'];
66
allFilterKeys.sort();
77

88
test('When you have no projects or flags, you should not get a project or flag filters', () => {
9-
const { result } = renderHook(() =>
10-
useEventLogFilters(
11-
() => ({ projects: [] }),
12-
() => ({ features: [] }),
13-
),
14-
);
9+
const { result } = renderHook(() => useEventLogFilters([], []));
1510
const filterKeys = result.current.map((filter) => filter.filterKey);
1611
filterKeys.sort();
1712

@@ -22,9 +17,9 @@ test('When you have no projects or flags, you should not get a project or flag f
2217
test('When you have no projects, you should not get a project filter', () => {
2318
const { result } = renderHook(() =>
2419
useEventLogFilters(
25-
() => ({ projects: [] }),
20+
[],
2621
// @ts-expect-error: omitting other properties we don't need
27-
() => ({ features: [{ name: 'flag' }] }),
22+
[{ name: 'flag' }],
2823
),
2924
);
3025
const filterKeys = result.current.map((filter) => filter.filterKey);
@@ -35,10 +30,7 @@ test('When you have no projects, you should not get a project filter', () => {
3530

3631
test('When you have only one project, you should not get a project filter', () => {
3732
const { result } = renderHook(() =>
38-
useEventLogFilters(
39-
() => ({ projects: [{ id: 'a', name: 'A' }] }),
40-
() => ({ features: [] }),
41-
),
33+
useEventLogFilters([{ id: 'a', name: 'A' }], []),
4234
);
4335
const filterKeys = result.current.map((filter) => filter.filterKey);
4436
filterKeys.sort();
@@ -49,13 +41,11 @@ test('When you have only one project, you should not get a project filter', () =
4941
test('When you have two one project, you should not get a project filter', () => {
5042
const { result } = renderHook(() =>
5143
useEventLogFilters(
52-
() => ({
53-
projects: [
54-
{ id: 'a', name: 'A' },
55-
{ id: 'b', name: 'B' },
56-
],
57-
}),
58-
() => ({ features: [] }),
44+
[
45+
{ id: 'a', name: 'A' },
46+
{ id: 'b', name: 'B' },
47+
],
48+
[],
5949
),
6050
);
6151
const filterKeys = result.current.map((filter) => filter.filterKey);

frontend/src/component/events/EventLog/EventLogFilters.tsx

+11-29
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,15 @@ import {
66
} from 'component/filter/Filters/Filters';
77
import useProjects from 'hooks/api/getters/useProjects/useProjects';
88
import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch';
9-
import {
10-
EventSchemaType,
11-
type FeatureSearchResponseSchema,
12-
type SearchFeaturesParams,
13-
} from 'openapi';
9+
import { EventSchemaType, type FeatureSearchResponseSchema } from 'openapi';
1410
import type { ProjectSchema } from 'openapi';
1511
import { useEventCreators } from 'hooks/api/getters/useEventCreators/useEventCreators';
1612

1713
export const useEventLogFilters = (
18-
projectsHook: () => { projects: ProjectSchema[] },
19-
featuresHook: (params: SearchFeaturesParams) => {
20-
features: FeatureSearchResponseSchema[];
21-
},
14+
projects: ProjectSchema[],
15+
features: FeatureSearchResponseSchema[],
2216
) => {
23-
const { projects } = projectsHook();
24-
const { features } = featuresHook({});
2517
const { eventCreators } = useEventCreators();
26-
2718
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
2819
useEffect(() => {
2920
const projectOptions =
@@ -124,22 +115,6 @@ export const useEventLogFilters = (
124115
};
125116

126117
type LogType = 'flag' | 'project' | 'global';
127-
const useEventLogFiltersFromLogType = (logType: LogType) => {
128-
switch (logType) {
129-
case 'flag':
130-
return useEventLogFilters(
131-
() => ({ projects: [] }),
132-
() => ({ features: [] }),
133-
);
134-
case 'project':
135-
return useEventLogFilters(
136-
() => ({ projects: [] }),
137-
useFeatureSearch,
138-
);
139-
case 'global':
140-
return useEventLogFilters(useProjects, useFeatureSearch);
141-
}
142-
};
143118

144119
type EventLogFiltersProps = {
145120
logType: LogType;
@@ -154,7 +129,14 @@ export const EventLogFilters: FC<EventLogFiltersProps> = ({
154129
state,
155130
onChange,
156131
}) => {
157-
const availableFilters = useEventLogFiltersFromLogType(logType);
132+
const { features } = useFeatureSearch({});
133+
const { projects } = useProjects();
134+
const featuresToFilter = logType !== 'flag' ? features : [];
135+
const projectsToFilter = logType === 'global' ? projects : [];
136+
const availableFilters = useEventLogFilters(
137+
projectsToFilter,
138+
featuresToFilter,
139+
);
158140

159141
return (
160142
<Filters

frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ export const EnvironmentAccordionBody = ({
8989
}
9090
}, []);
9191

92-
if (!featureEnvironment) {
93-
return null;
94-
}
95-
9692
const pageSize = 20;
9793
const { page, pages, setPageIndex, pageIndex } =
9894
usePagination<IFeatureStrategy>(strategies, pageSize);
9995

96+
if (!featureEnvironment) {
97+
return null;
98+
}
99+
100100
const onReorder = async (payload: { id: string; sortOrder: number }[]) => {
101101
try {
102102
await setStrategiesSortOrder(

frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/LegacyEnvironmentAccordionBody.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,14 @@ const EnvironmentAccordionBody = ({
101101
}
102102
}, []);
103103

104-
if (!featureEnvironment) {
105-
return null;
106-
}
107-
108104
const pageSize = 20;
109105
const { page, pages, setPageIndex, pageIndex } =
110106
usePagination<IFeatureStrategy>(strategies, pageSize);
111107

108+
if (!featureEnvironment) {
109+
return null;
110+
}
111+
112112
const onReorder = async (payload: { id: string; sortOrder: number }[]) => {
113113
try {
114114
await setStrategiesSortOrder(

frontend/src/component/feature/FeatureView/FeatureOverview/ManageTagsDialog/ManageBulkTagsDialog.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { TagTypeSelect } from './TagTypeSelect';
1111
import { type TagOption, TagsInput } from './TagsInput';
1212
import useTags from 'hooks/api/getters/useTags/useTags';
1313
import useTagTypes from 'hooks/api/getters/useTagTypes/useTagTypes';
14-
import type { ITag, ITagType } from 'interfaces/tags';
14+
import type { ITagType } from 'interfaces/tags';
1515
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
1616
import useTagApi from 'hooks/api/actions/useTagApi/useTagApi';
1717
import type { TagSchema } from 'openapi';

frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { useDefaultColumnVisibility } from './hooks/useDefaultColumnVisibility';
3535
import { TableEmptyState } from './TableEmptyState/TableEmptyState';
3636
import { useRowActions } from './hooks/useRowActions';
3737
import { useSelectedData } from './hooks/useSelectedData';
38-
import { FeatureOverviewCell } from 'component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell';
38+
import { createFeatureOverviewCell } from 'component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell';
3939
import {
4040
useProjectFeatureSearch,
4141
useProjectFeatureSearchActions,
@@ -213,7 +213,7 @@ export const ProjectFeatureToggles = ({
213213
columnHelper.accessor('name', {
214214
id: 'name',
215215
header: 'Name',
216-
cell: FeatureOverviewCell(onTagClick, onFlagTypeClick),
216+
cell: createFeatureOverviewCell(onTagClick, onFlagTypeClick),
217217
enableHiding: false,
218218
}),
219219
columnHelper.accessor('createdAt', {

frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ManageTags.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useMemo, useState, type VFC } from 'react';
22
import { Button } from '@mui/material';
33
import { ManageBulkTagsDialog } from 'component/feature/FeatureView/FeatureOverview/ManageTagsDialog/ManageBulkTagsDialog';
44
import type { FeatureSchema, TagSchema } from 'openapi';
5-
import type { ITag } from 'interfaces/tags';
65
import useTagApi from 'hooks/api/actions/useTagApi/useTagApi';
76
import useToast from 'hooks/useToast';
87
import { formatUnknownError } from 'utils/formatUnknownError';

frontend/src/component/project/Project/ProjectInsights/ProjectInsightsStats/ProjectInsightsStats.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ interface IProjectStatsProps {
3838
}
3939

4040
export const ProjectInsightsStats = ({ stats }: IProjectStatsProps) => {
41+
const projectId = useRequiredPathParam('projectId');
4142
if (Object.keys(stats).length === 0) {
4243
return null;
4344
}
44-
const projectId = useRequiredPathParam('projectId');
4545

4646
const {
4747
avgTimeToProdCurrentWindow,

0 commit comments

Comments
 (0)