From 4dc2757f05f48463a56ebc5c3800577b452478eb Mon Sep 17 00:00:00 2001 From: Carlin MacKenzie Date: Wed, 12 Feb 2025 17:44:15 +0100 Subject: [PATCH 1/3] deposit: add anchor IDs to sections --- src/lib/forms/widgets/custom_fields/CustomFields.js | 2 ++ src/lib/forms/widgets/custom_fields/DiscoverFieldsSection.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/forms/widgets/custom_fields/CustomFields.js b/src/lib/forms/widgets/custom_fields/CustomFields.js index abe69417..5980f4dc 100644 --- a/src/lib/forms/widgets/custom_fields/CustomFields.js +++ b/src/lib/forms/widgets/custom_fields/CustomFields.js @@ -85,12 +85,14 @@ export class CustomFields extends Component { displaySection = true, section: sectionName, } = section; + const sectionId = sectionName.toLowerCase().replace(/\s+/g, '-') + "-section"; return displaySection ? ( {fields} diff --git a/src/lib/forms/widgets/custom_fields/DiscoverFieldsSection.js b/src/lib/forms/widgets/custom_fields/DiscoverFieldsSection.js index cf0de41f..8c8cf997 100644 --- a/src/lib/forms/widgets/custom_fields/DiscoverFieldsSection.js +++ b/src/lib/forms/widgets/custom_fields/DiscoverFieldsSection.js @@ -100,7 +100,7 @@ export class DiscoverFieldsSection extends Component { ]; return ( - + {sections.map(({ fields, paths, ...sectionConfig }) => { const recordCustomFields = this.getFieldsWithValues(fields); if (_isEmpty(recordCustomFields)) { From 99c86ba2db300d7da6405c9924b7afa757b44bb5 Mon Sep 17 00:00:00 2001 From: Fatimah Zulfiqar Date: Thu, 6 Mar 2025 13:44:09 +0100 Subject: [PATCH 2/3] forms: added id in custom and path in discoverable fields --- src/lib/forms/widgets/custom_fields/CustomFields.js | 2 +- .../forms/widgets/custom_fields/DiscoverFieldsSection.js | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib/forms/widgets/custom_fields/CustomFields.js b/src/lib/forms/widgets/custom_fields/CustomFields.js index 5980f4dc..edb3cf20 100644 --- a/src/lib/forms/widgets/custom_fields/CustomFields.js +++ b/src/lib/forms/widgets/custom_fields/CustomFields.js @@ -84,8 +84,8 @@ export class CustomFields extends Component { paths, displaySection = true, section: sectionName, + id: sectionId, } = section; - const sectionId = sectionName.toLowerCase().replace(/\s+/g, '-') + "-section"; return displaySection ? ( value.key), ...recordFields, ]; + const tempFieldsPaths = tempFields.map((item) => item.key); return ( - + {sections.map(({ fields, paths, ...sectionConfig }) => { const recordCustomFields = this.getFieldsWithValues(fields); if (_isEmpty(recordCustomFields)) { From 8524ed7c0949bbfec50a15284a12e12ced4fda2e Mon Sep 17 00:00:00 2001 From: Fatimah Zulfiqar Date: Thu, 6 Mar 2025 13:44:52 +0100 Subject: [PATCH 3/3] forms: added error count label in accordion --- src/lib/forms/AccordionField.js | 119 ++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/src/lib/forms/AccordionField.js b/src/lib/forms/AccordionField.js index 326f5fea..ee75f783 100644 --- a/src/lib/forms/AccordionField.js +++ b/src/lib/forms/AccordionField.js @@ -1,56 +1,85 @@ // This file is part of React-Invenio-Forms // Copyright (C) 2020 CERN. // Copyright (C) 2020 Northwestern University. -// // React-Invenio-Forms is free software; you can redistribute it and/or modify it // under the terms of the MIT License; see LICENSE file for more details. import React, { Component, useState } from "react"; import PropTypes from "prop-types"; import { Field, FastField } from "formik"; -import { Accordion, Container, Icon } from "semantic-ui-react"; +import { Accordion, Container, Icon, Label } from "semantic-ui-react"; import _omit from "lodash/omit"; import _get from "lodash/get"; export class AccordionField extends Component { - hasError(errors, initialValues = undefined, values = undefined) { + hasError = (errors, initialValues = undefined, values = undefined) => { const { includesPaths } = this.props; for (const errorPath in errors) { for (const subPath in errors[errorPath]) { const path = `${errorPath}.${subPath}`; if ( _get(initialValues, path, "") === _get(values, path, "") && - includesPaths.includes(`${errorPath}.${subPath}`) - ) + includesPaths.includes(path) + ) { return true; + } } } return false; - } + }; + + flattenErrors = (errors) => { + let flattened = {}; + + const recurse = (obj, path = "") => { + if (Array.isArray(obj)) { + // For arrays, treat each item individually, appending the index to the path + obj.forEach((item, index) => { + recurse(item, `${path}[${index}]`); + }); + } else if (typeof obj === "object" && obj !== null) { + // If it's an object, recursively traverse it + for (const key in obj) { + const newPath = path ? `${path}.${key}` : key; + recurse(obj[key], newPath); + } + } else { + // If it's a value, save the path and value + flattened[path] = obj; + } + }; + + recurse(errors); + return flattened; + }; + + countErrors = (errors, includePaths) => { + const flattenedErrors = this.flattenErrors(errors); + let count = 0; + + // Count matching paths from includePaths + for (const path in flattenedErrors) { + if (includePaths.some((includePath) => path.startsWith(includePath))) { + count++; + } + } + + return count; + }; renderAccordion = (props) => { const { form: { errors, status, initialErrors, initialValues, values }, } = props; + const { includesPaths, label, children, active } = this.props; - // eslint-disable-next-line no-unused-vars - const { label, children, active, ...ui } = this.props; - const uiProps = _omit({ ...ui }, ["optimized", "includesPaths"]); - const hasError = status - ? this.hasError(status) - : this.hasError(errors) || this.hasError(initialErrors, initialValues, values); - const panels = [ - { - key: `panel-${label}`, - title: { - content: label, - }, - content: { - content: {children}, - }, - }, - ]; + const uiProps = _omit(this.props, ["optimized", "includesPaths"]); + const hasError = + this.hasError(errors, initialValues, values) || this.hasError(initialErrors); + const errorCount = + this.countErrors(errors, includesPaths) || + this.countErrors(initialErrors, includesPaths); const errorClass = hasError ? "error secondary" : ""; const [activeIndex, setActiveIndex] = useState(active ? 0 : -1); @@ -64,34 +93,34 @@ export class AccordionField extends Component { className={`invenio-accordion-field ${errorClass}`} {...uiProps} > - {panels.map((panel, index) => ( - - { - if (e.key === "Enter" || e.key === " ") { - handleTitleClick(e, { index }); - } - }} - tabIndex={0} - > - {panel.title.content} - - - - {panel.content.content} - - - ))} + { + if (e.key === "Enter" || e.key === " ") { + handleTitleClick(e, { index: 0 }); + } + }} + tabIndex={0} + > + {label} + {errorCount > 0 && ( + + )} + + + + {children} + ); }; render() { const { optimized } = this.props; - const FormikField = optimized ? FastField : Field; return ; }