From 51ee64b8d4940afd66f904eb5e97884deb13ee90 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 7 Feb 2024 19:50:11 -0400 Subject: [PATCH] Remove sitewide settings configuration. (#101) --- src/actions.ts | 29 --- src/components/ConfigTabContainer.tsx | 3 - src/components/SitewideSettingEditForm.tsx | 184 ------------- src/components/SitewideSettings.tsx | 70 ----- .../__tests__/ConfigTabContainer-test.tsx | 6 - .../SitewideSettingEditForm-test.tsx | 246 ------------------ .../__tests__/SitewideSettings-test.tsx | 48 ---- src/interfaces.ts | 12 - src/reducers/index.ts | 4 - src/reducers/sitewideSettings.ts | 8 - src/utils/sharedFunctions.ts | 2 +- 11 files changed, 1 insertion(+), 611 deletions(-) delete mode 100644 src/components/SitewideSettingEditForm.tsx delete mode 100644 src/components/SitewideSettings.tsx delete mode 100644 src/components/__tests__/SitewideSettingEditForm-test.tsx delete mode 100644 src/components/__tests__/SitewideSettings-test.tsx delete mode 100644 src/reducers/sitewideSettings.ts diff --git a/src/actions.ts b/src/actions.ts index d3b8b47a0..81c332067 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -9,7 +9,6 @@ import { CollectionsData, IndividualAdminsData, PatronAuthServicesData, - SitewideSettingsData, MetadataServicesData, DiscoveryServicesData, LibraryRegistrationsData, @@ -79,9 +78,6 @@ export default class ActionCreator extends BaseActionCreator { static readonly DELETE_PATRON_AUTH_SERVICE = "DELETE_PATRON_AUTH_SERVICE"; static readonly SITEWIDE_ANNOUNCEMENTS = "SITEWIDE_ANNOUNCEMENTS"; static readonly EDIT_SITEWIDE_ANNOUNCEMENTS = "EDIT_SITEWIDE_ANNOUNCEMENTS"; - static readonly SITEWIDE_SETTINGS = "SITEWIDE_SETTINGS"; - static readonly EDIT_SITEWIDE_SETTING = "EDIT_SITEWIDE_SETTING"; - static readonly DELETE_SITEWIDE_SETTING = "DELETE_SITEWIDE_SETTING"; static readonly METADATA_SERVICES = "METADATA_SERVICES"; static readonly EDIT_METADATA_SERVICE = "EDIT_METADATA_SERVICE"; static readonly DELETE_METADATA_SERVICE = "DELETE_METADATA_SERVICE"; @@ -545,31 +541,6 @@ export default class ActionCreator extends BaseActionCreator { ).bind(this); } - fetchSitewideSettings() { - const url = "/admin/sitewide_settings"; - return this.fetchJSON( - ActionCreator.SITEWIDE_SETTINGS, - url - ).bind(this); - } - - editSitewideSetting(data: FormData) { - const url = "/admin/sitewide_settings"; - return this.postForm(ActionCreator.EDIT_SITEWIDE_SETTING, url, data).bind( - this - ); - } - - deleteSitewideSetting(identifier: string | number) { - const url = "/admin/sitewide_setting/" + identifier; - return this.postForm( - ActionCreator.DELETE_SITEWIDE_SETTING, - url, - null, - "DELETE" - ).bind(this); - } - fetchMetadataServices() { const url = "/admin/metadata_services"; return this.fetchJSON( diff --git a/src/components/ConfigTabContainer.tsx b/src/components/ConfigTabContainer.tsx index d78a32227..f74c95f7b 100644 --- a/src/components/ConfigTabContainer.tsx +++ b/src/components/ConfigTabContainer.tsx @@ -5,7 +5,6 @@ import Collections from "./Collections"; import IndividualAdmins from "./IndividualAdmins"; import PatronAuthServices from "./PatronAuthServices"; import SitewideAnnouncements from "./SitewideAnnouncements"; -import SitewideSettings from "./SitewideSettings"; import MetadataServices from "./MetadataServices"; import CatalogServices from "./CatalogServices"; import DiscoveryServices from "./DiscoveryServices"; @@ -43,7 +42,6 @@ export default class ConfigTabContainer extends TabContainer< individualAdmins: IndividualAdmins, collections: Collections, patronAuth: PatronAuthServices, - sitewideSettings: SitewideSettings, metadata: MetadataServices, catalogServices: CatalogServices, discovery: DiscoveryServices, @@ -58,7 +56,6 @@ export default class ConfigTabContainer extends TabContainer< individualAdmins: "Admins", patronAuth: "Patron Authentication", sitewideAnnouncements: "Sitewide Announcements", - sitewideSettings: "Sitewide Settings", catalogServices: "External Catalogs", }; diff --git a/src/components/SitewideSettingEditForm.tsx b/src/components/SitewideSettingEditForm.tsx deleted file mode 100644 index 3c44db174..000000000 --- a/src/components/SitewideSettingEditForm.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import * as React from "react"; -import EditableInput from "./EditableInput"; -import { Button, Form } from "library-simplified-reusable-components"; -import { SitewideSettingsData, SitewideSettingData } from "../interfaces"; -import { clearForm } from "../utils/sharedFunctions"; - -export interface SitewideSettingEditFormProps { - data: SitewideSettingsData; - item?: SitewideSettingData; - disabled: boolean; - save?: (data: FormData) => void; - urlBase: string; - listDataKey: string; -} - -export interface SitewideSettingEditFormState { - inputKey: string; -} - -/** Form for editing a single sitewide setting. */ -export default class SitewideSettingEditForm extends React.Component< - SitewideSettingEditFormProps, - SitewideSettingEditFormState -> { - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.submit = this.submit.bind(this); - this.state = { - inputKey: this.availableSettings().length - ? this.availableSettings()[0].key - : "", - }; - } - - render(): JSX.Element { - const inputKey = this.state.inputKey; - const availableSettings = this.availableSettings(); - let settingToRender; - availableSettings.forEach((setting) => { - if (setting.key === inputKey) { - settingToRender = setting; - } - }); - const selectType = !!( - settingToRender && - settingToRender.options && - settingToRender.options.length - ); - return ( -
- {availableSettings.length > 0 && ( -
- - Enter or edit a value for the selected sitewide setting key - - - {availableSettings.map((setting) => ( - - ))} - - {selectType ? ( - - {availableSettings.map((setting) => { - if (setting.key === inputKey) { - return setting.options.map((s) => { - return ( - - ); - }); - } - })} - - ) : ( - - )} - {availableSettings.map((setting) => { - if (setting.key === inputKey && setting.description) { - return ( -

- ); - } - })} - - } - /> - )} - {this.availableSettings().length === 0 && ( -

All sitewide settings have already been created.

- )} -
- ); - } - - onChange(inputKey) { - this.setState({ inputKey }); - } - - availableSettings() { - const allSettings = (this.props.data && this.props.data.all_settings) || []; - if (this.props.item) { - for (const setting of allSettings) { - if (setting.key === this.props.item.key) { - return [setting]; - } - } - } else { - const availableSettings = []; - for (const possibleSetting of allSettings) { - let hasSetting = false; - for (const actualSetting of (this.props.data && - this.props.data.settings) || - []) { - if (actualSetting.key === possibleSetting.key) { - hasSetting = true; - } - } - if (!hasSetting) { - availableSettings.push(possibleSetting); - } - } - return availableSettings; - } - return allSettings; - } - - async submit(data: FormData) { - if (this.props.save) { - await this.props.save(data); - } - } - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.responseBody && !nextProps.fetchError) { - clearForm(this.refs); - } - } -} diff --git a/src/components/SitewideSettings.tsx b/src/components/SitewideSettings.tsx deleted file mode 100644 index b987624b3..000000000 --- a/src/components/SitewideSettings.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import EditableConfigList, { - EditableConfigListStateProps, - EditableConfigListDispatchProps, - EditableConfigListOwnProps, -} from "./EditableConfigList"; -import { connect } from "react-redux"; -import ActionCreator from "../actions"; -import { SitewideSettingsData, SitewideSettingData } from "../interfaces"; -import SitewideSettingEditForm from "./SitewideSettingEditForm"; - -/** Right panel for sitewide settings on the system configuration page. - Shows a list of current sitewide settings and allows creating a new - setting or editing or deleting an existing setting. */ -export class SitewideSettings extends EditableConfigList< - SitewideSettingsData, - SitewideSettingData -> { - EditForm = SitewideSettingEditForm; - listDataKey = "settings"; - itemTypeName = "sitewide setting"; - urlBase = "/admin/web/config/sitewideSettings/"; - identifierKey = "key"; - labelKey = "key"; - - label(item): string { - for (const setting of this.props.data.all_settings) { - if (setting.key === item.key) { - return setting.label; - } - } - return item.key; - } -} - -function mapStateToProps(state, ownProps) { - // fetchError = an error involving loading the list of sitewide settings; formError = an error upon submission of the - // create/edit form. - return { - data: state.editor.sitewideSettings && state.editor.sitewideSettings.data, - responseBody: - state.editor.sitewideSettings && - state.editor.sitewideSettings.successMessage, - fetchError: state.editor.sitewideSettings.fetchError, - formError: state.editor.sitewideSettings.formError, - isFetching: - state.editor.sitewideSettings.isFetching || - state.editor.sitewideSettings.isEditing, - }; -} - -function mapDispatchToProps(dispatch, ownProps) { - const actions = new ActionCreator(null, ownProps.csrfToken); - return { - fetchData: () => dispatch(actions.fetchSitewideSettings()), - editItem: (data: FormData) => dispatch(actions.editSitewideSetting(data)), - deleteItem: (identifier: string | number) => - dispatch(actions.deleteSitewideSetting(identifier)), - }; -} - -const ConnectedSitewideSettings = connect< - EditableConfigListStateProps, - EditableConfigListDispatchProps, - EditableConfigListOwnProps ->( - mapStateToProps, - mapDispatchToProps -)(SitewideSettings); - -export default ConnectedSitewideSettings; diff --git a/src/components/__tests__/ConfigTabContainer-test.tsx b/src/components/__tests__/ConfigTabContainer-test.tsx index 5beadc11d..1c9ce549f 100644 --- a/src/components/__tests__/ConfigTabContainer-test.tsx +++ b/src/components/__tests__/ConfigTabContainer-test.tsx @@ -10,7 +10,6 @@ import Libraries from "../Libraries"; import Collections from "../Collections"; import IndividualAdmins from "../IndividualAdmins"; import PatronAuthServices from "../PatronAuthServices"; -import SitewideSettings from "../SitewideSettings"; import MetadataServices from "../MetadataServices"; import CatalogServices from "../CatalogServices"; import DiscoveryServices from "../DiscoveryServices"; @@ -53,7 +52,6 @@ describe("ConfigTabContainer", () => { expect(linkTexts).to.contain("Admins"); expect(linkTexts).to.contain("Collections"); expect(linkTexts).to.contain("Patron Authentication"); - expect(linkTexts).to.contain("Sitewide Settings"); expect(linkTexts).to.contain("Metadata"); expect(linkTexts).to.contain("External Catalogs"); expect(linkTexts).to.contain("Sitewide Announcements"); @@ -65,7 +63,6 @@ describe("ConfigTabContainer", () => { IndividualAdmins, Collections, PatronAuthServices, - SitewideSettings, MetadataServices, CatalogServices, DiscoveryServices, @@ -114,7 +111,6 @@ describe("ConfigTabContainer", () => { expect(linkTexts).to.contain("Admins"); expect(linkTexts).not.to.contain("Collections"); expect(linkTexts).not.to.contain("Patron Authentication"); - expect(linkTexts).not.to.contain("Sitewide Settings"); expect(linkTexts).not.to.contain("Metadata"); expect(linkTexts).not.to.contain("External Catalogs"); }); @@ -131,7 +127,6 @@ describe("ConfigTabContainer", () => { const hiddenComponentClasses = [ Collections, PatronAuthServices, - SitewideSettings, MetadataServices, CatalogServices, DiscoveryServices, @@ -181,7 +176,6 @@ describe("ConfigTabContainer", () => { Collections, IndividualAdmins, PatronAuthServices, - SitewideSettings, MetadataServices, CatalogServices, DiscoveryServices, diff --git a/src/components/__tests__/SitewideSettingEditForm-test.tsx b/src/components/__tests__/SitewideSettingEditForm-test.tsx deleted file mode 100644 index 5e41b71bf..000000000 --- a/src/components/__tests__/SitewideSettingEditForm-test.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import { expect } from "chai"; -import { stub, spy } from "sinon"; - -import * as React from "react"; -import { shallow, mount } from "enzyme"; - -import SitewideSettingEditForm from "../SitewideSettingEditForm"; -import EditableInput from "../EditableInput"; -import { Button, Form } from "library-simplified-reusable-components"; - -describe("SitewideSettingEditForm", () => { - let wrapper; - let save; - const settingData = { - key: "test_key", - value: "value", - }; - const settingDataWithDescription = { - key: "other_key1", - value: "label1", - description: "some description", - }; - const settingDataWithSelect = { - key: "other_key2", - value: "label2", - }; - const allSettings = [ - { key: "test_key", label: "label" }, - { key: "other_key1", label: "label1", description: "some description" }, - { - key: "other_key2", - label: "label2", - type: "select", - options: [ - { key: "select_key1", label: "select label1" }, - { key: "select_key2", label: "select label2" }, - { key: "select_key3", label: "select label3" }, - { key: "select_key4", label: "select label4" }, - ], - }, - ]; - const settingsData = { - settings: [settingData], - all_settings: allSettings, - }; - - const editableInputByName = (name) => { - const inputs = wrapper.find(EditableInput); - if (inputs.length >= 1) { - return inputs.filterWhere((input) => input.props().name === name); - } - return []; - }; - - describe("rendering", () => { - beforeEach(() => { - save = stub(); - wrapper = mount( - - ); - }); - - it("renders message if there are no remaining fields", () => { - const data = { - settings: [settingData], - all_settings: [allSettings[0]], - }; - wrapper = shallow( - - ); - const message = wrapper.find("p"); - expect(wrapper.text()).to.contain("All sitewide settings"); - // prettier-ignore - const input = wrapper.find("input[name=\"csrf_token\"]"); - expect(input.length).to.equal(0); - }); - - it("renders key", () => { - let input = editableInputByName("key"); - expect(input.props().value).not.to.be.ok; - expect(input.props().readOnly).to.equal(false); - let children = input.find("option"); - expect(children.length).to.equal(2); - expect(children.at(0).text()).to.contain("label1"); - expect(children.at(1).text()).to.contain("label2"); - - wrapper.setProps({ item: settingData }); - input = editableInputByName("key"); - expect(input.props().value).to.equal("test_key"); - expect(input.props().readOnly).to.equal(true); - children = input.find("option"); - expect(children.length).to.equal(1); - }); - - it("renders value", () => { - let input = editableInputByName("value"); - expect(input.props().value).not.to.be.ok; - - wrapper.setProps({ item: settingData }); - input = editableInputByName("value"); - expect(input.props().value).to.equal("value"); - }); - - it("should render a description", () => { - wrapper.setProps({ item: settingDataWithDescription }); - const description = wrapper.find(Form).find(".description").at(1); - expect(description.prop("dangerouslySetInnerHTML")).to.eql({ - __html: "some description", - }); - }); - - it("should render a select value option", () => { - wrapper.setProps({ item: settingDataWithSelect }); - wrapper.setState({ inputKey: "other_key2" }); - - const firstSelect = editableInputByName("key"); - const dynamicSelect = editableInputByName("value"); - expect(firstSelect.props().value).to.equal("other_key2"); - expect(dynamicSelect.props().value).to.equal("label2"); - }); - }); - - describe("behavior", () => { - beforeEach(() => { - save = stub().returns( - new Promise((resolve) => resolve()) - ); - wrapper = mount( - - ); - }); - - it("calls save when the save button is clicked", () => { - const saveButton = wrapper.find(Button); - saveButton.simulate("click"); - expect(save.callCount).to.equal(1); - }); - - it("calls save when the form is submitted directly", () => { - const form = wrapper.find(Form); - form.prop("onSubmit")(); - expect(save.callCount).to.equal(1); - }); - - it("submits data", () => { - wrapper.setProps({ item: settingData }); - - const saveButton = wrapper.find(Button); - saveButton.simulate("click"); - - expect(save.callCount).to.equal(1); - const formData = save.args[0][0]; - expect(formData.get("key")).to.equal("test_key"); - expect(formData.get("value")).to.equal("value"); - }); - - const fillOutFormFields = () => { - wrapper.setProps({ item: settingData }); - const input = wrapper.find("input[name='value']"); - // For input elements, use `getDOMNode`. The previous API method, - // .get(0), outputs the following error message: - // Cannot add property value, object is not extensible - const inputElement = input.getDOMNode(); - inputElement.value = "new setting"; - input.simulate("change"); - }; - - it("clears the form", () => { - fillOutFormFields(); - let select = wrapper.find("select[name='key']"); - let input = wrapper.find("input[name='value']"); - - expect(select.props().value).to.equal("test_key"); - expect(input.props().value).to.equal("new setting"); - - wrapper.find("form").simulate("submit"); - const newProps = { responseBody: "new setting", ...wrapper.props() }; - wrapper.setProps(newProps); - - input = wrapper.find("input[name='value']"); - select = wrapper.find("select[name='key']"); - expect(input.props().value).to.equal(""); - expect(select.props().value).to.equal(""); - }); - - it("doesn't clear the form if there's an error message", () => { - fillOutFormFields(); - let select = wrapper.find("select[name='key']"); - let input = wrapper.find("input[name='value']"); - - expect(select.props().value).to.equal("test_key"); - expect(input.props().value).to.equal("new setting"); - - wrapper.find("form").simulate("submit"); - const newProps = { fetchError: "ERROR", ...wrapper.props() }; - wrapper.setProps(newProps); - - input = wrapper.find("input[name='value']"); - select = wrapper.find("select[name='key']"); - expect(input.props().value).to.equal("new setting"); - expect(select.props().value).to.equal("test_key"); - }); - - it("should switch between rendering a select and input value options", () => { - let select = wrapper.find("select"); - let input = wrapper.find("input"); - - expect(select.length).to.equal(1); - expect(input.length).to.equal(1); - - // select.simulate("change", { target: { value: "other_key2" } }); - wrapper.setState({ inputKey: "other_key2" }); - - select = wrapper.find("select"); - input = wrapper.find("input"); - expect(select.length).to.equal(2); - expect(input.length).to.equal(0); - - // select.simulate("change", { target: { value: "other_key2" } }); - wrapper.setState({ inputKey: "other_key1 " }); - - select = wrapper.find("select"); - input = wrapper.find("input"); - expect(select.length).to.equal(1); - expect(input.length).to.equal(1); - }); - }); -}); diff --git a/src/components/__tests__/SitewideSettings-test.tsx b/src/components/__tests__/SitewideSettings-test.tsx deleted file mode 100644 index 85fb17734..000000000 --- a/src/components/__tests__/SitewideSettings-test.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { expect } from "chai"; -import { stub } from "sinon"; - -import * as React from "react"; -import { mount } from "enzyme"; - -import { SitewideSettings } from "../SitewideSettings"; - -describe("SitewideSettings", () => { - let wrapper; - let fetchData; - let editItem; - const data = { - settings: [{ key: "test key", value: "test value" }], - all_settings: [{ key: "test key", label: "test label" }], - }; - - const pause = () => { - return new Promise((resolve) => setTimeout(resolve, 0)); - }; - - beforeEach(() => { - fetchData = stub(); - editItem = stub().returns( - new Promise((resolve) => resolve()) - ); - - wrapper = mount( - - ); - }); - - it("shows sitewide setting list", () => { - const sitewideSetting = wrapper.find("li"); - expect(sitewideSetting.length).to.equal(1); - expect(sitewideSetting.at(0).text()).to.contain("test label"); - const editLink = sitewideSetting.at(0).find("a").at(0); - expect(editLink.props().href).to.equal( - "/admin/web/config/sitewideSettings/edit/test key" - ); - }); -}); diff --git a/src/interfaces.ts b/src/interfaces.ts index 3f9234fe3..52540ed44 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -358,18 +358,6 @@ export interface PatronData { username?: string; } -export interface SitewideSettingData { - key: string; - value: string; - description?: string; - required?: boolean; -} - -export interface SitewideSettingsData { - settings: SitewideSettingData[]; - all_settings: SettingData[]; -} - export interface MetadataServiceData extends ServiceData {} export interface MetadataServicesData extends ServicesData { diff --git a/src/reducers/index.ts b/src/reducers/index.ts index 526626af7..4ebf606f9 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -13,7 +13,6 @@ import collections from "./collections"; import individualAdmins from "./individualAdmins"; import patronAuthServices from "./patronAuthServices"; import sitewideAnnouncements from "./sitewideAnnouncements"; -import sitewideSettings from "./sitewideSettings"; import metadataServices from "./metadataServices"; import catalogServices from "./catalogServices"; import discoveryServices from "./discoveryServices"; @@ -47,7 +46,6 @@ import { IndividualAdminsData, PatronAuthServicesData, SitewideAnnouncementsData, - SitewideSettingsData, MetadataServicesData, CatalogServicesData, DiscoveryServicesData, @@ -78,7 +76,6 @@ export interface State { individualAdmins: FetchEditState; patronAuthServices: FetchEditState; sitewideAnnouncements: FetchEditState; - sitewideSettings: FetchEditState; metadataServices: FetchEditState; catalogServices: FetchEditState; discoveryServices: FetchEditState; @@ -118,7 +115,6 @@ export default combineReducers({ individualAdmins, patronAuthServices, sitewideAnnouncements, - sitewideSettings, metadataServices, catalogServices, discoveryServices, diff --git a/src/reducers/sitewideSettings.ts b/src/reducers/sitewideSettings.ts deleted file mode 100644 index abdf6fde7..000000000 --- a/src/reducers/sitewideSettings.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SitewideSettingsData } from "../interfaces"; -import ActionCreator from "../actions"; -import createFetchEditReducer from "./createFetchEditReducer"; - -export default createFetchEditReducer( - ActionCreator.SITEWIDE_SETTINGS, - ActionCreator.EDIT_SITEWIDE_SETTING -); diff --git a/src/utils/sharedFunctions.ts b/src/utils/sharedFunctions.ts index 84dd9d340..0510a791a 100644 --- a/src/utils/sharedFunctions.ts +++ b/src/utils/sharedFunctions.ts @@ -21,7 +21,7 @@ export function findDefault(setting) { // Blank out the create form on successful submission, so that the user can go // ahead and create another new thing. Used by IndividualAdminEditForm, -// LibraryEditForm, SitewideSettingEditForm, and ServiceEditForm. +// LibraryEditForm, and ServiceEditForm. export function clearForm(refs, useCurrent = false) { if (refs) { const keys = Object.keys(refs);