Skip to content

Commit

Permalink
[DRAFT][3173] Add a widget image to help user to select a image
Browse files Browse the repository at this point in the history
Missing parts:

Backend:
 - widget preview
 - junit test for mutations
 - other tests?

Frontend:
 - Link tab
 - cypress integration test
 - other tests?

Signed-off-by: Jerome Gout <[email protected]>
  • Loading branch information
jerome-obeo committed Mar 5, 2024
1 parent bb2aa05 commit dd3796c
Show file tree
Hide file tree
Showing 81 changed files with 7,646 additions and 185 deletions.
5 changes: 5 additions & 0 deletions packages/core/frontend/sirius-components-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export * from './modals/confirmation/useConfirmationDialog';
export * from './modals/confirmation/useConfirmationDialog.types';
export * from './modals/share-representation/ShareRepresentationModal';
export * from './modals/share-representation/ShareRepresentationModal.types';
export * from './modals/upload/FileUpload';
export * from './modals/upload/FileUpload.types';
export * from './modals/upload/ImageUploadForm';
export * from './modals/upload/ImageUploadForm.types';
export * from './modals/upload/sendFile';
export * from './selection/SelectionContext';
export * from './selection/SelectionContext.types';
export * from './selection/useSelection';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const initialState: FileUploadState = {
file: null,
message: DEFAULT_MESSAGE,
};
export const FileUpload = ({ onFileSelected, 'data-testid': dataTestId }: FileUploadProps) => {
export const FileUpload = ({ onFileSelected, 'data-testid': dataTestId, disabled }: FileUploadProps) => {
const styles = useFileUploadViewStyles();
const fileInput = React.createRef<HTMLInputElement>();

Expand All @@ -67,13 +67,14 @@ export const FileUpload = ({ onFileSelected, 'data-testid': dataTestId }: FileUp

// Update the file selection.
const onFileInputChange = () => {
const { files } = fileInput.current;
let file: File = null;
if (files.length === 1) {
file = files[0];
let file: File | null = null;
if (fileInput.current) {
const { files } = fileInput.current;
if (files?.length === 1 && files[0]) {
file = files[0];
onFileSelected(file);
}
}

onFileSelected(file);
setState((prevState) => {
return { ...prevState, file };
});
Expand All @@ -89,6 +90,7 @@ export const FileUpload = ({ onFileSelected, 'data-testid': dataTestId }: FileUp
ref={fileInput}
onChange={onFileInputChange}
data-testid={dataTestId}
disabled={disabled}
/>

<Typography className={styles.message}>{message}</Typography>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
export interface FileUploadProps {
onFileSelected: (file: File) => void;
'data-testid'?: string;
disabled?: boolean;
}

export interface FileUploadState {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/

import { useEffect, useState } from 'react';
import FormGroup from '@material-ui/core/FormGroup';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core';
import { FileUpload } from './FileUpload';
import { ImageUploadFormProps, LabeledFile } from './ImageUploadForm.types';

const useUploadImageModalStyle = makeStyles((theme) => ({
form: {
display: 'flex',
flexDirection: 'column',
paddingTop: theme.spacing(1),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
'& > *': {
marginBottom: theme.spacing(2),
},
},
}));
export const ImageUploadForm = ({ onChange, disabled, onSubmit }: ImageUploadFormProps) => {
const classes = useUploadImageModalStyle();
const [state, setState] = useState<LabeledFile>({ label: '', file: null });

useEffect(() => {
onChange(state);
}, [state]);

const handleFileSelected = (file: File) => {
setState((prevState) => ({ ...prevState, file }));
};

const handleLabelChanged = (event) => {
setState((prevState) => ({ ...prevState, label: event.target.value }));
};

const handleSubmit = (event) => {
event.preventDefault();
if (onSubmit) {
onSubmit();
}
};

return (
<form onSubmit={handleSubmit} id="imageupload-form-id" encType="multipart/form-data" className={classes.form}>
<TextField
label="Label"
name="label"
value={state.label}
placeholder="Label for the image"
data-testid="imageupload-label"
inputProps={{ 'data-testid': 'label-input' }}
autoFocus={true}
onChange={handleLabelChanged}
disabled={disabled}
/>
<FormGroup>
<FileUpload disabled={disabled} onFileSelected={handleFileSelected} data-testid="imageupload-file" />
</FormGroup>
</form>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/

export interface ImageUploadFormProps {
onChange: (file: LabeledFile) => void;
onSubmit?: () => void;
disabled?: boolean;
}

export interface LabeledFile {
label: string;
file: File | null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@ export const sendFile = async (httpOrigin: string, query: string, variables: any
};

const getCookie = (name: string): string => {
let cookieValue: string = null;
let cookieValue: string | null = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
const cookie = cookies[i]?.trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === name + '=') {
if (cookie?.substring(0, name.length + 1) === name + '=') {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
return cookieValue || '';
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.forms.dto;

import java.util.UUID;

import org.eclipse.sirius.components.collaborative.forms.api.IFormInput;

/**
* The input object for the image picker image addition mutation.
*
* @author Jerome Gout
*/
public record AddImageInput(UUID id, String representationId, String editingContextId, String imagePickerId, String imageId) implements IFormInput {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.forms.dto;

import java.util.UUID;

import org.eclipse.sirius.components.collaborative.forms.api.IFormInput;

/**
* The input object for the image picker image deletion mutation.
*
* @author Jerome Gout
*/
public record RemoveImageInput(UUID id, String representationId, String editingContextId, String imagePickerId, String imageId) implements IFormInput {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.forms.handlers;

import java.util.Objects;

import org.eclipse.sirius.components.collaborative.api.ChangeDescription;
import org.eclipse.sirius.components.collaborative.api.ChangeKind;
import org.eclipse.sirius.components.collaborative.api.Monitoring;
import org.eclipse.sirius.components.collaborative.forms.api.IFormEventHandler;
import org.eclipse.sirius.components.collaborative.forms.api.IFormInput;
import org.eclipse.sirius.components.collaborative.forms.api.IFormQueryService;
import org.eclipse.sirius.components.collaborative.forms.dto.AddImageInput;
import org.eclipse.sirius.components.collaborative.forms.messages.ICollaborativeFormMessageService;
import org.eclipse.sirius.components.core.api.ErrorPayload;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.core.api.SuccessPayload;
import org.eclipse.sirius.components.forms.AbstractWidget;
import org.eclipse.sirius.components.forms.Form;
import org.eclipse.sirius.components.forms.ImagePicker;
import org.eclipse.sirius.components.representations.Failure;
import org.eclipse.sirius.components.representations.IStatus;
import org.eclipse.sirius.components.representations.Success;
import org.springframework.stereotype.Service;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import reactor.core.publisher.Sinks.Many;
import reactor.core.publisher.Sinks.One;

/**
* The handler of the image picker add image event.
*
* @author Jerome Gout
*/
@Service
public class AddImageEventHandler implements IFormEventHandler {
private final ICollaborativeFormMessageService messageService;

private final Counter counter;

private final IFormQueryService formQueryService;

public AddImageEventHandler(IFormQueryService formQueryService, ICollaborativeFormMessageService messageService, MeterRegistry meterRegistry) {
this.formQueryService = Objects.requireNonNull(formQueryService);
this.messageService = Objects.requireNonNull(messageService);

// @formatter:off
this.counter = Counter.builder(Monitoring.EVENT_HANDLER)
.tag(Monitoring.NAME, this.getClass().getSimpleName())
.register(meterRegistry);
// @formatter:on
}

@Override
public boolean canHandle(IFormInput formInput) {
return formInput instanceof AddImageInput;
}

@Override
public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDescriptionSink, IEditingContext editingContext, Form form, IFormInput formInput) {
this.counter.increment();
String message = this.messageService.invalidInput(formInput.getClass().getSimpleName(), AddImageInput.class.getSimpleName());
IPayload payload = new ErrorPayload(formInput.id(), message);
ChangeDescription changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, formInput.representationId(), formInput);

if (formInput instanceof AddImageInput input) {

var optionalImagePicker = this.formQueryService.findWidget(form, input.imagePickerId());

IStatus status;
if (optionalImagePicker.map(AbstractWidget::isReadOnly).filter(Boolean::booleanValue).isPresent()) {
status = new Failure("Read-only widget cannot be edited");
} else {
var handler = optionalImagePicker.filter(ImagePicker.class::isInstance)
.map(ImagePicker.class::cast)
.map(ImagePicker::getNewValueHandler);
status = handler.map(h -> h.apply(input.imageId())).orElse(new Failure(""));
}
if (status instanceof Success success) {
changeDescription = new ChangeDescription(success.getChangeKind(), formInput.representationId(), formInput, success.getParameters());
payload = new SuccessPayload(formInput.id(), success.getMessages());
} else if (status instanceof Failure failure) {
payload = new ErrorPayload(formInput.id(), failure.getMessages());
}
}

changeDescriptionSink.tryEmitNext(changeDescription);
payloadSink.tryEmitValue(payload);
}

}
Loading

0 comments on commit dd3796c

Please sign in to comment.