Skip to content

Commit 17fe03b

Browse files
authored
Remove tremor from stories (#3024)
* Create stories CSS module * Replace stories table with ag-grid Also refactored to improve code quality. * Remove now-unnecessary alias * Replace margin with gap * Replace stories actions with Blueprint components * Standardize to use buttons for actions * Update colors * Remove tremor from stories main page * Remove tremor from stories editor
1 parent 7e1a243 commit 17fe03b

File tree

5 files changed

+156
-118
lines changed

5 files changed

+156
-118
lines changed

src/pages/stories/Stories.tsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import '@tremor/react/dist/esm/tremor.css';
2-
3-
import { Button as BpButton, Icon as BpIcon, NonIdealState } from '@blueprintjs/core';
1+
import { Button, InputGroup, NonIdealState } from '@blueprintjs/core';
42
import { IconNames } from '@blueprintjs/icons';
5-
import { Card, Flex, TextInput, Title } from '@tremor/react';
63
import React, { useCallback, useState } from 'react';
74
import { useDispatch } from 'react-redux';
85
import { useNavigate } from 'react-router-dom';
96
import { StoriesRole } from 'src/commons/application/ApplicationTypes';
107
import ContentDisplay from 'src/commons/ContentDisplay';
8+
import GradingFlex from 'src/commons/grading/GradingFlex';
9+
import GradingText from 'src/commons/grading/GradingText';
1110
import { showSimpleConfirmDialog } from 'src/commons/utils/DialogHelper';
1211
import { useTypedSelector } from 'src/commons/utils/Hooks';
1312
import StoriesActions from 'src/features/stories/StoriesActions';
@@ -120,23 +119,25 @@ const Stories: React.FC = () => {
120119
<ContentDisplay
121120
loadContentDispatch={() => dispatch(StoriesActions.getStoriesList())}
122121
display={
123-
<Card>
124-
<Flex justifyContent="justify-between">
125-
<Flex justifyContent="justify-start" spaceX="space-x-6">
126-
<Title>All Stories</Title>
122+
<>
123+
<GradingFlex justifyContent="space-between">
124+
<GradingFlex justifyContent="flex-start" alignItems="center" style={{ columnGap: 16 }}>
125+
<GradingText style={{ fontSize: '1.125rem', opacity: 0.9 }}>All Stories</GradingText>
127126
{isLoggedIn && (
128-
<BpButton onClick={handleNewStory} icon={IconNames.PLUS}>
127+
<Button onClick={handleNewStory} icon={IconNames.PLUS}>
129128
Add Story
130-
</BpButton>
129+
</Button>
131130
)}
132-
</Flex>
133-
<TextInput
134-
maxWidth="max-w-xl"
135-
icon={() => <BpIcon icon={IconNames.SEARCH} style={{ marginLeft: '0.75rem' }} />}
131+
</GradingFlex>
132+
<InputGroup
133+
className="grading-search-input"
136134
placeholder="Search for author..."
135+
leftIcon="search"
136+
large={true}
137+
value={query}
137138
onChange={e => setQuery(e.target.value)}
138139
/>
139-
</Flex>
140+
</GradingFlex>
140141

141142
<StoriesTable
142143
headers={columns}
@@ -168,7 +169,7 @@ const Stories: React.FC = () => {
168169
);
169170
}}
170171
/>
171-
</Card>
172+
</>
172173
}
173174
/>
174175
);

src/pages/stories/StoriesTable.tsx

Lines changed: 64 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
import { Icon as BpIcon } from '@blueprintjs/core/lib/esm/components/icon/icon';
1+
import 'ag-grid-community/styles/ag-grid.css';
2+
import 'ag-grid-community/styles/ag-theme-quartz.css';
3+
4+
import { Icon } from '@blueprintjs/core';
25
import { IconNames } from '@blueprintjs/icons';
3-
import {
4-
Flex,
5-
Icon,
6-
Table,
7-
TableBody,
8-
TableCell,
9-
TableHead,
10-
TableHeaderCell,
11-
TableRow,
12-
Text
13-
} from '@tremor/react';
14-
import React from 'react';
6+
import { ColDef } from 'ag-grid-community';
7+
import { AgGridReact, CustomCellRendererProps } from 'ag-grid-react';
8+
import React, { useMemo } from 'react';
9+
import GradingFlex from 'src/commons/grading/GradingFlex';
1510
import { StoryListView } from 'src/features/stories/StoriesTypes';
11+
import classes from 'src/styles/Stories.module.scss';
1612

1713
type Props = {
1814
headers: Array<{ id: string; header: string }>;
@@ -22,45 +18,63 @@ type Props = {
2218

2319
const MAX_EXCERPT_LENGTH = 35;
2420

21+
const truncate = (content: string) => {
22+
return content.replaceAll(/\s+/g, ' ').length <= MAX_EXCERPT_LENGTH
23+
? content.replaceAll(/\s+/g, ' ')
24+
: content.split(/\s+/).reduce((acc, cur) => {
25+
return acc.length + cur.length <= MAX_EXCERPT_LENGTH ? acc + ' ' + cur : acc;
26+
}, '') + '…';
27+
};
28+
29+
const defaultColDef: ColDef<StoryListView> = {
30+
cellClass: ({ data }) => (data?.isPinned ? classes['highlight-row'] : '')
31+
};
32+
2533
const StoriesTable: React.FC<Props> = ({ headers, stories, storyActions }) => {
34+
const columns: ColDef<StoryListView>[] = useMemo(
35+
() => [
36+
{ flex: 2, field: 'authorName', headerName: 'Author' },
37+
{
38+
flex: 4,
39+
field: 'title',
40+
headerName: 'Title',
41+
cellRenderer: ({ data, value }: CustomCellRendererProps<StoryListView>) =>
42+
data && (
43+
<GradingFlex alignItems="center" style={{ columnGap: 8 }}>
44+
{data.isPinned && <Icon intent="primary" icon={IconNames.PIN} />}
45+
{value}
46+
</GradingFlex>
47+
)
48+
},
49+
{
50+
flex: 6,
51+
field: 'content',
52+
headerName: 'Content',
53+
valueFormatter: ({ value }) => truncate(value),
54+
cellStyle: { textAlign: 'left' }
55+
},
56+
{
57+
flex: 3,
58+
field: 'actions' as any,
59+
headerName: 'Actions',
60+
sortable: false,
61+
cellRenderer: ({ data }: CustomCellRendererProps<StoryListView>) => storyActions(data!)
62+
}
63+
],
64+
[storyActions]
65+
);
66+
2667
return (
27-
<Table marginTop="mt-10">
28-
<TableHead>
29-
<TableRow>
30-
{headers.map(({ id, header }) => (
31-
<TableHeaderCell key={id}>{header}</TableHeaderCell>
32-
))}
33-
</TableRow>
34-
</TableHead>
35-
<TableBody>
36-
{stories.map(story => {
37-
const { id, authorName, isPinned, title, content } = story;
38-
return (
39-
<TableRow key={id}>
40-
<TableCell>{authorName}</TableCell>
41-
<TableCell>
42-
<Flex justifyContent="justify-start">
43-
{isPinned && <Icon icon={() => <BpIcon icon={IconNames.PIN} />} />}
44-
<Text>{title}</Text>
45-
</Flex>
46-
</TableCell>
47-
<TableCell>
48-
<Text>
49-
{content.replaceAll(/\s+/g, ' ').length <= MAX_EXCERPT_LENGTH
50-
? content.replaceAll(/\s+/g, ' ')
51-
: content.split(/\s+/).reduce((acc, cur) => {
52-
return acc.length + cur.length <= MAX_EXCERPT_LENGTH
53-
? acc + ' ' + cur
54-
: acc;
55-
}, '') + '…'}
56-
</Text>
57-
</TableCell>
58-
<TableCell>{storyActions(story)}</TableCell>
59-
</TableRow>
60-
);
61-
})}
62-
</TableBody>
63-
</Table>
68+
<div className="ag-theme-quartz" style={{ marginTop: 24 }}>
69+
<AgGridReact
70+
rowData={stories}
71+
columnDefs={columns}
72+
defaultColDef={defaultColDef}
73+
domLayout="autoHeight"
74+
suppressMovableColumns
75+
suppressCellFocus
76+
/>
77+
</div>
6478
);
6579
};
6680

src/pages/stories/Story.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'js-slang/dist/editors/ace/theme/source';
22

3-
import { Classes } from '@blueprintjs/core';
4-
import { TextInput } from '@tremor/react';
3+
import { Classes, InputGroup } from '@blueprintjs/core';
54
import classNames from 'classnames';
65
import { useEffect, useState } from 'react';
76
import AceEditor, { IEditorProps } from 'react-ace';
@@ -57,8 +56,8 @@ const Story: React.FC<Props> = ({ isViewOnly = false }) => {
5756
isViewOnly ? (
5857
<>{title}</>
5958
) : (
60-
<TextInput
61-
maxWidth="max-w-xl"
59+
<InputGroup
60+
className="grading-search-input"
6261
placeholder="Enter story title"
6362
value={title}
6463
onChange={e => {

src/pages/stories/StoryActions.tsx

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { Icon as BpIcon } from '@blueprintjs/core';
1+
import { Button, Position, Tooltip } from '@blueprintjs/core';
22
import { IconNames } from '@blueprintjs/icons';
3-
import { Flex, Icon } from '@tremor/react';
43
import React from 'react';
5-
import { Link } from 'react-router-dom';
4+
import { useNavigate } from 'react-router-dom';
5+
import GradingFlex from 'src/commons/grading/GradingFlex';
66

77
type Props = {
88
storyId: number;
@@ -29,69 +29,88 @@ const StoryActions: React.FC<Props> = ({
2929
handleMovePinUp = () => {},
3030
handleMovePinDown = () => {}
3131
}) => {
32+
const navigate = useNavigate();
3233
return (
33-
<Flex justifyContent="justify-start" spaceX="space-x-2">
34+
<GradingFlex style={{ height: '100%' }}>
3435
{canView && (
35-
<Link to={`./view/${storyId}`}>
36-
<Icon
37-
tooltip="View"
38-
icon={() => <BpIcon icon={IconNames.EyeOpen} />}
39-
variant="light"
40-
color="green"
36+
<Tooltip
37+
targetProps={{ style: { display: 'flex' } }}
38+
placement={Position.TOP}
39+
content="View"
40+
>
41+
<Button
42+
intent="success"
43+
icon={IconNames.EyeOpen}
44+
minimal
45+
onClick={() => navigate(`./view/${storyId}`)}
4146
/>
42-
</Link>
47+
</Tooltip>
4348
)}
4449
{canEdit && (
45-
<Link to={`./edit/${storyId}`}>
46-
<Icon
47-
tooltip="Edit"
48-
icon={() => <BpIcon icon={IconNames.EDIT} />}
49-
variant="light"
50-
color="sky"
50+
<Tooltip
51+
targetProps={{ style: { display: 'flex' } }}
52+
placement={Position.TOP}
53+
content="Edit"
54+
>
55+
<Button
56+
intent="primary"
57+
icon={IconNames.EDIT}
58+
minimal
59+
onClick={() => navigate(`./edit/${storyId}`)}
5160
/>
52-
</Link>
61+
</Tooltip>
5362
)}
5463
{canPin && isPinned && (
5564
<>
56-
<button style={{ padding: 0 }} onClick={() => handleMovePinUp(storyId)}>
57-
<Icon
58-
tooltip="Reorder up"
59-
icon={() => <BpIcon icon={IconNames.ARROW_UP} />}
60-
variant="light"
61-
color="pink"
65+
<Tooltip
66+
targetProps={{ style: { display: 'flex' } }}
67+
placement={Position.TOP}
68+
content="Reorder up"
69+
>
70+
<Button icon={IconNames.ARROW_UP} minimal onClick={() => handleMovePinUp(storyId)} />
71+
</Tooltip>
72+
<Tooltip
73+
targetProps={{ style: { display: 'flex' } }}
74+
placement={Position.TOP}
75+
content="Reorder down"
76+
>
77+
<Button
78+
icon={IconNames.ARROW_DOWN}
79+
minimal
80+
onClick={() => handleMovePinDown(storyId)}
6281
/>
63-
</button>
64-
<button style={{ padding: 0 }} onClick={() => handleMovePinDown(storyId)}>
65-
<Icon
66-
tooltip="Reorder down"
67-
icon={() => <BpIcon icon={IconNames.ARROW_DOWN} />}
68-
variant="light"
69-
color="pink"
70-
/>
71-
</button>
82+
</Tooltip>
7283
</>
7384
)}
7485
{canPin && (
75-
<button style={{ padding: 0 }} onClick={() => handleTogglePin(storyId)}>
76-
<Icon
77-
tooltip={isPinned ? 'Unpin' : 'Pin'}
78-
icon={() => <BpIcon icon={isPinned ? IconNames.EXCLUDE_ROW : IconNames.PIN} />}
79-
variant="light"
80-
color="indigo"
86+
<Tooltip
87+
targetProps={{ style: { display: 'flex' } }}
88+
placement={Position.TOP}
89+
content={isPinned ? 'Unpin' : 'Pin'}
90+
>
91+
<Button
92+
intent="warning"
93+
icon={isPinned ? IconNames.EXCLUDE_ROW : IconNames.PIN}
94+
minimal
95+
onClick={() => handleTogglePin(storyId)}
8196
/>
82-
</button>
97+
</Tooltip>
8398
)}
8499
{canDelete && (
85-
<button style={{ padding: 0 }} onClick={() => handleDeleteStory(storyId)}>
86-
<Icon
87-
tooltip="Delete"
88-
icon={() => <BpIcon icon={IconNames.TRASH} />}
89-
variant="light"
90-
color="red"
100+
<Tooltip
101+
targetProps={{ style: { display: 'flex' } }}
102+
placement={Position.TOP}
103+
content="Delete"
104+
>
105+
<Button
106+
intent="danger"
107+
icon={IconNames.TRASH}
108+
minimal
109+
onClick={() => handleDeleteStory(storyId)}
91110
/>
92-
</button>
111+
</Tooltip>
93112
)}
94-
</Flex>
113+
</GradingFlex>
95114
);
96115
};
97116

src/styles/Stories.module.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@import '_global';
2+
3+
.highlight-row {
4+
background-color: #e0f2fe;
5+
}

0 commit comments

Comments
 (0)