mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
Refactor of components (#5499)
This commit is contained in:
parent
9ab6a8035f
commit
feb726dd98
@ -28,14 +28,26 @@ const middlewares = [thunk]
|
|||||||
const mockStore = configureStore(middlewares)
|
const mockStore = configureStore(middlewares)
|
||||||
|
|
||||||
describe("Buckets actions", () => {
|
describe("Buckets actions", () => {
|
||||||
it("creates buckets/SET_LIST after fetching the buckets", () => {
|
it("creates buckets/SET_LIST and buckets/SET_CURRENT_BUCKET after fetching the buckets", () => {
|
||||||
const store = mockStore()
|
const store = mockStore()
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: "buckets/SET_LIST", buckets: ["test1", "test2"] }
|
{ type: "buckets/SET_LIST", buckets: ["test1", "test2"] },
|
||||||
|
{ type: "buckets/SET_CURRENT_BUCKET", bucket: "test1" }
|
||||||
]
|
]
|
||||||
return store.dispatch(actionsBuckets.fetchBuckets()).then(() => {
|
return store.dispatch(actionsBuckets.fetchBuckets()).then(() => {
|
||||||
const actions = store.getActions()
|
const actions = store.getActions()
|
||||||
expect(actions).toEqual(expectedActions)
|
expect(actions).toEqual(expectedActions)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should update browser url and creates buckets/SET_CURRENT_BUCKET action when selectBucket is called", () => {
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: "buckets/SET_CURRENT_BUCKET", bucket: "test1" }
|
||||||
|
]
|
||||||
|
store.dispatch(actionsBuckets.selectBucket("test1"))
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
expect(window.location.pathname).toBe("/test1")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
41
browser/app/js/actions/__tests__/common.test.js
Normal file
41
browser/app/js/actions/__tests__/common.test.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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 configureStore from "redux-mock-store"
|
||||||
|
import thunk from "redux-thunk"
|
||||||
|
import * as actionsCommon from "../common"
|
||||||
|
|
||||||
|
jest.mock("../../web", () => ({
|
||||||
|
StorageInfo: jest.fn(() => {
|
||||||
|
return Promise.resolve({ storageInfo: { Total: 100, Free: 60 } })
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
const middlewares = [thunk]
|
||||||
|
const mockStore = configureStore(middlewares)
|
||||||
|
|
||||||
|
describe("Common actions", () => {
|
||||||
|
it("creates common/SET_STORAGE_INFO after fetching the storage details ", () => {
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: "common/SET_STORAGE_INFO", storageInfo: { total: 100, free: 60 } }
|
||||||
|
]
|
||||||
|
return store.dispatch(actionsCommon.fetchStorageInfo()).then(() => {
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
171
browser/app/js/actions/__tests__/objects.test.js
Normal file
171
browser/app/js/actions/__tests__/objects.test.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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 configureStore from "redux-mock-store"
|
||||||
|
import thunk from "redux-thunk"
|
||||||
|
import * as actionsObjects from "../objects"
|
||||||
|
|
||||||
|
jest.mock("../../web", () => ({
|
||||||
|
ListObjects: jest.fn(() => {
|
||||||
|
return Promise.resolve({
|
||||||
|
objects: [{ name: "test1" }, { name: "test2" }],
|
||||||
|
istruncated: false,
|
||||||
|
nextmarker: "test2"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
const middlewares = [thunk]
|
||||||
|
const mockStore = configureStore(middlewares)
|
||||||
|
|
||||||
|
describe("Objects actions", () => {
|
||||||
|
it("creates objects/SET_LIST action", () => {
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: "objects/SET_LIST",
|
||||||
|
objects: [{ name: "test1" }, { name: "test2" }],
|
||||||
|
isTruncated: false,
|
||||||
|
marker: "test2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
store.dispatch(
|
||||||
|
actionsObjects.setList(
|
||||||
|
[{ name: "test1" }, { name: "test2" }],
|
||||||
|
"test2",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates objects/SET_SORT_BY action", () => {
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: "objects/SET_SORT_BY",
|
||||||
|
sortBy: "name"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
store.dispatch(actionsObjects.setSortBy("name"))
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates objects/SET_SORT_ORDER action", () => {
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: "objects/SET_SORT_ORDER",
|
||||||
|
sortOrder: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
store.dispatch(actionsObjects.setSortOrder(true))
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates objects/SET_LIST after fetching the objects", () => {
|
||||||
|
const store = mockStore({
|
||||||
|
buckets: { currentBucket: "bk1" },
|
||||||
|
objects: { currentPrefix: "" }
|
||||||
|
})
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: "objects/SET_LIST",
|
||||||
|
objects: [{ name: "test1" }, { name: "test2" }],
|
||||||
|
marker: "test2",
|
||||||
|
isTruncated: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "objects/SET_SORT_BY",
|
||||||
|
sortBy: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "objects/SET_SORT_ORDER",
|
||||||
|
sortOrder: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return store.dispatch(actionsObjects.fetchObjects()).then(() => {
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates objects/APPEND_LIST after fetching more objects", () => {
|
||||||
|
const store = mockStore({
|
||||||
|
buckets: { currentBucket: "bk1" },
|
||||||
|
objects: { currentPrefix: "" }
|
||||||
|
})
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: "objects/APPEND_LIST",
|
||||||
|
objects: [{ name: "test1" }, { name: "test2" }],
|
||||||
|
marker: "test2",
|
||||||
|
isTruncated: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return store.dispatch(actionsObjects.fetchObjects(true)).then(() => {
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates objects/SET_SORT_BY and objects/SET_SORT_ORDER when sortObjects is called", () => {
|
||||||
|
const store = mockStore({
|
||||||
|
objects: {
|
||||||
|
list: [],
|
||||||
|
sortBy: "",
|
||||||
|
sortOrder: false,
|
||||||
|
isTruncated: false,
|
||||||
|
marker: ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: "objects/SET_SORT_BY",
|
||||||
|
sortBy: "name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "objects/SET_SORT_ORDER",
|
||||||
|
sortOrder: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "objects/SET_LIST",
|
||||||
|
objects: [],
|
||||||
|
isTruncated: false,
|
||||||
|
marker: ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
store.dispatch(actionsObjects.sortObjects("name"))
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update browser url and creates objects/SET_CURRENT_PREFIX action when selectPrefix is called", () => {
|
||||||
|
const store = mockStore({
|
||||||
|
buckets: { currentBucket: "test" }
|
||||||
|
})
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: "objects/SET_CURRENT_PREFIX", prefix: "abc/" }
|
||||||
|
]
|
||||||
|
store.dispatch(actionsObjects.selectPrefix("abc/"))
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
expect(window.location.pathname.endsWith("/test/abc/")).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import web from "../web"
|
import web from "../web"
|
||||||
|
import history from "../history"
|
||||||
|
|
||||||
export const SET_LIST = "buckets/SET_LIST"
|
export const SET_LIST = "buckets/SET_LIST"
|
||||||
export const SET_FILTER = "buckets/SET_FILTER"
|
export const SET_FILTER = "buckets/SET_FILTER"
|
||||||
@ -25,6 +26,9 @@ export const fetchBuckets = () => {
|
|||||||
return web.ListBuckets().then(res => {
|
return web.ListBuckets().then(res => {
|
||||||
const buckets = res.buckets ? res.buckets.map(bucket => bucket.name) : []
|
const buckets = res.buckets ? res.buckets.map(bucket => bucket.name) : []
|
||||||
dispatch(setList(buckets))
|
dispatch(setList(buckets))
|
||||||
|
if (buckets.length > 0) {
|
||||||
|
dispatch(selectBucket(buckets[0]))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,6 +47,13 @@ export const setFilter = filter => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const selectBucket = bucket => {
|
||||||
|
return function(dispatch) {
|
||||||
|
dispatch(setCurrentBucket(bucket))
|
||||||
|
history.push(`/${bucket}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const setCurrentBucket = bucket => {
|
export const setCurrentBucket = bucket => {
|
||||||
return {
|
return {
|
||||||
type: SET_CURRENT_BUCKET,
|
type: SET_CURRENT_BUCKET,
|
||||||
|
46
browser/app/js/actions/common.js
Normal file
46
browser/app/js/actions/common.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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 web from "../web"
|
||||||
|
|
||||||
|
export const TOGGLE_SIDEBAR = "common/TOGGLE_SIDEBAR"
|
||||||
|
export const CLOSE_SIDEBAR = "common/CLOSE_SIDEBAR"
|
||||||
|
export const SET_STORAGE_INFO = "common/SET_STORAGE_INFO"
|
||||||
|
|
||||||
|
export const toggleSidebar = () => ({
|
||||||
|
type: TOGGLE_SIDEBAR
|
||||||
|
})
|
||||||
|
|
||||||
|
export const closeSidebar = () => ({
|
||||||
|
type: CLOSE_SIDEBAR
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchStorageInfo = () => {
|
||||||
|
return function(dispatch) {
|
||||||
|
return web.StorageInfo().then(res => {
|
||||||
|
const storageInfo = {
|
||||||
|
total: res.storageInfo.Total,
|
||||||
|
free: res.storageInfo.Free
|
||||||
|
}
|
||||||
|
dispatch(setStorageInfo(storageInfo))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setStorageInfo = storageInfo => ({
|
||||||
|
type: SET_STORAGE_INFO,
|
||||||
|
storageInfo
|
||||||
|
})
|
126
browser/app/js/actions/objects.js
Normal file
126
browser/app/js/actions/objects.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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 web from "../web"
|
||||||
|
import history from "../history"
|
||||||
|
import {
|
||||||
|
sortObjectsByName,
|
||||||
|
sortObjectsBySize,
|
||||||
|
sortObjectsByDate
|
||||||
|
} from "../utils"
|
||||||
|
|
||||||
|
export const SET_LIST = "objects/SET_LIST"
|
||||||
|
export const APPEND_LIST = "objects/APPEND_LIST"
|
||||||
|
export const SET_SORT_BY = "objects/SET_SORT_BY"
|
||||||
|
export const SET_SORT_ORDER = "objects/SET_SORT_ORDER"
|
||||||
|
export const SET_CURRENT_PREFIX = "objects/SET_CURRENT_PREFIX"
|
||||||
|
|
||||||
|
export const setList = (objects, marker, isTruncated) => ({
|
||||||
|
type: SET_LIST,
|
||||||
|
objects,
|
||||||
|
marker,
|
||||||
|
isTruncated
|
||||||
|
})
|
||||||
|
|
||||||
|
export const appendList = (objects, marker, isTruncated) => ({
|
||||||
|
type: APPEND_LIST,
|
||||||
|
objects,
|
||||||
|
marker,
|
||||||
|
isTruncated
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchObjects = append => {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const {
|
||||||
|
buckets: { currentBucket },
|
||||||
|
objects: { currentPrefix, marker }
|
||||||
|
} = getState()
|
||||||
|
return web
|
||||||
|
.ListObjects({
|
||||||
|
bucketName: currentBucket,
|
||||||
|
prefix: currentPrefix,
|
||||||
|
marker: 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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sortObjects = sortBy => {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const { objects } = getState()
|
||||||
|
const sortOrder = objects.sortBy == sortBy ? !objects.sortOrder : true
|
||||||
|
dispatch(setSortBy(sortBy))
|
||||||
|
dispatch(setSortOrder(sortOrder))
|
||||||
|
let list
|
||||||
|
switch (sortBy) {
|
||||||
|
case "name":
|
||||||
|
list = sortObjectsByName(objects.list, sortOrder)
|
||||||
|
break
|
||||||
|
case "size":
|
||||||
|
list = sortObjectsBySize(objects.list, sortOrder)
|
||||||
|
break
|
||||||
|
case "last-modified":
|
||||||
|
list = sortObjectsByDate(objects.list, sortOrder)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
list = objects.list
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dispatch(setList(list, objects.marker, objects.isTruncated))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setSortBy = sortBy => ({
|
||||||
|
type: SET_SORT_BY,
|
||||||
|
sortBy
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setSortOrder = sortOrder => ({
|
||||||
|
type: SET_SORT_ORDER,
|
||||||
|
sortOrder
|
||||||
|
})
|
||||||
|
|
||||||
|
export const selectPrefix = prefix => {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
dispatch(setCurrentPrefix(prefix))
|
||||||
|
const currentBucket = getState().buckets.currentBucket
|
||||||
|
history.replace(`/${currentBucket}/${prefix}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setCurrentPrefix = prefix => {
|
||||||
|
return {
|
||||||
|
type: SET_CURRENT_PREFIX,
|
||||||
|
prefix
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ import React from "react"
|
|||||||
import classNames from "classnames"
|
import classNames from "classnames"
|
||||||
import { connect } from "react-redux"
|
import { connect } from "react-redux"
|
||||||
import SideBar from "./SideBar"
|
import SideBar from "./SideBar"
|
||||||
|
import MainContent from "./MainContent"
|
||||||
|
|
||||||
class Browser extends React.Component {
|
class Browser extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
@ -28,6 +29,7 @@ class Browser extends React.Component {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<SideBar />
|
<SideBar />
|
||||||
|
<MainContent />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
selectBucket: bucket => dispatch(actionsBuckets.setCurrentBucket(bucket))
|
selectBucket: bucket => dispatch(actionsBuckets.selectBucket(bucket))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
browser/app/js/components/Header.js
Normal file
28
browser/app/js/components/Header.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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 Path from "./Path"
|
||||||
|
import StorageInfo from "./StorageInfo"
|
||||||
|
|
||||||
|
export const Header = () => (
|
||||||
|
<header className="fe-header">
|
||||||
|
<Path />
|
||||||
|
<StorageInfo />
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Header
|
30
browser/app/js/components/MainContent.js
Normal file
30
browser/app/js/components/MainContent.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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 MobileHeader from "./MobileHeader"
|
||||||
|
import Header from "./Header"
|
||||||
|
import ObjectsSection from "./ObjectsSection"
|
||||||
|
|
||||||
|
export const MainContent = () => (
|
||||||
|
<div className="fe-body">
|
||||||
|
<MobileHeader />
|
||||||
|
<Header />
|
||||||
|
<ObjectsSection />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default MainContent
|
60
browser/app/js/components/MobileHeader.js
Normal file
60
browser/app/js/components/MobileHeader.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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 classNames from "classnames"
|
||||||
|
import { connect } from "react-redux"
|
||||||
|
import logo from "../../img/logo.svg"
|
||||||
|
import * as actionsCommon from "../actions/common"
|
||||||
|
|
||||||
|
export const MobileHeader = ({ sidebarOpen, toggleSidebar }) => (
|
||||||
|
<header className="fe-header-mobile hidden-lg hidden-md">
|
||||||
|
<div
|
||||||
|
id="sidebar-toggle"
|
||||||
|
className={
|
||||||
|
"feh-trigger " +
|
||||||
|
classNames({
|
||||||
|
"feht-toggled": sidebarOpen
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
toggleSidebar()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="feht-lines">
|
||||||
|
<div className="top" />
|
||||||
|
<div className="center" />
|
||||||
|
<div className="bottom" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img className="mh-logo" src={logo} alt="" />
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
sidebarOpen: state.common.sidebarOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
toggleSidebar: () => dispatch(actionsCommon.toggleSidebar())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(MobileHeader)
|
35
browser/app/js/components/ObjectContainer.js
Normal file
35
browser/app/js/components/ObjectContainer.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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 humanize from "humanize"
|
||||||
|
import Moment from "moment"
|
||||||
|
import ObjectItem from "./ObjectItem"
|
||||||
|
import * as actionsObjects from "../actions/objects"
|
||||||
|
|
||||||
|
export const ObjectContainer = ({ object }) => {
|
||||||
|
const actionButtons = []
|
||||||
|
const props = {
|
||||||
|
name: object.name,
|
||||||
|
contentType: object.contentType,
|
||||||
|
size: humanize.filesize(object.size),
|
||||||
|
lastModified: Moment(object.lastModified).format("lll"),
|
||||||
|
actionButtons: []
|
||||||
|
}
|
||||||
|
return <ObjectItem {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectContainer
|
58
browser/app/js/components/ObjectItem.js
Normal file
58
browser/app/js/components/ObjectItem.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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 humanize from "humanize"
|
||||||
|
import Moment from "moment"
|
||||||
|
import { getDataType } from "../mime"
|
||||||
|
|
||||||
|
export const ObjectItem = ({
|
||||||
|
name,
|
||||||
|
contentType,
|
||||||
|
size,
|
||||||
|
lastModified,
|
||||||
|
actionButtons,
|
||||||
|
onClick
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<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} checked={false} />
|
||||||
|
<i className="fis-icon" />
|
||||||
|
<i className="fis-helper" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="fesl-item fesl-item-name">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
onClick()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</a>
|
||||||
|
</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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectItem
|
99
browser/app/js/components/ObjectsHeader.js
Normal file
99
browser/app/js/components/ObjectsHeader.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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 classNames from "classnames"
|
||||||
|
import { connect } from "react-redux"
|
||||||
|
import * as actionsObjects from "../actions/objects"
|
||||||
|
|
||||||
|
export const ObjectsHeader = ({
|
||||||
|
sortNameOrder,
|
||||||
|
sortSizeOrder,
|
||||||
|
sortLastModifiedOrder,
|
||||||
|
sortObjects
|
||||||
|
}) => (
|
||||||
|
<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>
|
||||||
|
)
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
sortNameOrder: state.objects.sortBy == "name" && state.objects.sortOrder,
|
||||||
|
sortSizeOrder: state.objects.sortBy == "size" && state.objects.sortOrder,
|
||||||
|
sortLastModifiedOrder:
|
||||||
|
state.objects.sortBy == "last-modified" && state.objects.sortOrder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
sortObjects: sortBy => dispatch(actionsObjects.sortObjects(sortBy))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ObjectsHeader)
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Minio Cloud Storage (C) 2016 Minio, Inc.
|
* Minio Cloud Storage (C) 2016, 2018 Minio, Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,83 +14,19 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react'
|
import React from "react"
|
||||||
import Moment from 'moment'
|
import ObjectContainer from "./ObjectContainer"
|
||||||
import humanize from 'humanize'
|
import PrefixContainer from "./PrefixContainer"
|
||||||
import connect from 'react-redux/lib/components/connect'
|
|
||||||
import Dropdown from 'react-bootstrap/lib/Dropdown'
|
|
||||||
|
|
||||||
let ObjectsList = ({objects, currentPath, selectPrefix, dataType, showDeleteConfirmation, shareObject, loadPath, checkObject, checkedObjectsArray}) => {
|
export const ObjectsList = ({ objects }) => {
|
||||||
const list = objects.map((object, i) => {
|
const list = objects.map(object => {
|
||||||
let size = object.name.endsWith('/') ? '-' : humanize.filesize(object.size)
|
if (object.name.endsWith("/")) {
|
||||||
let lastModified = object.name.endsWith('/') ? '-' : Moment(object.lastModified).format('lll')
|
return <PrefixContainer object={object} key={object.name} />
|
||||||
let loadingClass = loadPath === `${currentPath}${object.name}` ? 'fesl-loading' : ''
|
} else {
|
||||||
let actionButtons = ''
|
return <ObjectContainer object={object} key={object.name} />
|
||||||
let deleteButton = ''
|
|
||||||
if (web.LoggedIn())
|
|
||||||
deleteButton = <a href="" className="fiad-action" onClick={ (e) => showDeleteConfirmation(e, `${currentPath}${object.name}`) }><i className="fa fa-trash"></i></a>
|
|
||||||
|
|
||||||
if (!checkedObjectsArray.length > 0) {
|
|
||||||
if (!object.name.endsWith('/')) {
|
|
||||||
actionButtons = <Dropdown id={ "fia-dropdown-" + object.name.replace('.', '-') }>
|
|
||||||
<Dropdown.Toggle noCaret className="fia-toggle"></Dropdown.Toggle>
|
|
||||||
<Dropdown.Menu>
|
|
||||||
<a href="" className="fiad-action" onClick={ (e) => shareObject(e, `${currentPath}${object.name}`) }><i className="fa fa-copy"></i></a>
|
|
||||||
{ deleteButton }
|
|
||||||
</Dropdown.Menu>
|
|
||||||
</Dropdown>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let activeClass = ''
|
|
||||||
let isChecked = ''
|
|
||||||
|
|
||||||
if (checkedObjectsArray.indexOf(object.name) > -1) {
|
|
||||||
activeClass = ' fesl-row-selected'
|
|
||||||
isChecked = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={ i } className={ "fesl-row " + loadingClass + activeClass } data-type={ dataType(object.name, object.contentType) }>
|
|
||||||
<div className="fesl-item fesl-item-icon">
|
|
||||||
<div className="fi-select">
|
|
||||||
<input type="checkbox"
|
|
||||||
name={ object.name }
|
|
||||||
checked={ isChecked }
|
|
||||||
onChange={ (e) => checkObject(e, object.name) } />
|
|
||||||
<i className="fis-icon"></i>
|
|
||||||
<i className="fis-helper"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="fesl-item fesl-item-name">
|
|
||||||
<a href="" onClick={ (e) => selectPrefix(e, `${currentPath}${object.name}`) }>
|
|
||||||
{ object.name }
|
|
||||||
</a>
|
|
||||||
</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>
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
return (
|
return <div>{list}</div>
|
||||||
<div>
|
|
||||||
{ list }
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe it to state changes.
|
export default ObjectsList
|
||||||
export default connect(state => {
|
|
||||||
return {
|
|
||||||
objects: state.objects,
|
|
||||||
currentPath: state.currentPath,
|
|
||||||
loadPath: state.loadPath
|
|
||||||
}
|
|
||||||
})(ObjectsList)
|
|
||||||
|
75
browser/app/js/components/ObjectsListContainer.js
Normal file
75
browser/app/js/components/ObjectsListContainer.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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 classNames from "classnames"
|
||||||
|
import { connect } from "react-redux"
|
||||||
|
import InfiniteScroll from "react-infinite-scroller"
|
||||||
|
import * as actionsObjects from "../actions/objects"
|
||||||
|
import ObjectsList from "./ObjectsList"
|
||||||
|
|
||||||
|
export class ObjectsListContainer extends React.Component {
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { currentBucket, currentPrefix, loadObjects } = this.props
|
||||||
|
if (
|
||||||
|
currentBucket != nextProps.currentBucket ||
|
||||||
|
currentPrefix != nextProps.currentPrefix
|
||||||
|
) {
|
||||||
|
loadObjects()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { objects, isTruncated, currentBucket, loadObjects } = this.props
|
||||||
|
return (
|
||||||
|
<div className="feb-container">
|
||||||
|
<InfiniteScroll
|
||||||
|
pageStart={0}
|
||||||
|
loadMore={() => loadObjects(true)}
|
||||||
|
hasMore={isTruncated}
|
||||||
|
useWindow={true}
|
||||||
|
initialLoad={false}
|
||||||
|
>
|
||||||
|
<ObjectsList objects={objects} />
|
||||||
|
</InfiniteScroll>
|
||||||
|
<div
|
||||||
|
className="text-center"
|
||||||
|
style={{ display: isTruncated && currentBucket ? "block" : "none" }}
|
||||||
|
>
|
||||||
|
<span>Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
currentBucket: state.buckets.currentBucket,
|
||||||
|
currentPrefix: state.objects.currentPrefix,
|
||||||
|
objects: state.objects.list,
|
||||||
|
isTruncated: state.objects.isTruncated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
loadObjects: append => dispatch(actionsObjects.fetchObjects(append))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||||
|
ObjectsListContainer
|
||||||
|
)
|
28
browser/app/js/components/ObjectsSection.js
Normal file
28
browser/app/js/components/ObjectsSection.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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 ObjectsHeader from "./ObjectsHeader"
|
||||||
|
import ObjectsListContainer from "./ObjectsListContainer"
|
||||||
|
|
||||||
|
export const ObjectsSection = () => (
|
||||||
|
<div>
|
||||||
|
<ObjectsHeader />
|
||||||
|
<ObjectsListContainer />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default ObjectsSection
|
@ -14,28 +14,57 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react'
|
import React from "react"
|
||||||
import connect from 'react-redux/lib/components/connect'
|
import { connect } from "react-redux"
|
||||||
|
import { getCurrentBucket, getCurrentPrefix } from "../selectors/buckets"
|
||||||
|
import * as actionsObjects from "../actions/objects"
|
||||||
|
|
||||||
let Path = ({currentBucket, currentPath, selectPrefix}) => {
|
export const Path = ({ currentBucket, currentPrefix, selectPrefix }) => {
|
||||||
|
const onPrefixClick = (e, prefix) => {
|
||||||
|
e.preventDefault()
|
||||||
|
selectPrefix(prefix)
|
||||||
|
}
|
||||||
let dirPath = []
|
let dirPath = []
|
||||||
let path = ''
|
let path = ""
|
||||||
if (currentPath) {
|
if (currentPrefix) {
|
||||||
path = currentPath.split('/').map((dir, i) => {
|
path = currentPrefix.split("/").map((dir, i) => {
|
||||||
dirPath.push(dir)
|
if (dir) {
|
||||||
let dirPath_ = dirPath.join('/') + '/'
|
dirPath.push(dir)
|
||||||
return <span key={ i }><a href="" onClick={ (e) => selectPrefix(e, dirPath_) }>{ dir }</a></span>
|
let dirPath_ = dirPath.join("/") + "/"
|
||||||
|
return (
|
||||||
|
<span key={i}>
|
||||||
|
<a href="" onClick={e => onPrefixClick(e, dirPath_)}>
|
||||||
|
{dir}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h2><span className="main"><a onClick={ (e) => selectPrefix(e, '') } href="">{ currentBucket }</a></span>{ path }</h2>
|
<h2>
|
||||||
|
<span className="main">
|
||||||
|
<a onClick={e => onPrefixClick(e, "")} href="">
|
||||||
|
{currentBucket}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{path}
|
||||||
|
</h2>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
currentBucket: state.currentBucket,
|
currentBucket: getCurrentBucket(state),
|
||||||
currentPath: state.currentPath
|
currentPrefix: state.objects.currentPrefix
|
||||||
}
|
}
|
||||||
})(Path)
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
selectPrefix: prefix => dispatch(actionsObjects.selectPrefix(prefix))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(Path)
|
||||||
|
45
browser/app/js/components/PrefixContainer.js
Normal file
45
browser/app/js/components/PrefixContainer.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 ObjectItem from "./ObjectItem"
|
||||||
|
import * as actionsObjects from "../actions/objects"
|
||||||
|
|
||||||
|
export const PrefixContainer = ({ object, currentPrefix, selectPrefix }) => {
|
||||||
|
const props = {
|
||||||
|
name: object.name,
|
||||||
|
contentType: object.contentType,
|
||||||
|
onClick: () => selectPrefix(`${currentPrefix}${object.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ObjectItem {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
return {
|
||||||
|
object: ownProps.object,
|
||||||
|
currentPrefix: state.objects.currentPrefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
selectPrefix: prefix => dispatch(actionsObjects.selectPrefix(prefix))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(PrefixContainer)
|
@ -24,13 +24,15 @@ import Dropdown from "react-bootstrap/lib/Dropdown"
|
|||||||
import BucketSearch from "./BucketSearch"
|
import BucketSearch from "./BucketSearch"
|
||||||
import BucketList from "./BucketList"
|
import BucketList from "./BucketList"
|
||||||
import Host from "./Host"
|
import Host from "./Host"
|
||||||
|
import * as actionsCommon from "../actions/common"
|
||||||
|
|
||||||
export const SideBar = () => {
|
export const SideBar = ({ sidebarOpen, clickOutside }) => {
|
||||||
return (
|
return (
|
||||||
<ClickOutHandler>
|
<ClickOutHandler onClickOut={clickOutside}>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
"fe-sidebar": true
|
"fe-sidebar": true,
|
||||||
|
toggled: sidebarOpen
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="fes-header clearfix hidden-sm hidden-xs">
|
<div className="fes-header clearfix hidden-sm hidden-xs">
|
||||||
@ -47,4 +49,16 @@ export const SideBar = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(state => state)(SideBar)
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
sidebarOpen: state.common.sidebarOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
clickOutside: () => dispatch(actionsCommon.closeSidebar())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(SideBar)
|
||||||
|
64
browser/app/js/components/StorageInfo.js
Normal file
64
browser/app/js/components/StorageInfo.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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 humanize from "humanize"
|
||||||
|
import * as actionsCommon from "../actions/common"
|
||||||
|
|
||||||
|
export class StorageInfo extends React.Component {
|
||||||
|
componentWillMount() {
|
||||||
|
const { fetchStorageInfo } = this.props
|
||||||
|
fetchStorageInfo()
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { total, free } = this.props.storageInfo
|
||||||
|
const used = total - free
|
||||||
|
const usedPercent = used / total * 100 + "%"
|
||||||
|
const freePercent = free * 100 / total
|
||||||
|
return (
|
||||||
|
<div className="feh-usage">
|
||||||
|
<div className="fehu-chart">
|
||||||
|
<div style={{ width: usedPercent }} />
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<span>Used: </span>
|
||||||
|
{humanize.filesize(total - free)}
|
||||||
|
</li>
|
||||||
|
<li className="pull-right">
|
||||||
|
<span>Free: </span>
|
||||||
|
{humanize.filesize(total - used)}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
storageInfo: state.common.storageInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
fetchStorageInfo: () => dispatch(actionsCommon.fetchStorageInfo())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(StorageInfo)
|
36
browser/app/js/components/__tests__/MobileHeader.test.js
Normal file
36
browser/app/js/components/__tests__/MobileHeader.test.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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 { MobileHeader } from "../MobileHeader"
|
||||||
|
|
||||||
|
describe("Bucket", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
shallow(<MobileHeader sidebarOpen={false} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should toggleSidebar when trigger is clicked", () => {
|
||||||
|
const toggleSidebar = jest.fn()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<MobileHeader sidebarOpen={false} toggleSidebar={toggleSidebar} />
|
||||||
|
)
|
||||||
|
wrapper
|
||||||
|
.find("#sidebar-toggle")
|
||||||
|
.simulate("click", { stopPropagation: jest.fn() })
|
||||||
|
expect(toggleSidebar).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
61
browser/app/js/components/__tests__/ObjectsHeader.test.js
Normal file
61
browser/app/js/components/__tests__/ObjectsHeader.test.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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 { ObjectsHeader } from "../ObjectsHeader"
|
||||||
|
|
||||||
|
describe("ObjectsHeader", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
const sortObjects = jest.fn()
|
||||||
|
shallow(<ObjectsHeader sortObjects={sortObjects} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render columns with asc classes by default", () => {
|
||||||
|
const sortObjects = jest.fn()
|
||||||
|
const wrapper = shallow(<ObjectsHeader sortObjects={sortObjects} />)
|
||||||
|
expect(
|
||||||
|
wrapper.find("#sort-by-name i").hasClass("fa-sort-alpha-asc")
|
||||||
|
).toBeTruthy()
|
||||||
|
expect(
|
||||||
|
wrapper.find("#sort-by-size i").hasClass("fa-sort-amount-asc")
|
||||||
|
).toBeTruthy()
|
||||||
|
expect(
|
||||||
|
wrapper.find("#sort-by-last-modified i").hasClass("fa-sort-numeric-asc")
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render name column with desc class when objects are sorted by name", () => {
|
||||||
|
const sortObjects = jest.fn()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ObjectsHeader sortObjects={sortObjects} sortNameOrder={true} />
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
wrapper.find("#sort-by-name i").hasClass("fa-sort-alpha-desc")
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should call sortObjects when a column is clicked", () => {
|
||||||
|
const sortObjects = jest.fn()
|
||||||
|
const wrapper = shallow(<ObjectsHeader sortObjects={sortObjects} />)
|
||||||
|
wrapper.find("#sort-by-name").simulate("click")
|
||||||
|
expect(sortObjects).toHaveBeenCalledWith("name")
|
||||||
|
wrapper.find("#sort-by-size").simulate("click")
|
||||||
|
expect(sortObjects).toHaveBeenCalledWith("size")
|
||||||
|
wrapper.find("#sort-by-last-modified").simulate("click")
|
||||||
|
expect(sortObjects).toHaveBeenCalledWith("last-modified")
|
||||||
|
})
|
||||||
|
})
|
39
browser/app/js/components/__tests__/ObjectsList.test.js
Normal file
39
browser/app/js/components/__tests__/ObjectsList.test.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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 { ObjectsList } from "../ObjectsList"
|
||||||
|
|
||||||
|
describe("ObjectsList", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
shallow(<ObjectsList objects={[]} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render ObjectContainer for every object", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ObjectsList objects={[{ name: "test1.jpg" }, { name: "test2.jpg" }]} />
|
||||||
|
)
|
||||||
|
expect(wrapper.find("ObjectContainer").length).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render PrefixContainer for every prefix", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ObjectsList objects={[{ name: "abc/" }, { name: "xyz/" }]} />
|
||||||
|
)
|
||||||
|
expect(wrapper.find("Connect(PrefixContainer)").length).toBe(2)
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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 { ObjectsListContainer } from "../ObjectsListContainer"
|
||||||
|
|
||||||
|
describe("ObjectsList", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
shallow(<ObjectsListContainer loadObjects={jest.fn()} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render ObjectsList with objects", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ObjectsListContainer
|
||||||
|
objects={[{ name: "test1.jpg" }, { name: "test2.jpg" }]}
|
||||||
|
loadObjects={jest.fn()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(wrapper.find("ObjectsList").length).toBe(1)
|
||||||
|
expect(wrapper.find("ObjectsList").prop("objects")).toEqual([
|
||||||
|
{ name: "test1.jpg" },
|
||||||
|
{ name: "test2.jpg" }
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should call loadObjects when currentBucket is changed", () => {
|
||||||
|
const loadObjects = jest.fn()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ObjectsListContainer currentBucket="test1" loadObjects={loadObjects} />
|
||||||
|
)
|
||||||
|
wrapper.setProps({ currentBucket: "test2" })
|
||||||
|
expect(loadObjects).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should call loadObjects when currentPrefix is changed", () => {
|
||||||
|
const loadObjects = jest.fn()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ObjectsListContainer currentPrefix="abc/" loadObjects={loadObjects} />
|
||||||
|
)
|
||||||
|
wrapper.setProps({ currentPrefix: "abc/xyz/" })
|
||||||
|
expect(loadObjects).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
37
browser/app/js/components/__tests__/ObjetctItem.test.js
Normal file
37
browser/app/js/components/__tests__/ObjetctItem.test.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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 { ObjectItem } from "../ObjectItem"
|
||||||
|
|
||||||
|
describe("ObjectItem", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
shallow(<ObjectItem name={"test"} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render with content type", () => {
|
||||||
|
const wrapper = shallow(<ObjectItem name={"test.jpg"} contentType={""} />)
|
||||||
|
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() })
|
||||||
|
expect(onClick).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
72
browser/app/js/components/__tests__/Path.test.js
Normal file
72
browser/app/js/components/__tests__/Path.test.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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 { Path } from "../Path"
|
||||||
|
|
||||||
|
describe("Path", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
shallow(<Path currentBucket={"test1"} currentPrefix={"test2"} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render only bucket if there is no prefix", () => {
|
||||||
|
const wrapper = shallow(<Path currentBucket={"test1"} currentPrefix={""} />)
|
||||||
|
expect(wrapper.find("span").length).toBe(1)
|
||||||
|
expect(wrapper.text()).toBe("test1")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render bucket and prefix", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<Path currentBucket={"test1"} currentPrefix={"a/b/"} />
|
||||||
|
)
|
||||||
|
expect(wrapper.find("span").length).toBe(3)
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find("span")
|
||||||
|
.at(0)
|
||||||
|
.text()
|
||||||
|
).toBe("test1")
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find("span")
|
||||||
|
.at(1)
|
||||||
|
.text()
|
||||||
|
).toBe("a")
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find("span")
|
||||||
|
.at(2)
|
||||||
|
.text()
|
||||||
|
).toBe("b")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should call selectPrefix when a prefix part is clicked", () => {
|
||||||
|
const selectPrefix = jest.fn()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<Path
|
||||||
|
currentBucket={"test1"}
|
||||||
|
currentPrefix={"a/b/"}
|
||||||
|
selectPrefix={selectPrefix}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
wrapper
|
||||||
|
.find("a")
|
||||||
|
.at(2)
|
||||||
|
.simulate("click", { preventDefault: jest.fn() })
|
||||||
|
expect(selectPrefix).toHaveBeenCalledWith("a/b/")
|
||||||
|
})
|
||||||
|
})
|
44
browser/app/js/components/__tests__/PrefixContainer.test.js
Normal file
44
browser/app/js/components/__tests__/PrefixContainer.test.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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 { PrefixContainer } from "../PrefixContainer"
|
||||||
|
|
||||||
|
describe("PrefixContainer", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
shallow(<PrefixContainer object={{ name: "abc/" }} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should render ObjectItem with props", () => {
|
||||||
|
const wrapper = shallow(<PrefixContainer object={{ name: "abc/" }} />)
|
||||||
|
expect(wrapper.find("ObjectItem").length).toBe(1)
|
||||||
|
expect(wrapper.find("ObjectItem").prop("name")).toBe("abc/")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should call selectPrefix when the prefix is clicked", () => {
|
||||||
|
const selectPrefix = jest.fn()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<PrefixContainer
|
||||||
|
object={{ name: "abc/" }}
|
||||||
|
currentPrefix={"xyz/"}
|
||||||
|
selectPrefix={selectPrefix}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
wrapper.find("ObjectItem").prop("onClick")()
|
||||||
|
expect(selectPrefix).toHaveBeenCalledWith("xyz/abc/")
|
||||||
|
})
|
||||||
|
})
|
41
browser/app/js/components/__tests__/StorageInfo.test.js
Normal file
41
browser/app/js/components/__tests__/StorageInfo.test.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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 { StorageInfo } from "../StorageInfo"
|
||||||
|
|
||||||
|
describe("StorageInfo", () => {
|
||||||
|
it("should render without crashing", () => {
|
||||||
|
shallow(
|
||||||
|
<StorageInfo
|
||||||
|
storageInfo={{ total: 100, free: 60 }}
|
||||||
|
fetchStorageInfo={jest.fn()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fetchStorageInfo before component is mounted", () => {
|
||||||
|
const fetchStorageInfo = jest.fn()
|
||||||
|
shallow(
|
||||||
|
<StorageInfo
|
||||||
|
storageInfo={{ total: 100, free: 60 }}
|
||||||
|
fetchStorageInfo={fetchStorageInfo}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(fetchStorageInfo).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
@ -20,6 +20,6 @@
|
|||||||
var p = window.location.pathname
|
var p = window.location.pathname
|
||||||
export const minioBrowserPrefix = p.slice(0, p.indexOf("/", 1))
|
export const minioBrowserPrefix = p.slice(0, p.indexOf("/", 1))
|
||||||
|
|
||||||
export const READ_ONLY = 'readonly'
|
export const READ_ONLY = "readonly"
|
||||||
export const WRITE_ONLY = 'writeonly'
|
export const WRITE_ONLY = "writeonly"
|
||||||
export const READ_WRITE = 'readwrite'
|
export const READ_WRITE = "readwrite"
|
||||||
|
24
browser/app/js/history.js
Normal file
24
browser/app/js/history.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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 createHistory from "history/createBrowserHistory"
|
||||||
|
import { minioBrowserPrefix } from "./constants"
|
||||||
|
|
||||||
|
const history = createHistory({
|
||||||
|
basename: minioBrowserPrefix
|
||||||
|
})
|
||||||
|
|
||||||
|
export default history
|
@ -19,7 +19,8 @@ import * as actions from "../../actions/buckets"
|
|||||||
|
|
||||||
describe("buckets reducer", () => {
|
describe("buckets reducer", () => {
|
||||||
it("should return the initial state", () => {
|
it("should return the initial state", () => {
|
||||||
expect(reducer(undefined, {})).toEqual({
|
const initialState = reducer(undefined, {})
|
||||||
|
expect(initialState).toEqual({
|
||||||
list: [],
|
list: [],
|
||||||
filter: "",
|
filter: "",
|
||||||
currentBucket: ""
|
currentBucket: ""
|
||||||
@ -27,38 +28,26 @@ describe("buckets reducer", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should handle SET_BUCKETS", () => {
|
it("should handle SET_BUCKETS", () => {
|
||||||
expect(
|
const newState = reducer(undefined, {
|
||||||
reducer(undefined, { type: actions.SET_LIST, buckets: ["bk1", "bk2"] })
|
type: actions.SET_LIST,
|
||||||
).toEqual({
|
buckets: ["bk1", "bk2"]
|
||||||
list: ["bk1", "bk2"],
|
|
||||||
filter: "",
|
|
||||||
currentBucket: ""
|
|
||||||
})
|
})
|
||||||
|
expect(newState.list).toEqual(["bk1", "bk2"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle SET_BUCKETS_FILTER", () => {
|
it("should handle SET_BUCKETS_FILTER", () => {
|
||||||
expect(
|
const newState = reducer(undefined, {
|
||||||
reducer(undefined, {
|
type: actions.SET_FILTER,
|
||||||
type: actions.SET_FILTER,
|
filter: "test"
|
||||||
filter: "test"
|
|
||||||
})
|
|
||||||
).toEqual({
|
|
||||||
list: [],
|
|
||||||
filter: "test",
|
|
||||||
currentBucket: ""
|
|
||||||
})
|
})
|
||||||
|
expect(newState.filter).toEqual("test")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle SELECT_BUCKET", () => {
|
it("should handle SELECT_BUCKET", () => {
|
||||||
expect(
|
const newState = reducer(undefined, {
|
||||||
reducer(undefined, {
|
type: actions.SET_CURRENT_BUCKET,
|
||||||
type: actions.SET_CURRENT_BUCKET,
|
bucket: "test"
|
||||||
bucket: "test"
|
|
||||||
})
|
|
||||||
).toEqual({
|
|
||||||
list: [],
|
|
||||||
filter: "",
|
|
||||||
currentBucket: "test"
|
|
||||||
})
|
})
|
||||||
|
expect(newState.currentBucket).toEqual("test")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
70
browser/app/js/reducers/__tests__/common.test.js
Normal file
70
browser/app/js/reducers/__tests__/common.test.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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 reducer from "../common"
|
||||||
|
import * as actionsCommon from "../../actions/common"
|
||||||
|
|
||||||
|
describe("common reducer", () => {
|
||||||
|
it("should return the initial state", () => {
|
||||||
|
expect(reducer(undefined, {})).toEqual({
|
||||||
|
sidebarOpen: false,
|
||||||
|
storageInfo: {
|
||||||
|
total: 0,
|
||||||
|
free: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle TOGGLE_SIDEBAR", () => {
|
||||||
|
expect(
|
||||||
|
reducer(
|
||||||
|
{ sidebarOpen: false },
|
||||||
|
{
|
||||||
|
type: actionsCommon.TOGGLE_SIDEBAR
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
sidebarOpen: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle CLOSE_SIDEBAR", () => {
|
||||||
|
expect(
|
||||||
|
reducer(
|
||||||
|
{ sidebarOpen: true },
|
||||||
|
{
|
||||||
|
type: actionsCommon.CLOSE_SIDEBAR
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
sidebarOpen: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle SET_STORAGE_INFO", () => {
|
||||||
|
expect(
|
||||||
|
reducer(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: actionsCommon.SET_STORAGE_INFO,
|
||||||
|
storageInfo: { total: 100, free: 40 }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
storageInfo: { total: 100, free: 40 }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
97
browser/app/js/reducers/__tests__/objects.test.js
Normal file
97
browser/app/js/reducers/__tests__/objects.test.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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 reducer from "../objects"
|
||||||
|
import * as actions from "../../actions/objects"
|
||||||
|
|
||||||
|
describe("objects reducer", () => {
|
||||||
|
it("should return the initial state", () => {
|
||||||
|
const initialState = reducer(undefined, {})
|
||||||
|
expect(initialState).toEqual({
|
||||||
|
list: [],
|
||||||
|
sortBy: "",
|
||||||
|
sortOrder: false,
|
||||||
|
currentPrefix: "",
|
||||||
|
marker: "",
|
||||||
|
isTruncated: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle SET_LIST", () => {
|
||||||
|
const newState = reducer(undefined, {
|
||||||
|
type: actions.SET_LIST,
|
||||||
|
objects: [{ name: "obj1" }, { name: "obj2" }],
|
||||||
|
marker: "obj2",
|
||||||
|
isTruncated: false
|
||||||
|
})
|
||||||
|
expect(newState.list).toEqual([{ name: "obj1" }, { name: "obj2" }])
|
||||||
|
expect(newState.marker).toBe("obj2")
|
||||||
|
expect(newState.isTruncated).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle APPEND_LIST", () => {
|
||||||
|
const newState = reducer(
|
||||||
|
{
|
||||||
|
list: [{ name: "obj1" }, { name: "obj2" }],
|
||||||
|
marker: "obj2",
|
||||||
|
isTruncated: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: actions.APPEND_LIST,
|
||||||
|
objects: [{ name: "obj3" }, { name: "obj4" }],
|
||||||
|
marker: "obj4",
|
||||||
|
isTruncated: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(newState.list).toEqual([
|
||||||
|
{ name: "obj1" },
|
||||||
|
{ name: "obj2" },
|
||||||
|
{ name: "obj3" },
|
||||||
|
{ name: "obj4" }
|
||||||
|
])
|
||||||
|
expect(newState.marker).toBe("obj4")
|
||||||
|
expect(newState.isTruncated).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle SET_SORT_BY", () => {
|
||||||
|
const newState = reducer(undefined, {
|
||||||
|
type: actions.SET_SORT_BY,
|
||||||
|
sortBy: "name"
|
||||||
|
})
|
||||||
|
expect(newState.sortBy).toEqual("name")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle SET_SORT_ORDER", () => {
|
||||||
|
const newState = reducer(undefined, {
|
||||||
|
type: actions.SET_SORT_ORDER,
|
||||||
|
sortOrder: true
|
||||||
|
})
|
||||||
|
expect(newState.sortOrder).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle SET_CURRENT_PREFIX", () => {
|
||||||
|
const newState = reducer(
|
||||||
|
{ currentPrefix: "test1/", marker: "abc", isTruncated: true },
|
||||||
|
{
|
||||||
|
type: actions.SET_CURRENT_PREFIX,
|
||||||
|
prefix: "test2/"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(newState.currentPrefix).toEqual("test2/")
|
||||||
|
expect(newState.marker).toEqual("")
|
||||||
|
expect(newState.isTruncated).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
39
browser/app/js/reducers/common.js
Normal file
39
browser/app/js/reducers/common.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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 * as actionsCommon from "../actions/common"
|
||||||
|
|
||||||
|
export default (
|
||||||
|
state = { sidebarOpen: false, storageInfo: { total: 0, free: 0 } },
|
||||||
|
action
|
||||||
|
) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case actionsCommon.TOGGLE_SIDEBAR:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sidebarOpen: !state.sidebarOpen
|
||||||
|
})
|
||||||
|
case actionsCommon.CLOSE_SIDEBAR:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sidebarOpen: false
|
||||||
|
})
|
||||||
|
case actionsCommon.SET_STORAGE_INFO:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
storageInfo: action.storageInfo
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
@ -15,12 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { combineReducers } from "redux"
|
import { combineReducers } from "redux"
|
||||||
|
import common from "./common"
|
||||||
import alert from "./alert"
|
import alert from "./alert"
|
||||||
import buckets from "./buckets"
|
import buckets from "./buckets"
|
||||||
|
import objects from "./objects"
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
|
common,
|
||||||
alert,
|
alert,
|
||||||
buckets
|
buckets,
|
||||||
|
objects
|
||||||
})
|
})
|
||||||
|
|
||||||
export default rootReducer
|
export default rootReducer
|
||||||
|
65
browser/app/js/reducers/objects.js
Normal file
65
browser/app/js/reducers/objects.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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 * as actionsObjects from "../actions/objects"
|
||||||
|
|
||||||
|
export default (
|
||||||
|
state = {
|
||||||
|
list: [],
|
||||||
|
sortBy: "",
|
||||||
|
sortOrder: false,
|
||||||
|
currentPrefix: "",
|
||||||
|
marker: "",
|
||||||
|
isTruncated: false
|
||||||
|
},
|
||||||
|
action
|
||||||
|
) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case actionsObjects.SET_LIST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
list: action.objects,
|
||||||
|
marker: action.marker,
|
||||||
|
isTruncated: action.isTruncated
|
||||||
|
}
|
||||||
|
case actionsObjects.APPEND_LIST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
list: [...state.list, ...action.objects],
|
||||||
|
marker: action.marker,
|
||||||
|
isTruncated: action.isTruncated
|
||||||
|
}
|
||||||
|
case actionsObjects.SET_SORT_BY:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
sortBy: action.sortBy
|
||||||
|
}
|
||||||
|
case actionsObjects.SET_SORT_ORDER:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
sortOrder: action.sortOrder
|
||||||
|
}
|
||||||
|
case actionsObjects.SET_CURRENT_PREFIX:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentPrefix: action.prefix,
|
||||||
|
marker: "",
|
||||||
|
isTruncated: false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,13 @@
|
|||||||
color: @text-color;
|
color: @text-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&:last-child {
|
||||||
|
&:after {
|
||||||
|
content: '/';
|
||||||
|
margin: 0 4px;
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,10 @@
|
|||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"setupTestFrameworkScriptFile": "./app/js/jest/setup.js",
|
"setupTestFrameworkScriptFile": "./app/js/jest/setup.js",
|
||||||
"testURL": "https://localhost:8080"
|
"testURL": "https://localhost:8080",
|
||||||
|
"moduleNameMapper": {
|
||||||
|
"\\.(css|scss|svg)$": "identity-obj-proxy"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -60,6 +63,7 @@
|
|||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"humanize": "0.0.9",
|
"humanize": "0.0.9",
|
||||||
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"local-storage-fallback": "^4.0.2",
|
"local-storage-fallback": "^4.0.2",
|
||||||
"material-design-iconic-font": "^2.2.0",
|
"material-design-iconic-font": "^2.2.0",
|
||||||
|
@ -2992,6 +2992,10 @@ har-validator@~5.0.3:
|
|||||||
ajv "^5.1.0"
|
ajv "^5.1.0"
|
||||||
har-schema "^2.0.0"
|
har-schema "^2.0.0"
|
||||||
|
|
||||||
|
harmony-reflect@^1.4.6:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.5.1.tgz#b54ca617b00cc8aef559bbb17b3d85431dc7e329"
|
||||||
|
|
||||||
has-ansi@^2.0.0:
|
has-ansi@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
||||||
@ -3271,6 +3275,12 @@ icss-replace-symbols@^1.1.0:
|
|||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
|
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
|
||||||
|
|
||||||
|
identity-obj-proxy@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14"
|
||||||
|
dependencies:
|
||||||
|
harmony-reflect "^1.4.6"
|
||||||
|
|
||||||
ieee754@^1.1.4:
|
ieee754@^1.1.4:
|
||||||
version "1.1.8"
|
version "1.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
|
||||||
|
Loading…
Reference in New Issue
Block a user