Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: MIT

extends:
- '@inveniosoftware/eslint-config-invenio'
- '@inveniosoftware/eslint-config-invenio/configs/react18.yaml'
- '@inveniosoftware/eslint-config-invenio/prettier'

parser: '@babel/eslint-parser'
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# SPDX-License-Identifier: MIT

extends:
- '@inveniosoftware/eslint-config-invenio'
- '@inveniosoftware/eslint-config-invenio/configs/react18.yaml'
- '@inveniosoftware/eslint-config-invenio/prettier'
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"@inveniosoftware/eslint-config-invenio/prettier-config.js"
"@inveniosoftware/eslint-config-invenio/prettier-config.js"
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@
"This package.json is needed to run the JS tests, locally and CI."
],
"scripts": {
"test": "react-scripts test"
"test": "vitest"
},
"devDependencies": {
"@testing-library/jest-dom": "^4.2.0",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.0",
"axios": "^1.7.7",
"coveralls": "^3.0.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.0",
"expect": "^26.0.0",
"lodash": "^4.17.0",
"luxon": "^1.23.0",
"react": "^16.13.0",
"react-dom": "^16.13.0",
"react-scripts": "^5.0.1",
"semantic-ui-react": "^2.1.0",
"react-overridable": "^0.0.3"
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@vitejs/plugin-react": "^6.0.1",
"axios": "^1.15.2",
"enzyme": "^3.11.0",
"expect": "^30.3.0",
"jsdom": "^29.1.1",
"lodash": "^4.18.1",
"luxon": "^3.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-overridable": "^1.0.0",
"semantic-ui-react": "^3.0.0-beta.2",
"vitest": "^4.1.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "@testing-library/jest-dom";
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,35 @@
*/

import PropTypes from "prop-types";
import React, { Component } from "react";
import { Modal } from "semantic-ui-react";
import Overridable from "react-overridable";

class ActionModal extends Component {
render() {
const { children, modalOpen, modalProps, resource } = this.props;

return (
<Overridable
id="InvenioAdministration.ActionModal.layout"
modalOpen={modalOpen}
// eslint-disable-next-line react/no-children-prop
children={children}
resource={resource}
>
<Modal role="dialog" open={modalOpen} {...modalProps}>
{children}
</Modal>
</Overridable>
);
}
}
const ActionModal = ({
children = null,
modalOpen = false,
modalProps = {},
resource,
}) => {
return (
<Overridable
id="InvenioAdministration.ActionModal.layout"
modalOpen={modalOpen}
// eslint-disable-next-line react/no-children-prop
children={children}
resource={resource}
>
<Modal role="dialog" open={modalOpen} {...modalProps}>
{children}
</Modal>
</Overridable>
);
};

ActionModal.propTypes = {
children: PropTypes.object,
children: PropTypes.node,
modalOpen: PropTypes.bool,
modalProps: PropTypes.object,
resource: PropTypes.object.isRequired,
};

ActionModal.defaultProps = {
modalOpen: false,
modalProps: {},
children: null,
};

export default Overridable.component("InvenioAdministration.ActionModal", ActionModal);
Original file line number Diff line number Diff line change
Expand Up @@ -6,94 +6,89 @@
import Edit from "./Edit";
import Delete from "./Delete";
import { DeleteModalTrigger } from "./DeleteModalTrigger";
import React, { Component } from "react";
import PropTypes from "prop-types";
import isEmpty from "lodash/isEmpty";
import ResourceActions from "./ResourceActions";
import { Button, Dropdown } from "semantic-ui-react";
import { i18next } from "@translations/invenio_administration/i18next";
import _get from "lodash/get";

export class Actions extends Component {
render() {
const {
title,
resourceName,
actions,
resource,
successCallback,
idKeyPath,
editUrl,
displayEdit,
displayDelete,
displayAsDropdown,
} = this.props;
export function Actions({
title,
resourceName,
actions,
resource,
successCallback,
idKeyPath = "pid",
editUrl,
displayEdit = true,
displayDelete = true,
displayAsDropdown = false,
}) {
// if number of actions is greater than 3, we display all in a dropdown
const _displayAsDropdown =
displayAsDropdown ||
(displayEdit && displayDelete && Object.keys(actions).length > 1);

// if number of actions is greater than 3, we display all in a dropdown
const _displayAsDropdown =
displayAsDropdown ||
(displayEdit && displayDelete && Object.keys(actions).length > 1);

if (_displayAsDropdown) {
return (
<Dropdown
trigger={
<Button
icon="cog"
size="tiny"
className="transparent rel-ml-1"
aria-label={i18next.t("Open list of actions")}
/>
}
>
<Dropdown.Menu>
{!isEmpty(actions) && (
<ResourceActions
resource={resource}
successCallback={successCallback}
idKeyPath={idKeyPath}
actions={actions}
Element={Dropdown.Item}
/>
)}
{displayEdit && <Edit editUrl={editUrl} resource={resource} />}
{displayDelete && (
<DeleteModalTrigger
title={title}
resourceName={resourceName}
apiEndpoint={_get(resource, "links.self")}
resource={resource}
successCallback={successCallback}
idKeyPath={idKeyPath}
Element={Dropdown.Item}
/>
)}
</Dropdown.Menu>
</Dropdown>
);
} else {
return (
<>
if (_displayAsDropdown) {
return (
<Dropdown
trigger={
<Button
icon="cog"
size="tiny"
className="transparent rel-ml-1"
aria-label={i18next.t("Open list of actions")}
/>
}
>
<Dropdown.Menu>
{!isEmpty(actions) && (
<ResourceActions
resource={resource}
successCallback={successCallback}
idKeyPath={idKeyPath}
actions={actions}
Element={Dropdown.Item}
/>
)}
{displayEdit && <Edit editUrl={editUrl} resource={resource} />}
{displayDelete && (
<Delete
successCallback={successCallback}
resource={resource}
resourceName={resourceName}
<DeleteModalTrigger
title={title}
resourceName={resourceName}
apiEndpoint={_get(resource, "links.self")}
resource={resource}
successCallback={successCallback}
idKeyPath={idKeyPath}
Element={Dropdown.Item}
/>
)}
</>
);
}
</Dropdown.Menu>
</Dropdown>
);
} else {
return (
<>
{!isEmpty(actions) && (
<ResourceActions
resource={resource}
successCallback={successCallback}
idKeyPath={idKeyPath}
actions={actions}
/>
)}
{displayEdit && <Edit editUrl={editUrl} resource={resource} />}
{displayDelete && (
<Delete
successCallback={successCallback}
resource={resource}
resourceName={resourceName}
title={title}
/>
)}
</>
);
}
}

Expand All @@ -109,10 +104,3 @@ Actions.propTypes = {
actions: PropTypes.object.isRequired,
editUrl: PropTypes.string.isRequired,
};

Actions.defaultProps = {
displayEdit: true,
displayDelete: true,
displayAsDropdown: false,
idKeyPath: "pid",
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,44 @@
*/

import { DeleteModalTrigger } from "./DeleteModalTrigger";
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Popup } from "semantic-ui-react";
import { i18next } from "@translations/invenio_administration/i18next";
import _get from "lodash/get";
import Overridable from "react-overridable";

class DeleteCmp extends Component {
render() {
const {
disabledMessage,
disable,
title,
resourceName,
successCallback,
idKeyPath,
resource,
} = this.props;
const DeleteCmp = ({
disabledMessage = i18next.t("Resource is not deletable."),
disable = () => false,
title,
resourceName,
successCallback,
idKeyPath = "pid",
resource,
}) => {
const disabled = disable(resource);

const disabled = disable(resource);

return (
<Popup
content={disabledMessage}
disabled={!disabled}
trigger={
<span>
<DeleteModalTrigger
title={title}
resourceName={resourceName}
resource={resource}
successCallback={successCallback}
idKeyPath={idKeyPath}
disabled={disabled}
apiEndpoint={_get(resource, "links.self")}
disabledDeleteMessage={disabledMessage}
/>
</span>
}
/>
);
}
}
return (
<Popup
content={disabledMessage}
disabled={!disabled}
trigger={
<span>
<DeleteModalTrigger
title={title}
resourceName={resourceName}
resource={resource}
successCallback={successCallback}
idKeyPath={idKeyPath}
disabled={disabled}
apiEndpoint={_get(resource, "links.self")}
disabledDeleteMessage={disabledMessage}
/>
</span>
}
/>
);
};

DeleteCmp.propTypes = {
disabledMessage: PropTypes.string,
Expand All @@ -58,10 +53,4 @@ DeleteCmp.propTypes = {
resource: PropTypes.object.isRequired,
};

DeleteCmp.defaultProps = {
disabledMessage: i18next.t("Resource is not deletable."),
idKeyPath: "pid",
disable: () => false,
};

export default Overridable.component("InvenioAdministration.DeleteAction", DeleteCmp);
Loading
Loading