Skip to content
This repository was archived by the owner on Jan 19, 2025. It is now read-only.

Commit 88b52f4

Browse files
authored
feat(gui): separate column for statistics (#689)
* feat(gui): reorder sections in statistics view * feat(gui): improve labels * feat(gui): statistics always visible * feat(gui): adjust width of left pane * feat(gui): make statistics view toggleable again * feat(gui): cleaner annotation statistics * style: apply automatic fixes of linters * style(gui): remove commented out code * feat(gui): consistent spacing Co-authored-by: lars-reimann <[email protected]>
1 parent f2f8681 commit 88b52f4

File tree

5 files changed

+99
-98
lines changed

5 files changed

+99
-98
lines changed

api-editor/gui/src/app/App.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
selectShowUsageImportDialog,
5252
selectUI,
5353
selectShowAddFilterDialog,
54+
selectShowStatistics,
5455
} from '../features/ui/uiSlice';
5556
import { initializeUsages, persistUsages, selectUsages } from '../features/usages/usageSlice';
5657
import { initializePythonPackage, selectRawPythonPackage } from '../features/packageData/apiSlice';
@@ -69,6 +70,7 @@ import { AbstractPythonFilter } from '../features/filter/model/AbstractPythonFil
6970
import { UsageCountStore } from '../features/usages/model/UsageCountStore';
7071
import { PythonDeclaration } from '../features/packageData/model/PythonDeclaration';
7172
import { SaveFilterDialog } from '../features/filter/SaveFilterDialog';
73+
import { StatisticsView } from '../features/packageData/selectionView/StatisticsView';
7274

7375
export const App: React.FC = function () {
7476
useIndexedDB();
@@ -93,25 +95,26 @@ export const App: React.FC = function () {
9395
const showUsagesImportDialog = useAppSelector(selectShowUsageImportDialog);
9496
const batchMode = useAppSelector(selectBatchMode);
9597
const showAddFilterDialog = useAppSelector(selectShowAddFilterDialog);
98+
const showStatistics = useAppSelector(selectShowStatistics);
9699

97100
return (
98101
<>
99102
<Grid
100103
autoColumns="0fr 1fr 0fr"
101-
autoRows="0fr 1fr"
102-
templateAreas='"menu menu" "leftPane rightPane" "footer footer"'
104+
autoRows="0fr 1fr 0fr"
105+
templateAreas='"menu menu menu" "leftPane middlePane rightPane" "footer footer footer"'
103106
w="100vw"
104107
h="100vh"
105108
>
106-
<GridItem gridArea="menu" colSpan={2}>
109+
<GridItem gridArea="menu" colSpan={3}>
107110
<MenuBar displayInferErrors={displayInferErrors} />
108111
</GridItem>
109112
<GridItem
110113
gridArea="leftPane"
111114
overflow="auto"
112115
minW="20vw"
113-
w="40vw"
114-
maxW="80vw"
116+
w="30vw"
117+
maxW="50vw"
115118
borderRight={1}
116119
layerStyle="subtleBorder"
117120
resize="horizontal"
@@ -155,7 +158,7 @@ export const App: React.FC = function () {
155158
)}
156159
{currentUserAction.type === 'todo' && <TodoForm target={userActionTarget || rawPythonPackage} />}
157160
</GridItem>
158-
<GridItem gridArea="rightPane" overflow="auto">
161+
<GridItem gridArea="middlePane" overflow="auto">
159162
<Box flexGrow={1} overflowY="auto" width="100%">
160163
{batchMode === BatchMode.None && <SelectionView />}
161164

@@ -196,7 +199,21 @@ export const App: React.FC = function () {
196199
)}
197200
</Box>
198201
</GridItem>
199-
<GridItem gridArea="footer" colSpan={2}>
202+
{showStatistics && (
203+
<GridItem
204+
gridArea="rightPane"
205+
overflow="auto"
206+
w="20vw"
207+
borderLeft={1}
208+
layerStyle="subtleBorder"
209+
resize="horizontal"
210+
>
211+
<Box padding={4}>
212+
<StatisticsView />
213+
</Box>
214+
</GridItem>
215+
)}
216+
<GridItem gridArea="footer" colSpan={3}>
200217
{currentUserAction.type === 'none' && <ActionBar declaration={declaration} />}
201218
</GridItem>
202219

api-editor/gui/src/common/MenuBar.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,19 @@ import {
2323
BatchMode,
2424
HeatMapMode,
2525
selectHeatMapMode,
26+
selectShowStatistics,
2627
selectSortingMode,
2728
setBatchMode,
2829
setHeatMapMode,
2930
setSortingMode,
3031
SortingMode,
3132
toggleAnnotationImportDialog,
3233
toggleAPIImportDialog,
34+
toggleStatisticsView,
3335
toggleUsageImportDialog,
3436
} from '../features/ui/uiSlice';
3537
import { DeleteAllAnnotations } from './DeleteAllAnnotations';
3638
import { GenerateAdapters } from './GenerateAdapters';
37-
import { useNavigate } from 'react-router-dom';
3839
import { FilterControls } from '../features/filter/FilterControls';
3940

4041
interface MenuBarProps {
@@ -44,11 +45,11 @@ interface MenuBarProps {
4445
export const MenuBar: React.FC<MenuBarProps> = function ({ displayInferErrors }) {
4546
const { colorMode, toggleColorMode } = useColorMode();
4647
const dispatch = useAppDispatch();
47-
const navigate = useNavigate();
4848

4949
const annotationStore = useAppSelector(selectAnnotationStore);
5050
const sortingMode = useAppSelector(selectSortingMode);
5151
const heatMapMode = useAppSelector(selectHeatMapMode);
52+
const showStatistics = useAppSelector(selectShowStatistics);
5253

5354
const exportAnnotations = () => {
5455
const a = document.createElement('a');
@@ -60,13 +61,12 @@ export const MenuBar: React.FC<MenuBarProps> = function ({ displayInferErrors })
6061
a.click();
6162
};
6263

63-
const setStatisticsViewPath = () => {
64-
navigate(`/statistics-view`);
65-
};
66-
67-
const colorModeArray: string[] = [];
64+
const visualSettings: string[] = [];
6865
if (colorMode === 'dark') {
69-
colorModeArray.push('darkMode');
66+
visualSettings.push('darkMode');
67+
}
68+
if (showStatistics) {
69+
visualSettings.push('statistics');
7070
}
7171

7272
return (
@@ -133,19 +133,25 @@ export const MenuBar: React.FC<MenuBarProps> = function ({ displayInferErrors })
133133
</Menu>
134134
</Box>
135135

136-
<Button onClick={setStatisticsViewPath}>Statistics View</Button>
137-
138136
<Box>
139137
<Menu closeOnSelect={false}>
140138
<MenuButton as={Button} rightIcon={<Icon as={FaChevronDown} />}>
141139
Settings
142140
</MenuButton>
143141
<MenuList>
144-
<MenuOptionGroup type="checkbox" value={colorModeArray}>
145-
<MenuItemOption value={'darkMode'} onClick={toggleColorMode}>
146-
Dark Mode
147-
</MenuItemOption>
148-
</MenuOptionGroup>
142+
<MenuGroup title="Visual">
143+
<MenuOptionGroup type="checkbox" value={visualSettings}>
144+
<MenuItemOption value={'darkMode'} onClick={toggleColorMode}>
145+
Dark Mode
146+
</MenuItemOption>
147+
<MenuItemOption
148+
value={'statistics'}
149+
onClick={() => dispatch(toggleStatisticsView())}
150+
>
151+
Show Statistics
152+
</MenuItemOption>
153+
</MenuOptionGroup>
154+
</MenuGroup>
149155
<MenuDivider />
150156
<MenuGroup title="Module/Class/Function Sorting">
151157
<MenuOptionGroup

api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,10 @@ import { ModuleView } from './ModuleView';
1111
import { ParameterView } from './ParameterView';
1212
import { useAppSelector } from '../../../app/hooks';
1313
import { selectRawPythonPackage } from '../apiSlice';
14-
import { StatisticsView } from './StatisticsView';
1514

1615
export const SelectionView: React.FC = function () {
1716
const rawPythonPackage = useAppSelector(selectRawPythonPackage);
1817
const declaration = rawPythonPackage.getDeclarationById(useLocation().pathname.split('/').splice(1).join('/'));
19-
const location = useLocation().pathname;
20-
21-
if (location === '/statistics-view') {
22-
return (
23-
<Box overflowY="auto" h="100%" w="100%" padding={4}>
24-
<StatisticsView />
25-
</Box>
26-
);
27-
}
2818

2919
if (!declaration) {
3020
return null;

api-editor/gui/src/features/packageData/selectionView/StatisticsView.tsx

Lines changed: 47 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { Bar, Line } from 'react-chartjs-2';
1313
import { PythonPackage } from '../model/PythonPackage';
1414
import { UsageCountStore } from '../../usages/model/UsageCountStore';
15-
import { Box, Flex, Button, Heading, VStack, Wrap, WrapItem } from '@chakra-ui/react';
15+
import { Box, Button, Flex, Heading, SimpleGrid, VStack } from '@chakra-ui/react';
1616
import { selectAnnotationStore } from '../../annotations/annotationSlice';
1717
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
1818
import { selectFilterString, setFilterString } from '../../ui/uiSlice';
@@ -64,21 +64,24 @@ export const StatisticsView: React.FC = function () {
6464
rawPythonPackage,
6565
thresholds,
6666
usages.getNumberOfUsedPublicClasses,
67-
'Classes per Threshold',
67+
'Classes',
68+
'Minimum usefulness',
6869
);
6970
const functionLineChart = createLineChart(
7071
usages,
7172
rawPythonPackage,
7273
thresholds,
7374
usages.getNumberOfUsedPublicFunctions,
74-
'Functions per Threshold',
75+
'Functions',
76+
'Minimum usefulness',
7577
);
7678
const parameterLineChart = createLineChart(
7779
usages,
7880
rawPythonPackage,
7981
thresholds,
8082
usages.getNumberOfUsefulPublicParameters,
81-
'Parameters per Threshold',
83+
'Parameters',
84+
'Minimum usefulness',
8285
);
8386

8487
const filterAction = (annotation: string) => {
@@ -100,9 +103,34 @@ export const StatisticsView: React.FC = function () {
100103
};
101104

102105
return (
103-
<VStack spacing="4">
104-
<Heading as="h2" size="md">
105-
Statistics
106+
<VStack spacing={4}>
107+
<Heading as="h3" size="md">
108+
Annotations
109+
</Heading>
110+
<SimpleGrid columns={2} spacing={2}>
111+
<Button onClick={() => filterAction('attribute')} children={'Attributes: ' + attributesSize}></Button>
112+
<Button onClick={() => filterAction('boundary')} children={'Boundaries: ' + boundariesSize}></Button>
113+
<Button
114+
onClick={() => filterAction('calledAfter')}
115+
children={'CalledAfter: ' + calledAftersSize}
116+
></Button>
117+
<Button onClick={() => filterAction('constant')} children={'Constants: ' + constantsSize}></Button>
118+
<Button
119+
onClick={() => filterAction('description')}
120+
children={'Descriptions: ' + descriptionSize}
121+
></Button>
122+
<Button onClick={() => filterAction('enum')} children={'Enums: ' + enumsSize}></Button>
123+
<Button onClick={() => filterAction('group')} children={'Groups: ' + groupsSize}></Button>
124+
<Button onClick={() => filterAction('move')} children={'Move: ' + movesSize}></Button>
125+
<Button onClick={() => filterAction('optional')} children={'Optionals: ' + optionalsSize}></Button>
126+
<Button onClick={() => filterAction('pure')} children={'Pures: ' + puresSize}></Button>
127+
<Button onClick={() => filterAction('remove')} children={'Removes: ' + removesSize}></Button>
128+
<Button onClick={() => filterAction('rename')} children={'Renaming: ' + renamingsSize}></Button>
129+
<Button onClick={() => filterAction('required')} children={'Requireds: ' + requiredsSize}></Button>
130+
<Button onClick={() => filterAction('todo')} children={'Todos: ' + todoSize}></Button>
131+
</SimpleGrid>
132+
<Heading as="h3" size="md">
133+
API Size
106134
</Heading>
107135
<Box width="100%">
108136
<Flex wrap="wrap">
@@ -117,6 +145,9 @@ export const StatisticsView: React.FC = function () {
117145
</Box>
118146
</Flex>
119147
</Box>
148+
<Heading as="h3" size="md">
149+
API Size per Minimum Usefulness Threshold
150+
</Heading>
120151
<Box width="100%">
121152
<Flex wrap="wrap">
122153
<Box minWidth="350px" flex="1 1 33%">
@@ -130,65 +161,6 @@ export const StatisticsView: React.FC = function () {
130161
</Box>
131162
</Flex>
132163
</Box>
133-
<Heading as="h3" size="md">
134-
Annotations
135-
</Heading>
136-
<Wrap mx="auto" padding="10px 10px 10px 10px">
137-
<WrapItem>
138-
<Button
139-
onClick={() => filterAction('attribute')}
140-
children={'Attributes: ' + attributesSize}
141-
></Button>
142-
</WrapItem>
143-
<WrapItem>
144-
<Button
145-
onClick={() => filterAction('boundary')}
146-
children={'Boundaries: ' + boundariesSize}
147-
></Button>
148-
</WrapItem>
149-
<WrapItem>
150-
<Button
151-
onClick={() => filterAction('calledAfter')}
152-
children={'CalledAfter: ' + calledAftersSize}
153-
></Button>
154-
</WrapItem>
155-
<WrapItem>
156-
<Button onClick={() => filterAction('constant')} children={'Constants: ' + constantsSize}></Button>
157-
</WrapItem>
158-
<WrapItem>
159-
<Button
160-
onClick={() => filterAction('description')}
161-
children={'Descriptions: ' + descriptionSize}
162-
></Button>
163-
</WrapItem>
164-
<WrapItem>
165-
<Button onClick={() => filterAction('enum')} children={'Enums: ' + enumsSize}></Button>
166-
</WrapItem>
167-
<WrapItem>
168-
<Button onClick={() => filterAction('group')} children={'Groups: ' + groupsSize}></Button>
169-
</WrapItem>
170-
<WrapItem>
171-
<Button onClick={() => filterAction('move')} children={'Move: ' + movesSize}></Button>
172-
</WrapItem>
173-
<WrapItem>
174-
<Button onClick={() => filterAction('optional')} children={'Optionals: ' + optionalsSize}></Button>
175-
</WrapItem>
176-
<WrapItem>
177-
<Button onClick={() => filterAction('pure')} children={'Pures: ' + puresSize}></Button>
178-
</WrapItem>
179-
<WrapItem>
180-
<Button onClick={() => filterAction('remove')} children={'Removes: ' + removesSize}></Button>
181-
</WrapItem>
182-
<WrapItem>
183-
<Button onClick={() => filterAction('rename')} children={'Renaming: ' + renamingsSize}></Button>
184-
</WrapItem>
185-
<WrapItem>
186-
<Button onClick={() => filterAction('required')} children={'Requireds: ' + requiredsSize}></Button>
187-
</WrapItem>
188-
<WrapItem>
189-
<Button onClick={() => filterAction('todo')} children={'Todos: ' + todoSize}></Button>
190-
</WrapItem>
191-
</Wrap>
192164
</VStack>
193165
);
194166
};
@@ -272,6 +244,7 @@ let createLineChart = function (
272244
labels: number[],
273245
getValue: Function,
274246
title: string,
247+
xAxisLabel: string,
275248
): ReactElement {
276249
const options = {
277250
responsive: true,
@@ -284,6 +257,14 @@ let createLineChart = function (
284257
text: title,
285258
},
286259
},
260+
scales: {
261+
x: {
262+
title: {
263+
display: true,
264+
text: xAxisLabel,
265+
},
266+
},
267+
},
287268
};
288269

289270
const dataValues = new Map();

api-editor/gui/src/features/ui/uiSlice.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface UIState {
2626
filterList: Filter[];
2727
sortingMode: SortingMode;
2828
batchMode: BatchMode;
29+
showStatistics: boolean;
2930
}
3031

3132
type UserAction =
@@ -147,6 +148,7 @@ export const initialState: UIState = {
147148
heatMapMode: HeatMapMode.None,
148149
sortingMode: SortingMode.Alphabetical,
149150
batchMode: BatchMode.None,
151+
showStatistics: true,
150152
};
151153

152154
// Thunks --------------------------------------------------------------------------------------------------------------
@@ -330,6 +332,9 @@ const uiSlice = createSlice({
330332
setBatchMode(state, action: PayloadAction<BatchMode>) {
331333
state.batchMode = action.payload;
332334
},
335+
toggleStatisticsView(state) {
336+
state.showStatistics = !state.showStatistics;
337+
},
333338
},
334339
extraReducers(builder) {
335340
builder.addCase(initializeUI.fulfilled, (state, action) => action.payload);
@@ -371,6 +376,7 @@ export const {
371376
removeFilter,
372377
setSortingMode,
373378
setBatchMode,
379+
toggleStatisticsView,
374380
} = actions;
375381
export const uiReducer = reducer;
376382

@@ -406,3 +412,4 @@ export const selectFilter = createSelector(
406412
);
407413
export const selectSortingMode = (state: RootState): SortingMode => selectUI(state).sortingMode;
408414
export const selectBatchMode = (state: RootState): BatchMode => selectUI(state).batchMode;
415+
export const selectShowStatistics = (state: RootState): boolean => selectUI(state).showStatistics;

0 commit comments

Comments
 (0)