From dba425219b785659e5ce6d6671452c0e6f92f04e Mon Sep 17 00:00:00 2001 From: phette23 Date: Fri, 18 Oct 2024 13:51:05 -0700 Subject: [PATCH] feat: improved community-specific custom field closes #8 this field is actually conditional on the selected community even if the community is added after the record is created --- .../templates/custom_fields/CommunityField.js | 41 ++++++++++++------- notes/configure.md | 9 ++-- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/assets/templates/custom_fields/CommunityField.js b/assets/templates/custom_fields/CommunityField.js index a622fd4..48724a3 100644 --- a/assets/templates/custom_fields/CommunityField.js +++ b/assets/templates/custom_fields/CommunityField.js @@ -1,23 +1,34 @@ // field is only visible when record is submitted to a particular community -import React, {Component} from "react" +import {useFormikContext} from "formik" +import React, {useState, useEffect} from "react" +// redux store getState().deposit.editorState.selectedCommunity is the only way to know the selected community +import {useSelector} from "react-redux" import {TextField} from "react-invenio-forms" -// ! Neither the values from useFormikContext nor the record from the props tell you what community the record is in. -// ! We can only look for ?community=id in the URL which is only present when the record is submitted to a particular community. -export class CommunityField extends Component { - render() { - const {fieldPath} = this.props - const community = new URLSearchParams(window.location.search).get("community") +const CommunityField = ({fieldPath}) => { + const {setFieldValue} = useFormikContext() + const community = useSelector(state => state.deposit.editorState.selectedCommunity?.slug) + const [active, setActive] = useState(community === 'test') + console.log("Initial state", community, active) - if (community === 'test') { - return + useEffect(() => { + console.log("useEffect state", community, active) + const isActive = (community === 'test') + const newKey = `communityfield-${community}` + if (active !== isActive) { + setActive(isActive) + // clear the field value when it becomes inactive + if (!isActive) setFieldValue(fieldPath, "") } - return null - } + }, [community, active, fieldPath, setFieldValue]) + + return } export default CommunityField diff --git a/notes/configure.md b/notes/configure.md index cedbf93..13b080c 100644 --- a/notes/configure.md +++ b/notes/configure.md @@ -89,11 +89,8 @@ Managed to build a custom "Academic Programs" field that uses a vocabulary, auto Our `ArchivesSeriesCF` is a custom field with our own structure (dictionary) and deposit form widget. We can build custom fields that do almost anything with this approach, which is not limited to the few types of custom fields Invenio provides. The biggest challenge is writing the React form widget. Helpful reference points are [Semantic UI React](https://react.semantic-ui.com/) and [react-invenio-forms](https://github.com/inveniosoftware/react-invenio-forms), specifically the [forms components](https://github.com/inveniosoftware/react-invenio-forms/tree/master/src/lib/forms). Sometimes limitations in one component force us to use a lower level one. Read the source code of components to understand how they work and what props they accept. For instance, `Dropdown` does not support change handlers, but it is a wrapper around `SelectField` which does. -[Formik](https://formik.org/docs/overview) is used for form state management and validation, but I never quite understood how it was affecting fields and its documentation did not prove fruitful. +[Formik](https://formik.org/docs/overview) is used for form state management and validation. You can `useFormikContext` in a functional React component to access the deposit form's current values, which allows you to make conditional fields. -**Questions** remaining for custom fields: +We have two demo custom fields `ConditionalField` and `CommunityField` which demonstrate conditional inputs that only show on the deposit form some of the time. `ConditionalField` checks if another metadata field has a particular value and disables the field if the condition is not met. `CommunityField` is analogous and shows only for submissions to a specific community. -- Can they be specific to a Community? -- Can they be specific to a Record type? - -The full record is passed as `this.props.record` to custom field components, but I'm not sure if changes to the record elsewhere on the deposit form rerender the component. Perhaps if the component's key is set to `key={this.props.record.metadata.resource_type.id + 'fieldname'}` it will rerender when the resource type changes (see how subseries key includes the series value in `ArchivesSeriesCF`). +The `props` passed to a custom field React components represent only the initial state of the record (e.g. they only have data for drafts or new versions being edited). In order to access the current and mutable state of the form, we have to use Formik and Redux.