Skip to content

Commit

Permalink
Implement a full-fledged plot search form section editor
Browse files Browse the repository at this point in the history
The editor allows the user to specify all data necessary for
setting up a customisable, but structurally coherent form for
any specific plot search. This includes automatic identifier
specification as well as protection of specific operationally
important fields and sections containing said fields.
  • Loading branch information
EmiliaMakelaVincit committed Oct 10, 2023
1 parent ed0eaf2 commit 8c58ba9
Show file tree
Hide file tree
Showing 24 changed files with 1,898 additions and 275 deletions.
14 changes: 13 additions & 1 deletion src/application/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export type FormSection = {
id: number;
identifier: string;
title: string;
title_fi: string;
title_en: string | null;
title_sv: string | null;
visible: boolean;
sort_order: number;
add_new_allowed: boolean;
Expand All @@ -50,7 +53,13 @@ export type FormField = {
identifier: string;
type: number;
label: string;
hint_text?: string;
label_fi: string;
label_en: string | null;
label_sv: string | null;
hint_text: string | null;
hint_text_fi: string | null;
hint_text_en: string | null;
hint_text_sv: string | null;
enabled: boolean;
required: boolean;
validation?: string | null;
Expand All @@ -64,6 +73,9 @@ export type FormField = {
export type FormFieldChoice = {
id: number;
text: string;
text_fi: string;
text_en: string | null;
text_sv: string | null;
value: string;
action?: string | null;
has_text_input: boolean;
Expand Down
41 changes: 27 additions & 14 deletions src/components/button/IconButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,34 @@ type Props = {
style?: Object,
title?: string,
type?: string,
id?: string,
}

const IconButton = ({children, className, disabled, onClick, style, title, type = 'button'}: Props) => {
return (
<button
className={classNames('icon-button-component', className)}
onClick={onClick}
disabled={disabled}
style={style}
title={title}
type={type}
>
{children}
</button>
);
};
const IconButton = (React.forwardRef<Props, any>(
({
children,
className,
disabled,
onClick,
style,
title,
id,
type = 'button',
}: Props, ref) => {
return (
<button
className={classNames('icon-button-component', className)}
onClick={onClick}
disabled={disabled}
style={style}
title={title}
type={type}
id={id}
ref={ref}
>
{children}
</button>
);
}): React$AbstractComponent<Props, 'button'>);

export default IconButton;
50 changes: 33 additions & 17 deletions src/components/collapse/Collapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Props = {
showTitleOnOpen?: boolean,
tooltipStyle?: Object,
uiDataKey?: ?string,
isOpen?: boolean,
}

type State = {
Expand All @@ -53,10 +54,13 @@ class Collapse extends PureComponent<Props, State> {
};

state: State = {
contentHeight: this.props.defaultOpen ? null : 0,
contentHeight: (this.props.isOpen !== undefined
? this.props.isOpen
: this.props.defaultOpen
) ? null : 0,
isCollapsing: false,
isExpanding: false,
isOpen: this.props.defaultOpen,
isOpen: this.props.isOpen !== undefined ? this.props.isOpen : this.props.defaultOpen,
};

setComponentRef: (any) => void = (el) => {
Expand All @@ -76,6 +80,10 @@ class Collapse extends PureComponent<Props, State> {
}

componentDidUpdate(prevProps: Object, prevState: Object) {
if (this.props.isOpen !== undefined && this.props.isOpen !== this.state.isOpen) {
this.handleToggleStateChange(this.props.isOpen);
}

if ((this.state.isOpen && !this.state.contentHeight) ||
(this.state.isOpen !== prevState.isOpen)) {
this.calculateHeight();
Expand All @@ -101,35 +109,43 @@ class Collapse extends PureComponent<Props, State> {
}

handleToggle: (SyntheticEvent<HTMLAnchorElement>) => void = (e) => {
const {onToggle} = this.props;
const {onToggle, isOpen: externalIsOpen} = this.props;
const {isOpen} = this.state;
const target = e.currentTarget;
const tooltipEl = ReactDOM.findDOMNode(this.tooltip);

const isExternallyControlled = externalIsOpen !== undefined;

if (!tooltipEl ||
(tooltipEl && target !== tooltipEl && !tooltipEl.contains(target))) {
if(isOpen) {
this.setState({
isCollapsing: true,
isExpanding: false,
isOpen: false,
});
} else {
this.setState({
isCollapsing: false,
isExpanding: true,
isOpen: true,
});
if (!isExternallyControlled) {
this.handleToggleStateChange(!isOpen);
}

if(onToggle) {
if (onToggle) {
onToggle(!isOpen);
}
}
};

handleToggleStateChange: (boolean) => void = (newIsOpen) => {
if (newIsOpen) {
this.setState({
isCollapsing: false,
isExpanding: true,
isOpen: true,
});
} else {
this.setState({
isCollapsing: true,
isExpanding: false,
isOpen: false,
});
}
}

handleKeyDown: (SyntheticKeyboardEvent<HTMLAnchorElement>) => void = (e) => {
if(e.keyCode === 13) {
if (e.keyCode === 13) {
e.preventDefault();
this.handleToggle(e);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/FieldTypeMultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const FieldTypeMultiSelect = ({
options={options}
onBlur={handleBlur}
onSelectedChanged={onChange}
selected={value}
selected={value instanceof Array ? value : []}
disabled={disabled}
isLoading={isLoading}
/>
Expand Down
1 change: 1 addition & 0 deletions src/components/form/FormField.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ type Props = {
filterOption?: Function,
invisibleLabel?: boolean,
isLoading?: boolean,
isMulti?: boolean,
language?: string,
name: string,
onBlur?: Function,
Expand Down
18 changes: 18 additions & 0 deletions src/components/icons/MoveDownIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @flow
import React from 'react';
import classNames from 'classnames';

type Props = {
className?: string,
}

const MoveDownIcon = ({className}: Props): React$Element<'svg'> =>
<svg className={classNames('icons', 'icons__move-down', className)} viewBox="11 0 16 27">
<title>Siirrä alas</title>
<g id="icon_sort2" stroke="none" strokeWidth="1" fillRule="evenodd">
<polygon id="Path-Copy-2" fillRule="nonzero" transform="translate(19.060000, 13.330000) rotate(-180.000000) translate(-19.060000, -13.330000) " points="19.06 5 24.12 10.41 20.19 10.41 20.19 21.66 17.93 21.66 17.93 10.41 14 10.41"></polygon>
</g>
</svg>;


export default MoveDownIcon;
17 changes: 17 additions & 0 deletions src/components/icons/MoveUpIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// @flow
import React from 'react';
import classNames from 'classnames';

type Props = {
className?: string,
}

const MoveUpIcon = ({className}: Props): React$Element<'svg'> =>
<svg className={classNames('icons', 'icons__move-up', className)} viewBox="0 0 16 27" version="1.1">
<title>Siirrä ylös</title>
<g id="icon_sort2" stroke="none" strokeWidth="1" fillRule="evenodd">
<polygon id="Path" fillRule="nonzero" points="8.06 5 13.12 10.41 9.19 10.41 9.19 21.66 6.93 21.66 6.93 10.41 3 10.41"></polygon>
</g>
</svg>;

export default MoveUpIcon;
2 changes: 1 addition & 1 deletion src/components/icons/TrashIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type Props = {
className?: string,
}

const TrashIcon = ({className}: Props) =>
const TrashIcon = ({className}: Props): React$Element<'svg'> =>
<svg className={classNames('icons', 'icons__trash', className)} viewBox="0 0 20 20">
<title>Poista</title>
<g stroke="none" strokeWidth="1" fill="none">
Expand Down
12 changes: 12 additions & 0 deletions src/components/icons/_icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@
stroke: none !important;
}

&.icons__move-up,
&.icons__move-down {
width: rem-calc(16px);

&.icon-medium {
width: rem-calc(12px);
}
&.icon-small {
width: rem-calc(9px);
}
}

&.icon-medium {
height: rem-calc(20px);
width: rem-calc(20px);
Expand Down
2 changes: 1 addition & 1 deletion src/components/multi-select/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const MultiSelect = ({
hasSelectAll = true,
onSelectedChanged,
valueRenderer,
}: Props) => {
}: Props): React$Node => {
const getSelectedText = () => {
const selectedOptions = selected
.map(s => options.find(o => o.value === s));
Expand Down
5 changes: 5 additions & 0 deletions src/enums.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,11 @@ export const ConfirmationModalTexts = {
LABEL: 'Haluatko varmasti poistaa kentän?',
TITLE: 'Poista kenttä',
},
DELETE_SECTION_SUBSECTION: {
BUTTON: 'Poista aliosio',
LABEL: 'Haluatko varmasti poistaa aliosion?',
TITLE: 'Poista aliosio',
},
DELETE_APPLICATION_TARGET_PROPOSED_MANAGEMENT: {
BUTTON: DELETE_MODAL_BUTTON_TEXT,
LABEL: 'Haluatko varmasti poistaa hallinta- ja rahoitusmuotoehdotuksen?',
Expand Down
12 changes: 12 additions & 0 deletions src/plotSearch/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ import type {
ResetPlanUnitDecisionsAction,
ShowEditModeAction,
TemplateFormsNotFoundAction,
ClearSectionEditorCollapseStatesAction,
SetSectionEditorCollapseStateAction,
InitializeSectionEditorCollapseStatesAction,
} from '$src/plotSearch/types';

export const fetchAttributes = (): FetchAttributesAction =>
Expand Down Expand Up @@ -238,3 +241,12 @@ export const directReservationLinkCreated = (): DirectReservationLinkCreatedActi

export const directReservationLinkCreationFailed = (payload: any): DirectReservationLinkCreationFailedAction =>
createAction('mvj/plotSearch/DIRECT_RESERVATION_LINK_CREATION_FAILED')(payload);

export const clearSectionEditorCollapseStates = (): ClearSectionEditorCollapseStatesAction =>
createAction('mvj/plotSearch/CLEAR_SECTION_EDITOR_COLLAPSE_STATES')();

export const setSectionEditorCollapseState = (key: string, isOpen: boolean): SetSectionEditorCollapseStateAction =>
createAction('mvj/plotSearch/SET_SECTION_EDITOR_COLLAPSE_STATE')({key, state: isOpen});

export const initializeSectionEditorCollapseStates = (states: {[key: string]: boolean}): InitializeSectionEditorCollapseStatesAction =>
createAction('mvj/plotSearch/INITIALIZE_SECTION_EDITOR_COLLAPSE_STATES')(states);
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ type State = {
class ApplicationEdit extends PureComponent<Props, State> {
state = {
isModalOpen: false,
modalSectionIndex: 0,
modalSectionIndex: -1,
}

componentDidMount() {
Expand Down Expand Up @@ -271,18 +271,6 @@ class ApplicationEdit extends PureComponent<Props, State> {
uiDataKey={getUiDataLeaseKey(ApplicationFieldPaths.NAME)}
/>
</TitleH3>
{/* <FieldArray
component={renderApplicant}
disabled={false}
formName={FormNames.PLOT_SEARCH_APPLICATION}
name={'applicants'}
/>
<FieldArray
component={renderTarget}
disabled={false}
formName={FormNames.PLOT_SEARCH_APPLICATION}
name={'targets'}
/>*/}
</WhiteBox>
</Collapse>
{formData.sections.map((section, index) =>
Expand Down
Loading

0 comments on commit 8c58ba9

Please sign in to comment.