revert browser newux changes (#5714)

This commit is contained in:
Kanagaraj M
2018-03-27 01:19:12 +05:30
committed by Dee Koder
parent 35e64573fa
commit 19451e374a
186 changed files with 4507 additions and 16033 deletions

View File

@@ -23,7 +23,7 @@ export const DeleteObjectConfirmModal = ({
}) => (
<ConfirmModal
show={true}
icon="zmdi zmdi-alert-octagon"
icon="fa fa-exclamation-triangle mci-red"
text="Are you sure you want to delete?"
sub="This cannot be undone!"
okText="Delete"

View File

@@ -0,0 +1,107 @@
/*
* Minio Cloud Storage (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from "react"
import { connect } from "react-redux"
import { Dropdown } from "react-bootstrap"
import ShareObjectModal from "./ShareObjectModal"
import DeleteObjectConfirmModal from "./DeleteObjectConfirmModal"
import * as objectsActions from "./actions"
import {
SHARE_OBJECT_EXPIRY_DAYS,
SHARE_OBJECT_EXPIRY_HOURS,
SHARE_OBJECT_EXPIRY_MINUTES
} from "../constants"
export class ObjectActions extends React.Component {
constructor(props) {
super(props)
this.state = {
showDeleteConfirmation: false
}
}
shareObject(e) {
e.preventDefault()
const { object, shareObject } = this.props
shareObject(
object.name,
SHARE_OBJECT_EXPIRY_DAYS,
SHARE_OBJECT_EXPIRY_HOURS,
SHARE_OBJECT_EXPIRY_MINUTES
)
}
deleteObject() {
const { object, deleteObject } = this.props
deleteObject(object.name)
}
showDeleteConfirmModal(e) {
e.preventDefault()
this.setState({ showDeleteConfirmation: true })
}
hideDeleteConfirmModal() {
this.setState({
showDeleteConfirmation: false
})
}
render() {
const { object, showShareObjectModal } = this.props
return (
<Dropdown id={`obj-actions-${object.name}`}>
<Dropdown.Toggle noCaret className="fia-toggle" />
<Dropdown.Menu>
<a
href=""
className="fiad-action"
onClick={this.shareObject.bind(this)}
>
<i className="fa fa-copy" />
</a>
<a
href=""
className="fiad-action"
onClick={this.showDeleteConfirmModal.bind(this)}
>
<i className="fa fa-trash" />
</a>
</Dropdown.Menu>
{showShareObjectModal && <ShareObjectModal object={object} />}
{this.state.showDeleteConfirmation && (
<DeleteObjectConfirmModal
deleteObject={this.deleteObject.bind(this)}
hideDeleteConfirmModal={this.hideDeleteConfirmModal.bind(this)}
/>
)}
</Dropdown>
)
}
}
const mapStateToProps = (state, ownProps) => {
return {
object: ownProps.object,
showShareObjectModal: state.objects.shareObject.show
}
}
const mapDispatchToProps = dispatch => {
return {
shareObject: (object, days, hours, minutes) =>
dispatch(objectsActions.shareObject(object, days, hours, minutes)),
deleteObject: object => dispatch(objectsActions.deleteObject(object))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ObjectActions)

View File

@@ -19,23 +19,37 @@ import { connect } from "react-redux"
import humanize from "humanize"
import Moment from "moment"
import ObjectItem from "./ObjectItem"
import ObjectActions from "./ObjectActions"
import * as actionsObjects from "./actions"
import { getCheckedList } from "./selectors"
export const ObjectContainer = ({ object, downloadObject }) => {
export const ObjectContainer = ({
object,
checkedObjectsCount,
downloadObject
}) => {
let props = {
name: object.name,
contentType: object.contentType,
size: humanize.filesize(object.size),
lastModified: Moment(object.lastModified).format("lll")
}
if (checkedObjectsCount == 0) {
props.actionButtons = <ObjectActions object={object} />
}
return <ObjectItem {...props} onClick={() => downloadObject(object.name)} />
}
const mapStateToProps = state => {
return {
checkedObjectsCount: getCheckedList(state).length
}
}
const mapDispatchToProps = dispatch => {
return {
downloadObject: object => dispatch(actionsObjects.downloadObject(object))
}
}
export default connect(undefined, mapDispatchToProps)(ObjectContainer)
export default connect(mapStateToProps, mapDispatchToProps)(ObjectContainer)

View File

@@ -15,8 +15,9 @@
*/
import React from "react"
import classNames from "classnames"
import { connect } from "react-redux"
import humanize from "humanize"
import Moment from "moment"
import { getDataType } from "../mime"
import * as actions from "./actions"
import { getCheckedList } from "./selectors"
@@ -29,20 +30,13 @@ export const ObjectItem = ({
checked,
checkObject,
uncheckObject,
actionButtons,
onClick
}) => {
return (
<div
className={classNames({
objects__row: true,
"objects__row--directory": getDataType(name, contentType) == "folder"
})}
>
<div
className="objects__column objects__column--select"
data-object-type={getDataType(name, contentType)}
>
<div className="objects__select">
<div className={"fesl-row"} data-type={getDataType(name, contentType)}>
<div className="fesl-item fesl-item-icon">
<div className="fi-select">
<input
type="checkbox"
name={name}
@@ -51,10 +45,11 @@ export const ObjectItem = ({
checked ? uncheckObject(name) : checkObject(name)
}}
/>
<i />
<i className="fis-icon" />
<i className="fis-helper" />
</div>
</div>
<div className="objects__column objects__column--name">
<div className="fesl-item fesl-item-name">
<a
href="#"
onClick={e => {
@@ -65,10 +60,9 @@ export const ObjectItem = ({
{name}
</a>
</div>
<div className="objects__column objects__column--size">{size}</div>
<div className="objects__column objects__column--date">
{lastModified}
</div>
<div className="fesl-item fesl-item-size">{size}</div>
<div className="fesl-item fesl-item-modified">{lastModified}</div>
<div className="fesl-item fesl-item-actions">{actionButtons}</div>
</div>
)
}

View File

@@ -0,0 +1,101 @@
/*
* Minio Cloud Storage (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from "react"
import { connect } from "react-redux"
import classNames from "classnames"
import * as actions from "./actions"
import { getCheckedList } from "./selectors"
import DeleteObjectConfirmModal from "./DeleteObjectConfirmModal"
export class ObjectsBulkActions extends React.Component {
constructor(props) {
super(props)
this.state = {
showDeleteConfirmation: false
}
}
deleteChecked() {
const { deleteChecked } = this.props
deleteChecked()
this.hideDeleteConfirmModal()
}
hideDeleteConfirmModal() {
this.setState({
showDeleteConfirmation: false
})
}
render() {
const { checkedObjectsCount, downloadChecked, clearChecked } = this.props
return (
<div
className={
"list-actions" +
classNames({
" list-actions-toggled": checkedObjectsCount > 0
})
}
>
<span className="la-label">
<i className="fa fa-check-circle" /> {checkedObjectsCount} Objects
selected
</span>
<span className="la-actions pull-right">
<button id="download-checked" onClick={downloadChecked}>
{" "}
Download all as zip{" "}
</button>
</span>
<span className="la-actions pull-right">
<button
id="delete-checked"
onClick={() => this.setState({ showDeleteConfirmation: true })}
>
{" "}
Delete selected{" "}
</button>
</span>
<i
className="la-close fa fa-times"
id="close-bulk-actions"
onClick={clearChecked}
/>
{this.state.showDeleteConfirmation && (
<DeleteObjectConfirmModal
deleteObject={this.deleteChecked.bind(this)}
hideDeleteConfirmModal={this.hideDeleteConfirmModal.bind(this)}
/>
)}
</div>
)
}
}
const mapStateToProps = state => {
return {
checkedObjectsCount: getCheckedList(state).length
}
}
const mapDispatchToProps = dispatch => {
return {
downloadChecked: () => dispatch(actions.downloadCheckedObjects()),
clearChecked: () => dispatch(actions.resetCheckedList()),
deleteChecked: () => dispatch(actions.deleteCheckedObjects())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ObjectsBulkActions)

View File

@@ -25,55 +25,59 @@ export const ObjectsHeader = ({
sortLastModifiedOrder,
sortObjects
}) => (
<div className="objects__row objects__header hidden-xs">
<div
className="objects__column objects__column--name"
id="sort-by-name"
onClick={() => sortObjects("name")}
data-sort="name"
>
Name
<i
className={classNames({
objects__sort: true,
zmdi: true,
"zmdi-sort-desc": sortNameOrder,
"zmdi-sort-asc": !sortNameOrder
})}
/>
</div>
<div
className="objects__column objects__column--size"
id="sort-by-size"
onClick={() => sortObjects("size")}
data-sort="size"
>
Size
<i
className={classNames({
objects__sort: true,
zmdi: true,
"zmdi-sort-amount-desc": sortSizeOrder,
"zmdi-sort-amount-asc": !sortSizeOrder
})}
/>
</div>
<div
className="objects__column objects__column--date"
id="sort-by-last-modified"
onClick={() => sortObjects("last-modified")}
data-sort="last-modified"
>
Last Modified
<i
className={classNames({
objects__sort: true,
zmdi: true,
"zmdi-sort-amount-desc": sortLastModifiedOrder,
"zmdi-sort-amount-asc": !sortLastModifiedOrder
})}
/>
</div>
<div className="feb-container">
<header className="fesl-row" data-type="folder">
<div className="fesl-item fesl-item-icon" />
<div
className="fesl-item fesl-item-name"
id="sort-by-name"
onClick={() => sortObjects("name")}
data-sort="name"
>
Name
<i
className={classNames({
"fesli-sort": true,
fa: true,
"fa-sort-alpha-desc": sortNameOrder,
"fa-sort-alpha-asc": !sortNameOrder
})}
/>
</div>
<div
className="fesl-item fesl-item-size"
id="sort-by-size"
onClick={() => sortObjects("size")}
data-sort="size"
>
Size
<i
className={classNames({
"fesli-sort": true,
fa: true,
"fa-sort-amount-desc": sortSizeOrder,
"fa-sort-amount-asc": !sortSizeOrder
})}
/>
</div>
<div
className="fesl-item fesl-item-modified"
id="sort-by-last-modified"
onClick={() => sortObjects("last-modified")}
data-sort="last-modified"
>
Last Modified
<i
className={classNames({
"fesli-sort": true,
fa: true,
"fa-sort-numeric-desc": sortLastModifiedOrder,
"fa-sort-numeric-asc": !sortLastModifiedOrder
})}
/>
</div>
<div className="fesl-item fesl-item-actions" />
</header>
</div>
)

View File

@@ -18,8 +18,6 @@ import React from "react"
import ObjectContainer from "./ObjectContainer"
import PrefixContainer from "./PrefixContainer"
const Aux = props => props.children
export const ObjectsList = ({ objects }) => {
const list = objects.map(object => {
if (object.name.endsWith("/")) {
@@ -28,7 +26,7 @@ export const ObjectsList = ({ objects }) => {
return <ObjectContainer object={object} key={object.name} />
}
})
return <Aux>{list}</Aux>
return <div>{list}</div>
}
export default ObjectsList

View File

@@ -15,25 +15,22 @@
*/
import React from "react"
import classNames from "classnames"
import { connect } from "react-redux"
import InfiniteScroll from "react-infinite-scroller"
import * as actionsObjects from "./actions"
import ObjectsList from "./ObjectsList"
const Aux = props => props.children
export class ObjectsListContainer extends React.Component {
render() {
const { objects, isTruncated, currentBucket, loadObjects } = this.props
return (
<Aux>
<div className="feb-container">
<InfiniteScroll
pageStart={0}
loadMore={() => loadObjects(true)}
hasMore={isTruncated}
useWindow={false}
element="div"
className="objects__lists"
useWindow={true}
initialLoad={false}
>
<ObjectsList objects={objects} />
@@ -44,7 +41,7 @@ export class ObjectsListContainer extends React.Component {
>
<span>Loading...</span>
</div>
</Aux>
</div>
)
}
}

View File

@@ -18,13 +18,11 @@ import React from "react"
import ObjectsHeader from "./ObjectsHeader"
import ObjectsListContainer from "./ObjectsListContainer"
const Aux = props => props.children
export const ObjectsSection = () => (
<Aux>
<div>
<ObjectsHeader />
<ObjectsListContainer />
</Aux>
</div>
)
export default ObjectsSection

View File

@@ -32,21 +32,25 @@ export const Path = ({ currentBucket, currentPrefix, selectPrefix }) => {
dirPath.push(dir)
let dirPath_ = dirPath.join("/") + "/"
return (
<a key={i} href="" onClick={e => onPrefixClick(e, dirPath_)}>
{dir}
</a>
<span key={i}>
<a href="" onClick={e => onPrefixClick(e, dirPath_)}>
{dir}
</a>
</span>
)
}
})
}
return (
<nav className="path hidden-xs">
<a onClick={e => onPrefixClick(e, "")} href="">
{currentBucket}
</a>
<h2>
<span className="main">
<a onClick={e => onPrefixClick(e, "")} href="">
{currentBucket}
</a>
</span>
{path}
</nav>
</h2>
)
}

View File

@@ -38,21 +38,11 @@ export class ShareObjectModal extends React.Component {
}
}
this.expiryRange = {
days: {
min: 0,
max: 7
},
hours: {
min: 0,
max: 23
},
minutes: {
min: 0,
max: 59
}
days: { min: 0, max: 7 },
hours: { min: 0, max: 23 },
minutes: { min: 0, max: 59 }
}
}
updateExpireValue(param, inc) {
let expiry = Object.assign({}, this.state.expiry)
@@ -78,16 +68,14 @@ export class ShareObjectModal extends React.Component {
expiry
})
const { shareObjectDetails: { object }, shareObject } = this.props
shareObject(object, expiry.days, expiry.hours, expiry.minutes)
const { object, shareObject } = this.props
shareObject(object.name, expiry.days, expiry.hours, expiry.minutes)
}
onUrlCopied() {
const { showCopyAlert, hideShareObject } = this.props
showCopyAlert("Link copied to clipboard!")
hideShareObject()
}
render() {
const { shareObjectDetails, shareObject, hideShareObject } = this.props
return (
@@ -97,34 +85,32 @@ export class ShareObjectModal extends React.Component {
onHide={hideShareObject}
bsSize="small"
>
<Modal.Header>Share Object</Modal.Header>
<Modal.Body>
<div className="form-group">
<ModalHeader>Share Object</ModalHeader>
<ModalBody>
<div className="input-group copy-text">
<label>Shareable Link</label>
<input
type="text"
className="form-group__field"
ref={node => (this.copyTextInput = node)}
readOnly="readOnly"
value={window.location.protocol + "//" + shareObjectDetails.url}
onClick={() => this.copyTextInput.select()}
/>
<i className="form-group__helper" />
</div>
<div
className="form-group"
className="input-group"
style={{ display: web.LoggedIn() ? "block" : "none" }}
>
<label>Expires in (Max 7 days)</label>
<div className="set-expire">
<div className="set-expire__item">
<div className="set-expire-item">
<i
id="increase-days"
className="set-expire__handle zmdi zmdi-chevron-up"
className="set-expire-increase"
onClick={() => this.updateExpireValue("days", 1)}
/>
<div className="set-expire__title">Days</div>
<div className="set-expire__value">
<div className="set-expire-title">Days</div>
<div className="set-expire-value">
<input
ref="expireDays"
type="number"
@@ -136,18 +122,18 @@ export class ShareObjectModal extends React.Component {
</div>
<i
id="decrease-days"
className="set-expire__handle zmdi zmdi-chevron-down"
className="set-expire-decrease"
onClick={() => this.updateExpireValue("days", -1)}
/>
</div>
<div className="set-expire__item">
<div className="set-expire-item">
<i
id="increase-hours"
className="set-expire__handle zmdi zmdi-chevron-up"
className="set-expire-increase"
onClick={() => this.updateExpireValue("hours", 1)}
/>
<div className="set-expire__title">Hours</div>
<div className="set-expire__value">
<div className="set-expire-title">Hours</div>
<div className="set-expire-value">
<input
ref="expireHours"
type="number"
@@ -158,19 +144,19 @@ export class ShareObjectModal extends React.Component {
/>
</div>
<i
className="set-expire__handle zmdi zmdi-chevron-down"
className="set-expire-decrease"
id="decrease-hours"
onClick={() => this.updateExpireValue("hours", -1)}
/>
</div>
<div className="set-expire__item">
<div className="set-expire-item">
<i
id="increase-minutes"
className="set-expire__handle zmdi zmdi-chevron-up"
className="set-expire-increase"
onClick={() => this.updateExpireValue("minutes", 1)}
/>
<div className="set-expire__title">Minutes</div>
<div className="set-expire__value">
<div className="set-expire-title">Minutes</div>
<div className="set-expire-value">
<input
ref="expireMins"
type="number"
@@ -182,21 +168,21 @@ export class ShareObjectModal extends React.Component {
</div>
<i
id="decrease-minutes"
className="set-expire__handle zmdi zmdi-chevron-down"
className="set-expire-decrease"
onClick={() => this.updateExpireValue("minutes", -1)}
/>
</div>
</div>
</div>
</Modal.Body>
</ModalBody>
<div className="modal-footer">
<CopyToClipboard
text={window.location.protocol + "//" + shareObjectDetails.url}
onCopy={this.onUrlCopied.bind(this)}
>
<button className="btn btn--link">Copy Link</button>
<button className="btn btn-success">Copy Link</button>
</CopyToClipboard>
<button className="btn btn--link" onClick={hideShareObject}>
<button className="btn btn-link" onClick={hideShareObject}>
Cancel
</button>
</div>
@@ -207,6 +193,7 @@ export class ShareObjectModal extends React.Component {
const mapStateToProps = (state, ownProps) => {
return {
object: ownProps.object,
shareObjectDetails: state.objects.shareObject
}
}
@@ -217,12 +204,7 @@ const mapDispatchToProps = dispatch => {
dispatch(objectsActions.shareObject(object, days, hours, minutes)),
hideShareObject: () => dispatch(objectsActions.hideShareObject()),
showCopyAlert: message =>
dispatch(
alertActions.set({
type: "success",
message: message
})
)
dispatch(alertActions.set({ type: "success", message: message }))
}
}

View File

@@ -1,144 +0,0 @@
/*
* Minio Cloud Storage (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from "react"
import { connect } from "react-redux"
import * as actions from "./actions"
import { getCheckedList } from "./selectors"
import DeleteObjectConfirmModal from "./DeleteObjectConfirmModal"
import BrowserDropdown from "../browser/BrowserDropdown"
import SidebarToggle from "../browser/SidebarToggle"
import { minioBrowserPrefix } from "../constants"
import ShareObjectModal from "./ShareObjectModal"
import web from "../web"
import * as objectsActions from "./actions"
import {
SHARE_OBJECT_EXPIRY_DAYS,
SHARE_OBJECT_EXPIRY_HOURS,
SHARE_OBJECT_EXPIRY_MINUTES
} from "../constants"
export class Toolbar extends React.Component {
constructor(props) {
super(props)
this.state = {
showDeleteConfirmation: false
}
}
deleteChecked() {
const { deleteChecked } = this.props
deleteChecked()
this.hideDeleteConfirmModal()
}
hideDeleteConfirmModal() {
this.setState({
showDeleteConfirmation: false
})
}
shareObject(e) {
e.preventDefault()
const { checkedObjects, shareObject } = this.props
if (checkedObjects.length != 1) {
return
}
const object = checkedObjects[0]
shareObject(
object,
SHARE_OBJECT_EXPIRY_DAYS,
SHARE_OBJECT_EXPIRY_HOURS,
SHARE_OBJECT_EXPIRY_MINUTES
)
}
render() {
const {
checkedObjectsCount,
downloadChecked,
object,
showShareObjectModal
} = this.props
const loggedIn = web.LoggedIn()
return (
<div className="toolbar">
<SidebarToggle />
<button
className="toolbar__item zmdi zmdi-delete"
onClick={() =>
this.setState({
showDeleteConfirmation: true
})
}
disabled={!checkedObjectsCount}
/>
{loggedIn ? (
<button
className="toolbar__item zmdi zmdi-share"
onClick={this.shareObject.bind(this)}
disabled={checkedObjectsCount != 1}
/>
) : (
""
)}
<button
className="toolbar__item zmdi zmdi-download"
onClick={downloadChecked}
disabled={!checkedObjectsCount}
/>
{showShareObjectModal && <ShareObjectModal object={object} />}
<div className="toolbar__end">
{loggedIn ? (
<BrowserDropdown />
) : (
<a
className="toolbar__item toolbar__item--alt btn btn--danger"
href={minioBrowserPrefix + "/login"}
>
Login
</a>
)}
</div>
{this.state.showDeleteConfirmation && (
<DeleteObjectConfirmModal
deleteObject={this.deleteChecked.bind(this)}
hideDeleteConfirmModal={this.hideDeleteConfirmModal.bind(this)}
/>
)}
</div>
)
}
}
const mapStateToProps = state => {
return {
checkedObjects: getCheckedList(state),
checkedObjectsCount: getCheckedList(state).length,
showShareObjectModal: state.objects.shareObject.show
}
}
const mapDispatchToProps = dispatch => {
return {
downloadChecked: () => dispatch(actions.downloadCheckedObjects()),
clearChecked: () => dispatch(actions.resetCheckedList()),
deleteChecked: () => dispatch(actions.deleteCheckedObjects()),
toggleSidebar: () => dispatch(actionsCommon.toggleSidebar()),
shareObject: (object, days, hours, minutes) =>
dispatch(objectsActions.shareObject(object, days, hours, minutes))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Toolbar)

View File

@@ -0,0 +1,95 @@
/*
* Minio Cloud Storage (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from "react"
import { shallow } from "enzyme"
import { ObjectActions } from "../ObjectActions"
describe("ObjectActions", () => {
it("should render without crashing", () => {
shallow(<ObjectActions object={{ name: "obj1" }} currentPrefix={"pre1/"} />)
})
it("should show DeleteObjectConfirmModal when delete action is clicked", () => {
const wrapper = shallow(
<ObjectActions object={{ name: "obj1" }} currentPrefix={"pre1/"} />
)
wrapper
.find("a")
.last()
.simulate("click", { preventDefault: jest.fn() })
expect(wrapper.state("showDeleteConfirmation")).toBeTruthy()
expect(wrapper.find("DeleteObjectConfirmModal").length).toBe(1)
})
it("should hide DeleteObjectConfirmModal when Cancel button is clicked", () => {
const wrapper = shallow(
<ObjectActions object={{ name: "obj1" }} currentPrefix={"pre1/"} />
)
wrapper
.find("a")
.last()
.simulate("click", { preventDefault: jest.fn() })
wrapper.find("DeleteObjectConfirmModal").prop("hideDeleteConfirmModal")()
wrapper.update()
expect(wrapper.state("showDeleteConfirmation")).toBeFalsy()
expect(wrapper.find("DeleteObjectConfirmModal").length).toBe(0)
})
it("should call deleteObject with object name", () => {
const deleteObject = jest.fn()
const wrapper = shallow(
<ObjectActions
object={{ name: "obj1" }}
currentPrefix={"pre1/"}
deleteObject={deleteObject}
/>
)
wrapper
.find("a")
.last()
.simulate("click", { preventDefault: jest.fn() })
wrapper.find("DeleteObjectConfirmModal").prop("deleteObject")()
expect(deleteObject).toHaveBeenCalledWith("obj1")
})
it("should call shareObject with object and expiry", () => {
const shareObject = jest.fn()
const wrapper = shallow(
<ObjectActions
object={{ name: "obj1" }}
currentPrefix={"pre1/"}
shareObject={shareObject}
/>
)
wrapper
.find("a")
.first()
.simulate("click", { preventDefault: jest.fn() })
expect(shareObject).toHaveBeenCalledWith("obj1", 5, 0, 0)
})
it("should render ShareObjectModal when an object is shared", () => {
const wrapper = shallow(
<ObjectActions
object={{ name: "obj1" }}
currentPrefix={"pre1/"}
showShareObjectModal={true}
/>
)
expect(wrapper.find("Connect(ShareObjectModal)").length).toBe(1)
})
})

View File

@@ -28,4 +28,22 @@ describe("ObjectContainer", () => {
expect(wrapper.find("Connect(ObjectItem)").length).toBe(1)
expect(wrapper.find("Connect(ObjectItem)").prop("name")).toBe("test1.jpg")
})
it("should pass actions to ObjectItem", () => {
const wrapper = shallow(
<ObjectContainer object={{ name: "test1.jpg" }} checkedObjectsCount={0} />
)
expect(wrapper.find("Connect(ObjectItem)").prop("actionButtons")).not.toBe(
undefined
)
})
it("should pass empty actions to ObjectItem when checkedObjectCount is more than 0", () => {
const wrapper = shallow(
<ObjectContainer object={{ name: "test1.jpg" }} checkedObjectsCount={1} />
)
expect(wrapper.find("Connect(ObjectItem)").prop("actionButtons")).toBe(
undefined
)
})
})

View File

@@ -25,17 +25,13 @@ describe("ObjectItem", () => {
it("should render with content type", () => {
const wrapper = shallow(<ObjectItem name={"test.jpg"} contentType={""} />)
expect(
wrapper.find(".objects__column--select").prop("data-object-type")
).toBe("image")
expect(wrapper.prop("data-type")).toBe("image")
})
it("should call onClick when the object isclicked", () => {
const onClick = jest.fn()
const wrapper = shallow(<ObjectItem name={"test"} onClick={onClick} />)
wrapper.find("a").simulate("click", {
preventDefault: jest.fn()
})
wrapper.find("a").simulate("click", { preventDefault: jest.fn() })
expect(onClick).toHaveBeenCalled()
})

View File

@@ -16,38 +16,42 @@
import React from "react"
import { shallow } from "enzyme"
import { Toolbar } from "../Toolbar"
import { ObjectsBulkActions } from "../ObjectsBulkActions"
jest.mock("../../web", () => ({
LoggedIn: jest
.fn(() => true)
.mockReturnValueOnce(true)
.mockReturnValueOnce(false)
}))
describe("Toolbar", () => {
describe("ObjectsBulkActions", () => {
it("should render without crashing", () => {
shallow(<Toolbar checkedObjectsCount={0} />)
shallow(<ObjectsBulkActions checkedObjectsCount={0} />)
})
it("should render Login button when the user has not LoggedIn", () => {
const wrapper = shallow(<Toolbar checkedObjectsCount={0} />)
expect(wrapper.find("a").text()).toBe("Login")
it("should show actions when checkObjectsCount is more than 0", () => {
const wrapper = shallow(<ObjectsBulkActions checkedObjectsCount={1} />)
expect(wrapper.hasClass("list-actions-toggled")).toBeTruthy()
})
it("should render StorageInfo and BrowserDropdown when the user has LoggedIn", () => {
const wrapper = shallow(<Toolbar checkedObjectsCount={0} />)
expect(wrapper.find("Connect(BrowserDropdown)").length).toBe(1)
it("should call downloadChecked when download button is clicked", () => {
const downloadChecked = jest.fn()
const wrapper = shallow(
<ObjectsBulkActions
checkedObjectsCount={1}
downloadChecked={downloadChecked}
/>
)
wrapper.find("#download-checked").simulate("click")
expect(downloadChecked).toHaveBeenCalled()
})
it("should enable delete action when checkObjectsCount is more than 0", () => {
const wrapper = shallow(<Toolbar checkedObjectsCount={1} />)
expect(wrapper.find(".zmdi-delete").prop("disabled")).toBeFalsy()
it("should call clearChecked when close button is clicked", () => {
const clearChecked = jest.fn()
const wrapper = shallow(
<ObjectsBulkActions checkedObjectsCount={1} clearChecked={clearChecked} />
)
wrapper.find("#close-bulk-actions").simulate("click")
expect(clearChecked).toHaveBeenCalled()
})
it("shoud show DeleteObjectConfirmModal when delete button is clicked", () => {
const wrapper = shallow(<Toolbar checkedObjectsCount={1} />)
wrapper.find("button.zmdi-delete").simulate("click")
it("shoud show DeleteObjectConfirmModal when delete-checked button is clicked", () => {
const wrapper = shallow(<ObjectsBulkActions checkedObjectsCount={1} />)
wrapper.find("#delete-checked").simulate("click")
wrapper.update()
expect(wrapper.find("DeleteObjectConfirmModal").length).toBe(1)
})
@@ -55,22 +59,16 @@ describe("Toolbar", () => {
it("shoud call deleteChecked when Delete is clicked on confirmation modal", () => {
const deleteChecked = jest.fn()
const wrapper = shallow(
<Toolbar checkedObjectsCount={1} deleteChecked={deleteChecked} />
<ObjectsBulkActions
checkedObjectsCount={1}
deleteChecked={deleteChecked}
/>
)
wrapper.find("button.zmdi-delete").simulate("click")
wrapper.find("#delete-checked").simulate("click")
wrapper.update()
wrapper.find("DeleteObjectConfirmModal").prop("deleteObject")()
expect(deleteChecked).toHaveBeenCalled()
wrapper.update()
expect(wrapper.find("DeleteObjectConfirmModal").length).toBe(0)
})
it("should call downloadChecked when download button is clicked", () => {
const downloadChecked = jest.fn()
const wrapper = shallow(
<Toolbar checkedObjectsCount={1} downloadChecked={downloadChecked} />
)
wrapper.find("button.zmdi-download").simulate("click")
expect(downloadChecked).toHaveBeenCalled()
})
})

View File

@@ -28,13 +28,13 @@ describe("ObjectsHeader", () => {
const sortObjects = jest.fn()
const wrapper = shallow(<ObjectsHeader sortObjects={sortObjects} />)
expect(
wrapper.find("#sort-by-name i").hasClass("zmdi-sort-asc")
wrapper.find("#sort-by-name i").hasClass("fa-sort-alpha-asc")
).toBeTruthy()
expect(
wrapper.find("#sort-by-size i").hasClass("zmdi-sort-amount-asc")
wrapper.find("#sort-by-size i").hasClass("fa-sort-amount-asc")
).toBeTruthy()
expect(
wrapper.find("#sort-by-last-modified i").hasClass("zmdi-sort-amount-asc")
wrapper.find("#sort-by-last-modified i").hasClass("fa-sort-numeric-asc")
).toBeTruthy()
})
@@ -44,7 +44,7 @@ describe("ObjectsHeader", () => {
<ObjectsHeader sortObjects={sortObjects} sortNameOrder={true} />
)
expect(
wrapper.find("#sort-by-name i").hasClass("zmdi-sort-desc")
wrapper.find("#sort-by-name i").hasClass("fa-sort-alpha-desc")
).toBeTruthy()
})
@@ -54,7 +54,7 @@ describe("ObjectsHeader", () => {
<ObjectsHeader sortObjects={sortObjects} sortSizeOrder={true} />
)
expect(
wrapper.find("#sort-by-size i").hasClass("zmdi-sort-amount-desc")
wrapper.find("#sort-by-size i").hasClass("fa-sort-amount-desc")
).toBeTruthy()
})
@@ -64,7 +64,7 @@ describe("ObjectsHeader", () => {
<ObjectsHeader sortObjects={sortObjects} sortLastModifiedOrder={true} />
)
expect(
wrapper.find("#sort-by-last-modified i").hasClass("zmdi-sort-amount-desc")
wrapper.find("#sort-by-last-modified i").hasClass("fa-sort-numeric-desc")
).toBeTruthy()
})

View File

@@ -32,12 +32,8 @@ describe("ObjectsList", () => {
)
expect(wrapper.find("ObjectsList").length).toBe(1)
expect(wrapper.find("ObjectsList").prop("objects")).toEqual([
{
name: "test1.jpg"
},
{
name: "test2.jpg"
}
{ name: "test1.jpg" },
{ name: "test2.jpg" }
])
})
@@ -45,9 +41,6 @@ describe("ObjectsList", () => {
const wrapper = shallow(
<ObjectsListContainer currentBucket="test1" isTruncated={true} />
)
expect(wrapper.find(".text-center").prop("style")).toHaveProperty(
"display",
"block"
)
expect(wrapper.find(".text-center").prop("style")).toHaveProperty("display", "block")
})
})

View File

@@ -25,7 +25,7 @@ describe("Path", () => {
it("should render only bucket if there is no prefix", () => {
const wrapper = shallow(<Path currentBucket={"test1"} currentPrefix={""} />)
expect(wrapper.find("a").length).toBe(1)
expect(wrapper.find("span").length).toBe(1)
expect(wrapper.text()).toBe("test1")
})
@@ -33,22 +33,22 @@ describe("Path", () => {
const wrapper = shallow(
<Path currentBucket={"test1"} currentPrefix={"a/b/"} />
)
expect(wrapper.find("a").length).toBe(3)
expect(wrapper.find("span").length).toBe(3)
expect(
wrapper
.find("a")
.find("span")
.at(0)
.text()
).toBe("test1")
expect(
wrapper
.find("a")
.find("span")
.at(1)
.text()
).toBe("a")
expect(
wrapper
.find("a")
.find("span")
.at(2)
.text()
).toBe("b")
@@ -66,9 +66,7 @@ describe("Path", () => {
wrapper
.find("a")
.at(2)
.simulate("click", {
preventDefault: jest.fn()
})
.simulate("click", { preventDefault: jest.fn() })
expect(selectPrefix).toHaveBeenCalledWith("a/b/")
})
})

View File

@@ -88,14 +88,8 @@ describe("ShareObjectModal", () => {
describe("Update expiry values", () => {
const props = {
object: {
name: "obj1"
},
shareObjectDetails: {
show: true,
object: "obj1",
url: "test"
}
object: { name: "obj1" },
shareObjectDetails: { show: true, object: "obj1", url: "test" }
}
it("should have default expiry values", () => {
const wrapper = shallow(<ShareObjectModal {...props} />)

View File

@@ -24,14 +24,7 @@ jest.mock("../../web", () => ({
LoggedIn: jest.fn(() => true).mockReturnValueOnce(false),
ListObjects: jest.fn(() => {
return Promise.resolve({
objects: [
{
name: "test1"
},
{
name: "test2"
}
],
objects: [{ name: "test1" }, { name: "test2" }],
istruncated: false,
nextmarker: "test2",
writable: false
@@ -39,38 +32,26 @@ jest.mock("../../web", () => ({
}),
RemoveObject: jest.fn(({ bucketName, objects }) => {
if (!bucketName) {
return Promise.reject({
message: "Invalid bucket"
})
return Promise.reject({ message: "Invalid bucket" })
}
return Promise.resolve({})
}),
PresignedGet: jest.fn(({ bucket, object }) => {
if (!bucket) {
return Promise.reject({
message: "Invalid bucket"
})
return Promise.reject({ message: "Invalid bucket" })
}
return Promise.resolve({
url: "https://test.com/bk1/pre1/b.txt"
})
return Promise.resolve({ url: "https://test.com/bk1/pre1/b.txt" })
}),
CreateURLToken: jest
.fn()
.mockImplementationOnce(() => {
return Promise.resolve({
token: "test"
})
return Promise.resolve({ token: "test" })
})
.mockImplementationOnce(() => {
return Promise.reject({
message: "Error in creating token"
})
return Promise.reject({ message: "Error in creating token" })
})
.mockImplementationOnce(() => {
return Promise.resolve({
token: "test"
})
return Promise.resolve({ token: "test" })
})
}))
@@ -83,28 +64,14 @@ describe("Objects actions", () => {
const expectedActions = [
{
type: "objects/SET_LIST",
objects: [
{
name: "test1"
},
{
name: "test2"
}
],
objects: [{ name: "test1" }, { name: "test2" }],
isTruncated: false,
marker: "test2"
}
]
store.dispatch(
actionsObjects.setList(
[
{
name: "test1"
},
{
name: "test2"
}
],
[{ name: "test1" }, { name: "test2" }],
"test2",
false
)
@@ -141,24 +108,13 @@ describe("Objects actions", () => {
it("creates objects/SET_LIST after fetching the objects", () => {
const store = mockStore({
buckets: {
currentBucket: "bk1"
},
objects: {
currentPrefix: ""
}
buckets: { currentBucket: "bk1" },
objects: { currentPrefix: "" }
})
const expectedActions = [
{
type: "objects/SET_LIST",
objects: [
{
name: "test1"
},
{
name: "test2"
}
],
objects: [{ name: "test1" }, { name: "test2" }],
marker: "test2",
isTruncated: false
},
@@ -183,24 +139,13 @@ describe("Objects actions", () => {
it("creates objects/APPEND_LIST after fetching more objects", () => {
const store = mockStore({
buckets: {
currentBucket: "bk1"
},
objects: {
currentPrefix: ""
}
buckets: { currentBucket: "bk1" },
objects: { currentPrefix: "" }
})
const expectedActions = [
{
type: "objects/APPEND_LIST",
objects: [
{
name: "test1"
},
{
name: "test2"
}
],
objects: [{ name: "test1" }, { name: "test2" }],
marker: "test2",
isTruncated: false
},
@@ -248,21 +193,12 @@ describe("Objects actions", () => {
it("should update browser url and creates objects/SET_CURRENT_PREFIX and objects/CHECKED_LIST_RESET actions when selectPrefix is called", () => {
const store = mockStore({
buckets: {
currentBucket: "test"
},
objects: {
currentPrefix: ""
}
buckets: { currentBucket: "test" },
objects: { currentPrefix: "" }
})
const expectedActions = [
{
type: "objects/SET_CURRENT_PREFIX",
prefix: "abc/"
},
{
type: "objects/CHECKED_LIST_RESET"
}
{ type: "objects/SET_CURRENT_PREFIX", prefix: "abc/" },
{ type: "objects/CHECKED_LIST_RESET" }
]
store.dispatch(actionsObjects.selectPrefix("abc/"))
const actions = store.getActions()
@@ -273,10 +209,7 @@ describe("Objects actions", () => {
it("create objects/SET_PREFIX_WRITABLE action", () => {
const store = mockStore()
const expectedActions = [
{
type: "objects/SET_PREFIX_WRITABLE",
prefixWritable: true
}
{ type: "objects/SET_PREFIX_WRITABLE", prefixWritable: true }
]
store.dispatch(actionsObjects.setPrefixWritable(true))
const actions = store.getActions()
@@ -285,12 +218,7 @@ describe("Objects actions", () => {
it("creates objects/REMOVE action", () => {
const store = mockStore()
const expectedActions = [
{
type: "objects/REMOVE",
object: "obj1"
}
]
const expectedActions = [{ type: "objects/REMOVE", object: "obj1" }]
store.dispatch(actionsObjects.removeObject("obj1"))
const actions = store.getActions()
expect(actions).toEqual(expectedActions)
@@ -298,19 +226,10 @@ describe("Objects actions", () => {
it("creates objects/REMOVE action when object is deleted", () => {
const store = mockStore({
buckets: {
currentBucket: "test"
},
objects: {
currentPrefix: "pre1/"
}
buckets: { currentBucket: "test" },
objects: { currentPrefix: "pre1/" }
})
const expectedActions = [
{
type: "objects/REMOVE",
object: "obj1"
}
]
const expectedActions = [{ type: "objects/REMOVE", object: "obj1" }]
store.dispatch(actionsObjects.deleteObject("obj1")).then(() => {
const actions = store.getActions()
expect(actions).toEqual(expectedActions)
@@ -319,21 +238,13 @@ describe("Objects actions", () => {
it("creates alert/SET action when invalid bucket is provided", () => {
const store = mockStore({
buckets: {
currentBucket: ""
},
objects: {
currentPrefix: "pre1/"
}
buckets: { currentBucket: "" },
objects: { currentPrefix: "pre1/" }
})
const expectedActions = [
{
type: "alert/SET",
alert: {
type: "danger",
message: "Invalid bucket",
id: 0
}
alert: { type: "danger", message: "Invalid bucket", id: 0 }
}
]
return store.dispatch(actionsObjects.deleteObject("obj1")).then(() => {
@@ -374,12 +285,8 @@ describe("Objects actions", () => {
it("creates objects/SET_SHARE_OBJECT when object is shared", () => {
const store = mockStore({
buckets: {
currentBucket: "bk1"
},
objects: {
currentPrefix: "pre1/"
}
buckets: { currentBucket: "bk1" },
objects: { currentPrefix: "pre1/" }
})
const expectedActions = [
{
@@ -407,12 +314,8 @@ describe("Objects actions", () => {
it("creates alert/SET when shareObject is failed", () => {
const store = mockStore({
buckets: {
currentBucket: ""
},
objects: {
currentPrefix: "pre1/"
}
buckets: { currentBucket: "" },
objects: { currentPrefix: "pre1/" }
})
const expectedActions = [
{
@@ -446,12 +349,8 @@ describe("Objects actions", () => {
}
})
const store = mockStore({
buckets: {
currentBucket: "bk1"
},
objects: {
currentPrefix: "pre1/"
}
buckets: { currentBucket: "bk1" },
objects: { currentPrefix: "pre1/" }
})
store.dispatch(actionsObjects.downloadObject("obj1"))
const url = `${
@@ -473,12 +372,8 @@ describe("Objects actions", () => {
}
})
const store = mockStore({
buckets: {
currentBucket: "bk1"
},
objects: {
currentPrefix: "pre1/"
}
buckets: { currentBucket: "bk1" },
objects: { currentPrefix: "pre1/" }
})
return store.dispatch(actionsObjects.downloadObject("obj1")).then(() => {
const url = `${
@@ -492,12 +387,8 @@ describe("Objects actions", () => {
it("create alert/SET action when CreateUrlToken fails", () => {
const store = mockStore({
buckets: {
currentBucket: "bk1"
},
objects: {
currentPrefix: "pre1/"
}
buckets: { currentBucket: "bk1" },
objects: { currentPrefix: "pre1/" }
})
const expectedActions = [
{
@@ -564,13 +455,8 @@ describe("Objects actions", () => {
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass)
const store = mockStore({
buckets: {
currentBucket: "bk1"
},
objects: {
currentPrefix: "pre1/",
checkedList: ["obj1"]
}
buckets: { currentBucket: "bk1" },
objects: { currentPrefix: "pre1/", checkedList: ["obj1"] }
})
return store.dispatch(actionsObjects.downloadCheckedObjects()).then(() => {
const requestUrl = `${

View File

@@ -40,25 +40,11 @@ describe("objects reducer", () => {
it("should handle SET_LIST", () => {
const newState = reducer(undefined, {
type: actions.SET_LIST,
objects: [
{
name: "obj1"
},
{
name: "obj2"
}
],
objects: [{ name: "obj1" }, { name: "obj2" }],
marker: "obj2",
isTruncated: false
})
expect(newState.list).toEqual([
{
name: "obj1"
},
{
name: "obj2"
}
])
expect(newState.list).toEqual([{ name: "obj1" }, { name: "obj2" }])
expect(newState.marker).toBe("obj2")
expect(newState.isTruncated).toBeFalsy()
})
@@ -66,44 +52,22 @@ describe("objects reducer", () => {
it("should handle APPEND_LIST", () => {
const newState = reducer(
{
list: [
{
name: "obj1"
},
{
name: "obj2"
}
],
list: [{ name: "obj1" }, { name: "obj2" }],
marker: "obj2",
isTruncated: true
},
{
type: actions.APPEND_LIST,
objects: [
{
name: "obj3"
},
{
name: "obj4"
}
],
objects: [{ name: "obj3" }, { name: "obj4" }],
marker: "obj4",
isTruncated: false
}
)
expect(newState.list).toEqual([
{
name: "obj1"
},
{
name: "obj2"
},
{
name: "obj3"
},
{
name: "obj4"
}
{ name: "obj1" },
{ name: "obj2" },
{ name: "obj3" },
{ name: "obj4" }
])
expect(newState.marker).toBe("obj4")
expect(newState.isTruncated).toBeFalsy()
@@ -111,53 +75,24 @@ describe("objects reducer", () => {
it("should handle REMOVE", () => {
const newState = reducer(
{
list: [
{
name: "obj1"
},
{
name: "obj2"
}
]
},
{ list: [{ name: "obj1" }, { name: "obj2" }] },
{
type: actions.REMOVE,
object: "obj1"
}
)
expect(newState.list).toEqual([
{
name: "obj2"
}
])
expect(newState.list).toEqual([{ name: "obj2" }])
})
it("should handle REMOVE with non-existent object", () => {
const newState = reducer(
{
list: [
{
name: "obj1"
},
{
name: "obj2"
}
]
},
{ list: [{ name: "obj1" }, { name: "obj2" }] },
{
type: actions.REMOVE,
object: "obj3"
}
)
expect(newState.list).toEqual([
{
name: "obj1"
},
{
name: "obj2"
}
])
expect(newState.list).toEqual([{ name: "obj1" }, { name: "obj2" }])
})
it("should handle SET_SORT_BY", () => {
@@ -178,11 +113,7 @@ describe("objects reducer", () => {
it("should handle SET_CURRENT_PREFIX", () => {
const newState = reducer(
{
currentPrefix: "test1/",
marker: "abc",
isTruncated: true
},
{ currentPrefix: "test1/", marker: "abc", isTruncated: true },
{
type: actions.SET_CURRENT_PREFIX,
prefix: "test2/"
@@ -225,9 +156,7 @@ describe("objects reducer", () => {
it("should handle SELECTED_LIST_REMOVE", () => {
const newState = reducer(
{
checkedList: ["obj1", "obj2"]
},
{ checkedList: ["obj1", "obj2"] },
{
type: actions.CHECKED_LIST_REMOVE,
object: "obj1"
@@ -238,9 +167,7 @@ describe("objects reducer", () => {
it("should handle CHECKED_LIST_RESET", () => {
const newState = reducer(
{
checkedList: ["obj1", "obj2"]
},
{ checkedList: ["obj1", "obj2"] },
{
type: actions.CHECKED_LIST_RESET
}

View File

@@ -60,40 +60,35 @@ export const fetchObjects = append => {
} = getState()
if (currentBucket) {
return web
.ListObjects({
bucketName: currentBucket,
prefix: currentPrefix,
marker: append ? marker : ""
})
.then(res => {
let objects = []
if (res.objects) {
objects = res.objects.map(object => {
return {
...object,
name: object.name.replace(currentPrefix, "")
}
})
}
if (append) {
dispatch(appendList(objects, res.nextmarker, res.istruncated))
} else {
dispatch(setList(objects, res.nextmarker, res.istruncated))
dispatch(setSortBy(""))
dispatch(setSortOrder(false))
}
dispatch(setPrefixWritable(res.writable))
})
.catch(err => {
dispatch(
alertActions.set({
type: "danger",
message: err.message
})
)
history.push("/login")
})
}
.ListObjects({
bucketName: currentBucket,
prefix: currentPrefix,
marker: append ? marker : ""
})
.then(res => {
let objects = []
if (res.objects) {
objects = res.objects.map(object => {
return {
...object,
name: object.name.replace(currentPrefix, "")
}
})
}
if (append) {
dispatch(appendList(objects, res.nextmarker, res.istruncated))
} else {
dispatch(setList(objects, res.nextmarker, res.istruncated))
dispatch(setSortBy(""))
dispatch(setSortOrder(false))
}
dispatch(setPrefixWritable(res.writable))
})
.catch(err => {
dispatch(alertActions.set({ type: "danger", message: err.message }))
history.push("/login")
})
}
}
}