diff --git a/src/actions.ts b/src/actions.ts index 578ccac3f..6cfd065b5 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -1,6 +1,5 @@ import { AdvancedSearchQuery, - BookData, ComplaintsData, GenreTree, ClassificationData, @@ -131,14 +130,6 @@ export default class ActionCreator extends BaseActionCreator { static readonly RESET_LANES = "RESET_LANES"; static readonly CHANGE_LANE_ORDER = "CHANGE_LANE_ORDER"; - // static readonly EDIT_BOOK_REQUEST = "EDIT_BOOK_REQUEST"; - // static readonly EDIT_BOOK_SUCCESS = "EDIT_BOOK_SUCCESS"; - // static readonly EDIT_BOOK_FAILURE = "EDIT_BOOK_FAILURE"; - // static readonly BOOK_ADMIN_REQUEST = "BOOK_ADMIN_REQUEST"; - // static readonly BOOK_ADMIN_SUCCESS = "BOOK_ADMIN_SUCCESS"; - // static readonly BOOK_ADMIN_FAILURE = "BOOK_ADMIN_FAILURE"; - // static readonly BOOK_ADMIN_LOAD = "BOOK_ADMIN_LOAD"; - static readonly COMPLAINTS_REQUEST = "COMPLAINTS_REQUEST"; static readonly COMPLAINTS_SUCCESS = "COMPLAINTS_SUCCESS"; static readonly COMPLAINTS_FAILURE = "COMPLAINTS_FAILURE"; @@ -329,14 +320,6 @@ export default class ActionCreator extends BaseActionCreator { }; } - // fetchBookAdmin(url: string) { - // return this.fetchOPDS(ActionCreator.BOOK_ADMIN, url).bind(this); - // } - - // editBook(url: string, data: FormData | null) { - // return this.postForm(ActionCreator.EDIT_BOOK, url, data).bind(this); - // } - fetchRoles() { const url = "/admin/roles"; return this.fetchJSON(ActionCreator.ROLES, url).bind(this); diff --git a/src/components/BookDetailsEditor.tsx b/src/components/BookDetailsEditor.tsx index 23f7f5229..bd8813145 100644 --- a/src/components/BookDetailsEditor.tsx +++ b/src/components/BookDetailsEditor.tsx @@ -26,7 +26,7 @@ export type BookDetailsEditorProps = ConnectedProps & export class BookDetailsEditor extends React.Component { constructor(props) { super(props); - this.editBook = this.editBook.bind(this); + this.postWithoutPayload = this.postWithoutPayload.bind(this); this.hide = this.hide.bind(this); this.restore = this.restore.bind(this); this.refreshMetadata = this.refreshMetadata.bind(this); @@ -36,7 +36,7 @@ export class BookDetailsEditor extends React.Component { UNSAFE_componentWillMount() { if (this.props.bookUrl) { const bookAdminUrl = this.props.bookUrl.replace("works", "admin/works"); - this.props.fetchBook(bookAdminUrl); + this.props.fetchBookData(bookAdminUrl); this.props.fetchRoles(); this.props.fetchMedia(); this.props.fetchLanguages(); @@ -46,7 +46,7 @@ export class BookDetailsEditor extends React.Component { UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.bookUrl && nextProps.bookUrl !== this.props.bookUrl) { const bookAdminUrl = nextProps.bookUrl.replace("works", "admin/works"); - this.props.fetchBook(bookAdminUrl); + this.props.fetchBookData(bookAdminUrl); } } @@ -100,7 +100,7 @@ export class BookDetailsEditor extends React.Component { media={this.props.media} languages={this.props.languages} disabled={this.props.isFetching} - editBook={this.props.editBook} + editBook={this.props.postBookData} refresh={this.refresh} /> )} @@ -114,24 +114,24 @@ export class BookDetailsEditor extends React.Component { } hide() { - return this.editBook(this.props.bookData.hideLink.href); + return this.postWithoutPayload(this.props.bookData.hideLink.href); } restore() { - return this.editBook(this.props.bookData.restoreLink.href); + return this.postWithoutPayload(this.props.bookData.restoreLink.href); } refreshMetadata() { - return this.editBook(this.props.bookData.refreshLink.href); + return this.postWithoutPayload(this.props.bookData.refreshLink.href); } refresh() { - this.props.fetchBook(this.props.bookAdminUrl); + this.props.fetchBookData(this.props.bookAdminUrl); this.props.refreshCatalog(); } - editBook(url) { - return this.props.editBook(url, null).then(this.refresh); + postWithoutPayload(url) { + return this.props.postBookData(url, null).then(this.refresh); } } @@ -166,9 +166,9 @@ function mapDispatchToProps( const fetcher = new DataFetcher({ adapter: editorAdapter }); const actions = new ActionCreator(fetcher, ownProps.csrfToken); return { - editBook: (url: string, data) => + postBookData: (url: string, data) => dispatch(submitBookData({ url, data, csrfToken: ownProps.csrfToken })), - fetchBook: (url: string) => dispatch(getBookData({ url })), + fetchBookData: (url: string) => dispatch(getBookData({ url })), fetchRoles: () => dispatch(actions.fetchRoles()), fetchMedia: () => dispatch(actions.fetchMedia()), fetchLanguages: () => dispatch(actions.fetchLanguages()), diff --git a/src/features/book/bookEditorSlice.ts b/src/features/book/bookEditorSlice.ts index a2c335084..5f237ed5c 100644 --- a/src/features/book/bookEditorSlice.ts +++ b/src/features/book/bookEditorSlice.ts @@ -6,6 +6,7 @@ import DataFetcher, { import editorAdapter from "../../editorAdapter"; import { submitForm } from "../../api/submitForm"; import { RootState } from "../../store"; +import ActionCreator from "../../actions"; export interface BookState { url: string; @@ -33,13 +34,17 @@ interface InitialState { const bookEditorSlice = createSlice({ name: "bookEditor", initialState, - reducers: { - bookCleared(state, action) { - state = initialState; - }, - }, + reducers: {}, extraReducers: (builder) => { builder + .addCase(ActionCreator.BOOK_CLEAR, (state, action) => { + // Handle resetting the book data via actions from the web-opds-client. + console.log("*** Handling clear book data action ***", action.type, { + action, + state, + }); + return initialState; + }) .addCase(getBookData.pending, (state, action) => { // console.log("getBookData.pending", { action, state }); const { url } = action.meta.arg; @@ -49,7 +54,6 @@ const bookEditorSlice = createSlice({ state.fetchError = null; }) .addCase(getBookData.fulfilled, (state, action) => { - // console.log("getBookData.fulfilled", { action, state }); const { url } = action.meta.arg; state.url = url; state.data = action.payload as BookData; @@ -57,7 +61,6 @@ const bookEditorSlice = createSlice({ state.fetchError = null; }) .addCase(getBookData.rejected, (state, action) => { - // console.log("getBookData.rejected", { action, state }); const { url } = action.meta.arg; state.url = url; state.data = null; @@ -65,17 +68,14 @@ const bookEditorSlice = createSlice({ state.fetchError = action.payload as RequestError; }) .addCase(submitBookData.pending, (state, action) => { - // console.log("submitBookData.pending", { action, state }); state.isFetching = true; state.editError = null; }) .addCase(submitBookData.fulfilled, (state, action) => { - // console.log("submitBookData.fulfilled", { action, state }); state.isFetching = false; state.editError = null; }) .addCase(submitBookData.rejected, (state, action) => { - // console.log("submitBookData.rejected", { action, state }); state.isFetching = true; state.editError = action.payload as RequestError; }) diff --git a/src/reducers/__tests__/book-test.ts b/src/reducers/__tests__/book-test.ts index ca9e660ce..bff2e3f67 100644 --- a/src/reducers/__tests__/book-test.ts +++ b/src/reducers/__tests__/book-test.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; -import book from "../book"; +// import book from "../book"; +import book from "../../features/book/bookEditorSlice"; import ActionCreator from "../../actions"; import bookEditorSlice, { getBookData, @@ -26,15 +27,16 @@ describe("book reducer", () => { editError: null, }; - it("returns initial state for unrecognized action", () => { - expect(book(undefined, {})).to.deep.equal(initState); - }); + // it("returns initial state for unrecognized action", () => { + // expect(book(undefined, {})).to.deep.equal(initState); + // }); // TODO: test clearBook - // it("handles CLEAR_BOOK", () => { - // const action = { type: ActionCreator.BOOK_CLEAR }; - // expect(book(fetchedState, action)).to.deep.equal(initState); - // }); + it("handles CLEAR_BOOK", () => { + const action = { type: ActionCreator.BOOK_CLEAR }; + console.log("result", book(fetchedState, action)); + expect(book(fetchedState, action)).to.deep.equal(initState); + }); // it("handles BOOK_ADMIN_REQUEST", () => { // const action = { type: ActionCreator.BOOK_ADMIN_REQUEST, url: "test url" }; diff --git a/tests/jest/features/book.test.ts b/tests/jest/features/book.test.ts index 609db94a6..1129b61d2 100644 --- a/tests/jest/features/book.test.ts +++ b/tests/jest/features/book.test.ts @@ -10,6 +10,7 @@ import { store } from "../../../src/store"; import { BookData } from "@thepalaceproject/web-opds-client/lib/interfaces"; import { RequestError } from "@thepalaceproject/web-opds-client/lib/DataFetcher"; import { AsyncThunkAction, Dispatch } from "@reduxjs/toolkit"; +import ActionCreator from "@thepalaceproject/web-opds-client/lib/actions"; const SAMPLE_BOOK_ADMIN_DETAIL = ` @@ -49,6 +50,14 @@ const FETCH_OPDS_PARSE_ERROR_MESSAGE = "Failed to parse OPDS data"; describe("Redux bookEditorSlice...", () => { const bookData = { id: "urn:something:something", title: "test title" }; + const fetchedState = { + url: "test url", + data: { ...bookData }, + isFetching: false, + fetchError: null, + editError: null, + }; + describe("reducers...", () => { it("should return the initial state from undefined, if no action is passed", () => { expect(reducer(undefined, { type: "unknown" })).to.deep.equal( @@ -60,6 +69,12 @@ describe("Redux bookEditorSlice...", () => { initialState ); }); + it("should handle BOOK_CLEAR", () => { + const action = { type: ActionCreator.BOOK_CLEAR }; + + expect(reducer(fetchedState, action)).to.deep.equal(initialState); + }); + it("should handle getBookData.pending", () => { const action = { type: getBookData.pending.type,