diff --git a/app/assets/stylesheets/scss/_buttons.scss b/app/assets/stylesheets/scss/_buttons.scss index 276c084bfe..fb92e10965 100644 --- a/app/assets/stylesheets/scss/_buttons.scss +++ b/app/assets/stylesheets/scss/_buttons.scss @@ -101,7 +101,12 @@ input[type=file]::file-selector-button { @extend %o-button__link-style; padding: 5px 0; color: $dark-blue; - font-size: 0.9em; + font-size: 0.98rem; +} + +.o-button__plain-textlink { + @extend %o-button__link-style; + color: $dark-blue; } .o-button__plain-text6 { diff --git a/app/assets/stylesheets/scss/_drop.scss b/app/assets/stylesheets/scss/_drop.scss index dd7fb9e806..5c2117dedd 100644 --- a/app/assets/stylesheets/scss/_drop.scss +++ b/app/assets/stylesheets/scss/_drop.scss @@ -25,9 +25,12 @@ .dd-list-item { list-style: ""; margin: 1rem auto; - padding: 1rem; + padding: 1rem; background-color: $lightest-blue; - text-align: left; + display: flex; + align-items: flex-start; + flex-wrap: wrap; + gap: 1.5ch; &.dragon-inactive { } @@ -36,7 +39,6 @@ @extend %o-button__link-style; cursor: ns-resize; float: left; - margin-right: 1.5ch; margin-top: 28px; &:before { font-family: 'Font Awesome 6 Free'; diff --git a/app/assets/stylesheets/scss/_input.scss b/app/assets/stylesheets/scss/_input.scss index 80229daec1..284c8cc93f 100644 --- a/app/assets/stylesheets/scss/_input.scss +++ b/app/assets/stylesheets/scss/_input.scss @@ -15,13 +15,40 @@ input { flex-wrap: wrap; column-gap: 2ch; row-gap: 1ch; + + &.spaced { + justify-content: space-between; + align-items: baseline; + } } .input-label { color: $dark-navy; } -label.optional:after, label span.details { +.input-example, [id$='-ex'] { + font-size: .98rem; + color: $medium-gray; + margin-top: .35ch; + i:not(.fas, .far) { + &:before { + font-style: normal; + display: inline-block; + content: 'e.g.'; + color: $dark-gray; + margin-right: .5ch; + } + &.hint:before { + content: 'Hint:'; + } + &.ie:before { + content: 'i.e.' + } + } +} + +label.optional:after, +label span.details { display: inline-block; font-size: .98rem; font-weight: normal; diff --git a/app/assets/stylesheets/scss/_keywords.scss b/app/assets/stylesheets/scss/_keywords.scss index 71acec6f1e..5f967abd03 100644 --- a/app/assets/stylesheets/scss/_keywords.scss +++ b/app/assets/stylesheets/scss/_keywords.scss @@ -37,7 +37,9 @@ .c-keywords__keyword-remove { border: none; background-color: $medium-green; + border: thin solid $light-green; color: white; + font-size: .98rem; line-height: 0; &:hover, &:active, &:focus { background-color: $dark-blue; diff --git a/app/assets/stylesheets/scss/_steps.scss b/app/assets/stylesheets/scss/_steps.scss index c617fec30c..ce01eb2b8a 100644 --- a/app/assets/stylesheets/scss/_steps.scss +++ b/app/assets/stylesheets/scss/_steps.scss @@ -9,6 +9,10 @@ flex-direction: column; align-items: center; flex: 1; + cursor: pointer; + &:hover .step-name { + text-decoration: underline; + } &::after { position: absolute; diff --git a/app/assets/stylesheets/scss/_submission-help.scss b/app/assets/stylesheets/scss/_submission-help.scss index 60fd359b1c..2bc8dcf70b 100644 --- a/app/assets/stylesheets/scss/_submission-help.scss +++ b/app/assets/stylesheets/scss/_submission-help.scss @@ -1,3 +1,40 @@ +#submission-help { + //position: sticky; + //bottom: 0; + //box-shadow: $lightest-green 0px -4px 4px -6px; + border-top: thick solid $lightest-green; + background-color: white; + padding: 2rem; + padding-left: 30px; + width: 100%; + display: flex; + gap: 2ch; + align-items: flex-start; + justify-content: space-between; + flex-direction: row-reverse; + + #submission-help-text { + font-size: 1rem; + *:first-child { + margin-top: 0; + } + *:last-child { + margin-top: 0; + } + } + + @media (max-width: 811px){ + flex-direction: column-reverse; + *:first-child { + margin-left: auto; + } + } + + button { + white-space: nowrap; + } +} + #infographic { margin: 2ch auto; display: grid; diff --git a/app/assets/stylesheets/scss/_submission.scss b/app/assets/stylesheets/scss/_submission.scss index 9bca27d5c4..c946e756a0 100644 --- a/app/assets/stylesheets/scss/_submission.scss +++ b/app/assets/stylesheets/scss/_submission.scss @@ -503,45 +503,22 @@ dialog#submission-step[open] { h1 { margin-bottom: 0; } - & + h2 { + & + h2, & + div > h2:first-child { margin-top: 1.1rem; } } -#submission-help { - //position: sticky; - //bottom: 0; - //box-shadow: $lightest-green 0px -4px 4px -6px; - border-top: thick solid $lightest-green; - background-color: white; - padding: 2rem; - padding-left: 30px; - width: 100%; +.drag-instruct { + font-size: .98rem; display: flex; + align-items: baseline; gap: 2ch; - align-items: flex-start; - justify-content: space-between; - flex-direction: row-reverse; - - #submission-help-text { - font-size: 1rem; - *:first-child { - margin-top: 0; - } - *:last-child { - margin-top: 0; - } - } - - @media (max-width: 811px){ - flex-direction: column-reverse; - *:first-child { - margin-left: auto; - } + flex-wrap: wrap; + i { + margin: 0 .5ch; } - - button { - white-space: nowrap; + & > * { + margin-bottom: 0; } } @@ -571,52 +548,95 @@ dialog#submission-step[open] { flex-wrap: wrap; column-gap: 1.5ch; row-gap: 1ch; + flex: 1; - & > span { - display: block; - align-self: start; - } - - .remove-record { + .remove-record, & + .remove-record { @extend %o-button__link-style; margin-top: 28px; } } +%author-form-button { + color: $dark-blue; + background-color: white; + border: thin solid $medium-blue; + padding: 2px 5px; + &:hover, &:active, &:focus { + background-color: $dark-blue; + color: white; + } +} + .author-form > div { - flex-basis: 25%; - max-width: calc(25% - 2.2ch); + flex-basis: calc(33% - 1ch); + flex-shrink: 1; + flex-grow: 0; + max-width: 100%; + min-width: 165px; * { max-width: 100%; text-overflow: ellipsis; } + &.affiliation-input { + flex: 1; + min-width: 200px; + + .input-line { + align-items: baseline; + gap: 1ch; + flex-wrap: nowrap; + button { + @extend %author-form-button; + line-height: 0; + font-size: .9rem; + } + } + + & + span { + display: block; + align-self: start; + button { + @extend %author-form-button; + margin-top: 28px; + font-size: .98rem; + padding-bottom: 0; + } + } + } @media (max-width: $screen-lg-min) { - flex-basis: 50%; - max-width: calc(50% - 2.2ch); + flex-basis: calc(50% - 1ch); } - @media (max-width: 600px) { + @media (max-width: 520px) { flex-basis: 100%; - max-width: 100%; + } +} + +.open .author-form > div { + @media (max-width: 740px) { + flex-basis: 100%; + } +} + +.auth-buttons { + display: flex; + flex-direction: row-reverse; + align-items: center; + + i { + transform: rotate(75deg); + color: $dark-gray; } } .funder-form { padding-bottom: 1ch; & > div { - flex-basis: 33%; - max-width: calc(33% - 2.2ch); + flex-basis: 25%; + flex: 1; * { max-width: 100%; text-overflow: ellipsis; } - @media (max-width: 800px) { - flex-basis: 50%; - max-width: calc(50% - 2.2ch); - } - @media (max-width: 600px) { - flex-basis: 100%; - max-width: 100%; - } } } diff --git a/app/assets/stylesheets/scss/_upload-table.scss b/app/assets/stylesheets/scss/_upload-table.scss index ce9cfc9dff..f6c82c7a4c 100644 --- a/app/assets/stylesheets/scss/_upload-table.scss +++ b/app/assets/stylesheets/scss/_upload-table.scss @@ -16,10 +16,11 @@ .c-uploadtable { min-width: 100%; + font-size: 1rem; th, td { - padding: $spacing-sm; + padding: .75rem; border: thin solid #888; text-align: left; } diff --git a/app/assets/stylesheets/scss/_variables.scss b/app/assets/stylesheets/scss/_variables.scss index 922961ded0..bd1cfbd523 100644 --- a/app/assets/stylesheets/scss/_variables.scss +++ b/app/assets/stylesheets/scss/_variables.scss @@ -15,6 +15,7 @@ $light-blue: rgb(55, 150, 196); $lighter-blue: lighten($light-blue, 25%); $lightest-blue: lighten($light-blue, 44%); $dark-gray: rgb(78, 80, 80); +$medium-gray: #767676; $light-gray: rgb(157, 159, 162); $lighter-gray: rgb(211, 211, 211); $lightest-gray: rgb(235, 235, 235); diff --git a/app/controllers/stash_datacite/authors_controller.rb b/app/controllers/stash_datacite/authors_controller.rb index d8a4952e9b..0f931f93e3 100644 --- a/app/controllers/stash_datacite/authors_controller.rb +++ b/app/controllers/stash_datacite/authors_controller.rb @@ -16,7 +16,6 @@ def new def create respond_to do |format| @author = StashEngine::Author.create(author_params) - process_affiliation unless params[:affiliation].nil? @author.reload format.js format.json { render json: @author.as_json(include: :affiliations) } @@ -27,7 +26,7 @@ def create def update respond_to do |format| @author.update(author_params) - process_affiliation + process_affiliations format.js { render template: 'stash_datacite/shared/update.js.erb' } format.json { render json: @author.as_json(include: :affiliations) } end @@ -75,29 +74,38 @@ def set_author # Only allow a trusted parameter "white list" through. def author_params - params.require(:author).permit(:id, :author_first_name, :author_last_name, :author_middle_name, - :author_email, :resource_id, :author_orcid, :author_order, + params.require(:author).permit(:id, :author_first_name, :author_last_name, :author_org_name, + :author_email, :resource_id, :author_orcid, :author_order, :corresp, affiliation: %i[id ror_id long_name]) end + def aff_params + params.require(:author).permit(:id, :author_first_name, :author_last_name, :author_org_name, + :author_email, :resource_id, :author_orcid, :author_order, :corresp, + affiliations: %i[id ror_id long_name]) + end + def check_for_orcid(author) author&.author_orcid ? true : false end # find correct affiliation based on long_name and ror_id and set it, create one if needed. - def process_affiliation + def process_affiliations return nil unless @author.present? - args = author_params - if args['affiliation']['long_name'].blank? - @author.affiliations.destroy_all - return + @author.affiliations.destroy_all + args = aff_params + affs = args['affiliations']&.reject { |a| a['long_name'].blank? } + affs.each do |aff| + process_affiliation(aff['long_name'], aff['ror_id']) end + end + + def process_affiliation(name, ror_val) + return nil unless @author.present? # find a matching pre-existing affiliation affil = nil - name = args['affiliation']['long_name'] - ror_val = args['affiliation']['ror_id'] if ror_val.present? # - find by ror_id if avaialable affil = StashDatacite::Affiliation.where(ror_id: ror_val).first @@ -115,10 +123,9 @@ def process_affiliation StashDatacite::Affiliation.create(long_name: name.to_s, ror_id: nil) end end - return if @author.affiliation == affil + return if @author.affiliations.pluck(:ror_id).include?(affil.ror_id) @author.affiliation = affil - @author.save end def check_reorder_valid diff --git a/app/controllers/stash_engine/resources_controller.rb b/app/controllers/stash_engine/resources_controller.rb index da03858a4b..0209389a5d 100644 --- a/app/controllers/stash_engine/resources_controller.rb +++ b/app/controllers/stash_engine/resources_controller.rb @@ -174,20 +174,20 @@ def display_collection end def dupe_check - dupes = nil + dupes = [] if @resource.title && @resource.title.length > 3 other_submissions = params.key?(:admin) ? StashEngine::Resources.all : @resource.user.resources other_submissions = other_submissions.latest_per_dataset.where.not(identifier_id: @resource.identifier_id) primary_article = @resource.related_identifiers.find_by(work_type: 'primary_article')&.related_identifier manuscript = @resource.resource_publication.manuscript_number - dupes = other_submissions.where(title: @resource.title).select(:id, :title).to_a + dupes = other_submissions.where(title: @resource.title)&.select(:id, :title).to_a if primary_article.present? dupes.concat(other_submissions.joins(:related_identifiers) - .where(related_identifiers: { work_type: 'primary_article', related_identifier: primary_article }).select(:id, :title).to_a) + .where(related_identifiers: { work_type: 'primary_article', related_identifier: primary_article })&.select(:id, :title).to_a) end if manuscript.present? dupes.concat( - other_submissions.joins(:resource_publication).find_by(resource_publication: { manuscript_number: manuscript }).select(:id, :title).to_a + other_submissions.joins(:resource_publication).find_by(resource_publication: { manuscript_number: manuscript })&.select(:id, :title).to_a ) end end diff --git a/app/javascript/react/components/MarkdownEditor/CodeEditor.jsx b/app/javascript/react/components/MarkdownEditor/CodeEditor.jsx index 4cfbd04013..b73d8cbf3c 100644 --- a/app/javascript/react/components/MarkdownEditor/CodeEditor.jsx +++ b/app/javascript/react/components/MarkdownEditor/CodeEditor.jsx @@ -105,6 +105,10 @@ export default function CodeEditor({ base: markdownLanguage, extensions: [markdownTags], }), + EditorView.contentAttributes.of({ + 'aria-labelledby': 'md_editor_label', + 'aria-errormessage': 'readme_error', + }), EditorView.updateListener.of((v) => { if (v.docChanged) { onChange(v.state.doc.toString()); diff --git a/app/javascript/react/components/MarkdownEditor/milkdownConfig.jsx b/app/javascript/react/components/MarkdownEditor/milkdownConfig.jsx index 3bb0d8e787..10111c05a1 100644 --- a/app/javascript/react/components/MarkdownEditor/milkdownConfig.jsx +++ b/app/javascript/react/components/MarkdownEditor/milkdownConfig.jsx @@ -9,6 +9,7 @@ const dryadConfig = (ctx) => { attributes: { class: 'milkdown dryad-milkdown-theme', 'aria-errormessage': 'readme_error', + 'aria-labelledby': 'md_editor_label', }, })); }; diff --git a/app/javascript/react/components/MarkdownEditor/milkdown_editor.css b/app/javascript/react/components/MarkdownEditor/milkdown_editor.css index d4f7e1dafb..f52c5efc68 100644 --- a/app/javascript/react/components/MarkdownEditor/milkdown_editor.css +++ b/app/javascript/react/components/MarkdownEditor/milkdown_editor.css @@ -15,6 +15,7 @@ height: 55vh; position: relative; overflow: auto; + background-color: white; } #readme_step_editor .md_editor_textarea { diff --git a/app/javascript/react/components/MetadataEntry/Authors/Affiliations.jsx b/app/javascript/react/components/MetadataEntry/Authors/Affiliations.jsx new file mode 100644 index 0000000000..0fb7b8c4b4 --- /dev/null +++ b/app/javascript/react/components/MetadataEntry/Authors/Affiliations.jsx @@ -0,0 +1,62 @@ +import React, {useState, useEffect} from 'react'; +import RorAutocomplete from '../RorAutocomplete'; +/* eslint-disable react/no-array-index-key */ + +export default function Affiliations({ + formRef, id, affiliations, setAffiliations, +}) { + const [affs, setAffs] = useState(affiliations.length > 0 ? affiliations : [{long_name: '', ror_id: ''}]); + + useEffect(() => { + setAffiliations(affs); + }, [affs]); + + const updateName = (i, v) => { + setAffs((afs) => afs.map((a, x) => (i === x ? {...a, long_name: v} : a))); + }; + const updateID = (i, v) => { + setAffs((afs) => afs.map((a, x) => (i === x ? {...a, ror_id: v} : a))); + }; + const newAff = (e) => { + setAffs((afs) => afs.concat([{long_name: '', ror_id: ''}])); + e.target.blur(); + }; + const removeAff = (i) => { + affs.splice(i, 1); + setAffs(affs); + }; + + return ( + <> + {affs.map((aff, i) => ( +
+
+ + {i !== 0 && ( +