Commit ad0325ee authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Fixed eslinting for ci.

parent 361e2af1
......@@ -55,6 +55,14 @@ linting:
- $CI_COMMIT_REF_NAME =~ /^dev-.*$/
- $CI_COMMIT_MESSAGE =~ /\[skip[ _-]tests?\]/i
gui_linting:
stage: test
image: node
script:
- cd gui
- yarn
- yarn run eslint 'src/**/*.js'
tests:
stage: test
image: $TEST_IMAGE
......
......@@ -8,10 +8,16 @@ module.exports = {
}
},
"globals": {
"fetch": false
"fetch": false,
"browser": true
},
"rules": {
"space-before-function-paren": ["error", "never"],
"camelcase": [0]
},
"settings": {
"react": {
"version": "detect"
}
}
}
\ No newline at end of file
......@@ -91,10 +91,10 @@ class ApiDialogUnstyled extends React.Component {
<div className={classes.code}>
<div className={classes.json}>
<ReactJson
src={data}
enableClipboard={false}
collapsed={2}
displayObjectSize={false}
src={data}
enableClipboard={false}
collapsed={2}
displayObjectSize={false}
/>
</div>
</div>
......@@ -154,10 +154,10 @@ class ApiDialogButtonUnstyled extends React.Component {
return (
<div className={classes.root}>
{component ? component({onClick: this.handleShowDialog}) : <Tooltip title="Show API code">
<IconButton onClick={this.handleShowDialog}>
<CodeIcon />
</IconButton>
</Tooltip>
<IconButton onClick={this.handleShowDialog}>
<CodeIcon />
</IconButton>
</Tooltip>
}
<ApiDialog
{...dialogProps} open={showDialog}
......
......@@ -56,7 +56,7 @@ function ReloadSnack() {
>
<SnackbarContent
style={{backgroundColor: amber[700]}}
message={<span>There is a new NOMAD version. Please press your browser's reload (or even shift+reload) button.</span>}
message={<span>There is a new NOMAD version. Please press your browser&apos;s reload (or even shift+reload) button.</span>}
/>
</Snackbar>
}
......
......@@ -39,7 +39,7 @@ class DownloadButton extends React.Component {
handleClick(event) {
event.stopPropagation()
this.setState({ anchorEl: event.currentTarget });
this.setState({ anchorEl: event.currentTarget })
}
async handleSelect(choice) {
......
......@@ -30,7 +30,7 @@ class FAQ extends React.Component {
### How can I be sure that my data will be cited properly?
Sharing means a change of culture. Making data open access is comparable to a
Sharing means a change of culture. Making data open access is comparable to a
publication where references to other work are common practice ever since.
Likewise, using someone's data requires proper citation. We recommend the uploader
to provide references to their data (publications, websites) and the users to
......@@ -43,7 +43,7 @@ class FAQ extends React.Component {
associated with your data might not change right away, but it will be updated
eventually.
### I'd like to install NOMAD on my local computers to be only used by my group
### I'd like to install NOMAD on my local computers to be only used by my group
Local NOMAD deployments are not actively supported at the moment. However, all
[NOMAD software](https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR) is
......
export default function ReloadSnack() {
return <Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open
message={<span id="message-id">There is a new version. Please press your browsers Reload (or even Shift+Reload) button.</span>}
/>
}
\ No newline at end of file
......@@ -322,6 +322,8 @@ class Api {
}))
.catch(handleApiError)
.then(response => {
/* global Blob */
/* eslint no-undef: "error" */
if (response.data instanceof Blob) {
if (response.data.type.endsWith('empty')) {
return {
......@@ -397,7 +399,9 @@ class Api {
// this helps to keep consistent values, e.g. in the metadata search view
if (response.statistics) {
const empty = {}
Object.keys(response.statistics.total.all).forEach(metric => empty[metric] = 0)
Object.keys(response.statistics.total.all).forEach(metric => {
empty[metric] = 0
})
Object.keys(response.statistics)
.filter(key => !['total', 'authors', 'atoms'].includes(key))
.forEach(key => {
......
......@@ -5,7 +5,6 @@ import { Quantity } from '../search/QuantityHistogram'
import SearchContext from '../search/SearchContext'
import { withApi } from '../api'
class DFTSearchAggregations extends React.Component {
static propTypes = {
info: PropTypes.object
......
......@@ -7,6 +7,8 @@ import EMSEntryOverview from './ems/EMSEntryOverview'
import EMSEntryCards from './ems/EMSEntryCards'
import DFTSearchByPropertyAggregations from './dft/DFTSearchByPropertyAggregations'
/* eslint-disable react/display-name */
export const domains = ({
dft: {
name: 'DFT',
......@@ -24,7 +26,7 @@ export const domains = ({
* onChange (callback to propagate searchValue changes).
*/
SearchAggregations: DFTSearchAggregations,
/**
/**
* A component that is used to render the search aggregations by property.
*/
SearchByPropertyAggregations: DFTSearchByPropertyAggregations,
......
......@@ -22,7 +22,6 @@ learn more about NOMAD's archive format [here](/metainfo).
`
class MetainfoDialogUnstyled extends React.PureComponent {
static propTypes = {
classes: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
......@@ -45,7 +44,7 @@ class MetainfoDialogUnstyled extends React.PureComponent {
render() {
const {classes, metaInfoData, onClose} = this.props
return <Dialog open className={classes.root}>
return <Dialog open className={classes.root}>
<DialogTitle>
{metaInfoData.name}
</DialogTitle>
......@@ -142,7 +141,7 @@ class ArchiveEntryView extends React.Component {
updateMetaInfo() {
if (this.props.api && this.props.info && !this.state.metaInfo) {
this.props.api.getMetaInfo(this.props.info.domains.find(domain => domain.name === 'dft').metainfo.all_package).then(metaInfo => { // TODO handle the domain specificity
this.props.api.getMetaInfo(this.props.info.domains.find(domain => domain.name === 'dft').metainfo.all_package).then(metaInfo => { // TODO handle the domain specificity
if (!this.unmounted) {
this.setState({metaInfo: metaInfo})
}
......
......@@ -209,8 +209,8 @@ class RawFiles extends React.Component {
downloadUrl = `raw/calc/${uploadId}/${calcId}/*?strip=true`
} else if (selectedFiles.length > 0) {
// use a prefix to shorten the url
const prefix = selectedFiles[0].substring(0, selectedFiles[0].lastIndexOf("/"))
const files = selectedFiles.map(path => path.substring(path.lastIndexOf("/") + 1)).join(',')
const prefix = selectedFiles[0].substring(0, selectedFiles[0].lastIndexOf('/'))
const files = selectedFiles.map(path => path.substring(path.lastIndexOf('/') + 1)).join(',')
downloadUrl = `raw/${uploadId}?files=${encodeURIComponent(files)}&prefix=${prefix}&strip=true`
}
......
......@@ -51,7 +51,7 @@ function getSuggestionValue(suggestion) {
const styles = theme => ({
container: {
position: 'relative',
position: 'relative'
},
suggestionsContainerOpen: {
position: 'absolute',
......
......@@ -161,7 +161,8 @@ export class EntryListUnstyled extends React.Component {
}
}
componentWillUpdate(prevProps) {
// TODO was this really intentional
UNSAFE_componentWillUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
this.setState({selected: []})
}
......
......@@ -35,7 +35,7 @@ const _mapping = {
'oscillator_strengths': 'Oscillator strengths',
'transition_dipole_moments': 'Transition dipole moments'}
function map_key (name) {
function mapKey(name) {
if (name in _mapping) {
return _mapping[name]
}
......@@ -102,7 +102,7 @@ class QuantityHistogramUnstyled extends React.Component {
const data = Object.keys(this.props.data)
.map(key => ({
name: map_key(key),
name: mapKey(key),
value: this.props.data[key][this.props.metric]
}))
......@@ -247,7 +247,6 @@ class QuantityHistogramUnstyled extends React.Component {
export const QuantityHistogram = withStyles(QuantityHistogramUnstyled.styles)(QuantityHistogramUnstyled)
class QuantityUnstyled extends React.Component {
static propTypes = {
classes: PropTypes.object.isRequired,
......
import React from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
/* eslint-disable-next-line */
import { domains } from '../domains' // TODO this causes a weird import bug
import ChipInput from 'material-ui-chip-input'
import Autosuggest from 'react-autosuggest'
......
......@@ -98,7 +98,7 @@ class UploadsHistogramUnstyled extends React.Component {
}
]
timeInterval = Object.assign({}, ...this.intervals.map(e => ( {[e.value]: e.number})))
timeInterval = Object.assign({}, ...this.intervals.map(e => ({[e.value]: e.number})))
componentDidMount() {
const from_time = new Date(this.startDate).getTime()
......@@ -120,9 +120,7 @@ class UploadsHistogramUnstyled extends React.Component {
handleIntervalChange(newInterval) {
// TODO: add a refresh button so directly updating interval is not necessary
this.state.interval = newInterval
//this.setState({interval: newInterval})
this.handleQueryChange()
this.setState({interval: newInterval}, () => this.handleQueryChange())
}
handleTimeChange(newTime, key, target) {
......@@ -136,7 +134,7 @@ class UploadsHistogramUnstyled extends React.Component {
key === 'from_time' ? this.setState({from_time: date.getTime()}) : this.setState({until_time: date.getTime()})
}
if (target === 'picker' || target === 'all') {
document.getElementById(key).value = date.toISOString().substring(0,10)
document.getElementById(key).value = date.toISOString().substring(0, 10)
}
}
......@@ -145,21 +143,21 @@ class UploadsHistogramUnstyled extends React.Component {
if (selected === this.state.time) {
this.props.onChanged(null, null, null)
} else {
const deltaT = this.timeInterval[this.state.interval]
const deltaT = this.timeInterval[this.state.interval]
this.handleTimeChange(selected, 'from_time', 'all')
this.handleTimeChange(selected + deltaT, 'until_time', 'all')
this.handleQueryChange()
}
}
resolveDate (name) {
resolveDate(name) {
const date = new Date(parseInt(name, 10))
const year = date.toLocaleDateString(undefined, {year: 'numeric'})
const month = date.toLocaleDateString(undefined, {month: 'short'})
const day = date.toLocaleDateString(undefined, {day: 'numeric'})
const hour = date.toLocaleTimeString(undefined, {hour: 'numeric'})
const min = date.toLocaleTimeString(undefined, {minute: 'numeric'})
const sec= date.toLocaleTimeString(undefined, {second: 'numeric'})
const sec = date.toLocaleTimeString(undefined, {second: 'numeric'})
const intervals = {
'1y': year,
......@@ -173,54 +171,54 @@ class UploadsHistogramUnstyled extends React.Component {
return intervals[this.state.interval]
}
hover (svg, bar) {
hover(svg, bar) {
const textOffset = 25
const tooltip = svg.append('g')
.attr('class', 'tooltip')
.style('display', 'none')
.attr('class', 'tooltip')
.style('display', 'none')
const hoverBox = tooltip.append('rect')
.attr('width', 10)
.attr('height', 20)
.attr('fill', 'white')
.style('opacity', 0.0)
.attr('width', 10)
.attr('height', 20)
.attr('fill', 'white')
.style('opacity', 0.0)
const text = tooltip.append('text')
.attr('x', textOffset)
.attr('dy', '1.2em')
.style('text-anchor', 'start')
.attr('font-size', '12px')
//.attr('font-weight', 'bold')
.attr('x', textOffset)
.attr('dy', '1.2em')
.style('text-anchor', 'start')
.attr('font-size', '12px')
// .attr('font-weight', 'bold')
bar
.on('mouseover', () => {
tooltip.style('display', null)
let { width } = text.node().getBBox()
hoverBox.attr('width', `${ width + textOffset }px`)
})
.on('mouseout', () => tooltip.style('display', 'none'))
.on('mousemove', function(d) {
let xPosition = d3.mouse(this)[0] - 15
let yPosition = d3.mouse(this)[1] - 25
tooltip.attr('transform', `translate( ${ xPosition }, ${ yPosition })`)
tooltip.attr('data-html', 'true')
tooltip.select('text').text( new Date(d.time).toISOString() + ': ' + d.value )
})
.on('mouseover', () => {
tooltip.style('display', null)
let { width } = text.node().getBBox()
hoverBox.attr('width', `${width + textOffset}px`)
})
.on('mouseout', () => tooltip.style('display', 'none'))
.on('mousemove', function(d) {
let xPosition = d3.mouse(this)[0] - 15
let yPosition = d3.mouse(this)[1] - 25
tooltip.attr('transform', `translate( ${xPosition}, ${yPosition})`)
tooltip.attr('data-html', 'true')
tooltip.select('text').text(new Date(d.time).toISOString() + ': ' + d.value)
})
}
updateChart () {
updateChart() {
let data = []
if (! this.props.data) {
if (!this.props.data) {
return
} else {
data = Object.keys(this.props.data).map(key => ({
time: parseInt(key, 10),
name: this.resolveDate(key),
value: this.props.data[key][this.props.metric]
}))}
}))
}
data.sort((a, b) => d3.ascending(a.time, b.time))
if (data.length > 0) {
......@@ -228,13 +226,13 @@ class UploadsHistogramUnstyled extends React.Component {
this.handleTimeChange(this.state.until_time, 'until_time', 'picker')
}
const scalePower = this.state.scalePower
const scalePower = this.state.scalePower
const width = this.container.current.offsetWidth
const height = this.props.height
const margin = Math.round(0.1*height)
const margin = Math.round(0.1 * height)
const x = scaleBand().rangeRound([margin, width]).padding(0.1)
const y = scalePow().range([height-margin, margin]).exponent(scalePower)
const y = scalePow().range([height - margin, margin]).exponent(scalePower)
const max = d3.max(data, d => d.value) || 0
x.domain(data.map(d => d.name))
......@@ -247,36 +245,36 @@ class UploadsHistogramUnstyled extends React.Component {
const xAxis = d3.axisBottom(x)
svg.select('.xaxis').remove()
svg.append('g')
.attr('transform', `translate(0,${height-margin})`)
.attr('class', 'xaxis')
.call(xAxis)
.attr('transform', `translate(0,${height - margin})`)
.attr('class', 'xaxis')
.call(xAxis)
svg.select('.xlabel').remove()
svg.append('text')
.attr('class', 'xlabel')
.attr("x", width)
.attr("y", height-4)
.attr('dy', ".35em")
.attr('font-size', '12px')
.style('text-anchor', 'end')
.attr('class', 'xlabel')
.attr('x', width)
.attr('y', height - 4)
.attr('dy', '.35em')
.attr('font-size', '12px')
.style('text-anchor', 'end')
const yAxis = d3.axisLeft(y)
svg.select('.yaxis').remove()
svg.append('g')
.attr('transform', `translate(${margin}, 0)`)
.attr('class', 'yaxis')
.call(yAxis)
.attr('transform', `translate(${margin}, 0)`)
.attr('class', 'yaxis')
.call(yAxis)
const {label, shortLabel} = this.props.metricsDefinitions[this.props.metric]
svg.select('.ylabel').remove()
svg.append('text')
.attr('class','ylabel')
.attr('x', 0)
.attr('y', 0)
.attr('dy', "1em")
.attr('text-anchor', 'start')
.attr('font-size', '12px')
.text(`${shortLabel ? shortLabel : label}`)
.attr('class', 'ylabel')
.attr('x', 0)
.attr('y', 0)
.attr('dy', '1em')
.attr('text-anchor', 'start')
.attr('font-size', '12px')
.text(`${shortLabel || label}`)
let withData = svg
.selectAll('.bar').remove().exit()
......@@ -300,9 +298,9 @@ class UploadsHistogramUnstyled extends React.Component {
svg.select('.tooltip').remove()
svg.call(this.hover, item)
}
}
render () {
render() {
return (
<div>
<Grid container justify='space-around' spacing={24}>
......@@ -314,10 +312,10 @@ class UploadsHistogramUnstyled extends React.Component {
onChange={(event) => this.setState({scalePower: event.target.value})}
label= 'scale'
> {this.scales.map(item => (
<MenuItem
value={item.value}
key={item.label}> {item.label}
</MenuItem>))}
<MenuItem
value={item.value}
key={item.label}> {item.label}
</MenuItem>))}
</Select>
</Grid>
<Grid item xs={3}>
......@@ -327,17 +325,17 @@ class UploadsHistogramUnstyled extends React.Component {
type="date"
onChange={(event) => this.handleTimeChange(event.target.value, 'from_time', 'state')}
InputLabelProps={{
shrink: true,
shrink: true
}}
/>
</Grid>
<Grid item xs={3}>
<Select
id='interval'
value={this.state.interval}
onChange={(event) => this.handleIntervalChange(event.target.value)}
label= 'interval'
> {this.intervals.map(item => (
id='interval'
value={this.state.interval}
onChange={(event) => this.handleIntervalChange(event.target.value)}
label= 'interval'
> {this.intervals.map(item => (
<MenuItem value={item.value} key={item.value}>
{item.label}
</MenuItem>))}
......@@ -350,7 +348,7 @@ class UploadsHistogramUnstyled extends React.Component {
type="date"
onChange={(event) => this.handleTimeChange(event.target.value, 'until_time', 'state')}
InputLabelProps={{
shrink: true,
shrink: true
}}
/>
</Grid>
......@@ -366,7 +364,6 @@ class UploadsHistogramUnstyled extends React.Component {
export const UploadsHistogram = withStyles(UploadsHistogramUnstyled.styles)(UploadsHistogramUnstyled)
class UploadersListUnstyled extends React.Component {
static propTypes = {
classes: PropTypes.object.isRequired
}
......@@ -379,18 +376,18 @@ class UploadersListUnstyled extends React.Component {
static contextType = SearchContext.type
render () {
render() {
const {state: {usedMetric}} = this.context
return (
<Grid>
<Quantity quantity="uploader" title="Top Uploaders" scale={1} metric={usedMetric} />
</Grid>
<Grid>
<Quantity quantity="uploader" title="Top Uploaders" scale={1} metric={usedMetric} />
</Grid>
)
}
}
export const UploadersList = withStyles(UploadersListUnstyled.styles)(UploadersListUnstyled)
export const UploadersList = withStyles(UploadersListUnstyled.styles)(UploadersListUnstyled)
class UploadsChart extends React.Component {
static propTypes = {
......@@ -407,7 +404,7 @@ class UploadsChart extends React.Component {
render() {
const {classes, metricsDefinitions, ...props} = this.props
const {state: {response, usedMetric, query, }, setQuery} = this.context
const {state: {response, usedMetric, query}, setQuery} = this.context
return (
<Grid container spacing={24}>
......@@ -427,8 +424,8 @@ class UploadsChart extends React.Component {
<UploadersList />
</Grid>
</Grid>
)
)
}
}
export default compose(withApi(false, false), withStyles(UploadsChart.styles))(UploadsChart)
\ No newline at end of file
export default compose(withApi(false, false), withStyles(UploadsChart.styles))(UploadsChart)
......@@ -18,7 +18,7 @@ class ConfirmDialog extends React.Component {
}
render() {
const { onConfirm, onClose, open, title, content,confirmLabel } = this.props
const { onConfirm, onClose, open, title, content, confirmLabel } = this.props