-
Notifications
You must be signed in to change notification settings - Fork 31
Feat/thumbnail #3797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat/thumbnail #3797
Conversation
📝 WalkthroughWalkthroughThese changes introduce thumbnail support to the file upload table UI. Translation entries are added across multiple languages for a new "Thumbnail" column header. A new AttachmentThumbnail component renders thumbnail images for uploaded attachments. The FileTable and FileTableRow components are updated to conditionally display thumbnails. A metadata field is added to the IData type to support attachment metadata. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Heterogeneous changes across multiple concerns: repetitive translation entries, new component logic with null guards, significant table rendering updates with column span calculations in FileTable.tsx, conditional prop threading in FileTableRow.tsx, and type system extensions. The FileTable.tsx changes introduce calculated column span logic and conditional rendering patterns that diverge from straightforward updates, requiring careful review of interaction with existing layout constraints. Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (5)
src/types/shared.ts (2)
24-27: Make metadata fields required (call sites assume presence)Downstream code checks
meta.key === 'thumbnailLink'. Optionalkey/valueweakens safety. Make both required (and optionally narrow key type).-export interface IMetadata { - key?: string; - value?: string; -} +export type MetadataKey = 'thumbnailLink' | (string & {}); +export interface IMetadata { + key: MetadataKey; + value: string; +}
59-59: Sanitize/validate thumbnail URL before useIf
metadatawill carry external URLs, ensure the renderer only accepts http(s) and same-origin (or approved) hosts to avoid mixed content/SSRF/image beacons. Add a small helper to validate before rendering.Would you like me to add a safe
isTrustedThumbnailUrl(url: string)and wire it in AttachmentThumbnail?src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.module.css (1)
8-13: Prevent layout shift and improve perceived performanceAdd explicit width/height (or aspect-ratio) so cells reserve space before images load.
.thumbnail { - max-width: 100px; - max-height: 70px; + inline-size: 100px; + block-size: 70px; + aspect-ratio: 100 / 70; object-fit: contain; border-radius: 2px; } .thumbnailMobile { - max-width: 80px; - max-height: 60px; + inline-size: 80px; + block-size: 60px; + aspect-ratio: 80 / 60; object-fit: contain; border-radius: 2px; }Also applies to: 15-20
src/layout/FileUpload/FileUploadTable/FileTable.tsx (1)
90-93: Handle missing tagTitle gracefullyIf
tagTitleis undefined,<Lang id={undefined} />may render poorly. Fallback or skip the column.- {hasTag && !mobileView && ( + {hasTag && !mobileView && tagTitle && ( <th> <Lang id={tagTitle} /> </th> )}src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.tsx (1)
49-51: Remove redundant null check.The
thumbnailUrlwill always be a string since bothgetDataElementUrlandmakeUrlRelativeIfSameDomainalways return strings. This check is redundant and can be safely removed.Apply this diff to remove the unnecessary check:
const thumbnailUrl = makeUrlRelativeIfSameDomain(getDataElementUrl(instanceId, thumbnailDataElement.id, language)); - if (!thumbnailUrl) { - return null; - } - return (
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/language/texts/en.ts(1 hunks)src/language/texts/nb.ts(1 hunks)src/language/texts/nn.ts(1 hunks)src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.module.css(1 hunks)src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.tsx(1 hunks)src/layout/FileUpload/FileUploadTable/FileTable.tsx(1 hunks)src/layout/FileUpload/FileUploadTable/FileTableRow.tsx(1 hunks)src/types/shared.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.module.css
📄 CodeRabbit inference engine (CLAUDE.md)
Use CSS Modules for component styling and follow existing patterns in
*.module.cssfiles
Files:
src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.module.css
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Avoid usinganyand unnecessary type casts (as Type) in TypeScript; prefer precise typings and refactor existing casts/anys
For TanStack Query, use objects to manage query keys and functions, and centralize shared options viaqueryOptions
Files:
src/language/texts/nb.tssrc/types/shared.tssrc/language/texts/nn.tssrc/layout/FileUpload/FileUploadTable/AttachmentThumbnail.tsxsrc/layout/FileUpload/FileUploadTable/FileTableRow.tsxsrc/language/texts/en.tssrc/layout/FileUpload/FileUploadTable/FileTable.tsx
🧬 Code graph analysis (4)
src/types/shared.ts (2)
src/types/index.ts (1)
LooseAutocomplete(51-51)src/features/attachments/types.ts (1)
FileScanResult(1-1)
src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.tsx (5)
src/features/attachments/index.ts (3)
IAttachment(22-22)isAttachmentUploaded(29-31)UploadedAttachment(20-20)src/features/instance/InstanceContext.tsx (2)
useInstanceDataElements(139-143)useLaxInstanceId(58-62)src/features/language/LanguageProvider.tsx (1)
useCurrentLanguage(79-79)src/utils/urls/urlHelper.ts (1)
makeUrlRelativeIfSameDomain(107-117)src/utils/urls/appUrlHelper.ts (1)
getDataElementUrl(78-79)
src/layout/FileUpload/FileUploadTable/FileTableRow.tsx (13)
src/features/attachments/index.ts (2)
IAttachment(22-22)isAttachmentUploaded(29-31)src/utils/layout/hooks.ts (1)
useExternalItem(16-22)src/features/pdf/PDFWrapper.tsx (1)
usePdfModeActive(11-15)src/utils/attachmentsUtils.ts (1)
getSizeWithUnit(72-82)src/core/contexts/TaskOverrides.tsx (1)
useTaskOverrides(32-32)src/features/attachments/types.ts (1)
FileScanResults(3-8)src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.tsx (1)
AttachmentThumbnail(15-65)src/layout/Summary2/CommonSummaryComponents/EditButton.tsx (1)
EditButton(48-116)src/layout/FileUpload/FileUploadTable/AttachmentFileName.tsx (1)
AttachmentFileName(16-56)src/theme/altinnAppTheme.tsx (1)
AltinnPalette(2-25)src/components/AltinnLoader.tsx (1)
AltinnLoader(17-29)src/layout/FileUpload/FileUploadTable/FileTableRowContext.tsx (1)
useFileTableRow(12-12)src/layout/FileUpload/FileUploadTable/FileTableButtons.tsx (1)
FileTableButtons(26-109)
src/layout/FileUpload/FileUploadTable/FileTable.tsx (3)
src/features/attachments/index.ts (2)
IAttachment(22-22)isAttachmentUploaded(29-31)src/utils/formComponentUtils.ts (1)
atLeastOneTagExists(20-29)src/layout/FileUpload/FileUploadTable/FileTableRow.tsx (1)
FileTableRow(32-126)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Analyze (javascript)
- GitHub Check: Type-checks, eslint, unit tests and SonarCloud
- GitHub Check: Install
🔇 Additional comments (6)
src/language/texts/en.ts (1)
77-77: LGTMKey name and placement align with existing patterns.
src/layout/FileUpload/FileUploadTable/FileTable.tsx (1)
99-103: A11y: column header aligns to data cell semanticsThumb column is purely decorative; ensure cells use
role="presentation"or images have emptyaltwhen decorative. Verify AttachmentThumbnail sets appropriatealt.Would you like a quick sweep to enforce
alt=""andaria-hiddenwhere appropriate?src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.tsx (2)
1-13: LGTM!The imports and interface definition are well-structured and appropriate for the component's functionality.
53-64: LGTM!The rendering logic is well-implemented with proper accessibility (alt text), conditional styling for mobile/desktop views, and test ID for testing purposes.
src/layout/FileUpload/FileUploadTable/FileTableRow.tsx (2)
13-13: LGTM!The new import and optional
hasImagesprop are correctly integrated into the component interface.Also applies to: 29-29
32-39: LGTM!The
hasImagesprop is properly destructured and used for conditional rendering of the thumbnail cell.
| 'form_filler.file_uploader_delete_warning': 'Er du sikker på at du vil slette dette vedlegget?', | ||
| 'form_filler.file_uploader_delete_button_confirm': 'Ja, slett vedlegg', | ||
| 'form_filler.file_uploader_list_header_file_size': 'Filstørrelse', | ||
| 'form_filler.file_uploader_list_header_thumbnail': 'Thumbnail', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use native Bokmål term for clarity
Consider "Miniatyrbilde" instead of the English "Thumbnail" for consistency with other localized strings.
- 'form_filler.file_uploader_list_header_thumbnail': 'Thumbnail',
+ 'form_filler.file_uploader_list_header_thumbnail': 'Miniatyrbilde',📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'form_filler.file_uploader_list_header_thumbnail': 'Thumbnail', | |
| 'form_filler.file_uploader_list_header_thumbnail': 'Miniatyrbilde', |
🤖 Prompt for AI Agents
In src/language/texts/nb.ts around line 78, the localized label currently uses
the English word "Thumbnail"; replace its value with the native Bokmål term
"Miniatyrbilde" so the entry reads
'form_filler.file_uploader_list_header_thumbnail': 'Miniatyrbilde', ensuring
consistency with other Norwegian translations and preserving the same key.
| 'form_filler.file_uploader_delete_warning': 'Er du sikker på at du vil sletta dette vedlegget?', | ||
| 'form_filler.file_uploader_delete_button_confirm': 'Ja, slett vedlegg', | ||
| 'form_filler.file_uploader_list_header_file_size': 'Filstorleik', | ||
| 'form_filler.file_uploader_list_header_thumbnail': 'Thumbnail', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use native Nynorsk term for consistency
Consider "Miniatyrbilete" instead of "Thumbnail".
- 'form_filler.file_uploader_list_header_thumbnail': 'Thumbnail',
+ 'form_filler.file_uploader_list_header_thumbnail': 'Miniatyrbilete',📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'form_filler.file_uploader_list_header_thumbnail': 'Thumbnail', | |
| 'form_filler.file_uploader_list_header_thumbnail': 'Miniatyrbilete', |
🤖 Prompt for AI Agents
In src/language/texts/nn.ts around line 78, the value for
'form_filler.file_uploader_list_header_thumbnail' uses the English word
"Thumbnail"; replace it with the native Nynorsk term "Miniatyrbilete" (preserve
the same key, only change the string value) so the translation is consistent
with other Nynorsk entries.
| const thumbnailLink = | ||
| (attachment as UploadedAttachment)?.data?.metadata?.find( | ||
| (meta: { key: string; value: string }) => meta.key === 'thumbnailLink', | ||
| )?.value ?? null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove unnecessary type cast and use proper type.
The type cast (attachment as UploadedAttachment) is unnecessary because the type guard at line 22 already narrows attachment to UploadedAttachment. Additionally, the inline type { key: string; value: string } should be replaced with the IMetadata type from src/types/shared.ts.
As per coding guidelines.
Apply this diff to improve type safety:
- const thumbnailLink =
- (attachment as UploadedAttachment)?.data?.metadata?.find(
- (meta: { key: string; value: string }) => meta.key === 'thumbnailLink',
- )?.value ?? null;
+ const thumbnailLink = attachment.data.metadata?.find((meta) => meta.key === 'thumbnailLink')?.value ?? null;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const thumbnailLink = | |
| (attachment as UploadedAttachment)?.data?.metadata?.find( | |
| (meta: { key: string; value: string }) => meta.key === 'thumbnailLink', | |
| )?.value ?? null; | |
| const thumbnailLink = attachment.data.metadata?.find((meta) => meta.key === 'thumbnailLink')?.value ?? null; |
🤖 Prompt for AI Agents
In src/layout/FileUpload/FileUploadTable/AttachmentThumbnail.tsx around lines 27
to 30, remove the unnecessary (attachment as UploadedAttachment) cast and
replace the inline type annotation for metadata items with the shared IMetadata
type; use the already-established type guard (from line 22) so the code reads:
attachment.data.metadata.find((meta: IMetadata) => meta.key ===
'thumbnailLink')?.value ?? null. Also add an import for IMetadata from
src/types/shared.ts if it’s not already imported.
| //Check if any uploaded attachment has thumbnails | ||
| const hasImages = attachments.some((attachment) => { | ||
| if (!isAttachmentUploaded(attachment)) { | ||
| return false; | ||
| } | ||
| return attachment.data.metadata?.some((meta) => meta.key === 'thumbnailLink'); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard on value, not just key, when detecting thumbnails
Avoid showing a thumbnail column when thumbnailLink exists without a usable value.
- const hasImages = attachments.some((attachment) => {
+ const hasImages = attachments.some((attachment) => {
if (!isAttachmentUploaded(attachment)) {
return false;
}
- return attachment.data.metadata?.some((meta) => meta.key === 'thumbnailLink');
+ return attachment.data.metadata?.some((meta) => meta.key === 'thumbnailLink' && !!meta.value?.trim());
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| //Check if any uploaded attachment has thumbnails | |
| const hasImages = attachments.some((attachment) => { | |
| if (!isAttachmentUploaded(attachment)) { | |
| return false; | |
| } | |
| return attachment.data.metadata?.some((meta) => meta.key === 'thumbnailLink'); | |
| }); | |
| //Check if any uploaded attachment has thumbnails | |
| const hasImages = attachments.some((attachment) => { | |
| if (!isAttachmentUploaded(attachment)) { | |
| return false; | |
| } | |
| return attachment.data.metadata?.some((meta) => meta.key === 'thumbnailLink' && !!meta.value?.trim()); | |
| }); |
🤖 Prompt for AI Agents
In src/layout/FileUpload/FileUploadTable/FileTable.tsx around lines 54 to 61,
the current thumbnail detection only checks for metadata key 'thumbnailLink'
which can be present but empty; change the predicate to ensure the metadata
entry has a truthy/non-empty value (e.g., check meta.key === 'thumbnailLink' &&
Boolean(meta.value) or meta.value?.trim()) and keep the existing
uploaded-attachment guard so the hasImages computation only returns true when a
usable thumbnail URL exists.
| const calculateColSpan = () => { | ||
| if (mobileView) { | ||
| return hasImages ? 4 : 3; | ||
| } | ||
| return hasImages ? 7 : 6; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
colSpan is off; causes misalignment, especially on mobile
Compute colSpan from the same conditions as headers to avoid drift.
- const calculateColSpan = () => {
- if (mobileView) {
- return hasImages ? 4 : 3;
- }
- return hasImages ? 7 : 6;
- };
+ const calculateColSpan = () => {
+ let count = 1; // Name
+ if (!mobileView) count += 1; // File size
+ if (hasTag && !mobileView) count += 1; // Tag
+ if (!pdfModeActive && !mobileView) count += 1; // Status
+ if (hasImages) count += 1; // Thumbnail
+ if (!pdfModeActive) count += 1; // Delete/Action
+ return count;
+ };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const calculateColSpan = () => { | |
| if (mobileView) { | |
| return hasImages ? 4 : 3; | |
| } | |
| return hasImages ? 7 : 6; | |
| }; | |
| const calculateColSpan = () => { | |
| let count = 1; // Name | |
| if (!mobileView) count += 1; // File size | |
| if (hasTag && !mobileView) count += 1; // Tag | |
| if (!pdfModeActive && !mobileView) count += 1; // Status | |
| if (hasImages) count += 1; // Thumbnail | |
| if (!pdfModeActive) count += 1; // Delete/Action | |
| return count; | |
| }; |
🤖 Prompt for AI Agents
In src/layout/FileUpload/FileUploadTable/FileTable.tsx around lines 62 to 67,
the calculateColSpan function returns hardcoded numbers that drift from the
actual headers and cause misalignment on mobile; change it to compute colspan
from the same visible headers used to render the table (e.g., derive the headers
array based on mobileView and hasImages and return headers.length or
visibleHeaders.length) instead of returning fixed constants so colspan always
matches header count.
| {hasImages && ( | ||
| <td> | ||
| <AttachmentThumbnail | ||
| attachment={attachment} | ||
| mobileView={mobileView} | ||
| /> | ||
| </td> | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify column alignment with table headers.
The thumbnail cell is conditionally rendered when hasImages is true. Ensure that the FileTable component's header columns match this row structure across all conditional rendering paths (mobileView, hasTag, pdfModeActive, isSummary).
Run the following script to examine the FileTable component's header structure:
🏁 Script executed:
#!/bin/bash
# Description: Verify that FileTable.tsx headers align with FileTableRow cells
# Search for the FileTable component definition and header rendering
ast-grep --pattern $'export function FileTable($$$) {
$$$
}'
# Also search for thead or th elements in FileTable
rg -n -A 10 -B 2 '<thead|<th' src/layout/FileUpload/FileUploadTable/FileTable.tsxLength of output: 1624
I need to examine the full FileTableRow structure to verify column alignment. Let me retrieve the complete cell structure:
---
🏁 Script executed:
```shell
#!/bin/bash
# Extract lines 84-123 from FileTableRow.tsx to see all cells
sed -n '80,130p' src/layout/FileUpload/FileUploadTable/FileTableRow.tsx | cat -n
Length of output: 1760
Missing file size cell in FileTableRow causes column misalignment.
The FileTable component renders a file size header when !mobileView (lines 84-88 in FileTable.tsx), but FileTableRow lacks the corresponding cell. This causes table columns to misalign when the file size header is displayed, with data populating incorrect visual columns.
Add a file size cell to FileTableRow matching the FileTable header condition:
{!mobileView && <FileSizeCell readableSize={readableSize} />}
This should be inserted after the NameCell and before the FileTypeCell to match the column order in FileTable.
🤖 Prompt for AI Agents
In src/layout/FileUpload/FileUploadTable/FileTableRow.tsx around lines 100 to
107, the row is missing the file size cell which causes column misalignment with
the table header; add a conditional file size cell matching the header by
inserting `{!mobileView && <FileSizeCell readableSize={readableSize} />}`
immediately after the NameCell and before the FileTypeCell so the columns align
when `!mobileView`.
Description
Added metadata to the data element, along with functionality to store the metadata within it.
Additional functionality in app-lib (Altinn/app-lib-dotnet#1531) and localtest. Storage is in process.
Design in figma, pre development:


Mobileview:
Current layout:


Mobilview:
Related Issue(s)
#792
Verification/QA
kind/*andbackport*label to this PR for proper release notes groupingSummary by CodeRabbit
Release Notes