Skip to content

Commit 528e96a

Browse files
rdonigianCarsonF
andauthored
Convert Language List to Datagrid (#1644)
Co-authored-by: Carson Full <[email protected]>
1 parent 801566a commit 528e96a

File tree

10 files changed

+193
-198
lines changed

10 files changed

+193
-198
lines changed

src/api/schema/typePolicies/typePolicies.base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export const typePolicies: TypePolicies = {
9292
progressReports: {},
9393
partners: {},
9494
users: {},
95+
languages: {},
9596
},
9697
},
9798
};
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {
2+
DataGridProProps as DataGridProps,
3+
GridColDef,
4+
GridToolbarColumnsButton,
5+
GridToolbarFilterButton,
6+
} from '@mui/x-data-grid-pro';
7+
import {
8+
booleanColumn,
9+
getInitialVisibility,
10+
QuickFilterButton,
11+
QuickFilterResetButton,
12+
QuickFilters,
13+
textColumn,
14+
Toolbar,
15+
useFilterToggle,
16+
} from '../Grid';
17+
import { LanguageNameColumn } from '../Grid/Columns/LanguageNameColumn';
18+
import { SensitivityColumn } from '../Grid/Columns/SensitivityColumn';
19+
import { LanguageDataGridRowFragment as Language } from './languageDataGridRow.graphql';
20+
21+
export const LanguageColumns: Array<GridColDef<Language>> = [
22+
LanguageNameColumn({
23+
field: 'name',
24+
headerName: 'Name',
25+
valueGetter: (_, language) => language,
26+
}),
27+
{
28+
field: 'ethnologue.code',
29+
headerName: 'Eth Code',
30+
...textColumn(),
31+
valueGetter: (_, { ethnologue }) => ethnologue.code.value,
32+
width: 90,
33+
},
34+
{
35+
field: 'registryOfLanguageVarietiesCode',
36+
headerName: 'ROLV',
37+
...textColumn(),
38+
width: 90,
39+
valueGetter: (_, { registryOfLanguageVarietiesCode }) =>
40+
registryOfLanguageVarietiesCode.value,
41+
},
42+
SensitivityColumn({}),
43+
{
44+
field: 'population',
45+
headerName: 'Population',
46+
type: 'number',
47+
width: 100,
48+
valueGetter: (_, { population }) => population.value,
49+
filterable: false,
50+
},
51+
{
52+
field: 'id',
53+
headerName: 'ID',
54+
valueGetter: (_, language) => language.id,
55+
width: 150,
56+
},
57+
{
58+
field: 'pinned',
59+
headerName: 'Pinned',
60+
...booleanColumn(),
61+
},
62+
];
63+
64+
export const LanguageInitialState = {
65+
pinnedColumns: {
66+
left: LanguageColumns.slice(0, 1).map((column) => column.field),
67+
},
68+
columns: {
69+
columnVisibilityModel: {
70+
...getInitialVisibility(LanguageColumns),
71+
pinned: false,
72+
id: false,
73+
},
74+
},
75+
} satisfies DataGridProps['initialState'];
76+
77+
export const LanguageToolbar = () => (
78+
<Toolbar sx={{ justifyContent: 'flex-start', gap: 2 }}>
79+
<GridToolbarColumnsButton />
80+
<GridToolbarFilterButton />
81+
<QuickFilters sx={{ flex: 1 }}>
82+
<QuickFilterResetButton />
83+
<QuickFilterButton {...useFilterToggle('pinned')}>
84+
Pinned
85+
</QuickFilterButton>
86+
</QuickFilters>
87+
</Toolbar>
88+
);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './LanguageColumns';
2+
export * from './languageDataGridRow.graphql';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
fragment languageDataGridRow on Language {
2+
id
3+
name {
4+
value
5+
}
6+
displayName {
7+
value
8+
}
9+
ethnologue {
10+
code {
11+
value
12+
}
13+
}
14+
registryOfLanguageVarietiesCode {
15+
value
16+
}
17+
population {
18+
value
19+
}
20+
sensitivity
21+
pinned
22+
}

src/scenes/Languages/List/LanguageFilterOptions.tsx

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {
2+
DataGridPro as DataGrid,
3+
DataGridProProps as DataGridProps,
4+
} from '@mui/x-data-grid-pro';
5+
import { merge } from 'lodash';
6+
import { useMemo } from 'react';
7+
import {
8+
DefaultDataGridStyles,
9+
flexLayout,
10+
noFooter,
11+
noHeaderFilterButtons,
12+
useDataGridSource,
13+
} from '~/components/Grid';
14+
import {
15+
LanguageDataGridRowFragment as Language,
16+
LanguageColumns,
17+
LanguageInitialState,
18+
LanguageToolbar,
19+
} from '~/components/LanguageDataGrid';
20+
import { LanguagesDocument } from './languages.graphql';
21+
22+
export const LanguageGrid = () => {
23+
const [dataGridProps] = useDataGridSource({
24+
query: LanguagesDocument,
25+
variables: { input: {} },
26+
listAt: 'languages',
27+
initialInput: {
28+
sort: LanguageColumns[0]!.field,
29+
},
30+
});
31+
32+
const slots = useMemo(
33+
() =>
34+
merge({}, DefaultDataGridStyles.slots, dataGridProps.slots, {
35+
toolbar: LanguageToolbar,
36+
} satisfies DataGridProps['slots']),
37+
[dataGridProps.slots]
38+
);
39+
40+
const slotProps = useMemo(
41+
() => merge({}, DefaultDataGridStyles.slotProps, dataGridProps.slotProps),
42+
[dataGridProps.slotProps]
43+
);
44+
45+
return (
46+
<DataGrid<Language>
47+
{...DefaultDataGridStyles}
48+
{...dataGridProps}
49+
slots={slots}
50+
slotProps={slotProps}
51+
columns={LanguageColumns}
52+
initialState={LanguageInitialState}
53+
headerFilters
54+
hideFooter
55+
sx={[flexLayout, noHeaderFilterButtons, noFooter]}
56+
/>
57+
);
58+
};
Lines changed: 21 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,29 @@
1-
import { TabList as ActualTabList, TabContext, TabPanel } from '@mui/lab';
2-
import {
3-
type Tabs as __Tabs,
4-
Divider,
5-
Grid,
6-
Skeleton,
7-
Tab,
8-
Typography,
9-
} from '@mui/material';
10-
import { omit, pickBy } from 'lodash';
11-
import { useRef } from 'react';
1+
import { Paper, Stack, Typography } from '@mui/material';
122
import { Helmet } from 'react-helmet-async';
13-
import { makeStyles } from 'tss-react/mui';
14-
import { Language } from '~/api/schema.graphql';
15-
import { FilterButtonDialog } from '../../../components/Filter';
16-
import { useNumberFormatter } from '../../../components/Formatters';
17-
import { LanguageListItemCard as LanguageCard } from '../../../components/LanguageListItemCard';
18-
import { ContentContainer } from '../../../components/Layout';
19-
import { List, useListQuery } from '../../../components/List';
20-
import { SortButtonDialog, useSort } from '../../../components/Sort';
21-
import {
22-
LanguageFilterOptions,
23-
useLanguageFilters,
24-
} from './LanguageFilterOptions';
25-
import { LanguagesDocument as Languages } from './languages.graphql';
26-
import { LanguageSortOptions } from './LanguageSortOptions';
27-
28-
const TabList = ActualTabList as typeof __Tabs;
29-
30-
const useStyles = makeStyles()(({ spacing, breakpoints }) => ({
31-
options: {
32-
margin: spacing(3, 0),
33-
},
34-
maxWidth: {
35-
maxWidth: breakpoints.values.sm,
36-
},
37-
tabPanel: {
38-
overflowY: 'auto',
39-
// allow card shadow to bleed over instead of cutting it off
40-
padding: spacing(0, 0, 0, 2),
41-
margin: spacing(0, 0, 0, -2),
42-
},
43-
items: {
44-
maxWidth: 400,
45-
},
46-
total: {
47-
marginTop: spacing(2),
48-
},
49-
}));
3+
import { LanguageGrid } from './LanguageGrid';
504

515
export const LanguageList = () => {
52-
const sort = useSort<Language>();
53-
const [filters, setFilters] = useLanguageFilters();
54-
const list = useListQuery(Languages, {
55-
listAt: (data) => data.languages,
56-
variables: {
57-
input: {
58-
...sort.value,
59-
filter: {
60-
...omit(filters, 'tab'),
61-
...(filters.tab === 'pinned' ? { pinned: true } : {}),
62-
},
63-
},
64-
},
65-
});
66-
67-
const { classes } = useStyles();
68-
const formatNumber = useNumberFormatter();
69-
const scrollRef = useRef<HTMLElement>(null);
70-
716
return (
72-
<ContentContainer>
7+
<Stack sx={{ flex: 1, padding: 4, pt: 2 }}>
738
<Helmet title="Languages" />
74-
<Typography variant="h2" paragraph>
75-
Languages
76-
</Typography>
77-
<Grid container spacing={1} className={classes.options}>
78-
<Grid item>
79-
<SortButtonDialog {...sort}>
80-
<LanguageSortOptions />
81-
</SortButtonDialog>
82-
</Grid>
83-
<Grid item>
84-
<FilterButtonDialog
85-
values={pickBy(omit(filters, 'tab'))}
86-
onChange={setFilters}
9+
<Stack component="main" sx={{ flex: 1 }}>
10+
<Typography variant="h2" paragraph>
11+
Languages
12+
</Typography>
13+
<Stack sx={{ flex: 1, containerType: 'size' }}>
14+
<Paper
15+
sx={{
16+
flex: 1,
17+
minHeight: 375,
18+
maxHeight: '100cqh',
19+
width: 'min-content',
20+
maxWidth: '100cqw',
21+
}}
8722
>
88-
<LanguageFilterOptions />
89-
</FilterButtonDialog>
90-
</Grid>
91-
</Grid>
92-
93-
<TabContext value={filters.tab}>
94-
<TabList
95-
onChange={(_e, tab) => setFilters({ ...filters, tab })}
96-
aria-label="language navigation tabs"
97-
className={classes.maxWidth}
98-
>
99-
<Tab label="Pinned" value="pinned" />
100-
<Tab label="All" value="all" />
101-
</TabList>
102-
<Divider className={classes.maxWidth} />
103-
<TabPanel
104-
value={filters.tab}
105-
className={classes.tabPanel}
106-
ref={scrollRef}
107-
>
108-
<Typography variant="h3" paragraph className={classes.total}>
109-
{list.data ? (
110-
`${formatNumber(list.data.total)} Languages`
111-
) : (
112-
<Skeleton width="14ch" />
113-
)}
114-
</Typography>
115-
<List
116-
{...list}
117-
classes={{ container: classes.items }}
118-
renderItem={(item) => <LanguageCard language={item} />}
119-
renderSkeleton={<LanguageCard />}
120-
scrollRef={scrollRef}
121-
/>
122-
</TabPanel>
123-
</TabContext>
124-
</ContentContainer>
23+
<LanguageGrid />
24+
</Paper>
25+
</Stack>
26+
</Stack>
27+
</Stack>
12528
);
12629
};

src/scenes/Languages/List/LanguageSortOptions.stories.tsx

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)