Skip to content

Commit 1de53e8

Browse files
committed
Pr feedback changes
PageRecent cleanup: Removed duplicate handleTemplateRowClick. Removed inline template normalization. Uses new TemplatesSection. Both view-mode save effects now use one merged settings payload. New Templates component: Added src/components/Recent/TemplatesSection.tsx. Added empty co-located TemplatesSection.module.css for repo convention. TemplatesContext: Replaced any[] with TemplateInput / Template types. Typed children. Normalizes template data once in the context. Tooltip/CSS: Added optional tooltipClassName. Restored global tooltip width to 300px. Scoped Templates tooltip width to 500px. Removed unused arrow-right tooltip CSS. Localization: Added title.text.templates and recent.templates.tooltip across all locale files. Renamed old tooltip.text.templates usage to recent.templates.tooltip. Types: Updated Graph shape to use DateModified. Added optional Author / Description. Added tooltipClassName to Tooltip.
1 parent 291de24 commit 1de53e8

22 files changed

Lines changed: 205 additions & 126 deletions

src/assets/home.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ export const graphs = [
6262
}
6363
];
6464

65-
export const templates = graphs;
65+
export const templates = graphs;

src/components/Common/Tooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState, useRef, useEffect, CSSProperties } from 'react';
22
import Portal from './Portal'; // Import your Portal component
33

4-
export const Tooltip = ({ children, content, verticalOffset = 12, position: positionProp = 'below' }: Tooltip) => {
4+
export const Tooltip = ({ children, content, verticalOffset = 12, position: positionProp = 'below', tooltipClassName = '' }: Tooltip) => {
55
const [show, setShow] = useState<boolean>(false);
66
const [position, setPosition] = useState<CSSProperties>({});
77
const tooltipRef = useRef<HTMLSpanElement | null>(null);
@@ -75,7 +75,7 @@ export const Tooltip = ({ children, content, verticalOffset = 12, position: posi
7575
{children}
7676
{show && (
7777
<Portal>
78-
<div className={`tooltip-box ${show ? 'show' : ''} ${positionProp === 'right' ? 'arrow-left' : ''}`} ref={contentRef} style={position}>
78+
<div className={`tooltip-box ${show ? 'show' : ''} ${positionProp === 'right' ? 'arrow-left' : ''} ${tooltipClassName}`} ref={contentRef} style={position}>
7979
<div className="tooltip-arrow" />
8080
<div style={{ whiteSpace: 'pre-line' }}>{content}</div>
8181
</div>

src/components/Recent/PageRecent.tsx

Lines changed: 16 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { CustomNameCellRenderer } from './CustomNameCellRenderer';
55
import { CustomLocationCellRenderer } from './CustomLocationCellRenderer';
66
import { CustomAuthorCellRenderer } from './CustomAuthorCellRenderer';
77
import { GraphTable } from './GraphTable';
8-
import { GridViewIcon, ListViewIcon, QuestionMarkIcon } from '../Common/CustomIcons';
8+
import { TemplatesSection } from './TemplatesSection';
9+
import { GridViewIcon, ListViewIcon } from '../Common/CustomIcons';
910
import { openFile, saveHomePageSettings } from '../../functions/utility';
1011
import { FormattedMessage } from 'react-intl';
1112
import { Tooltip } from '../Common/Tooltip';
@@ -71,21 +72,23 @@ export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) =>
7172
useEffect(() => {
7273
if (initialized || recentPageViewMode !== viewMode) {
7374
setInitialized(true);
74-
updateSettings({ recentPageViewMode: viewMode });
75+
const mergedSettings = { ...settings, recentPageViewMode: viewMode };
76+
updateSettings(mergedSettings);
7577

7678
// Send settings to Dynamo to save
77-
saveHomePageSettings({ ...settings, recentPageViewMode: viewMode });
79+
saveHomePageSettings(mergedSettings);
7880
}
7981
// eslint-disable-next-line react-hooks/exhaustive-deps
8082
}, [viewMode]);
8183

8284
useEffect(() => {
8385
if (templatesInitialized || (settings?.templatesPageViewMode && settings.templatesPageViewMode !== templatesViewMode)) {
8486
setTemplatesInitialized(true);
85-
updateSettings({ templatesPageViewMode: templatesViewMode });
87+
const mergedSettings = { ...settings, templatesPageViewMode: templatesViewMode };
88+
updateSettings(mergedSettings);
8689

8790
// Send settings to Dynamo to save
88-
saveHomePageSettings({ ...settings, templatesPageViewMode: templatesViewMode });
91+
saveHomePageSettings(mergedSettings);
8992
}
9093
// eslint-disable-next-line react-hooks/exhaustive-deps
9194
}, [templatesViewMode]);
@@ -127,23 +130,6 @@ export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) =>
127130
openFile(contextData);
128131
};
129132

130-
// Handles mouse click over each template row
131-
const handleTemplateRowClick = (row: Row) => {
132-
// freezes the UI
133-
setIsDisabled(true);
134-
135-
const contextData = row.original.ContextData;
136-
openFile(contextData);
137-
};
138-
139-
// Map templates to match Graph structure for table view (templates use 'date' instead of 'DateModified')
140-
const templatesForTable = templates.map(template => ({
141-
...template,
142-
DateModified: template.date || template.DateModified || '',
143-
Author: template.Author || '',
144-
Description: template.Description || ''
145-
}));
146-
147133
return(
148134
<div data-testid="page-recent">
149135
{/* Recent Section */}
@@ -183,53 +169,14 @@ export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) =>
183169
)}
184170
</div>
185171

186-
{/* Templates Section */}
187-
<div className='drop-shadow-2xl' style={{ display: 'flex', alignItems: 'center' }}>
188-
<p className='title-paragraph' style={{ display: 'inline-block', width: 'fit-content' }}>
189-
<FormattedMessage id="title.text.templates"/>
190-
</p>
191-
<Tooltip content={<FormattedMessage id="tooltip.text.templates" />} position="right">
192-
<QuestionMarkIcon />
193-
</Tooltip>
194-
</div>
195-
<div style={{ display: 'flex', alignItems: 'center', marginBottom:'10px' }}>
196-
<button
197-
className={`viewmode-button ${templatesViewMode === 'grid' ? 'active' : ''}`}
198-
onClick={() => setTemplatesViewMode('grid')}
199-
disabled={templatesViewMode === 'grid'}
200-
data-testid="templates-view-toggle-grid">
201-
<Tooltip content={<FormattedMessage id="tooltip.text.grid.view.button" />}>
202-
<GridViewIcon/>
203-
</Tooltip>
204-
</button>
205-
<button
206-
className={`viewmode-button ${templatesViewMode === 'list' ? 'active' : ''}`}
207-
onClick={() => setTemplatesViewMode('list')}
208-
disabled={templatesViewMode === 'list'}
209-
data-testid="templates-view-toggle-list">
210-
<Tooltip content={<FormattedMessage id="tooltip.text.list.view.button" />}>
211-
<ListViewIcon/>
212-
</Tooltip>
213-
</button>
214-
</div>
215-
<div style={{ marginRight: '20px', paddingBottom: '35px' }}>
216-
{templatesViewMode === 'list' && (
217-
<GraphTable columns={columns} data={templatesForTable} onRowClick={handleTemplateRowClick}/>
218-
)}
219-
{templatesViewMode === 'grid' && (
220-
<div className="main-graph-grid" id="templatesContainer">
221-
{templates.map(template => (
222-
<GraphGridItem
223-
key={template.id}
224-
{...template}
225-
DateModified={template.date || template.DateModified || ''}
226-
Description={template.Description || ''}
227-
setIsDisabled={setIsDisabled}
228-
/>
229-
))}
230-
</div>
231-
)}
232-
</div>
172+
<TemplatesSection
173+
columns={columns}
174+
templates={templates}
175+
templatesViewMode={templatesViewMode}
176+
setTemplatesViewMode={setTemplatesViewMode}
177+
onRowClick={handleRowClick}
178+
setIsDisabled={setIsDisabled}
179+
/>
233180
</div>
234181
);
235182
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { FormattedMessage } from 'react-intl';
2+
import { GraphGridItem } from './GraphGridItem';
3+
import { GraphTable } from './GraphTable';
4+
import { GridViewIcon, ListViewIcon, QuestionMarkIcon } from '../Common/CustomIcons';
5+
import { Tooltip } from '../Common/Tooltip';
6+
7+
type TemplateItem = Graph & {
8+
Author: string;
9+
Description: string;
10+
};
11+
12+
interface TemplatesSectionProps {
13+
columns: Column[];
14+
templates: TemplateItem[];
15+
templatesViewMode: string;
16+
setTemplatesViewMode: (viewMode: string) => void;
17+
onRowClick: (row: Row) => void;
18+
setIsDisabled: (disable: boolean) => void;
19+
}
20+
21+
export const TemplatesSection = ({
22+
columns,
23+
templates,
24+
templatesViewMode,
25+
setTemplatesViewMode,
26+
onRowClick,
27+
setIsDisabled,
28+
}: TemplatesSectionProps) => {
29+
return (
30+
<>
31+
<div className='drop-shadow-2xl' style={{ display: 'flex', alignItems: 'center' }}>
32+
<p className='title-paragraph' style={{ display: 'inline-block', width: 'fit-content' }}>
33+
<FormattedMessage id="title.text.templates"/>
34+
</p>
35+
<Tooltip
36+
content={<FormattedMessage id="recent.templates.tooltip" />}
37+
position="right"
38+
tooltipClassName="template-info-tooltip">
39+
<QuestionMarkIcon />
40+
</Tooltip>
41+
</div>
42+
<div style={{ display: 'flex', alignItems: 'center', marginBottom:'10px' }}>
43+
<button
44+
className={`viewmode-button ${templatesViewMode === 'grid' ? 'active' : ''}`}
45+
onClick={() => setTemplatesViewMode('grid')}
46+
disabled={templatesViewMode === 'grid'}
47+
data-testid="templates-view-toggle-grid">
48+
<Tooltip content={<FormattedMessage id="tooltip.text.grid.view.button" />}>
49+
<GridViewIcon/>
50+
</Tooltip>
51+
</button>
52+
<button
53+
className={`viewmode-button ${templatesViewMode === 'list' ? 'active' : ''}`}
54+
onClick={() => setTemplatesViewMode('list')}
55+
disabled={templatesViewMode === 'list'}
56+
data-testid="templates-view-toggle-list">
57+
<Tooltip content={<FormattedMessage id="tooltip.text.list.view.button" />}>
58+
<ListViewIcon/>
59+
</Tooltip>
60+
</button>
61+
</div>
62+
<div style={{ marginRight: '20px', paddingBottom: '35px' }}>
63+
{templatesViewMode === 'list' && (
64+
<GraphTable columns={columns} data={templates} onRowClick={onRowClick}/>
65+
)}
66+
{templatesViewMode === 'grid' && (
67+
<div className="main-graph-grid" id="templatesContainer">
68+
{templates.map(template => (
69+
<GraphGridItem
70+
key={template.id}
71+
{...template}
72+
setIsDisabled={setIsDisabled}
73+
/>
74+
))}
75+
</div>
76+
)}
77+
</div>
78+
</>
79+
);
80+
};
Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,81 @@
11
import { createContext, useContext, useState, useEffect } from 'react';
2+
import type { ReactNode } from 'react';
3+
4+
type TemplateInput = Omit<Graph, 'DateModified'> & {
5+
date?: string;
6+
Author?: string;
7+
DateModified?: string;
8+
Description?: string;
9+
};
10+
11+
type Template = Graph & {
12+
Author: string;
13+
Description: string;
14+
};
15+
16+
const normalizeTemplate = (template: TemplateInput): Template => {
17+
const { date, DateModified, Author, Description, ...templateData } = template;
18+
19+
return {
20+
...templateData,
21+
DateModified: DateModified || date || '',
22+
Author: Author || '',
23+
Description: Description || '',
24+
};
25+
};
226

327
// Create the context
4-
const TemplatesContext = createContext<any[]>([]);
28+
const TemplatesContext = createContext<Template[]>([]);
29+
30+
type TemplatesProviderProps = {
31+
children: ReactNode;
32+
};
533

634
// Provider component that wraps the app components
7-
export const TemplatesProvider = ({ children }) => {
8-
// Set a placeholder for the templates which will be used differently during dev and prod
9-
let initialTemplates = [];
10-
11-
// If we are under development, we will load the templates from the local asset folder
12-
if (process.env.NODE_ENV === 'development') {
13-
initialTemplates = require('../assets/home').templates;
14-
}
35+
export const TemplatesProvider = ({ children }: TemplatesProviderProps) => {
36+
// Set a placeholder for the templates which will be used differently during dev and prod
37+
let initialTemplates: Template[] = [];
1538

16-
const [templates, setTemplates] = useState(initialTemplates);
17-
18-
// Set up the backend handler once in the provider
19-
useEffect(() => {
20-
// If we are under production, we will set up the handler for templates data from Dynamo
21-
if (process.env.NODE_ENV !== 'development') {
22-
// A method exposed to the backend used to set the templates data coming from Dynamo
23-
window.receiveTemplatesDataFromDotNet = (jsonData: any) => {
24-
try {
25-
// jsonData is already an object, so no need to parse it
26-
const data = jsonData || [];
27-
setTemplates(data);
28-
} catch (error) {
29-
console.error('Error processing templates data:', error);
30-
}
31-
};
39+
// If we are under development, we will load the templates from the local asset folder
40+
if (process.env.NODE_ENV === 'development') {
41+
// eslint-disable-next-line @typescript-eslint/no-require-imports
42+
initialTemplates = require('../assets/home').templates.map(normalizeTemplate);
43+
}
44+
45+
const [templates, setTemplates] = useState(initialTemplates);
46+
47+
// Set up the backend handler once in the provider
48+
useEffect(() => {
49+
// If we are under production, we will set up the handler for templates data from Dynamo
50+
if (process.env.NODE_ENV !== 'development') {
51+
// A method exposed to the backend used to set the templates data coming from Dynamo
52+
window.receiveTemplatesDataFromDotNet = (jsonData: TemplateInput[] | null) => {
53+
try {
54+
// jsonData is already an object, so no need to parse it
55+
const data = (jsonData || []).map(normalizeTemplate);
56+
setTemplates(data);
57+
} catch (error) {
58+
console.error('Error processing templates data:', error);
3259
}
60+
};
61+
}
3362

34-
// Cleanup function
35-
return () => {
36-
if (process.env.NODE_ENV !== 'development') {
37-
delete window.receiveTemplatesDataFromDotNet;
38-
}
39-
};
40-
}, []);
41-
42-
return (
43-
<TemplatesContext.Provider value={templates}>
44-
{children}
45-
</TemplatesContext.Provider>
46-
);
47-
}
63+
// Cleanup function
64+
return () => {
65+
if (process.env.NODE_ENV !== 'development') {
66+
delete window.receiveTemplatesDataFromDotNet;
67+
}
68+
};
69+
}, []);
70+
71+
return (
72+
<TemplatesContext.Provider value={templates}>
73+
{children}
74+
</TemplatesContext.Provider>
75+
);
76+
};
4877

4978
// Use templates hook
5079
export function useTemplates() {
51-
return useContext(TemplatesContext);
80+
return useContext(TemplatesContext);
5281
}

src/index.css

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ select {
199199
font-size: small;
200200
border-radius: 2px;
201201
z-index: 1000;
202-
max-width: 500px; /* Increased width to make it wider */
202+
max-width: 300px;
203203
white-space: normal;
204204
overflow-wrap: break-word;
205205
opacity: 0; /* Initially, set the opacity to 0 to hide the tooltip */
@@ -211,6 +211,10 @@ select {
211211
animation-play-state: running; /* Start the animation when the tooltip is shown */
212212
}
213213

214+
.tooltip-box.template-info-tooltip {
215+
max-width: 500px;
216+
}
217+
214218
.tooltip-arrow {
215219
position: absolute;
216220
bottom: 100%; /* Align it at the bottom of the tooltip box */
@@ -234,17 +238,6 @@ select {
234238
transform: translateY(-50%);
235239
}
236240

237-
/* Arrow pointing right (for tooltips on the left side) */
238-
.tooltip-box.arrow-right .tooltip-arrow {
239-
bottom: auto;
240-
right: -5px; /* Position on the right side of the tooltip box */
241-
top: 50%;
242-
width: 0;
243-
height: 0;
244-
border-color: transparent transparent transparent #eeeeee; /* Point right (toward the title) */
245-
transform: translateY(-50%);
246-
}
247-
248241
/* Remove background color of the scrollbar track */
249242
::-webkit-scrollbar {
250243
width: 14px; /* Adjust the width as needed */

0 commit comments

Comments
 (0)