diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index 28bb9d3b4..b96acd481 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -328,8 +328,9 @@ class ArticlesController < ApplicationController # NOTE: the organization logic is still a little counter intuitive but this should # fix the bug - if params["article"]["user_id"] && org_admin_user_change_privilege + if org_admin_user_change_privilege allowed_params << :user_id + allowed_params << :co_author_ids_list elsif params["article"]["organization_id"] && allowed_to_change_org_id? # change the organization of the article only if explicitly asked to do so allowed_params << :organization_id diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index ee5937b9d..4a2b91902 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -97,6 +97,11 @@ class OrganizationsController < ApplicationController def members @organization = Organization.find_by(slug: params[:slug]) @members = @organization.users + + respond_to do |format| + format.json { render json: @members.to_json(only: %i[id name username]) } + format.html + end end private diff --git a/app/javascript/__tests__/__snapshots__/convertCoauthorIdsToUsernameInputs.test.js.snap b/app/javascript/__tests__/__snapshots__/convertCoauthorIdsToUsernameInputs.test.js.snap new file mode 100644 index 000000000..c1f618830 --- /dev/null +++ b/app/javascript/__tests__/__snapshots__/convertCoauthorIdsToUsernameInputs.test.js.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`convertCoauthorIdsToUsernameInputs when there *is* a pre-existing id value renders matching snapshot 1`] = ` +" +
+ + +
+ +
+ +
Maximum 4 selections

Selected items:

    + +
    +
    + + + " +`; + +exports[`convertCoauthorIdsToUsernameInputs when there is no pre-existing id value renders matching snapshot 1`] = ` +" +
    + + +
    + +
    + +
    Maximum 4 selections

    Selected items:

      + +
      +
      + + + " +`; diff --git a/app/javascript/packs/__tests__/billboard.test.js b/app/javascript/__tests__/billboard.test.js similarity index 93% rename from app/javascript/packs/__tests__/billboard.test.js rename to app/javascript/__tests__/billboard.test.js index e77a695fe..b5ee4dade 100644 --- a/app/javascript/packs/__tests__/billboard.test.js +++ b/app/javascript/__tests__/billboard.test.js @@ -1,4 +1,4 @@ -import { getBillboard } from '../billboard'; +import { getBillboard } from '../packs/billboard'; describe('getBillboard', () => { beforeEach(() => { diff --git a/app/javascript/__tests__/convertCoauthorIdsToUsernameInputs.test.js b/app/javascript/__tests__/convertCoauthorIdsToUsernameInputs.test.js new file mode 100644 index 000000000..2723bde77 --- /dev/null +++ b/app/javascript/__tests__/convertCoauthorIdsToUsernameInputs.test.js @@ -0,0 +1,162 @@ +import fetch from 'jest-fetch-mock'; +import '@testing-library/jest-dom'; +import userEvent from '@testing-library/user-event'; + +import { convertCoauthorIdsToUsernameInputs } from '../packs/dashboards/convertCoauthorIdsToUsernameInputs'; + +global.fetch = fetch; + +function fakeFetchResponseJSON() { + return JSON.stringify([ + { name: 'Alice', username: 'alice', id: 1 }, + { name: 'Bob', username: 'bob', id: 2 }, + { name: 'Charlie', username: 'charlie', id: 3 }, + ]); +} + +describe('convertCoauthorIdsToUsernameInputs', () => { + beforeEach(() => { + global.Honeybadger = { notify: jest.fn() }; + + fetch.resetMocks(); + fetch.mockResponse(fakeFetchResponseJSON()); + }); + + describe('when there is no pre-existing id value', () => { + beforeEach(() => { + window.document.body.innerHTML = ` +
      +
      + + +
      + +
      + +
      + +
      +
      + + +
      + `; + }); + + it('calls fetch, with exception for author ID', async () => { + await convertCoauthorIdsToUsernameInputs(); + expect(fetch).toHaveBeenCalledWith('/org7053/members.json'); + }); + + it('makes the co-author field hidden', async () => { + await convertCoauthorIdsToUsernameInputs(); + const co_author_field = document.getElementById( + 'article_ID_co_author_ids_list', + ); + expect(co_author_field.type).toBe('hidden'); + }); + + it('renders matching snapshot', async () => { + await convertCoauthorIdsToUsernameInputs(); + expect(document.forms[0].innerHTML).toMatchSnapshot(); + }); + + it('works as expected', async () => { + await convertCoauthorIdsToUsernameInputs(); + const input = document.querySelector( + "input[placeholder='Add up to 4...']", + ); + input.focus(); + await userEvent.type(input, 'Bob,'); + + const hiddenField = document.querySelector( + "input[name='article[co_author_ids_list]']", + ); + expect(hiddenField.value).toBe('2'); + }); + }); + + describe('when there *is* a pre-existing id value', () => { + beforeEach(() => { + window.document.body.innerHTML = ` +
      +
      + + +
      + +
      + +
      + +
      +
      + + +
      + `; + }); + + it('renders matching snapshot', async () => { + await convertCoauthorIdsToUsernameInputs(); + expect(document.forms[0].innerHTML).toMatchSnapshot(); + }); + + it('works as expected', async () => { + await convertCoauthorIdsToUsernameInputs(); + const input = document.querySelector( + "input[placeholder='Add another...']", + ); + input.focus(); + await userEvent.type(input, 'Bob,'); + + const hiddenField = document.querySelector( + "input[name='article[co_author_ids_list]']", + ); + expect(hiddenField.value).toBe('3, 2'); + }); + + it('can remove previously selected', async () => { + await convertCoauthorIdsToUsernameInputs(); + const deselect = document.querySelector( + '.c-autocomplete--multi__selected', + ); + await userEvent.click(deselect); + + const hiddenField = document.querySelector( + "input[name='article[co_author_ids_list]']", + ); + expect(hiddenField.value).toBe(''); + }); + }); +}); diff --git a/app/javascript/crayons/MultiSelectAutocomplete/MultiSelectAutocomplete.jsx b/app/javascript/crayons/MultiSelectAutocomplete/MultiSelectAutocomplete.jsx index 25d738fdd..959b813f2 100644 --- a/app/javascript/crayons/MultiSelectAutocomplete/MultiSelectAutocomplete.jsx +++ b/app/javascript/crayons/MultiSelectAutocomplete/MultiSelectAutocomplete.jsx @@ -598,7 +598,12 @@ export const MultiSelectAutocomplete = ({ className={`c-autocomplete--multi__wrapper${ border ? '-border crayons-textfield' : ' border-none p-0' } flex items-center cursor-text`} - onClick={() => inputRef.current?.focus()} + onClick={(event) => { + // Stopping propagation here so that clicks on the 'x' close button + // don't appear to be "outside" of any container (eg, dropdown) + event.stopPropagation(); + inputRef.current?.focus(); + }} >