diff --git a/__tests__/components/editor/InputLookupQA.test.js b/__tests__/components/editor/InputLookupQA.test.js index 8e071e337..00a798312 100644 --- a/__tests__/components/editor/InputLookupQA.test.js +++ b/__tests__/components/editor/InputLookupQA.test.js @@ -1,6 +1,8 @@ // Copyright 2018 Stanford University see Apache2.txt for license +import 'jsdom-global/register' import React from 'react' import { shallow } from 'enzyme' +import { mount } from "enzyme" import InputLookup from '../../../src/components/editor/InputLookupQA' const plProps = { @@ -23,7 +25,37 @@ const plProps = { "propertyURI": "http://id.loc.gov/ontologies/bflc/target", "propertyLabel": "Name Lookup" } -} +}; +const p2Props = { + "propertyTemplate": + { + "mandatory": "false", + "repeatable": "true", + "type": "lookup", + "resourceTemplates": [], + "valueConstraint": { + "valueTemplateRefs": [], + "useValuesFrom": [ + 'lookupQaLocNames', + 'lookupQaLocSubjects' + ], + "valueDataType": { + "dataTypeURI": "http://id.loc.gov/ontologies/bibframe/Agent" + }, + "defaults": [] + }, + "propertyURI": "http://id.loc.gov/ontologies/bflc/target", + "propertyLabel": "Name Lookup" + } +}; + +const multipleResults = [{"authLabel":"Person", + "authURI":"PersonURI", + "body":[{"uri":"puri","label":"plabel"}]}, + {"authLabel":"Subject", + "authURI":"SubjectURI", + "body":[{"uri":"suri","label":"slabel"}] + }]; describe('', () => { // our mock formData function to replace the one provided by mapDispatchToProps @@ -82,4 +114,35 @@ describe('', () => { const propertyRemark = wrapper.find('label > PropertyRemark') expect(propertyRemark).toBeTruthy() }) + + //Institute wrapper with multiple lookup options + const multipleWrapper = shallow() + it('should pass multiple lookup results in state with search event', () => { + const event = (wrap) => { + wrap.setState({options: multipleResults}) + } + multipleWrapper.find('#lookupComponent').simulate('search', event(multipleWrapper)) + expect(multipleWrapper.state().options[0]).toEqual(multipleResults[0]) + expect(multipleWrapper.state().options[1]).toEqual(multipleResults[1]) + + }) + //Headers expected + + it('should show menu headers with lookup source labels and values in the dropdown when provided results', () => { + const instance = multipleWrapper.instance(); + const menuWrapper = shallow(instance.renderMenuFunc(multipleResults, p2Props)); + const menuChildrenNumber = menuWrapper.children().length; + //One top level menu component + expect(menuWrapper.find('ul').length).toEqual(1); + //Four children, with two headings and two items + expect(menuChildrenNumber).toEqual(4); + expect(menuWrapper.childAt(0).html()).toEqual("
  • Person
  • "); + expect(menuWrapper.childAt(1).childAt(0).text()).toEqual("plabel"); + expect(menuWrapper.childAt(2).html()).toEqual("
  • Subject
  • "); + expect(menuWrapper.childAt(3).childAt(0).text()).toEqual("slabel"); + }) + + + + }) diff --git a/package-lock.json b/package-lock.json index 8abe39ad0..10665a10c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6327,7 +6327,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6351,13 +6352,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6374,19 +6377,22 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6517,7 +6523,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6531,6 +6538,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6547,6 +6555,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6555,13 +6564,15 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -6582,6 +6593,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6670,7 +6682,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6684,6 +6697,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6779,7 +6793,8 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6821,6 +6836,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6842,6 +6858,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6890,13 +6907,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src/components/editor/InputLookupQA.jsx b/src/components/editor/InputLookupQA.jsx index 414ef31b2..179a54397 100644 --- a/src/components/editor/InputLookupQA.jsx +++ b/src/components/editor/InputLookupQA.jsx @@ -1,102 +1,200 @@ // Copyright 2018 Stanford University see Apache2.txt for license import React, { Component } from 'react' -import { asyncContainer, Typeahead } from 'react-bootstrap-typeahead' +import { asyncContainer, Typeahead, Menu, MenuItem } from 'react-bootstrap-typeahead' import PropTypes from 'prop-types' import Swagger from 'swagger-client' import { connect } from 'react-redux' import { changeSelections } from '../../actions/index' -const AsyncTypeahead = asyncContainer(Typeahead) +const AsyncTypeahead = asyncContainer( Typeahead ) class InputLookupQA extends Component { - constructor(props) { - super(props) - this.state = { - isLoading: false - } - } - - render() { - let isMandatory, isRepeatable, authority, subauthority, language - try { - isMandatory = JSON.parse(this.props.propertyTemplate.mandatory) - isRepeatable = JSON.parse(this.props.propertyTemplate.repeatable) - authority = this.props.lookupConfig.value.authority - subauthority = this.props.lookupConfig.value.authority - language = this.props.lookupConfig.value.language - } catch (error) { - console.log(`Problem with properties fetched from resource template: ${error}`) + constructor( props ) { + super( props ) + this.state = { + isLoading: false + } } - const typeaheadProps = { - required: isMandatory, - multiple: isRepeatable, - placeholder: this.props.propertyTemplate.propertyLabel, - useCache: true, - isLoading: this.state.isLoading, - options: this.state.options, - selected: this.state.selected, - delay: 300 - } + //Render menu function to be used by typeahead + renderMenuFunc = ( results, menuProps ) => { + //Returning results per each promise + //If error is returned, it will be used to display for that source + const items = []; + let r, i, authLabel, resultsLength, authURI, headerKey, result; + resultsLength = results.length; + let idx = 0; + for ( i = 0; i < resultsLength; i++ ) { + result = results[i]; + authLabel = result.authLabel; + authURI = result.authURI; + headerKey = authURI + "-header"; + //Add header only if more than one authority request + if ( resultsLength > 1 ) + items.push( + {authLabel} + ); + //For this authority, display results + if ( "isError" in result ) { + //if error, then get error from within result and display that message + let errorMessage = "An error occurred in retrieving results"; + let errorHeaderKey = headerKey + "-error"; + items.push( + {errorMessage} + ); + } else { + //if not error, print out items for result + r = result.body; + r.forEach( function( result ) { + items.push( + {result.label} + ); + idx++; + } ); + //if the length of results is zero we need to show that as well + if ( r.length == 0 ) { + let noResultsMessage = "No results for this lookup"; + let noResultsHeaderKey = headerKey + "-noResults"; + items.push( + {noResultsMessage} + ); + } - return ( -
    - { - this.setState({isLoading: true}); - Swagger({ url: "src/lib/apidoc.json" }).then((client) => { - client - .apis - .SearchQuery - .GET_searchAuthority({ - q: query, - vocab: authority, - subauthority: subauthority, - maxRecords: 8, - lang: language - }) - .then(response => this.setState({ - isLoading: false, - options: response.body - })) - }).catch(() => { return false }) - }} - onChange={selected => { - let payload = { - id: this.props.propertyTemplate.propertyURI, - items: selected, - rtId: this.props.rtId - } - this.props.handleSelectedChange(payload) } - } - {...typeaheadProps} - /> -
    - ) - } + } + + return ( + + {items} + + ) + } + + render() { + + let isMandatory, isRepeatable, authority, subauthority, language + const lookupConfigs = this.props.lookupConfig + + try { + isMandatory = JSON.parse( this.props.propertyTemplate.mandatory ) + isRepeatable = JSON.parse( this.props.propertyTemplate.repeatable ) + } catch ( error ) { + console.log( `Problem with properties fetched from resource template: ${error}` ) + } + const typeaheadProps = { + required: isMandatory, + multiple: isRepeatable, + placeholder: this.props.propertyTemplate.propertyLabel, + useCache: true, + isLoading: this.state.isLoading, + options: this.state.options, + selected: this.state.selected, + delay: 300 + } + + return ( +
    + { + return ( this.renderMenuFunc( results, menuProps ) ); + } + } + + + onSearch={query => { + this.setState( { isLoading: true } ); + Swagger( { url: "src/lib/apidoc.json" } ).then(( client ) => { + //create array of promises based on the lookup config array that is sent in + let lookupPromises = lookupConfigs.map( lookupConfig => { + authority = lookupConfig.value.authority; + subauthority = lookupConfig.value.authority; + language = lookupConfig.value.language; + //return the 'promise' + //Since we don't want promise.all to fail if + //one of the lookups fails, we want a catch statement + //at this level which will then return the error + return client + .apis + .SearchQuery + .GET_searchAuthority( { + q: query, + vocab: authority, + subauthority: subauthority, + maxRecords: 8, + lang: language + } ) + .catch( function( err ) { + console.error( "Error in executing lookup against source", err ); + //return information along with the error in its own object + return { "isError": true, "errorObject": err }; + } ); + + } ); + + Promise.all( lookupPromises ).then(( values ) => { + + let valuesLength = values.length; + let i; + for ( i = 0; i < valuesLength; i++ ) { + //If undefined, add info - note if error, error object returned in object + //which allows attaching label and uri for authority + if ( values[i] ) { + values[i]["authLabel"] = lookupConfigs[i].value.label; + values[i]["authURI"] = lookupConfigs[i].value.uri; + } + } + + this.setState( { + isLoading: false, + options: values + } ) + } + ) + } ).catch(() => { return false } ) + }} + onChange={selected => { + let payload = { + id: this.props.propertyTemplate.propertyURI, + items: selected, + rtId: this.props.rtId + } + this.props.handleSelectedChange( payload ) + } + } + {...typeaheadProps} + + filterBy={() => { + /** Currently don't want any default filtering as we want all the results returned from QA, also we are passing in a complex object **/ + /* Your own filtering code goes here. */ + return true; + }} + /> +
    + ) + } } InputLookupQA.propTypes = { - propertyTemplate: PropTypes.shape({ - propertyLabel: PropTypes.string, - mandatory: PropTypes.oneOfType([ PropTypes.string, PropTypes.bool]), - repeatable: PropTypes.oneOfType([ PropTypes.string, PropTypes.bool]), - valueConstraint: PropTypes.shape({ - useValuesFrom: PropTypes.oneOfType([ PropTypes.string, PropTypes.array]) - }) - }).isRequired + propertyTemplate: PropTypes.shape( { + propertyLabel: PropTypes.string, + mandatory: PropTypes.oneOfType( [PropTypes.string, PropTypes.bool] ), + repeatable: PropTypes.oneOfType( [PropTypes.string, PropTypes.bool] ), + valueConstraint: PropTypes.shape( { + useValuesFrom: PropTypes.oneOfType( [PropTypes.string, PropTypes.array] ) + } ) + } ).isRequired } -const mapStatetoProps = (state) => { - let result = Object.assign({},state) - return result +const mapStatetoProps = ( state ) => { + let result = Object.assign( {}, state ) + return result } -const mapDispatchtoProps = dispatch => ({ - handleSelectedChange(selected){ - dispatch(changeSelections(selected)) - } -}) +const mapDispatchtoProps = dispatch => ( { + handleSelectedChange( selected ) { + dispatch( changeSelections( selected ) ) + } +} ) -export default connect(mapStatetoProps, mapDispatchtoProps)(InputLookupQA) +export default connect( mapStatetoProps, mapDispatchtoProps )( InputLookupQA ) diff --git a/src/components/editor/ResourceTemplateForm.jsx b/src/components/editor/ResourceTemplateForm.jsx index 66dc62795..23e7b408f 100644 --- a/src/components/editor/ResourceTemplateForm.jsx +++ b/src/components/editor/ResourceTemplateForm.jsx @@ -1,6 +1,6 @@ // Copyright 2018 Stanford University see Apache2.txt for license -import React, {Component} from 'react' +import React, { Component } from 'react' import { connect } from 'react-redux' import PropTypes from 'prop-types' import shortid from 'shortid' @@ -10,262 +10,278 @@ import InputLookupQA from './InputLookupQA' import PropertyPanel from './PropertyPanel' import PropertyResourceTemplate from './PropertyResourceTemplate' import lookupConfig from '../../../static/spoofedFilesFromServer/fromSinopiaServer/lookupConfig.json' -import {getLD, setItems, removeAllContent} from '../../actions/index' +import { getLD, setItems, removeAllContent } from '../../actions/index' import { getResourceTemplate } from '../../sinopiaServer' -const N3 = require('n3') +const N3 = require( 'n3' ) const { DataFactory } = N3 const { blankNode } = DataFactory // renders the input form for a ResourceTemplate class ResourceTemplateForm extends Component { - constructor(props) { - super(props) - this.defaultValues() - this.state = { - showRdf: false, - rdfOuterSubject: this.makeSubject(), - inputs: {} + constructor( props ) { + super( props ) + this.defaultValues() + this.state = { + showRdf: false, + rdfOuterSubject: this.makeSubject(), + inputs: {} + } } - } - defaultValues = () => { - this.props.propertyTemplates.map( (pt) =>{ - if (pt.mandatory == undefined) pt.mandatory = "true" - if (pt.repeatable == undefined) pt.repeatable = "false" - if (pt.editable == undefined) pt.editable = "true" - }) - } + defaultValues = () => { + this.props.propertyTemplates.map(( pt ) => { + if ( pt.mandatory == undefined ) pt.mandatory = "true" + if ( pt.repeatable == undefined ) pt.repeatable = "false" + if ( pt.editable == undefined ) pt.editable = "true" + } ) + } - makeSubject = () => { - // in the future we will return a blank node or an IRI (using namedNode in the DataFactory ^^)... - // return namedNode('http://example.com') - return blankNode() - } + makeSubject = () => { + // in the future we will return a blank node or an IRI (using namedNode in the DataFactory ^^)... + // return namedNode('http://example.com') + return blankNode() + } - rdfClose = () => { - this.setState( { showRdf: false } ) - } + rdfClose = () => { + this.setState( { showRdf: false } ) + } - // TODO: deal with more than one default value? - defaultsForLiteral = (content, predicate) => { - return [{ - content: content, - id: 0, - bnode: this.state.rdfOuterSubject, - propPredicate: predicate - }] - } + // TODO: deal with more than one default value? + defaultsForLiteral = ( content, predicate ) => { + return [{ + content: content, + id: 0, + bnode: this.state.rdfOuterSubject, + propPredicate: predicate + }] + } - setDefaultsForLiteralWithPayLoad = (button, propURI, propPredicate, defaults, rtid) => { - let useUri - propPredicate !== undefined ? useUri = propPredicate : useUri = propURI + setDefaultsForLiteralWithPayLoad = ( button, propURI, propPredicate, defaults, rtid ) => { + let useUri + propPredicate !== undefined ? useUri = propPredicate : useUri = propURI - const payload = { - id: button, - uri: useUri, - items: defaults, - rtId: rtid - } + const payload = { + id: button, + uri: useUri, + items: defaults, + rtId: rtid + } - if (defaults != undefined) { - this.props.handleMyItemsChange(payload) + if ( defaults != undefined ) { + this.props.handleMyItemsChange( payload ) + } } - } - getContentForModalButton = (rtId) => { - let content - let resourceTemplate = getResourceTemplate(rtId) - const pt = resourceTemplate.propertyTemplates[0] - if (this.isLiteralWithDefaultValue(pt)) { - content = pt.valueConstraint.defaults[0].defaultLiteral + getContentForModalButton = ( rtId ) => { + let content + let resourceTemplate = getResourceTemplate( rtId ) + const pt = resourceTemplate.propertyTemplates[0] + if ( this.isLiteralWithDefaultValue( pt ) ) { + content = pt.valueConstraint.defaults[0].defaultLiteral + } + return content } - return content - } - // Note: rtIds is expected to be an array of length at least one - resourceTemplateFields = (rtIds) => { - const rtProperties = [] - rtIds.map((rtId, i) => { - rtProperties.push() - if ((rtIds.length - i) > 1) { - rtProperties.push(
    ) - } - }) - return rtProperties - } + // Note: rtIds is expected to be an array of length at least one + resourceTemplateFields = ( rtIds ) => { + const rtProperties = [] + rtIds.map(( rtId, i ) => { + rtProperties.push( ) + if ( ( rtIds.length - i ) > 1 ) { + rtProperties.push(
    ) + } + } ) + return rtProperties + } - defaultValues = () => { - this.props.propertyTemplates.map((pt) => { - if (pt.mandatory == undefined) pt.mandatory = "true" - if (pt.repeatable == undefined) pt.repeatable = "false" - if (pt.editable == undefined) pt.editable = "true" - }) - } + defaultValues = () => { + this.props.propertyTemplates.map(( pt ) => { + if ( pt.mandatory == undefined ) pt.mandatory = "true" + if ( pt.repeatable == undefined ) pt.repeatable = "false" + if ( pt.editable == undefined ) pt.editable = "true" + } ) + } - isLiteralWithDefaultValue = (pt) => { - return Boolean( - pt.type === 'literal' && - pt.valueConstraint !== undefined && - pt.valueConstraint.defaults !== undefined && - pt.valueConstraint.defaults.length > 0 - ) - } + isLiteralWithDefaultValue = ( pt ) => { + return Boolean( + pt.type === 'literal' && + pt.valueConstraint !== undefined && + pt.valueConstraint.defaults !== undefined && + pt.valueConstraint.defaults.length > 0 + ) + } - isResourceWithValueTemplateRef = (pt) => { - return Boolean( - pt.type === 'resource' && - pt.valueConstraint != null && - pt.valueConstraint.valueTemplateRefs != null && - pt.valueConstraint.valueTemplateRefs.length > 0 - ) - } + isResourceWithValueTemplateRef = ( pt ) => { + return Boolean( + pt.type === 'resource' && + pt.valueConstraint != null && + pt.valueConstraint.valueTemplateRefs != null && + pt.valueConstraint.valueTemplateRefs.length > 0 + ) + } - setInputs() { - let inputs = {} - inputs['literals'] = this.props.literals - inputs['lookups'] = this.props.lookups - inputs['rtId'] = this.props.rtId - inputs['resourceURI'] = this.props.resourceTemplate.resourceURI - inputs['linkedNode'] = this.state.rdfOuterSubject - return inputs - } + setInputs() { + let inputs = {} + inputs['literals'] = this.props.literals + inputs['lookups'] = this.props.lookups + inputs['rtId'] = this.props.rtId + inputs['resourceURI'] = this.props.resourceTemplate.resourceURI + inputs['linkedNode'] = this.state.rdfOuterSubject + return inputs + } - handleTrashValue = (buttonIndex) => { - this.props.handleRemoveAllContent(buttonIndex) - } + handleTrashValue = ( buttonIndex ) => { + this.props.handleRemoveAllContent( buttonIndex ) + } - renderValueForButton(buttonValue, buttonIndex) { - if (buttonValue != undefined) { - return ( -
    - - - -
    - ) + renderValueForButton( buttonValue, buttonIndex ) { + if ( buttonValue != undefined ) { + return ( +
    + + + +
    + ) + } } - } - render() { + render() { - if (this.props.propertyTemplates.length === 0 || this.props.propertyTemplates[0] === {}) { - return

    There are no propertyTemplates - probably an error.

    - } else { - return ( -
    + if ( this.props.propertyTemplates.length === 0 || this.props.propertyTemplates[0] === {} ) { + return

    There are no propertyTemplates - probably an error.

    + } else { + return ( +
    -
    -
    - { this.props.propertyTemplates.map( (pt, index) => { + +
    + {this.props.propertyTemplates.map(( pt, index ) => { - let isLookupWithConfig = Boolean( - lookupConfig !== undefined && - pt.valueConstraint !== undefined && - pt.valueConstraint.useValuesFrom - ) + let isLookupWithConfig = Boolean( + lookupConfig !== undefined && + pt.valueConstraint !== undefined && + pt.valueConstraint.useValuesFrom + ) - let lookupConfigItem, templateUri, listComponent + let lookupConfigItem, templateUris, listComponent, lookupConfigItems - if (isLookupWithConfig) { - templateUri = pt.valueConstraint.useValuesFrom[0] - for(var i in lookupConfig){ - lookupConfigItem = Object.getOwnPropertyDescriptor(lookupConfig, i) - if(lookupConfigItem.value.uri === templateUri){ - listComponent = lookupConfigItem.value.component - break - } - } - } + if ( isLookupWithConfig ) { + templateUris = pt.valueConstraint.useValuesFrom; + /*Only one input is possible even with multiple vocabularies + or value in "useValuesFrom" which is an array + The first templateUri that matches is used to generate + the listComponent but we need to pass on multiple values for useValueFrom + Assumption here is multi-useValuesFrom will still all be the same type + of list component */ + lookupConfigItems = []; + templateUris.forEach( templateUri => { + for ( var i in lookupConfig ) { + lookupConfigItem = Object.getOwnPropertyDescriptor( lookupConfig, i ); + if ( lookupConfigItem.value.uri === templateUri ) { + /*listComponent = lookupConfigItem.value.component + break*/ + lookupConfigItems.push( lookupConfigItem ); + } + } + } ); + if ( lookupConfigItems.length > 0 ) { + listComponent = lookupConfigItems[0].value.component; + lookupConfigItem = lookupConfigItems[0]; + } + } - if (listComponent === 'list'){ - return ( - - - - ) - } - else if (listComponent === 'lookup'){ - return( - - - - ) - } - else if(pt.type == 'literal'){ - return( - - - - ) - } - else if (this.isResourceWithValueTemplateRef(pt)) { - let valueForButton - return ( - - {this.resourceTemplateFields(pt.valueConstraint.valueTemplateRefs)} - {this.renderValueForButton(valueForButton, index)} - - ) - } - else if (pt.type == 'resource'){ - return (

    {pt.propertyLabel}: NON-modal resource

    ) - } - } - ) - } + if ( listComponent === 'list' ) { + return ( + + + + ) + } + else if ( listComponent === 'lookup' ) { + /**Changing to pass along the array of lookup configs and not just a single item in case more than one useValueFrom is specified**/ + return ( -
    - -
    - ) + + + + ) + } + else if ( pt.type == 'literal' ) { + return ( + + + + ) + } + else if ( this.isResourceWithValueTemplateRef( pt ) ) { + let valueForButton + return ( + + {this.resourceTemplateFields( pt.valueConstraint.valueTemplateRefs )} + {this.renderValueForButton( valueForButton, index )} + + ) + } + else if ( pt.type == 'resource' ) { + return (

    {pt.propertyLabel}: NON-modal resource

    ) + } + } + ) + } + +
    + +
    + ) + } } - } } ResourceTemplateForm.propTypes = { - literals: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - lookups: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - handleGenerateLD: PropTypes.func, - propertyTemplates: PropTypes.arrayOf(PropTypes.object).isRequired, - resourceTemplate: PropTypes.object.isRequired, - rtId: PropTypes.string, - parentResourceTemplate: PropTypes.string, - rdfOuterSubject: PropTypes.object, - propPredicate: PropTypes.string, - buttonID: PropTypes.number, - generateLD: PropTypes.object.isRequired, - handleMyItemsChange: PropTypes.func, - handleRemoveAllContent: PropTypes.func + literals: PropTypes.oneOfType( [PropTypes.array, PropTypes.object] ), + lookups: PropTypes.oneOfType( [PropTypes.array, PropTypes.object] ), + handleGenerateLD: PropTypes.func, + propertyTemplates: PropTypes.arrayOf( PropTypes.object ).isRequired, + resourceTemplate: PropTypes.object.isRequired, + rtId: PropTypes.string, + parentResourceTemplate: PropTypes.string, + rdfOuterSubject: PropTypes.object, + propPredicate: PropTypes.string, + buttonID: PropTypes.number, + generateLD: PropTypes.object.isRequired, + handleMyItemsChange: PropTypes.func, + handleRemoveAllContent: PropTypes.func } -const mapStateToProps = (state) => { - return { - literals: state.literal, - lookups: state.lookups, - generateLD: state.generateLD - } +const mapStateToProps = ( state ) => { + return { + literals: state.literal, + lookups: state.lookups, + generateLD: state.generateLD + } } -const mapDispatchToProps = dispatch => ({ - handleMyItemsChange(user_input){ - dispatch(setItems(user_input)) - }, - handleRemoveAllContent(id){ - dispatch(removeAllContent(id)) - }, - handleGenerateLD(inputs){ - dispatch(getLD(inputs)) - } -}) +const mapDispatchToProps = dispatch => ( { + handleMyItemsChange( user_input ) { + dispatch( setItems( user_input ) ) + }, + handleRemoveAllContent( id ) { + dispatch( removeAllContent( id ) ) + }, + handleGenerateLD( inputs ) { + dispatch( getLD( inputs ) ) + } +} ) -export default connect(mapStateToProps, mapDispatchToProps)(ResourceTemplateForm) +export default connect( mapStateToProps, mapDispatchToProps )( ResourceTemplateForm) \ No newline at end of file diff --git a/src/styles/main.css b/src/styles/main.css index 2fcc99baa..f4a3e671b 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -171,3 +171,14 @@ input.rbt-input-main { .editor-navtabs a { color: #2F2424; } + +/** For lookup menu headers **/ +.dropdown-header { + font-weight:800; + color:#333; + background-color:#d3d3d3; +} +.dropdown-empty, .dropdown-error { + font-weight:400; + color: #999999; +} \ No newline at end of file diff --git a/static/spoofedFilesFromServer/fromSinopiaServer/resourceTemplates/MonographInstance.json b/static/spoofedFilesFromServer/fromSinopiaServer/resourceTemplates/MonographInstance.json index a8059117d..c148b63bc 100644 --- a/static/spoofedFilesFromServer/fromSinopiaServer/resourceTemplates/MonographInstance.json +++ b/static/spoofedFilesFromServer/fromSinopiaServer/resourceTemplates/MonographInstance.json @@ -168,7 +168,8 @@ "valueConstraint": { "valueTemplateRefs": [], "useValuesFrom": [ - "urn:ld4p:qa:names:person" + "urn:ld4p:qa:names:person", + "urn:ld4p:qa:subjects:person" ], "valueDataType": { "dataTypeURI": "http://id.loc.gov/ontologies/bibframe/Agent"