Skip to content

Commit

Permalink
fix(ffe-file-upload-react): håndter iOS bildenavn og forbedre tilgjen…
Browse files Browse the repository at this point in the history
…gelighet

- Legg til unik navngenerering for iOS kamerabilder som alltid får navnet 'image.jpg'
- Fiks ESLint feil med no-shadow ved å endre parameternavn
- Forbedre tilgjengelighet med tastatur og skjermleser støtte
- Legg til korrekt ARIA-roller og tastaturfunksjonalitet
  • Loading branch information
hagenek committed Jan 31, 2025
1 parent 7b65209 commit 26e39a7
Showing 1 changed file with 78 additions and 9 deletions.
87 changes: 78 additions & 9 deletions packages/ffe-file-upload-react/src/FileUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,66 @@ export interface FileUploadProps<Document> {
accept?: string;
}

/**
* En komponent for opplasting av filer, som kan brukes til å håndtere dokumentopplasting i ulike formater.
*
* Filer kan lastes opp på to måter:
* 1. Via den native filopplastingsdialogen i nettleseren
* 2. Ved å dra og slippe filer direkte i opplastingssonen
*
* Filhåndteringsflyt:
* 1. Brukeren velger filer via opplastingsdialog eller drag-and-drop
* 2. Nettleseren sender event-callback med info om valgte filer
* 3. Konsumenten må selv hente filinnholdet fra brukerens filsystem
* 4. Konsumenten oppretter et objekt med filinformasjon som sendes til komponenten
* 5. Validering av filstørrelse og type må håndteres av konsumenten
*
* Files-objektet er indeksert på filnavn og har følgende struktur:
* @example
* ```typescript
* const files = {
* fileBeingUploaded: {
* name: 'fileBeingUploaded',
* },
* fileWithError: {
* name: 'fileWithError',
* error: 'Feil filtype',
* },
* fileUploaded: {
* name: 'fileUploaded',
* document: {
* content: '(data)',
* },
* },
* };
* ```
*
* Merk:
* - Komponenten er tilstandsløs - all filhåndtering må gjøres i foreldrekomponenten
* - Duplikate filnavn støttes ikke
* - Ved opplasting fra iOS-kamera må filnavnhåndtering implementeres i konsumenten
*
* @template Document - Type for dokumentobjektet som kan legges til i files-objektet
* @param {Object} props - Komponentens props
* @param {string} props.id - ID for input-feltet
* @param {string} props.label - Tekst på knappen for filopplasting
* @param {Record<string, FileItemProps<Document>['file']>} props.files - Objekt med filer indeksert på filnavn
* @param {string} [props.cancelText] - Tekst på avbryt-knappen
* @param {string} [props.deleteText] - Tekst på slett-knappen
* @param {boolean} [props.multiple] - Om flere filer kan lastes opp samtidig
* @param {string} props.title - Tittel på modulen
* @param {string} props.infoText - Tekst i info-seksjonen
* @param {string} [props.infoSubText] - Undertekst i info-seksjonen
* @param {string} props.uploadTitle - Tittel på opplastingsseksjonen
* @param {string} props.uploadMicroText - Mikrotekst i opplastingsseksjonen
* @param {string} props.uploadSubText - Undertekst i opplastingsseksjonen
* @param {string} [props.accept] - Filtypespesifikasjon (f.eks. ".pdf")
* @param {(fileList: FileList | null) => void} props.onFilesDropped - Callback når filer droppes
* @param {(file: FileItemProps<Document>['file']) => void} props.onFileDeleted - Callback når en fil slettes
* @param {(fileList: FileList | null) => void} props.onFilesSelected - Callback når filer velges
*
* @returns {JSX.Element} FileUpload-komponenten
*/
export function FileUpload<Document>({
id,
label,
Expand All @@ -77,6 +137,20 @@ export function FileUpload<Document>({
const downloadIcon =
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Ik00ODAtMzQzLjUzOXEtNy4yMzEgMC0xMy40NjEtMi4zMDgtNi4yMzEtMi4zMDctMTEuODQ2LTcuOTIzTDMzMC4zMDktNDc4LjE1M3EtOC45MjMtOC45MjMtOC44MDctMjAuODg0LjExNS0xMS45NjEgOC44MDctMjEuMjY5IDkuMzA4LTkuMzA3IDIxLjM4NC05LjYxNSAxMi4wNzctLjMwOCAyMS4zODUgOWw3Ni45MjMgNzYuOTIzdi0zMDYuMDAxcTAtMTIuNzY5IDguNjE1LTIxLjM4NCA4LjYxNS04LjYxNiAyMS4zODQtOC42MTZ0MjEuMzg0IDguNjE2cTguNjE1IDguNjE1IDguNjE1IDIxLjM4NHYzMDYuMDAxbDc2LjkyMy03Ni45MjNxOC45MjMtOC45MjMgMjEuMTkyLTguODA4IDEyLjI2OS4xMTYgMjEuNTc3IDkuNDIzIDguNjkyIDkuMzA4IDguOTk5IDIxLjA3Ny4zMDggMTEuNzY5LTguOTk5IDIxLjA3Nkw1MDUuMzA3LTM1My43N3EtNS42MTUgNS42MTYtMTEuODQ2IDcuOTIzLTYuMjMgMi4zMDgtMTMuNDYxIDIuMzA4Wk0yNTIuMzA5LTE4MC4wMDFxLTMwLjMwOCAwLTUxLjMwOC0yMXQtMjEtNTEuMzA4di03OC40NjFxMC0xMi43NjkgOC42MTYtMjEuMzg0IDguNjE1LTguNjE1IDIxLjM4NC04LjYxNXQyMS4zODQgOC42MTVRMjQwLTM0My41MzkgMjQwLTMzMC43N3Y3OC40NjFxMCA0LjYxNiAzLjg0NiA4LjQ2MyAzLjg0NyAzLjg0NiA4LjQ2MyAzLjg0Nmg0NTUuMzgycTQuNjE2IDAgOC40NjMtMy44NDYgMy44NDYtMy44NDcgMy44NDYtOC40NjN2LTc4LjQ2MXEwLTEyLjc2OSA4LjYxNS0yMS4zODR0MjEuMzg0LTguNjE1cTEyLjc2OSAwIDIxLjM4NCA4LjYxNSA4LjYxNiA4LjYxNSA4LjYxNiAyMS4zODR2NzguNDYxcTAgMzAuMzA4LTIxIDUxLjMwOHQtNTEuMzA4IDIxSDI1Mi4zMDlaIi8+PC9zdmc+';

const triggerUploadFileNativeHandler = () => {
if (fileInputElement.current) {
fileInputElement.current.value = '';
fileInputElement.current.click();
}
};

const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
triggerUploadFileNativeHandler();
}
};

const handleFilesDropped = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsHover(false);
Expand All @@ -87,14 +161,6 @@ export function FileUpload<Document>({
onFileDeleted(files[event.currentTarget.id]);
};

const triggerUploadFileNativeHandler = () => {
// clear file input to trigger onChange when uploading same filename
if (fileInputElement.current) {
fileInputElement.current.value = '';
fileInputElement.current.click();
}
};

const handleFileSelected = (event: React.ChangeEvent<HTMLInputElement>) => {
onFilesSelected(event.target.files);
};
Expand Down Expand Up @@ -124,7 +190,6 @@ export function FileUpload<Document>({
</div>
</div>
)}
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div
className="ffe-file-upload__upload-section"
onDrop={handleFilesDropped}
Expand All @@ -133,6 +198,10 @@ export function FileUpload<Document>({
setIsHover(true);
}}
onDragLeave={() => setIsHover(false)}
role="button"
tabIndex={0}
onKeyDown={handleKeyDown}
aria-label={label}
>
<div
className={classNames(
Expand Down

0 comments on commit 26e39a7

Please sign in to comment.