Skip to content

Commit

Permalink
Apply Custom Fields page components for record
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladyslav-Velytskyi committed Mar 17, 2020
1 parent 3106950 commit 8bda292
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 5 deletions.
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ export { default as NoteEditPage } from './lib/Notes/NoteEditPage';
export { default as NotesSmartAccordion } from './lib/Notes/NotesSmartAccordion';
export { default as ViewCustomFieldsSettings } from './lib/CustomFields/pages/ViewCustomFieldsSettings';
export { default as EditCustomFieldsSettings } from './lib/CustomFields/pages/EditCustomFieldsSettings';
export { default as EditCustomFieldsRecord } from './lib/CustomFields/pages/EditCustomFieldsRecord';
export { default as ViewCustomFieldsRecord } from './lib/CustomFields/pages/ViewCustomFieldsRecord';
11 changes: 11 additions & 0 deletions lib/CustomFields/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import {
Checkbox,
TextField,
TextArea,
} from '@folio/stripes-components';

export const fieldTypes = {
TEXTBOX_SHORT: 'TEXTBOX_SHORT',
Expand Down Expand Up @@ -40,3 +45,9 @@ export const defaultFieldConfigs = {
}
},
};

export const fieldComponents = {
[fieldTypes.TEXTBOX_SHORT]: TextField,
[fieldTypes.TEXTBOX_LONG]: TextArea,
[fieldTypes.SINGLE_CHECKBOX]: Checkbox,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React from 'react';
import { PropTypes } from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { chunk } from 'lodash';
import {
Accordion,
Callout,
Col,
Row,
InfoPopover,
} from '@folio/stripes-components';

import { fieldComponents } from '../../constants';
import {
selectModuleId,
selectOkapiData,
} from '../../selectors';
import {
useCustomFieldsFetch,
useSectionTitleFetch,
useLoadingErrorCallout,
valuesValidation,
} from '../../utils';

const propTypes = {
accordionId: PropTypes.string.isRequired,
backendModuleId: PropTypes.string,
backendModuleName: PropTypes.string.isRequired,
columnCount: PropTypes.number,
entityType: PropTypes.string.isRequired,
expanded: PropTypes.bool.isRequired,
fieldComponent: PropTypes.node.isRequired,
okapi: PropTypes.shape({
tenant: PropTypes.string.isRequired,
token: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
}).isRequired,
onToggle: PropTypes.func.isRequired,
};

const defaultProps = {
columnCount: 4,
};

const EditCustomFieldsRecord = ({
accordionId,
onToggle,
expanded,
okapi,
backendModuleId,
backendModuleName,
entityType,
fieldComponent,
columnCount,
}) => {
const {
customFields,
customFieldsLoaded,
customFieldsFetchFailed,
} = useCustomFieldsFetch(okapi, backendModuleId, entityType);
const {
sectionTitle,
sectionTitleLoaded,
sectionTitleFetchFailed,
} = useSectionTitleFetch(okapi, backendModuleName.toUpperCase());
const { calloutRef } = useLoadingErrorCallout(customFieldsFetchFailed || sectionTitleFetchFailed);
const dataIsLoaded = sectionTitleLoaded && customFieldsLoaded;
const customFieldsIsVisible = customFields?.length > 0 && customFields?.some(customField => customField.visible);
const accordionIsVisible = dataIsLoaded && customFieldsIsVisible;

if (!accordionIsVisible) {
return null;
}

const Field = fieldComponent;
const formatedCustomFields = chunk(customFields, columnCount);
const columnWidth = 12 / columnCount;
const defaultLabel = <FormattedMessage id="stripes-smart-components.customFields.recordAccordion.defaultName" />;

return (
<>
<Accordion
open={expanded}
id={accordionId}
onToggle={onToggle}
label={sectionTitle.value || defaultLabel}
>
{
formatedCustomFields.map((row, i) => (
<Row key={i}>
{
row.map(customField => (
customField.visible ? (
<Col
data-test-record-edit-custom-field
key={customField.refId}
xs={columnWidth}
>
<Field
component={fieldComponents[customField.type]}
name={`customFields.${customField.refId}`}
required={customField.required}
label={
<>
{customField.name}
{customField.helpText && (
<InfoPopover
content={customField.helpText}
iconSize="small"
/>
)}
</>
}
validate={value => {
if (valuesValidation[customField.type]) {
return valuesValidation[customField.type](value, customField);
}

return null;
}
}
/>
</Col>
) : null
))
}
</Row>
))
}
</Accordion>
<Callout ref={calloutRef} />
</>
);
};

EditCustomFieldsRecord.propTypes = propTypes;
EditCustomFieldsRecord.defaultProps = defaultProps;

export default connect(
(state, ownProps) => ({
backendModuleId: selectModuleId(state, ownProps.backendModuleName),
okapi: selectOkapiData(state),
})
)(EditCustomFieldsRecord);
1 change: 1 addition & 0 deletions lib/CustomFields/pages/EditCustomFieldsRecord/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './EditCustomFieldsRecord';
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';
import { PropTypes } from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';

import { chunk } from 'lodash';

import {
Accordion,
Callout,
KeyValue,
NoValue,
Row,
Col,
} from '@folio/stripes-components';

import {
selectModuleId,
selectOkapiData,
} from '../../selectors';

import {
useCustomFieldsFetch,
useSectionTitleFetch,
useLoadingErrorCallout,
} from '../../utils';

const propTypes = {
accordionId: PropTypes.string.isRequired,
backendModuleId: PropTypes.string,
backendModuleName: PropTypes.string.isRequired,
columnCount: PropTypes.number,
customFieldsValues: PropTypes.object.isRequired,
entityType: PropTypes.string.isRequired,
expanded: PropTypes.bool.isRequired,
okapi: PropTypes.shape({
tenant: PropTypes.string.isRequired,
token: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
}).isRequired,
onToggle: PropTypes.func.isRequired,
};

const defaultProps = {
columnCount: 4,
};

const ViewCustomFieldsRecord = ({
accordionId,
onToggle,
expanded,
okapi,
backendModuleId,
backendModuleName,
entityType,
customFieldsValues,
columnCount,
}) => {
const {
customFields,
customFieldsLoaded,
customFieldsFetchFailed,
} = useCustomFieldsFetch(okapi, backendModuleId, entityType);
const {
sectionTitle,
sectionTitleLoaded,
sectionTitleFetchFailed,
} = useSectionTitleFetch(okapi, backendModuleName.toUpperCase());
const { calloutRef } = useLoadingErrorCallout(customFieldsFetchFailed || sectionTitleFetchFailed);
const accordionIsVisible = sectionTitleLoaded && customFieldsLoaded && customFields?.length > 0;

if (!accordionIsVisible) {
return null;
}

const formatedCustomFields = chunk(customFields, columnCount);
const defaultLabel = <FormattedMessage id="stripes-smart-components.customFields.recordAccordion.defaultName" />;

return (
<>
<Accordion
open={expanded}
id={accordionId}
onToggle={onToggle}
label={sectionTitle.value || defaultLabel}
>
{
formatedCustomFields.map((row, i) => (
<Row key={i}>
{
row.map(customField => (
customField.visible ? (
<Col
key={customField.refId}
xs={12 / columnCount}
>
<KeyValue
label={customField.name}
value={customFieldsValues[customField.refId] || <NoValue />}
/>
</Col>
) : null
))
}
</Row>
))
}
</Accordion>
<Callout ref={calloutRef} />
</>
);
};

ViewCustomFieldsRecord.propTypes = propTypes;
ViewCustomFieldsRecord.defaultProps = defaultProps;

export default connect(
(state, ownProps) => ({
backendModuleId: selectModuleId(state, ownProps.backendModuleName),
okapi: selectOkapiData(state),
})
)(ViewCustomFieldsRecord);
1 change: 1 addition & 0 deletions lib/CustomFields/pages/ViewCustomFieldsRecord/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ViewCustomFieldsRecord';
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@ const propTypes = {
redirectToEdit: PropTypes.func.isRequired,
};

const ViewCustomFieldsSettings = ({ redirectToEdit, okapi, backendModuleId, entityType, backendModuleName, permissions }) => {
const ViewCustomFieldsSettings = ({
redirectToEdit,
okapi,
backendModuleId,
entityType,
backendModuleName,
permissions
}) => {
const {
customFields,
customFieldsLoaded,
Expand Down
1 change: 1 addition & 0 deletions lib/CustomFields/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as makeRequest } from './makeRequest';
export { default as useCustomFieldsFetch } from './useCustomFieldsFetch';
export { default as useLoadingErrorCallout } from './useLoadingErrorCallout';
export { default as useSectionTitleFetch } from './useSectionTitleFetch';
export { valuesValidation } from './valuesValidation';
2 changes: 2 additions & 0 deletions lib/CustomFields/utils/useLoadingErrorCallout.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import React, { useEffect, useRef } from 'react';
import { FormattedMessage } from 'react-intl';

const useLoadingErrorCallout = hasError => {
console.log(hasError);
const calloutRef = useRef();
console.log(calloutRef);
const showErrorNotification = () => {
calloutRef.current.sendCallout({
type: 'error',
Expand Down
8 changes: 4 additions & 4 deletions lib/CustomFields/utils/useSectionTitleFetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ const useSectionTitleFetch = (okapi, moduleName) => {
const response = await makeOkapiRequest(url)({
method: 'GET',
});

/*
if (response.ok) {
const { configs } = await response.json();

console.log('hehe', configs);
setSectionTitle({ ...configs[0] });
setSectionTitleLoaded(true);
} else {
} else {*/
setSectionTitleFetchFailed(true);
}
//}
};

fetchCustomFields();
Expand Down
28 changes: 28 additions & 0 deletions lib/CustomFields/utils/valuesValidation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';

const TEXTBOX_SHORT_MAX_LENGTH = 150;
const TEXTBOX_LONG_MAX_LENGTH = 1500;

const textBoxValidation = length => (value, customField) => {
if (customField.required && !value) {
return <FormattedMessage
id="stripes-smart-components.customFields.fieldValue.required"
values={{ field: customField.name }}
/>;
}

if (value?.length > length) {
return <FormattedMessage
id="stripes-smart-components.customFields.fieldValue.length"
values={{ field: customField.name }}
/>;
}

return null;
};

export const valuesValidation = {
TEXTBOX_SHORT: textBoxValidation(TEXTBOX_SHORT_MAX_LENGTH),
TEXTBOX_LONG: textBoxValidation(TEXTBOX_LONG_MAX_LENGTH),
};
2 changes: 2 additions & 0 deletions translations/stripes-smart-components/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@
"customFields.fieldTypes.SINGLE_CHECKBOX": "Checkbox",
"customFields.fieldLabel": "Field label",
"customFields.fieldFormat": "Field format",
"customFields.fieldValue.required": "{field} is required",
"customFields.fieldValue.length": "{field} character limit has been exceeded. Please revise.",
"customFields.helperText": "Help text",
"customFields.helperText.description": "This will show under an information icon",
"customFields.required": "Required",
Expand Down

0 comments on commit 8bda292

Please sign in to comment.